Tuesday, July 8, 2014

SharePoint 2013 - Filtrare per Eventi Ricorrenti senza i duplicati

La creazione di un evento ricorrente genera tante istanze dell'evento creato, quante sono le date configurate per la ricorrenza.

Esiste la possibilità di visualizzare tutti gli eventi, con le ricorrenze, tramite la vista Calendario e la vista Eventi Correnti.

La vista Tutti gli Eventi mostra invece gli eventi ricorrenti "Parent", quindi i soli item effettivamente inseriti, con la regola di ricorrenza.

Quando modifichiamo una istanza di una ricorrenza, possiamo decidere se modificare tutta la serie o il singolo elemento: scegliendo l'ultima opzione, viene creato un nuovo item (evento), con un suo id, con le stesse regole della ricorrenza, ma "slegato" dalla ricorrenza "Parent" originaria (pur mantenendo un collegamento interno).

Se vogliamo visualizzare solo gli eventi "Parent" originali, senza gli eventi modificati a partire dalla serie (come descritto sopra), dobbiamo filtrare la web part modifiicando, tramite il Designer, la CAML Query.

Identifichiamo il tag <View> e inseriamo nel tag <Query>, il seguente codice:

<Where><Neq><FieldRef Name="EventType"/><Value Type="Integer">4</Value></Neq></Where>

Questo esclude dalla visualizzazione gli eventi con EventType = 4, cioè quelli generati dalla modifica di una singola ricorrenza.

Wednesday, June 18, 2014

SharePoint - Spostare Document Set di grandi dimensioni

Per lo spostamento di Document Set da una libreria ad un'altra occorre tenere conto del contesto in cui si implementa la procedura di spostamento, in particolare se esiste la possibilità che i Document Set contengano documenti di dimensioni superiori ai 10 Mb.

Export/Import

In rete è la soluzione più consigliata, ma attenzione: per file di grandi dimensioni (superiori a 10Mb), funziona solo nel caso di Event Receiver (o Console Application).
Utilizzato in un Timer Job si riceve un errore decisamente incomprensibile, in fase di Export: "Unable to determine the identity of domain".
Questo messaggio ci ha fatto penare un bel po' ed è riferito ad una classe utilizzata dal package di gestione dello stream in fase di esportazione del Document Set (class IsolatedStorage).

//get Document Set Source
DocumentSet documentSet = DocumentSet.GetDocumentSet(itemToMove.Folder);
//get Target List
SPList targetList = web.Lists[docSetListTargetName];
//get content type id for target list 
SPContentTypeId contentTypeId = targetList.ContentTypes["Custom Document Set Name"].Id;
//export in a stream
byte[] documentSetData = documentSet.Export();
//get document set name
string documentSetName = documentSet.Item.Name;

//get folder target
SPFolder targetFolder = targetList.RootFolder;

//Assign all properties
System.Collections.Hashtable propertiesParent = itemToMove.Properties;

//Create new Document Set 
DocumentSet docSet = DocumentSet.Import(documentSetData, documentSetName, targetFolder, contentTypeId, propertiesParent, currentWeb.CurrentUser);

//then go to delete source document set


Folder.MoveTo

Utilizzando questo metodo viene spostato il Document Set con il suo contenuto, qualsiasi peso abbia, come fosse una Cartella (SPFolder) e successivamente, viene convertita in Document Set Content Type.

La cosa buona è che funziona anche per i Timer Job quindi senza problemi di size.

//get Document Set Source
DocumentSet documentSet = DocumentSet.GetDocumentSet(itemToMove.Folder);
//get Target List
SPList targetList = web.Lists[docSetListTargetName];
//get content type id for target list 
SPContentTypeId contentTypeId = targetList.ContentTypes["Custom Document Set Name"].Id;
//set Url of new Document Set in target list
string moveUrl = targetList.RootFolder + "/" + itemToArc.Folder.Name;
//move document set as folder
itemToMove.Folder.MoveTo(moveUrl);
//check new Document Set
SPFolder newDocset = currentWeb.GetFolder(moveUrl);
if (newDocset.Exists)
{
    //Update all fields from source
    foreach (SPField f in itemToMove.Fields)
    {
         if (!f.ReadOnlyField)
         {
              newDocset.Item[f.StaticName] = itemToMove[f.StaticName];
         }
    }
    //IMPORTANT: convert Folder to Document Set 
    newDocset.Item["ContentTypeId"] = contentTypeId;
    newDocset.Item["HTML File Type"] = "SharePoint.DocumentSet";                                                
    //then update
    newDocset.Item.Update();
}
else
{
   // Failed moving the docset or setting ... 
}

//no need to delete source Document Set


Tuesday, June 17, 2014

SharePoint - Il Check-Out e i Documenti Fantasma!

Lo scenario spettrale nel quale i Documenti scompaiono alla vista di tutti, compreso il System Account, ha una spiegazione (quasi logica) e anche una soluzione!

Il Cliente segnala l'impossibilità di salvare un nuovo Documento (nuovo), creato con Word, con un dato nome.
L'utente X accede alla Document Library su cui è impostato check-in/out per modifica.
Clicca su Nuovo Documento, apre Word, modifica il file e, per qualche ragione, NON esegue Check-in.
Chiude Word, oppure si pianta (non raro), o il browser va in errore (ancora meno raro), o qualsiasi altro motivo per cui non viene archiviato il file.

In questo scenario, gli altri utenti non vedono il documento. Nessuno. Solo l'utente X.

L'utente Y crea un nuovo file e lo vuole salvare, chiamandolo come il file creato (e non archiviato) dall'utente X.
MA riceve un messaggio: Non è possibile salvare il file perché l'utente X sta già modificando un documento con quel nome.

Il sistema dice che c'è quel file, ma non si vede e non possiamo sovrascriverlo né modificarlo perché lo fa già utente X. Neppure System Account lo vede, neppure con Power Shell..

L'utente X sembra l'unico che può vederlo.
L'utente X è l'unico che può cancellarlo.
L'utente X può risolvere il problema.

L'utente X è in maternità.

Microsoft ha previsto, fortunatamente (per loro), lo scenario descritto, fornendo la possibilità, nelle configurazioni della Document Library, di visualizzare i documenti che non hanno ancora una versione: ovvero sono stati creati, ma mai ne è stato eseguito un check-in.

Il Link Manage files which have no checked in version è abbastanza esplicativo.


Mostra i file non archiviati, senza versione:

Selezionando un documento e cliccando su Take Ownership of Selection, l'utente corrente ha la possibilità di prendere il possesso dei file, sostituirsi all'utente che non ha eseguito l'archiviazione, e modificare il file.


Risulta quindi, nella library, finalmente il file:



"SharePoint Mystery - The case of the missing documents" racconta la nostra storia
http://www.networkworld.com/article/2346678/microsoft-subnet/sharepoint-mystery--the-case-of-the-missing-documents.html


Wednesday, May 28, 2014

SharePoint - Usare Information Management Policy per l'invio di Notifiche di Scadenza


Definiamo un campo di tipo Date Time per immagazzinare il giorno per l’invio dell’alert, ad es “AlertDate”

Definire da SharePoint Designer, un Workflow, in creazione/modifica, denominato “Set Alert Date” che aggiorna il campo AlertDate con la scadenza desiderata.
Ad es, si vuole essere avvisati 7 giorni prima della scadenza (EndDate)?
Impostiamo AlerDate = EndDate – 8 giorni (poi vediamo perché 8 e non 7)

Definire un secondo Workflow, senza nessuna spunta (no manuale, no automatico) così non verrà visto da nessuno, se non l’admin
Chiamiamolo “Send Alert For End Date”
Il Worflow invia una mail ai destinatari da avvisare.
Eventualmente, possiamo fare in modo, dopo l'invio, di impostare AlertDate = AlertDate+1 (se AlertDate < EndDate), così da inviare mail anche nei giorni successivi fino alla scadenza.

Ora, navigare sulla lista/library interessata dall’alert ed entrare in List Settings > Information Management Policy.
Definiamo una Policy per l’Expiration, utilizzando le impostazioni seguenti:



NOTE:
Come Item Properties non è possibile (almeno su MOSS2007) utilizzare un campo calcolato.
Il +1 è necessario (non accetta 0): da qui il fatto che nel workflow aumentiamo di 8 giorni.

Salviamo.

Che cosa succede?
SharePoint ha un job predefinito che gira ogni sera (ma schedulabile a piacere) e che esegue il controllo su tutte le liste su cui è definita una Policy di Expiration.
Se incontra un item che risponde a questa Policy, controlla il campo definito per il controllo ("AlertDate"), se Today = AlertDate+1, avvia il Workflow indicato che invia la notifica (ed eventualmente, aggiorna AlertDate = ALertDate+1, così da scattare nuovamente il giorno successivo).

Se la notifica invita l'utente a cambiare la EndDate dell'item, il Workflow "Set Alert Date", va ad aggiornare l’Alert Date con la nuova data (EndDate-8gg) e ricomincia il giro.

NOTA IMPORTANTE:
Se si modifica il Workflow “Send Alert For End Date”, occorre riconfigurare la Policy, andando a selezionare la nuova versione del Workflow.
Se si elimina il Workflow, occorre controllare che la Policy non vada a prendere il primo Workflow che ha in elenco, visto che abbiamo tolto quello corrente...

Non ci resta che attendere il giro del job di Expiration Policy o forzarne l'esecuzione da Central Administration.


Friday, January 24, 2014

SharePoint 2010 - Target Audience non funzionante? Un semplice flag che risolve..

Diventare matti per la funzionalità di Target Audience su web part, navigation...

Il problema è che la Target Audience è correttamente configurata in ogni sua parte, vedo la possibilità di usarla ovunque, nella navigazione, ad esempio. Inserisco un Gruppo di SharePoint, salvo e verifico..
Verifico che non funziona e continua ad ignorare i miei settings.
Da Site Collection Administration, tutte le configurazioni erano corrette.
Allora ci siamo spostati sulla Central Administration, e abbiamo verificato che lo User Profile Service funzionasse correttamente.

Siamo andati in Application Management > Manage Web Applications, selezioniamo la Web Application in cui non funziona la Target Audience e clicchiamo su Service Connections.



Abbiamo trovato che era impostata a [custom], ma soprattutto che il check su User Profile Service Application era a false...
Lo abbiamo messo a true, flaggandolo.


Poi, andiamo in Application Management > Manager Service Applications e clicchiamo su User Profile Service Application



Clicchiamo su Start Profile Syncronization e.... attendiamo che giri.

Al termine, o anche prima, se siete fortunati, vedrete che la Target Audience funziona...

Finalmente!






Wednesday, January 22, 2014

SharePoint 2007 - Modificare il Subject delle mail di Notifica/Alert con proprietà dell'item con AlertTemplate.xml

Il Cliente chiede di modificare il Subject delle mail di Alert che vengono inviate alla modifica di item di lista, in particolare di Task list personalizzate.

La modifica dei template è "semplice", secondo quanto si legge in giro, ma non è per nulla ben documentata.
In particolare per l'utilizzo di campi dell'item da utilizzare al di fuori del corpo della mail.

Per modificare il template, occorre andare sul server nella cartella C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML.

Eseguire una copia del file alerttemplates.xml: questo XML definisce tutti i template delle mail inviate alla sottoscrizione del servizio di Alert di liste e library.

Creare un nuovo file XML con un nome diverso, ad es, custom_alerttemplates.xml.

Nota: non modificate il file originale, ma solo questo.

Ora, apriamo il nuovo XML e cerchiamo il template da modificare, nel nostro caso devo cercare il blocco:

<AlertTemplate Type="List"  Name="SPAlertTemplateType.Tasks">

Vado nel blocco <immediate> che rappresenta il template delle mail che partono alla modifica di un item, quindi non quelle raggruppate per periodo (che per forza di cose non potranno avere un subject personalizzato per singolo item).

Modifichiamo ora il blocco (il primo) <Subject> in cui viene definito l'oggetto della mail.
Di default, lo troviamo così:

  <Subject>   
    <GetVar Name="AlertTitle" />      
    <HTML><![CDATA[ - ]]></HTML>
    <GetVar Name="ItemName" />      
  </Subject>

Che corrisponde ad un oggetto del tipo: "titolo dato all'alert" - "titolo dell'item"

Il cliente vuole ad esempio : "titolo dell'alert" - "priorità" - "stato" - "titolo dell'item".
Dove i campi "priorità" e "stato" sono quelli definiti nei task.
Poiché non è detto che le task list siano tutte uguali, potrebbero avere nomi interni (internal name) differenti.
Ad esempio, ho una task list che non ha il campo "stato", internal name "Status" (non era necessario ed è stato tolto), l'altra non ha il campo "priorità", internal name "Priority", ma un campo personalizzato con internal name "Issue_Priority". Le Task list standard hanno "Status" e "Priority".

Occorre quindi tenere conto di queste differenze poiché quanto stiamo facendo ha impatti su tutta la web application e non sul singolo sito.

Quindi inseriamo un blocco If all'interno del Subject, che diventa come segue:

<Immediate>
  <Subject>   
    <GetVar Name="AlertTitle" />      
    <IfEqual>
      <Expr1>
        <GetVar Name="RawValue#Status"/>
      </Expr1>
      <Expr2/>
      <Then/>
      <Else>
        <HTML><![CDATA[ - ]]></HTML>
        <GetVar Name="RawValue#Status" />
      </Else>
    </IfEqual>
    <IfEqual>
      <Expr1>
        <GetVar Name="RawValue#Issue_Priority"/>
      </Expr1>
      <Expr2/>
      <Then>
        <HTML><![CDATA[ - ]]></HTML>
        <GetVar Name="RawValue#Priority" />
      </Then>
      <Else>
        <HTML><![CDATA[ - ]]></HTML>
        <GetVar Name="RawValue#Issue_Priority" />
      </Else>
    </IfEqual>
    <HTML><![CDATA[ - ]]></HTML>
    <GetVar Name="ItemName" />      
  </Subject>

Nota: per richiamare il metadato desiderato dobbiamo utilizzare

<GetVar Name="RawValue#INTERNALNAME" />


Salviamo il file ed eseguiamo il comando seguente che imposta il nuovo file come template standard per tutta la web application:

stsadm -o updatealerttemplates -url http://URLWEBAPP -filename "c:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML\custom_alerttemplates.xml"

Eseguiamo un iisreset e verifichiamo gli oggetti delle mail per modifiche ai task.

Nota: per ripristinare il file originale, eseguire lo stesso comando, ma con il file alerttemplates.xml e successivamente un iisreset.

stsadm -o updatealerttemplates -url http://URLWEBAPP -filename "c:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML\alerttemplates.xml"







Wednesday, January 15, 2014

SharePoint - Check People Picker con jQuery

Se si vuol controllare il valore inserito in un People Picker utilizzando jQuery, facciamo così!

Controllando l'HTML source della pagina, vediamo che al People Picker corrispondono molto elementi, ma quando l'utente scrive il nome per inserire l'utente, non scrive in un input o in una textarea, ma... in un DIV con l'attributo contentEditable = true.

Possiamo quindi controllare il dato in un New/Edit form prima di salvare l'item tramite la funzione js PreSaveAction().

Prima di tutto, creiamo un custom form (new o edit) e cerchiamo la riga corrispondente alla colonna People da controllare. Nell'HTML, individuiamo la TD che contiene il controllo SharePoint e aggiungiamo all'attributo "class" un nome che ci aiuterà ad identificare il campo desiderato (ad es "check-this-pp").
Se abbiamo da controllare più People Picker, usiamo classi diverse.

Ora, aggiungiamo i riferimenti a jQuery e il codice seguente, in fondo alla pagina:

function PreSaveAction(){

var checkPP = $(".check-this-pp div[contentEditable='true']").text();
if(checkPP == ""){
alert("Warning: Insert User in People Picker before saving item.");
return false;
}
return true;
}

Salviamo e andiamo a controllare.

Apriamo il form, nel Browser. 
Non inseriamo nomi nel People Picker e clicchiamo su Save.
Il form darà il messaggio del nostro codice js e non continua.
Inseriamo un nome e clicchiamo su Save.
Il form si ricarica correttamente (eseguendo quindi il controllo del nome, individua l'utente e, se l'utente esiste, salva il form).
Ancora un'ultima prova: apriamo il form di edit (con il codice inserito), vedremo il nome dell'utente.
Se lo cancelliamo e clicchiamo su Save, il form restituisce il warning per l'inserimento dell'utente. 


Friday, January 10, 2014

SharePoint - Check File Name in Upload form in Document Library con Javascript

Se si desidera controllare il nome del file di un nuovo documento in un Upload form, possiamo farlo con del buon Javascript !

Ho creato una funzione che controlla il nome del file e poi disattiva il bottone OK se la condizione non è verificata


Ho eseguito un override della funzione CheckAssetLibMediaExtension sull'evento "onchange" del controllo input file nel file Upload.aspx.

Se vediamo il codice HTML del file Upload.aspx (da browser), troviamo:

<input name="ctl00$PlaceHolderMain$UploadDocumentSection$ctl05$InputFile" type="file" id="ctl00_PlaceHolderMain_UploadDocumentSection_ctl05_InputFile" class="ms-fileinput ms-fullWidth" size="35" onfocus="ResetSpFormOnSubmitCalled();" onchange="CheckAssetLibMediaExtension()" title="Scegliere un file" />

Cercando la funzione, troviamo:

function CheckAssetLibMediaExtension()
{

}

Così, possiamo farne l'override!

Creiamo nella masterpage la referenza al jQuery e aggiungiamo la funzione seguente:

function CheckAssetLibMediaExtension(){
var inputFile = $("input[name$='InputFile']");
var nomeFile = inputFile.val().split('\\').pop();
if(!(nomeFile.indexOf('STRINGTOAVOID')>-1)){
           alert('Warning: file name not must contains "STRINGTOAVOID"!!');
   $("input[name$='btnOK']").attr('disabled','disabled');
   return;
}
        $("input[name$='btnOK']").removeAttr('disabled');
}

Quando l'utente seleziona un file, l'Input File cambia e la funzione viene chiamata.
La funzione mostra un alert se nel nome file c'è la stringa "STRINGTOAVOID".
Quindi, disabilita il bottone OK, e l'utente è obbligato a cambiare il nome del file prima di selezionarlo.

SharePoint - Check File Name in Upload form in Document Library with Javascript

If you want to check file name of new document in Upload form, you can do with Javascript!

I created a custom js that check filename then disables button OK if condition is not verified.


I override the function CheckAssetLibMediaExtension on event "onchange" of input file control in Upload.aspx.

If you see HTML Source of Upload.aspx (rendered by browser) you see:

<input name="ctl00$PlaceHolderMain$UploadDocumentSection$ctl05$InputFile" type="file" id="ctl00_PlaceHolderMain_UploadDocumentSection_ctl05_InputFile" class="ms-fileinput ms-fullWidth" size="35" onfocus="ResetSpFormOnSubmitCalled();" onchange="CheckAssetLibMediaExtension()" title="Scegliere un file" />

Search for function in HTML Source, you find:

function CheckAssetLibMediaExtension()
{

}

So, you can override it!

Create in masterpage reference to jQuery and add function below :

function CheckAssetLibMediaExtension(){
var inputFile = $("input[name$='InputFile']");
var nomeFile = inputFile.val().split('\\').pop();
if(!(nomeFile.indexOf('STRINGTOAVOID')>-1)){
           alert('Warning: file name not must contains "STRINGTOAVOID"!!');
   $("input[name$='btnOK']").attr('disabled','disabled');
   return;
}
        $("input[name$='btnOK']").removeAttr('disabled');
}

When user select file, InputFile control changes and function called.
Function show alert if fileName user try to upload has string "STRINGTOAVOID" in name.
Then, disables button OK, so user need to edit filename before select file.

SharePoint - Aggiungere un bottone Applica al form di Edit di un List Item per salvare senza chiudere il form

Questo articolo descrive come creare un bottone "Applica" in un edit form per un list item, in modo da salvare i dati e ricaricare il form stesso, senza essere reindirizzati alla lista (o al Source indicato nel querystring).

Creare un edit form personalizzato con SharePoint Designer.
Aprire in modifica il form.

Creare un nuovo parametro di tipo QueryString per il Source, chiamiamolo Source: quando la pagine viene ricaricata dopo aver salvato i dati, l'url nel Source viene mantenuto (ad es, per essere reindirizzati correttamente quando si clicca su Salva o Annulla).

Quindi, cercare nel codice i tag seguenti:

<xsl:param name="dvt_apos">&apos;</xsl:param>
<xsl:variable name="dvt_1_automode">0</xsl:variable>

E aggiungere sotto il codice seguente:

<xsl:param name="ListItemId">0</xsl:param>
<xsl:param name="Source"></xsl:param>

Cercare il bottone Salva (o Annulla) nell'xslt:

<SharePoint:SaveButton runat="server" ControlMode="Edit" id="savebutton2"/>

Aggiungere il codice seguente (il bottone):

<input type="button" value="Applica" name="btnSave" onclick="javascript: {ddwrt:GenFireServerEvent(concat('__commit;__redirect={ModificaPratica.aspx?ID=',$ListItemId,'&amp;Source=',$Source,'}'))}" />

Nel codice inserito, notare la funzione GeFireServerEvent: questa esegue il salvataggio e reindirizza all'URL indicato.
L'URL concatena l'ID con il parametro ListItemId e il Source con il corrispondente parametro.

Il risultato: quando clicchi su Applica, l'item viene salvato e la pagina ricaricata (con la stessa Source in querystring), invece che essere reindirizzati al Source.

Tips: puoi aggiungere una Custom Action al list item (con SPDesigner), chiamando una function JS che eseguirà il click sul bottone Applica (button con name="btnSave").

$("input[name='btnSave']").click();

Così, hai un bottone nel ribbon per Appica (salva e reload form), Salva (salva e chiude, con redirect al source), Annulla (chiude e redirect al source)

Thursday, January 9, 2014

SharePoint - Add button Apply to List Item Edit Form for Saving Item without closing form

This post describes how to create an Apply button in list item edit form, to save data and reload form, without redirect to list (or Source).

Create a custom edit form with SharePoint Designer.
Open form in edit mode.

Create new parameter of type QueryString for Source, named Source: when page refresh after saving data, url maintains Source (i.e. for redirect to list when you click on Save or Cancel).

Then, search for this code

<xsl:param name="dvt_apos">&apos;</xsl:param>
<xsl:variable name="dvt_1_automode">0</xsl:variable>

And add code below:

<xsl:param name="ListItemId">0</xsl:param>
<xsl:param name="Source"></xsl:param>

Search for button save/cancel in xslt, find this code:

<SharePoint:SaveButton runat="server" ControlMode="Edit" id="savebutton2"/>

Add code below (the button):

<input type="button" value="Applica" name="btnSave" onclick="javascript: {ddwrt:GenFireServerEvent(concat('__commit;__redirect={ModificaPratica.aspx?ID=',$ListItemId,'&amp;Source=',$Source,'}'))}" />

In code, you see GeFireServerEvent function: this executes save and then redirect to url shown.
The url concatenates ID and ListItemId parameter and Source with correspondant parameter.

Result: when you click on Apply, Item is saved and page reload (with same Source in querystring), insthead of redirect to Source.

Tips: you can add new Custom Action to list item (with SPDesigner), adding call to js function that execute click on button with name="btnSave".

$("input[name='btnSave']").click();

So, you have ribbon button for Apply (save and reload form), Save (save and close, redirect to source), Cancel (close and redirect to source)