How to create new tasks in Microsoft Project Server 2013 - sharepoint

We are using Microsoft Project Server 2013 within Microsoft SharePoint 2013. Is it possible to create a Task in a Project for example from a WebPart via C# in CodeBehind?

I don't know if you can do a Webpart to create tasks but, via CSOM you can connect to your PS2013 Projects and create some tasks.
I will post you here a piece of code that could help you:
You must first "Check-Out" the project like this:
projContext.Load(projContext.Projects);
projContext.ExecuteQuery();
var proj = projContext.Projects.First(p => p.Name == "Project");
projContext.ExecuteQuery();
var draftProj = proj.CheckOut();
projContext.Load(draftProj.Tasks);
projContext.ExecuteQuery();
CreateNewTask(draftProj);
Then, you can call the method of create new task sending the "draft project"
private static void CreateNewTask(DraftProject draftProj)
{
TaskCreationInformation nt = new TaskCreationInformation();
nt.Name = "Task name";
nt.Start = DateTime.Today;
nt.Duration = "20d";
nt.Id = Guid.NewGuid();
draftProj.Tasks.Add(nt);
projContext.Load(draftProj.Tasks);
draftProj.Update();
projContext.ExecuteQuery();
}
Hope that helps,

Related

Embedding Excel Add-Ins with OpenXml

My team is working on an Office 365 add-in for Excel, and as part of the project, we’re creating Excel documents through the GraphAPI with the end goal of having the add-in already setup for the document. We’re using the .NET OpenXml library to create the document before copying it through the GraphAPI.
We haven’t been able to find many resources for how to setup an add-in through OpenXml and have not been able to get anything working. The last thing we tried was copying the example we found here, but we couldn’t get it working. Does anyone know how to setup add-ins using the OpenXml library?
Note: the add-in is already in the Office Add-Ins store, and we have information like the AppSource ID.
Thank you!
We're actually about to publish a new sample around this scenario. The sample shows how to create an Excel document using OOXML, embed your add-in, and then upload the file to OneDrive. It also creates a Team chat that links to the file.
You can try out the sample here: Open data from your web site in a spreadsheet in Microsoft Teams
Or give us feedback on the PR: https://github.com/OfficeDev/PnP-OfficeAddins/pull/197
To answer your question about how to embed the add-in, you need to create a web extension section. I've copied the relevant code here. Note this is the same code from the Office-OOXML-EmbedAddin sample you already looked at. We reused it for the new sample. You can change the CUSTOM MODIFICATION section to provide any custom properties you want to your add-in when it opens.
// Embeds the add-in into a file of the specified type.
private void EmbedAddin(SpreadsheetDocument spreadsheet)
{
spreadsheet.DeletePart(spreadsheet.WebExTaskpanesPart);
var webExTaskpanesPart = spreadsheet.AddWebExTaskpanesPart();
CreateWebExTaskpanesPart(webExTaskpanesPart);
}
// Adds child parts and generates content of the specified part.
private void CreateWebExTaskpanesPart(WebExTaskpanesPart part)
{
WebExtensionPart webExtensionPart1 = part.AddNewPart<WebExtensionPart>("rId1");
GenerateWebExtensionPart1Content(webExtensionPart1);
GeneratePartContent(part);
}
// Generates content of webExtensionPart1.
private void GenerateWebExtensionPart1Content(WebExtensionPart webExtensionPart1)
{
// Add web extension containg Id for Script Lab add-in
We.WebExtension webExtension1 = new We.WebExtension() { Id = "{635BF0CD-42CC-4174-B8D2-6D375C9A759E}" };
webExtension1.AddNamespaceDeclaration("we", "http://schemas.microsoft.com/office/webextensions/webextension/2010/11");
// Add store information for Script Lab add-in
We.WebExtensionStoreReference webExtensionStoreReference1 = new We.WebExtensionStoreReference() { Id = "wa104380862", Version = "1.1.0.0", Store = "en-US", StoreType = "OMEX" };
We.WebExtensionReferenceList webExtensionReferenceList1 = new We.WebExtensionReferenceList();
We.WebExtensionPropertyBag webExtensionPropertyBag1 = new We.WebExtensionPropertyBag();
// Add the property that makes the taskpane visible.
We.WebExtensionProperty webExtensionProperty1 = new We.WebExtensionProperty() { Name = "Office.AutoShowTaskpaneWithDocument", Value = "true" };
webExtensionPropertyBag1.Append(webExtensionProperty1);
// CUSTOM MODIFICATION BEGIN
// Add the property that specifies the snippet to import.
string snippetToImportValue = string.Format("{{\"type\":\"gist\",\"id\":\"{0}\"}}", "{72189570-AE11-4207-9DEE-C8BDE4B83188}");
We.WebExtensionProperty webExtensionProperty2 = new We.WebExtensionProperty() { Name = "SnippetToImport", Value = snippetToImportValue };
webExtensionPropertyBag1.Append(webExtensionProperty2);
// CUSTOM MODIFICATION END
We.WebExtensionBindingList webExtensionBindingList1 = new We.WebExtensionBindingList();
We.Snapshot snapshot1 = new We.Snapshot();
snapshot1.AddNamespaceDeclaration("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
webExtension1.Append(webExtensionStoreReference1);
webExtension1.Append(webExtensionReferenceList1);
webExtension1.Append(webExtensionPropertyBag1);
webExtension1.Append(webExtensionBindingList1);
webExtension1.Append(snapshot1);
webExtensionPart1.WebExtension = webExtension1;
}
// Generates content of part.
private void GeneratePartContent(WebExTaskpanesPart part)
{
Wetp.Taskpanes taskpanes1 = new Wetp.Taskpanes();
taskpanes1.AddNamespaceDeclaration("wetp", "http://schemas.microsoft.com/office/webextensions/taskpanes/2010/11");
Wetp.WebExtensionTaskpane webExtensionTaskpane1 = new Wetp.WebExtensionTaskpane() { DockState = "right", Visibility = true, Width = 350D, Row = (UInt32Value)4U };
Wetp.WebExtensionPartReference webExtensionPartReference1 = new Wetp.WebExtensionPartReference() { Id = "rId1" };
webExtensionPartReference1.AddNamespaceDeclaration("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
webExtensionTaskpane1.Append(webExtensionPartReference1);
taskpanes1.Append(webExtensionTaskpane1);
part.Taskpanes = taskpanes1;
}

SharePoint 2016 Custom Action to trigger a workflow

I am trying to create a custom action that can be chosen by right-clicking on a file in a any folder of a particular SharePoint Library. This custom action would copy the file into the same folder with the user's login name appended to the end of the file name.
I currently have an event receiver that will perform the custom action when a file is being updated but that's not when I want it to happen. I was able to add a custom action to the right-click file menu using SharePoint Designer but SharePoint Designer only lets the custom action trigger special SharePoint 2010 compatible workflows or load a web page. I need to make it so the event handler (or possibly a workflow) fires when the user chooses the custom action after right-clicking on the file. I'm not sure what approach or what kind of project or app I need to create in Visual Studio 2017 to get this functionality.
Your custom action should call javascript function or perform GET request to your SharePoint hosted WCF or ASMX WebService.
ASMX
Official MSDN Walktrought: Creating a Custom ASP.NET Web Service
For additional resources with more screenshots check this blog post: Walkthrough: Creating a Custom ASP.NET (ASMX) Web Service in SharePoint 2010
WCF
Official Technet Walkthrough: SharePoint 2013: Create a Custom WCF REST Service Hosted in SharePoint and Deployed in a WSP
Note: with GET request you will need web.AllowUnsafeUpdate = true
With javascript u need AJAX call ie jQuery.ajax()
/edit
To hook up web service and your custom action use SharePoint Desinger, delete or change your existing custom action, change type to Navigate to URL and in the textbox type:
javascript: (function() { console.log('Testing...' + {ItemId}); /* your web service call */ })();
Use {ItemId} alias to pass proper item id to your AJAX call.
On the other hand, on web service side use SPWorkflowManager class to start a workflow on item. Check the code belowe (link):
public void StartWorkflow(SPListItem listItem, SPSite spSite, string wfName) {
SPList parentList = listItem.ParentList;
SPWorkflowAssociationCollection associationCollection = parentList.WorkflowAssociations;
foreach (SPWorkflowAssociation association in associationCollection) {
if (association.Name == wfName){
association.AutoStartChange = true;
association.AutoStartCreate = false;
association.AssociationData = string.Empty;
spSite.WorkflowManager.StartWorkflow(listItem, association, association.AssociationData);
}
}
}
I found a way to do this using JavaScript, without SharePoint Designer. I put the following script in a Content Editor web part on the page where the listview webpart is and now I can right click on a file and get the option to "Get My Copy". If you have a Comments sub folder, the renamed copy will get put there.
<script type="text/javascript">
// adds the menu option to Get My Copy
function Custom_AddDocLibMenuItems(m, ctx)
{
var strDisplayText = "Get My Copy"; //Menu Item Text
var strAction = "copyFile()";
var strImagePath = ""; //Menu item Image path
CAMOpt(m, strDisplayText, strAction, strImagePath); // Add our new menu item
CAMSep(m); // add a separator to the menu
return false; // false means standard menu items should also be rendered
}
// append current user account to filename and copy to subfolder named Comments
function copyFile()
{
// get web and current user from context
var context = new SP.ClientContext.get_current();
var web = context.get_web();
this.currentUser = web.get_currentUser();
context.load(currentUser);
// load the folder
var currentFolder = decodeURIComponent(ctx.rootFolder);
var folderSrc = web.getFolderByServerRelativeUrl(currentFolder);
context.load(folderSrc,'Files');
context.executeQueryAsync(
function() {
// get the first (and hopefully only) file in the folder
var files = folderSrc.get_files();
var e = files.getEnumerator();
e.moveNext()
var file = e.get_current();
// get user account
var curUserAcct = currentUser.get_loginName();
curUserAcct = curUserAcct.substring(curUserAcct.indexOf("\\") + 1);
// get file without extension
var file_with_ext = file.get_name();
var name_without_ext = file_with_ext.substr(0, file_with_ext.lastIndexOf("."));
var destLibUrl = currentFolder + "/Comments/" + name_without_ext + " " + curUserAcct + ".docx";
file.copyTo(destLibUrl, true);
context.executeQueryAsync(
function() { alert("Success! File File successfully copied to: " + destLibUrl); },
function(sender, args) { alert("error: " + args.get_message()) }
);
},
function(sender, args){ alert("Something went wrong with getting current user or getting current folder '" + currentFolder + "'. " + args.get_message()); }
);
}
</script>

SharePoint 2013 Activity Event in Newsfeed

I need to add custom notifications to the personal Newsfeed on people's MySites. I found several tutorials and code examples for SharePoint 2010 on the net and tried to do the same with SharePoint 2013. They're all about creating ActivityEvents with the ActivityManager.
Here's the code I tried:
var targetSite = new SPSite("URL to MySite webapp");
SPServiceContext context = SPServiceContext.GetContext(targetSite);
var userProfileManager = new UserProfileManager(context);
var ownerProfile = userProfileManager.GetUserProfile("domain\\user1");
var publisherProfile = userProfileManager.GetUserProfile("domain\\user2");
var activityManager = new ActivityManager(ownerProfile, context);
Entity publisher = new MinimalPerson(publisherProfile).CreateEntity(activityManager);
Entity owner = new MinimalPerson(ownerProfile).CreateEntity(activityManager);
ActivityEvent activityEvent = ActivityEvent.CreateActivityEvent(activityManager, 17, owner, publisher);
activityEvent.Name = "StatusMessage";
activityEvent.ItemPrivacy = (int)Privacy.Public;
activityEvent.Owner = owner;
activityEvent.Publisher = publisher;
activityEvent.Value = "HELLOOOO";
activityEvent.Commit();
ActivityFeedGatherer.BatchWriteActivityEvents(new List<ActivityEvent> { activityEvent }, 0, 1);
The Id 17 in the CreateActivityEvent function is for the StatusMessage activity type, which is layouted like {Publisher} says: {Value} in the ressource files, so I provide the Value property of my ActivityEvent.
The code runs without any exception and in the User Profile Service Application_ProfileDB database I can see the right entries appear in the ActivityEventsConsolidated table.
But the activity is not visible in the activity feed, neither on the Owner's one, nor on the Publisher's one, even though these people follow each other. I ran the Activity Feed Job in the CA manually to update the activity feed.
Also, I tried to do the same with custom ActivityTypes with own ressource files, same result: The entry in the ActivityEventsConsolidated table (or ActivityEventsPublished if Owner=Publisher) appear, but no entries on the MySite.
Can anyone help?
I found the solution for this problem myself.
In Central Administration, Setup MySites, you have to enable the Enable SharePoint 2010 activity migration setting in the Newsfeed section in order to support SP1010 legacy activities in SP2013.

Access denied office 365 / SharePoint online with Global Admin account

I am going crazy since two days solving an issue. The problem is;
I am making a console APP which is talking to SharePoint Online using global admin account (One which was specified as admin while making a new subscription). What I am trying to achieve is, I want to add a custom action using CSOM to each site collection and subsite of office 365. That code works fine except on the root site collection which is pre-created by office 365 while signing up (i.e. https://xyz.sharepoint.com)
For any tenant for root site collection, it gives me below error;
{
"SchemaVersion":"15.0.0.0","LibraryVersion":"16.0.3912.1201","ErrorInfo":{
"ErrorMessage":"Access denied. You do not have permission to perform
this action or access this
resource.","ErrorValue":null,"TraceCorrelationId":"2a47fd9c-c07b-1000-cfb7-cdffbe3ab83a","ErrorCode":-2147024891,"ErrorTypeName":"System.UnauthorizedAccessException"
},"TraceCorrelationId":"2a47fd9c-c07b-1000-cfb7-cdffbe3ab83a" }
Now the user is global admin. I also added again that user as site collection admin.
The same piece of code works fine on other site collections (search site collection, any newly made site collection...).
here is a code;
using (ClientContext spcollContext = new ClientContext(web.Url))
{
SecureString passWord = new SecureString();
foreach (char c in strAdminPassword.ToCharArray()) passWord.AppendChar(c);
SharePointOnlineCredentials creds = new SharePointOnlineCredentials(strAdminUser, passWord);
spcollContext.Credentials = creds;
Web currentweb = spcollContext.Web;
spcollContext.Load(currentweb);
spcollContext.ExecuteQuery();
// authCookie = creds.GetAuthenticationCookie(new Uri(web.Url));
var existingActions2 = currentweb.UserCustomActions;
spcollContext.Load(existingActions2);
spcollContext.ExecuteQuery();
var actions2 = existingActions2.ToArray();
foreach (var action in actions2)
{
if (action.Description == "CustomScriptCodeForEachsite" &&
action.Location == "ScriptLink")
{
action.DeleteObject();
spcollContext.ExecuteQuery();
}
}
var newAction2 = existingActions2.Add();
newAction2.Description = "CustomScriptCodeForEachsite";
newAction2.Location = "ScriptLink";
newAction2.ScriptBlock = scriptBlock;
newAction2.Update();
spcollContext.Load(currentweb, s => s.UserCustomActions);
spcollContext.ExecuteQuery(); // GETTING ERROR ON THIS LINE.
}
Note: Above error is Fiddler traces.
Most probably this behavior is caused by Custom Script feature, basically
the issue occurs when the Custom Script feature is turned off
How to verify?
You could verify the site permissions using the following console app:
using (var ctx = GetContext(webUri, userName, password))
{
var rootWeb = ctx.Site.RootWeb;
ctx.Load(rootWeb, w => w.EffectiveBasePermissions);
ctx.ExecuteQuery();
var permissions = rootWeb.EffectiveBasePermissions;
foreach (var permission in Enum.GetValues(typeof(PermissionKind)).Cast<PermissionKind>())
{
var permissionName = Enum.GetName(typeof(PermissionKind), permission);
var hasPermission = permissions.Has(permission);
Console.WriteLine("Permission: {0}, HasPermission: {1}", permissionName, hasPermission);
}
}
where
public static ClientContext GetContext(Uri webUri, string userName, string password)
{
var securePassword = new SecureString();
foreach (var ch in password) securePassword.AppendChar(ch);
return new ClientContext(webUri) {Credentials = new SharePointOnlineCredentials(userName, securePassword)};
}
When SP.PermissionKind.AddAndCustomizePages is set to False, the Access denied error occurs while adding user custom action.
Solution
According to Turn scripting capabilities on or off:
For self-service created sites, custom scripting is disabled by
default
Solution: enable Allow users to run custom scripts on self-service created sites
To enable or disable scripting from the SharePoint admin center
Sign in to Office 365 with your work or school account.
Go to the SharePoint admin center.
Select Settings.
Under Custom Script choose:
Prevent users from running custom script on personal sites or Allow
users to run custom script on personal sites.
Prevent users from running custom script on user created sites or
Allow users to run custom script on self-service created sites.
Select OK. It takes about 24 hours for the change to take
effect.
Since any change to the scripting setting made through the SharePoint Online admin center may take up to 24 hours to take effect, you could enable scripting on a particular site collection immediately via CSOM API (SharePoint Online Client Components SDK) as demonstrated below:
public static void DisableDenyAddAndCustomizePages(ClientContext ctx, string siteUrl)
{
var tenant = new Tenant(ctx);
var siteProperties = tenant.GetSitePropertiesByUrl(siteUrl, true);
ctx.Load(siteProperties);
ctx.ExecuteQuery();
siteProperties.DenyAddAndCustomizePages = DenyAddAndCustomizePagesStatus.Disabled;
var result = siteProperties.Update();
ctx.Load(result);
ctx.ExecuteQuery();
while (!result.IsComplete)
{
Thread.Sleep(result.PollingInterval);
ctx.Load(result);
ctx.ExecuteQuery();
}
}
Usage
using (var ctx = GetContext(webUri, userName, password))
{
using (var tenantAdminCtx = GetContext(tenantAdminUri, userName, password))
{
DisableDenyAddAndCustomizePages(tenantAdminCtx,webUri.ToString());
}
RegisterJQueryLibrary(ctx);
}
where
public static void RegisterJQueryLibrary(ClientContext context)
{
var actions = context.Site.UserCustomActions;
var action = actions.Add();
action.Location = "ScriptLink";
action.ScriptSrc = "~SiteCollection/Style Library/Scripts/jQuery/jquery.min.js";
action.Sequence = 1482;
action.Update();
context.ExecuteQuery();
}
If you don't have time for CSOM as described by Vadim, the page also links to a powershell script you can use:
Set-SPOsite <SiteURL> -DenyAddAndCustomizePages 0
But note that SiteUrl needs to be the admin url. If your tenant is https://mysite.sharepoint.com, the url you use is https://mysite-admin.sharepoint.com"
In our case, we were in the midst of a deployment when this hit and could not wait 24 hours (or even one hour!) to continue. Everything had been fine in our testing site collections, but when we deployed to the tenant root, we hit the error described above and this script fixed it. Apparently the feature is turned off by default on the tenant root.
Current site is not a tenant administration site
Turn scripting capabilities on or off
My first response would be that you shouldn't add a CustomAction on the fly through code. That said, I'm sure you have a good reason to need to do so.
Try to set the AllowUnsafeUpdates flag on SPWeb to true as soon as you reference currentWeb. Make sure to also set it back to false after you call the final ExecuteQuery()
By default, AllowUnsafeUpdates is false. It is used to block cross-site scripting attacks.
https://msdn.microsoft.com/en-us/library/Microsoft.SharePoint.SPWeb_properties.aspx

Creating new site collection in Office 365 from an APP

I have a requirement to create a new site collection from within an App in Office 365 programmatically. What I mean by a new site collection, is that after creation, it should appear on the list of site collections under the Admin --> Sharepoint tab of Office 365. I tried using a similar code below within a sharepoint hosted app that i had created,
//create sp context and get root
var clientContext = new SP.ClientContext.get_current();
var rootWeb = clientContext.site.rootWeb();
this.clientContext.load(rootWeb);
this.clientContext.executeQUery();
//set web info
var webInfo = new SP.WebCreationInformation();
webInfo.set_webTemplate('YourTemplateName');
webInfo.set_description('Your site description');
webInfo.set_title('Your site tittle');
webInfo.set_url(siteUrl);
webInfo.set_language(yourLangCode);
this.rootWeb.get_webs().add(webInfo);
this.rootWeb.update();
// save site and set callbacks
this.clientContext.load(this.rootWeb);
this.clientContext.executeQueryAsync(
Function.createDelegate(this, this.OnSiteCreationSuccess),
Function.createDelegate(this, this.Error));
However this just creates a sub site under the site collection that hosts my App.
Any suggestions on how i could implement this would be greatly appreciated.
It can be done with SharePoint Object Model 2013, the functions you need are inside the assembly: Microsoft.Online.SharePoint.Client.Tenant.dll, which is located at C:\Program Files\SharePoint Client Components\Assemblies after you install the SharePoint Client Object model 2013.
There's not much doc on this, but SharePoint Online Management Shell has the command to create the site collection, so I think it can be done with C# and figured it out. The code snippet shows how to do it.
using System;
using Microsoft.Online.SharePoint.TenantAdministration;
using Microsoft.SharePoint.Client;
using System.Security;
namespace SharePoint123
{
class Program
{
static void Main(string[] args)
{
//please change the value of user name, password, and admin portal URL
string username = "xxxx#xxxx.onmicrosoft.com";
String pwd = "xxxx";
ClientContext context = new ClientContext("https://xxxx-admin.sharepoint.com");
SecureString password = new SecureString();
foreach (char c in pwd.ToCharArray())
{
password.AppendChar(c);
}
context.Credentials = new SharePointOnlineCredentials(username, password);
Tenant t = new Tenant(context);
context.ExecuteQuery();//login into SharePoint online
//code to create a new site collection
var newsite = new SiteCreationProperties()
{
Url = "https://xxxxx.sharepoint.com/sites/createdbyProgram1",
Owner = "xxxxx#xxxxx.onmicrosoft.com",
Template = "STS#0", //using the team site template, check the MSDN if you want to use other template
StorageMaximumLevel = 100,
UserCodeMaximumLevel = 100,
UserCodeWarningLevel = 100,
StorageWarningLevel = 300,
Title = "CreatedbyPrgram",
CompatibilityLevel = 15, //15 means Shapoint online 2013, 14 means Sharepoint online 2010
};
t.CreateSite(newsite);
context.ExecuteQuery();
//end
}
}
}

Resources