Replacing Field values in Word Document QAxObject QT / C++ - c++

I'm really new to QT and I was tasked to update some field values in word document programmatically, currently I can replace text from word document fine, but when that field value is inside an object (table or anything) it's not working, my code is:
QString outFile("D:\\#test files\\output.docx");
QString inFile1("D:\\#test files\\input.docx");
QAxObject axObject("Word.Application");
QAxObject* documents = axObject.querySubObject("Documents");
QAxObject* document = documents->querySubObject("Open(const QString&, bool)", inFile1, true);
QAxObject* selection = axObject.querySubObject("Selection");
auto find = selection->querySubObject("Find");
QString sOld = "${name}";
QString sNew = "Ibrahim";
bool bMatchCase = false;
bool bMatchWholeWord = false;
bool bMatchWildCards = false;
bool bReplaceAll = true;
QVariantList vl = { sOld, bMatchCase, bMatchWholeWord, bMatchWildCards, false, false, true, 1, false, sNew, bReplaceAll ? "2" : "1" };
find->dynamicCall("Execute(QString,bool,bool,bool,bool,bool,bool,int,bool,QString,int)", vl);
document->dynamicCall("SaveAs(const QString&)", outFile);
document->dynamicCall("Close()");
axObject.dynamicCall("Quit()");
If you can help it would be awesome :)

If you can change the nature of the target files, you would do well replacing your targets with real Word DocVariable or DocProperty fields. Then use your code to change the variable or properties and update the related fields in the documents. Some document properties (under the Quick Parts > Document Properties menu) are mapped to XML data points and do not require updating of fields if those are used.
The placeholder can be (1) a DocVariable field or (2) a DocProperty field. You can change the variables or properties using code and then update the field.
You can also use one of the built-in mapped Document Property Content Controls in which case there is no need to update a field if the property is changed. It is automatic. More on these in my related page: Repeating Data Mapped Document Property Content Controls or Other Mapped Content Controls.
Here are links to two Word MVP pages on accessing Document Properties using vba.
How Can I Get Access to the Document Properties of a Document Without Opening the Document
How to Use a Single vba Procedure to Read or Write Both Built-In and Custom Document Properties

Related

Sitecore get all parent (ancestor) with little processing (performance)

I'm trying to come up with solution on getting all the ancestors for the context item. One option is to store _path in index and other option is to do similar to one below:
http://www.glass.lu/Blog/GettingAncestors
I'm having no luck with getting the solution work for above (glass mapper) solution.
I got the index solution working but would like to avoid using index just to get the _path (collection of ancestors) as we don't have any other requirements to use index e.g. search etc.
Appreciate if someone could share the snippet for working solution or even better if Glassmapper has already included the above blog solution.
The most efficient way to check if one item is a descendant of another is to simply check that the current item property Paths.LongID starts with the LongID of the parent item:
Item currentItem = Sitecore.Context.Item;
IList<Item> menuItems = GetMenuItems();
foreach (var menuItem in menuItems)
{
bool isActive = currentItem.Paths.LongID.StartsWith(menuItem.Paths.LongID);
// do code
}
This will work since the path GUIDs are unique for each item.
Alternatively, if you want to use Glass models only then you can use the SitecoreInfoType.FullPath attribute:
[SitecoreInfo(SitecoreInfoType.FullPath)]
public virtual string FullPath { get; private set; }
And then from your code you can simply check:
Item currentItem = Sitecore.Context.Item; //or use SitecoreContext() to get a strongly types model
IEnumerable<MenuItem> menuItems = GetMenuItems();
foreach (var menuItem in menuItems)
{
bool isActive = currentItem.Paths.FullPath.StartsWith(menuItem.FullPath);
// do code
}
Just a word of warning, since each menu item now need code to run in order to determine state, this will make your menu component difficult to cache, resulting caching too many variations or caching per page. You would be better to move this logic into Javascript to set the menu state using the current page URL, this will allow your component to be cached once for all pages.

sitecore programmatically change imagefield datasource

I need image field source of a product item to change depending on the folder path I am creating a product item under when creating the item in content or page editor.
If I am creating a product under /home/bicycles, I need the product item image field to auto change to /sitecore/media library/Images/bicycles
If I am creating a product under /home/cars, I need the product item image field to auto change to /sitecore/media library/Images/cars
If I am creating a product under /home/scooters, I need the product item image field to auto change to /sitecore/media library/Images/scooters
The default setting for that image field source in the datatemplate is /sitecore/media library/Images/bicycles
How can I go about doing this?
One option is to create a custom Content Editor field that extends the default image field type.
Start by creating a class that inherits from Sitecore.Shell.Applications.ContentEditor.Image. Then override the OnPreRender method to determine and set the Source property of the image field based on your location criteria/requirements.
See comments in the code below for more information.
public class ContextAwareImageField : Sitecore.Shell.Applications.ContentEditor.Image
{
/// <summary>
/// The ItemID proprety is set by the Content Editor via reflection
/// </summary>
public string ItemID { get; set; }
/// <summary>
/// Override the OnPreRender method.
/// The base OnPreRender method assigns a value to the Source viewstate property and we need to overwrite it.
/// </summary>
/// <param name="e"></param>
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
Source = GetSource();
}
protected virtual string GetSource()
{
//retrieve and return the computed source value if it has already been set
var contextSource = GetViewStateString("ContextSource");
if (!string.IsNullOrWhiteSpace(contextSource))
return contextSource;
//retrieve the context item (the item containing the image field)
var contextItem = Sitecore.Context.ContentDatabase.GetItem(ItemID);
if (contextItem == null)
return string.Empty;
//determine the source to be used by the media browser
//in this case we're just checking based on parent path, but any logic could be inserted
contextSource = "/sitecore/media library/Images";
switch (contextItem.Parent.Paths.FullPath.ToLowerInvariant())
{
case "/sitecore/content/home/bicycles":
contextSource = "/sitecore/media library/Images/Bicycles";
break;
case "/sitecore/content/home/cars":
contextSource = "/sitecore/media library/Images/Cars";
break;
case "/sitecore/content/home/scooters":
contextSource = "/sitecore/media library/Images/Scooters";
break;
}
//store the computed source value in view bag for later retrieval
SetViewStateString("ContextSource", contextSource);
//return the computed source value
return contextSource;
}
}
Next, perform the following steps:
Login to the Sitecore desktop with administrator rights and use the database icon in the lower right to switch to the Core database.
In the Core database, open the Content Editor then navigate to /sitecore/system/Field Types/Simple Types. There you will find an item representing the Image field type.
Duplicate the Image field type item and rename the duplicated item to something relevant (e.g. Context Aware Image).
Edit the duplicated item
In the Assembly field, provide the name of the assembly file containing your custom image field class (e.g. MyClient.MySite.dll)
In the Class field, provide the name of the custom image field class, including the namespace (e.g. MyClient.MySite.CustomFields.ContextAwareImageField)
Delete the value in the Control field
Save your changes
Switch back to the Master database.
Open the Content Editor, then navigate to a template that should contain your new image field.
Either create a new field in the template and choose your new custom field type in the Type dropdown. Or, change the Type for an existing image field.
Save your template changes.
In the content tree, navigate to an item based on the template above and click the Browse button for the contained image field. The media browser dialog should default to the source location specified by the logic in your custom field.
Note: if you are using a version of Sitecore that contains a SPEAK-based media browser dialog, you will have to switch to Tree View in the dialog (icon in the upper right) in order to see the source location specified by your custom field.

QT tree that allows multiselection

I'm making a simple file explorer and I ran into some problems with Qt. I want to show the user a tree view of files on his computer, but I also want to be able to select multiple files/directories and do something with them later on (by selecting checkboxes or multiple select using ctrl+left click or shift+left click). I've placed the QTreeView element and set up a model to it (QFileSystemModel). It gives me a good tree view, but I can't modify the headers (column names) or add my own column with checkbox in every row (for example). Qt is new to me, I've searched for few good hours for some tips/solutions, but nothing is working with QFileSystemModel. Is there anything I can do to get this working?
The code is short and simple:
QString lPath = "C:/";
QString rPath = "C:/";
leftTree_model = new QFileSystemModel(this);
rightTree_model = new QFileSystemModel(this);
leftTree_model->setRootPath(lPath);
rightTree_model->setRootPath(rPath);
//i have actually 2 tree views that work the same
ui->leftTree->setModel(leftTree_model); //ui->leftTree is the first tree view
ui->rightTree->setModel(rightTree_model); //the second
Use something of the following:
CheckStateRole to add checkboxes to your model. To do this, you inherit your custom item model (which you're going to use) from the QFileSystemModel, and reimplement the data() method, where you return bool values for CheckStateRole. You will also need the QAbstractItemModel::setData method to handle changes. You can also check the docs for QAbstractItemModel to see how to change header texts (headerData())
Change the selection mode of your view to allow multiple selections
EDIT:
here's a sample code to inherit from the model
class MyFancyModel : public QFileSystemModel
{
public:
MyFancyModel(QObject* pParent = NULL) : QFileSystemModel(pParent)
{
}
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole ) const
{
if (role == Qt::CheckStateRole)
{
// stub value is true
return true; // here you will return real values
// depending on which item is currently checked
}
return QFileSystemModel::data(index, role);
}
};

Sitecore: multilist during deployment

How to enable the multilist to be control in content editor?
for example I have a list of item, item1 to item10. In the standard template value, I defined item1,2,3. After I have deploy the solution, how am I going to enable users in content editor mode or page editor mode to select item7,8,9 and 10?
And also, after I tested/rendered the multilist, only RAW VALUES are being rendered, is there any possible to render the item name such as item1? Do I need to customize the multilist?
The multilist control should be directly visible to the user in the Content Editor, you do not need to do anything else. Since you defined some items in standard values then those will be "pre-selected" when that item is first created. The user can then add the additional items as required.
To allow users to select values from the Page Editor you can Use Sitecore EditFrame in PageEdit
The reason the item is being rendered as the raw value is because you need to get the item and then iterate over the target id's. There is an example of this here here
//Get a multilist field from the current item
Sitecore.Data.Fields.MultilistField multilistField = Sitecore.Context.Item.Fields["myMultilistField"];
if (multilistField != null)
{
//Iterate over all the selected items by using the property TargetIDs
foreach (ID id in multilistField.TargetIDs)
{
Item targetItem = Sitecore.Context.Database.Items[id];
litItemTitle = targetItem.DisplayName;
// Do something with the target items
// ...
}
}
You can use the following instead for the datasource of a repeater
Sitecore.Data.Fields.MultilistField multilistField = Sitecore.Context.Item.Fields["myMultilistField"];
Sitecore.Data.Items.Item[] items = multilistField.GetItems();

How to render referenced items from current item in Sitecore?

Let say I have item which has its presentation details configured.
In that item I have TreelistEx field keeping reference (GUIDs) to 10+ other items (different templates) somewhere in the tree structure each of them has their own presentation details (sublayouts).
How can I present all reference items on the same page ( as current item) based on their own settings?
I would like to see one page and 10+ pieces of content each with its own layout presentation.
I had a similar problem. Here is the code for rendering sublayouts and xsl renderings:
public IEnumerable<Control> GetRenderingControls(Item item)
{
// Loop through all renderings on the item
foreach (RenderingReference rendering in item.Visualization.GetRenderings(Context.Device, false))
{
// Get the path to the Sublayout
string path = rendering.RenderingItem.InnerItem["Path"];
switch(rendering.RenderingItem.InnerItem.TemplateName.ToLower())
{
case "xsl rendering":
// Create an instance of a XSL
XslFile xslFile = new XslFile();
xslFile.Path = path;
xslFile.DataSource = item.Paths.FullPath;
xslFile.Parameters = GetParameters(xslFile);
yield return xslFile;
break;
case "sublayout":
// Create an instance of a sublayout
Sublayout sublayout = new Sublayout();
sublayout.Path = path;
sublayout.DataSource = item.Paths.FullPath;
sublayout.Parameters = GetParameters(sublayout);
yield return sublayout.GetUserControl();
break;
default:
throw new Exception(string.Format("Unknown rendering template - {0}", rendering.RenderingItem.InnerItem.TemplateName));
}
}
}
Once you get the Controls you can add them to a placeholder and they will be rendered to the page.
Each item with its own layout presentation implies you are getting an entire HTML file... each item will have its own <html>, <head>, and <form> tags. Needless to say, that's going to create a problem.
That being said... This is exactly what Server.Execute() is for. Loop through your TreeList items, use LinkManager to get the URL for the item, and then Server.Execute() to get the rendered output.