Building your first Project Server app : Part 2 – Getting the basic app up and running
By blurg64
In this second post covering how to build an app for Project Server to publish all projects, we will take a look at a new component of Project Server, the JavaScript Object Model or JSOM.
In the past when you wished to interact with Project Server the only real option you had would be to use the PSI, or Project Server Interface, a set of API’s that could be called that would perform tasks within the project server instance like reading a project, creating a resource, or adding a job to the queue.
With Project Server 2013, Microsoft introduced another API called the Client Side Object Model (CSOM) which can be accessed via .Net, Silverlight, and Windows Phone. They also exposed the CSOM via JavaScript and gave it a different name, the JSOM, which is accessible from web pages, custom application pages and the ribbon, which is exactly what we want for our app that will publish our projects.
It’s important to note that the CSOM and JSOM do not expose all the capabilities of the PSI at present, but I understand these capabilities are constantly being updated to the point that in the future we should eventually see parity.
For our app we need to do a few things with the JSOM, firstly, we need to get all of the projects within the Project Web App instance then we need to iterate through them.
To do this:
-
Open up your Napa project and navigate to the App.js file in the script folder. Out of the box Napa will add some default content into the App.js file, select it all and delete the content.
-
Now we are going to add some code to perform the main functions of our app. We need to add some global variables to store the information we will use later and set up to call Project Server for the project data.
[sourcecode lang=”javascript”]
// Declare global variables.
var projContext;
var projects;
// Get the projects collection.
function GetProjects() {
// Display a message to the user to show we are reading the projects.
$(‘#spanMessage’).text(‘Reading projects…’);
// Initialize the current client context.
projContext = PS.ProjectContext.get_current();
// Get the projects collection.
projects = projContext.get_projects();
// Register the request for information that you want to run on the server.
projContext.load(projects);
// Run the request on the server.
projContext.executeQueryAsync(IterateThroughProjects, QueryFailed);
}
[/sourcecode]
The final statement projContext.executeQueryAsync(IterateThroughProjects, QueryFailed) sends the request off to the server and if successful will call IterateThroughProjects or if it failed calls QueryFailed.
- The real guts of our app is in the IterateThroughProjects function which takes the list of projects and loops through them, checking the project out, publishing it and then checking it back in.
[sourcecode language=”javascript”]
function IterateThroughProjects(response) {
// Get the enumerator and iterate through the collection.
var enumerator = projects.getEnumerator();
while (enumerator.moveNext()) {
var project = enumerator.get_current();
// Get the target project and then check it out. The checkOut function
// returns the draft version of the project.
var draftProject = project.checkOut();
// Specify "true" to also check the project in.
var publishJob = draftProject.publish(true);
// Register the job that you want to run on the server and specify the
// timeout duration and callback function.
projContext.waitForQueueAsync(publishJob, 30, QueueJobSent);
}
}
[/sourcecode]
Notice how easy it is in the code to check out the project, publish it and drop it back on the queue? Three simple lines of code.
- Finally all that’s required is to add a bit of error handling code..
[sourcecode language=”javascript”]
// Print the JobState return code, which gives the status of the queue job.
function QueueJobSent(response) {
if (response == 0) {
$(‘#spanMessage’).text(‘Publishing projects…’);
} else
if (response == 4) {
$(‘#spanMessage’).text(‘Projects published…’);
}
}
function QueryFailed(sender, args) {
$(‘#spanMessage’).text(‘Request failed: ‘ + args.get_message());
}
[/sourcecode]
- And a little bit of code at the beginning to make sure that our JSOM doesn’t kick off until the CSOM library has been loaded and the final code should look like this:
[sourcecode language=”javascript”]
// Declare global variables.
var projContext;
var projects;
$(document).ready(function () {
SP.SOD.executeFunc(‘sp.js’, ‘SP.ClientContext’, GetProjects);
});
// Get the projects collection.
function GetProjects() {
// Display a message to the user to show we are reading the projects.
$(‘#spanMessage’).text(‘Reading projects…’);
// Initialize the current client context.
projContext = PS.ProjectContext.get_current();
// Get the projects collection.
projects = projContext.get_projects();
// Register the request for information that you want to run on the server.
projContext.load(projects);
// Run the request on the server.
projContext.executeQueryAsync(IterateThroughProjects, QueryFailed);
}
function IterateThroughProjects(response) {
// Get the enumerator and iterate through the collection.
var enumerator = projects.getEnumerator();
while (enumerator.moveNext()) {
var project = enumerator.get_current();
// Get the target project and then check it out. The checkOut function
// returns the draft version of the project.
var draftProject = project.checkOut();
// Specify "true" to also check the project in.
var publishJob = draftProject.publish(true);
// Register the job that you want to run on the server and specify the
// timeout duration and callback function.
projContext.waitForQueueAsync(publishJob, 30, QueueJobSent);
}
}
// Print the JobState return code, which gives the status of the queue job.
function QueueJobSent(response) {
// Whilst the call is status = 0, i.e happening, then show the publishing message
if (response == 0) {
$(‘#spanMessage’).text(‘Publishing projects…’);
}
else
// When the call has come back successfully, show the published message and then navigate back
if (response == 4) {
$(‘#spanMessage’).text(‘Projects published…’);
}
}
// If there is a failure, show it and stay on the page.
function QueryFailed(sender, args) {
$(‘#spanMessage’).text(‘Request failed: ‘ + args.get_message());
}
[/sourcecode]
- Next we need to update the Default.aspx page in order to call our code and to display what is going on. To do this, open the Default.aspxpage in the Pages folder and replace the contents with the text below:
[sourcecode language=”html”]
<%– The following 4 lines are ASP.NET directives needed when using SharePoint components –%>
<%@ Page Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” MasterPageFile="~masterurl/default.master” language="C#” %>
<%@ Register Tagprefix="SharePoint” Namespace="Microsoft.SharePoint.WebControls” Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” %>
<%@ Register Tagprefix="Utilities” Namespace="Microsoft.SharePoint.Utilities” Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” %>
<%@ Register Tagprefix="WebPartPages” Namespace="Microsoft.SharePoint.WebPartPages” Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” %>
<%– The markup and script in the following Content element will be placed in the
of the page –%><asp:Content ID="Content1” ContentPlaceHolderId="PlaceHolderAdditionalPageHead” runat="server">
<!– The following scripts are needed when using the SharePoint object model –>
<!– Add your CSS styles to the following file –>
<!– Add your JavaScript to the following file –>
</asp:Content>
<%– The markup and script in the following Content element will be placed in the
of the page –%><asp:Content ID="Content2” ContentPlaceHolderId="PlaceHolderMain” runat="server">
</asp:Content>
[/sourcecode]
- Before the app can be run we need to the permissions for the app, to do this click on the Properties icon in the bottom left hand corner.
The properties of the app will be displayed, where you can change the name and other properties of the app. To change the permission click on permissions on the left hand side and find the permission for Projects. Set this to be write.
This permission allows the app to read & write all projects in the PWA app.
- Now the main components of the app have been built, click on the Run Project icon to compile and deploy the app.
On clicking Run Project, Napa will take the code, compile it and deploy the app to the developer site automatically ready for testing.
- Once deployed click on the link in the Launch App dialog to initiate the app. The first time you run the app you will be asked if you trust it the app, choose ‘Trust it’
The app will then run and you will see the following error message, which is perfectly normal.
The reason the app errors is that it is trying to contact Project Server’s JSOM, but the developer site is not a Project Server site, instead it’s a normal SharePoint site, in order to test our app correctly we need to deploy it over to a PWA site.
- To publish the app to PWA, click on the publish icon in the bottom left hand corner
Napa will then take you through the publishing process and provide a link to the final .app file that we can then upload to our organization specific app store that we can use to deploy our in house built apps called the Corporate catalog. By default, in Office 365 tenant the Corporate catalog is not configured so we have to spend a few moments setting it up.
- The catalog is now ready to deploy our app. To do so click on New App and choose to upload the app we created in Napa above.
- You will be prompted to add additional information for the app including logo’s, icons etc. For now we’ll just leave them and click Save.
With that, the app should now be in the Corporate Catalog and ready for us to use in our Project Online instance.
- To add it to Project Online, open up the PWA site, click on the Gear icon and choose Add an App
- In the list of available apps you will see our publish app, click on it.
- Once again you will be asked if you trust the app, choose to Trust It. After a few seconds the app will be installed and ready to use.
Running the app
To run the app, make sure you have some enterprise projects in your PWA instance. Having an ‘Enterprise Project’ is important, the app will not publish task list linked projects as the publish concept doesn’t exist for these projects.
- To kick the app off, click on the app tile. The default.aspx page will render and kick off our JSOM code to retrieve the projects, check the projects out and then publish and check them back in. When the projects have published you will see a screen like below.
- To check the app worked as advertised, click on the Gear icon, choose PWA Settings and Manage Queue Settings. Change the filter to show succeeded queue jobs and you should see all the published projects
So there we have it an app that will publish all enterprise projects in a PWA instance using the new SharePoint app model and JSOM. Hopefully you can follow the steps in these blog posts of how to build your own, but in case not, I have shared the Napa project so you can run the project and publish it yourself. To get access to the source go to this link (http://aka.ms/ypwd0c).
In the next post in this series, we will look at how we can submit the app to the SharePoint store to distribute to other Project Server users.