My Mobile App

Ad oggi sono riuscito a mettere sul Windows Phone Store 3 App divertenti:

Quiz Nautica (>5K download) http://www.windowsphone.com/it-it/store/app/quiz-nautica/f4fcd446-4873-4d76-a61f-fbc6afc7bef0 

Trattoria Romana (new entry) http://www.windowsphone.com/it-it/store/app/trattoria-romana/17a6f47b-9637-4c0e-ab13-068472b2105d 

happinApp (new entry) http://www.windowsphone.com/it-it/store/app/happinapp/acc5f2e4-62c8-4758-9462-4d6616d21166 

 

Le App sono state realizzate con Visual Studio 2013 Ultimate Edition, Windows Phone 8.1 SDK

 

 

 

 

 

 

 

SharePoint Saturday Italy @Rome - Slide della sessione

Ciao a tutti, l'evento è stato fantastico, prima volta a fare gli speaker per me e per Matteo Lenzo (collega e amico di molte avventure), spero che il pubblico sia rimasto contento e soddisfatto.

Grazie mille agli sponsor in particolare Indra e Avanade per aver creduto in noi.

Su Slideshare ho aggiunto le slide utilizzate per la sessione su SharePoint Search. 

Alla fine posso solo dire:

Kid's stuff

SharePoint Saturday Italy @Rome - Agenda

Una piccola anticipazione della sessione SharePoint Search all'evento Sharepoint Saturday Italy @Rome con Matteo Lenzo il 25 gennaio.
AGENDA:
SharePoint and Search
SharePoint Search: Yesterday
- FAST Search
- High-value search solutions
- Advanced content processing pipeline
- Pipeline Extensibility
- Visual UI
SharePoint Search: Today
- Architecture
- What’s new?
- UI improvements
- Query APIs
Demo

SharePoint Saturday Italy @Rome

Iniziamo il 2014 carichissimi e con tante novità, il 25 gennaio terrò una sessione all'SPS Italy sul tema SharePoint Search, le informazioni sono tantissime e non sarà facile comprimerle in 1h di tempo, cercherò di postare tutto il possibile sul mio blog.

La sessione la terrò insieme a Matteo Lenzo, esperto SharePoint Search. 

Tutte le informazioni sono disponibili sul sito SharePoint Saturday, per acquistare gratuitamente il biglietto di ingresso è necessario andare qui

 

 

 

Search: indicizzazione colonne hidden

Dopo alcune prove arrivo alla conclusione che se creo delle colonne HIDDEN="TRUE" e poi queste colonne le aggiungo nelle Mapped Properties del Search Service Application queste non conterranno valori.

Per ovviare ho impostato gli attributi nel Field definition del Content Type relativi alla visualizzazione della colonna nei form di SharePoint a FALSE:

ShowInDisplayForm="FALSE" 

ShowInEditForm="FALSE" 

ShowInFileDlg="FALSE" 

ShowInNewForm="FALSE"

:)

Sharepoint Saturday Italy on tour @Rome

Con grande piacere annunciamo che lo SharePoint Saturday Italy riparte il 25 Gennaio 2014 da Roma.

Avremo a disposizione una mega sala con una capienza di più di 130 persone e da oggi parte la call for speaker che rimarrà aperta fino al 11/11 alle ore 11:11.

Le info per sottomettere la propria sessione le trovate sul sito o direttamente qui http://www.emailmeform.com/builder/form/cd05b33I8GZK7YN1w

Segui tutti gli aggiornamenti sulla pagina facebook: https://www.facebook.com/SharepointSaturdayItaly?hc_location=stream

SPDocumentLibrary a supporto di item non visibili

Poco tempo fa ho trovato una lista con circa 300K di item, più nello specifico pagine, (non spiego per quale motivo c'erano tutti quegli elementi) di cui circa il 90% erano in Check Out e non visualizzabili nella lista, nè con l'interfaccia di SharePoint nè con l'utilizzo dell'object model (SPList), la cosa buffa è che ero Site Collection Administrator e che la count degli item mi forniva un valore diverso rispetto a quanto invece riuscivo a visualizzare. A questo punto avevo la necessità, sia per riorganizzare la lista che per riportare le performance al top, di cancellare tutti quegli item.

Come dicevo prima l'oggetto SPList era coerente solo con la count ma non con l'accesso ai dati quindi, navigando sulla rete, ho trovato un altro modo per riportare alla normalità la lista.

L'oggetto SPDocumentLibrary consente di accedere alla lista, diciamo a basso livello, vediamo nello specifico caso il codice da me utilizzato:

SPSecurity.RunWithElevatedPrivileges(delegate() {
      using (SPSite site = new SPSite(URL)) {
           using (SPWeb web = site.OpenWeb()) {
                 SPDocumentLibrary docLib = (SPDocumentLibrary)web.Lists["Pagine"];

                 StringBuilder sbDelete = new StringBuilder();
                 sbDelete.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>               <Batch>");  
                 string command = "<Method><SetList Scope=\"Request\">" + docLib.ID + "</SetList><SetVar Name=\"ID\">{0}</SetVar><SetVar Name=\"Cmd\">Delete</SetVar><SetVar Name=\"owsfileref\">{1}</SetVar></Method>";

                  foreach (var item in docLib.CheckedOutFiles) {
                         item.TakeOverCheckOut();
                         sbDelete.Append(string.Format(command, item.ListItemId.ToString(), "/" + item.Url));
                  }

                  sbDelete.Append("</Batch>");
                  String results = web.ProcessBatchData(sbDelete.ToString());
                  docLib.Update();
           }
}}

User Profile vs User Information List + Performance Test

Analizzando un aspetto interessante, sempre in ambito di sviluppi custom, inerente l'accesso alle informazioni personali degli utenti (DisplayName, Account Name, Picture, ecc.) ho messo a confronto l'uso del servizio User Profile e l'uso dell'oggetto SiteUserInfoList, vediamo il codice:

Utilizzo del servizio User Profile

 

SPServiceContext context = SPServiceContext.GetContext(site);
UserProfileManager objUserProfileManager = new UserProfileManager(context);
UserProfile profile = objUserProfileManager.GetUserProfile(LoginName);

string Title1 = profile.DisplayName;

 

Utilizzo dell'oggetto SiteUserInfoList

 

SPUser userinweb = web.EnsureUser(LoginName);
string Query = "<Where><Eq><FieldRef Name='ID' /><Value Type='Counter'>" + userinweb.ID + "</Value></Eq></Where>";
string ViewFields = "<FieldRef Name='Title' Nullable='TRUE' /><FieldRef Name='Picture' Nullable='TRUE' />";

SPQuery _query = new SPQuery();
_query.ViewFields = ViewFields;
_query.Query = Query;

SPListItem userItem = web.SiteUserInfoList.GetItems(_query)[0];

string Title2 = userItem["Title"].ToString();

 

Come si può notare, nel secondo caso, ho preferito utilizzare una CAML Query e quindi prendere il risulato con l'oggetto GetItems invece di usare il GetItemsById(userinweb.ID) perchè a livello di performance è più vantaggioso (strano ma vero) il primo metodo invece del secondo.

Ho chiamato questi due metodi, con un ciclo, per 100 volte e il risultato è stato: vince 7 a 0 l'oggetto SiteUserInfoList, a titolo di esempio i tempi che ho preso:

In questo link che ho trovato sulla rete si possono vedere tutti i campi che potranno essere interrogati per il SiteUserInfoList: 

http://social.msdn.microsoft.com/Forums/en-US/sharepointdevelopment/thread/1c90abd0-54d7-482b-b16c-4c323bfbbd2c/

Il test l'ho eseguito in locale con un Notebook con processore i7, SSD e 8Gb di Ram, ma l'ho provato anche su una farm composta da 30 server (tipo NASA) e il risultato è stato che per la SiteUserInfoList la differenza tra il tempo finale e quello iniziale è pari a 0, mentre per il servizio User Profile rimane in termine di millesimi un leggero attimo di latenza.

Social Comments e Security Trimming

Eccomi qui a scrivere un bel post sui Commenti di SharePoint, che mi ha fatto tribolare per un pò ma che alla fine sono riuscito a svangarla (direi come al solito :P).

In un ambiente enterprise dove la security trimming è attivata potremmo avere qualche difficoltà con alcuni oggetti "social" di SharePoint, in particolare io mi sono scontrato con il metodo GetComments dell'oggetto SocialCommentManager, perchè? Perchè avevo la necessità di far visualizzare a livello pubblico i commenti inseriti su una determinata Uri dagli utenti della intranet che sto sviluppando. Il risultato era che sul mio ambiente di sviluppo, come al solito, tutto fila liscio, mentre una volta fatto il deploy dal cliente...succedeva che ogni utente vedeva i propri commenti :( azz!

Il seguente snippet riporta il modo con cui richiamavo il metodo GetComments:

 

SPWeb web = SPContext.Current.Web;
SPServiceContext context = SPServiceContext.Current;

Uri uri = new Uri(web.Url + pageUrl);

SocialCommentManager cm = new SocialCommentManager(context);
SocialComment[] commenti = cm.GetComments(uri);

 

Ma non finisce qui, dopo che ho certificato che il codice era giusto ma il risultato atteso non era esatto, provo con il web service di SharePoint e quindi altro snippet:

 

SocialDataService sds = new SocialDataService();
            Microsoft.Office.Server.SocialData.SocialDataService.SocialCommentDetail[] comments = sds.GetCommentsOnUrl(string.Concat(SPContext.Current.Site.Url, pageUrl), null, null, null);

 

Niente, sempre lo stesso risultato :(

Bene, disassembliamo e vediamo che cosa fanno i metodi e faccio una scoperta che mi lascia stupefatto. C'è un bel metodo INTERNAL ..... GetComments che accetta un parametro bool needSecurityTrim e ora?

 

Sia lodata la Reflection, con l'ultimo snippet potrete avere la soluzione ai vostri problemi, senza dover ricorrere a disabilitare il Security Trimming via PowerShell:

 

var type = typeof(SocialCommentManager);
            var methods = type.GetMethods(System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            var method = methods.FirstOrDefault(m => m.ToString() == "Microsoft.Office.Server.SocialData.SocialComment[] GetComments(System.Uri, Int32, Int32, System.DateTime, Boolean)");
            if (method == null) throw new MissingMethodException("Social GetComment method not found.");
            var comments = method.Invoke(cm, new object[] { uri, 10000, 0, DateTime.MinValue, false }) as SocialComment[];

 

SPQuery: count items

Per recuperare il numero degli items di una lists per una determinata Query, eventualmente creata a run time, la sintassi base che uno scriverebbe è la seguente:

 

int ItemCount;
SPQuery query = new SPQuery();
query.Query = view.Query;
ItemCount = list.GetItems(query).Count;

 

da questo snippet ho omesso il RowLimit perchè altrimenti non avrei una Count valida, supponendo di creare un paginatore. 

Ma qui ho riscontrato un enorme problema, i tempi di risposta, perchè il GetItems anche se alla fine richiediamo il Count comunque restituisce tutti gli items e poi ritorna il totale, ho notato che già con 3000 items i tempi di attesa si aggirano sui 10/14 secondi (un botto!!!).

La soluzione è banalissima, comunque siamo costretti a fare il GetItems, ma considerato che abbiamo necessità solo di una Count cerchiamo di minimizzare il tutto e quindi nella mia query specificherò che i ViewFields dovranno essere solamente 1 cioè l'ID (numerico), quindi insieme allo snippet di prima aggiungeremo anche:

 

query.ViewFields = "<FieldRef Name='ID' />";

 

Kid's stuff

 

Month List