As a requirement of our application, we add files with other file's properties within document libraries using the following method:
private static SPFile AddFile(SPListItem item, Stream stream, string filename, SPFolder destinationFolder, string comment)
{
string destinationFilePath = destinationFolder.Url + "/" + filename;
using (var web = item.Web)
{
object file;
switch (SPFarm.Local.BuildVersion.Major)
{
case 12:
var parameters2007 = new object[] { destinationFilePath, stream, false, item.File.Author, web.CurrentUser, item.File.TimeCreated, item.File.TimeLastModified, null, comment, true };
file = destinationFolder.Files.GetType().GetMethod("AddInternal", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(Stream), typeof(Boolean), typeof(SPUser), typeof(SPUser), typeof(DateTime), typeof(DateTime), typeof(Hashtable), typeof(string), typeof(Boolean) }, null).Invoke(destinationFolder.Files, parameters2007);
break;
default:
case 14:
var parameters2010 = new object[] { destinationFilePath, stream, null, item.File.Author, web.CurrentUser, item.File.TimeCreated, item.File.TimeLastModified, comment, true };
file = destinationFolder.Files.GetType().GetMethod("Add", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(Stream), typeof(Hashtable), typeof(SPUser), typeof(SPUser), typeof(DateTime), typeof(DateTime), typeof(string), typeof(Boolean) }, null).Invoke(destinationFolder.Files, parameters2010);
break;
}
return file as SPFile;
}
}
The reason we use reflection is that, we need to override created_by and modified_by properties of the file when we create it; which had proved very painful to do by other methods. And Sharepoint 2007's API was limited to accomplish that, hence reflection.
There is a scenario where the web.CurrentUser is sharepoint/system (usually because the context is in elevated state) which is fine as long it is used for modified_by. However, in Sharepoint 2013, SPFileCollection.Add method does not override created_by property, it simply keeps it as CurrentUser(independent to modified_by parameter) whatever we do about it. That results messing the whole workflow of our application.
I've checked Sharepoint 2013's dll file using reflector and it seems the method and its parameters are the same as 2010.
Updating properties after creating the files is something I'd like to avoid since I have bad memories with it. Usually fixing one problem creates several, and I don't recall the problems in detail. Not to mention that that change would need extensive testing in 3 different versions.
Is there a functional way to override created_by property of a file using SPFileCollection.Add on Sharepoint 2013?
Related
I am trying to override the default __import__ method provided by IronPython to handle database imports. I have already run through the example provided here: https://stackoverflow.com/a/4127766/862319
Everything is working so far, but there is a minor issue related to namespace resolution within CLR types. I am able to import using the syntax import ClrAssembly.Type but the syntax from ClrAssembly import Type does not work. This is a minor inconvenience, but I would like to get it resolved. My suspicion is that there are two method signatures tied to the __import__method in IronPython:
But, the SO link above results in only a single method being applied with the 5 parameter signature. Here is the __import__ variable after being set:
How would I go about constructing a custom IronPython.Runtime.Types.BuiltinFunction that maps to 2 method signatures (5 param and 2 param version of DoDatabaseImport) and assign it back to the __import__ variable?
Hopefully this answer helps someone who is looking at this old question.
Looking at the source for IronPython 2.7.7 specifically the builtin.cs file from
https://github.com/IronLanguages/main/blob/3d9c336e1b6f0a86914a89ece171a22f48b0cc6e/Languages/IronPython/IronPython/Modules/Builtin.cs
you can see that the 2 parameter function calls the 5 parameter overload with default values.
public static object __import__(CodeContext/*!*/ context, string name) {
return __import__(context, name, null, null, null, -1);
}
To replicate this I used a delegate with similar default values.
delegate object ImportDelegate(CodeContext context, string moduleName, object globals = null, object locals = null, object fromlist = null, int level = -1);
protected object ImportOverride(CodeContext context, string moduleName, object globals = null, object locals = null, object fromlist = null, int level = -1)
{
// do custom import logic here
return IronPython.Modules.Builtin.__import__(context, moduleName, globals, locals, fromlist, level);
}
Overriding the builtin import function is shown below
private ScriptEngine pyengine;
private ScriptingEngine()
{
Dictionary<string, object> options = new Dictionary<string, object>();
options["Debug"] = true;
pyengine = Python.CreateEngine(options);
var builtinscope = Python.GetBuiltinModule(pyengine);
builtinscope.SetVariable("__import__", new ImportDelegate(ImportOverride));
}
I had the same problem. My solution was, to search for all clr types and store them in a HashSet. If IronPython tries to import some thing from the clr, i ingored my own set of rules and called the built-in importer from IronPython. This is working pretty well for a while now.
Hope this helps.
So, the below code used to work in .NET 4 to get a System.Net.Mail.MailMessage object as a MemoryStream, however with the release of .NET 4.5 beta a runtime exception occurs.
Assembly assembly = typeof(SmtpClient).Assembly;
Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");
using (MemoryStream stream = new MemoryStream())
{
ConstructorInfo mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null);
object mailWriter = mailWriterContructor.Invoke(new object[] { stream });
MethodInfo sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic);
sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true }, null);
.....
}
Runtime exception occurs on sendMethod.Invoke().
Managed to figure out how to get this working again in .NET 4.5 beta. The private API Send() method in MailMessage has changed to: internal void Send(BaseWriter writer, bool sendEnvelope, bool allowUnicode)
Please find updated code below.
Assembly assembly = typeof(SmtpClient).Assembly;
Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");
using (MemoryStream stream = new MemoryStream())
{
ConstructorInfo mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null);
object mailWriter = mailWriterContructor.Invoke(new object[] { stream });
MethodInfo sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic);
sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true, true }, null);
.....
}
This might be usable if you don't want to go with unsupported hacks and don't mind extra performance hit.
public static class MailMessageExtensions
{
public static string RawMessage(this MailMessage m)
{
var smtpClient = new SmtpClient { DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory };
using (var tempDir = new TemporaryDirectory())
{
smtpClient.PickupDirectoryLocation = tempDir.DirectoryPath;
smtpClient.Send( m );
var emlFile = Directory.GetFiles( smtpClient.PickupDirectoryLocation ).FirstOrDefault();
if ( emlFile != null )
{
return File.ReadAllText( emlFile );
}
else
return null;
}
return null;
}
}
class TemporaryDirectory : IDisposable
{
public TemporaryDirectory()
{
DirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory( DirectoryPath );
}
public string DirectoryPath { get; private set; }
public void Dispose()
{
if ( Directory.Exists( DirectoryPath ) )
Directory.Delete( DirectoryPath, true );
}
}
for checking if extra boolean i use :
If _sendMethod.GetParameters.Length = 2 Then
_sendMethod.Invoke(Message, BindingFlags.Instance Or BindingFlags.NonPublic, Nothing, New Object() {_mailWriter, True}, Nothing)
Else
_sendMethod.Invoke(Message, BindingFlags.Instance Or BindingFlags.NonPublic, Nothing, New Object() {_mailWriter, True, True}, Nothing)
End If
The proposed solution with the extra TRUE works beautifully.
I started to getting the error while running my project in VS2012 even though I am not using .net 4.5 but 4.0 in all my libraries.
The error only happens on the machine where you have installed VS2012, looks like VS2012 makes reference to .net 4.5 while you are debugging. When you deploy and run the application in clients running .net 4.0 everything works fine.
Thus : If you run 4.0 - do not add the extra TRUE, if you run 4.5 add it.
We fought with the mail message conversion for a long time. Ultimately the solution was to use MimeKit.
var memoryStream = new MemoryStream();
var mimeMessage = MimeMessage.CreateFromMailMessage(message);
mimeMessage.WriteTo(memoryStream);
If you use the methods above you will get really close and it will work in most cultures but eventually the subject encoding will defeat you.
For those, who are struggling with mailWriterContructor being null in .NET 5 or facing Parameter count mismatch exception, take a closer look on my solution usable for any stream. Link here
I had a working code using Java using this method for creating document and folders in alfresco using CMIS.
Folder.createFolder(
Map<string, ?> properties,
List<Policy> policies, List<Ace> addAce, List<Ace> removeAce,
OperationContext context);
And I used Folder.createDocument for creating document (they have the same parameter) and used it as follow:
AlfrescoFolder.java
parentFolder.createFolder(
AlfrescoUtilities.mapAlfrescoObjectProperties("cmis:folder",
folderName, title, description, tags),
null, null, null,
AlfrescoSession.getSession().getDefaultContext()
);
// AlfrescoSession.getSession() a custom method that we created to
// create a Session variable
AlfrescoUtilities.java
public static Map<String, Object> mapAlfrescoObjectProperties(
String objectType, String name, String title, String description,
List<String> tags)
{
Map<String, Object> properties = new HashMap<>();
properties.put(PropertyIds.OBJECT_TYPE_ID,
objectType + ",P:cm:titled,P:cm:taggable");
properties.put(PropertyIds.NAME, name);
if (title != null) properties.put("cm:title", title);
if (description != null) properties.put("cm:description", description);
if (tags != null) properties.put("cm:taggable", tags);
return properties;
}
}
In the code above, the objectType parameter there will be either cmis:folder or cmis:document and we discovered that to add aspects for adding description is to add P:cm:titled to add description and title and P:cm:taggable to attach tags.
Now, I'm working on a .NET application using C#.
When I translated it and used the same methods, the only problem is it is only working when I removed the P:cm:tittled; P:cm:taggable
Here is the current code for creating the properties:
AlfrescoUtilities.cs
public static Dictionary<string, object> mapAlfrescoObjectProperties(
string objectType, string name, string title, string description,
List<string> tags)
{
Dictionary<string, object> properties = new Dictionary<string, object>();
properties[PropertyIds.ObjectTypeId] = objectType;
// +",P:cm:titled,P:cm:taggable";
properties[PropertyIds.Name] = name; /*
if (title != null) properties["cm:title"] = title;
if (description != null) properties["cm:description"] = description;
if (tags != null) properties["cm:taggable"] = tags;
*/
return properties;
}
And as you noticed, I commented the other codes.
The only once that work is the objecttypeid (whether cmis:folder or cmis:document)
and the name.
Kindly help me regarding this. This is a windows application using .NET 3.5 and C#. Alfresco Version is 4.2.3
We verified that dotCmis <= 0.6.0 does not support CMIS 1.1 (and thus has no native support for aspect properties).
However, we successfully tested the approach described in
http://mail-archives.apache.org/mod_mbox/chemistry-dev/201202.mbox/%3C2D9094AD2E4FBE4B86B8B274D9DB9E081F7A754BF1#SSWPROD1001.synapps-solutions.com%3E
to work with the low-level CMIS API and manually take advantage of Alfresco CMIS extensions.
We also verified that in session.Query("select * from nm:aspectName ") result do contain the aspect properties.
I'm trying to figure out how to get the currently opened document on Lotus Notes through C#, but I cannot. Even though I researched half a day on Google, I couldn't find anything useful.
With my code I get the view I want, the database I want, etc, but I just would like to get the opened document. I tried something like IsUIDocOpen, but none of the full collection contains it as true.
Does someone know if there is any different between an opened document and a non-opened document trough Domino API? My workaround is to get the subject of the email and the size of the email and compare each one and when it matches get the Entry ID and then get the information I need - but that takes too long, especially when the inbox is big.
Any suggestions?
Here is my code:
NotesSession session = new NotesSession();
session.Initialize(sPassword);
notedb = session.GetDatabase(server, filename, false);
if (notedb.IsOpen)
{
mailView = notedb.GetView("$Inbox");
mailDoc = mailView.GetLastDocument();
//mailDoc = mailView.GetDocumentByKey();
try
{
while (mailDoc != null)
{
NotesItem item = mailDoc.GetFirstItem("From");
if (item != null)
{
MessageBox.Show("From = " + item.Text);
}
}
}
}
Solution: should be something like: mailDoc = mailView.GetCurrentDocument(); // But obviously this method does not exist :D
=====================================================================================
Solution code:
Type NotesUIWorkspaceType = Type.GetTypeFromProgID("Notes.NotesUIWorkspace", true);
object workspace = Activator.CreateInstance(NotesUIWorkspaceType);
object uiDoc = NotesUIWorkspaceType.InvokeMember("CurrentDocument", BindingFlags.GetProperty, null, workspace, null);
Type NotesUIDocument = uiDoc.GetType();
object Subject = NotesUIDocument.InvokeMember("FieldGetText", BindingFlags.InvokeMethod, null, uiDoc, new Object[] { "Subject" });
string subject = "test";
NotesUIDocument.InvokeMember("FieldSetText", BindingFlags.InvokeMethod, null, uiDoc, new Object[] { "Subject", subject });
object Body = NotesUIDocument.InvokeMember("FieldGetText", BindingFlags.InvokeMethod, null, uiDoc, new Object[] { "Body" });
What you actually need is the Notes OLE classes.
The C# Interop classes are based on the Notes COM classes. The COM classes only have access to the "back end". I.e., the root object is Lotus.NotesSession, and all the classes work against data stored in .NSF files. They have no access to anything in the Notes UI.
The Notes OLE classes have access to both the "back end", with the root object Notes.NotesSession, and the "front end" with the root object Notes.NotesUIWorkspace. As you can tell by the name of that class, it's the front end classes that give you access to elements of the Notes client UI.
Note the subtle difference: the prefix for the OLE classes is "Notes.", instead of the prefix "Lotus." for the COM classes.
In old-style VB late binding, the OLE classes are instantiated this way:
CreateObject("Notes.NotesUIWorkspace")
I'm not sure how that translates into C#.
Anyhow, once you have the NotesUIWorkspace object, the currently opened document is available with the NotesUIWorkspace.CurrentDocument method.
IF you are using the Interop Classes you need to use NotesUIView.CurrentView.Documents to get what you want... see here.
You'll need to get the NotesUIWorkspace first, then use the CurrentDocument property
NotesUIWorkspace workspace = new NotesUIWorkspace();
NotesUIDocument uidoc = workspace.CurrentDocument();
I want to convert an OpenOffice Impress Presentation file and convert it to HTML or JPEG.
I have found a few examples, but they appear to be broken. I would like to do it, in a way that it does not matter what version of OpenOffice is installed, and I do not want to bundle any interop dlls with my application. Therefore, I am looking for a solution that is done in C# reflection, preferably, or Perl using Win32-OLE.
Also, how would you hide the OpenOffice GUI?
Check out this solution . There might need some changes on the declaration of the PropertyValues
public void Conversion(string sourcefile, string exportfile)
{
Type tServiceManager = Type.GetTypeFromProgID("com.sun.star.ServiceManager", true);
object oServiceManager = System.Activator.CreateInstance(tServiceManager);
object oDesktop = Invoke(oServiceManager,"createinstance",BindingFlags.InvokeMethod,"com.sun.star.frame.Desktop");
//Load Document
Object[] arg = new Object[4];
arg[0] = PathConverter(sourcefile); // or "private:factory/swriter" for a blank Document
arg[1] = "_blank";
arg[2] = 0;
object loadproperty1 = CreatePropertyValue("Hidden", true); // Executes the OpenOffice without UI
arg[3] = new Object[] { loadproperty1};
object oComponent = Invoke(oDesktop,"loadComponentFromUrl",BindingFlags.InvokeMethod,arg);
//Create an array for the storeToUrl method
arg = new Object[2];
arg[0] = PathConverter(exportfile);
object storeproperty1 = CreatePropertyValue("Overwrite", true); // Overrites if file exits and prevents errors
object storeproperty2 = CreatePropertyValue("FilterName", "HTML (StarWriter)"); // Export to HTML
arg[1] = new Object[] { storeproperty1,storeproperty2 };
Invoke(oComponent,"storeToUrl",BindingFlags.InvokeMethod,arg);
}
I published a previous solution regarding the exportformats and the string you need to pass
Helper Methods:
private static object CreatePropertyValue(object serviceManager,string name, object value)
{
object propertyvalue = Invoke(serviceManager, "Bridge_GetStruct", BindingFlags.CreateInstance|BindingFlags.InvokeMethod|BindingFlags.GetProperty,
"com.sun.star.beans.PropertyValue");
Invoke(propertyvalue, "Name", BindingFlags.SetProperty, name);
Invoke(propertyvalue, "Value", BindingFlags.SetProperty, value);
return propertyvalue;
}
private static object Invoke(object obj, string method, BindingFlags binding, params object[] par)
{
return obj.GetType().InvokeMember(method, binding, null, obj, par);
}
/// Convert into OO file format
/// The file.
/// The converted file
private static string PathConverter( string file)
{
try
{
file = file.Replace(#"\", "/");
return "file:///"+file;
}
catch (System.Exception ex)
{
throw ex;
}
}
Use OpenOffice::OODoc, it understands the XML format of OpenOffice documents, requires no openoffice binaries to be running or even to have openoffice installed.