Per un cliente avevo l'esigenza di rendere un documento immodificabile e non cancellabile.
In questo post ho intenzione di raccontarvi come ho risolto il problema con le nuove activity di SharePoint Designer 2010, con una sorpesa finale.
Vi raconterò:
- Come creare i worflow per togliere e rimettere i permessi al documento
- Come fare delle custom action accessibili dal ribbon con SharePoint Designer, per eseguire il workflow
- Come crare delle custom action che tramite ECMA Client Object Model eseguono un workflow associato alla lista
Prima di tutto si comincia creando un nuovo Workflow con SharePoint Designer, molto comodi sono i Globally Reusable Workflow, che possono essere associati a verie liste e usati in tutti i siti della site collection (ricordatevi di premere il pulsante Publish Globally nel ribbon del Designer). Il primo step è quello di creare un Impersonation Step che ci consente di impersonare in fase di esecuzione un altro utente e nella fattispecie l'utente che ha creato il workflow. In questo modo è possibile far eseguire a utenti che non ne avrebbero i permessi determinate azioni.
L'azione da aggiungere è "Replece List Item permissions". Di seguito i passi per aggiungere, modificare o rimuovere permessi ad un item.
Usando l'action "Inherit parent permissions for item..." è possibile riportare la situazione dei permessi alla condizione originale.
Quick Step con SharePoint Designer
Nel ribbon di ogni Library (tab Library), nella sezione "Customize Library" c'è il bottone "New Quick Step". Premendo questo bottone si apre SharePoint Designer sull'edit della lista corrente e viene visualizzato un pannello per configurare una custom action che sarà visibile nel tab Documents sotto la voce "Quick Steps". Da questa interfaccia è possibile associare all'azione: un link ad una pagina aspx del sito, l'avvio di un workflow associato alla lista oppure un link ad una URL esterna. Vedi fugure di seguito.
Custom Action per attivare un workflow
Visto che da Designer è possibile creare una custom action che consente di avviare un workflow, ho voluto provare a creare delle mie custom action,da posizionare in punti diversi del ribbon e con logiche di attivazione tutte mie, che facessero la stessa cosa. Il primo passo è stato cercare di capire quale azione fosse associata e questi Quick Step. Analizzando un po' il codice HTML generato e relativi Javascript ho scoperto, che queste actuion invocano la pagina "/_layouts/wfstart.aspx" con i seguenti parametri:
- List={ListId}
- ID={ItemId}
- TemplateID
- AssociationName
<?xml version="1.0" encoding="utf-8"?> <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <CustomAction Id="Install JS" ScriptSrc="/_layouts/RibbonButtonsDemo/demo.js" Location="ScriptLink" Sequence="100"> </CustomAction> <CustomAction Id="SharePoint.Ribbon.LockDocuments" Title="Lock Documents" Location="CommandUI.Ribbon" RegistrationType="List" RegistrationId="101"> <CommandUIExtension> <CommandUIDefinitions> <CommandUIDefinition Location="Ribbon.Documents.Manage.Controls._children"> <Button Id="Ribbon.Documents.New.MsysLockDocsButton" Alt="Lock selected document" Sequence="5" LabelText="Lock" Image16by16="/_layouts/images/RibbonButtonsDemo/lock-icon16x16.png" Image32by32="/_layouts/images/RibbonButtonsDemo/page-lock-icon32x32.png" Command="Command.LockButton"/> </CommandUIDefinition> <CommandUIDefinition Location="Ribbon.Documents.Manage.Controls._children"> <Button Id="Ribbon.Documents.New.MsysUnlockDocsButton" Alt="Unlock selected document" Sequence="5" LabelText="Unlock" Image16by16="/_layouts/images/RibbonButtonsDemo/lock-off-icon16x16.png" Image32by32="/_layouts/images/RibbonButtonsDemo/page-lock-off-icon32x32.png" Command="Command.UnlockButton"/> </CommandUIDefinition> </CommandUIDefinitions> <CommandUIHandlers> <CommandUIHandler Command="Command.LockButton" CommandAction="javascript:ExecuteOrDelayUntilScriptLoaded(LockDocs, 'sp.js');" EnabledScript="javascript:onlyOne();"/> <CommandUIHandler Command="Command.UnlockButton" CommandAction="javascript:ExecuteOrDelayUntilScriptLoaded(UnLockDocs, 'sp.js');" EnabledScript="javascript:onlyOne();"/> </CommandUIHandlers> </CommandUIExtension> </CustomAction> </Elements>Tutto il codice Javascript lo messo in un file js esterno salvato nella cartella layouts.
var context; function LockDocs() { // First get the context and web context = SP.ClientContext.get_current(); this.web = context.get_web(); // Get the current selected list, then load the list using the getById method of Web (SPWeb) var listId = SP.ListOperation.Selection.getSelectedList(); var sdlist = this.web.get_lists().getById(listId); this.wfa = sdlist.get_workflowAssociations().getByName('Change permissions'); context.load(this.wfa); context.executeQueryAsync(Function.createDelegate(this, this.OnSuccess), Function.createDelegate(this, this.OnFail)); } function UnLockDocs() { // First get the context and web context = SP.ClientContext.get_current(); this.web = context.get_web(); // Get the current selected list, then load the list using the getById method of Web (SPWeb) var listId = SP.ListOperation.Selection.getSelectedList(); var sdlist = this.web.get_lists().getById(listId); this.wfa = sdlist.get_workflowAssociations().getByName('Re-active Permissions'); context.load(this.wfa); context.executeQueryAsync(Function.createDelegate(this, this.OnSuccess), Function.createDelegate(this, this.OnFail)); } function OnSuccess(sender, args) { var listId = SP.ListOperation.Selection.getSelectedList(); // Get the currently selected item of the list. This will return a dicustonary with an id field var items = SP.ListOperation.Selection.getSelectedItems(context); var mijnid = items[0]; var itemId = mijnid.id; var _url = '/_layouts/wfstart.aspx?List=' + listId + '&ID=' + itemId + '&TemplateID=' + this.wfa.get_id() + '&AssociationName=Change permissions'; alert(_url); document.location = _url; } function OnFail(sender, args) { alert('Fail'); //alert('Fail to get list list. Error :'+ args.get_message()); } function moreThanOneEnabled() { var items = SP.ListOperation.Selection.getSelectedItems(); var ci = CountDictionary(items); return (ci > 0); } function onlyOne() { var items = SP.ListOperation.Selection.getSelectedItems(); var ci = CountDictionary(items); return (ci == 1); }
Come potete vedere recupero il TemplateID dal workflow associato, nel metodo di feedback OnSuccess.
Sorpresa
La sorpresa consiste nel fatto che in SharePoint 2010 esiste una nuova funzionalità (una feature da attivare a livello di site collection), chiamata "In place Record", che fa proprio quello che serviva al mio cliente e in modo più elegante. Quindi tutta la fatica fatta è servita solo a titolo di studio :-)
La sorpresa consiste nel fatto che in SharePoint 2010 esiste una nuova funzionalità (una feature da attivare a livello di site collection), chiamata "In place Record", che fa proprio quello che serviva al mio cliente e in modo più elegante. Quindi tutta la fatica fatta è servita solo a titolo di studio :-)