Rename PDF file - kofax

I would like to create a custom module that renames the generated PDF files by taking a generated filename string containg some field values from index fields, batch fields etc.
So when it comes to batch processing i could go for this (setupTransformator contains the parsed values from the custom storage strings)
public void ProcessBatch(IBatch batch)
{
IACDataElement batchElement = GetBatchElementFromBatch(batch);
IACDataElementCollection currentDocuments = GetDocumentsFromBatchElement(batchElement);
IACDataElement customStorageStrings = GetCustomStorageStringsFromBatch(batch);
IACDataElementCollection batchFields = GetElementsByName(batchElement, ResourcesKofax.BATCH_FIELDS, ResourcesKofax.BATCH_FIELD);
setupTransformator = new SetupTransformator(customStorageStrings);
for (int i = 0; i < currentDocuments.Count; i++)
{
int currentDocumentIndex = i + 1;
IACDataElement currentDocument = currentDocuments[currentDocumentIndex];
IACDataElementCollection indexFields = GetElementsByName(currentDocument, ResourcesKofax.INDEX_FIELDS, ResourcesKofax.INDEX_FIELD);
string targetFilename = setupTransformator.GetFilename(batchElement, currentDocument, batchFields, indexFields);
string documentFilePath = currentDocument[ResourcesKofax.PDF_GENERATION_FILE_NAME];
// rename the PDF file
}
batch.BatchClose(KfxDbState.KfxDbBatchReady, KfxDbQueue.KfxDbQueueNext, 0, string.Empty);
}
private IACDataElement GetBatchElementFromBatch(IBatch batch)
{
IACDataElement rootElement = batch.ExtractRuntimeACDataElement(0);
return rootElement.FindChildElementByName(ResourcesKofax.BATCH);
}
private IACDataElementCollection GetDocumentsFromBatchElement(IACDataElement batchElement)
{
return GetElementsByName(batchElement, ResourcesKofax.DOCUMENTS, ResourcesKofax.DOCUMENT);
}
private IACDataElement GetCustomStorageStringsFromBatch(IBatch batch)
{
IACDataElement setupElement = batch.ExtractSetupACDataElement(0);
IACDataElementCollection batchClasses = GetElementsByName(setupElement, ResourcesKofax.BATCH_CLASSES, ResourcesKofax.BATCH_CLASS);
IACDataElement batchClass = batchClasses[1];
return batchClass.FindChildElementByName(ResourcesKofax.BATCH_CLASS_CUSTOM_STORAGE_STRINGS);
}
private IACDataElementCollection GetElementsByName(IACDataElement dataElement, string rootName, string targetName)
{
return dataElement.FindChildElementByName(rootName).FindChildElementsByName(targetName);
}
Do I have to use the File.Move method or is there a method from the Kofax library I can use?

File names should be handled by Export Connectors only. As long as the batch is in the system you shouldn't have their names changed as this could lead to data loss and corruption.
This especially applies when using field values for a PDF's name - since values are subject to change as long as the batch is in the system, how would you accommodate this? Nothing is stopping your users from processing a batch in your custom module and set the batch back to validation and change one or more fields.
Speaking of the Export Connector and its API:
By default, Kofax offers two methods to export a PDF - both on the ReleaseData object (this is taken from the API docs):
CopyKofaxPDFFile: Copy the PDF file that belongs to a document into the export PDF path that is defined during export setup.
CopyKofaxPDFFileToPath: Copy the PDF file that belongs to a document into a specified path (the path is a string input argument for this method).
Both methods make use of something that you could define during Setup - for example, CopyKofaxPDFFile makes use of the KofaxPDFPath property. I am not sure if there's a property reserved for the file name.
I usually stick with the KofaxPDFProperty exposed during runtime and perform a File.Copy operation. I wouldn't recommend moving the file or deleting it as this is something that KC automatically handles once the batch was exported successfully (in theory, there could be another export, or the export might just fail).
Use the ReleaseData object to access field values, and string interpolation to define the PDF's final name.

Related

How can I turn a txt file into a string just using its path?

So I have a program that turns a .txt file into a string to then send it via bluetooth to a printer, the problem is that right now I'm doing it using the file name but I wanted to do it only using the path of the file, this has to do with the fact that I need to search on the folder for any existing txt files and if there are any I need to print the first one and then delete it, so I can't be doing it by using the file's name. This is my code so far:
private fun readFile() String {
val file = File(storage/emulated/0/IS4-PDF-RDP/00233116695912019091310005913BLUETOOTH.txt)
var ins InputStream = file.inputStream()
read contents of IntputStream to String
var content = ins.readBytes().toString(Charset.defaultCharset())
return content
}
You can find the first file in the folder read it and then delete it as per your requirements
File("/storage/emulated/0/IS4-PDF-RDP/").walk().find {
it.extension == "txt"
}?.apply {
inputStream().readBytes().toString(Charset.defaultCharset())
delete()
}

SSIS Script Component Source - Read from SharePoint 365 List via Odata

I apologize if my question is not adequately described. I am a .Net / C# / SSIS newbie. Also apologize if already answered, I've tried searching here and Google for a few hours without luck.
Background: I need to pull data rows from a SharePoint 365 list and unpivot certain columns into a format ready for import to a SQL Server table. I realize that SSIS has an Odata Source and built-in Unpivot component, and I've successfully used those for proof of concept.
However, I believe I need a custom script component because the nummber of columns to unpivot from the source SharePoint list is variable. Each month or so, a new column will be added (it relates to a financial forecasting "tool" in SharePoint, and the latest forecasted month changes). My understanding is that source columns must be defined in SSIS at design time, so if my source columns are changing, the only way I can think to address this without manually changing the SSIS data flow each month is to programatically combine the Odata source and unpivot functions into a custom script component.
I understand, or can figure out, the unpivot logic. The part I am struggling with is how to actually connect to and expose a given list and it's data rows / columns as lists that I can loop through and perform my mapping to the output columns.
My "starting point" for requested guidance is this:
1) Created and successfully connected to the SharePoint site in question using standard SSIS Odata Connection Manager.
2) Created standard "Script Component", type = source, on the visual designer.
3) From script component properties, associated the Odata connection manager with the name of "myConnection".
4) Need help -> within the script component, open a connection to a specific list, read it's contents, and perform unpivot logic.
For illustration purposes, assume the source is a SharePoint list with two "fixed" string columns titled Study and Site, and a variable number of columns with names matching month-end dates (e.g. 9/30/2016, 10/31/2016, etc.) that contain integer values. I want to map the study and site source columns to destination columns of the same name and unpivot the month columns where column name is mapped to ProjectionMonth and the integer value is mapped to ProjectionValue.
Here's the basic algorithm I have in mind (I realize this isn't compilable - that's where I need your help!):
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
using System.Data.SqlClient;
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
IDTSConnectionManager100 connMgr;
SqlConnection sqlConn; // from MSDN tutorial, but I don't know how to adapt to Odata/SharePoint 365 list
SqlDataReader sqlReader; // not sure how to adapt
public override void AcquireConnections(object Transaction)
{
connMgr = this.Connections.myConnection;
sqlConn = (SqlConnection)connMgr.AcquireConnection(null); // This is from MSDN tutorial, but I don't know how to adapt to Odata
}
public override void PreExecute()
{
//Not sure how to adapt to odata to read specific SharePoint list
SqlCommand cmd = new SqlCommand("SELECT * FROM <some sharepoint list>", sqlConn);
sqlReader = cmd.ExecuteReader();
}
public override void PostExecute()
{
sqlReader.Close(); // Not sure how to adapt.
}
public override void CreateNewOutputRows()
{
string myStudy;
string mySite;
string myProjectionMonth;
string myProjectionValue;
// This is a rough representation of the logic needed.
// I realize that the actual code to access column values / names depends on the class(es) I need to use, but not sure what those classes are / how to access
foreach (myListRow in sqlConn.rows)
{
myStudy = myListRow.Columns["Study"].value;
mySite = myListRow.Columns["Site"].value;
foreach (myColumn in myListRow.Columns)
if (DateTime.TryParse(myColumn.Name, out dateValue))
{
myProjectionMonth = myColumn.Name;
myProjectionValue = myColumn.Value;
Output0Buffer.AddRow();
Output0Buffer.Study = myStudy;
Output0Buffer.Site = mySite;
Output0Buffer.ProjectionMonth = myProjectionMonth;
Output0Buffer.ProjectionValue = myProjectionValue;
}
}
}
}
Edit: as an example, assume the source SharePoint list has the following:
Study Site 9/30/2016 10/31/2016
123 ABC 5 10
I want to the script component to connect to the list, read it's contents, and return the following unpivoted data set for eventual load into SQL Server:
Study Site ProjectionMonth ProjectionValue
123 ABC 9/30/2016 5
123 ABC 10/31/2016 10
So this is probably not an ideal way of doing it, and it doesn't leverage the standard SSIS Odata Connection Manager that I wanted... but it does technically get the job done and it's good enough for me, for now.
Would be interested on any suggested feedback / improvements / etc., if you have any.
#region Namespaces
using System;
using Microsoft.SharePoint.Client;
using System.Security;
using System.Collections.Generic;
#endregion
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
public override void CreateNewOutputRows()
{
// Connect to SharePoint
ClientContext context = new ClientContext("https://<redacted>.sharepoint.com/Development");
SecureString passWord = new SecureString();
foreach (char c in Variables.sharepointPassword.ToCharArray()) passWord.AppendChar(c);
context.Credentials = new SharePointOnlineCredentials("<redacted>#<redacted>.onmicrosoft.com", passWord);
// Define the target list
List EnrollmentList = context.Web.Lists.GetByTitle("Enrollment Projections");
// Find all fields in the target list
FieldCollection myFields = EnrollmentList.Fields;
context.Load(myFields);
context.ExecuteQuery();
// Load all rows from the target list
CamlQuery query = CamlQuery.CreateAllItemsQuery(100);
ListItemCollection items = EnrollmentList.GetItems(query);
context.Load(items);
context.ExecuteQuery();
//Store valid date fields
List<Field> myDateFields = new List<Field>();
foreach (Field tempField in myFields)
{
DateTime tempDate;
if (DateTime.TryParse(tempField.Title, out tempDate))
{
myDateFields.Add(tempField);
}
}
string myStudy;
string mySite;
string myMonth;
string myValue;
foreach (ListItem listItem in items)
{
myStudy = listItem["Study"].ToString();
mySite = listItem["Site"].ToString();
foreach (Field tempField in myDateFields)
{
myMonth = tempField.Title;
myValue = listItem[tempField.InternalName.ToString()].ToString();
Output0Buffer.AddRow();
Output0Buffer.Study = myStudy;
Output0Buffer.Site = mySite;
Output0Buffer.ProjectedMonth = myMonth;
Output0Buffer.ProjectedValue = Convert.ToInt32(myValue);
}
}
}
}

Search fileName and get full path based on a pattern in C#

How do I get the full path for a file based on first 6 letters in the filename in C#? Is there any Regex for this?
I have a folder with 500K+ images.
All the images are named as per the database column value.
I am querying the table and based on the column value I have to search the folder for the specific file.
If the column value is 209050 there will be one file in the folder like 209050.pdf or 209050_12345.pdf. There will always be one image for 209050.
Please help
var files = Directory.EnumerateFiles(#"C:\MyRootDir", "209050*", SearchOption.AllDirectories);
This will return an enumerable of strings. Each string will the full path of the file whose name matches the specified pattern, in this case, any file whose name begins with '209050', irrespective of the extension. This will even search within sub directories of the folder MyRootDir.
If you wish to only filter for jpg files, change the 2nd argument accordingly.
var files = Directory.EnumerateFiles(#"C:\MyRootDir", "209050*.jpg", SearchOption.AllDirectories);
In case you are NOT using .Net Framework 4 or 4.5, you could use the GetFiles method instead.
var files = Directory.GetFiles(#"C:\MyRootDir", "209050*.jpg", SearchOption.AllDirectories);
The following will show you all .jpg files starting with 209050 in C:\MyDirectory:
using System;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string[] myFiles = Directory.GetFiles(#"C:\MyDirectory");
foreach (var x in myFiles)
{
string[] splitName = x.Split('\\');
string fileName = splitName[splitName.Length - 1];
if (fileName.StartsWith("209050") && fileName.EndsWith(".jpg"))
{
Console.WriteLine(x);
}
}
Console.ReadLine();
}
}
}
Here is my directory:
Here is the output:
Does this help?

Dynamic data structures in C#

I have data in a database, and my code is accessing it using LINQ to Entities.
I am writing some software where I need to be able to create a dynamic script. Clients may write the scripts, but it is more likely that they will just modify them. The script will specify stuff like this,
Dataset data = GetDataset("table_name", "field = '1'");
if (data.Read())
{
string field = data["field"];
while (cway.Read())
{
// do some other stuff
}
}
So that script above is going to read data from the database table called 'table_name' in the database into a list of some kind based on the filter I have specified 'field='1''. It is going to be reading particular fields and performing normal comparisons and calculations.
The most important thing is that this has to be dynamic. I can specify any table in our database, any filter and I then must be able to access any field.
I am using a script engine that means the script I am writing has to be written in C#. Datasets are outdated and I would rather keep away from them.
Just to re-iterate I am not really wanting to keep with the above format, and I can define any method I want to behind the scenes for my C# script to call. The above could end up like this for instance,
var data = GetData("table_name", "field = '1'");
while (data.ReadNext())
{
var value = data.DynamicField;
}
Can I use reflection for instance, but perhaps that would be too slow? Any ideas?
If you want to read dynamically a DataReader context, it's a pretty easy step:
ArrayList al = new ArrayList();
SqlDataReader dataReader = myCommand.ExecuteReader();
if (dataReader.HasRows)
{
while (dataReader.Read())
{
string[] fields = new string[datareader.FieldCount];
for (int i =0; i < datareader.FieldCount; ++i)
{
fields[i] = dataReader[i].ToString() ;
}
al.Add(fields);
}
}
This will return an array list composed by a dynamic object based on the number of field the reader has.

What is the correct way to format SPGridView values being displayed?

Problem
As we know, SharePoint saves data in database in plain text. Some fields even have concatenated strings like <id>;#<value> for user fields. Percents are saved as doubles (1.00000000000000 for 100%) and etc.
Ofcourse, I want to display data as they are displayed in lists.
What should I do?
Should I use derived SPBoundField to format values (Which I actually did and it works fine until you want to filter (probably SPBoundField won't format me values because i use ObjectDataSource not list and with reflector I saw if there are SPListItems in datasource, then it formats correctly. Not my case)
alt text http://img199.imageshack.us/img199/2797/ss20090820110331.png
Or must I loop through all the DataTable and format each row accordingly?
What are Your techniques?
Thank you.
Here is how I solved this issue.
<asp:TemplateField HeaderText="Campaign Members">
<ItemTemplate>
<%# RemoveCharacters(Eval("CampaignMembers").ToString())%>
</ItemTemplate>
</asp:TemplateField>
// Make sure declare using System.Text.RegularExpression;
protected string RemoveCharacters(object String)
{
string s1 = String.ToString();
string newString = Regex.Replace(s1, #"#[\d-];", string.Empty);
newString = Regex.Replace(newString, "#", " ");
return newString.ToString();
}
I normaly use ItemTemplates that inherit from ITemplate. With in the ItemTemplate I use the SPFieldxxxValue classes or some custom formating code. This saves looping through the DataTable and the ItemTemplates can be reused.
The ItemTemplates are attached in Column Binding
E.G
// Normal Data Binding
SPBoundField fld = new SPBoundField();
fld.HeaderText = field.DisplayName;
fld.DataField = field.InternalName;
fld.SortExpression = field.InternalName;
grid.Columns.Add(fld);
// ItemTemplate Binding
TemplateField fld = new TemplateField();
fld.HeaderText = field.DisplayName;
fld.ItemTemplate = new CustomItemTemplateClass(field.InternalName);
fld.SortExpression = field.InternalName;
grid.Columns.Add(fld);
An example of a ItemTemplate
public class CustomItemTemplateClass : ITemplate
{
private string FieldName
{ get; set; }
public CustomItemTemplateClass(string fieldName, string formatString)
{
FieldName = fieldName;
}
#region ITemplate Members
public void InstantiateIn(Control container)
{
Literal lit = new Literal();
lit.DataBinding += new EventHandler(lit_DataBinding);
container.Controls.Add(lit);
}
#endregion
void lit_DataBinding(object sender, EventArgs e)
{
Literal lit = (Literal)sender;
SPGridViewRow container = (SPGridViewRow)lit.NamingContainer;
string fieldValue = ((DataRowView)container.DataItem)[FieldName].ToString();
//Prosses Filed value here
SPFieldLookupValue lookupValue = new SPFieldLookupValue(fieldValue);
//Display new value
lit.Text = lookupValue.LookupValue;
}
}
Here are a few options. I don't know the output of all of them (would be a good blog post) but one of them should do what you want:
SPListItem.GetFormattedValue()
SPField.GetFieldValue()
SPField.GetFieldValueAsHtml()
SPField.GetFieldValueAsText()
It may also be handy to know that if you ever want to make use of the raw values then have a look at the SPField*XYZ*Value classes. For example the form <id>;#<value> you mention is represented by the class SPFieldUserValue. You can pass the raw text to its constructor and extract the ID, value, and most usefully User very easily.
I would suggest either to format the values before binding them to the spgridview. Linq and an anonymous type is preffered or to call a code behind function on the field that needs the formatting upon binding.
DataField='<%# FormatUserField(Eval("UserFieldName")) %>'
or...maybe a templated field?
After all, i did have not know any other solution to loop through DataTable rows and format them accordingly.
If your SPGridView's data source is list, try out SPBoundField.

Resources