这是我的第一个 SO 问题,所以如果这个问题不是很清楚或者我遗漏了什么,请告诉我。
仅供参考,我无法附加链接,对于所有错误的格式,我们深表歉意。
概述
我正在尝试使用 Microsoft 提供的 CSOM 库来读取(和写入)Project Server Online 中资源的“实际工作”。只要我正在阅读当前经过身份验证的用户的作业,阅读和编写作业和实际工作就可以完美地工作。如果我尝试为其他资源读取此内容,则会收到 GeneralSecurityAccessDenied 错误。
我过去曾使用 Impersonation 完成此操作,如果用户拥有 StatusBrokerPermission,它应该在后台透明地调用,但它似乎对我不起作用。模拟已在 2013+ 中删除,因此不再是一个选项。
问题总结
CSOM 应该透明地启用状态扩展,以允许对当前经过身份验证的用户以外的资源进行状态更新(只要用户具有状态代理权限)。这适用于添加新任务,但在尝试通过 TimePhased 任务更新实际 TimePhased 小时数时不起作用。无法查询分配,因此我们无法调用 SubmitAllStatusUpdates 来提交小时数。
研究
CSOM 的使用场景:https://msdn.microsoft.com/en-us/library/office/jj163082(v=office.15).aspx#pj15_WhatTheCSOM_UsageScenarios
模拟已弃用:https://msdn.microsoft.com/en-us/library/office/ee767690(v=office.15).aspx#pj15_WhatsNew_Deprecated)
有同样问题的人#1:https://social.technet.microsoft.com/Forums/projectserver/en-US/dccdb543-18a1-4a0e-a948-5d861305516e/how-to-get-resource-assignments-summary-查看数据项目服务器在线 2013?forum=projectonline)
有同样问题的人#2:http://uzzai.com/ZB43wp95/ps2013-app-how-to-read-and-update-timephased-data-with-jsom-javascript-csom.html
有同样问题的人#4:https://social.technet.microsoft.com/Forums/Sharepoint/en-US/be27d497-e959-44b6-97cb-8f19fe0278fe/csom-how-to-set-timephase-data- on-an-assignment?forum=project2010custprog
我尝试过的其他事情
- 使用 CSOM 和 MsOnlineClaimsHelper 来检索用户的 FedAuth cookie(并使用 CookieContainer 分配它们)。
- 使用 REST/OData API。a) https://URL.sharepoint.com/sites/pwa/_api/ProjectServer/EnterpriseResources('c39ba8f1-00fe-e311-8894-00155da45f0e')/Assignments/GetTimePhaseByUrl(start='2014-12-09',end ='2014-12-09')/作业
- 为用户启用“StatusBrokerPermission”
- 取消选中“仅允许通过任务和时间表更新任务”。服务器设置屏幕中的选项(任务设置和显示)。
- 创建 SharePoint 托管的应用程序并使用与上述 CSOM 代码等效的 JSOM 代码。a) 我们编写的代码是从 SharePoint 应用程序中执行的 JavaScript,因此我们不需要提供身份验证。登录的用户拥有 StatusBrokerPermission。
- 使用提供商托管的 SharePoint 应用程序并使用上面的 CSOM 代码。我们尝试使用上述 CSOM 的所有身份验证方法,并进行附加测试:a) 使用 Fiddler 查看由 SharePoint 应用身份验证设置的 FedAuth cookie,并覆盖 WebRequest 以手动插入 FedAuth/rtFA cookie:webRequestEventArgs.WebRequestExecutor.WebRequest .CookieContainer = getStaticCookieContainer();
- 使用时间表提交时间分段数据。a) 我们只能为当前经过身份验证的用户创建时间表,并且不能使用他不可用的项目/分配填充时间表行(或抛出 GeneralItemDoesNotExist 错误)。
- 使用 fiddler 手动发出“SubmitAllStatusUpdates”CSOM 请求,作为不同的用户。a) 这个测试的目的是确定我们是否可以写入时间分段数据,即使我们无法读取它。
- 确保项目已签出给当前用户。
- 对资源使用管理委派。
- 在项目权限内设置所有可用选项。
- 使用 Project Web UI 输入其他资源的 TimePhased 数据。
- 使用 SharePoint 权限模式而不是项目权限模式。
编码
using System;
using System.Security;
using Microsoft.ProjectServer.Client;
using Microsoft.SharePoint.Client;
namespace ProjectOnlineActuals
{
static class Program
{
const string projectSite = "https://URL.sharepoint.com/sites/pwa/";
private const string edward = "c39ba8f1-00fe-e311-8894-00155da45f0e";
private const string admin = "8b1bcfa4-1b7f-e411-af75-00155da4630b";
static void Main(string[] args)
{
TestActuals();
}
private static void TestActuals()
{
Console.WriteLine("Attempting test # 1 (login: admin, resource: admin)");
TestActuals("admin@URL.onmicrosoft.com", "123", admin);
Console.WriteLine("Attempting test # 2 (login: admin, resource: edward)");
TestActuals("adminy@hmssoftware.onmicrosoft.com", "123", edward);
Console.ReadLine();
}
private static void TestActuals(string username, string password, string resourceID)
{
try
{
using (ProjectContext context = new ProjectContext(projectSite))
{
DateTime startDate = DateTime.Now.Date;
DateTime endDate = DateTime.Now.Date;
Login(context, username, password);
context.Load(context.Web); // Query for Web
context.ExecuteQuery(); // Execute
Guid gResourceId = new Guid(resourceID);
EnterpriseResource enterpriseResource = context.EnterpriseResources.GetByGuid(gResourceId);
context.Load(enterpriseResource, p => p.Name, p => p.Assignments, p => p.Email);
Console.Write("Loading resource...");
context.ExecuteQuery();
Console.WriteLine("done! {0}".FormatWith(enterpriseResource.Name));
Console.Write("Adding new resource assignment to collection...");
enterpriseResource.Assignments.Add(new StatusAssignmentCreationInformation
{
Comment = "testing comment - 2016-02-17",
ProjectId = new Guid("27bf182c-2339-e411-8e76-78e3b5af0525"),
Task = new StatusTaskCreationInformation
{
Start = DateTime.Now,
Finish = DateTime.Now.AddDays(2),
Name = "testing - 2016-02-17",
}
});
Console.WriteLine("done!");
Console.Write("Trying to save new resource assignment...");
enterpriseResource.Assignments.Update();
context.ExecuteQuery();
Console.WriteLine("done!");
Console.Write("Loading TimePhase...");
TimePhase timePhase = enterpriseResource.Assignments.GetTimePhase(startDate.Date, endDate.Date);
context.ExecuteQuery();
Console.WriteLine("done!");
Console.Write("Loading TimePhase assignments...");
context.Load(timePhase.Assignments);
context.ExecuteQuery();
Console.WriteLine("done! Found {0} assignments.".FormatWith(timePhase.Assignments.Count));
Console.WriteLine("Updating TimePhase assignments...");
foreach (var assignment in timePhase.Assignments)
{
Console.WriteLine("Updating assignment: {0}. ActualWork: {1}".FormatWith(assignment.Name, assignment.ActualWork));
assignment.ActualWork = "9h";
assignment.RegularWork = "3h";
assignment.RemainingWork = "0h";
}
timePhase.Assignments.SubmitAllStatusUpdates("Status update comment test 2016-02-17");
context.ExecuteQuery();
Console.WriteLine("done!");
Console.WriteLine("Success (retrieved & updated {0} time phase assignments)!".FormatWith(timePhase.Assignments.Count));
}
}
catch (Exception ex)
{
if (ex.ToString().Contains("GeneralSecurityAccessDenied"))
Console.WriteLine("ERROR! - GeneralSecurityAccessDenied");
else
throw;
}
finally
{
Console.WriteLine();
Console.WriteLine();
}
}
private static void Login(ProjectContext projContext, string username, string password)
{
var securePassword = new SecureString();
foreach (char c in password)
securePassword.AppendChar(c);
projContext.Credentials = new SharePointOnlineCredentials(username, securePassword);
}
static string FormatWith(this string str, params object[] args)
{
return String.Format(str, args);
}
}
}
有人可以帮忙吗??