I have a situation where a user can upload a word document which contains placeholders for certain properties (i.e. in MS Word the user has gone to Insert > Quick Parts > Document Properties and chosen a property). The specific properties I want to support are Title, Author, Company and Publish Date.
Title and Author are set as Package Properties, and Company is set as an Extended File Property. These are set with the below code, which works correctly:
private static void SetDocumentProperties(ReportTemplateModel model, WordprocessingDocument wordDocument)
{
//these properties always exist
wordDocument.PackageProperties.Title = model.Title;
wordDocument.PackageProperties.Creator = model.Author;
wordDocument.PackageProperties.Created = DateTime.Now;
wordDocument.PackageProperties.Modified = DateTime.Now;
//these properties can be null
if (wordDocument.ExtendedFilePropertiesPart == null)
{
wordDocument.AddNewPart<ExtendedFilePropertiesPart>();
}
if (wordDocument.ExtendedFilePropertiesPart.Properties == null)
{
wordDocument.ExtendedFilePropertiesPart.Properties = new Properties(new Company(model.SiteName));
}
else
{
wordDocument.ExtendedFilePropertiesPart.Properties.Company = new Company(model.SiteName);
}
}
My problem is that I can't work out how the set the Publish Date property. I have tried adding it as a Custom File Property using the below code (which is adapted from https://www.snip2code.com/Snippet/292005/WDSetCustomProperty), but this does not work. I've read a few things about setting custom properties, but I'm confused how they're meant to work. I'm also unsure if the Publish Date should actually be set as a custom property, or some other type of property.
var customProps = wordDocument.CustomFilePropertiesPart;
if (customProps == null)
{
customProps = wordDocument.AddCustomFilePropertiesPart();
customProps.Properties = new DocumentFormat.OpenXml.CustomProperties.Properties();
}
var properties1 = new DocumentFormat.OpenXml.CustomProperties.Properties();
//I have tried both of these lines, neither worked.
//properties1.AddNamespaceDeclaration("op", "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties");
properties1.AddNamespaceDeclaration("vt", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes");
var customDocumentProperty1 = new DocumentFormat.OpenXml.CustomProperties.CustomDocumentProperty()
{
FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}",
PropertyId = 2,
Name = "Publish Date"
};
customDocumentProperty1.Append(new DocumentFormat.OpenXml.VariantTypes.VTLPWSTR { Text = DateTime.Today.ToShortDateString() });
properties1.Append(customDocumentProperty1);
part.Properties = properties1;
What type of property should the Publish Date be set as, and what is the right syntax for setting this?
Update: I have found that Publish Date is a CoverPageProperty which can be created using the below snippet, but I still haven't found how to set it correctly within the document.
var coverPageProps = new DocumentFormat.OpenXml.Office.CoverPageProps.CoverPageProperties
{
PublishDate = new PublishDate(DateTime.Today.ToShortDateString())
};
Adding the below code to my SetDocumentProperties method seems to work. I must admit I don't fully understand the below code, so any explanation would still be welcome. Additionally, if anyone has a solution which doesn't include writing XML as a string inside C# I would much prefer to avoid that.
const string publishDatePartId = "publishDatePart";
var publishDateXmlPart = wordDocument.MainDocumentPart.AddNewPart<CustomXmlPart>("application/xml", publishDatePartId);
var writer = new XmlTextWriter(publishDateXmlPart.GetStream(FileMode.Create), System.Text.Encoding.UTF8);
writer.WriteRaw($"<CoverPageProperties xmlns=\"http://schemas.microsoft.com/office/2006/coverPageProps\">" +
$"<PublishDate>{DateTime.Today.ToShortDateString()}</PublishDate>" +
$"</CoverPageProperties>");
writer.Flush();
writer.Close();
var customXmlPropertiesPart = publishDateXmlPart.AddNewPart<CustomXmlPropertiesPart>(publishDatePartId);
customXmlPropertiesPart.DataStoreItem = new DocumentFormat.OpenXml.CustomXmlDataProperties.DataStoreItem()
{
//I don't know what this ID is, but it seems to somehow relate to the Publish Date
ItemId = "{55AF091B-3C7A-41E3-B477-F2FDAA23CFDA}"
};
Related
Background: I'm trying to write a program to insert an image into a cell of a spreadsheet. LibreOffice recently changed how this is done, and all the samples I could find use the old method which no longer works.
Technically I know that you can't "insert" an image into a cell and that such an image is an overlay on a DrawPage that sits on top of the spreadsheet to "decorate" it.
One of the first steps in doing this (the new way) is to create an XGraphic object which contains the image. The process is to create an XGraphicProvider and call it with MediaProperties that specify the image file URL to be loaded. I have a program that is supposed to do this but the resulting XGraphic is null. The LO SDK gives pretty much no information when you do something wrong; it just doesn't work.
Here is the code I have, with all the headers removed:
// addpic
// add picture to spreadsheet - debug version
class OpenOfficeApp {
[STAThread]
static void Main(string[] args) {
bool lreadonly;
string pqfile;
string pqURL;
string pqpic;
pqfile = "file:///D:/Documents/NSexeye/ODS%20File%20Access/"+
"addpix/addpic.ods";
pqpic = "addpic2";
pqURL = pqpic+".jpg";
lreadonly = false;
Console.WriteLine("Using: "+pqfile);
// get the desktop
XComponentContext XCC = uno.util.Bootstrap.bootstrap();
XMultiComponentFactory XMCF =
(XMultiComponentFactory)XCC.getServiceManager();
XMultiServiceFactory XMSF = (XMultiServiceFactory)XCC.getServiceManager();
XComponentLoader XCL =
(XComponentLoader)XMSF.createInstance("com.sun.star.frame.Desktop");
// open the spreadsheet
PropertyValue[] pPV = new PropertyValue[2];
pPV[0] = new PropertyValue();
pPV[0].Name = "Hidden";
pPV[0].Value = new uno.Any(true);
pPV[1] = new PropertyValue();
pPV[1].Name = "ReadOnly";
if (lreadonly) pPV[1].Value = new uno.Any(true);
else pPV[1].Value = new uno.Any(false);
XComponent XCo = XCL.loadComponentFromURL(pqfile,"_blank",0,pPV);
// create graphic object containing image
object oGP = XMCF.createInstanceWithContext(
"com.sun.star.graphic.GraphicProvider",XCC);
if (oGP == null) {
Console.WriteLine("oGP is null. Aborting.");
return;
}
XGraphicProvider XGP = (XGraphicProvider)oGP;
if (XGP == null) {
Console.WriteLine("XGP is null. Aborting.");
return;
}
pPV = new PropertyValue[1];
pPV[0] = new PropertyValue();
pPV[0].Name = "URL";
pPV[0].Value = new uno.Any(pqURL);
Console.WriteLine("Creating XGraphic containing "+pqURL);
XGraphic XG = XGP.queryGraphic(pPV);
// *** XG is null here
if (XG == null) {
Console.WriteLine("XG is null. Aborting.");
return;
}
// ... lots of stuff to be added here
// save and close the spreadsheet
XModifiable XM = (XModifiable)XCo;
XM.setModified(true);
XStorable XSt = (XStorable)XCo;
XSt.store();
XCloseable XCl = (XCloseable)XCo;
XCl.close(true);
// terminate LibreOffice
// *** I want this to not terminate it if something else is open
XDesktop XD = (XDesktop)XCL;
if (XD != null) XD.terminate();
}
}
I get a null for the XGraphic, in the place indicated in the comments. I don't know if the call to create it is failing, or if one of the earlier steps of the process are incorrect.
My goal here, in addition to getting my program working, is to create a sample program showing how to add an image to a Calc spreadsheet cell, and to manipulate such images. There are a fair number of people asking questions about this and none of the examples I've found will work. I think a good working sample will be of value.
I've spent a lot of time searching for information and code samples for this, with nothing that helps. I've tried to find ways to verify the validity of the XGraphicProvider interface with no luck. I've run out of things to try.
I'm hoping someone who knows about the LibreOffice SDK can take a look and maybe see what I'm doing wrong.
Update: I figured out what I was doing wrong: I was passing a bare filename in the "URL" property to XGraphicProvider. It has to be the same format (starting with "file:///") as the spreadsheet's file name specification.
Now I'm stuck with another property problem. The XGraphic has to be specified as a parameter to the GraphicObjectShape's Graphic property, but the setPropertyValue() function requires that it be a uno.Any type. I can't figure out how to specify an interface name like XGraphic as a uno.Any.
Here is the piece of code that won't compile, complaining that it can't convert an XGraphic to a uno.Any, in the first setPropertyValue call:
// set image XGraphic
XPropertySet XPS = (XPropertySet)XS;
XPS.setPropertyValue("Graphic",XG);
XPS.setPropertyValue("Name",new uno.Any(pqpic));
XG is an XGraphic type. Using "new uno.Any(XG)" doesn't work either, giving a similar compiler error.
After trying unsuccessfully for a few hours to get the latest LO SDK up and running, let me offer some untested ideas.
First of all, here is some working Basic code, no doubt similar to what you're translating from. The important line is oShape.Graphic = oProvider.queryGraphic(Props()).
oDoc = ThisComponent
oSheet = oDoc.CurrentController.ActiveSheet
pqURL = "file:///C:/Users/JimK/Desktop/addpic.jpg"
oProvider = createUnoService("com.sun.star.graphic.GraphicProvider")
oShape = oDoc.createInstance("com.sun.star.drawing.GraphicObjectShape")
Dim Props(0) as new com.sun.star.beans.PropertyValue
Props(0).Name= "URL"
Props(0).Value = pqURL
oShape.Graphic = oProvider.queryGraphic(Props())
oCell = oSheet.getCellByPosition(5,5)
oShape.Name = oCell.AbsoluteName + "##" + Props(0).Value
oShape.Anchor = oCell
oSheet.DrawPage.add(oShape)
'Resize
w = oShape.Graphic.Size.Width
h = oShape.Graphic.Size.Height
wcl = oCell.Size.Width
hcl = oCell.Size.Height
If w<>0 and h<>0 then
oCell.String=""
Dim Size as new com.sun.star.awt.Size
Size.Width = wcl
Size.Height = h*wcl/w
If Size.Height > hcl then
Size.Width = hcl*w/h
Size.Height = hcl
Endif
oShape.setSize(Size)
oShape.setPosition(oCell.Position)
erase oShape
Else
oShape.dispose()
Endif
Now, how to translate this to C#? It looks like you may need to explicitly specify the type. In the SDK example, there are calls like this.
xFieldProp.setPropertyValue(
"Orientation",
new uno.Any(
typeof (unoidl.com.sun.star.sheet.DataPilotFieldOrientation),
unoidl.com.sun.star.sheet.DataPilotFieldOrientation.DATA ) );
So in your case, something like this:
XPS.setPropertyValue(
"Graphic"
new uno.Any(
typeof(unoidl.com.sun.star.graphic.XGraphic),
XG));
Alternatively, follow the suggestion here: set GraphicURL, which should load the image and set Graphic for you.
I have a field in CROpportunity named UsrOrderTotalValue.
Sales Order(SO301000) and Opportunitis(CR304000) are linked with added to SOOrder tab custome field UsrOpportunityID.
When Save Button pusshed on Sales Order, it's needed to save to a UsrOrderTotalValue some Value.
Some code snipet is given below.
Besides below I tried different approaches of using Persist, but nothing works.
My Acumatica version:
Acumatica 2018 R1 (18.110.0017)
OpportunityMaint opportunityGraph = PXGraph.CreateInstance<OpportunityMaint>();
SOOrderExt sOOrderExt = row.GetExtension<SOOrderExt>();
CROpportunity cROpportunity = Opportunity.Select(sOOrderExt.UsrOpportunityID);
opportunityGraph.Opportunity.Update(cROpportunity);
opportunityGraph.Save.Press();
Try like below, I have hardcoded value, please replace with your fields.
[PXOverride]
public void Persist(Action del)
{
if ((Base.Document.Cache.GetStatus(Base.Document.Current) == PXEntryStatus.Inserted || Base.Document.Cache.GetStatus(Base.Document.Current) == PXEntryStatus.Updated))
{
OpportunityMaint opportunityGraph = PXGraph.CreateInstance<OpportunityMaint>();
CROpportunity cROpportunity = new CROpportunity();
cROpportunity.OpportunityID = "000001";
cROpportunity = opportunityGraph.Opportunity.Current = opportunityGraph.Opportunity.Select(cROpportunity);
if (cROpportunity != null)
cROpportunity.OpportunityName = "test";
opportunityGraph.Opportunity.Update(cROpportunity);
opportunityGraph.Save.Press();
}
del();
}
I'm working with MFiles API...
I want to pass a propertyDef to a propertyValue...
This code is working... but I have to create the MFiles object first.
ObjectVersionAndProperties objVersion =
mFilesStructure.MFileVault.ObjectOperations.CreateNewObject(objTypeID,
propValues);
var testPropValues = new PropertyValues();
testPropValues = FilesStructure.MFileVault.ObjectPropertyOperations.GetProperties(objVersion.ObjVer);
var testPropValue = new PropertyValue();
testPropValue = testPropValues.SearchForProperty(typeClientID);
it work fine "testPropValue" has all the property set correctly espacially the DataType... but don't want to create the MFiles at first...
This should do the same, in my opinion but doesn't
var test = new PropertyDef();
test = mFilesStructure.MFileVault.PropertyDefOperations.GetPropertyDef(typeClientID);
var testPropValue = new PropertyValue();
testPropValue.PropertyDef = test.ID;
the properties doesn't setup correctly...
Any one can help
Best regards,
Steph
I just stumbled across this looking for something else and thought I might help.
You actually have it a little backwards. The creation of the new object is actually the last step in the process. You need to create a collection of PropertyValues() by creating each individual PropertyValue() and then adding them to the collection.
So something like this:
public static PropertyValue GetPropertyValue(int propertyDefId, object value)
{
//resolve property def by ID
PropertyDef propertyDef = Vault.PropertyDefOperations.GetPropertyDef(propertyDefId);
//create the property value with prop def ID and value
return GetPropertyValue(propertyDefId, propertyDef.DataType, value);
}
public static PropertyValue GetPropertyValue(int propertyDefId, MFDataType dataType, object value)
{
PropertyValue propertyValue = new PropertyValue();
propertyValue.PropertyDef = propertyDefId;
propertyValue.TypedValue.SetValue(dataType, value);
return propertyValue;
}
public static ObjectVersionAndProperties CreateDocument(PropertyValues propertyValues, string filepath, Vault vault)
{
// Create the Source File object from the filepath.
SourceObjectFile sourceFile = new SourceObjectFile();
sourceFile.SourceFilePath = filepath;
sourceFile.Extension = Path.GetExtension(filepath).TrimStart('.');
sourceFile.Title = Path.GetFileNameWithoutExtension(filepath).TrimEnd('.');
// Create the document object.
return vault.ObjectOperations.CreateNewSFDObject((int)MFBuiltInObjectType.MFBuiltInObjectTypeDocument,
propertyValues, sourceFile, true);
}
Once you set up the above functions you could call them like:
//If the document doesn't exist, go ahead and create a new one
//creat and add all the properties
PropertyValues props = new PropertyValues();
//class
props.Add(-1, HelperMF.GetClassPropertyValue(classId, env.Vault));
//job
int jobId = env.Vault.ValueListItemOperations.GetValueListItemByDisplayID(Structure.ObjType.Job.ID, jobDisplayId).ID;
props.Add(-1, HelperMF.GetPropertyValue(Properties.Job.ID, jobId, env.Vault));
//estimates
props.Add(-1, HelperMF.GetPropertyValueFromListOfDisplayIds(env.Vault, Properties.Estimate.ID,
MFDataType.MFDatatypeMultiSelectLookup, Structure.ObjType.Estimate.ID, estimateDisplayIds));
//Add the relationship to the return doc that was uploaded
props.Add(-1, HelperMF.GetPropertyValue(Properties.Document.ID, movingDocId, env.Vault));
//create the new object in the vault
ObjectVersionAndProperties newDoc = HelperMF.CreateDocument(props, docDownloadPath, env.Vault);
I use a lot of help functions and classes but you should get the gist from my samples. Also, I would highly recommend you use the M-Files community website for research as they have a lot of code samples there geared specifically for M-Files.
https://community.m-files.com/
Also, if you don't already, use the API documentation as it also includes code samples.
http://www.m-files.com/api/documentation/2015.2/
Hopefully this helps,
Mike
I'm creating an import tool that programatically creates items in Sitecore. The item gets created, but when I view it, it says 'The current item does not have a version in "English : English."' I put in using (new LanguageSwitcher("en-gb")) but that didn't fix it. The way my code works is that it looks for the folder that the item is supposed to be put in (all folders are based on year, e.g. 2016, 2017); if the folder doesn't exist, I create that folder before creating the item. This is my code:
protected void PublishRelease(PressReleaseItem release)
{
using (new LanguageSwitcher("en-gb"))
{
var year = release.ReleaseDate.Year;
// create year folder if it doesn't exist
var folderQuery = String.Format(PressReleaseYearFolderFastQuery, year);
Item folder = _db.SelectItems(folderQuery).ToList().FirstOrDefault();
if (folder == null)
{
var templateId = _templateFactory.GetTemplateId<IPressReleaseYearFolderItem>();
TemplateID pressReleaseFolderTemplateId = new TemplateID(templateId.ToID());
folder = _pressReleaseFolder.Add(year.ToString(), pressReleaseFolderTemplateId);
}
if (folder == null) return;
// add item to folder
var itemTemplateId = _templateFactory.GetTemplateId<IPRNewswirePressReleaseItem>();
TemplateID pressReleaseTemplateId = new TemplateID(itemTemplateId.ToID());
item = folder.Add(SanitizeHeadline(release.Headline), pressReleaseTemplateId);
if (item == null) return;
item.Fields.ReadAll();
item.Editing.BeginEdit();
try
{
item.Fields["External ID"].Value = release.ExternalId;
item.Fields["Active"].Value = release.Active.ToString();
item.Fields["Image Url"].Value = release.ImageUrl;
item.Fields["PDF Url"].Value = release.PdfUrl;
item.Fields["Description"].Value = release.SubHeadline;
item.Fields["Headline"].Value = release.Headline;
item.Fields["Date"].Value = release.ReleaseDate.ToString("d");
item.Fields["Longtext"].Value = release.Body;
item.Fields["Category"].Value = SetReleaseCategories(release.Category);
}
catch (Exception ex)
{
item.Editing.EndEdit();
}
item.Editing.EndEdit();
}
}
When I view the new item in Sitecore, it says it has no version in English; when I click to add a new version, all of the fields are blank.
I would have expected the code you have above to default to creating a new version in the en-GB language. Like Richard mentions, validate if your 'english' is set to 'en' or 'en-gb'. If your default English has a different code, you might have to update your language switcher.
Alternatively, have you tried doing something like below to force a version?
var result = item.Versions.AddVersion();
This would at least allow you to test if the version creation is working at all, though you shouldn't need it for a new item.
We are having an issue with searching a custom record through SuiteTalk. Below is a sample of what we are calling. The issue we are having is in trying to set up the search using the internalId of the record. The issue here lies in in our initial development account the internal id of this custom record is 482 but when we deployed it through the our bundle the record was assigned with the internal Id of 314. It would stand to reason that this internal id is not static in a site per site install so we wondered what property to set up to reference the custom record. When we made the record we assigned its “scriptId’ to be 'customrecord_myCustomRecord' but through suitetalk we do not have a “scriptId”. What is the best way for us to allow for this code to work in all environments and not a specific one? And if so, could you give an example of how it might be used.
Code (C#) that we are attempting to make the call from. We are using the 2013.2 endpoints at this time.
private SearchResult NetSuite_getPackageContentsCustomRecord(string sParentRef)
{
List<object> PackageSearchResults = new List<object>();
CustomRecord custRec = new CustomRecord();
CustomRecordSearch customRecordSearch = new CustomRecordSearch();
SearchMultiSelectCustomField searchFilter1 = new SearchMultiSelectCustomField();
searchFilter1.internalId = "customrecord_myCustomRecord_sublist";
searchFilter1.#operator = SearchMultiSelectFieldOperator.anyOf;
searchFilter1.operatorSpecified = true;
ListOrRecordRef lRecordRef = new ListOrRecordRef();
lRecordRef.internalId = sParentRef;
searchFilter1.searchValue = new ListOrRecordRef[] { lRecordRef };
CustomRecordSearchBasic customRecordBasic = new CustomRecordSearchBasic();
customRecordBasic.recType = new RecordRef();
customRecordBasic.recType.internalId = "314"; // "482"; //THIS LINE IS GIVING US THE TROUBLE
//customRecordBasic.recType.name = "customrecord_myCustomRecord";
customRecordBasic.customFieldList = new SearchCustomField[] { searchFilter1 };
customRecordSearch.basic = customRecordBasic;
// Search for the customer entity
SearchResult results = _service.search(customRecordSearch);
return results;
}
I searched all over for a solution to avoid hardcoding internalId's. Even NetSuite support failed to give me a solution. Finally I stumbled upon a solution in NetSuite's knowledgebase, getCustomizationId.
This returns the internalId, scriptId and name for all customRecord's (or customRecordType's in NetSuite terms! Which is what made it hard to find.)
public string GetCustomizationId(string scriptId)
{
// Perform getCustomizationId on custom record type
CustomizationType ct = new CustomizationType();
ct.getCustomizationTypeSpecified = true;
ct.getCustomizationType = GetCustomizationType.customRecordType;
// Retrieve active custom record type IDs. The includeInactives param is set to false.
GetCustomizationIdResult getCustIdResult = _service.getCustomizationId(ct, false);
foreach (var customizationRef in getCustIdResult.customizationRefList)
{
if (customizationRef.scriptId == scriptId) return customizationRef.internalId;
}
return null;
}
you can make the internalid as an external property so that you can change it according to environment.
The internalId will be changed only when you install first time into an environment. when you deploy it into that environment, the internalid will not change with the future deployments unless you choose Add/Rename option during deployment.