public static void CopyFile(
            [ActivityTrigger] ApprovalStartInfo approvalStartInfo,
            ILogger log,
            ExecutionContext context)
        {
            using (var cc = new OfficeDevPnP.Core.AuthenticationManager().GetAppOnlyAuthenticatedContext(ConfigurationManager.AppSettings["siteUrl"], ConfigurationManager.AppSettings["clientId"], ConfigurationManager.AppSettings["clientSecret"]))
            {
                List     docLib = cc.Web.Lists.GetByTitle("Drafts");
                ListItem item   = docLib.GetItemById(approvalStartInfo.itemId);
                Microsoft.SharePoint.Client.File file = item.File;

                cc.Load(file);
                cc.Load(item);

                cc.ExecuteQuery();
                string dest = ConfigurationManager.AppSettings["siteUrl"] + "Published/" + file.Name;
                file.CopyTo(dest, true);
                cc.ExecuteQuery();
            };
        }
        public static ListItemData GetListItemData(
            [ActivityTrigger] ApprovalStartInfo approvalStartInfo,
            ILogger log,
            ExecutionContext context)
        {
            using (var cc = new OfficeDevPnP.Core.AuthenticationManager().GetAppOnlyAuthenticatedContext(ConfigurationManager.AppSettings["siteUrl"], ConfigurationManager.AppSettings["clientId"], ConfigurationManager.AppSettings["clientSecret"]))

            {
                List     docLib = cc.Web.Lists.GetByTitle("Drafts");
                ListItem item   = docLib.GetItemById(approvalStartInfo.itemId);
                cc.Load(item);
                cc.ExecuteQuery();
                var listItemData = new ListItemData();
                listItemData.DocumentOwner = ((FieldUserValue)item["DocumentOwner"]).LookupId;
                listItemData.StakeHolders  = new List <int>();
                foreach (FieldUserValue sh in (FieldUserValue[])item["StakeHolders"])
                {
                    listItemData.StakeHolders.Add(sh.LookupId);
                }
                return(listItemData);
            };
        }
        public static async Task <HttpResponseMessage> ApprovalStart(
            [HttpTrigger(AuthorizationLevel.Function, "OPTIONS", "POST")] HttpRequestMessage req,
            [OrchestrationClient] DurableOrchestrationClient starter,
            TraceWriter log,
            ExecutionContext context)
        {
            // Function input comes from the request content.

            if (req.Method == HttpMethod.Options)
            {
                var response = req.CreateResponse();
                if (AddCORSHeaders(req, ref response, "POST,OPTIONS", log))
                {
                    response.StatusCode = HttpStatusCode.OK;
                }
                else
                {
                    response.StatusCode = HttpStatusCode.InternalServerError;
                }
                return(response);
            }
            ApprovalStartInfo approvalStartInfo = await req.Content.ReadAsAsync <ApprovalStartInfo>();

            string instanceId = await starter.StartNewAsync("Publish", approvalStartInfo);

            log.Info($"Started orchestration with ID = '{instanceId}'.");
            var resp = starter.CreateCheckStatusResponse(req, instanceId);

            if (AddCORSHeaders(req, ref resp, "POST,OPTIONS", log))
            {
                resp.StatusCode = HttpStatusCode.OK;
            }
            else
            {
                resp.StatusCode = HttpStatusCode.InternalServerError;
            }
            return(resp);
        }
        public static void DocownerRejected(
            [ActivityTrigger] ApprovalStartInfo approvalStartInfo,
            TraceWriter log,
            ExecutionContext context
            )
        {
            using (var cc = new OfficeDevPnP.Core.AuthenticationManager().GetAppOnlyAuthenticatedContext(ConfigurationManager.AppSettings["siteUrl"], ConfigurationManager.AppSettings["clientId"], ConfigurationManager.AppSettings["clientSecret"]))
            {
                var emailp = new EmailProperties();
                emailp.BCC = new List <string> {
                    approvalStartInfo.startedByEmail
                };
                emailp.To = new List <string> {
                    approvalStartInfo.startedByEmail
                };
                emailp.From    = "*****@*****.**";
                emailp.Body    = "<b>rejected</b>";
                emailp.Subject = "rejected";

                Utility.SendEmail(cc, emailp);
                cc.ExecuteQuery();
            };
        }
        public static async Task Publish(
            [OrchestrationTrigger] DurableOrchestrationContext context,
            ILogger log
            )

        {
            // get the parameters passed in to the this (the publish/orchestrator function)
            ApprovalStartInfo approvalStartInfo = context.GetInput <ApprovalStartInfo>();


            context.SetCustomStatus("Fetching list data");

            // 'Call' a function to fetch the info from the sharepoint item being approvd.
            // this call writes an entry to the workitem queue and stops execution of the the publish/orchestrator function)
            // the GetListItemData function gets triggered when it sees the entry in the workItem queue
            ListItemData listItemData = await context.CallActivityAsync <ListItemData>("GetListItemData", approvalStartInfo);

            // when the GetListItemData returns a value , that value gets written into the controle queuw
            // that will cause this function to restart. When this function restarts, and comes to the line above,
            // the CallActivityAsync method will see that GetListItemData has already been called  (by looking in the control queue
            // So it wont call the function again, it will just put the returned valuse in the listItemData variable.

            context.SetCustomStatus("Sceduling Docownwer Approval");

            // 'Call' a function to get the document ownwers approval
            // this call writes an entry to the workitem queue and stops execution of the the publish/orchestrator function)
            // the ScheduleDocOwnerApproval function gets triggered when it sees the entry in the workItem queue
            // the ScheduleDocOwnerApproval function creates a task for the document owner to approve/reject.
            // when the document ownwer approves or rejects the task, the TaskNotifications Azure Function will send a
            // DocOwnerApproved or  DocOwnerRejected external event to this function.
            await context.CallActivityAsync <string>("ScheduleDocOwnerApproval", new ScheduleDocOwnerApprovalParms()
            {
                instanceId = context.InstanceId, DocumentOwner = listItemData.DocumentOwner
            });

            var docOwnerApproved = context.WaitForExternalEvent("DocOwnerApproved");
            var docOwnerRejected = context.WaitForExternalEvent("DocOwnerRejected");

            context.SetCustomStatus("Awaiting  Docownwer approval");

            // wait for one of those two events (no tiemout here)
            var winner = await Task.WhenAny(docOwnerApproved, docOwnerRejected);

            if (winner == docOwnerRejected)
            {
                // if the Document Owner rejected the task trigger execution of the DocownerRejected function
                // which just sends an email. The workflow is then done!
                context.SetCustomStatus("Docownwer Rejected");
                await context.CallActivityAsync <string>("DocownerRejected", approvalStartInfo);
            }
            else
            {
                // stakeholder IDs is an array of the ids of the stakeholders (fron the user information list)
                List <int> stakeholderIDs = listItemData.StakeHolders;

                // create a list of tasks that need to be approved -- one per stakeholder
                var stakeHolderApprovalTasks = new Task[stakeholderIDs.Count];

                for (int i = 0; i < stakeholderIDs.Count; i++)
                {
                    //'Call' the ScheduleStakeholderApproval function to get the stakeholders approval.
                    // The ScheduleStakeholderApproval function will create a task in the Tasks list for the stakeholder.
                    // When the stakeholder Approves the task, the TaskNotifications Azure Function will send a
                    // StakeHolderApproval:xx (where xx is the ID of the stakeholder) event to this function.
                    // When the stakeholder Rejects  the task, the TaskNotifications Azure Function will send a
                    // StakeHolderRejection event to this function.
                    await context.CallActivityAsync("ScheduleStakeholderApproval", new ScheduleStakeHolderApprovalParams()
                    {
                        instanceId = context.InstanceId, stakeHolderId = stakeholderIDs[i]
                    });

                    string eventName = "StakeHolderApproval:" + stakeholderIDs[i];

                    // add the approval task to the list of stakeholder tasks that we will be waiting on/
                    stakeHolderApprovalTasks[i] = context.WaitForExternalEvent(eventName);
                }

                // the stakeHolderApprovalTask will be completed when ALL stakeholders approve their tasks
                var stakeHolderApprovalTask = Task.WhenAll(stakeHolderApprovalTasks);
                // the stakeHolderRejectionTask will be completed when ANY stakeholder rejects her task
                var stakeHolderRejectionTask = context.WaitForExternalEvent("StakeHolderRejection");


                // Now we need to set up a CancellationTokenSource so that we can wait for a certain amout of ti,e
                using (var cts = new CancellationTokenSource())
                {
                    //for this sample we will wait for just one minute.
                    var timeoutAt = context.CurrentUtcDateTime.AddMinutes(1);
                    //now we can create a task that will timeout after one minute.
                    var timeoutTask = context.CreateTimer(timeoutAt, cts.Token);

                    // And now.... wait for EITHER the timeout, a SINGLE rejection, or ALL stakeholders to approve.
                    var stakeHolderResults = await Task.WhenAny(stakeHolderApprovalTask, timeoutTask, stakeHolderRejectionTask);

                    if (stakeHolderResults == stakeHolderApprovalTask)
                    {
                        cts.Cancel(); //  cancel the timeout task
                        log.LogCritical("received stakeholder approvals");
                        // copy the file
                        await context.CallActivityAsync <ListItemData>("CopyFile", approvalStartInfo);
                    }
                    else if (stakeHolderResults == stakeHolderRejectionTask)
                    {
                        cts.Cancel(); // we should cancel the timeout task
                        log.LogCritical("A stakeholder rejected");
                        // should send email that action was rejected
                    }
                    else
                    {
                        // timed out
                        log.LogCritical(" Timed out waiting for stakeholder approvals");
                        // copy the file
                        await context.CallActivityAsync <ListItemData>("CopyFile", approvalStartInfo);
                    }
                }
            }
        }