Posted on:
Categories: SharePoint;Office 365
Description: Strange behaviour can be seen when making a project summary responsive. Let's take a look as to why that's happening
Setup​To gain understanding of the observation and to see first-hand, here are the things I’ve done to reproduce the weird project site timeline summary issue. Create a new site collection with Publishing template. Create a Project site subsite. Edit the page and remove all the generated web parts except for the Project Summary web part on the home page. Set these tasks (note they are all expired) Use these rules (I set it as alternate CSS for quick reference) #s4-workspace width auto !important; #contentBox min-width 0; #DeltaPlaceHolderSearchArea display none; That is all in terms of setup. To elaborate on the CSS rules, there are defaults for each of those elements. s4-workspace has a default width that SharePoint sets via Javascript; for me, it was 1061px. Contentbox’s min-width is set by core15.css, which is 703px. These two elements need to be overwritten so that the inner contents can be exposed to the changing window size. Similarly, the search box is removed simplicity because after a certain width, the search box prevents the page from shrinking.Behaviour When you land on your project page, Project Summary’s default behaviour will display the timeline view, then the late/upcoming view for a second, and then switch back to the timeline view automatically. Of course, users can use the pagination arrows to manually navigate through the views as well. We want a responsive project site; using the CSS above makes the project summary an overly simplified responsive site for this blog’s purposes. Meaning, the Project Summary web part will shrink according to window width. With a little more work, the timeline view can be fully responsive, but the problem lies with the late view. If you resize the window, at some point, the late view items disappear, where everything else stays the same. It’s easy to point fingers at the branding or customizations, but in fact, the disappearing act is something that SharePoint Online does natively and this can cause a problem for the end user given the behavior. Inspecting the page, and on the late area, the focus is brought to As you resize the page, an additional "display none" is added on dynamically via javscript. Searching the source code for the full ID will return nothing. Luckily, if we search for _LatePanel, we have a single result from a SharePoint Online .js file Using the Pretty Print function that comes with Chrome, we have resulting code that looks like this function i() var e = $get(a._controlId + "_LatePanel"), d = $get(a._controlId + "_UpcomingPanel"), g; //... some code here … if (d) if (b > 0) d.style.display = ""; ​ d.style.width = k * b + "px"; m("UpcomingPanel", b) else d.style.display = "none"; if (e) if (c > 0) e.style.display = ""; e.style.width = k * c + "px"; m("LatePanel", c) else e.style.display = "none"; //... some code here … return (b + c) * k And by connecting the dots, you see that here, SharePoint is using a if-else statement to dynamically set the display (back from none if it was attached earlier), and width, otherwise, hide the panel. As for why SharePoint decides to do that and what purpose that served, we have no idea. Perhaps it was a left-over artifact from SharePoints prior, but keep in mind, we had to remove 3 “levels of fail-safes” in the form of set width, min-width, and unshrinkable search bar, to figure find out this oddity.




Posted on:
Categories: Office 365;SharePoint
Description: Learn how to use Javascript to retrieve the current user's Delve boards and only the current user's
​So I was tasked with creating a SharePoint-hosted add-in that goes to Delve and retrieves only the current user’s boards. Delve boards are user created, dynamically or using previously created boards; oddly enough, it’s also synonymous with adding a card to favorites. If you go to Delve, Delve boards are currently located to the left of the screen, under the People section.​ Because it’s SharePoint-hosted, we must use Javascript for any logic. There are plenty of articles on the internet about how to retrieve Delve boards using the REST API and Javascript. Most of them will tell you to use the query string​ 'PathTAG//PUBLIC/?NAME=*'&Properties='IncludeExternalContenttrue’ The "Properties" part is what returns the Delve boards; you must specify IncludeExternalContent set to true. Without it, the code won't return any boards. At first glance, the task is already done since you can just follow the guides and be done in a day or so. Easy, right? Turns out, the answer is "No". According to this Microsoft post So while this addresses the privacy issue of documents on public boards, this does not help when using the Delve API. Meaning, because the boards are public, when you use the API to retrieve Delve boards, it returns EVERYONE’s boards, not just the ones that show up in your Delve. If you search around, you’ll quickly see no one talks about getting the current user’s Delve boards, just “how to get Delve boards” in general or specific boards with known names. Nor does any blog talk about using multiple Properties, only multiple SelectProperties.But since Delve uses Office Graph in the background, so we can use that as a hint. Querytext='IsDocument1'&Properties='GraphQueryACTOR(ME)'&SelectProperties='DocId,WebId,UniqueId'Using GQL (Graph Query Language), we can get a bunch of things from Office Graph, EXCEPT user’s Delve boards. The snippet above will retrieve the current user’s cards, and cards only. Dead end there. Unfortunately, after much research, no one has actually ever combined the two and wrote about it. Turns out, there was a very simple way to retrieve the boards; and it’s not tacking on more and more roundabout logic. Looking at the query string for Delve boards and the GQL, you can see they are extremely similar. A closer look shows we should do something with the “Properties”, which, surprisingly once again, no one has inserted more than once. And so the proper syntax for using multiple properties took a little ingenuity and some trial and error, but ultimately, if we include the GQL actor ME as part of the REST API call, we can get Delve boards that belong to the current user. "'PathTAG//PUBLIC/?NAME=*'&Properties='IncludeExternalContenttrue,GraphQueryACTOR(ME)'";




Posted on:
Categories: SharePoint;Office 365
Description: At Softlanding we strongly follow the cliché but important motto "practice what you preach". It's simple, until you have truly used something in your day-to-day work environment, you won't be able to know its behaviour in "real life" scenarios.
​At Softlanding we strongly follow the cliché but important motto "practice what you preach". It's simple, until you have truly used something in your day-to-day work environment, you won't be able to know its behaviour in "real life" scenarios. At Softlanding within the applications group, we have regularly scheduled meetings called Creativity and Inspiration Conversations (CIC) to discuss emerging technologies, features, practices or generally a "cool" solution we might have come across. These are informal discussions rather than rigid meetings, which are intended to promote thinking outside of the box and sharing ideas. In the past we have supported this discussion by simply setting up a recurring calendar invite. Typically before the meeting we assign someone to generate a topic and maybe review the topic with members prior to the meeting. Again this is intended to be informal and more of a conversation starter, so when I heard of Office Groups, it just seemed like a natural fit to support our CIC use case. It was simple, I created a new public Office Group, added the application team as members of the group. Then with the shared calendar feature of Office Groups I created a recurring meeting and invited the members. The members were notified that they had been added to the new Office Group, and automatically they were able to see the Groups calendar events overlaid on their calendar in outlook. Next I started a new conversation in the Office Group, where I had control of notifying individual members or the entire group of the potential topics for the next CIC session. We were able to host discussions within the group conversation area, without having a dreaded mass email chain. Any supporting files for the meeting are stored in the Files area of the Group. This cuts down having to email things like screenshots back and forth or creating a formal team site to store temporary documents. The Good Groups are easy and quick to set up, which really makes sharing content with others more natural The calendar within the Group is really a core functionality, being notified for meetings and seeing Group meetings overlaid on your own calendar makes it hard to convenient and centralized Office Groups provide a great way of potentially cutting down on email discussions, by providing a dedicated place to have conversations and be notified when you have been mentioned Rather than adding individuals as members to your group, if you add an Active Directory group, it will detect the users within the group and add them for you The Bad There seemed to be a lag time between the web app and installed client version of Outlook when updating events in Groups. It took between 30 minutes to one hour to reflect event updates between both versions of Outlook, which I can see frustrating many users Additionally, there also seemed to be a long waiting period before members were notified that they had been added to a Group The Great There is a dedicated Office Groups app also available for Windows, Android and IOS There is now integration between Microsoft Planner and Office Groups, you automatically have a plan created per Group where you can start tracking and assigning tasks It was also announced that there will be integration between Groups and team sites. This is for more complex scenarios where you want to have the power of using team sites and the versatility of Groups' scheduling and discussions




Posted on:
Categories: PowerShell;SharePoint
Description: Issue using Content Query Web Part on a SP2013 Developer Site
​Recently, I added a Content Query Web Part (CQWP) to a new page residing on a fresh instance of a SP2013 site collection. This site collection was created using the Developer Site template to allow for side-loading add-ins developed and deployed directly from Visual Studio. When accessing the web part settings, I encountered a generic error page with "Sorry, something went wrong. An unexpected error has occurred." Looking at the ULS logs using ULSViewer.exe, I found some logs that may point to the cause of the issue ​ConsoleUtilies.GetContextualControlMode had no currentPage so the current SPWebPartManager mode cannot be retrieved. Application error when access /sites/appdev/SitePages/Home.aspx, Error=Key cannot be null. Parameter name key at System.Collections.SortedList.IndexOfKey(Object key) at System.Collections.SortedList.ContainsKey(Object key) at Microsoft.SharePoint.Publishing.WebControls.ContentByQueryToolPart.AppendListTypes(SortedList sortedListItems, SPListTemplateCollection listTypes) at Microsoft.SharePoint.Publishing.WebControls.ContentByQueryToolPart.populateListTypeDropDown() at Microsoft.SharePoint.Publishing.WebControls.ContentByQueryToolPart.populateCBQControls() at Microsoft.SharePoint.Publishing.WebControls.ContentByQueryToolPart.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) With a bit of Google-fu, I found these links that discusses this issue and the possible fixes https//social.technet.microsoft.com/Forums/en-US/c51ba710-f314-40ad-8387-3cb8a302677dhttps//pholpar.wordpress.com/2016/05/29/content-query-web-part-issue-on-sharepoint-2013-developer-site-just-another-workaround/​ Based on the suggestions in the links, I disabled the Developer Site Collection Feature using this command in the SharePoint 2013 Management Shell and confirmed that the issue is resolved ​Disable-SPFeature -identity "E374875E-06B6-11E0-B0FA-57F5DFD72085" -URL <SiteUrl>




Posted on:
Categories: SharePoint
Description: How to enable IntelliSense in Visual Studio for SharePoint-addin project type
​Let me start this post with a statement I'm a developer and developers love IntelliSense. If you are a developer too, you know what I'm talking about! Recently, I created a SharePoint-hosted add-in in Visual Studio 2013. While starting to code, I recognized that IntelliSense wasn't working correctly. Here is an example I tried to create a new ClientContext, but as you can see, IntelliSense is not providing the proper functions nor properties In fact, all the JSOM functions and properties were missing, which makes coding a SharePoint-hosted add-in needlessly difficult. IntelliSense is a very helpful feature, and I refused to continue coding without proper IntelliSense. So I started some investigation to see why IntelliSense wasn't able to show the JSOM stuff. First, I checked the _references file which is usually located in the project's Scripts folder. This file was existing, and its content looked correct. In a nutshell, this file is referencing the SharePoint JSOM libraries and the files listed here are supposed to be added to the IntelliSense database – but for some reason, this isn't working for my current project. My next step was to check if the _references file is actually used by Visual Studio to feed the IntelliSense database. To check this, I opened <Tools> <Options> in Visual Studio. In the text box at the top, I typed in 'JavaScript' and the following dialog is shown I clicked on References just below IntelliSense to see which libraries were included to build the IntelliSense database. The file _references.js was included just like some other libraries. Although _references file was listed here, it was not used to update the IntelliSense database. The basic configuration looked ok, and so I was looking for a workaround to get IntelliSense to show the functions and properties of the JSOM objects. I manually downloaded the file SP.runtime.js from SharePoint and added it to the Scripts section of my Visual Studio project. Because I did not want this file to be re-deployed back to SharePoint each time the app gets deployed to SharePoint, I changed the Deployment Type of this file to NoDeployment and I repeated this with the file SP.js. Time for a new test - and this time IntelliSense was working as expected! Now the IntelliSense database has been updated properly and was able to show all functions and properties which are part of the JSOM libraries – and this makes coding with JSOM a lot easier! It looked like Visual Studio wasn't able to access the referenced files which are listed in _references.js. Adding them as downloaded copies to the Scripts folder in my Visual Studio project enables IntelliSense to access them and update its internal database. As long as those files are kept locally and are not deployed back to SharePoint, this workaround can be used to fix the initial IntelliSense problem. But keep in mind as those files are local copies now, they won't get updated automatically!




Posted on:
Categories: Office 365;SharePoint
Description: How to code Cross-Domain access within a SharePoint-hosted add-in
​Recently I was debugging a SharePoint-hosted add-in, which should be used in 'SharePoint online' and in 'SharePoint on-premises'. The initial situation was this the add-in was working on 'SharePoint online' as expected, but when trying to run it on SharePoint on-premises, an error was displayed immediately. The error reported that the response received from the server was unexpected. Because this error happened as soon as the add-in was trying to access the host web for the first time, I checked this part of the add-in's code first. Let's see what this code is doing. If you run a SharePoint add-in, SharePoint is providing some additional parameters via the URL. In my local development environment, the URL of the add-in looks like this http//app-af2e37d0f342ef.apps.domain.com/sites/ABAT/AP/Pages/Default.aspx?SPHostUrl=http//sp2013-ow/sites/ABAT&SPLanguage=en-US&SPClientTag=0&SPProductNumber=15.0.4797.1000&SPAppWebUrl=http//app-af2e37d0f342ef.apps.domain.com/sites/ABAT/AP The above code is trying to extract the values for SPHostUrl and SPAppWeb from this URL. "SPHostUrl" is the URL of the add-in's host web (the web that this add-in should be used in) and "SPAppWeb" is the URL of the internal web of this add-in (the web that add-ins are usually using to store their configuration data and that's not accessible to the common user). As you can see, both URLs are very different, and they are pointing to different domains. The host URL starts with http//sp2013-ow and the app web URL starts with http//app-af1e37d0f372ef.apps.domain.com. That's intended because SharePoint add-ins should be separated from common SharePoint webs and that's done by providing a separate app domain (it's usually called domain separation). In SharePoint on-premises, this app domain is configurable via the Central Administration, in SharePoint online the app domain is configured upon setting up the tenant automatically. If a SharePoint add-in needs to access its host web, it first needs to have proper permissions. Usually, this is done by adding the desired permissions to the AppManifest.xml file. The following image shows an example When using the add-in for the first time, a user needs to trust this add-in and while doing so, these permissions are granted to the add-in. But that's just the easy part. With proper permissions, the add-in is just allowed to access its host web, but some lines of code need to be added as well to finally enable the add-in to access the host web. We have just learned that SharePoint add-ins are usually existing in a different domain. To be able to access the web which is existing outside of the add-in's app domain, the add-in needs to perform something which is called Cross-Domain Access. Let's have a look back on the initial code. This code is trying to get access to the host web like this var hostWebContext = new SP.ClientContext(getRelativeUrlFromAbsolute(hostWebUrl)); The code takes the URL of the host web and creates a new ClientContext based on this URL. Although this does not take into account that we need to perform a Cross-Domain access, this line appears to be working in a SharePoint online environment. In other words getting the host web object like this is working in SharePoint online, although it is not the recommended way of doing this var hostWeb = hostWebContext.get_web(); You can find some posts on the internet providing the above code as a sample. I don't want to speculate, why this is working in a SharePoint online environment and not in a SharePoint on-premises environment. Let' focus on how to do it the right way. Microsoft published a great article which is explaining the details. You can access this article by clicking on this link. The recommended way how to access an add-in's host web is this // Get URL of the host web var hostWebUrl = getQueryStringParameter("SPHostUrl"); // Get URL of the app web (web where the add-in is living) appWebUrl = getQueryStringParameter("SPAppWebUrl"); // Get context of host web to be able to access it context = new SP.ClientContext(appWebUrl); var factory = new SP.ProxyWebRequestExecutorFactory(appWebUrl); context.set_webRequestExecutorFactory(factory); hostWebContextSite = new SP.AppContextSite(context, hostWebUrl); The first two steps are almost the same the URLs need to be extracted from the add-in's URL. The next step is that we need to create a new client context based on the URL of the add-in's web (not the host web!). To enable the Cross-Domain access, a new object of type ProxyWebRequestExecutorFactory needs to be created. This object will take care of handling the Cross-Domain access we are about to establish. To be able to create that object, another JavaScript library needs to be loaded first. This could be done within the content of the page or by manually loading the library. This is how the library is loaded when using the first approach The additional JavaScript library (SP.RequestExecutor.js) is loaded by the last line in the above snippet. This new RequestExecutor object is then passed to the client context by a call to set_webRequestExecutorFactory(). Now the client context can handle a Cross-domain access. Technically this is achieved by installing a local proxy object with is handling the cross-domain data transfer. But there is an additional subject we need to take care of too. The way we need to access an object which is existing in the host web, changed also. Instead of using this code var hostWeb = hostWebContext.get_web(); we now need to get a web object which is existing in the host web like this var oWeb = hostWebContextSite.get_web(); hostWebContext.load(files); hostWebContext.executeQueryAsync(onSuccess, onFail); As you can see the host web is accessed by using the hostWebContextSite object now – and not by using the context object directly. If you are accessing an add-in's host web like this, your add-in will run on SharePoint on-line and SharePoint on-premises – and probably won't have any issues with Cross-Domain access anymore. Before I close this blog post, here is another tip which usually helps me when debugging a SharePoint-hosted add-in. I usually change the onFail functions used by executeQueryAsync() like this function onFail(sender, args) // Called if operation not has been successful ReasonWhyFailed = args.get_message(); StackTrace = args.get_stackTrace(); With this modification, you will get a better error description if something went wrong.