VSTO How to identify parts (like a table) in a word document? - ms-word

I have multiple tables in a word template and need to change each of it in an other way. Also it is possible that one or the other will be deleted or inserted so I can't say I take the 5th and that's always the same one.
The identification has to be saved so I can't use the .ID value.
What way is there to identify a specific table with VSTO? Preferable one which can also be set in the document without VSTO.

I found a way to do it:
Mark the table in word and add a bookmark to it. You have to choose a unique name so you can also identify the table. The identification can be done with a method like the following:
public Word.Bookmark GetBookmark(String bookmarkName)
{
// Find bookmark
Word.Bookmark bookmark = null;
foreach (Word.Bookmark curBookmark in Globals.ThisDocument.Bookmarks)
{
if (curBookmark.Name.Equals(bookmarkName))
{
bookmark = curBookmark;
break;
}
}
return bookmark;
}

I ran into similar problem. To solve the problem I set title of the table in word tamplate(Right click table->Table Properties...->Alt text->Title) and search all the table in word document for the title. Below is the code I used to search the table.
public static Table getTable(Document doc, String title){
int totalTables = doc.Tables.Count;
Microsoft.Office.Interop.Word.Table ret = null;
for (int i = 1; i <= totalTables; i++){
if (title.Equals(doc.Tables[i].Title, StringComparison.OrdinalIgnoreCase)){
ret = doc.Tables[i];
break;
}
}
return ret;
}

Is it possible to use alternative text for the tables as identifier?
Use the below code to retrieve them (code copied from here)
Word.Application wdApp = Application;
Word.Document wdDoc = wdApp.ActiveDocument;
Word.Table wdTable = wdDoc.Tables[1];
MessageBox.Show(wdTable.Title + "\n" + wdTable.Descr);

Related

Salesforce trigger-Not able to understand

Below is the code written by my collegue who doesnt work in the firm anymore. I am inserting records in object with data loader and I can see success message but I do not see any records in my object. I am not able to understand what below trigger is doing.Please someone help me understand as I am new to salesforce.
trigger DataLoggingTrigger on QMBDataLogging__c (after insert) {
Map<string,Schema.RecordTypeInfo> recordTypeInfo = Schema.SObjectType.QMB_Initial_Letter__c.getRecordTypeInfosByName();
List<QMBDataLogging__c> logList = (List<QMBDataLogging__c>)Trigger.new;
List<Sobject> sobjList = (List<Sobject>)Type.forName('List<'+'QMB_Initial_Letter__c'+'>').newInstance();
Map<string, QMBLetteTypeToVfPage__c> QMBLetteTypeToVfPage = QMBLetteTypeToVfPage__c.getAll();
Map<String,QMBLetteTypeToVfPage__c> mapofLetterTypeRec = new Map<String,QMBLetteTypeToVfPage__c>();
set<Id>processdIds = new set<Id>();
for(string key : QMBLetteTypeToVfPage.keyset())
{
if(!mapofLetterTypeRec.containsKey(key)) mapofLetterTypeRec.put(QMBLetteTypeToVfPage.get(Key).Letter_Type__c, QMBLetteTypeToVfPage.get(Key));
}
for(QMBDataLogging__c log : logList)
{
Sobject logRecord = (sobject)log;
Sobject QMBLetterRecord = new QMB_Initial_Letter__c();
if(mapofLetterTypeRec.containskey(log.Field1__c))
{
string recordTypeId = recordTypeInfo.get(mapofLetterTypeRec.get(log.Field1__c).RecordType__c).isAvailable() ? recordTypeInfo.get(mapofLetterTypeRec.get(log.Field1__c).RecordType__c).getRecordTypeId() : recordTypeInfo.get('Master').getRecordTypeId();
string fieldApiNames = mapofLetterTypeRec.containskey(log.Field1__c) ? mapofLetterTypeRec.get(log.Field1__c).FieldAPINames__c : '';
//QMBLetterRecord.put('Letter_Type__c',log.Name);
QMBLetterRecord.put('RecordTypeId',tgh);
processdIds.add(log.Id);
if(string.isNotBlank(fieldApiNames) && fieldApiNames.contains(','))
{
Integer i = 1;
for(string fieldApiName : fieldApiNames.split(','))
{
string logFieldApiName = 'Field'+i+'__c';
fieldApiName = fieldApiName.trim();
system.debug('fieldApiName=='+fieldApiName);
Schema.DisplayType fielddataType = getFieldType('QMB_Initial_Letter__c',fieldApiName);
if(fielddataType == Schema.DisplayType.Date)
{
Date dateValue = Date.parse(string.valueof(logRecord.get(logFieldApiName)));
QMBLetterRecord.put(fieldApiName,dateValue);
}
else if(fielddataType == Schema.DisplayType.DOUBLE)
{
string value = (string)logRecord.get(logFieldApiName);
Double dec = Double.valueOf(value.replace(',',''));
QMBLetterRecord.put(fieldApiName,dec);
}
else if(fielddataType == Schema.DisplayType.CURRENCY)
{
Decimal decimalValue = Decimal.valueOf((string)logRecord.get(logFieldApiName));
QMBLetterRecord.put(fieldApiName,decimalValue);
}
else if(fielddataType == Schema.DisplayType.INTEGER)
{
string value = (string)logRecord.get(logFieldApiName);
Integer integerValue = Integer.valueOf(value.replace(',',''));
QMBLetterRecord.put(fieldApiName,integerValue);
}
else if(fielddataType == Schema.DisplayType.DATETIME)
{
DateTime dateTimeValue = DateTime.valueOf(logRecord.get(logFieldApiName));
QMBLetterRecord.put(fieldApiName,dateTimeValue);
}
else
{
QMBLetterRecord.put(fieldApiName,logRecord.get(logFieldApiName));
}
i++;
}
}
}
sobjList.add(QMBLetterRecord);
}
if(!sobjList.isEmpty())
{
insert sobjList;
if(!processdIds.isEmpty()) DeleteDoAsLoggingRecords.deleteTheProcessRecords(processdIds);
}
Public static Schema.DisplayType getFieldType(string objectName,string fieldName)
{
SObjectType r = ((SObject)(Type.forName('Schema.'+objectName).newInstance())).getSObjectType();
DescribeSObjectResult d = r.getDescribe();
return(d.fields.getMap().get(fieldName).getDescribe().getType());
}
}
You might be looking in the wrong place. Check if there's an unit test written for this thing (there should be one, especially if it's deployed to production), it should help you understand how it's supposed to be used.
You're inserting records of QMBDataLogging__c but then it seems they're immediately deleted in DeleteDoAsLoggingRecords.deleteTheProcessRecords(processdIds). Whether whatever this thing was supposed to do succeeds or not.
This seems to be some poor man's CSV parser or generic "upload anything"... that takes data stored in QMBDataLogging__c and creates QMB_Initial_Letter__c out of it.
QMBLetteTypeToVfPage__c.getAll() suggests you could go to Setup -> Custom Settings, try to find this thing and examine. Maybe it has some values in production but in your sandbox it's empty and that's why essentially nothing works? Or maybe some values that are there are outdated?
There's some comparison if what you upload into Field1__c can be matched to what's in that custom setting. I guess you load some kind of subtype of your QMB_Initial_Letter__c in there. Record Type name and list of fields to read from your log record is also fetched from custom setting based on that match.
Then this thing takes what you pasted, looks at the list of fields in from the custom setting and parses it.
Let's say the custom setting contains something like
Name = XYZ, FieldAPINames__c = 'Name,SomePicklist__c,SomeDate__c,IsActive__c'
This thing will look at first record you inserted, let's say you have the CSV like that
Field1__c,Field2__c,Field3__c,Field4__c
XYZ,Closed,2022-09-15,true
This thing will try to parse and map it so eventually you create record that a "normal" apex code would express as
new QMB_Initial_Letter__c(
Name = 'XYZ',
SomePicklist__c = 'Closed',
SomeDate__c = Date.parse('2022-09-15'),
IsActive__c = true
);
It's pretty fragile, as you probably already know. And because parsing CSV is an art - I expect it to absolutely crash and burn when text with commas in it shows up (some text,"text, with commas in it, should be quoted",more text).
In theory admin can change mapping in setup - but then they'd need to add new field anyway to the loaded file. Overcomplicated. I guess somebody did it to solve issue with Record Type Ids - but there are better ways to achieve that and still have normal CSV file with normal columns and strong type matching, not just chucking everything in as strings.
In theory this lets you have "jagged" csv files (row 1 having 5 fields, row 2 having different record type and 17 fields? no problem)
Your call whether it's salvageable or you'd rather ditch it and try normal loading of QMB_Initial_Letter__c records. (get back to your business people and ask for requirements?) If you do have variable number of columns at source - you'd need to standardise it or group the data so only 1 "type" of records (well, whatever's in that "Field1__c") goes into each file.

How to determine if something was copied or cut to the clipboard

in my #execute method I am able to get the selection out of the clipboard / LocalSelectionTransfer. But I have no idea how to react on that based on how the user has put the content to the clipboard.
I have to decide whether I duplicate or not the content.
This is what I have:
#Execute
public void execute(#Named(IServiceConstants.ACTIVE_SHELL) Shell shell, #Named(IServiceConstants.ACTIVE_PART) MPart activePart) {
Clipboard clipboard = new Clipboard(shell.getDisplay());
TransferData[] transferDatas = clipboard.getAvailableTypes();
boolean weCanUseIt= false;
for(int i=0; i<transferDatas.length; i++) {
if(LocalSelectionTransfer.getTransfer().isSupportedType(transferDatas[i])) {
weCanUseIt = true;
break;
}
}
if (weCanUseIt) {
#SuppressWarnings("unchecked")
List<Object> objects = ((StructuredSelection)LocalSelectionTransfer.getTransfer().getSelection()).toList();
for(Object o: objects) {
System.out.println(o.getClass());
}
}
}
any Ideas???
You only get something in the clipboard using LocalSelectionTransfer if you code a part in your RCP to use this transfer type for a Copy operation. It provides a way to transfer the selection directly.
This transfer type will not be used if something is copied to the clipboard any other way (in this case it might be something like TextTransfer or FileTransfer).
So you will only be using LocalSelectionTransfer to deal with a selection from another part in which case you presumably know how to deal with the objects.
If you are trying to do Copy and Cut then you should do the Cut in the source viewer - but this will remove the selection so you can't use LocalSelectionTransfer for that. Use a transfer such as FileTransfer or TextTransfer which doesn't rely on the current selection.

Inserting a cross-reference with Aspose Words

Does anybody know if it is possible to insert a cross reference (I want to reference a bookmark, but I could make anything else work as well), using Aspose Words, in C#?
If you want to insert a footnote or endnote, you can use the following code.
DocumentBuilder builder = new DocumentBuilder(doc);
builder.Write("Some text is added.");
Footnote endNote = new Footnote(doc, FootnoteType.Endnote);
builder.CurrentParagraph.AppendChild(endNote);
endNote.Paragraphs.Add(new Paragraph(doc));
endNote.FirstParagraph.Runs.Add(new Run(doc, "Endnote text."));
doc.Save(MyDir + #"FootNote.docx");
If you want to insert a bookmark, you can use the following code.
Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);
builder.StartBookmark("MyBookmark");
builder.Writeln("Text inside a bookmark.");
builder.EndBookmark("MyBookmark")
If you want to update a bookmark, you can use the following code.
Document doc = new Document(MyDir + "Bookmark.doc");
// Use the indexer of the Bookmarks collection to obtain the desired bookmark.
Bookmark bookmark = doc.Range.Bookmarks["MyBookmark"];
// Get the name and text of the bookmark.
string name = bookmark.Name;
string text = bookmark.Text;
// Set the name and text of the bookmark.
bookmark.Name = "RenamedBookmark";
bookmark.Text = "This is a new bookmarked text.";
I work as developer evangelist at Aspose.
You can use the following code to get the page number of a bookmark.
Document doc = new Document("Bookmark.docx");
Aspose.Words.Layout.LayoutCollector layoutCollector = new Aspose.Words.Layout.LayoutCollector(doc);
// Use the indexer of the Bookmarks collection to obtain the desired bookmark.
Bookmark bookmark = doc.Range.Bookmarks["MyBookmark"];
// Get the name and text of the bookmark.
string name = bookmark.Name;
string text = bookmark.Text;
int pageNumber = layoutCollector.GetStartPageIndex(bookmark.BookmarkStart);
Console.Write("Bookmark name is {0}, it is placed on page number {1} and following is the text inside it: {2}", name, pageNumber, text);

What does this string of code: gr.sys_id[key] = current.getValue(glideElement.getName());

I'm trying to copy (duplicate) a record in ServiceNow table of incidents, but can not make this string work: gr.sys_id[key] = current.getValue(glideElement.getName());
The goal is to copy all fields values except sys_id.
Take a look at the UI Action Insert & Stay which is kind of a Duplicate Script.
You can use the same functionality in your Business rule or any other server side script:
doInsertAndStay();
function doInsertAndStay() {
var saveMe = current;
if (typeof current.number != 'undefined' && current.number){
current.number = ""; // generate a new number
}
current.insert();
action.setRedirectURL(saveMe);
}
The GlideRecord function insert() duplicates a record and of course a new sys_id is used for the new record. As far as I know you are not able to define the sys_id by your self.

How Do I detect Text and Cursor position changes in Word using VSTO

I want to write a word addin that does some computations and updates some ui whenever the user types something or moves the current insertion point. From looking at the MSDN docs, I don't see any obvious way such as an TextTyped event on the document or application objects.
Does anyone know if this is possible without polling the document?
Actually there is a way to run some code when a word has been typed, you can use SmartTags, and override the Recognize method, this method will be called whenever a word is type, which means whenever the user typed some text and hit the space, tab, or enter keys.
one problem with this however is that if you change the text using "Range.Text" it will detect it as a word change and call the function so it can cause infinite loops.
Here is some code I used to achieve this:
public class AutoBrandSmartTag : SmartTag
{
Microsoft.Office.Interop.Word.Document cDoc;
Microsoft.Office.Tools.Word.Action act = new Microsoft.Office.Tools.Word.Action("Test Action");
public AutoBrandSmartTag(AutoBrandEngine.AutoBrandEngine _engine, Microsoft.Office.Interop.Word.Document _doc)
: base("AutoBrandTool.com/SmartTag#AutoBrandSmartTag", "AutoBrand SmartTag")
{
this.cDoc = _doc;
this.Actions = new Microsoft.Office.Tools.Word.Action[] { act };
}
protected override void Recognize(string text, Microsoft.Office.Interop.SmartTag.ISmartTagRecognizerSite site,
Microsoft.Office.Interop.SmartTag.ISmartTagTokenList tokenList)
{
if (tokenList.Count < 1)
return;
int start = 0;
int length = 0;
int index = tokenList.Count > 1 ? tokenList.Count - 1 : 1;
ISmartTagToken token = tokenList.get_Item(index);
start = token.Start;
length = token.Length;
}
}
As you've probably discovered, Word has events, but they're for really coarse actions like a document open or a switch to another document. I'm guessing MS did this intentionally to prevent a crappy macro from slowing down typing.
In short, there's no great way to do what you want. A Word MVP confirms that in this thread.