Category Archives: SharePoint Server

How To : Plan the Deployment of Farm Solutions for SharePoint 2013

SharePoint 2013

While everyone is talking about Apps, there are still significant investments in Full Trust Solutions (a.k.a. Farm Solutions) and I am sure that many OnPrem deployments will want to carry these forward when upgrading to SharePoint 2013.  The new SharePoint 2013 upgrade model allows Sites to continue to run in 2010 mode after upgrading and each Site Collection explicitly has to be upgraded individually.

Not the way it worked in 2010 with Visual Upgrade, but this time there is actually both a 14 and 15 Root folder deployed and all the Features and Layout files from SharePoint 2010 are deployed as part of the 2013 installation.

For those of you new to SharePoint, the root folder is where SharePoint keeps most of its application files and the default location for this is “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\[SharePoint Internal Version]”, where the versions for the last releases have been 60 (6.0), 12, 14, and now 15. The location is also known as “The xx hive.

This is great in an upgrade scenario, where you may want to do a platform upgrade first or only want to share the new features of 2013 with a few users while maintaining an unchanged experience for the rest of the organization.  This also gives us the opportunity to have different functionality and features for sites running in 2010 and 2013 mode.  However, this requires some extra thought in the development and deployment process that I will give an introduction to here.

Because you can now have Sites running in both 2010 and 2013 mode, SharePoint 2013 introduces a new concept of a Compatibility Level.  Right now it can only be 14 or 15, but you can imagine that there is room for growth.  This Compatibility Level is available at Site Collection and Site (web) level and can be used in code constructs and PowerShell commands.  I will start by explaining how you use it while building and deploying wsp-files for SharePoint 2013 and then finish off with a few things to watch out for and some code tips.

Deployment Considerations

If you take your wsp-files from SharePoint 2010 and just deploy these with Add-SPSolution -> Install-SPSolution as you did in 2010, then SharePoint will assume it is a 2010 solution or a “14” mode solution.  If the level is not specified in the PowerShell command, it determines the level based on the value of the SharePointProductVersion attribute in the Solution manifest file of the wsp-package.  The value can currently be 15.0 or 14.0. If this attribute is missing, it will assume 14.0 (SharePoint 2010) and since this attribute did not exist in 2010, only very well informed people will have this included in existing packages.

For PowerShell cmdlets related to installing solutions and features, there is a new parameter called CompatibilityLevel. This can override the settings of the package itself and can assume the following values: 14, 15, New, Old, All and “14,15” (the latter currently also means All).

The parameter is available for Install-SPSolution, Uninstall-SPSolution, Install-SPFeature and Uninstall-SPFeature.  There is no way to specify “All” versions in the package itself – only the intended target – and therefore these parameters need to be specified if you want to deploy to both targets.

It is important to note that Compatibility Level impacts only files deployed to the Templates folder in the 14/15 Root folder. That is:  Features, Layouts-files, Images, ControlTemplates, etc.

This means that files outside of this folder (e.g. a WCF Service deployed to the ISAPI folder) will be deployed to the 15/ISAPI no matter what level is set in the manifest or PowerShell.  Files such as Assemblies in GAC/Bin and certain resource files will also be deployed to the same location regardless of the Compatibility Level.

It is possible to install the same solution in both 14 and 15 mode, but only if it is done in the same command – specifying Compatibility Level as either “All” or “14,15”.  If it is first deployed with 14 and then with 15, it will throw an exception.  It can be installed with the –Force parameter, but this is not recommended as it could hide other errors and lead to an unknown state for the system.

The following three diagrams illustrate where files go depending on parameters and attributes set (click on the individual images for a larger view). Thanks to the Ignite Team for creating these. I did some small changes from the originals to emphasize a few points.

CompatibilityLevelOld

CompatibilityLevelNew

CompatibilityLevelAll

When retracting the solutions, there is also an option to specify Compatibility Level.  If you do not specify this, it will retract all – both 14 and 15 files if installed.  When deployed to both levels, you can retract one, but the really important thing to understand here is that it will not only retract the files from the version folder, but also all version neutral files – such as Assemblies, ISAPI deployed files, etc. – leaving only the files from the Root folder you did not retract.

To plan for this, my suggestion would be the following during development/deployment:

  • If you want to only run sites in 2013 mode, then deploy the Solutions with CompatibilityLevel 15 or SharePointProductVersion 15.0.
  • If you want to run with both 2010 and 2013 mode, and want to share features and layout files, then deploy to both (All or “14,15”).
  • If you want to differentiate the files and features that are used in 2010 and 2013 mode, then the solutions should be split into two or three solutions:
    • One solution (“Xxx – SP2010”), which contains the files and features to be deployed to the 14 folder for 2010 mode.  including code-behind (for things like feature activation and Application pages), but excluding shared assemblies and files.
    • One solution (“Xxx – SP2013”), which contains the files and features to be deployed to the 15 folder for 2013 mode, including code-behind (for things like feature activation and Application pages), but excluding shared assemblies and files.
    • One solution (“Xxx – Common”), which contains shared files (e.g. common assemblies or web services). This solution would also include all WebApplication scoped features such as bin-deployed assemblies and assemblies with SafeControl entries.
  • If you only want to have two solutions for various reasons, the Common solution can be joined with the SP2013 solution as this is likely to be the one you will keep the longest.
  • The assemblies being used as code-files for the artifacts in SP2010 and SP2013 need to have different names or at least different versions to differentiate them. Web Parts need to go in the Common package and should be shared across the versions, however the installed Web Part templates can be unique to the version mode.

Things to watch out for…

There are a few issues that are worth being aware of that may be fixed in future updates, but you’ll need to watch out for these currently.  I’ve come across an issue where installing the same solution in both levels can go wrong.  If you install it with level All and then uninstall it with level 14 two times, the deployment logic will think that it completely removed the solution, but the files in the 15/Templates folder will still be there.

To recover from this, you can install it with –Force in the orphan level and then uninstall it.  Again, it is better to not get in this situation.

Another scenario that can get you in trouble is if you install a solution in one Compatibility Level (either through PowerShell Parameter or manifest file attribute) and then uninstall with the other level.  It will then remove the common files but leave the specific 14 or 15 folder files and display the solution as fully retracted.

Unfortunately there is no public API to query which Compatibility Levels a package is deployed to.  So you need to get it right the first time or as quickly as possible move to native 2013 mode and packages (this is where we all want to be anyway).

Code patterns

An additional tip is to look for hard coded paths in you custom code such as _layouts and _controltemplates.  The SPUtility class has been updated with static methods to help you parse the current location based on the upgrade status of the Site.   For example, SPUtility.ContextLayoutsFolder will give you the path to the correct layouts folder.  See the reference article on SPUtility properties for more examples.

Round up

I hope this gave you an insight into some of the things you need to consider when deploying Farm Solutions for SharePoint 2013. There are lots of scenarios that are not covered here. If you find some, please share these or share your concerns and I will try to add it as comments or an additional post.

How To : Implement Business Data Connectivity in SharePoint 2013

Business Data Connectivity

Business Connectivity Services is a centralized infrastructure in SharePoint 2013 and Office 2013 that supports integrated data solutions. With Business Connectivity Services, you can use SharePoint 2013 and Office 2013 clients as interfaces into data that doesn’t live in SharePoint 2013 itself. For example, this external data may be in a database and it is accessed by using the out-of-the-box Business Connectivity Services connector for that database.

DuetEnterpriseDesign[1]

Business Connectivity Services can also connect to data that is available through a web service, or data that is published as an OData source or many other types of external data. Business Connectivity Services does this through out-of-the box or custom connectors.

External Content Types in BCS

External content types are the core of BCS. They enable you to manage and reuse the metadata and behaviors of a business entity, such as Customer or Order, from a central location. They enable users to interact with that external data and process it in a more meaningful way.

For more information about using external content types in BCS, see External content types in SharePoint 2013.

How to Connect With SQL External Data Source

Open the SharePoint Designer 2013 and click on the open site icon:

Input the site URL which we need to open:

Enter your site credentials here:

Now we need to create the new external content type and here we have the options for changing the name of the content type and creating the connection for external data source:

And click on the hyperlink text “Click here to discover the external data source operations, now this window will open:

Click on the “Add Connection “button, we can create a new connection. Here we have the different options to select .NET Type, SQL Server, WCF Service.

Here we selected SQL server, now we need to provide the Server credentials:

Now, we can see all the tables and views from the database.

In this screen, we have the options for creating different types of operations against the database:

Click on the next button:

Parameters Configurations:

Options for Filter parameters Configuration:

Here we need to add new External List, Click on the “External List”:

Select the Site here and click ok button:

Enter the list name here and click ok button:

After that, refresh the SharePoint site, we can see the external list here and click on the list:

Here we have the error message “Access denied by Business Connectivity.”

Solution for this Error

SharePoint central admin, click on the Manage service application:

Click on the Business Data Connectivity Service:

Set the permission for this list:

Click ok after setting the permissions:

After that, refresh the site and hope this will work… but again, it has a problem. The error message like Login failed for user “NT AUTHORITY\ANONYMOUS LOGON”.

Solution for this Error

We need to edit the connection properties, the Authentication mode selects the value ‘BDC Identity’.

Then follow the below mentioned steps.

Open PowerShell and type the following lines:

$bdc = Get-SPServiceApplication | 
where {$_ -match “Business Data Connectivity Service”}
$bdc.RevertToSelfAllowed = $true
$bdc.Update();

Now it’s working fine.

And there is a chance for one more error like:

Database Connector has throttled the response.
The response from database contains more than '2000' rows. 
The maximum number of rows that can be read through Database Connector is '2000'. 
The limit can be changed via the 'Set-SPBusinessDataCatalogThrottleConfig' cmdlet

It’s because it depends on the number of recodes that exist in the table.

Solution for this Error

Follow the below steps:

Open PowerShell and type the following lines and execute:

$bcs = Get-SPServiceApplicationProxy | where{$_.GetType().FullName 
-eq (‘Microsoft.SharePoint.BusinessData.SharedService.’ + ‘BdcServiceApplicationProxy’)}
$BCSThrottle = Get-SPBusinessDataCatalogThrottleConfig -Scope database 
-ThrottleType items -ServiceApplicationProxy $bcs
Set-SPBusinessDataCatalogThrottleConfig -Identity $BCSThrottle -Maximum 1000000 -Default 20000

How To : Access SAP Business Data From Silverlight 4 Clients Using WCF RIA Services And LINQ to SAP

Introduction

The introduction of Microsoft’s WCF RIA Services for Silverlight 4 simplified very much the development process of N-tier business applications using Silverlight and ASP.NET. By using this new technology, we can also easily access and integrate SAP business data in Silverlight clients.

This article shows how to provide a SAP domain service as web service that will be consumed by a Silverlight client. The sample application will allow the user to query customer data. The service uses LINQ to SAP from Theobald Software to connect to a SAP R/3 system.

Project Setup

The first step in setting up a new Silverlight 4 project with WCF RIA Services is to create a solution using the Visual Studio template Silverlight Navigation Application:

Screenshot-01.png - Click to enlarge imageVisual Studio 2010 then asks you to create an additional web application, which hosts the Silverlight application. It’s important to select the checkbox Enable WCF RIA Services (see screenshot below):

SAP2Silverlight/Screenshot-02.pngAfter clicking the Ok button, Visual Studio generates a solution with two projects, one Silverlight 4 project and one ASP.NET project. In the next section, we will create the SAP data access layer using the LINQ to SAP designer.

LINQ to SAP

The LINQ to SAP provider and its Visual Studio 2010 designer offers a very handy way to design SAP interfaces visually. The designer will generate the code for the SAP data access layer automatically, similar to LINQ to SQL. The LINQ provider is part of the .NET library ERPConnect.net from Theobald Software. The company offers a demo version for download on its homepage.

The next step is to create the needed LINQ to SAP file by opening the Add New Item dialog:

Screenshot-03.png - Click to enlarge imageLINQ to SAP is internally called LINQ to ERP.

Clicking the Add button will create a new ERP file and opens the LINQ designer. Now, drag the Function object from the toolbox and drop it onto the designer surface. If you have not entered the SAP connection data so far, you are now asked to do so:

Screenshot-04.png - Click to enlarge imageEnter the connection data for your SAP R/3 system and then click the Ok button. Next, search for and select the SAP function module named SD_RFC_CUSTOMER_GET. The function module provides a list of customer data.

The RFC Function modules dialog opens and lets you define the necessary parameters:

SAP2Silverlight/Screenshot-05.pngIn the above function dialog, change the method name to GetCustomers and mark the Pass checkbox for theNAME1 parameter in the Exports tab. Also set the variable name to namePattern. On the Tables tab, mark the Return checkbox for the table parameter CUSTOMER_T and set the table and structure name to CustomerTable andCustomerRow:

SAP2Silverlight/Screenshot-06.pngAfter clicking the Ok button and saving the ERP file, the LINQ designer will generate a SAPContext class which contains a method called GetCustomers with an input parameter named namePattern. This method executes a search for SAP customer data allowing the user to enter a wildcard pattern. The method returns a table of customer data:

SAP2Silverlight/Screenshot-07.pngOn the LINQ designer level (click on the free part of the LINQ designer surface) property, Create Object Outside Of Context Class must be set to True:

Screenshot-08.png - Click to enlarge imageNow, we finally add a Customer class which we use in our SAP domain service later on. This class and its values will be transmitted to the Silverlight client by the WCF RIA Services. It’s important to set the Key attribute on the identifier fields for WCF RIA Services, otherwise the project will not compile:

Screenshot-09.png - Click to enlarge imageThat’s it! We now have our SAP data access layer ready to use and can start adding the domain service in the next section.

SAP Domain Service

The next step is to add the SAP domain service to our web project. A domain service is a specialized WCF service and is one of the core constructs of WCF RIA Services. The service exposes operations that can be called from the client generated code. On the client side, we use the domain context to access the domain service on the server side.

Add a new Domain Service Class and name it SAPService:

Screenshot-10.png - Click to enlarge imageIn the upcoming dialog, create an empty domain service class by just clicking the Ok button:

SAP2Silverlight/Screenshot-11.pngNext, we add the service operation GetCustomers to the SAP service with a name pattern parameter. The operation then returns a list of Customer objects. The Query attribute limits the result set to 200 entries.

The operation uses the visually designed SAP data access logic to retrieve the SAP customer data. First of all, an instance of the SAPContext class will be created using a connection string (see sample in code). For more details regarding the SAP connection string, see the ERPConnect.net manual.

The LINQ to SAP context class contains the GetCustomers method which we will call using the given namePatternparameter. Next, the operation creates an instance of the Customer class for each customer record returned by SAP.

The license code for the ERPConnect.net library is set in the constructor of our domain service class.

Screenshot-12.png - Click to enlarge imageThat’s all we need on the server side.

In the next section, we will implement the Silverlight client.

Silverlight Client

The implementation of the client side is straightforward. The home view contains a DataGrid control to display the list of customer data as well as a search area with TextBox and Button controls to allow users to enter name search pattern.

The click event handler of the load button, called OnLoadButtonClick, will execute the SAP service. The boilerplate code to access the web service was generated by WCF RIA Services in the subfolder Generated_Code in the Silverlight project.

First of all, an instance of the SAPContext will be created. Then, we load the query GetCustomersQuery and execute the service operation on the server side using WCF RIA Services. If the domain service returns an error, the callback anonymous method will mark the error as handled and display the error message.

If the execution of the service operation succeeded, the result set gets displayed in the DataGrid control.

Screenshot-13.png - Click to enlarge imageThe next screenshot shows the final result:

Screenshot-14.png - Click to enlarge imageThat’s it.

Summary

This article has shown how easily SAP customer data can be integrated within Silverlight clients using tools like WCF RIA Services and LINQ to SAP. It is quite simple to extend the SAP service to integrate all kinds of operations.

How To : Add a Promoted Links Web Part to SharePoint 2013 App Default page

This article helps you to add Promoted links web part to your default app page as the following figure:

 

To do this follow the following steps:
Open the shortcut menu for the project, and then choose Add, New Item
Add Picture Textbox, and two buttons to infopath form

 

In the Templates pane, choose the List template, and then choose the Add button :

Enter list name and choose the Create a non-customizable list based on an existing list type of option button, and then, in its list, choose Promoted links, and then choose the Finish button

Binding the CAPTCHA image
In Solution Explorer, under the list instance node, open the Elements.xml file.
Add the promoted links items as the following:
<?versionencodingutf-8?>
Elementsxmlnshttp://schemas.microsoft.com/sharepoint/
ListInstanceTitleMyPromotedLinks
OnQuickLaunch
TemplateType
FeatureId192efa95-e50c-475e-87ab-361cede5dd7f
Lists/MyPromotedLinks
DescriptionMy List Instance
FieldTitleTwitter</Field
FieldBackgroundImageLocation/PromotedLinksApp/Images/twitter.png
FieldDescriptionMuawiyah Shannak Twitter
FieldLinkLocationhttps://twitter.com/MuShannak</Field
FieldOrder</Field
</
FieldTitle</Field
FieldBackgroundImageLocation/PromotedLinksApp/Images/blogger.png
FieldDescriptionMuawiyah Shannak Blog
FieldLinkLocationhttp://mushannak.blogspot.com</Field
FieldOrder</Field
</
FieldTitleLinkedin</Field
FieldBackgroundImageLocation/PromotedLinksApp/Images/linkedin.png
FieldDescriptionMuawiyah Shannak Linkedin
FieldLinkLocationhttp://ae.linkedin.com/in/shannak</Field
FieldOrder</Field
</
</
</
<!–ListInstance
</Elements
In Solution Explorer, under the Pages node, open the Default.aspx file. Add following tags inside the PlaceHolderMain Place Holder:
WebPartPagesWebPartZone=”WebPartZone”runat=”server”FrameType=”None”>
WebPartPagesXsltListViewWebPart=”XsltListViewAppPromotedList”
runat=”server”ListUrl=”Lists/MyPromotedLinks”IsIncluded=”True”
NoDefaultStyle=”TRUE”Title=”Images used in switcher”
PageType=”PAGE_NORMALVIEW”Default=”False”
ViewContentTypeId=”0x”>
</WebPartPagesXsltListViewWebPart
</WebPartPagesWebPartZone

Deploy a solution and you will find nice promoted links web part in the app default page!

New Highly Customisable SharePoint CRM Template Available

A CRM/Project Management Site Template for SharePoint 2010 Enterprise or SharePoint Online tenants.
This extensive solution offers the following features:

  • User Friendly – Due to a custom User Interface & Pre-Populated InfoPath forms where possible
  • Contacts Management
  • Project Management – Associated sub tasks, documents & sales
  • Products & Services Catalog
  • Sales Register & Invoice Generation
  • Client Enquiry – Showing any items related to a client
  • Reporting
  • Integrated User Guide

To give you an idea of how SharePoint CRM looks, below is a selection of screenshots:

Home Screen

The buttons displayed are defined by a list within SharePoint CRM so can easily be modified.

Contacts

Project Management

Sales Register

Being SharePoint all aspects of the SharePoint CRM Template can be customised to meet your organisations needs:

For example, to customise the home page :

Customising your homepage

Rather than the buttons on the homepage being hardcoded, they are defined by a list within the CRM site. This means you can easily add/remove/edit buttons using just your web browser, here’s how to do so:

  1. From the Site Actions, choose View All Site Content
  2. Open the PortalMenu list

Items can then be edited in the same way as any other SharePoint 2010 list, below is a description of options available:

  • Section – Defines which section the button will be shown in on your homepage
  • Order – Defines the order of the buttons within a section
  • Button Name – The text that will be displayed within the button
  • Link – The URL that users will be taken to when the button is clicked
  • Hover – The text shown when a user hovers over a button
  • Dialog – Specifies whether the page that the button links to will open in a popup dialog box
  • New Project Form – If this option is checked the Link field will be ignored an the button will open the new project form.

Adding new Sections

New sections can be added to the homepage, but will required the use of SharePoint Designer to do so:

  1. Open the PortalMenu list as described above, the go to the List Settings page
  2. Edit the Section column, then add the name of your new section as a choice
  3. Create a new list item with your new choice set within the Section field
  4. Navigate to your homepage, and set the page to edit mode
  5. Export any sections web part, then import the web part and add it to any zone
  6. Using SharePoint Designer open the homepage (SitePages\default.aspx)
  7. Select the new web part, the update the filter to match the new choice you added to the section column

This template and other SharePoint Web Parts, Apps, Custom SharePoint Templates, Tools for SharePoint, Azure and Office365 is available by contacting me through my website at http://sharepointsamurai,wordpress.com/ 

SharePoint Online: Software Boundaries, Limits and Planning Guide

This article describes some important limitations that you might need to know for different SharePoint Online plans in Office 365.
For example, it provides information about number of supported users, storage quotas, and file-size limits. This article covers a range of plans:
SharePoint Online in Office 365 Small Business and in Office 365 Enterprise, plus standalone plans.
The limits that are listed are for paid subscriptions. You might see different limits for trial plans andSharePoint Online preview sites. 

Note    In Office 365 plans, software boundaries and limits for SharePoint Online are managed separately from mailbox storage limits. Mailbox storage limits are set up and managed by using Exchange Online. For more information about how Exchange manages mailbox limits, see Mailbox types and storage limits for Recipients.

In this article

SharePointOnline2L-1[1]

 

SharePoint Online Feature availability

Need help determining which SharePoint solution best fits your organization’s needs?

The various Office 365 plans include different SharePoint Online offerings. These include:

  • SharePoint Online for Office 365 Small Business
  • SharePoint Online for Office 365 Midsize Business
  • SharePoint Online for Office 365 Enterprise, Education, and Government

You can choose the plan that best fits your organization’s needs. Each person who accesses the SharePoint Online service must be assigned to a subscription plan. SharePoint Online can be included in a Microsoft Office 365 plan, or it can be purchased as a standalone plan, such as SharePoint Enterprise Plan 1 or SharePoint Enterprise Plan 2.

Limits in SharePoint Online in Office 365 plans

In this section:

Limits for SharePoint Online for Office 365 Small Business

SharePoint Online Small Business and SharePoint Online Small Business Premium have common boundaries and limits. The following table describes those limits.

Feature Description
Storage per user (contributes to total storage base of tenant) 500 megabytes (MB) per subscribed user.
Site collection quota limit Up to 1 TB per site collection. (25 GB for a trial).

5,000 items in site libraries, including files and folders.

The minimum storage allocation per site collection is 100 MB.

Site collections (#) per tenant 1 site collection per tenant.
Subsites Up to 2,000 subsites per site collection
Total available tenant storage 10 GB + 500 MB per user.

For example, if you have 10 users, the base storage allocation is 15 GB (10 GB + 500 MB * 10 users).

You can purchase additional storage up to a maximum of 1TB.

Personal site storage 1 TB per user, as soon as provisioned.

This amount is counted separately, and does not add to or subtract from the overall storage allocation for a tenant. Personal site storage applies to a user’s OneDrive for Business library and personal newsfeed. For more information, see Additional information about OneDrive for Business limits.

Public Website storage default 5 GB

A SharePoint admin can allocate up to 1 TB (the limit for a site collection).

File upload limit 2 GB per file.
File attachment size limit 250 MB
Sync limits 20,000 items in the OneDrive for Business library, including files and folders.

5,000 items in site libraries, including files and folders.

Number of users 1 – 25 users
Number of external users invitees There is no limit to number of external users you can invite to your SharePoint Online site Collections. For more information, see Manage external sharing for your SharePoint Online environment

When reviewing the information on the previous table, remember that the base storage limits for Office 365 for Small Business (10 GB + 500 MB per subscribed user) will affect some of these values. For example, although SharePoint Online for Small Business imposes a limit of 1 TB per site collection, your particular tenant might not have enough storage available to contain a site collection of 1 TB.

 

 Important    It’s a good idea to monitor the Recycle Bin and empty it regularly. Content in the Recycle Bin is counted against the storage quota for a tenant. For example, if the Recycle Bin on a site contains 5 GB of content, that 5 GB is subtracted from the available storage.

 

Limits for SharePoint Online for Office 365 Midsize Business

The following table shows the software boundaries and limits for the SharePoint Online Midsize Business plan.

Feature Description
Storage per user (contributes to total storage base of tenant) 500 megabytes (MB) per subscribed user.
Storage base per tenant 10 GB + 500 MB per subscribed user.

For example, if you have 250 users, the base storage allocation is 135 GB (10 GB + 500 MB * 250 users).

You can purchase additional storage up to a maximum of 20 TB.

Additional storage at a cost per GB per month. To buy storage, see Change storage space for your subscription.

 Important    You can’t buy additional storage for a trial subscription.

Site collection quota limit Up to 1 TB per site collection. (25 GB for a trial).

5,000 items in site libraries, including files and folders.

SharePoint admins can set storage limits for site collections and sites. The minimum storage allocation per site collection is 100 MB.

Site collections (#) per tenant 20 site collections (other than personal sites).
Subsites Up to 2,000 subsites per site collection.
Personal site storage 1TB per user, as soon as provisioned.

Personal site storage applies to a user’s OneDrive for Business library and personal newsfeed. This amount is counted separately, and does not add to or subtract the overall storage allocation for a tenant. For more information about OneDrive for Business, see Additional information about OneDrive for Business limits later in this article.

Public Website storage default 5 GB

A SharePoint admin can allocate up to 1 TB (the limit for a site collection).

File upload limit 2 GB per file.
File attachment size limit 250 MB
Sync limits 20,000 items in the OneDrive for Business library, including files and folders.

5,000 items in site libraries, including files and folders.

Number of users 1 – 250 users
Number of external user invitees There is no limit to number of external users you can invite to your SharePoint Online site Collections. For more information see, Manage external sharing for your SharePoint Online environment

When reviewing the information on the previous table, remember that the base storage limits for Office 365 for Midsize Business (10 GB + 500 MB per subscribed user) will affect some of these values. For example, although SharePoint Online for Midsize Business imposes a limit of 1 TB per site collection and a limit of 20 site collections, your particular tenant might not have enough storage available to contain 20 site collections of 1 TB each.

 Important    It’s a good idea to monitor the Recycle Bin and empty it regularly. Content in the Recycle Bin is counted against the storage quota for a tenant. For example, if the Recycle Bin on a site contains 25 GB of content, that 25 GB is subtracted from the available storage.

 

 

Limits for SharePoint Online for Office 365 Enterprise, Education, and Government

One or more Office 365 subscriptions plans can be included as part of your subscription. This is true for the following plan offerings:

  • Microsoft Office 365 Enterprise subscriptions (E1 – E4)
  • Microsoft Office 365 Government subscriptions (G1 – G4)
  • Microsoft Office 365 Education subscriptions (A2 – A4)
  • Microsoft Office 365 Kiosk subscriptions (K1-K2)
  • SharePoint Online stand-alone subscription plans (Plan 1 and Plan 2).

 

These plans have common boundaries and limits. The following table describes those limits.

 

 

Feature Office 365 Enterprise plans (including E1 – E4, A2-A4, G1-G4, and SharePoint Online Plan 1 and Plan 2) Office 365 Kiosk plans (Enterprise and Government K1 – K2)
Storage per user (contributes to total storage base of tenant) 500 megabytes (MB) per subscribed user. Zero (0).

Licensed Kiosk Workers do not add to the tenant storage base.

Additional storage (per GB per month); no minimum purchase To buy storage, see Change storage space for your subscription.

 Important    You can’t buy additional storage for a trial subscription.

To buy storage, see Change storage space for your subscription.

 Important    You can’t buy additional storage for a trial subscription.

Storage base per tenant 10 GB + 500 MB per subscribed user + additional storage purchased.

For example, if you have 10,000 users, the base storage allocation is approximately 5 TB (10 GB + 500 MB * 10,000 users).

You can purchase an unlimited amount of additional storage.

 Important    If you have a Government Community Cloud plan, you can purchase additional storage up to 25 TB.

10 GB + additional storage purchased.

You can purchase an unlimited amount of additional storage.

 Important    If you have a Government Community Cloud plan, you can purchase additional storage up to 25 TB.

Site collection storage limit Up to 1 TB per site collection. (25 GB for trial).

SharePoint admins can set storage limits for site collections and sites. The minimum storage allocation per site collection is 100 MB.

5,000 items in site libraries, including files and folders.

 Important    If you have a Government Community Cloud plan, the limit is 100 GB.

Up to 1 TB per site collection. (25 GB for a trial). SharePoint admins can set storage limits for site collections and sites. The minimum storage allocation per site collection is 100 MB.

 Important    If you have a Government Community Cloud plan, the limit is 100 GB.

Kiosk workers (plans K1-K2) cannot administer SharePoint site collections. You will need a license for at least one Enterprise plan user to manage Kiosk site collections.

Site collections (#) per tenant 500,000 site collections (other than personal sites). 500,000 site collections.
Subsites Up to 2,000 subsites per site collection Up to 2,000 subsites per site collection
Personal site storage 1 TB per user (100 GB for government plans), as soon as provisioned.

Personal site storage applies to a user’s OneDrive for Business library and personal newsfeed. This amount is counted separately, and does not add to or subtract the overall storage allocation for a tenant.

For more information about OneDrive for Business, see Additional information about OneDrive for Business limits later in this article.

Not available.
Public Website storage default 5 GB

A SharePoint admin can allocate up to 1 TB (the limit for a site collection).

5 GB

A SharePoint admin can allocate up to 1 TB (the limit for a site collection).

Kiosk workers (plans K1-K2) cannot administer Sharepoint site collections. You will need a license for at least one Enterprise plan user to manage Kiosk site collections.

File upload limit 2 GB per file. 2 GB per file.
File attachment size limit 250 MB 250 MB
Sync limits 20,000 items in the OneDrive for Business library, including files and folders.

5,000 items in site libraries, including files and folders.

20,000 items in the OneDrive for Business library, including files and folders.

5,000 items in site libraries, including files and folders.

Maximum number of users per tenant 1 – 500,000+

 Note    If you have more than 500,000 users, please contact the Microsoft representative to discuss detailed requirements.

1 – 500,000+

 Note    If you have more than 500,000 users, please contact the Microsoft representative to discuss detailed requirements.

Number of external user invitees There is no limit to number of external users you can invite to your SharePoint Online site Collections. For more information, see Manage external sharing for your SharePoint Online environment There is no limit to number of external users you can invite to your SharePoint Online site Collections. For more information, see Manage external sharing for your SharePoint Online environment

When reviewing the information on the previous table, remember that the base storage limits for Office 365 for Enterprises (10 GB + 500 MB per subscribed user) will affect some of these values. For example, although SharePoint Online for Enterprise plans imposes a limit of 1 TB per site collection and a limit of 500,000 site collections, your particular tenant might not have enough storage available to contain 500,000 site collections of 1 TB each.

 Important    It’s a good idea to monitor the Recycle Bin and empty it regularly. Content in the Recycle Bin is counted against the storage quota for a tenant. For example, if the Recycle Bin on a site contains 25 GB of content, that 25 GB is subtracted from the available storage.

 

 

Limits for site elements in SharePoint Online

There are also limits for site elements of a SharePoint Online site. Here are some examples:

  • List and Library limits    Different types of columns have different limitations. For example, you can have up to 276 columns in a list for columns that contain a single line of text.
  • Page limits    You can add up to 25 Web Parts to a single wiki or web page.
  • Security limits    Different security features have different limits. For example, a single user can belong to no more than 5,000 security groups.

 

The specific elements for the previous site elements are too numerous to list here, but you can learn more about them in the TechNet article Software Boundaries and Limits for SharePoint 2013. In this linked article, only the sections on List and Library Limits, Page Limits, and Security Limits apply to SharePoint Onl

 

Additional information about OneDrive for Business limits

Each user in SharePoint Online for Office 365 gets an individual storage allocation of 1 TB for personal site content (100 GB for government plans). Personal sites include the user’s OneDrive for Business library, a Recycle Bin, and personal newsfeed information.

All SharePoint Online in Office 365 plans include the same storage allocation for individual personal sites. This storage allocation is separate from the tenant allocation.

For more information about how users can manage their individual OneDrive for Business allocation, see OneDrive for Business library limits.

 

 

Additional Resources

 

For information about this: Go here:
Office 365 connectivity limits To learn more about Internet bandwidth, port and protocol considerations for Office 365 plans, see Office 365 Ports and Protocols.
SharePoint feature availability To learn more about SharePoint feature availability and the SharePoint Online service in Office 365, see SharePoint Online Service Descriptions.
SharePoint Online search limits To learn more about the search limits for SharePoint Online, see Search limits for SharePoint Online.
Mobile devices To learn more about opening a SharePoint Online site from a mobile device, see Use a mobile device to work with SharePoint Online sites.
File types To learn about file types that you can’t add to a list, see Types of files that cannot be added to a list or library.
Online URLs To learn about SharePoint Online addresses, see SharePoint Online URLs and IP Addresses.
Site languages To learn how to set language for your sites, see Change your language and region settings.
Planning and deploying SharePoint Online
Change storage space

 Important    You can’t buy additional storage for a trial subscription.

How To : SharePoint Cross-site Publishing and Free code for Web Part

Cross-site publishing is one of the powerful new capabilities in SharePoint 2013.  It enables the separation of data entry from display and breaks down the container barriers that have traditionally existed in SharePoint (ex: rolling up information across site collections). 

 cross-site-publishing

Cross-site publishing is delivered through search and a number of new features, including list/library catalogs, catalog connections, and the content search web part.  Unfortunately, SharePoint Online/Office 365 doesn’t currently support these features.  Until they are added to the service (possibly in a quarterly update), customers will be looking for alternatives to close the gap.  In this post, I will outline several alternatives for delivering cross-site and search-driven content in SharePoint Online and how to template these views for reuse

I’m a huge proponent of SharePoint Online.  After visiting several Microsoft data centers, I feel confident that Microsoft is better positioned to run SharePoint infrastructure than almost any organization in the world.  SharePoint Online has very close feature parity to SharePoint on-premise, with the primary gaps existing in cross-site publishing and advanced business intelligence.  Although these capabilities have acceptable alternatives in the cloud (as will be outlined in this post), organizations looking to maximize the cloud might consider SharePoint running in IaaS for immediate access to these features.

 

Apps for SharePoint

The new SharePoint app model is fully supported in SharePoint Online and can be used to deliver customizations to SharePoint using any web technology.  New SharePoint APIs can be used with the app model to deliver an experience similar to cross-site publishing.  In fact, the content search web part could be re-written for delivery through the app model as an “App Part” for SharePoint Online. 
Although the app model provides great flexibility and reuse, it does come with some drawbacks.  Because an app part is delivered through a glorified IFRAME, it would be challenging to navigate to a new page from within the app part.  A link within the app would only navigate within the IFRAME (not the parent of the IFRAME).  Secondly, there isn’t a great mechanism for templating a site to automatically leverage an app part on its page(s).  Apps do not work with site templates, so a site that contains an app cannot be saved as a template.  Apps can be “stapled” to sites, but the app installed event (which would be needed to add the app part to a page) only fires when the app is installed into the app catalog.

REST APIs and Script Editor

The script editor web part is a powerful new tool that can help deliver flexible customization into SharePoint Online.  The script editor web part allows a block of client-side script to be added to any wiki or web part page in a site.  Combined with the new SharePoint REST APIs, the script editor web part can deliver mash-ups very similar to cross-site publishing and the content search web part.  Unlike apps for SharePoint, the script editor isn’t constrained by IFRAME containers, app permissions, or templating limitations.  In fact, a well-configured script editor web part could be exported and re-imported into the web part gallery for reuse.

Cross-site publishing leverages “catalogs” for precise querying of specific content.  Any List/Library can be designated as a catalog.  By making this designation, SharePoint will automatically create managed properties for columns of the List/Library and ultimately generate a search result source in sites that consume the catalog.  Although SharePoint Online doesn’t support catalogs, it support the building blocks such as managed properties and result sources.  These can be manually configured to provide the same precise querying in SharePoint Online and exploited in the script editor web part for display.

Calling Search REST APIs

<div id=”divContentContainer”></div>
<script type=”text/javascript”>
    $(document).ready(function ($) {
        var basePath = “https://tenant.sharepoint.com/sites/somesite/_api/&#8221;;
        $.ajax({
            url: basePath + “search/query?Querytext=’ContentType:News'”,
            type: “GET”,
            headers: { “Accept”: “application/json;odata=verbose” },
            success: function (data) {
                //script to build UI HERE
            },
            error: function (data) {
                //output error HERE
            }
        });
    });
</script>

 

An easier approach might be to directly reference a list/library in the REST call of our client-side script.  This wouldn’t require manual search configuration and would provide real-time publishing (no waiting for new items to get indexed).  You could think of this approach similar to a content by query web part across site collections (possibly even farms) and the REST API makes it all possible!

List REST APIs

<div id=”divContentContainer”></div>
<script type=”text/javascript”>
    $(document).ready(function ($) {
        var basePath = “https://tenant.sharepoint.com/sites/somesite/_api/&#8221;;
        $.ajax({
            url: basePath + “web/lists/GetByTitle(‘News’)/items/?$select=Title&$filter=Feature eq 0”,
            type: “GET”,
            headers: { “Accept”: “application/json;odata=verbose” },
            success: function (data) {
                //script to build UI HERE
            },
            error: function (data) {
                //output error HERE
            }
        });
    });
</script>

 

The content search web part uses display templates to render search results in different arrangements (ex: list with images, image carousel, etc).  There are two types of display templates the content search web part leverages…the control template, which renders the container around the items, and the item template, which renders each individual item in the search results.  This is very similar to the way a Repeater control works in ASP.NET.  Display templates are authored using HTML, but are converted to client-side script automatically by SharePoint for rendering.  I mention this because our approach is very similar…we will leverage a container and then loop through and render items in script.  In fact, all the examples in this post were converted from display templates in a public site I’m working on. 

Item display template for content search web part

<!–#_
var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + “_ImageTitle_”);
var rem = index % 3;
var even = true;
if (rem == 1)
    even = false;

var pictureURL = $getItemValue(ctx, “Picture URL”);
var pictureId = encodedId + “picture”;
var pictureMarkup = Srch.ContentBySearch.getPictureMarkup(pictureURL, 140, 90, ctx.CurrentItem, “mtcImg140”, line1, pictureId);
var pictureLinkId = encodedId + “pictureLink”;
var pictureContainerId = encodedId + “pictureContainer”;
var dataContainerId = encodedId + “dataContainer”;
var dataContainerOverlayId = encodedId + “dataContainerOverlay”;
var line1LinkId = encodedId + “line1Link”;
var line1Id = encodedId + “line1”;
 _#–>
<div style=”width: 320px; float: left; display: table; margin-bottom: 10px; margin-top: 5px;”>
   <a href=”_#= linkURL =#_”>
      <div style=”float: left; width: 140px; padding-right: 10px;”>
         <img src=”_#= pictureURL =#_” class=”mtcImg140″ style=”width: 140px;” />
      </div>
      <div style=”float: left; width: 170px”>
         <div class=”mtcProfileHeader mtcProfileHeaderP”>_#= line1 =#_</div>
      </div>
   </a>
</div>

 

Script equivalent

<div id=”divUnfeaturedNews”></div>
<script type=”text/javascript”>
    $(document).ready(function ($) {
        var basePath = “https://richdizzcom.sharepoint.com/sites/dallasmtcauth/_api/&#8221;;
        $.ajax({
            url: basePath + “web/lists/GetByTitle(‘News’)/items/?$select=Title&$filter=Feature eq 0”,
            type: “GET”,
            headers: { “Accept”: “application/json;odata=verbose” },
            success: function (data) {
                //get the details for each item
                var listData = data.d.results;
                var itemCount = listData.length;
                var processedCount = 0;
                var ul = $(“<ul style=’list-style-type: none; padding-left: 0px;’ class=’cbs-List’>”);
                for (i = 0; i < listData.length; i++) {
                    $.ajax({
                        url: listData[i].__metadata[“uri”] + “/FieldValuesAsHtml”,
                        type: “GET”,
                        headers: { “Accept”: “application/json;odata=verbose” },
                        success: function (data) {
                            processedCount++;
                            var htmlStr = “<li style=’display: inline;’><div style=’width: 320px; float: left; display: table; margin-bottom: 10px; margin-top: 5px;’>”;
                            htmlStr += “<a href=’#’>”;
                            htmlStr += “<div style=’float: left; width: 140px; padding-right: 10px;’>”;
                            htmlStr += setImageWidth(data.d.PublishingRollupImage, ‘140’);
                            htmlStr += “</div>”;
                            htmlStr += “<div style=’float: left; width: 170px’>”;
                            htmlStr += “<div class=’mtcProfileHeader mtcProfileHeaderP’>” + data.d.Title + “</div>”;
                            htmlStr += “</div></a></div></li>”;
                            ul.append($(htmlStr))
                            if (processedCount == itemCount) {
                                $(“#divUnfeaturedNews”).append(ul);
                            }
                        },
                        error: function (data) {
                            alert(data.statusText);
                        }
                    });
                }
            },
            error: function (data) {
                alert(data.statusText);
            }
        });
    });

    function setImageWidth(imgString, width) {
        var img = $(imgString);
        img.css(‘width’, width);
        return img[0].outerHTML;
    }
</script>

 

Even one of the more complex carousel views from my site took less than 30min to convert to the script editor approach.

Advanced carousel script

<div id=”divFeaturedNews”>
    <div class=”mtc-Slideshow” id=”divSlideShow” style=”width: 610px;”>
        <div style=”width: 100%; float: left;”>
            <div id=”divSlideShowSection”>
                <div style=”width: 100%;”>
                    <div class=”mtc-SlideshowItems” id=”divSlideShowSectionContainer” style=”width: 610px; height: 275px; float: left; border-style: none; overflow: hidden; position: relative;”>
                        <div id=”divFeaturedNewsItemContainer”>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<script type=”text/javascript”>
    $(document).ready(function ($) {
        var basePath = “https://richdizzcom.sharepoint.com/sites/dallasmtcauth/_api/&#8221;;
        $.ajax({
            url: basePath + “web/lists/GetByTitle(‘News’)/items/?$select=Title&$filter=Feature eq 1&$top=4”,
            type: “GET”,
            headers: { “Accept”: “application/json;odata=verbose” },
            success: function (data) {
                var listData = data.d.results;
                for (i = 0; i < listData.length; i++) {
                    getItemDetails(listData, i, listData.length);
                }
            },
            error: function (data) {
                alert(data.statusText);
            }
        });
    });
    var processCount = 0;
    function getItemDetails(listData, i, count) {
        $.ajax({
            url: listData[i].__metadata[“uri”] + “/FieldValuesAsHtml”,
            type: “GET”,
            headers: { “Accept”: “application/json;odata=verbose” },
            success: function (data) {
                processCount++;
                var itemHtml = “<div class=’mtcItems’ id=’divPic_” + i + “‘ style=’width: 610px; height: 275px; float: left; position: absolute; border-bottom: 1px dotted #ababab; z-index: 1; left: 0px;’>”
                itemHtml += “<div id=’container_” + i + “‘ style=’width: 610px; height: 275px; float: left;’>”;
                itemHtml += “<a href=’#’ title='” + data.d.Caption_x005f_x0020_x005f_Title + “‘ style=’width: 610px; height: 275px;’>”;
                itemHtml += data.d.Feature_x005f_x0020_x005f_Image;
                itemHtml += “</a></div></div>”;
                itemHtml += “<div class=’titleContainerClass’ id=’divTitle_” + i + “‘ data-originalidx='” + i + “‘ data-currentidx='” + i + “‘ style=’height: 25px; z-index: 2; position: absolute; background-color: rgba(255, 255, 255, 0.8); cursor: pointer; padding-right: 10px; margin: 0px; padding-left: 10px; margin-top: 4px; color: #000; font-size: 18px;’ onclick=’changeSlide(this);’>”;
                itemHtml += data.d.Caption_x005f_x0020_x005f_Title;
                itemHtml += “<span id=’currentSpan_” + i + “‘ style=’display: none; font-size: 16px;’>” + data.d.Caption_x005f_x0020_x005f_Body + “</span></div>”;
                $(‘#divFeaturedNewsItemContainer’).append(itemHtml);

                if (processCount == count) {
                    allItemsLoaded();
                }
            },
            error: function (data) {
                alert(data.statusText);
            }
        });
    }
    window.mtc_init = function (controlDiv) {
        var slideItems = controlDiv.children;
        for (var i = 0; i < slideItems.length; i++) {
            if (i > 0) {
                slideItems[i].style.left = ‘610px’;
            }
        };
    };

    function allItemsLoaded() {
        var slideshows = document.querySelectorAll(“.mtc-SlideshowItems”);
        for (var i = 0; i < slideshows.length; i++) {
            mtc_init(slideshows[i].children[0]);
        }

        var div = $(‘#divTitle_0’);
        cssTitle(div, true);
        var top = 160;
        for (i = 1; i < 4; i++) {
            var divx = $(‘#divTitle_’ + i);
            cssTitle(divx, false);
            divx.css(‘top’, top);
            top += 35;
        }
    }

 

bottlenecks[1]

 

    function cssTitle(div, selected) {
        if (selected) {
            div.css(‘height’, ‘auto’);
            div.css(‘width’, ‘300px’);
            div.css(‘top’, ’10px’);
            div.css(‘left’, ‘0px’);
            div.css(‘font-size’, ’26px’);
            div.css(‘padding-top’, ‘5px’);
            div.css(‘padding-bottom’, ‘5px’);
            div.find(‘span’).css(‘display’, ‘block’);
        }
        else {
            div.css(‘height’, ’25px’);
            div.css(‘width’, ‘auto’);
            div.css(‘left’, ‘0px’);
            div.css(‘font-size’, ’18px’);
            div.css(‘padding-top’, ‘0px’);
            div.css(‘padding-bottom’, ‘0px’);
            div.find(‘span’).css(‘display’, ‘none’);
        }
    }

    window.changeSlide = function (item) {
        //get all title containers
        var listItems = document.querySelectorAll(‘.titleContainerClass’);
        var currentIndexVals = { 0: null, 1: null, 2: null, 3: null };
        var newIndexVals = { 0: null, 1: null, 2: null, 3: null };

        for (var i = 0; i < listItems.length; i++) {
            //current Index
            currentIndexVals[i] = parseInt(listItems[i].getAttribute(‘data-currentidx’));
        }

        var selectedIndex = 0; //selected Index will always be 0
        var leftOffset = ”;
        var originalSelectedIndex = ”;

        var nextSelected = ”;
        var originalNextIndex = ”;

        if (item == null) {
            var item0 = document.querySelector(‘[data-currentidx=”‘ + currentIndexVals[0] + ‘”]’);
            originalSelectedIndex = parseInt(item0.getAttribute(‘data-originalidx’));
            originalNextIndex = originalSelectedIndex + 1;
            nextSelected = currentIndexVals[0] + 1;
        }
        else {
            nextSelected = item.getAttribute(‘data-currentidx’);
            originalNextIndex = item.getAttribute(‘data-originalidx’);
        }

        if (nextSelected == 0) { return; }

        for (i = 0; i < listItems.length; i++) {
            if (currentIndexVals[i] == selectedIndex) {
                //this is the selected item, so move to bottom and animate
                var div = $(‘[data-currentidx=”0″]’);
                cssTitle(div, false);
                div.css(‘left’, ‘-400px’);
                div.css(‘top’, ‘230px’);

                newIndexVals[i] = 3;
                var item0 = document.querySelector(‘[data-currentidx=”0″]’);
                originalSelectedIndex = item0.getAttribute(‘data-originalidx’);

                //annimate
                div.delay(500).animate(
                    { left: ‘0px’ }, 500, function () {
                    });
            }
            else if (currentIndexVals[i] == nextSelected) {
                //this is the NEW selected item, so resize and slide in as selected
                var div = $(‘[data-currentidx=”‘ + nextSelected + ‘”]’);
                cssTitle(div, true);
                div.css(‘left’, ‘-610px’);

                newIndexVals[i] = 0;

                //annimate
                div.delay(500).animate(
                    { left: ‘0px’ }, 500, function () {
                    });
            }
            else {
                //move up in queue
                var curIdx = currentIndexVals[i];
                var div = $(‘[data-currentidx=”‘ + curIdx + ‘”]’);

                var topStr = div.css(‘top’);
                var topInt = parseInt(topStr.substring(0, topStr.length – 1));

                if (curIdx != 1 && nextSelected == 1 || curIdx > nextSelected) {
                    topInt = topInt – 35;
                    if (curIdx – 1 == 2) { newIndexVals[i] = 2 };
                    if (curIdx – 1 == 1) { newIndexVals[i] = 1 };
                }

                //move up
                div.animate(
                    { top: topInt }, 500, function () {
                    });
            }
        };

        if (originalNextIndex < 0)
            originalNextIndex = itemCount – 1;

        //adjust pictures
        $(‘#divPic_’ + originalNextIndex).css(‘left’, ‘610px’);
        leftOffset = ‘-610px’;

        $(‘#divPic_’ + originalSelectedIndex).animate(
            { left: leftOffset }, 500, function () {
            });

        $(‘#divPic_’ + originalNextIndex).animate(
            { left: ‘0px’ }, 500, function () {
            });

        var item0 = document.querySelector(‘[data-currentidx=”‘ + currentIndexVals[0] + ‘”]’);
        var item1 = document.querySelector(‘[data-currentidx=”‘ + currentIndexVals[1] + ‘”]’);
        var item2 = document.querySelector(‘[data-currentidx=”‘ + currentIndexVals[2] + ‘”]’);
        var item3 = document.querySelector(‘[data-currentidx=”‘ + currentIndexVals[3] + ‘”]’);
        if (newIndexVals[0] != null) { item0.setAttribute(‘data-currentidx’, newIndexVals[0]) };
        if (newIndexVals[1] != null) { item1.setAttribute(‘data-currentidx’, newIndexVals[1]) };
        if (newIndexVals[2] != null) { item2.setAttribute(‘data-currentidx’, newIndexVals[2]) };
        if (newIndexVals[3] != null) { item3.setAttribute(‘data-currentidx’, newIndexVals[3]) };
    };
</script>

 

End-result of script editors in SharePoint Online

Separate authoring site collection

Final Thoughts

How to connect a SharePoint 2013 Document Library to Outlook 2013

 

How to connect a SharePoint 2013 Document Library to Outlook 2013One of the key methods of gaining User Adoption of SharePoint is ensuring and pushing the integration it has with Microsoft Office to information workers. After all, information workers generally use Outlook as their ‘mother-ship’. Getting those users to switch immediately to SharePoint or, asking them to visit a document library in a SharePoint site which they will need to access could take time, especially since it means opening a browser, navigating to the site, covering their beloved Outlook client in the process.

 

The following describes how to connect a typical SharePoint 2013 document library to Outlook 2013 client.

  1. Access your SharePoint site; go into the relevant Documents library. In the below example, I clicked on the default Team Site Documents repository link in the Quick Launch bar, which has around 140 documents.

 

  1. Ok, that’s the Document library displayed, now to get to the Library Tab on the Ribbon bar; the option we are looking for is within the Library options available there.

  1. When the Library ribbon is displayed, click the Connect To Outlook button in the Connect & Export section. Note. If Connect to Outlook is greyed out ensure that Outlook 2013 is fully operational. I’ve come across examples where Outlook is installed, but no email account has been enabled in Outlook – if that’s the case this button will be greyed out.

  1. Once the Connect To Outlook button is clicked, you may receive a warning message informing you that you allow SharePoint to connect with Outlook – Click ALLOW.

  1. Outlook 2013 will be displayed. A dialog will also then be displayed that asks you to confirm that you wish to connect the Document Library to Outlook. The below dialog shows the Site Name and Document Library title, along with the URL of the document library being connected. Below, and to the right is a button that shows more information about the connection (ADVANCED button). The following screenshot shows the information displayed if the Advanced Button is clicked. There is not much you can do on that screen, for now, click YES to confirm the connection.

Here’s an example of the Advanced dialog associated. The most interesting aspect is the Permissions line. For Document Library connection to Outlook, this will be set as READ. This is by design, and for good reason. Things like classified metadata are not exposed to be writeable from Outlook including other document library settings like CheckIn/Out etc. However, this does not prevent you from modifying a file in the resultant list. If the document needs to be updated, simply double-click on the document which will open it in the local application, click the edit offline option, make your changes, click save, click close, and then a prompt should appear to allow you to update to the server.

Once completed, the documents will be listed in Outlook. The following screenshot shows the result of a Shared Document Library from a SharePoint 2013 site connected to Outlook 2013. Note the following features which in my view are awesome for User Adoption particularly from those whose centre of the universe happens to be the Outlook client; without going into jargon try to explain the following features:

  • That users are able to switch from connected library to connected library using the navigation options, each connected library shows the number of unread items (un-previewed or un-opened documents).
  • That each document (if the previewer is available) when clicked on will display a preview of the document; meaning that you can read a Word Document, for example, without having to open it in the client application.
  • That information concerning the state of the document is displayed, showing the last modifier, whether the document is checked out, when it was last modified and the document size.

 

Note. There is a problem I have noticed in the preview section when highlighting any file whilst working with SharePoint 2013, Office 2013 on a sandbox; the message:

‘This file cannot be previewed because of an error with the following previewer: Microsoft xxxxx previewer – To open this file in its own program, double-click it;’.

There is an article that seems to describe the issue (but does not directly mention when it’s likely to occur); and is known to Microsoft. A description of the alternatives whilst a fix is being provided here: http://support.microsoft.com/kb/983097. I will further investigate this and update this article.

How To : Use the REST API and AngularJS to Create a Web Part to retrieve List Items

Introduction

This article explains how to get the data from a SharePoint List using Angular JavaScript and the REST API. I used the REST API to talk to SharePoint and get the data from the list.

In this script we just see that we have first created an Angular Controller with the name “spCustomerController”. We have also injected $scope and $http service.

The $http service will fetch the list data from the specific columns of the SharePoint list. $scope is a glue between a Controller and a View. It acts as execution context for Expressions. Angular expressions are code snippets that are usually placed in bindings such as {{ expression }}.

Angular Controller.jpg

Use the following procedure to create a sample.

  1. <h1>WelCome To Angular JS Sharepoint 2013 REST API !!</h1>  
  2.   
  3. <div ng-app=“SharePointAngApp” class=“row”>  
  4.     <div ng-controller=“spCustomerController” class=“span10”>  
  5.         <table class=“table table-condensed table-hover”>  
  6.             <tr>  
  7.                 <th>Title</th>  
  8.                 <th>Employee</th>  
  9.                 <th>Company</th>  
  10.                  
  11.             </tr>  
  12.             <tr ng-repeat=“customer in customers”>  
  13.                 <td>{{customer.Title}}</td>  
  14.                 <td>{{customer.Employee}}</td>  
  15.                 <td>{{customer.Company}}</td>  
  16.                 </tr>  
  17.         </table>  
  18.     </div>  
  19. </div>  

 

Step 1: Navigate to your SharePoint 2013 site.

Step 2: From this page select the Site Actions | Edit Page.

Edit the page, go to the “Insert” tab in the Ribbon and click the “Web Part” option. In the “Web Parts” picker area, go to the “Media and Content” category, select the “Script Editor” Web Part and press the “Add button”.

Step 3: Once the Web Part is inserted into the page, you will see an “EDIT SNIPPET” link; click it. You can insert the HTML and/or JavaScript as in the following

  1. <style>  
  2. table, td, th {  
  3.     border: 1px solid green;  
  4. }  
  5.   
  6. th {  
  7.     background-color: green;  
  8.     color: white;  
  9. }  
  10. </style>  
  11. <script src=https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js&#8221;></script>  
  12. <script src=http://code.jquery.com/ui/1.10.3/jquery-ui.min.js&#8221;></script>  
  13.   
  14. <script>  
  15.       
  16.   
  17.     var myAngApp = angular.module(‘SharePointAngApp’, []);  
  18.     myAngApp.controller(‘spCustomerController’, function ($scope, $http) {  
  19.         $http({  
  20.             method: ‘GET’,  
  21.             url: _spPageContextInfo.webAbsoluteUrl + “/_api/web/lists/getByTitle(‘InfoList’)/items?$select=Title,Employee,Company”,  
  22.             headers: { “Accept”: “application/json;odata=verbose” }  
  23.         }).success(function (data, status, headers, config) {  
  24.             $scope.customers = data.d.results;  
  25.         }).error(function (data, status, headers, config) {  
  26.          
  27.         });  
  28.     });  
  29.       
  30. </script>  
  31.   
  32. <h1> Angular JS SharePoint 2013 REST API !!</h1>  
  33.   
  34. <div ng-app=“SharePointAngApp” class=“row”>  
  35.     <div ng-controller=“spCustomerController” class=“span10”>  
  36.         <table class=“table table-condensed table-hover”>  
  37.             <tr>  
  38.                 <th>Title</th>  
  39.                 <th>Employee</th>  
  40.                 <th>Company</th>  
  41.                  
  42.             </tr>  
  43.             <tr ng-repeat=“customer in customers”>  
  44.                 <td>{{customer.Title}}</td>  
  45.                 <td>{{customer.Employee}}</td>  
  46.                 <td>{{customer.Company}}</td>  
  47.                 </tr>  
  48.         </table>  
  49.     </div>  
  50. </div>  

 

Finally the result show look as below:

 

Finally result show.jpg

How To : Develop a Single-page Application in SharePoint

sharepointKnockoutJS

A single-page application in SharePoint

This app will be a single-page app and heavily javascript based, taking advantage of ajax and web services. As mentioned earlier, we’re going to base this app on the TodoMVC project. More specifically, we’re going to use the Knockout version of the TodoMVC app. So download the knockout todomvc app from github here and incorporate it into your project as follows:

  • Copy the js and bower_components folders into the Scripts folder. To do this quickly:
    1. Copy the folders in Windows Explorer
    2. Visual Studio, enable Project -> Show All Files
    3. The folders will now appear in Solution Explorer. Right click bower_components, and select Include In Project. Do the same for the js folder.
  • Copy the contents of index.html into Views/Index.cshtml (discard whatever is there).
  • Open Views/Index.cshtml and edit the script and css references to point to the Scripts folder (eg. find any references to bower_components and change it to Scripts/bower_components, do the same for js)
  • Open the Shared/_Layout.cshtml file and replace its contents with a single call to @RenderBody():

Your solution should now look like the following:

Hit F5 and you should now have a running TodoMVC app!

Data Storage

In previous articles I have described how to use the Azure database for storage of your data. In a provider-hosted app, you can equally as easily store your data in your own database. However, performance issues aside, it’s really handy to store data in the customer’s SharePoint system itself where possible. This has a number of advantages:

  • Security – you don’t need to store customer data in your own data center
  • App interoperability – if you have multiple apps talking to SharePoint, the architecture will be greatly simplified by by using SharePoint as the central data store
  • Transparency – customers can see their data transparently in lists and understand better what data is stored and how it’s used
  • Tight SharePoint integration – this is often useful since you can later easily take advantage of features such as workflows and event receivers.

In this article therefore I’ll use SharePoint lists for data storage. Let’s create a list to use for storage of our Todo items. Firstly, right-click on the TodoApp project, and select Add -> New Item. Choose List, and call it TodoList.

Click Add. On the next dialog, leave the list template as Default and click Finish.

Now you’ll be presented with a list designer form. By default it already has a Title column, so just add another column called Completed which should be a Boolean:

In my case, Completed was available as a site column. However, it was hidden by default. To remedy this, open up Schema.xml and ensure the Completed field has Hidden=FALSE:

Open up the TodoListInstance designer again and click the Views tab, and add the Completed column to the default view:

Now we’re going to add the server-side code for retrieving the list items. Firstly add a class called TodoItemViewModel and give it the relevant properties<!–. Note that the properties are not capitalised to match what we’ve got on the client side already–>:

    public class TodoItemViewModel
    {
        public string Title { get; set; }
        public bool Completed { get; set; }
    }
    

Next, open up the HomeController class and change the Index method to load the contents of the TodoList:

    [SharePointContextFilter]
    public ActionResult Index()
    {
        List<TodoItemViewModel> result = new List<TodoItemViewModel>();
        var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
        using (var clientContext = spContext.CreateAppOnlyClientContextForSPAppWeb())
        {
            if(clientContext != null)
            {
                //Load list items
                List list = clientContext.Web.Lists.GetByTitle("TodoList");
                ListItemCollection items = list.GetItems(CamlQuery.CreateAllItemsQuery());
                clientContext.Load(items);
                clientContext.ExecuteQuery();
                //Create the Todo item view models
                result = items.Select(li => new TodoItemViewModel()
                {
                    Title = (string)li["Title"],
                    Completed = (bool)li["Completed"]
                }).ToList();
            }
        }
        return View(result); //Pass the items into the view
    }
    

You may notice we’re using CreateAppOnlyClientContextForSPAppWeb. This means we are accessing the list under the App identity, not the user identity. This is so that we don’t need to require the user themselves to have any special permissions – this should an important consideration throughout your app, as different resources may be only available to certain users and this will form a crucial part of your security model.

Now, since we want to access the list under the app identity, we need to enable that by opening AppManifest.xml under TodoApp, click the Permissions tab and check the option:

Client Side Code with Knockout.js

Now we move to the client side. We’re going to do something a bit scary and rewrite the app.js file, which contains all of the Todo app JavaScript we imported. We’re going to rewrite it so that firstly, we understand it, and secondly, we can integrate it with our AJAX methods more easily. You can always go back to study the original later on as it’ll be more advanced and refined.

If you are new to the Knockout way of data-binding in JavaScript, it might be a good time to visit the knockout.js website to familiarise yourself with it. It’s very powerful and yet pretty easy to pick up. and it makes writing javascript applications incredibly fast and easy.

So open app.js, delete what’s already there, and let’s start by creating a simple ViewModel for each Todo item:

    window.TodoApp = window.TodoApp || {};
    
    window.TodoApp.Todo = function (id, title, completed) {
        var me = this;
        this.id = ko.observable(id);
        this.title = ko.observable(title);
        this.completed = ko.observable(completed || false);
        this.editing = ko.observable(false);

        // edit an item
        this.startEdit = function () {
            me.editing(true);
        };

        // stop editing an item
        this.stopEdit = function (data, event) {
            if (event.keyCode == 13) {
                me.editing(false);
            }
            return true;
        };
    };

    

Next up we add the main ViewModel:

    window.TodoApp.ViewModel = function (spHostUrl) {
        var me = this;
        this.todos = ko.observableArray(); //List of todos
        this.current = ko.observable(); // store the new todo value being entered
        this.showMode = ko.observable('all'); //Current display mode

        //List which is currently displayed
        this.filteredTodos = ko.computed(function () {
            switch (me.showMode()) {
            case 'active':
                return me.todos().filter(function (todo) {
                    return !todo.completed();
                });
            case 'completed':
                return me.todos().filter(function (todo) {
                    return todo.completed();
                });
            default:
                return me.todos();
            }
        });        

        this.addTodo = function (todo) {
            me.todos.push(todo);
        }

        // add a new todo, when enter key is pressed
        this.add = function (data, event) {
            if (event.keyCode == 13) {
                var current = me.current().trim();
                if (current) {        
                    var todo = new window.TodoApp.Todo(0, current);
                    me.addTodo(todo);
                    me.current('');
                }
            }
            return true;
        };

        // remove a single todo
        this.remove = function (todo) {
            me.todos.remove(todo);
        };

        // remove all completed todos
        this.removeCompleted = function () {
            var todos = me.todos().slice(0);
            for (var i = 0; i < todos.length; i++) {
                if (todos[i].completed()) {
                    me.remove(todos[i]);
                }
            }
        };

        // count of all completed todos
        this.completedCount = ko.computed(function () {
            return me.todos().filter(function (todo) {
                return todo.completed();
            }).length;
        });

        // count of todos that are not complete
        this.remainingCount = ko.computed(function () {
            return me.todos().length - me.completedCount();
        });

        // writeable computed observable to handle marking all complete/incomplete
        this.allCompleted = ko.computed({
            //always return true/false based on the done flag of all todos
            read: function () {
                return !me.remainingCount();
            },
            // set all todos to the written value (true/false)
            write: function (newValue) {
                me.todos().forEach(function (todo) {
                    // set even if value is the same, as subscribers are not notified in that case
                    todo.completed(newValue);
                });
            }
        });
    };

Take a read through the above JavaScript – I have tried to simplify it from the original TodoMVC Knockout code so it should be a little more understandable if you’re new to Knockout.

Next we’re going to change the HTML to match our updated JavaScript. Open index.cshtml and replace the entire contents of the <body> tag with the following:

    <section id="todoapp">
        <header id="header">
            <h1>todos</h1>
            <input id="new-todo" data-bind="value: current, valueUpdate: 'afterkeydown', event: { keypress: add }" placeholder="What needs to be done?" autofocus>
        </header>
        <section id="main" data-bind="visible: todos().length">
            <input id="toggle-all" data-bind="checked: allCompleted" type="checkbox">
            <label for="toggle-all">Mark all as complete</label>
            <ul id="todo-list" data-bind="foreach: filteredTodos">
                <li data-bind="css: { completed: completed, editing: editing }">
                    <div class="view">
                        <input class="toggle" data-bind="checked: completed" type="checkbox">
                        <label data-bind="text: title, event: { dblclick: startEdit }"></label>
                        <button class="destroy" data-bind="click: $root.remove"></button>
                    </div>
                    <input class="edit" data-bind="value: title, valueUpdate: 'afterkeydown', event: { keypress: stopEdit }">
                </li>
            </ul>
        </section>
        <footer id="footer" data-bind="visible: completedCount() || remainingCount()">
            <span id="todo-count">
                <strong data-bind="text: remainingCount">0</strong> item(s) left
            </span>
            <ul id="filters">
                <li>
                    <a data-bind="css: { selected: showMode() == 'all' }, click: function(){showMode('all');}">All</a>
                </li>
                <li>
                    <a data-bind="css: { selected: showMode() == 'active' }, click: function(){showMode('active');}">Active</a>
                </li>
                <li>
                    <a data-bind="css: { selected: showMode() == 'completed' }, click: function(){showMode('completed');}">Completed</a>
                </li>
            </ul>
            <button id="clear-completed" data-bind="visible: completedCount, click: removeCompleted">
                Clear completed (<span data-bind="text: completedCount"></span>)
            </button>
        </footer>
    </section>
    <footer id="info">
        <p>Double-click to edit a todo</p>
        <p>Written by <a href="https://github.com/ashish01/knockoutjs-todos">Ashish Sharma</a> and <a href="http://knockmeout.net">Ryan Niemeyer</a></p>
        <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
    </footer>
    <script src="Scripts/bower_components/todomvc-common/base.js"></script>
    <script src="Scripts/bower_components/knockout.js/knockout.debug.js"></script>
    <script src="Scripts/jquery-1.10.2.js"></script>
    <script src="Scripts/js/app.js"></script>
        
    <script>
        var viewModel = new window.TodoApp.ViewModel(spHostUrl);
        ko.applyBindings(viewModel);
    </script>

Again, this is slightly simplified from the original TodoMVC code.

You should now be able to run the app as before. In the next step we’ll add the client-side code required to load the data back from the server. Update the final script block in index.cshtml to the following:

        
    <script>
        var model = @(Html.Raw(Json.Encode(Model)));
        var viewModel = new window.TodoApp.ViewModel();

        for(var i=0; i<model.length; i++) {
            viewModel.addTodo(new window.TodoApp.Todo(model[i].Id, model[i].Title, model[i].Completed));
        }

        ko.applyBindings(viewModel);
    </script>

    

Now we’re ready to give the app a quick test. Hit F5 and wait for you app to open. Since we don’t yet have a way of saving data, we’re going to add some directly into the SharePoint list. Just browse to the list using the url /[YourSPsite]/TodoApp/Lists/TodoList/ and you should see the standard List UI:

Add a couple of test items and refresh your app:

Saving Data via AJAX

The final piece of the puzzle is to react to user input and save the data back to the server. When an item is deleted, we’ll need to know the Id of the SharePoint ListItem which should be deleted. Therefore, we’ll add an Id property to the TodoItemViewModel class:

    public class TodoItemViewModel
    {
        public int Id { get; set; } //New!
        public string Title { get; set; }
        public bool Completed { get; set; }
    }
    

Don’t forget to set the Id property when we’re loading the items in the HomeController Index method:

Now we’ll create web service methods for adding, deleting and updating an item. Add these methods to HomeController:

    [HttpPost]
    public JsonResult AddItem(string title)
    {
        int newItemId = 0;
        var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
        using (var clientContext = spContext.CreateAppOnlyClientContextForSPAppWeb())
        {
            if (clientContext != null)
            {
                List list = clientContext.Web.Lists.GetByTitle("TodoList");
                ListItem listItem = list.AddItem(new ListItemCreationInformation());
                listItem["Title"] = title;
                listItem.Update();
                clientContext.Load(listItem, li => li.Id);
                clientContext.ExecuteQuery();
                newItemId = listItem.Id;
            }
        }
        return Json(newItemId, JsonRequestBehavior.AllowGet);
    }

    [HttpPost]
    public JsonResult RemoveItem(int id)
    {
        var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
        using (var clientContext = spContext.CreateAppOnlyClientContextForSPAppWeb())
        {
            if (clientContext != null)
            {
                List list = clientContext.Web.Lists.GetByTitle("TodoList");
                ListItem item = list.GetItemById(id);
                item.DeleteObject();
                clientContext.ExecuteQuery();
            }
        }
        return new JsonResult();
    }

    [HttpPost]
    public JsonResult UpdateItem(int id, string title, bool completed)
    {
        var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
        using (var clientContext = spContext.CreateAppOnlyClientContextForSPAppWeb())
        {
            if (clientContext != null)
            {
                List list = clientContext.Web.Lists.GetByTitle("TodoList");
                ListItem item = list.GetItemById(id);
                item["Title"] = title;
                item["Completed"] = completed;
                item.Update();
                clientContext.ExecuteQuery();
            }
        }
        return new JsonResult();
    }
    

The above methods use fairly simple CSOM code to add, remove and update a list item. You can accomplish the same goal by using the JavaScript version of the CSOM which would have the added benefit of calling SharePoint directly without going via the app server. However, I haven’t done this because I want to illustrate how to make AJAX calls between the app client and server. click here to read more about the javascript CSOM library.

Now we’ll add the JavaScript code to call these server methods when an add, update or delete occurs. Earlier you may have noticed that we included a reference to the jQuery script. This is because we’re going to use jQuery’s AJAX helper methods:

Now all we need to do is add code to react to the add, remove and update events, and call the server methods. The first step is to open HomeController.cs and add the SPHostUrl, which is a crucial value for authentication, to the ViewBag:

The purpose of this is so that we can access SPHostUrl on the client, and pass it back to the server during AJAX requests. The authentication cookie will also be passed, and together this forms everything required for authentication to take place.

Next, open up the Index.cshtml file and update the last script block to match the following:

    <script>
        var model = @(Html.Raw(Json.Encode(Model)));
        
        var spHostUrl = '@ViewBag.SPHostUrl'; //<-- Add this line
        var viewModel = new window.TodoApp.ViewModel(spHostUrl); <-- Pass SPHostUrl into the ViewModel

        for(var i=0; i<model.length; i++) {        
            viewModel.addTodo(new window.TodoApp.Todo(model[i].Id, model[i].Title, model[i].Completed));
        }
        ko.applyBindings(viewModel);
    </script>
    

Next we’ll update the app.js code to call the web services at appropriate times by adding HTTP requests using jQuery’s $.ajax helper. Firstly, update the add function:

    this.add = function (data, event) {
        if (event.keyCode == 13) {
            var current = me.current().trim();
            if (current) {
                var todo = new window.TodoApp.Todo(0, current);
                me.addTodo(todo);
                me.current('');

                $.ajax({
                    type: 'POST',
                    url: "/Home/AddItem?SPHostUrl=" + encodeURIComponent(spHostUrl),
                    contentType: "application/json; charset=utf-8",
                    data: JSON.stringify({
                        title: todo.title()
                    }),
                    dataType: "json",
                    success: function (id) {
                        todo.id(id);
                    }
                });
            }
        }
        return true;
    };
    

Next up is the remove function:

    this.remove = function (todo) {
        me.todos.remove(todo);
        $.ajax({
            type: 'POST',
            url: "/Home/RemoveItem?SPHostUrl=" + encodeURIComponent(spHostUrl),
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify({
                id: todo.id()
            }),
            dataType: "json"
        });
    };
    

Now that additions and deletions are being handled, it only remains to handle updates. We will do this by adding code within the addTodo function that listens for changes to the title or completed properties, and submits a change to the server. The updated addTodo function should look like this:

    this.addTodo = function (todo) {
        me.todos.push(todo);
        var adding = true;
        ko.computed(function () {
            var title = todo.title(),
                completed = todo.completed();
            if (!adding) {
                $.ajax({
                    type: 'POST',
                    url: "/Home/UpdateItem?SPHostUrl=" + encodeURIComponent(spHostUrl),
                    contentType: "application/json; charset=utf-8",
                    data: JSON.stringify({
                        id: todo.id(),
                        title: title,
                        completed: completed
                    }),
                    dataType: "json"
                });
            }
        });
        adding = false;
    }
    

In the code above, we add a Knockout computed property which listens to the title and completed observable properties. If either value changes, we make a call to the UpdateItem web service. Note that we use a flag called adding to ensure we don’t call UpdateItems during the initial item addition.

Run the project again. If all has gone to plan, you should hopefully be able to insert, edit and delete items and have them immediately saved back to SharePoint.

Tidying Up

It’s time to do some tidy up to make sure we’re following MVC conventions correctly. Firstly, we should be using the script loader to ensure the JavaScript is loaded as efficiently as possible. Open up App_Start\BundleConfig.cs and replace its contents with the following:

    public static void RegisterBundles(BundleCollection bundles)
    {
        bundles.Add(new ScriptBundle("~/bundles/scripts").Include(
                    "~/Scripts/bower_components/todomvc-common/base.js",
                    "~/Scripts/bower_components/knockout.js/knockout.debug.js",
                    "~/Scripts/jquery-{version}.js",
                    "~/Scripts/js/app.js"));

        bundles.Add(new StyleBundle("~/Content/css").Include(
                    "~/Scripts/bower_components/todomvc-common/base.css"));
    }

This creates two bundles: a CSS bundle and a JavaScript bundle. The advantage of this is that in production, your scripts will be loaded in a single request, and can be easily minified. Update your Index.cshtml file by removing existing script and style references and replace them by referencing your bundles in the <head> tag:

    <head>
        <meta charset="utf-8">
        <title>Knockout.js TodoMVC</title>
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/scripts")
    </head>
    

Since your app doesn’t use the About or Contact pages which were included in the default project template, you can remove those:

Also, remove the associated methods within the HomeController class.

Adding an App Part (WebPart)

Adding a web part is really easy! You can create an app part to point to any page in your app, and it simply displays in an iframe inside your SharePoint site. In practice you’ll probably want to create a new page. We’re going to create a page that differs slightly in style from the app.

Now, this view will be identical to the main Index view except for styling. We want to avoid copy-pasting the original view into this one, so we’ll created a Shared view that we can use for both.

Start by adding a new View by right-clicking the Views/Home folder and selecting Add -> View: and name it IndexCommon:

Now copy the entire contents of the body tag from Index to IndexCommon. Then you can reference your IndexCommon from Index using a call to @Html.Partial. Your Index.cshtml should look as follows:

    <!doctype html>
    <html lang="en" data-framework="knockoutjs">
    <head>
        <meta charset="utf-8">
        <title>Knockout.js TodoMVC</title>
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/scripts")
    </head>
    <body>
        @Html.Partial("IndexCommon")
    </body>
    </html>
    

Add a new view called IndexTrimmed:

Again, use the same content for Index. This time however we’re going to make one change, which is to reference an alternative CSS file. So change the @Styles.Render tag as follows:

Now open App_Start/BundleConfig.cs and add a new bundle:

    
    bundles.Add(new StyleBundle("~/Content/css-trimmed").Include(
                "~/Scripts/bower_components/todomvc-common/base-trimmed.css"));
    

And add the css file by copying base.css to base-trimmed.css:

You can make modifications to this CSS file at this point. I deleted the following rules:

  • #todoapp { margin: 130px 0 40px 0; } (the large title at the top)
  • body { margin: 0 auto; } (this caused page centering)
  • background: #eaeaea url(‘bg.png’); (grey background image)

I added these rules:

  • #info { display:none; } (to hide the info footer)

The next step is to add a Controller method for our new View. Open HomeController and add a method called IndexTrimmed. It should hold the exact same content as Index, so I’ve extracted it to a common method:

    [SharePointContextFilter]
    public ActionResult IndexTrimmed()
    {
        return View(GetItems()); //Pass the items into the view
    }

    [SharePointContextFilter]
    public ActionResult Index()
    {
        return View(GetItems()); //Pass the items into the view
    }

    private List<TodoItemViewModel> GetItems()
    {
        List<TodoItemViewModel> result = new List<TodoItemViewModel>();
        var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
        ViewBag.SPHostUrl = spContext.SPHostUrl;
        using (var clientContext = spContext.CreateAppOnlyClientContextForSPAppWeb())
        {
            if (clientContext != null)
            {
                //Load list items
                List list = clientContext.Web.Lists.GetByTitle("TodoList");
                ListItemCollection items = list.GetItems(CamlQuery.CreateAllItemsQuery());
                clientContext.Load(items);
                clientContext.ExecuteQuery();
                //Create the Todo item view models
                result = items.ToArray().Select(li => new TodoItemViewModel()
                {
                    Id = (int)li.Id,
                    Title = (string)li["Title"],
                    Completed = (bool)li["Completed"]
                }).ToList();
            }
        }
        return result;
    }

Now we’re ready to add the Web Part. Right click on TodoApp in solution explorer, and select Add -> New Item. Choose Client Web Part:

Give it a name and click Next:. We want to use our new page, so enter its url:

Open the TodoApp/TodoWebPart/Elements.xml file and change the default width from 300px to 600px:

Now run your app again. Make sure the entire app reinstalls, since the app part is installed to the SharePoint server itself. Add the app part by visiting your SharePoint site and clicking Page, then Edit:

Then select Insert -> App Part, and choose the TodoWebPart from the list. Click Add.

Click Save to save your changes to the page, and your web part should be shown:

Making Your Web Part Resize Dynamically

Now, you’ll notice that the Web Part isn’t resizing correctly when you add items to the list. This is because as the app itself gets larger, the App Part doesn’t get larger – it is after all an iframe with a fixed height. This isn’t a problem for apps that don’t resize, for example forms or fixed-length lists. However, we want our app to resize as though it were a normal part of the main website flow.

Luckily, there’s a workaround for this involving posting a message to the iframe and asking it to resize.

Add the following code to your HomeController, in the IndexTrimmed method:

    public ActionResult IndexTrimmed()
    {
        ViewBag.SenderId = HttpContext.Request.Params["SenderId"];
        return View(GetItems());
    }
    

The purpose of the above code is to retrieve the SenderId parameter from the URL, and make it available (via the ViewBag) to the client. This SenderId parameter represents the ID of the iframe which is hosting the app part.

Next, we’ll use the Sender ID in the client to request a resize of the iframe whenever the app resizes – more specifically, whenever an item is added to or removed from the list. This script should be added to the end of the body in IndexTrimmed.cshtml:

    //Retrieve the sender ID from the viewbag
    var senderId = '@ViewBag.SenderId';

    //Create a function that will change the height of the iframe
    function setHeight() {
        var width = $('#todoapp').outerWidth(true),
            height = $('#todoapp').outerHeight(true);

        //Notify SP that it should resize its iframe to the appropriate height.
        window.parent.postMessage('<message senderId=' + senderId + '>resize(' + width + ', ' + (height + 50) + ')</message>', "*");
    }

    //List for changes to the todo list, and call setHeight when that happens
    viewModel.todos.subscribe(setHeight);

    //Set the correct initial height when the app first loads.
    setHeight();
    

For the avoidance of doubt, here’s where this script should go:

The script is intentionally placed below the call to load the IndexCommon partial view, because then it will have access to the JavaScript viewModel object.

Packaging your app

With autohosted apps, the process is simpler: you just right-click on your app and select Publish. From there, you click Package and you are given a .app file. This app file contains everything involved: both the SharePoint app and also the remove app (your Web project). It’s super simple because the autohosting process takes care of creating a Client ID and Client Secret (for OAuth authentiction), and also takes care of physical hosting.

With Provider-hosted apps, things are more complex, and the steps you follow depend on how you want to host your web project. Your first step will be to visit this page and create a Client ID and Client Secret by registering your app. Once you’ve done that, you can right-click your app project and click Package. Then follow the publish wizard which will involve entering your Client ID and Client Secret.

Note that with your provider hosted app, you will deploy your Web project separately from your App project. This is obvious really: you’re going to be hosting your Web project yourself (you’re the provider, after all), and the small app project will be packaged up and listed on the Office Store or sent manually to a customer. Once they install it, it’ll just point to your web server where your web app is installed. To configure this properly, open AppManifest.xml in the App project, and enter the URL to where your app is installed:

How To : Use the CSOM to Update SharePoint Web Part Properties

List in SharePoint9

I wanted to share two methods I developed for retrieving and updating web part properties from JavaScript using CSOM in SharePoint 2013 (I haven’t seen a reference for getting a page’s web part manager through REST).

The web part ID should be available through the “webpartid” attribute included in the page markup.

The methods use the jQuery deferred object, but that could easily be replaced with anything else to handle the asynchronous events. Using this I’m hoping to create configurable client side web parts which is a problem I’ve recently had to tackle.

View on GitHub

app.js

  1. //pass in the web part ID as a string (guid)
  2. function getWebPartProperties(wpId) {
  3. var dfd = $.Deferred();
  4.  
  5. //get the client context
  6. var clientContext =
  7. new SP.ClientContext(_spPageContextInfo.webServerRelativeUrl);
  8. //get the current page as a file
  9. var oFile = clientContext.get_web()
  10. .getFileByServerRelativeUrl(_spPageContextInfo.serverRequestPath);
  11. //get the limited web part manager for the page
  12. var limitedWebPartManager =
  13. oFile.getLimitedWebPartManager(SP.WebParts.PersonalizationScope.shared);
  14. //get the web parts on the current page
  15. var collWebPart = limitedWebPartManager.get_webParts();
  16.  
  17. //request the web part collection and load it from the server
  18. clientContext.load(collWebPart);
  19. clientContext.executeQueryAsync(Function.createDelegate(this, function () {
  20. var webPartDef = null;
  21. //find the web part on the page by comparing ID’s
  22. for (var x = 0; x < collWebPart.get_count() && !webPartDef; x++) {
  23. var temp = collWebPart.get_item(x);
  24. if (temp.get_id().toString() === wpId) {
  25. webPartDef = temp;
  26. }
  27. }
  28. //if the web part was not found
  29. if (!webPartDef) {
  30. dfd.reject(“Web Part: “ + wpId + ” not found on page: “
  31. + _spPageContextInfo.webServerRelativeUrl);
  32. return;
  33. }
  34.  
  35. //get the web part properties and load them from the server
  36. var webPartProperties = webPartDef.get_webPart().get_properties();
  37. clientContext.load(webPartProperties);
  38. clientContext.executeQueryAsync(Function.createDelegate(this, function () {
  39. dfd.resolve(webPartProperties, webPartDef, clientContext);
  40. }), Function.createDelegate(this, function () {
  41. dfd.reject(“Failed to load web part properties”);
  42. }));
  43. }), Function.createDelegate(this, function () {
  44. dfd.reject(“Failed to load web part collection”);
  45. }));
  46.  
  47. return dfd.promise();
  48. }
  49.  
  50. //pass in the web part ID and a JSON object with the properties to update
  51. function saveWebPartProperties(wpId, obj) {
  52. var dfd = $.Deferred();
  53.  
  54. getWebPartProperties(wpId).done(
  55. function (webPartProperties, webPartDef, clientContext) {
  56. //set web part properties
  57. for (var key in obj) {
  58. webPartProperties.set_item(key, obj[key]);
  59. }
  60. //save web part changes
  61. webPartDef.saveWebPartChanges();
  62. //execute update on the server
  63. clientContext.executeQueryAsync(Function.createDelegate(this, function () {
  64. dfd.resolve();
  65. }), Function.createDelegate(this, function () {
  66. dfd.reject(“Failed to save web part properties”);
  67. }));
  68. }).fail(function (err) { dfd.reject(err); });
  69.  
  70. return dfd.promise();
  71. }

SharePoint 2013 – Creating a Word document with OOXML

This solution is based on the SharePoint-hosted app template provided by Visual Studio 2012. The solution enumerates through each document library in the host website, and adds the library to a drop-down list.

2008040211105590dad[1]

 

When the user selects a library and clicks a tile, the app creates a sample Word 2013 document by using OOXML in the selected library.

Prerequisites

This sample requires the following:

  • Visual Studio 2012
  • Office Developer Tools for Visual Studio 2012
  • Either of the following:
    • SharePoint Server 2013 configured to host apps, and with a Developer Site collection already created; or,
    • Access to an Office 365 Developer Site configured to host apps.

Key components of the sample

The sample app contains the following:

  • The Default.aspx webpage, which is used to enumerate through each document library in the host website, and render tiles for each MP4 video in the app.
  • The Point8020Metro.css style sheet (in the CSS folder) which contains some simple styles for rendering tiles.
  • The AppManifest.xml file, which has been edited to specify that the app requests Full Control permissions for the hosting web.
  • References to the DocumentFormat.OpenXml assembly provided by the OpenXML SDK 2.5.

All other files are automatically provided by the Visual Studio project template for apps for SharePoint, and they have not been modified in the development of this sample.

Configure the sample

Follow these steps to configure the sample.

  1. Open the SP_Autohosted_OOXML_cs.sln file using Visual Studio 2012.
  2. In the Properties window, add the full URL to your SharePoint Server 2013 Developer Site collection or Office 365 Developer Site to the Site URL property.

No other configuration is required.

Build the sample

To build the sample, press CTRL+SHIFT+B.

Run and test the sample

To run and test the sample, do the following:

  1. Press F5 to run the app.
  2. Sign in to your SharePoint Server 2013 Developer Site collection or Office 365 Developer Site if you are prompted to do so by the browser.
  3. Trust the app when you are prompted to do so.

The following images illustrate the app. In Figure 1 the app has been trusted and libraries added to the drop-down list.

Figure 1. View of the app with drop-down list

Figure 1

In Figure 2, the user has clicked the orange tile. The document is created and the red tile provides a link to the appropriate library (Figure 3), which the user reaches by clicking on the red tile.

Figure 2. Open XML document creator

Figure 2

Figure 3. Document library

Figure 3

Troubleshooting

Ensure that you have SharePoint Server 2013 configured to host apps (with a Developer Site Collection already created), or that you have signed up for an Office 365 Developer Site configured to host apps.

Change log

First release: January 30, 2013.

Related content

SharePoint Samurai