これは私の最初の SO の質問です。この質問があまり明確でない場合、または何か不足している場合はお知らせください。
参考までに、リンクを添付できませんでした。フォーマットが不適切で申し訳ありません。
概要
Microsoft が提供する CSOM ライブラリを使用して、Project Server Online のリソースの "実績作業時間" を読み取ろうとしています (および書き込もうとしています)。現在認証されているユーザーの割り当てを読んでいる限り、割り当てと実際の作業の読み取りと書き込みは完全に機能しています。これを別のリソースで読み取ろうとすると、GeneralSecurityAccessDenied エラーが発生します。
ユーザーが StatusBrokerPermission を持っている場合、バックグラウンドで透過的に呼び出されるはずの Impersonation を使用して過去にこれを行いましたが、うまくいかないようです。偽装は 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- view-data-project-server-online-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 のすべての認証方法を使用してみましたが、次の追加のテストを行いました。 .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);
}
}
}
誰か助けてくれませんか??