document.contentControls not returning all ContentControls in the document - ms-word

I have a document with three ContentControl objects that looks like this:
Here is the .docx file in its entirety - but essentially the markup of the document body looks like this:
<w:body>
<w:p w:rsidR="0075044D" w:rsidRDefault="0075044D">
<w:r>
<w:t xml:space="preserve">Video provides a powerful way to help you </w:t>
</w:r>
<w:sdt>
<w:sdtPr>
<w:alias w:val="cc1"/>
<w:tag w:val="prove"/>
<w:id w:val="806369342"/>
<w:placeholder>
<w:docPart w:val="1F3FDE3D075A4E8AADE251C4E318E379"/>
</w:placeholder>
<w15:color w:val="FF9900"/>
<w15:appearance w15:val="tags"/>
<w:text/>
</w:sdtPr>
<w:sdtContent>
<w:r>
<w:t>prove</w:t>
</w:r>
</w:sdtContent>
</w:sdt>
<w:r>
<w:t xml:space="preserve"> your point. When you click Online Video, you can paste in the embed code for the video you want to add. You can also </w:t>
</w:r>
<w:sdt>
<w:sdtPr>
<w:alias w:val="cc2"/>
<w:tag w:val="number 2"/>
<w:id w:val="1463999480"/>
<w:placeholder>
<w:docPart w:val="1F3FDE3D075A4E8AADE251C4E318E379"/>
</w:placeholder>
<w15:color w:val="FF0000"/>
<w15:appearance w15:val="tags"/>
</w:sdtPr>
<w:sdtContent>
<w:r>
<w:t>type</w:t>
</w:r>
</w:sdtContent>
</w:sdt>
<w:r>
<w:t xml:space="preserve"> a keyword to search online for the video that best fits your document.</w:t>
</w:r>
</w:p>
<w:p w:rsidR="0075044D" w:rsidRDefault="0075044D">
<w:r>
<w:t xml:space="preserve">To make your document look professionally produced, Word provides </w:t>
</w:r>
<w:sdt>
<w:sdtPr>
<w:alias w:val="cc3"/>
<w:tag w:val="xxx"/>
<w:id w:val="1703202634"/>
<w:placeholder>
<w:docPart w:val="1F3FDE3D075A4E8AADE251C4E318E379"/>
</w:placeholder>
<w15:color w:val="FF99CC"/>
<w15:appearance w15:val="tags"/>
<w:text/>
</w:sdtPr>
<w:sdtContent>
<w:r>
<w:t>header</w:t>
</w:r>
</w:sdtContent>
</w:sdt>
<w:r>
<w:t>, footer, cover page, and text box designs that complement each other. For example, you can add a matching cover page, header, and sidebar. Click Insert and then choose the elements you want from the different galleries.</w:t>
</w:r>
</w:p>
<w:p w:rsidR="0075044D" w:rsidRDefault="0075044D"/>
<w:p w:rsidR="00000000" w:rsidRDefault="0075044D"/>
<w:sectPr w:rsidR="00000000">
<w:pgSz w:w="11906" w:h="16838"/>
<w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="708" w:footer="708" w:gutter="0"/>
<w:cols w:space="708"/>
<w:docGrid w:linePitch="360"/>
</w:sectPr>
</w:body>
When I run the code below, only the control in the middle is returned by document.contentControls and therefore changed by my code. Any ideas why the other two controls are not returned? Did anyone else encounter this issue? Is there a way to fix it?
Word.run(function (context) {
var myContentControls = context.document.contentControls;
myContentControls.load("tag");
return context.sync()
.then(function () {
for (var i = 0; i < myContentControls.items.length; i++)
{
myContentControls.items[i].color = "blue";
myContentControls.items[i].title = "myCC";
myContentControls.items[i].appearance = "tags";
}
return context.sync();
});
}).catch(OfficeHelpers.Utilities.log);
Here is a ScriptLab gist for convenience.
Interestingly, this VBA code returns the correct result (3):
Sub Main()
MsgBox ActiveDocument.ContentControls.Count
End Sub
I have only tried this on Windows 10 / Office 365 desktop client.

As stated in the Word JS APIs' documentation for content controls, only Rich Text content controls are supported / recognized. So code will not "see" plain text content controls.
Whether a content control is plain text or rich text is not visually recognizable (unless it contains formatting or content other than text). There's also nothing in the Word Open XML to differentiate the type of content control, unless it contains formatting or a non-text object.

As Cindy Meister already said, the Word OfficeJS API only returns RichText content controls - see Word.ContentControl.
If you dive into the OOXML, you'll find that plain text context controls (the ones that are not returned) main difference from the rich text ones is that they are marked with <w:text/>. This essentially means that if that tag is removed, then the plain text content control becomes a rich text content control. So here is my workaround to this issue:
var ooxml = context.document.body.getOoxml();
await context.sync();
var newxml = ooxml.value.replace(/<w:text\/>/g, '');
context.document.body.insertOoxml(newxml, Word.InsertLocation.replace);
var myContentControls = context.document.contentControls;
myContentControls.load();
await context.sync();
console.log(myContentControls.items.length); // now returns 3. Yay!!!
return context.sync();
Nota Bene: getOoxml() and insertOoxml() are slow methods and this workaround will take a long time to execute, so only use when strictly necessary.

Related

How to get text ranges based on fonts

I have a paragraph that has multiple fonts applied. There are a number of ranges because of that. Is there a way (without working with the OOXML directly) to get these ranges, their fonts, and their text? Here is an example OOXML snippet I'm talking about:
<w:p w:rsidR="00301FAD" w:rsidRDefault="00301FAD">
<w:r w:rsidRPr="001D4040">
<w:rPr>
<w:b/>
<w:bCs/>
</w:rPr>
<w:t>Spam</w:t>
</w:r>
<w:r>
<w:t xml:space="preserve"/>
</w:r>
<w:r w:rsidRPr="001D4040">
<w:rPr>
<w:b/>
<w:bCs/>
<w:i/>
<w:iCs/>
</w:rPr>
<w:t>and</w:t>
</w:r>
<w:r>
<w:t xml:space="preserve"/>
</w:r>
<w:r w:rsidRPr="001D4040">
<w:rPr>
<w:i/>
<w:iCs/>
</w:rPr>
<w:t>eggs</w:t>
</w:r>
</w:p>
The paragraph text in Word looks like this:
Spam and eggs
You can use the paragraph class' split method which can split the paragraph into ranges based on a string. I used this function to print the font and the text of the example provided:
async function run() {
await Word.run(async (context) => {
const body = context.document.body;
var par = body.paragraphs.getFirst();
let words = par.split([" "]);
let first_word = words.getFirst();
first_word.load(["font", "text"]);
await context.sync();
console.log(f_word.font);
console.log(f_word.text);
});
}
One downside to this is that you can't differentiate between a word that has fonts like this: Spam, or like this: Spam since it will set the bold property to null both times.

Does the w:style with the w:type="table" and w:default="1" attribute automatically apply to a w:tbl without a w:tblStyle?

I found a table has a padding sytle, but its w:tbl does not have a w:tblStyle. Does the w:style which has the w:type="table" and w:default="1" attributes have an effect in this case?
The background is that I'm using XSLT to transform the Open XML markup.
The short answer to your question is "Yes"; the default style will apply if no style is explicitly assigned.
There are four different types of styles in WordprocessingML, i.e., paragraph, character, table, and numbering styles. For each type (e.g., table), one style is marked as the default style for related Open XML elements (e.g., w:tbl elements) by having an attribute w:default="1". The typical default styles are shown below:
<w:style w:type="paragraph" w:default="1" w:styleId="Normal">
<w:name w:val="Normal"/>
<w:qFormat/>
</w:style>
<w:style w:type="character" w:default="1" w:styleId="DefaultParagraphFont">
<w:name w:val="Default Paragraph Font"/>
<w:uiPriority w:val="1"/>
<w:semiHidden/>
<w:unhideWhenUsed/>
</w:style>
<w:style w:type="table" w:default="1" w:styleId="TableNormal">
<w:name w:val="Normal Table"/>
<w:uiPriority w:val="99"/>
<w:semiHidden/>
<w:unhideWhenUsed/>
<w:tblPr>
<w:tblInd w:w="0" w:type="dxa"/>
<w:tblCellMar>
<w:top w:w="0" w:type="dxa"/>
<w:left w:w="108" w:type="dxa"/>
<w:bottom w:w="0" w:type="dxa"/>
<w:right w:w="108" w:type="dxa"/>
</w:tblCellMar>
</w:tblPr>
</w:style>
<w:style w:type="numbering" w:default="1" w:styleId="NoList">
<w:name w:val="No List"/>
<w:uiPriority w:val="99"/>
<w:semiHidden/>
<w:unhideWhenUsed/>
</w:style>
For example, if you have w:p (paragraph), w:r (run), or w:tbl (table) elements without an explicit style being assigned to them (e.g., using w:pStyle, w:rStyle, or w:tblStyle), the default styles for those types of elements will apply.

Image is not displayed sapui5

I am displaying images in my page so i am using Image control
<m:Image src="{path: 'MyModel>Link', formatter:'.formatter.imageFormatter' }" visible= "true" alt="{i18n>picname}"></m:Image>
and the formatter
imageFormatter: function(val) {
return val;
},
when i see the network in the debbuger the image is loaded perfectlly with status 200 and when i see preview it is an empty image But in My page nothing is displayed (When i open the image in other tab when i go back to my app all images are displayed )
I Don't know the problem
Set the "densityAware" property to false in the sap.m.Image control and try again
Simon.
Set a value in the Column Header - e.g.: (please namespace correctly - you need to prefix with m:)
<Column
hAlign="Center"
visible="true"
width="145px">
<Text text="Image"/>
</Column>
Not doing so may be having consequences you are not anticipating.
Also, to me, your cells collection seems incorrect - can you try:
<m:items>
<m:ColumnListItem type="Navigation" detailPress="onEdit">
<m:cells>
<m:Image src="{path: 'MyModel>Link', formatter:'.formatter.imageFormatter' }" visible="true" alt="{i18n>picname} />
</m:cells>
</m:ColumnListItem>
</m:items>

How can I add sort items into a fragment dialog?

I have this fragment retrieve by this page https://openui5.hana.ondemand.com/explored.html#/sample/sap.m.sample.TableViewSettingsDialog/code
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core">
<ViewSettingsDialog
confirm="handleConfirm" id='viewSettingsDialogId'>
<sortItems id="sortItemsId">
<!-- <ViewSettingsItem text="Product" key="Name" selected="true" />
<ViewSettingsItem text="Supplier" key="SupplierName" />
<ViewSettingsItem text="Weight" key="WeightMeasure" />
<ViewSettingsItem text="Price" key="Price" /> -->
</sortItems>
</ViewSettingsDialog>
</core:FragmentDefinition>
I want insert manually the sortItems in the control (or by data-binding in the xml-View).
How can I do it?
I try to do it by code in my controller:
//IF CLICK ON SETTINGS BUTTON
handleViewSettingsDialogButtonPressed: function (oEvent) {
if (!this._oDialog) {
this._oDialog = sap.ui.xmlfragment("apps.appIntra.fragment.settingDialog", this);
}
// toggle compact style
jQuery.sap.syncStyleClass("sapUiSizeCompact", this.getView(), this._oDialog);
this._oDialog.open();
var element=sap.ui.getCore().byId("sortItemsId");
this.byId('sortItemsId').addSortItem(new sap.m.ViewSettingsItem({text:"field1", key:"Price"}));
this.byId('sortItemsId').addSortItem(new sap.m.ViewSettingsItem({text:"field2", key:"PLUTO"}));
},
But it not work...
I see this guide http://scn.sap.com/community/developer-center/front-end/blog/2014/02/20/sapui5-dialogwith-businesscard-as-xml-fragment-along-with-controller
but if I use
var element=sap.ui.getCore().byId("sortItemsId");
element value is undefined
The addSortItem is a method that works on the ViewSettingsDialog and not on sortItems.
Therefore, as you have already provided the id viewSettingsDialogId for the ViewSettingsDialog control, in you controller you can do the following,
var oViewSettingsDialog = sap.ui.getCore().byId("viewSettingsDialogId");
oViewSettingsDialog.addSortItem(new sap.m.ViewSettingsItem({text:"field1",
key:"Price"}));
/* and so on... */
This would add the sort items into the sortItems list.

Unpredictible results with buttons (better their eventhandlers) inside conditional rendered elements (i. e. panels)

I am trying to hide/show a panel based on a condition like this
<xp:panel id="panelUsersInput">
<!--<xp:this.rendered><![CDATA[#{javascript:return ((getComponent("optAttendees").getAttributes().get("value")=="defined"));}]]></xp:this.rendered>-->
<xp:inputText id="namUsersInput">
<xp:typeAhead mode="partial" minChars="3" ignoreCase="true" var="lupUsersInput">
<xp:this.valueList><![CDATA[#{javascript:#DbLookup( [ database.getServer(), 'names.nsf' ], '($VIMPeople)', lupUsersInput, 1, '[PARTIALMATCH]' );}]]></xp:this.valueList>
</xp:typeAhead>
</xp:inputText>
<xp:button value="" id="btnAddUser" styleClass="button add clear" title="add attendant">
<xp:span/>
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="panelUsersList" execMode="partial" execId="panelSelectionAttendees">
<xp:this.onStart><![CDATA[(dojo.byId("#{id:namUsersInput}").value!="");]]></xp:this.onStart>
<xp:this.action>
<![CDATA[#{javascript:
var lstUsers = getComponent("namUsers").getAttributes().get("value");
if(typeof(lstUsers)==typeof(java.util.Vector)) {
lstUsers.push(getComponent("namUsersInput").getAttributes().get("value"));
lstUsers.sort();
} else {
lstUsers = #Trim(#List(lstUsers, getComponent("namUsersInput").getAttributes().get("value")));
}
getComponent("namUsersInput").getAttributes().put("value", "");
getComponent("namUsers").getAttributes().put("value", lstUsers);
}]]>
</xp:this.action>
<xp:this.onComplete><![CDATA[if(dojo.byId("#{id:rpUsersSelection}")) { highlightSection("#{id:rpUsersSelection}"); }]]></xp:this.onComplete>
</xp:eventHandler>
</xp:button>
<div class="clearAll"/>
</xp:panel>
As you see I have disabled the rendered option for the panel. With this setting the button inside this panel is working as it is told to.
When enabling the rendered option for the panel, the button inside this panel is doing something, but the result returned by the partial refresh is like reloading the specified section (with the partial id).
Much more interesting is this fact: when the panels rendering condition is false, the event triggered by the button still is rendered. But the eventhandler cannot find it's parent element to bind to. The result is a global binding of this event to the whole page (body-element).
XSP.addOnLoad(function() {
...
XSP.attachPartial("view:_id1:_id11:_id47", "view:_id1:_id11:btnAddUser", "view:_id1:_id11:panelSelectionAttendees", "onclick", view__id1__id11__id47_clientSide_onclick, 2, "view:_id1:_id11:panelUsersList", null, "if(dojo.byId(\"view:_id1:_id11:namUsers\").value!=\"\") { dojo.byId(\"view:_id1:_id11:namUsersInput\").value=\"\"; highlightSection(\"view:_id1:_id11:panelUsersList\"); }", null);
...
});
Has someone else noticed such a behaviour? I thought that hiding a panel will also hide all underlying elements inside this panel.
Seems to be a known bug
LO73179: XPAGES EVENT HANDLER RENDERING DOES NOT MATCH RENDERING OF PARENT COMPONENT
http://www-01.ibm.com/support/docview.wss?uid=swg1LO73179.