MOSSのワークフローで動的にアクセス権を制御する

「下書きで保存」する機能を承認ステータスを利用してつくろうとしたら、SharePointのバグで断念した訳ですが、それなら、と今度は別の角度からのアプローチを考えました。

ワークフローで、登録されたアイテムの権限を動的に変更してしまうのはどうでしょう?「公開/下書き」のフラグ列を用意して、保存・変更時にワークフローを起動。フラグ列の値に応じて、アクセス権の設定を変更する。こうすれば、「下書き時は自分だけ見えて、公開すると全員が見られる」という機能が実装できそうです。

早速、Visual Sutudio でデザインしてみました。以下、そのソースです。ご参考まで。

[code]
namespace setDraftWorkflow
{
public static class SPEventManagerWrapper
{

private static readonly string _className = "Microsoft.SharePoint.SPEventManager";
private static readonly string _eventFiringSwitchName = "EventFiringDisabled";
private static Type _eventManagerType;

/// gets the status of event firing on the current thread
public static bool EventFiringDisabled
{
get { return GetEventFiringSwitchValue(); }
}

private static Type EventManagerType
{
get
{
if (_eventManagerType == null)
GetEventManagerType();
return _eventManagerType;
}
}

/// enables event firing on the current thread
public static void EnableEventFiring()
{
SetEventFiringSwitch(false);
}

/// disables sharepoint event firing on the current thread
public static void DisableEventFiring()
{
SetEventFiringSwitch(true);
}

/// sets the event firing switch on Microsoft.SharePoint.SPEventManager class using reflection
private static void SetEventFiringSwitch(bool value){
PropertyInfo pi = EventManagerType.GetProperty(_eventFiringSwitchName, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
pi.SetValue(null, value, null);
}

private static bool GetEventFiringSwitchValue(){
PropertyInfo pi = EventManagerType.GetProperty(_eventFiringSwitchName, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
object val = pi.GetValue(null, null);
return (bool)val;
}

private static Type GetEventManagerType()
{
_eventManagerType = typeof(SPList).Assembly.GetType(_className, true);
return _eventManagerType;
}
}

public sealed partial class Workflow1: SequentialWorkflowActivity
{
public Workflow1()
{
InitializeComponent();
}

public Guid workflowId = default(System.Guid);
public Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties workflowProperties = new Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties();

private SPPrincipal getUserPrincipal( SPWeb objWeb, string strUser )
{
SPPrincipal objPrincipal;
try
{
//objPrincipal = objWeb.Users[ strUser ];
objPrincipal = objWeb.Groups[strUser];
}
catch
{
objWeb.SiteUsers.Add(strUser, null, null, null);
objPrincipal = (SPPrincipal)objWeb.SiteGroups[ strUser ];
}
return objPrincipal;
}

private void onWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e)
{
SPListItem activator = workflowProperties.Item;
if (activator.GetFormattedValue("状態").CompareTo("公開") == 0)
{
// inherit role.
activator.ResetRoleInheritance();
}
else
{
activator.BreakRoleInheritance(true);
SPEventManagerWrapper.DisableEventFiring();
activator.Update();
SPEventManagerWrapper.EnableEventFiring();

// remove roles.
SPRoleAssignment roleAssign = new SPRoleAssignment(workflowProperties.OriginatorUser);
for (int idx = activator.RoleAssignments.Count -1; idx >= 0; idx--)
{
activator.RoleAssignments.Remove( idx );
}

// add myrole.
roleAssign.RoleDefinitionBindings.Add(activator.Web.RoleDefinitions["削除"]);
activator.RoleAssignments.Add(roleAssign);

SPEventManagerWrapper.DisableEventFiring();
activator.Update();
SPEventManagerWrapper.EnableEventFiring();
}

activator["更新者"] = workflowProperties.OriginatorUser;
SPEventManagerWrapper.DisableEventFiring();
activator.Update();
SPEventManagerWrapper.EnableEventFiring();
}
}
}
[/code]
リストアイテムの「状態」列を参照して、値が「公開」でなかった場合に、作成者にアクセス権限レベル「削除」(MOSSデフォルトではありません。私が設定しました)を付与している訳です。
逆に、値が「公開」になった場合は、リストの権限を継承させます。

これで完璧だ~、ひゃっほ~ぃ …と喜んだのですが、実は、この手法にも、想定外の落とし穴があったのです…。

続きはまた明日(笑)


Author

中村 和彦(シンプレッソ・コンサルティング株式会社 代表)が「ユーザ視点の SharePoint 情報」を発信します。元大手製造業 SharePoint 運用担当。現SharePoint コンサルタント。お仕事のお問い合わせはこちらまでお願いします。当ブログにおける発信内容は個人に帰属し所属組織の公式発信/見解ではありません。
FB : 中村 和彦
blog: Be・Better!
MS MVP SharePoint 2009/10-2011/9
MS MVP Office 365 2012/10-2014/9