Mittwoch, 10. September 2014

Adding ECB directly to document libraries

Recently I used to look for a possibility to change the SharePoint 2013 ECB (Edit Control Block) from CallOut-Popup back to direct call as known from SharePoint 2010.

I found this way as a quick work around:

But by this solution, the drag-and-drop area is not rendered anymore and the old-fashioned "Add new item"-link below the list returns.

So I spent a little more time with googeling and found this page which adds an additional icon to click which displays the ECB directly:

The site describes the How-To and provides a sandboxed-solution for directly adding to the solutions-gallery of the SiteCollection. After activating everything to do is finished.

As you can see, the standard behaviour is not touched but extended by a new functionality:

It depends to the customer's wish if the three-dots-button now should be hidden (needs some additional work) or both buttons should remain.

Dienstag, 8. Juli 2014

SPFile.Publish() writes 'System Account' to 'ModifiedBy'-column instead of current user

I had the following scenario:

Documents that are dragged&dropped in a documentlibrary should be enriched with additional metadata and after that they should be published.
The document library has versions enabled with minor versions.

All action takes place in a SPItemEventReceiver, the details for that I'll omit for now.

Now when it came to do spFile.Publish(), after that the "ModifiedBy"-Column has 'System Account' instead of the current user and there seemed no way to set it manually. Neither the Publish-Method had a parameter to set the current user, nor SPListItem.SystemUpdate or SPListItem.UpdateOverwriteVersion worked:

- SystemUpdate won't save changes to CreatedBy/Created or ModifiedBy/Modified-fields.
- UpdateOverwriteVersion would work but creates a minor version (1.1 in my case)

But then I found a solution here.

I couldn't believe that and tried it out and it really worked! The post said, the SPSite-Object should be opened by adding the SPUserToken to the constructor and then SPFile.Publish() will save the user as last modifier. To show a concrete minimal example:

using (SPSite spSite = new SPSite(properties.SiteId, properties.OriginatingUserToken)) {
 using (SPWeb spWeb = spSite.OpenWeb(serverRelativeWebUrl)) {
  SPListItem spListItem = spWeb.GetListItem(properties.Web.ServerRelativeUrl); // make sure, the file is checked in before or try to acces or you get a file-not-found-error here 
  SPFile spFile = spListItem.File; 
  ... add more data to spListItem... 
  ... check for enabled versions, etc... 
  spFile.Publish(); // writes the SPUser related to the SPUserToken from properties.OriginatingUserToken to 'ModifiedBy'-column, if SPSite is instantiated with new SPSite(properties.SiteId) only, 'System Account' is used

properties is the default SPItemEventProperties-object from the SPItemEventReceiver.

Pay attention, the "Modified"-Column (DateTime) changes to DateTime.Now. But this was not in the focus of my requirement.

I always thought, using new SPSite(Guid) would open  the SPSite in current user's context but that's not true as a closer look by using Reflector shows. Have a look at two of the default constructors from SPSite-class:

public SPSite(System.Guid id) : this(id, SPFarm.Local, SPUrlZone.Default, SPSecurity.GetDefaultUserToken())

public SPSite(System.Guid id, SPUserToken userToken) : this(id, SPUrlZone.Default, userToken)

Do you note the difference? :-)   

Montag, 30. Juni 2014

Avoiding conflicts if Custom_AddDocLibMenuItems-function is implemented multiple

Today I found a really helpful tip how to add custom items to the SharePoint Edit Control Block (ECB) without getting in conflict with other used Custom_AddDocLibMenuItems-function-calls (for e.g. from another deployed wsp-solution or script added in a Content Editor WebPart (in SharePoint 2010) or ScriptEditor-WebPart (in SharePoint 2013).
You can find it here in Stuart Roberts' blog:

Montag, 25. November 2013

A potentially dangerous Request.Form value was detected from the client (ctl00$PlaceHolderMain$...

Recently I got the following errormessage in SharePoint 2013 after trying to save an edited publishing page. Also it was not possible to add any webpart.
Some googling resultet in different web.config-modifications, some said to set requestValidationMode="4.0" to requestValidationMode="2.0" in <system.web>-node, others recommended to set validateRequest="false"in <pages>-node. In fact for it only worked after I did both changes. So search for

 <httpRuntime requestValidationMode="4.0" />

and set requestValidationMode to "2.0" and search for

<pages enableSessionState="false" enableViewState="true" enableViewStateMac="true" validateRequest="true" clientIDMode="AutoID" pageParserFilterType="Microsoft.SharePoint.ApplicationRuntime.SPPageParserFilter, Microsoft.SharePoint, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c" asyncTimeout="7">
        <remove namespace="System.Web.UI.WebControls.WebParts" />
        <add tagType="System.Web.UI.WebControls.SqlDataSource, System.Web, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" mappedTagType="Microsoft.SharePoint.WebControls.SPSqlDataSource, Microsoft.SharePoint, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
        <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        <add tagPrefix="spsswc" namespace="Microsoft.Office.Server.Search.WebControls" assembly="Microsoft.Office.Server.Search, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

and set validateRequest to "false".

Donnerstag, 2. Mai 2013

Get current versions for activated features (not feature definitions) in a web or site collection

Recently I wrote a PowerShell-Script which writes the current version-numbers of web-features specified by a name-filter into a file. These versions are not from the feature-definition but from the feature activated in a web.

An example for this could be the following:
On a SharePoint-server a feature named custom_feature_1 was installed which has a version of because it's been updated a few times during the lifecycle of the appication.
Because the rootweb was created first, the feature's version is there. A newer subweb below has been created after the feature was updated and there the feature's version is Now, after the third update, another web will be created where the feature now has a version of

So how can you easily get the version of the feature activated in one of these webs?

You can use this PowerShell-script to get all information about your features:

get-spweb http://customsitecollection/customweb |% {$_.Features} | where-object {$_.Definition -ne $null} | where-object {$_.Definition.DisplayName.StartsWith("custom")} |% {new-object psobject -Property @{Id = $_.DefinitionId; Version = $_.Version; DisplayName = ($_ | select -ExpandProperty Definition).DisplayName; Scope = ($_ | select -ExpandProperty Definition).Scope; }} | format-table -Property * -Autosize | Out-String -Width 120 | out-file custom_web_features.txt

Just copy these lines and replace the bold-marked parts by your own needs:
http://customsite/customweb - The full path to the web you want the feature-versions from
custom - The name that the features starts with in SharePoint-Root features-folder
custom_web_features.txt - The name of the file that's created with information

The content of the file should look like this:

You can reuse the script for site-scoped features too. Just replace get-spweb by get-spsite and a valid URL to a sitecollection.

Dienstag, 23. April 2013

"Active Deployment Configuration"-setting in Visual Studio 2012 is always "Default"

There is a bug in current Visual Studio 2012 which always sets the "Active Deployment Configuration" to "Default" after re-opening the program/solution.
If you set "No Activation", this is correctly stored in the .csproj-file but VS ignores this and preselectes "Default" on opening:

I reported this to Microsoft and finally got an answer that said I should install the Microsoft Office Developer Tools which were released in March 2013 (can be found here). After installing, the bug indeed was fixed and now the configuration is loaded correctly:

Personally I think, Microsoft should fix this bug in a Visual Studio update but not in an separate downloadable add-on because it's a bug by Visual Studio itself.

Freitag, 23. November 2012

Issues with Lookup-Fields with each more than 20 items

If you have multiple lookup-fields in your form which reference to more than 20 items (hardcoded limit in the LookupField-Control!), you may notice to the display and behaviour of your control:

- The rendering of the field switches from select-tag to an input-tag and an img-tag. A click on the image shows a layer with the selectfield.
- This only occures to Internet Explorer, not to Firefox.

The naughty thing on this is the issue, that the selectfield-layer appears below the first-clicked lookup-field, no matter, which lookupfield you click afterwards.

Click on the first lookup:

Click on the second lookup:

This bug can be fixed by override a function in the core.js that must be loaded immediatly in the masterpage. A good piece of working code I found here today:

I copied the code found on this page and added it here:

function FilterChoice(opt, ctrl, strVal, filterVal) {
if (typeof (opt) != "undefined") {
var i,
cOpt = 0,
bSelected = false,
strHtml = "",
strId =,
strName =,
strMatch = "",
strMatchVal = "",
strOpts = ctrl.choices,
rgopt = strOpts.split("|"),
offSet = $(ctrl).position(),
x = offSet.left,
y = 15,
strHidden = ctrl.optHid,
iMac = rgopt.length - 1,
iMatch = -1,
unlimitedLength = false,
strSelectedLower = "";
if (opt != null && opt.selectedIndex >= 0) {
bSelected = true;
strSelectedLower = opt.options[opt.selectedIndex].innerText;
for (i = 0; i < rgopt.length; i = i 2) {
var strOpt = rgopt[i];
while (i < iMac - 1 && rgopt[i 1].length == 0) {
strOpt = strOpt "|";
i ;
if (i < iMac - 1) {
strOpt = strOpt rgopt[i 1];
i ;
var strValue = rgopt[i 1];
var strLowerOpt = strOpt.toLocaleLowerCase();
var strLowerVal = strVal.toLocaleLowerCase();
if (filterVal.length != 0)
bSelected = true;
if (strLowerOpt.indexOf(strLowerVal) == 0) {
var strLowerFilterVal = filterVal.toLocaleLowerCase();
if ((strLowerFilterVal.length != 0) && (strLowerOpt.indexOf(strLowerFilterVal) == 0) && (strMatch.length == 0))
bSelected = false;
if (strLowerOpt.length > 20) {
unlimitedLength = true;
if (!bSelected || strLowerOpt == strSelectedLower) {
strHtml = "<option selected value=\"" strValue "\">" STSHtmlEncode(strOpt) "</option>";
bSelected = true;
strMatch = strOpt;
strMatchVal = strValue;
iMatch = i;
else {
strHtml = "<option value=\"" strValue "\">" STSHtmlEncode(strOpt) "</option>";
cOpt ;
var strHandler = " onclick=\"HandleOptDblClick()\" onkeydown=\"HandleOptKeyDown()\"";
var strOptHtml = "";
if (unlimitedLength) {
strOptHtml = "<select tabIndex=\"-1\" ctrl=\"" "\" name=\"" strName "\" id=\"" strId "\"" strHandler;
else {
strOptHtml = "<select class=\"ms-lookuptypeindropdown\" tabIndex=\"-1\" ctrl=\"" "\" name=\"" strName "\" id=\"" strId "\"" strHandler;
if (cOpt == 0) {
strOptHtml = " style=\"display:none;position:absolute;z-index:2;left:" x "px;top:" y "px\" onfocusout=\"OptLoseFocus(this)\"></select>";
else {
strOptHtml = " style=\"position:absolute;z-index:2;left:" x "px;top:" y "px\"" " size=\"" (cOpt <= 8 ? cOpt : 8) "\"" (cOpt == 1 ? "multiple=\"true\"" : "") " onfocusout=\"OptLoseFocus(this)\">" strHtml "</select>";
opt.outerHTML = strOptHtml;
var hid = document.getElementById(strHidden);
if (iMatch != 0 || rgopt[1] != "0")
hid.value = strMatchVal;
hid.value = "0";
if (iMatch != 0 || rgopt[1] != "0")
return strMatch;
else return "";

function EnsureSelectElement(ctrl, strId) {
$("#" strId).remove();
var select = document.getElementById(strId);
if (select == null) {
select = document.createElement("SELECT");
select.outerHTML = "<select id=\"" strId "\" ctrl=\"" "\" class=\"ms-lookuptypeindropdown\" name=\"" strId "\" style=\"display:none\" onfocusout=\"OptLoseFocus(this)\"></select>";
FilterChoice(select, ctrl, ctrl.value, "");
return document.getElementById(strId); ;

After that, the select is displayed correctly:

Please be sure to include there functions in your own js-file that's loaded after the core.js and avoid modifying the original core.js.