Uncategorized

I just moved to my new domain to www.luisevalencia.com

Posted on

Finally, I was able to configure my own domain with wordpress, after 10 years blogging I have tried many platforms, I tried blogger, wordpress, then blogger again, then wordpress, then sharepoint.

Sharepoint as a blogging platform is very limited, specially in Office 365, you need to customize a lot and that takes a lot of time.

Blogger is really a blogging platform, better than Sharepoint, but the Theme choice and plugin choice is very limited.

WordPress hosted at wordpress.com really sucks if you need to paste some code, as you cant install plugins, you are stuck.

Thats why I moved to wordpress in my own server, because I can install any plugin and use any theme.

So, welcome to my new blog @ www.luisevalencia.com

Advertisements

HOW TO ENABLE PDF PREVIEW IN OFFICE WEB APPS/SHAREPOINT 2013

Posted on

Enabling PDF Preview is a must in today’s environments.  We have used Sharepoint 2013 for over 1 year and we didnt have this feature, we never thought it could be possible because well, the name is called “Office Web Apps”, but indeed is possible and straightforward.

Please note the solution below only works if you have office web apps, March 213 PU.

 

SOLUTION

1. Execute the following script.

2. Dont forget to run a full crawl afterwards


$tenantOwner = Get-SPEnterpriseSearchOwner -Level SSA $ssa = "Search Service Application" #change if you have a different name $rule = get-SPEnterpriseSearchPropertyRule -PropertyName "FileType" -Operator "IsEqual" $rule.AddValue("pdf") $ruleCollection = Get-SPEnterpriseSearchPropertyRuleCollection $ruleCollection.Add($rule) $item = new-SPEnterpriseSearchResultItemType -Owner $tenantOwner -SearchApplication$ssa -Name "PDF with Preview" -Rules $ruleCollection -RulePriority 1 -DisplayProperties "Title,Author,Size,Path,Description,EditorOWSUSER,LastModifiedTime,CollapsingStatus,DocId,HitHighlightedSummary,HitHighlightedProperties,FileExtension,ViewsLifeTime,P arentLink,ViewsRecent,FileType,IsContainer,SecondaryFileExtension,DisplayAuthor,docaclmeta,ServerRedirectedURL,SectionNames,SectionIndexes,ServerRedirectedEmbedURL,S erverRedirectedPreviewURL" -DisplayTemplateUrl "~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Word.js" -OptimizeForFrequentUse $true

solving error signing credentials was not initialized on sharepoint provider apps

Posted on

Today I was trying to create a sharepoint provider hosted app by using many of the blog posts and tutorials on the web.

​​​The code I was using was:


Uri hostWeb = new Uri(Request.QueryString["SPHostUrl"]);
using(var clientContext=TokenHelper.GetS2SClientContextWithWindowsIdentity(hostWeb,Request.LogonUserIdentity))
{
var web= clientContext.Web;
clientContext.Load(web, w=>w.Lists.Include(l=>l.Title).Where(l=> !l.Hidden));
clientContext.ExecuteQuery();
return View(web.Lists);
}

 

The exception was thrown explicitly by the TokenHelper Class

 

21M3r

 

​​sol​​ution​

​Well, if you analyze the code for a second the name of the method is GetS2SClientContextWithWindowsIdentity, but I am creating a Sharepoint Online Provider Hosted App.

Does it make sense to get the client contex from a windows identity? obviously it does not.

After investigating for some minutes, I found there are other methods in the token helper class, and the one below makes more sense, doesnt it?


var contextToken = TokenHelper.GetContextTokenFromRequest(Request);
var hostWeb = Request.QueryString["SPHostUrl"];
using (var clientContext = TokenHelper.GetClientContextWithContextToken(hostWeb, contextToken, Request.Url.Authority))
{
var web = clientContext.Web;
clientContext.Load(web, w=>w.Lists.Include(l=>l.Title).Where(l=> !l.Hidden));
clientContext.ExecuteQuery();
return View(web.Lists);
​}

The option for the Sharepoint 2013 workflow platform is not available because the workflow service is not configured on the server

Posted on

​​I have correctly configured Sharepoint 2013 Workflow Manager, however I was not able to create workflows with Sharepoint Designer using the 2013 option.

 

See my previous posts here:

1. How to install workflow manager offline

2. How to install workflow manager ​

 

After several hours looking for documentation and asking on stackexchange​​, I was able to fix it.

Thanks to Cameron Verhelst​.​

 

​​Sol​​ution​

​You have to enable a feature on the site where you want to create the Sharepoint 2013 Workflow.

 

Enable-SPFeature -Identity WorkflowServiceStore –Url $yourUrl​​

 

Add callout cu​stom action based on content TYPE

Posted on

I had a strange requirement this week.  We have a document library with 3000 items where there is a workflow that was approved by the users some months ago, the workflow is quite complex.

However after some months using it, the users complain that some documents dont need the long approval and can just go to Completed.

The status field is of course a hidden field, so they cant just modify it, normally the workflow would update this field for the users.

 

Solution

​​Create a callout custom action, for those who dont know the callout its the small pop window that appears when you click on the 3 dots in a document library item.

Callouts are not limited to list items, they can be done everywhere in every element you want, and they are used extensively in search results as display templates.

In  my case I didnt need to put the custom action in search results, just in the document library.

 

​​1. ​add code snippet webpart

​The code below is for a normal document library, as I was working during the weekend at home, I did it on my sharepoint online (Documents Library),  but in your environment you only need to change the list title and the field you want to change.


SP.SOD.executeFunc("callout.js", "Callout", function () {
var itemCtx = {};
itemCtx.Templates = {};
itemCtx.BaseViewID = 'Callout';
// Define the list template type
itemCtx.ListTemplateType = 101;

itemCtx.Templates.Footer = function (itemCtx) {
return CalloutRenderFooterTemplate(itemCtx, AddCustomCompleteAction, true);
};

SPClientTemplates.TemplateManager.RegisterTemplateOverrides(itemCtx);
});

function AddCustomCompleteAction (renderCtx, calloutActionMenu) {
if(renderCtx.CurrentItem.ContentType=='Document') {
calloutActionMenu.addAction (new CalloutAction ({
text: 'COMPLETE',
tooltip: 'This action will set status to Complete, this action cant be rolled back.',
onClickCallback: function() {UpdateBillCycleStatusToCompleted(1);}
}));
}

// Show the default document library actions
CalloutOnPostRenderTemplate(renderCtx, calloutActionMenu);

// Show the follow action
calloutActionMenu.addAction(new CalloutAction({
text: Strings.STS.L_CalloutFollowAction,
tooltip: Strings.STS.L_CalloutFollowAction_Tooltip,
onClickCallback: function (calloutActionClickEvent, calloutAction) {
var callout = GetCalloutFromRenderCtx(renderCtx);
if (!(typeof(callout) === 'undefined' || callout === null))
callout.close();
SP.SOD.executeFunc('followingcommon.js', 'FollowSelectedDocument', function() { FollowSelectedDocument(renderCtx); });
}
}));
}

function UpdateBillCycleStatusToCompleted(itemId) {
var clientContext = new SP.ClientContext.get_current();
var oList = clientContext.get_web().get_lists().getByTitle('Documents');
this.oListItem = oList.getItemById(itemId);
oListItem.set_item('Title', 'Complet');
oListItem.update();
clientContext.executeQueryAsync(Function.createDelegate(this, this.StatusCompletedSucceeded), Function.createDelegate(this, this.StatusCompletedFailed));
}

function StatusCompletedSucceeded() {
alert('Item updated!');
}

function StatusCompletedFailed(sender, args) {
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}

function IsCurrentUserMemberOfGroup(groupName, callback) {
var currentContext = new SP.ClientContext.get_current();
var currentWeb = currentContext.get_web();

var currentUser = currentContext.get_web().get_currentUser();
currentContext.load(currentUser);

var allGroups = currentWeb.get_siteGroups();
currentContext.load(allGroups);

var group = allGroups.getByName(groupName);
currentContext.load(group);

var groupUsers = group.get_users();
currentContext.load(groupUsers);

currentContext.executeQueryAsync(OnSuccess,OnFailure);

function OnSuccess(sender, args) {
var userInGroup = false;
var groupUserEnumerator = groupUsers.getEnumerator();
while (groupUserEnumerator.moveNext()) {
var groupUser = groupUserEnumerator.get_current();
if (groupUser.get_id() == currentUser.get_id()) {
userInGroup = true;
break;
}
}
callback(userInGroup);
}

function OnFailure(sender, args) {
callback(false);
}
}​

​2. next challenge.

​Now I need to show the option based on the current user group, if the user its in a specific group, then show the custom action, otherwise dont show it, that will be my next blog post.

 

Hope you enjoy it, I had fun times coding this.

Thanks to : @eliostruyf

http://www.eliostruyf.com/adding-a-custom-action-to-a-callout-in-sharepoint-2013/​​​

INSTALLING WORKFLOW MANAGER AND WORKFLOW MANAGER TOOLS OFFLINE

Posted on

f you are as unlucky as me, then you are in a very restricted environment where the dev servers dont have internet connectivity.

In this case you need to install both WorkflowManager and WorkflowManagerClient and WorkflowManagerTools in your environment offline.

WebPlatform Installer is not an option (not online)

How to fix this:?

1. Dowload them offline:

webpicmd /offline /Products:WorkflowManager /Path:C:\temp\WorkflowManager

webpicmd /offline /Products:WorkflowManagerTools /Path:C:\temp\WorkflowManagerTools

2. Copy the files to your server

3. Install them using webplatform installer on the server, please note you need to install this first on the server even if you dont have internet connectivity you can use command line arguments to intall offline:

http://www.microsoft.com/web/downloads/platform.aspx​

4. Install the components

WebpiCmd.exe /Install /Products:WorkflowManager /XML:c:/temp/WorkflowManager/feeds/latest/webproductlist.xml

WebpiCmd.exe /Install /Products:WorkflowManagerTools /XML:c:/temp/WorkflowManagerTools/feeds/latest/webproductlist.xml

Please note that the first command will run a wizard at the end to join the workflow manager into the farm, I am still struggling with this step, will post something later

– See more at: http://levalencia-public.sharepoint.com/Pages/2014/01/Installing-Workflow-Manager-and-Workflow-Manager-Tools-Offline.aspx#sthash.Ybj3Qdk8.dpuf

Linq and Sharepoint SPSite.AllWebs not good friends.

Posted on

In my current project, we have a page on the root web of every site collection (18,000 site collections), every welcome page has 2 webparts which renders the last created sites.

We have 2 types of sites, jobs and opportunities.  This information in saved in the property bag under the key “WebProperty”.

The requirement is to show the last 5 created sites of each type.

This code is as I had it before.

private void LoadGridData()
        {
            try
            {
                String currentUrl = SPContext.Current.Site.Url;

                var jobInfoList = new List<JobInfo>();

                SPSecurity.RunWithElevatedPrivileges(delegate
                {
                    using (var clientSiteCollection = new SPSite(currentUrl))
                    {
                        foreach (
                            SPWeb web in
                                clientSiteCollection.AllWebs.Where(
                                    c =>
                                    c.AllProperties[Constants.WebProperties.General.WebTemplate] != null &&
                                    c.AllProperties[Constants.WebProperties.General.WebTemplate].ToString() ==
                                    Constants.WebTemplates.JobWebPropertyName).OrderByDescending(d => d.Created).Take(5)
                            )
                        {
                            if (web.DoesUserHavePermissions(SPContext.Current.Web.CurrentUser.LoginName,
                                SPBasePermissions.Open))
                            {
                                SPList jobInfoListSp = web.Lists.TryGetList(Constants.Lists.JobInfoName);
                                if (jobInfoListSp != null)
                                {
                                    if (jobInfoListSp.Items.Count > 0)
                                    {
                                        var value =
                                            new SPFieldUrlValue(
                                                jobInfoListSp.Items[0][Constants.FieldNames.Job.iPowerLink].ToString());

                                        jobInfoList.Add(new JobInfo
                                        {
                                            JobName =
                                                jobInfoListSp.Items[0][Constants.FieldNames.Job.JobName].ToString(),
                                            JobCode =
                                                jobInfoListSp.Items[0][Constants.FieldNames.Job.JobCode].ToString(),
                                            IPowerLink = value.Url,
                                            JobWebsite = web.Url,
                                            IsConfidential =
                                                HelperFunctions.ConvertToBoolean(
                                                    jobInfoListSp.Items[0][Constants.FieldNames.Job.Confidential]
                                                        .ToString())
                                        });
                                    }
                                }
                            }

                            web.Dispose();
                        }
                    }
                });

                _lastCreatedJobsGrid.DataSource = jobInfoList;
                _lastCreatedJobsGrid.DataBind();
            }
            catch (Exception ex)
            {
                LoggingService.LogError(LoggingCategory.Job, ex);
            }
        }


Well, you can see there is a Dispose() inside the foreach, that is not the problem, the problem is the AllWebs.

So, I dig deep into it and I found the following link : http://solutionizing.net/2009/01/05/linq-for-spwebcollection-revisited-assafeenumerable/

So, I replaced the AllWebs with AllWebs.AsSafeEnumerable().

Then, the memory leak went away.

But guess what, this code introduced another problem.

53b416d1-1497-4b40-beb5-cd261180ece8 Stack trace:   
 at Microsoft.SharePoint.SPWeb.get_Created()    
 at xx.SP.DMS.WebParts.WebParts.LastCreatedJobs.LastCreatedJobs.<>c__DisplayClass8.<LoadGridData>b__7(SPWeb d)    
 at System.Linq.EnumerableSorter`2.ComputeKeys(TElement[] elements, Int32 count)    
 at System.Linq.EnumerableSorter`1.Sort(TElement[] elements, Int32 count)    
 at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__0.MoveNext()    
 at System.Linq.Enumerable.<TakeIterator>d__3a`1.MoveNext()    
 at xx.SP.DMS.WebParts.WebParts.LastCreatedJobs.LastCreatedJobs.<>c__DisplayClass8.<LoadGridData>b__5()    
 at Microsoft.SharePoint.SPSecurity.<>c__DisplayClass5.<RunWithElevatedPrivileges>b__3()    
 at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode)    
 at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param)    
 at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode)    
 at xx.SP.DMS.WebParts.WebParts.LastCreatedJobs.LastCreatedJobs.LoadGridData()    
 at xx.SP.DMS.WebParts.WebParts.LastCreatedJobs.LastCreatedJobs.OnPreRender(EventArgs e)    
 at System.Web.UI.Control.PreRenderRecursiveInternal()    
 at System.Web.UI.Control.PreRenderRecursiveInternal()    
 at System.Web.UI.Control.PreRenderRecursiveInternal()    
 at System.Web.UI.Control.PreRenderRecursiveInternal()    
 at System.Web.UI.Control.PreRenderRecursiveInternal()    
 at System.Web.UI.Control.PreRenderRecursiveInternal()    
 at System.Web.UI.Control.PreRenderRecursiveInternal()    
 at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)    
 at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)    
 at System.Web.UI.Page.ProcessRequest()    
 at System.Web.UI.Page.ProcessRequest(HttpContext context)    
 at Microsoft.SharePoint.Publishing.TemplateRedirectionPage.ProcessRequest(HttpContext context)    
 at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()    
 at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)    
 at System.Web.HttpApplication.PipelineStepManager.ResumeSteps(Exception error)    
 at System.Web.HttpApplication.BeginProcessRequestNotification(HttpContext context, AsyncCallback cb)    
 at System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context)    
 at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)    
 at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)    
 at System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr pHandler, RequestNotificationStatus& notificationStatus)    
 at System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr pHandler, RequestNotificationStatus& notificationStatus)    
 at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)    
 at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags)

When I debugged line by line, my catches were no hit, but then I saw that my new method AsSafeEnumerable(), didnt have a catch. just a finally, so I guessed there was an exception there, just not caught.

My good friends here helped me to find the problem, apparently the .Where from Linq and the .Take, are not “Safe” with SPWeb objects, so extension methods had to be done to overcome this error.

See original question here:
http://stackoverflow.com/questions/21756403/error-on-system-linq-enumerable-takeiteratord-3a1-movenext

I fixed the problem by creation these 2 methods.

public static IEnumerable<TSource> SafeTake<TSource>(
    this IEnumerable<TSource> source,
    int count
) where TSource : IDisposable
{
    foreach(var item in source)
    {
        if(--count >= 0)
        {
            yield return item;
        }
        else if (item != null)
        {
            item.Dispose();
        }
    }
}

public static IEnumerable<TSource> SafeWhere<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate
) where TSource: IDisposable
{
    return source.SelectMany(x => {
        var result = predicate(x);
        if(!result && x != null) x.Dispose();
        return Enumerable.Repeat(x, result ? 1 : 0);
    });
}

And then of course replacing the Where with SafeWher and the Take with SafeTake


foreach (var web in clientSiteCollection.AllWebs.SafeWhere(
c =>
c.AllProperties[Constants.WebProperties.General.WebTemplate] != null &&
c.AllProperties[Constants.WebProperties.General.WebTemplate].ToString() ==
Constants.WebTemplates.JobWebPropertyName).OrderByDescending(d => d.Created).SafeTake(5)
)