dinsdag 6 november 2012

Horizontal alignment of Choise Fields


This week I found a way to change the alignment of a Choice Field.
Nice to know if you have list of questions where somebody has to choose
between for example three answers. 

You only have to make a custom New/Edit page in Sharepoint Designer
and add the next line
<xsl:comment>FieldName="Display Name"</xsl:comment>                                                          
just before the sharepoint Formfield definition <SharePoint:FormField …

Change the "Display Name" by the display name of your Sharepoint Field.

Add a Content Editor webpart to your page and 
put the next Jquery script in this webpart.


<script>
$( document ).ready( function() {
// columnName = Field Display Name
// perRow = how many choices you want to see in one row.
            $().SPServices.SPArrangeChoices({
                        columnName : "Display Name",
                        perRow: 3
                        });
});
</script>
 

donderdag 11 oktober 2012

Check if Sharepoint Richt Text Editor Field is empty or not (works in IE, Firefox and Chrome)


Found out this week that the code for checking the content of a multi-line rich text field was 
not working for other than IE-browsers.
So after some debugging in Chrome I found a solution that works in Chrome and in Firefox.
I hope that it's also working in other browsers... but I don't have them to my position at work.
Have fun with my new code... 



 firefox/chrome bugfix...
    var scomments = "";
    if ($.browser.msie) 
 {     
  // IE browser, check on language needed because Sharepoint and IE Translates the IFrame name in different languages.   
  if (_spPageContextInfo.currentLanguage == 1036) 
  {
            var systemDescriptionRTE = $("textarea[title='Comments']").closest("span").find("iframe[Title='Éditeur de texte enrichi']").contents().find("body");
            scomments = $(systemDescriptionRTE).text();
        }
        else
        {
            if (_spPageContextInfo.currentLanguage == 1043) 
   {
                var systemDescriptionRTE = $("textarea[title='Comments']").closest("span").find("iframe[Title='RTF-editor']").contents().find("body");
                scomments = $(systemDescriptionRTE).text();
            }
            else
            {
                var systemDescriptionRTE = $("textarea[title='Comments']").closest("span").find("iframe[Title='Rich Text Editor']").contents().find("body");
                scomments = $(systemDescriptionRTE).text();
            }
        }
 }
 else
 {
 // if other browser
        var systemDescriptionRTETextArea = $("textarea[Title='Comments']"); 
        scomments = $(systemDescriptionRTETextArea).val(); 
 }
  

Now you can check with the 'scomments' variable is it is empty or containing some values.

donderdag 13 september 2012

Hide a webpart or Columns of a ListView webpart.


Found some interesting JQuery for hiding a SharePoint Webpart, 
or some columns of a ListviewWebpart.
This can be usefull if you have some columns that are language depentend. 
So of that moment you can hide for example a column with dutch text 
or an other column with other information. 
Or if you want that someone has less columns than another user 
in the same view and you don't want to duplicate the views. 

My example below will hide a complete webpart and the first three columns 
of a ListView webpart.
Attention, if you have more than one listView webpart on your page, 
all the same columns will be hidden.

If you know how I can determ to hide the columns of a specific webpart... 
I'm interested in the code.


$( document ).ready( function() {
 // hide new webpart
 $(MSOZoneCell_WebPartWPQ5).hide();
 // hide column 1, 2 and 3           
    $(".ms-listviewtable,.ms-emptyView").find("td:nth-child(1),th:nth-child(1)").hide();     
    $(".ms-listviewtable,.ms-emptyView").find("td:nth-child(2),th:nth-child(2)").hide();     
 $(".ms-listviewtable,.ms-emptyView").find("td:nth-child(3),th:nth-child(3)").hide();      
});


 

dinsdag 4 september 2012

Change ContentType and Metadata of your Documents uploaded in a Sharepoint Library


This part for updating a sharepoint contentType for a uploaded Document in a SharePoint Library.
So Once you have uploaded a File into a SharePoint Library, you needs it's ID for changing the
metadata or contentType of a Document.
So first of all call my function below for getting the ID.

    //Call to the function to get the ID
    string sID = sGetID(sSiteUrl, sSiteList, sFileName);
    private string sGetID(string sURL, string sListName, string sFileName) 
    {
        string sID = "";
        SPListsService.Lists list = new SPListsService.Lists();
        list.Url = sSiteUrl + "/_vti_bin/lists.asmx";
  // I have a common Boolean field for determing if I use the current logged on user or to use a Technical User. 
  // attention !!!! The user must have access to your Sharepoint environment.
        if (bDefaultLogon)
            list.Credentials = System.Net.CredentialCache.DefaultCredentials;
        else
        {
            list.PreAuthenticate = true;
            list.Credentials = new System.Net.NetworkCredential(sTechnicalUser, sTechnicalPw, sTechnicalDomain);
        }
        try
        {
   string rowLimit = "99999";
            XmlDocument xmlDoc = new System.Xml.XmlDocument();
            XmlNode listQuery = xmlDoc.CreateNode(XmlNodeType.Element, "Query", "");
            listQuery.InnerXml = "" + sFileName + "";
            listQuery.InnerXml = listQuery.InnerXml + "\"";
            XmlNode listViewFields = xmlDoc.CreateNode(XmlNodeType.Element, "ViewFields", "");
            listViewFields.InnerXml = "" +
                                      "";
            XmlNode listQueryOptions = xmlDoc.CreateNode(XmlNodeType.Element, "QueryOptions", "");
            listQueryOptions.InnerXml = "";
            XmlNode ndListItems = null;
            Guid g = GetWebID(sSiteUrl); // Function to get the ID of your Web
            ndListItems = list.GetListItems(sSiteList, null, listQuery, listViewFields, rowLimit, listQueryOptions, g.ToString());
            sID = ndListItems.ChildNodes[1].ChildNodes[1].Attributes["ows_ID"].Value;
        }
        catch { }
        finally {
           list.Dispose();
        }
        return sID;
    }    
    /* -- Get the Guid of your List  -- */
    private Guid GetWebID(string webPath)
    {
        SPSiteDataService.SiteData siteDataWS = new SPSiteDataService.SiteData();
        if (bDefaultLogon)
            siteDataWS.Credentials = System.Net.CredentialCache.DefaultCredentials;
        else
        {
            siteDataWS.PreAuthenticate = true;
            siteDataWS.Credentials = new System.Net.NetworkCredential(sTechnicalUser, sTechnicalPw, sDomain);
        }
        SPSiteDataService._sWebMetadata webMetaData;
        SPSiteDataService._sWebWithTime[] arrWebWithTime;
        SPSiteDataService._sListWithTime[] arrListWithTime;
        SPSiteDataService._sFPUrl[] arrUrls;
        string roles; string[] roleUsers; string[] roleGroups;
        siteDataWS.Url = webPath + "/_vti_bin/sitedata.asmx";
        uint i = siteDataWS.GetWeb(out webMetaData, out arrWebWithTime, out arrListWithTime, out arrUrls, out roles, out roleUsers, out roleGroups);
        Guid g = new Guid(webMetaData.WebID);
        return g;
    }
 
 


My Code below will change the ContentType of your Document and also some extra Metadata. 
Now it is posible that you would not want to change the ContentType,
 so if not, put the call in comment.
The reason why you need the change of your ContentType and 
the rest of your Metadata in a different XML update block
is because by changing the ContentType, other Columns/MetaData becomes at your position, 
as examle your colums that you have created in your ContentType 
are not at your position before you have changed your ContentType. 
Only the default Columns are available.


    // Call to the function for update file metadate
 bool ret = UpdateFileDataViaBatch(sID, sSiteUrl, sSiteList, Array_FileMetaData);   

 /* --  Update File columns in Sharepoint List-- */
    private Boolean UpdateFileDataViaBatch(string sId, string sSiteUrl, string sSiteList, DataTable ExtraFileMetaData)
    {
        Boolean retcode = true;
        XmlDocument xmlDoc = new XmlDocument();
        XmlElement elBatch = xmlDoc.CreateElement("Batch");
        elBatch.SetAttribute("OnError", "Continue");
        elBatch.SetAttribute("ViewName", GetViewId(sSiteUrl, sSiteList, ""));
        try
        {
            string strBatch = "";
            int id = 0;
            if (ExtraFileMetaData.Rows.Count > 0)
            {
                DataRow drFileInfo = ExtraFileMetaData.Rows[0];
    // call for creating your xml-element for changing the contentType... If you don't need to change it, put it in comment
                strBatch = changeContentType(id,drFileInfo,  sId);
    // call for creating you xml-element for updating your document with extra info.
                strBatch = strBatch + createXmlUpdateBatch(++id, drFileInfo, sId);
            }
            elBatch.InnerXml = strBatch;
            Boolean retcodeUpdSPList = UpdateSPList(sSiteUrl, sSiteList, elBatch);
        }
        catch { }
        return retcode;
    }

    private string changeContentType(int id, DataRow drFileInfo, string sID)
    {
        String strBatch = "";
        strBatch = strBatch + "";
        strBatch = strBatch + "" + sID + "";
        strBatch = strBatch + "" + replaceSpecialChars(drFileInfo["ContentType"].ToString()) + "";
        strBatch = strBatch + "";
        return strBatch;
    }
    private string createXmlUpdateBatch(int id,  DataRow drFileInfo, string sID)
    {
        String strBatch = "";
        strBatch = strBatch + "";
        strBatch = strBatch + "" + sID + "";
        strBatch = strBatch + "" + replaceSpecialChars(drFileInfo["ExtraFileMetaData1"].ToString()) + "";
        strBatch = strBatch + "" + replaceSpecialChars(drFileInfo["ExtraFileMetaData2"].ToString()) + "";
        strBatch = strBatch + "";
        return strBatch;
    }

    private string replaceSpecialChars(string mystring)
    {
        mystring = mystring.Replace("&", "&");
        mystring = mystring.Replace("<", "<");
        mystring = mystring.Replace(">", ">");
        mystring = mystring.Replace("\"", """);
        mystring = mystring.Replace("'", "'");
        return mystring;
    }

 
 

At least you also need the function for the real update... 
otherwise you have your batch XML document, 
but your data isn't updated yet.
So you also need my function below for executing your 
Batch Element by using the Lists.asmx webservice.


 /* -- Update SharePoint List -- */
    private Boolean UpdateSPList(string sSiteUrl, string sSiteList, XmlElement elBatch)
    {
        Boolean retcode = true;
        SPListsService.Lists list = new SPListsService.Lists();
        list.Url = sSiteUrl + "/_vti_bin/lists.asmx";
        if (bDefaultLogon)
            list.Credentials = System.Net.CredentialCache.DefaultCredentials;
        else
        {
            list.PreAuthenticate = true;
            list.Credentials = new System.Net.NetworkCredential(sTechnicalUser, sTechnicalPw, sDomain);
        }
        try
        {
            XmlNode ndReturn = list.UpdateListItems(sSiteList, elBatch);
            // Instantiate a new XML document object to hold the return value(s) 
            XmlDocument xmlResult = new XmlDocument();
            xmlResult.LoadXml(ndReturn.OuterXml);
            // SharePoint XML always uses a fixed namespace; you'll need your own NamespaceManager object to parse the return values 
            XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlResult.NameTable);
            nsMgr.AddNamespace("sp", ndReturn.NamespaceURI);
            XmlNode ndRoot = xmlResult.SelectSingleNode("sp:Results", nsMgr);
            // Find the ErrorCode node, which exists for all operations regardless of status. 
            XmlNodeList nlResults = ndRoot.SelectNodes("//sp:Result/sp:ErrorCode", nsMgr);
            // Loop through the node collection and find each ErrorCode entry 
            foreach (XmlNode ndResult in nlResults)
            {
                // Check the value of the node to determine its status 
                if (ndResult.InnerText != "0x00000000")
                {
                    XmlNode ndError = ndResult.NextSibling;
                    string sError = ndError.InnerText;
                    // Set the value of string variable to hold the error code 
                    // If you want to trip the Try…Catch, throw and exception of whatever type suits you 
                }
                else
                {
                    // put your code on success... whatever you want 
                }
            }
        }
        catch (Exception e)
        {
            retcode = false;
        }
        finally
        {
            list.Dispose();
        }
        return retcode;
    }
 
 
 

If you have questions, don't hesitate to contact me... 
I only use the webservices to be undependent of the environment
to run the code on... So it hasn't to be on a sharepoint Environment !
If there are problems with my code, please let me know... 

Upload File from a networkdrive into a Sharepoint Library


Last week I wrote some code for migrating a networkdrive folder content into a SharePoint Library. 
You cannot use the Lists.asmx for uploading the documents because this webservice does not provide
the functionality for uploading files. 
But after a little bit of research I found quickly that you can use the copy.asmx webservice. 
Normally it is used for copying files between two SharePoint Libraries but it also 
work for copying files from a networkdrive into a SharePoint Library.

So you can use the code behind for copying a file on a network drive in a Sharepoint Library.



        SPCopyService.Copy copy = new SPCopyService.Copy();
  copy.Url = sSiteUrl + "/_vti_bin/copy.asmx";
        try
        {
            if (bDefaultLogon)
                copy.Credentials = System.Net.CredentialCache.DefaultCredentials;
            else
            {
                copy.PreAuthenticate = true;
                copy.Credentials = new System.Net.NetworkCredential(sTechnicalUserID, sTechnicalUserPW, sTechnicalUserDomain);
            }

            string destinationUrl = sSharePointDestinationFolder + "/" + sfilename;
            // destinationUrl = SiteUrl + "/" + SiteLibrary + "/" + OptionalFolder + "/" + filename.Extention
            string[] destinationUrls = { destinationUrl };
            SPCopyService.FieldInformation info1 = new SPCopyService.FieldInformation();
            info1.DisplayName = sfilename;
            info1.InternalName = replaceSpecialChars(sfilename);
            info1.Type = SPCopyService.FieldType.Text;
            info1.Value = replaceSpecialChars(sfilename);
            SPCopyService.FieldInformation[] info = { info1 };
            SPCopyService.CopyResult copyResult = new SPCopyService.CopyResult();
            SPCopyService.CopyResult[] copyResults = { copyResult };
            //Reading the document contents in to stream
            FileStream strm = new FileStream(sFilePath + @"\" + sfilename, FileMode.Open, FileAccess.Read);
            byte[] fileContents = new Byte[strm.Length];
            byte[] r = new Byte[strm.Length];
            int ia = strm.Read(fileContents, 0, Convert.ToInt32(strm.Length));
            strm.Close();
            // call the webservice procedure CopyIntoItems       
            uint result = copy.CopyIntoItems(sfilename, destinationUrls, info, fileContents, out copyResults);
            // Check for Errors:     
            foreach (SPCopyService.CopyResult copyRes in copyResults)
            {
                string msg =  "====================================" +
                "\n " + "SharePoint Error:" +
                "\n " + "              Url: " + copyRes.DestinationUrl +
                "\n " + "              Error Code: " + copyRes.ErrorCode +
                "\n " + "              Message: " + copyRes.ErrorMessage +
                "\n " + "====================================";
                if (copyRes.ErrorCode.ToString().Equals("Success"))
                {
                    // code for success if needed...
                }
                else
                {
                    // code for a failure if needed...
                }
                Console.WriteLine(msg);
            }
        }
        catch (Exception e)
        {
            iFailedUpdatedRecords++;
            Console.WriteLine("   Error on upload...");
        }
        finally {
            copy.Dispose();
        }
 

 

donderdag 2 augustus 2012

Custom Webpart Page with Attachments


What a day, searching for several hours to found out how it come's 
that the attachment functionality didn't work at a custom aspx webpart page.
After searching and searching I found the solution. 
You may not use New/Edit/Display Form directly from your list, but use it via the Custom List Form. 
Otherwise the Attachment functionality will not be provided and I hadn't found
a way to let it functioning. 
So far even that I didn't got an error message anymore and that the attachment 
wasn't saved into your item.

So how to do it... Follow these steps:
 
1.       Create a custom list.

2.       Open that site in SharePoint designer, 
         and create a new web part page in Site Page, 
         then open it in advanced mode.

3.       Then new web part page is opened. 
         Click Insert > New Item Form > Custom List Form > 
         Select that custom list to use for form > 
         Select Item content type to use for form > 
         Select New item form to create > 
         Keeping Show standard toolbar is checked > Click OK.

Then I can get a SharePoint:FormToolBar with Attach File and Spelling options. 
Then save and preview this page, you can see the toolbar in the ribbon is enabled.
Go back to the web part in SharePoint Designer, 
and delete the SharePoint:FormToolBar control, 
save the page and preview it, we can see, 
the toolbar with Save, Cancel, Paste and Attach File buttons is disabled.


 

donderdag 5 juli 2012

Workflow with Impersonation Step and his Publisher


Important to know if you use an impersonation step in a workflow, and you change the rights of
the user that has published the workflow, the workflow will start failing to execute his code. 
Last week we cleaned up some user accounts of employees that din't work any more in the company
and suddenly we remarked that certain workflows were not working any more. 

The workflow impersonates the "Author" from a SharePoint point of view for that step.
So if she somehow looses her rights if'll fail.
The reason I'm putting "Author" in quotes is that in my option it's a wrong term, 
because in fact it's the last publisher's identity which is used.
So if the workflow fails due to the "Author" loosing the rights any user with sufficient rights 
can go in and publish the workflow and it'll start working again.

In fact you can do this as a general rule and have some "service account" 
which is used to publish all workflows so they'll run with the permissions of that account 
which should then not be a real person.