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: http://dipaktele.blogspot.de/2012/07/two-lookup-field-with-more-than-20.html

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 = opt.id,
strName = opt.name,
strMatch = "",
strMatchVal = "",
strOpts = ctrl.choices,
rgopt = strOpts.split("|"),
offSet = $(ctrl).position(),
x = offSet.left,
y = offSet.top 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=\"" ctrl.id "\" name=\"" strName "\" id=\"" strId "\"" strHandler;
}
else {
strOptHtml = "<select class=\"ms-lookuptypeindropdown\" tabIndex=\"-1\" ctrl=\"" ctrl.id "\" 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;
else
hid.value = "0";
if (iMatch != 0 || rgopt[1] != "0")
return strMatch;
else return "";
}
}

_spBodyOnLoadFunctionNames.push("FilterChoice");
function EnsureSelectElement(ctrl, strId) {
$("#" strId).remove();
var select = document.getElementById(strId);
if (select == null) {
select = document.createElement("SELECT");
ctrl.parentNode.appendChild(select);
select.outerHTML = "<select id=\"" strId "\" ctrl=\"" ctrl.id "\" 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.



Donnerstag, 15. November 2012

New SharePoint 2013 / 15 cmdlets for Powershell you shouldn't use...

I just browse the web for new things that come with SharePoint 2013 / 15. Now I'm landed on the technet-page for all new Powershell-Cmdlets:

http://technet.microsoft.com/library/ff678226%28office.15%29.aspx

A funny thing are a few new cmdlets you explicitely should avoid to use but the funniest imo are Add-SPSocialAppPermissions and Remove-SPSocialAppPermissions.

So if you belong to the 1% of people with a completely working user profile synchronization service without any issues and you are bored of all the perfectly working sharepoint environment, you should definitively run this cmdlet, because "The use of the Add-SPSocialAppPermissions cmdlet can result in profile synchronization connections that are unusable and non-editable".

More cmdlets you called upon not to use are:

- New-SPBECWebServiceApplicationProxy
- Remove-SPSocialAppPermissions
- New-SPBECWebServiceApplicationProxy

If you have any idea, why Microsoft creates such cmdlets not intended to use by people, please leave a comment :-D

Mittwoch, 4. April 2012

ArgumentException when setting SPUser to a UserField in SPListItem

A little hint for everyone that ever got an argument exception if he tried to set a SPUser-object to an userfield in a list item:

SPListItem listItem = list.Items.Add(listRootFolder.ServerRelativeUrl, SPFileSystemObjectType.File, null);
listItem["Owner"] = spUser; <-- if you step through the code while debugging, an argument exception may occur here 
listItem["Url"] = url; 
listItem["Title"] = title; 
listItem.Update();


In case of an argument exception, you have to check if the assigned spUser-object has been opened from the same parent web as the list.

Freitag, 17. Februar 2012

Bugfix for 'Walkthrough: Replacing a Button on the Server Ribbon'

There is an example how to replace a button on the server ribbon available in the MSDN with one issue: it doesn't work ... ;-)

But I found the error and commented it below in the Community Content-section. For completeness I'll post it in my blog again:

There exists no button with the ID 'Ribbon.Library.Actions.ConnectToClient.ReplacementButton' so a javascript-error is thrown in the browser. The part of the xml must changed to this line and then it works:

<CommandUIDefinition Location="Ribbon.Library.Actions.ConnectToClient">
  <Button Id="Ribbon.Library.Actions.ConnectToClient" Command="ReplacementButtonCommand" ...>


Just remove the string '.ReplacementButton' in the Id of the Button from the example and it works.

Dienstag, 4. Oktober 2011

Error occurs while trying to upgrade installed features of an upgraded solution

It’s way back since my last post, but at last I found something very useful to post.

During the process of developing on a SharePoint 2010 solution, to have clean presuppositions for testing and avoid phantom mistakes I very often create new site collections and delete them afterwards. A part of this process is also removing and adding newer versions of the solutions to the solution-store. So from time to time I was confronted with strange error messages when I tried to upgrade features to new versions.

I concretely tried to do it the Microsoft recommended way and used a modified version of the program, you can find here: http://msdn.microsoft.com/en-us/library/ff798298.aspx

The program that iterates over the SPWebApplication to find upgradable features sometimes fails with an error message that says that it can’t find a special SiteCollection: The site with the id D0D529C1-DC06-4DB0-A8A3-81466E1E75DB could not be found.
This is true because it has been previously deleted by me and is no longer available in Central Administration or anywhere else. It seems, there are still some SiteCollection-related artifacts in the content database stored that prevent a clean upgrading of the new versioned features.

Have a look at the Default TimerJobs,  the promisingly entry “Gradual Site Delete”, we perhaps can use to solve this issue with tools out of the box. By default it’s executed daily but we can change the cycle to earlier executions or even start it manually.

This blog described its functionality as “Deletes all the data from the host content database for all deleted site collections”. What a pitty, in my case, it doesn’t. The error remains.

Now it’s time to have a look at the content database. The name of this table looks encouraging: dbo.SiteDeletion

After opening the table, an entry appears that includes the evil SiteId, which seems to be the crux of the matter:


Luckily Microsoft gave us the method ForceDeleteSite on the SPContentDatabase-class.

If you write a really short program, for e.g. a console application that takes the Uri to get the SPWebApplication-object and the Guid of the corrupted SiteCollection, you can create a new entry to the database-table with a datetime of 1900-01-01.


Simply, there are only those two lines needed:

SPWebApplication spWebApplication = SPWebApplication.Lookup(new Uri("http://mywebapplication")); // insert the webapplication

spWebApplication.ContentDatabases[0].ForceDeleteSite(new Guid("D0D529C1-DC06-4DB0-A8A3-81466E1E75DB"), true, false); // insert the ID from the errormessage


After that, retry running the “Gradual Site Delete”-timerjob and – surprise – both entries are deleted.


If you retry upgrading your feature, no more annoying error message occurs.

Dienstag, 12. Juli 2011

Anonymous access prompted for login after click in web part area on publishing portal

Currently I'm experimenting with publishing portal and the ability to use it for publicfacing internet sites where anonymous access is allowed. In the site permissions I gave the anonymous users access to the entire website.
Afterwards I created a custom list named 'Test' and entered three simple items with just the title-field filled.

On the default.aspx of the publishing portal I added a ListVievWebPart to the 'Test'-list and published it.

Now if the anonymous user loggs in, he sees the default publishing page with that LVWP and three items in it.But if he clicked on an item, the login-popup appeared. Also if I tried to break the list-permission-inheritance from the web, the list items would open but the first click in the webpart resulted in that nasty login-popup.

So what to do? A lot of pages, blogs and forums on the internet suggest to deactivate a hidden feature called 'ViewFormPagesLockDown' to allow anonymous users to have access to list-items.
Another approach I found on several pages was to remove the coupling to the code-behind in the file wpribbon.aspx on the _layouts-folder directly to workaround the other problem.

I didn't like neither the first nor the second way. The one kills the security concept, the other transgesses the rules of Microsoft by changing files directly on the server.

So I grabbed good old SharePoint Manager 2010 and examined the 'Test'-List where I detected the AnonymousPermMask64-Attribute that had this string set by default: ViewListItems, ViewVersions, Open, ViewPages, UseClientIntegration.

This had to be the key to solve the problem... I went to the MSDN-Article describing all SPBasePermissions-enum-values and found... ViewFormPages (View forms, views, and application pages, and enumerate lists).

Okay, rest was easy... If there is a feature activated on publishing-portal called ViewFormPagesLockDown that prevents the anonymous user from entering formpages, then the basepermission ViewFormPages perhaps grants access for the element.

So I programmatically broke the inheritance of the list-permissions and reset the spbasepermissions to the AnonymousPermMask64-Attribute as you can see in the following code-segment and everything worked as I imagined:



SPList spList = spWeb.GetList("/Lists/Test");

spList.BreakRoleInheritance(false);
spList.AnonymousPermMask64 =
 SPBasePermissions.ViewListItems |
 SPBasePermissions.ViewVersions |
 SPBasePermissions.Open |
 SPBasePermissions.ViewPages |
 SPBasePermissions.UseClientIntegration |
 SPBasePermissions.ViewFormPages;

spList.Update();


Donnerstag, 7. Juli 2011

Add customaction to ribbonbar in sharepoint 2010 wiki page

Just a simple copy & paste of a custom action that adds a button to the ribbonbar in a publishingpage in the pageslist of a wiki-site:

<CustomAction Id="WikiDocumentExportCustomizationPageView" Rights="Contribute" Location="CommandUI.Ribbon" Sequence="30" Title="Wiki-Document-Export"> 
 <CommandUIExtension> 
  <CommandUIDefinitions> 
   <CommandUIDefinition Location="Ribbon.WikiPageTab.Share.Controls._children"> 
    <Button Id="WikiDocumentExportCustomizationPageView.Button" Command="ExportCurrentWikiDocumentFile2" CommandType="General" Description="Export Wiki-Document" TemplateAlias="o1" Sequence="30" Image32by32="/_layouts/images/wiki/pdf_export.png" LabelText="Export Wiki-Document"/> 
   </CommandUIDefinition> 
  </CommandUIDefinitions> 
  <CommandUIHandlers> 
   <CommandUIHandler Command="ExportCurrentWikiDocumentFile2" CommandAction="javascript:ExportCurrentWikiDocumentFile('{SiteUrl}');"/>
  </CommandUIHandlers> 
 </CommandUIExtension> 
</CustomAction>

Pay attention that this button is only visible to users with contribute-rights on the list.
ExportCurrentWikiDocumentFile is a custom external javascript-function that implements the code behind the button. You must define your own function here...