Posts mit dem Label Lookup werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Lookup werden angezeigt. Alle Posts anzeigen

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, 12. März 2009

Mit SPQuery nach Items in DocumentLibrary suchen

Eine Anforderung, die es gestern umzusetzen galt, bestand darin, nach Items (in diesem speziellen Falle Subfoldern) in einer DocumentLibrary zu suchen und diesen Wert einem speziellen Lookup-Feld als Standardauswahl anzugeben. Die normale SPQuery-Abfrage sucht leider nur auf der obersten Ebene. Abhilfe schafft hier das ViewAttribute Scope="RecursiveAll". Dieses sucht rekursiv in der DocumentLibrary und zwar nach allem, also Dokumenten und Foldern.

Die Code-Abfrage lautet dann so:


SPFieldLookup spFieldLookup = (SPFieldLookup)spListItem.Fields[property];
SPList spLookupList = spListItem.Web.Lists[new Guid(spFieldLookup.LookupList)];

SPQuery query = String.Format("{1}", spLookupFieldName, value);
query.ViewAttributes += " Scope=\"RecursiveAll\"";
SPListItemCollection results = spLookupList.GetItems(query);


spListItem und property sind das SPListItem welches ich updaten möchte und der Name des Lookup-Feldes, welches benutzt werden soll. value ist der Foldername, nach dem gesucht werden soll.

Mehr zum dem Attribut "Scope" gibt es auf http://msdn.microsoft.com/en-us/library/ms438338.aspx.

Montag, 14. Juli 2008

Probleme beim Updaten von SPFieldLookup-Objekten

Ein Lookup-Feld programmatisch auf eine Liste verlinken ist ein Kinderspiel: einfach die Attribute LookupList mit der GUID der auswählbaren Liste und LookupWebId mit der jeweiligen WebID belegen. Fertig!...

Von wegen! Das ganze funktioniert nämlich nur, solange der aktuelle Benutzer Site Collection Administrator ist.

Wird das von einem User, der nicht Site Collection Administrator ist, angestoßen, kracht es und der Aufruf
spFieldLookup.LookupWebId = spRootWeb.ID;

wirft eine System.UnauthorizedAccessException: Access is denied, bzw. Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)

Eine Lösung für das Problem bietet das SDK durch den Einsatz der Methode SPSecurity.RunWithElevatedPrivileges, der ein delegate übergeben werden kann in dem dann der jeweilige Code ausgeführt wird.

Laut SDK wird die spezifizierte Methode mit FULL CONTROL-Rechten ausgeführt, egal, ob der User diese Rechte besitzt oder nicht.

Ein Beispiel dafür findet sich in der MSDN: *click*

Bei uns sieht das dann in etwa so aus (ich habe ein paar Code-Zeilen der Übersichtlichkeit halber ausgelassen):

SPSecurity.RunWithElevatedPrivileges(delegate()
{
SPWeb site = (SPWeb)properties.Feature.Parent;
SPSite siteColl = site.Site;

// get current site
using (SPSite elevatedSiteColl = new SPSite(siteColl.ID))
{
// get current web
using (SPWeb spCurrentWeb = elevatedSiteColl.OpenWeb(site.ID))
{
// get rootweb from this web's parent site
using (SPWeb spRootWeb = (SPWeb)spCurrentWeb.Site.RootWeb)
{
...anderer code, der z.b. die liste, auf die das lookup-feld referenzieren soll, einlist...

SPFieldLookup spFieldLookup = (SPFieldLookup)spList.Fields.GetFieldByInternalName(lookupInfo.FieldInternalName);
spFieldLookup.LookupWebId = spRootWeb.ID;
spFieldLookup.LookupList = listGuid.ToString();
spFieldLookup.Update();
}
}
}
}
});

Ganz wichtig ist, dass die aktuelle SiteCollection, neu instanziiert wird. Ansonsten wird das SPSite-Objekt des aktuellen Users benutzt was ja ausserhalb des SPSecurity.RunWithElevatedPrivileges-Kontexted generiert wurde und daher keine Berechtigung hat, das Lookup-Feld zu verändern.

Meiner Meinung nach sollte diese Methode wirklich nur an Stellen benutzt werden, an denen es keine Alternative gibt, da sie das Sicherheitskonzept völlig aushebelt und dem Code, der vom aktuellen Benutzer ausgeführt wird, viele Rechte gewährt...