예제 #1
0
        void ContentTypeRetentionEnforcementJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            try
            {
                Log.Info("ContentTypeRetentionEnforcementJob", "Scanning web {0}", e.Url);

                //Get all document libraries. Lists are excluded.
                var documentLibraries = GetAllDocumentLibrariesInWeb(e.WebClientContext, e.WebClientContext.Web);

                //Iterate through all document libraries
                foreach (var documentLibrary in documentLibraries)
                {
                    Log.Info("ContentTypeRetentionEnforcementJob", "Scanning library {0}", documentLibrary.Title);

                    //Iterate through configured content type retention policies in app.config
                    foreach (var contentTypeName in configContentTypeRetentionPolicyPeriods.Keys)
                    {
                        var retentionPeriods = configContentTypeRetentionPolicyPeriods.GetValues(contentTypeName as string);
                        if (retentionPeriods != null)
                        {
                            var retentionPeriod = int.Parse(retentionPeriods[0]);
                            ApplyRetentionPolicy(e.WebClientContext, documentLibrary, contentTypeName, retentionPeriod);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("ContentTypeRetentionEnforcementJob", "Exception processing site {0}. Exception is {1}", e.Url, ex.Message);
            }
        }
예제 #2
0
        private void ExecuteProvisioningJobs(object sender, TimerJobRunEventArgs e)
        {
            Console.WriteLine("Starting job");

            // Show the current context
            Web web = e.SiteClientContext.Web;

            web.EnsureProperty(w => w.Title);
            Console.WriteLine("Processing jobs in Site: {0}", web.Title);

            // Retrieve the list of pending jobs
            var provisioningJobs = ProvisioningRepositoryFactory.Current.GetTypedProvisioningJobs <ProvisioningJob>(
                ProvisioningJobStatus.Pending);

            foreach (var job in provisioningJobs)
            {
                Console.WriteLine("Processing job: {0} - Owner: {1} - Title: {2}",
                                  job.JobId, job.Owner, job.Title);

                Type jobType = job.GetType();

                if (PnPPartnerPackSettings.ScheduledJobHandlers.ContainsKey(jobType))
                {
                    PnPPartnerPackSettings.ScheduledJobHandlers[jobType].RunJob(job);
                }
            }

            Console.WriteLine("Ending job");
        }
예제 #3
0
        private void EnforceTwoAdministratorsTimerJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            Console.WriteLine("Starting job");
            var web = e.SiteClientContext.Web;

            var siteUsers = e.SiteClientContext.LoadQuery(web.SiteUsers.Include(u => u.Email).Where(u => u.IsSiteAdmin));

            e.SiteClientContext.ExecuteQueryRetry();

            if (siteUsers.Count() < 2)
            {
                Console.WriteLine("Site found");
                if (!web.IsPropertyAvailable("Url"))
                {
                    e.SiteClientContext.Load(web, w => w.Url);
                    e.SiteClientContext.ExecuteQueryRetry();
                }
                var adminUser = siteUsers.FirstOrDefault();

                EmailProperties mailProps = new EmailProperties();
                mailProps.Subject = "Action required: assign an additional site administrator to your site";
                StringBuilder bodyBuilder = new StringBuilder();
                bodyBuilder.Append("<html><body style=\"font-family:sans-serif\">");
                bodyBuilder.AppendFormat("<p>Your site with address <a href=\"{0}\">{0}</a> has only one site administrator defined: you. Please assign an additional site administrator.</p>", e.SiteClientContext.Web.Url);
                bodyBuilder.AppendFormat("<p>Click here to <a href=\"{0}/_layouts/mngsiteadmin.aspx\">assign an additional site collection adminstrator.</a></p>", e.SiteClientContext.Web.Url);
                bodyBuilder.Append("</body></html>");
                mailProps.Body = bodyBuilder.ToString();
                mailProps.To   = new[] { adminUser.Email };
                Utility.SendEmail(e.SiteClientContext, mailProps);
                e.SiteClientContext.ExecuteQueryRetry();
            }
            Console.WriteLine("Ending job");
        }
예제 #4
0
        /// <summary>
        /// Load the latest site collection status from SharePoint
        /// </summary>
        /// <param name="e">Timer job event arguments</param>
        /// <param name="tenant">The tenant object</param>
        /// <param name="site">The site object</param>
        /// <param name="properties">The site properties object</param>
        private void LoadSiteStatus(TimerJobRunEventArgs e, out Tenant tenant, out Site site, out SiteProperties properties)
        {
            var tenantClientContext = e.TenantClientContext;

            Log.Info(base.Name, TimerJobsResources.SynchJob_GetSiteStatus, e.Url);
            tenant     = new Tenant(tenantClientContext);
            site       = tenant.GetSiteByUrl(e.Url);
            properties = tenant.GetSitePropertiesByUrl(e.Url, includeDetail: false);
            tenantClientContext.Load(tenant,
                                     t => t.SharingCapability);
            tenantClientContext.Load(site,
                                     s => s.RootWeb.Title,
                                     s => s.RootWeb.Description,
                                     s => s.RootWeb.Language,
                                     s => s.RootWeb.Created,
                                     s => s.Id);
            tenantClientContext.Load(properties,
                                     s => s.StorageMaximumLevel,
                                     s => s.StorageWarningLevel,
                                     s => s.UserCodeMaximumLevel,
                                     s => s.UserCodeWarningLevel,
                                     s => s.TimeZoneId,
                                     s => s.SharingCapability);
            tenantClientContext.ExecuteQueryRetry();
        }
예제 #5
0
        private void TestTimerJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            var web = e.SiteClientContext.Web;

            var users = e.SiteClientContext.LoadQuery(web.SiteUsers.Include(uc => uc.Email).Where(uc => uc.IsSiteAdmin));

            e.SiteClientContext.ExecuteQueryRetry();
        }
예제 #6
0
        void SiteGovernanceJob_TimerJobRun(object o, TimerJobRunEventArgs e)
        {
            try
            {
                string library = "";

                // Get the number of admins
                var admins = e.WebClientContext.Web.GetAdministrators();

                Log.Info("SiteGovernanceJob", "ThreadID = {2} | Site {0} has {1} administrators.", e.Url, admins.Count, Thread.CurrentThread.ManagedThreadId);

                // grab reference to list
                library = "SiteAssets";
                List list = e.WebClientContext.Web.GetListByUrl(library);

                if (!e.GetProperty("ScriptFileVersion").Equals("1.0", StringComparison.InvariantCultureIgnoreCase))
                {
                    if (list == null)
                    {
                        // grab reference to list
                        library = "Style%20Library";
                        list    = e.WebClientContext.Web.GetListByUrl(library);
                    }

                    if (list != null)
                    {
                        // upload js file to list
                        list.RootFolder.UploadFile("sitegovernance.js", "sitegovernance.js", true);

                        e.SetProperty("ScriptFileVersion", "1.0");
                    }
                }

                if (admins.Count < 2)
                {
                    // Oops, we need at least 2 site collection administrators
                    e.WebClientContext.Site.AddJsLink(SiteGovernanceJobKey, BuildJavaScriptUrl(e.Url, library));
                    Console.WriteLine("Site {0} marked as incompliant!", e.Url);
                    e.SetProperty("SiteCompliant", "false");
                }
                else
                {
                    // We're all good...let's remove the notification
                    e.WebClientContext.Site.DeleteJsLink(SiteGovernanceJobKey);
                    Console.WriteLine("Site {0} is compliant", e.Url);
                    e.SetProperty("SiteCompliant", "true");
                }

                e.CurrentRunSuccessful = true;
                e.DeleteProperty("LastError");
            }
            catch (Exception ex)
            {
                Log.Error("SiteGovernanceJob", "Error while processing site {0}. Error = {1}", e.Url, ex.Message);
                e.CurrentRunSuccessful = false;
                e.SetProperty("LastError", ex.Message);
            }
        }
예제 #7
0
        /// <summary>
        /// Timer Job worker method
        /// </summary>
        /// <param name="sender">The event sender</param>
        /// <param name="e">The timer job argument</param>
        protected override void TimerJobRunImpl(object sender, TimerJobRunEventArgs e)
        {
            // build site information from database or create a new one
            var siteInformation = GetSiteInformation(e.Url);

            // load the current site status from SPO
            Tenant         tenant;
            Site           site;
            SiteProperties properties;

            LoadSiteStatus(e, out tenant, out site, out properties);

            // update the site information object with updated site status
            siteInformation.Guid                 = site.Id;
            siteInformation.Title                = site.RootWeb.Title;
            siteInformation.Description          = site.RootWeb.Description;
            siteInformation.Lcid                 = (int)site.RootWeb.Language;
            siteInformation.CreatedDate          = site.RootWeb.Created;
            siteInformation.StorageMaximumLevel  = properties.StorageMaximumLevel;
            siteInformation.StorageWarningLevel  = properties.StorageWarningLevel;
            siteInformation.UserCodeMaximumLevel = properties.UserCodeMaximumLevel;
            siteInformation.UserCodeWarningLevel = properties.UserCodeWarningLevel;
            siteInformation.TimeZoneId           = properties.TimeZoneId;
            siteInformation.SharingStatus        = IsExternalSharingEnabled(properties.SharingCapability, tenant.SharingCapability);
            // update site collection administrators
            var admins = site.RootWeb.GetAdministrators();

            siteInformation.Administrators = (from a in admins
                                              select new SiteUser()
            {
                LoginName = a.LoginName,
                Email = a.Email,
            }).ToList();
            // update external users
            if (siteInformation.SharingStatus != 0)
            {
                var externalUsers = site.RootWeb.GetExternalUsersForSiteTenant(new Uri(e.Url));
                siteInformation.ExternalUsers = (from u in externalUsers
                                                 select new ExternalSiteUser()
                {
                    LoginName = u.AcceptedAs,
                    Email = u.InvitedAs,
                    ExternalUser_AcceptedAs = u.AcceptedAs,
                    ExternalUser_CreatedDate = u.WhenCreated,
                    ExternalUser_DisplayName = u.DisplayName,
                    ExternalUser_InvitedAs = u.InvitedAs,
                    ExternalUser_InvitedBy = u.InvitedBy,
                    ExternalUser_UniqueId = u.UniqueId,
                } as SiteUser).ToList();
            }

            // write site information into database
            Log.Info(base.Name, TimerJobsResources.SynchJob_UpdateDbRecord, e.Url);
            DbRepository.UsingContext(dbContext =>
            {
                dbContext.SaveSite(siteInformation);
            });
        }
 /// <summary>
 /// Run preprocess for the current site
 /// </summary>
 /// <param name="sender">The current timer job instance</param>
 /// <param name="e">The timer job run event arguments</param>
 protected override void TimerJobRunImpl(object sender, TimerJobRunEventArgs e)
 {
     DbRepository.UsingContext(dbContext => {
         var web  = dbContext.GetWeb(e.Url);
         var site = dbContext.GetSite(web.SiteUrl);
         Policy.Preprocess(site, web, e);
         dbContext.SaveChanges();
     });
 }
예제 #9
0
        void TenantAPIJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            Tenant t     = new Tenant(e.TenantClientContext);
            var    sites = t.GetSiteProperties(0, true);

            e.TenantClientContext.Load(sites);
            e.TenantClientContext.ExecuteQueryRetry();

            foreach (var site in sites)
            {
                Console.WriteLine(site.Template);
            }
        }
예제 #10
0
        /// <summary>
        /// Remove database record if the site is not existing in SharePoint
        /// </summary>
        /// <param name="sender">The current time job instance</param>
        /// <param name="e">Time job event arguments</param>
        protected override void TimerJobRunImpl(object sender, TimerJobRunEventArgs e)
        {
            var  tenant  = new Tenant(e.TenantClientContext);
            bool existed = tenant.SiteExists(e.Url);

            if (existed)
            {
                return;
            }
            Log.Info(TimerJobsResources.CleanUpJob_RemoveSite, e.Url);
            DbRepository.UsingContext(context => {
                var site = context.GetSite(e.Url);
                context.Sites.Remove(site);
            });
        }
예제 #11
0
        void ExpandJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            // Read the title from the site being processed
            e.WebClientContext.Load(e.WebClientContext.Web, p => p.Title);
            e.WebClientContext.ExecuteQueryRetry();

            // Read the title from the root site of the site being processed
            e.SiteClientContext.Load(e.SiteClientContext.Web, p => p.Title);
            e.SiteClientContext.ExecuteQueryRetry();

            Console.WriteLine("Root site of site {0} has title {1}", e.Url, e.SiteClientContext.Web.Title);
            Console.WriteLine("Sub site {0} has title {1}", e.Url, e.WebClientContext.Web.Title);

            // Show some threading information
            ThreadingDebugInformation();
        }
예제 #12
0
 /// <summary>
 /// Timer job run event handler to report the current job progress to console
 /// </summary>
 /// <param name="sender">The current timer job instnace</param>
 /// <param name="e">Timer job run event arguments</param>
 protected void ManagementTimerJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
 {
     try
     {
         TimerJobRunImpl(sender, e);
     }
     catch (Exception exception)
     {
         Console.WriteLine(TimerJobsResources.TenantJob_SiteError, Name, e.Url);
         Console.WriteLine(exception.Message);
         throw;
     }
     finally
     {
         e.TenantClientContext.Dispose(); // Dispose the tenant client context object
         Console.WriteLine(TimerJobsResources.TenantJob_Progress, ++CompletedSites, TotalSites, Name);
     }
 }
예제 #13
0
        /// <summary>
        /// Run policy checking and enforcement for the current site colleciton
        /// </summary>
        /// <param name="sender">The current timer job instance</param>
        /// <param name="e">Time job run event arguments</param>
        protected override void TimerJobRunImpl(object sender, TimerJobRunEventArgs e)
        {
            SiteInformation site = null;

            DbRepository.UsingContext(dbContext =>
            {
                site = dbContext.GetSite(e.Url);
                if (site == null)
                {
                    return;
                }
                PolicyManager.Run(e.TenantClientContext, site, SuppressEmail);
                if (site.ComplianceState.DeleteDate == DateTime.MaxValue)
                {
                    dbContext.Sites.Remove(site);
                }
                dbContext.SaveChanges();
            });
        }
예제 #14
0
        void SiteCollectionScopedJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            // Get all the sub sites in the site we're processing
            IEnumerable <string> expandedSites = GetAllSubSites(e.SiteClientContext.Site);

            // Manually iterate over the content
            foreach (string site in expandedSites)
            {
                // Clone the existing ClientContext for the sub web
                using (ClientContext ccWeb = e.SiteClientContext.Clone(site))
                {
                    // Here's the timer job logic, but now a single site collection is handled in a single thread which
                    // allows for further optimization or prevents race conditions
                    ccWeb.Load(ccWeb.Web, s => s.Title);
                    ccWeb.ExecuteQueryRetry();
                    Console.WriteLine("Here: {0} - {1}", site, ccWeb.Web.Title);
                }
            }
        }
예제 #15
0
        private void PolicyJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            try
            {
                Console.WriteLine($"Processing site {e.Url}");

                ProvisioningTemplateApplyingInformation ptai = new ProvisioningTemplateApplyingInformation()
                {
                    HandlersToProcess = Handlers.SiteSecurity,
                };

                XMLTemplateProvider  provider       = new XMLFileSystemTemplateProvider(@".", "");
                ProvisioningTemplate sourceTemplate = provider.GetTemplate(ProvisioningTemplateToApply);

                e.WebClientContext.Web.ApplyProvisioningTemplate(sourceTemplate, ptai);
            }
            catch (Exception ex)
            {
                // Catch exceptions to avoid ending the program run since we're typically processing multiple site collections
                Console.WriteLine($"Error: {ex.Message}");
            }
        }
예제 #16
0
 void Job_TimerJobRun(object sender, TimerJobRunEventArgs e)
 {
     Console.WriteLine("----- Timer Job Triggering ---");
     try
     {
         XmlReaderSettings settings = new XmlReaderSettings();
         settings.DtdProcessing = DtdProcessing.Parse;
         e.WebClientContext.Load(e.WebClientContext.Web);
         e.WebClientContext.ExecuteQueryRetry();
         Web web = e.WebClientContext.Web;
         Console.WriteLine(web.Title);
         e.WebClientContext.Load(web);
         e.WebClientContext.ExecuteQuery();
         Console.WriteLine("Site Request");
         List list = web.Lists.GetByTitle("Site Request");
         e.WebClientContext.Load(list);
         e.WebClientContext.ExecuteQuery();
         GetSiteCollectionDetails(e.WebClientContext, list);
     }
     catch (Exception ex)
     {
         Console.WriteLine(ex.ToString());
     }
 }
예제 #17
0
        /// <summary>
        /// Event handler that's being executed by the threads processing the sites. Everything in here must be coded in a thread-safe manner
        /// </summary>
        private void SBScanner_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            lock (scannedSitesLock)
            {
                ScannedSites++;
            }

            Console.WriteLine("Processing site {0}...", e.Url);
            try
            {
                if (!firstSiteCollectionDone)
                {
                    firstSiteCollectionDone = true;

                    // Telemetry
                    e.WebClientContext.ClientTag = "SPDev:SBScanner";
                    e.WebClientContext.Load(e.WebClientContext.Web, p => p.Description);
                    e.WebClientContext.ExecuteQuery();
                }

                // Query the solution gallery
                CamlQuery          camlQuery      = CamlQuery.CreateAllItemsQuery();
                ListItemCollection itemCollection = e.WebClientContext.Web.GetCatalog(121).GetItems(camlQuery);
                e.WebClientContext.Load(e.WebClientContext.Site, s => s.Id);
                e.WebClientContext.Load(itemCollection);
                e.WebClientContext.ExecuteQueryRetry();

                string siteOwner               = string.Empty;
                int    totalSolutions          = 0;
                int    assemblySolutions       = 0;
                int    activeSolutions         = 0;
                int    activeAssemblySolutions = 0;

                foreach (ListItem item in itemCollection)
                {
                    // We've found solutions
                    totalSolutions++;
                    bool status = false;
                    if (item["Status"] != null)
                    {
                        activeSolutions++;
                        status = true;
                    }
                    bool hasAssembly = false;
                    foreach (string s in item["MetaInfo"].ToString().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
                    {
                        if (s.Contains("SolutionHasAssemblies"))
                        {
                            if (s.Contains("1"))
                            {
                                assemblySolutions++;
                                if (status)
                                {
                                    activeAssemblySolutions++;
                                }
                                hasAssembly = true;
                            }
                            break;
                        }
                    }

                    if (hasAssembly && string.IsNullOrEmpty(siteOwner))
                    {
                        // Let's add site owners for the solutions which need our attention
                        List <string>  admins = new List <string>();
                        UserCollection users  = e.WebClientContext.Web.SiteUsers;
                        e.WebClientContext.Load(users);
                        e.WebClientContext.ExecuteQueryRetry();
                        foreach (User u in users)
                        {
                            if (u.IsSiteAdmin)
                            {
                                if (!string.IsNullOrEmpty(u.Email) && u.Email.Contains("@"))
                                {
                                    admins.Add(u.Email);
                                }
                            }
                        }

                        if (this.Separator == ";")
                        {
                            siteOwner = string.Join(",", admins.ToArray());
                        }
                        else
                        {
                            siteOwner = string.Join(";", admins.ToArray());
                        }
                    }

                    SBScanResult result = new SBScanResult()
                    {
                        SiteURL       = e.Url,
                        SiteOwner     = hasAssembly ? siteOwner : "",
                        WSPName       = item["FileLeafRef"].ToString(),
                        Author        = ((FieldUserValue)item["Author"]).LookupValue.Replace(",", ""),
                        CreatedDate   = Convert.ToDateTime(item["Created"]),
                        Activated     = status,
                        HasAssemblies = hasAssembly,
                        SolutionHash  = item["SolutionHash"].ToString(),
                        SolutionID    = item["SolutionId"].ToString(),
                        SiteId        = e.WebClientContext.Site.Id.ToString(),
                    };

                    // Doing more than a simple scan...
                    if (Mode == Mode.scananddownload || Mode == Mode.scanandanalyze)
                    {
                        // Only download the solution when there's an assembly. By default we're only downloading and scanning each unique solution just once
                        if (hasAssembly && (!SBProcessed.ContainsKey(result.SolutionHash) || Duplicates == true))
                        {
                            // Add this solution hash to the dictionary
                            SBProcessed.TryAdd(result.SolutionHash, "");

                            // Download the WSP package
                            ClientResult <Stream> data = item.File.OpenBinaryStream();
                            e.WebClientContext.Load(item.File);
                            e.WebClientContext.ExecuteQueryRetry();

                            if (data != null)
                            {
                                int    position      = 1;
                                int    bufferSize    = 200000;
                                Byte[] readBuffer    = new Byte[bufferSize];
                                string localFilePath = System.IO.Path.Combine(".", this.OutputFolder, e.WebClientContext.Site.Id.ToString());
                                System.IO.Directory.CreateDirectory(localFilePath);

                                string wspPath = System.IO.Path.Combine(localFilePath, item["FileLeafRef"].ToString());
                                using (System.IO.Stream stream = System.IO.File.Create(wspPath))
                                {
                                    while (position > 0)
                                    {
                                        // data.Value holds the Stream
                                        position = data.Value.Read(readBuffer, 0, bufferSize);
                                        stream.Write(readBuffer, 0, position);
                                        readBuffer = new Byte[bufferSize];
                                    }
                                    stream.Flush();
                                }

                                // Analyze the WSP package by cracking it open and looking inside
                                if (Mode == Mode.scanandanalyze)
                                {
                                    Analyzer analyzer = new Analyzer();
                                    analyzer.Init(this.Verbose);
                                    var res = analyzer.ProcessFileInfo(System.IO.Path.GetFullPath(wspPath));

                                    result.IsEmptyAssembly     = (res.Assemblies.Count == 1 && res.Assemblies[0].ReferencedAssemblies.Count <= 1 && res.Assemblies[0].Classes.Count == 0);
                                    result.IsInfoPath          = res.InfoPathSolution;
                                    result.HasWebParts         = (res.WebPartsCount > 0) || (res.UserControlsCount > 0) || res.Features.Where(f => f.WebParts.Any()).Count() > 0;
                                    result.HasWebTemplate      = res.Features.Where(f => f.WebTemplateDetails.Any()).Count() > 0;
                                    result.HasFeatureReceivers = res.FeatureReceiversCount > 0 || res.Features.Where(f => f.FeatureReceivers.Any()).Count() > 0;
                                    result.HasEventReceivers   = res.EventHandlersCount > 0 || res.Features.Where(f => f.EventReceivers.Any()).Count() > 0;
                                    result.HasListDefinition   = res.ListTemplatesCount > 0 || res.Features.Where(f => f.ListTemplates.Any()).Count() > 0;
                                    result.HasWorkflowAction   = res.Features.Where(f => f.WorkflowActionDetails.Any()).Count() > 0;

                                    if (res.InfoPathSolution)
                                    {
                                        result.IsEmptyInfoPathAssembly = IsEmptyInfoPathAssembly(res);
                                    }

                                    // Dump the analysis results
                                    var serializer = new XmlSerializer(typeof(SolutionInformation));
                                    using (var writer = new StreamWriter(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(wspPath), (System.IO.Path.GetFileNameWithoutExtension(wspPath) + ".xml"))))
                                    {
                                        serializer.Serialize(writer, res);
                                    }

                                    // Create new package without assembly
                                    if (result.IsEmptyAssembly.Value)
                                    {
                                        string tempFolder = null;
                                        tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString().Replace("-", ""));
                                        // unpack
                                        analyzer.UnCab(System.IO.Path.GetFullPath(wspPath), tempFolder);
                                        // delete all assemblies
                                        var filesToDelete = Directory.GetFiles(tempFolder, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase));
                                        foreach (var file in filesToDelete)
                                        {
                                            System.IO.File.Delete(file);
                                        }
                                        // repack (also deletes the temp folder)
                                        analyzer.ReCab(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(wspPath), (System.IO.Path.GetFileNameWithoutExtension(wspPath) + "_fixed.wsp")), tempFolder);
                                    }
                                }
                            }
                        }
                    }

                    this.SBScanResults.Push(result);
                }

                if (totalSolutions > 0)
                {
                    Console.WriteLine("Site {0} processed. Found {1} solutions in total of which {2} have assemblies and are activated", e.Url, totalSolutions, activeAssemblySolutions);
                }
            }
            catch (Exception ex)
            {
                SBScanError error = new SBScanError()
                {
                    Error   = ex.Message,
                    SiteURL = e.Url,
                };
                this.SBScanErrors.Push(error);
                Console.WriteLine("Error for site {1}: {0}", ex.Message, e.Url);
            }
        }
예제 #18
0
 /// <summary>
 /// The abstract method which should be implemented in overriden classes to
 /// </summary>
 /// <param name="sender">The current timer job instnace</param>
 /// <param name="e">Timer job run event arguments</param>
 protected abstract void TimerJobRunImpl(object sender, TimerJobRunEventArgs e);
예제 #19
0
 public virtual void Preprocess(SiteInformation siteCollection, WebInformation web, TimerJobRunEventArgs e)
 {
 }
예제 #20
0
 void SimpleJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
 {
     e.WebClientContext.Load(e.WebClientContext.Web, p => p.Title);
     e.WebClientContext.ExecuteQueryRetry();
     Console.WriteLine("Site {0} has title {1}", e.Url, e.WebClientContext.Web.Title);
 }
예제 #21
0
        private void ReferenceScanJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            // thread safe increase of the sites counter
            IncreaseScannedSites();

            try
            {
                Console.WriteLine("Processing site {0}...", e.Url);

                #region Basic sample

                /*
                 * // Set the first site collection done flag + perform telemetry
                 * SetFirstSiteCollectionDone(e.WebClientContext);
                 *
                 * // add your custom scan logic here, ensure the catch errors as we don't want to terminate scanning
                 * e.WebClientContext.Load(e.WebClientContext.Web, p => p.Title);
                 * e.WebClientContext.ExecuteQueryRetry();
                 * ScanResult result = new ScanResult()
                 * {
                 *  SiteColUrl = e.Url,
                 *  SiteURL = e.Url,
                 *  SiteTitle = e.WebClientContext.Web.Title
                 * };
                 *
                 * // Store the scan result
                 * if (!ScanResults.TryAdd(e.Url, result))
                 * {
                 *  ScanError error = new ScanError()
                 *  {
                 *      SiteURL = e.Url,
                 *      SiteColUrl = e.Url,
                 *      Error = "Could not add scan result for this site"
                 *  };
                 *  this.ScanErrors.Push(error);
                 * }
                 */
                #endregion

                #region Search based sample
                // Set the first site collection done flag + perform telemetry
                SetFirstSiteCollectionDone(e.WebClientContext);

                // Need to use search inside this site collection?
                List <string> propertiesToRetrieve = new List <string>
                {
                    "Title",
                    "SPSiteUrl",
                    "FileExtension",
                    "OriginalPath"
                };
                var searchResults = this.Search(e.SiteClientContext.Web, $"((fileextension=htm OR fileextension=html) AND contentclass=STS_ListItem_DocumentLibrary AND Path:{e.Url.TrimEnd('/')}/*)", propertiesToRetrieve);
                foreach (var searchResult in searchResults)
                {
                    ScanResult result = new ScanResult()
                    {
                        SiteColUrl = e.Url,
                        FileName   = searchResult["OriginalPath"]
                    };

                    // Get web url
                    var webUrlData = Web.GetWebUrlFromPageUrl(e.SiteClientContext, result.FileName);
                    e.SiteClientContext.ExecuteQueryRetry();
                    result.SiteURL = webUrlData.Value;

                    // Store the scan result, use FileName as unique key in this sample
                    if (!ScanResults.TryAdd(result.FileName, result))
                    {
                        ScanError error = new ScanError()
                        {
                            SiteURL    = e.Url,
                            SiteColUrl = e.Url,
                            Error      = "Could not add scan result for this web"
                        };
                        this.ScanErrors.Push(error);
                    }
                }
                #endregion

                #region Sub site iteration sample

                /***
                 * // Set the first site collection done flag + perform telemetry
                 * SetFirstSiteCollectionDone(e.WebClientContext);
                 *
                 * // Manually iterate over the content
                 * IEnumerable<string> expandedSites = GetAllSubSites(e.SiteClientContext.Site);
                 * bool isFirstSiteInList = true;
                 * string siteCollectionUrl = "";
                 *
                 * foreach (string site in expandedSites)
                 * {
                 *  // thread safe increase of the webs counter
                 *  IncreaseScannedWebs();
                 *
                 *  // Clone the existing ClientContext for the sub web
                 *  using (ClientContext ccWeb = e.SiteClientContext.Clone(site))
                 *  {
                 *      Console.WriteLine("Processing site {0}...", site);
                 *
                 *      if (isFirstSiteInList)
                 *      {
                 *          // Perf optimization: do one call per site to load all the needed properties
                 *          var spSite = (ccWeb as ClientContext).Site;
                 *          ccWeb.Load(spSite, p => p.RootWeb, p => p.Url);
                 *          ccWeb.Load(spSite.RootWeb, p => p.Id);
                 *
                 *          isFirstSiteInList = false;
                 *      }
                 *
                 *      // Perf optimization: do one call per web to load all the needed properties
                 *      ccWeb.Load(ccWeb.Web, p => p.Id, p => p.Title);
                 *      ccWeb.Load(ccWeb.Web, p => p.WebTemplate, p => p.Configuration);
                 *      ccWeb.Load(ccWeb.Web, p => p.Lists.Include(li => li.UserCustomActions, li => li.Title, li => li.Hidden, li => li.DefaultViewUrl, li => li.BaseTemplate, li => li.RootFolder, li => li.ListExperienceOptions));
                 *      ccWeb.ExecuteQueryRetry();
                 *
                 *      // Fill site collection url
                 *      if (string.IsNullOrEmpty(siteCollectionUrl))
                 *      {
                 *          siteCollectionUrl = ccWeb.Site.Url;
                 *      }
                 *
                 *      // Need to know if this is a sub site?
                 *      if (ccWeb.Web.IsSubSite())
                 *      {
                 *          // Sub site specific logic
                 *      }
                 *
                 *      ScanResult result = new ScanResult()
                 *      {
                 *          SiteColUrl = e.Url,
                 *          SiteURL = site,
                 *          SiteTitle = ccWeb.Web.Title,
                 *      };
                 *
                 *      // Store the scan result
                 *      if (!ScanResults.TryAdd(site, result))
                 *      {
                 *          ScanError error = new ScanError()
                 *          {
                 *              SiteURL = site,
                 *              SiteColUrl = e.Url,
                 *              Error = "Could not add scan result for this web"
                 *          };
                 *          this.ScanErrors.Push(error);
                 *      }
                 *  }
                 * }
                 **/
                #endregion
            }
            catch (Exception ex)
            {
                ScanError error = new ScanError()
                {
                    Error      = ex.Message,
                    SiteColUrl = e.Url,
                    SiteURL    = e.Url,
                    Field1     = "put additional info here"
                };
                this.ScanErrors.Push(error);
                Console.WriteLine("Error for site {1}: {0}", ex.Message, e.Url);
            }

            // Output the scanning progress
            try
            {
                TimeSpan ts = DateTime.Now.Subtract(this.StartTime);
                Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId}. Processed {this.ScannedSites} of {this.SitesToScan} site collections ({Math.Round(((float)this.ScannedSites / (float)this.SitesToScan) * 100)}%). Process running for {ts.Days} days, {ts.Hours} hours, {ts.Minutes} minutes and {ts.Seconds} seconds.");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error showing progress: {ex.ToString()}");
            }
        }
        private void ValidateExternalUsersTimerJob_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            Console.WriteLine("Starting job");
            var    web    = e.SiteClientContext.Web;
            Tenant tenant = new Tenant(e.TenantClientContext);

            var siteAdmins = e.SiteClientContext.LoadQuery(web.SiteUsers.Include(u => u.Email).Where(u => u.IsSiteAdmin));

            e.SiteClientContext.ExecuteQueryRetry();

            List <string> adminEmails = new List <string>();

            foreach (var siteAdmin in siteAdmins)
            {
                adminEmails.Add(siteAdmin.Email);
            }

            SiteProperties p = tenant.GetSitePropertiesByUrl(e.SiteClientContext.Url, true);
            var            sharingCapability = p.EnsureProperty(s => s.SharingCapability);

            if (sharingCapability != Microsoft.Online.SharePoint.TenantManagement.SharingCapabilities.Disabled)
            {
                DateTime checkDate     = DateTime.Now;
                var      lastCheckDate = e.WebClientContext.Web.GetPropertyBagValueString(PNPCHECKDATEPROPERTYBAGKEY, string.Empty);
                if (lastCheckDate == string.Empty)
                {
                    // new site. Temporary set the check date to less than one Month
                    checkDate = checkDate.AddMonths(-2);
                }
                else
                {
                    if (!DateTime.TryParse(lastCheckDate, out checkDate))
                    {
                        // Something went wrong with trying to parse the date in the propertybag. Do the check anyway.
                        checkDate = checkDate.AddMonths(-2);
                    }
                }
                if (checkDate.AddMonths(1) < DateTime.Now)
                {
                    e.SiteClientContext.Web.EnsureProperty(w => w.Url);
                    e.WebClientContext.Web.EnsureProperty(w => w.Url);
                    EmailProperties mailProps = new EmailProperties();
                    mailProps.Subject = "Review required: external users with access to your site";
                    StringBuilder bodyBuilder = new StringBuilder();

                    bodyBuilder.AppendFormat("<html><head>{0}</head><body style=\"font-family:sans-serif\">", CSSSTYLE);
                    bodyBuilder.AppendFormat("<p>Your site with address {0} has one or more external users registered. Please review the following list and take appropriate action if such access is not wanted anymore for a user.</p>", e.SiteClientContext.Web.Url);
                    bodyBuilder.Append("<table class=\"tg\"><tr><th>Name</th><th>Invited by</th><th>Created</th><th>Invited As</th><th>Accepted As</th></tr>");

                    var externalusers = e.TenantClientContext.Web.GetExternalUsersForSiteTenant(new Uri(e.WebClientContext.Web.Url));
                    if (externalusers.Any())
                    {
                        foreach (var externalUser in externalusers)
                        {
                            bodyBuilder.AppendFormat("<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td></tr>", externalUser.DisplayName, externalUser.InvitedBy, externalUser.WhenCreated, externalUser.InvitedAs, externalUser.AcceptedAs);
                        }
                        bodyBuilder.Append("</table></body></html>");
                        mailProps.Body = bodyBuilder.ToString();
                        mailProps.To   = adminEmails.ToArray();

                        Utility.SendEmail(e.SiteClientContext, mailProps);
                        e.SiteClientContext.ExecuteQueryRetry();
                    }
                    e.WebClientContext.Web.SetPropertyBagValue(PNPCHECKDATEPROPERTYBAGKEY, DateTime.Now.ToString());
                }
            }

            Console.WriteLine("Ending job");
        }
예제 #23
0
        /// <summary>
        /// Update HasBroadAccess to 1 for both of the site collection and the current web record if any large security group permission assignment was found on the current web
        /// </summary>
        /// <param name="dbSiteRecord">The site collection record</param>
        /// <param name="dbWebRecord">The current web record</param>
        /// <param name="e">The timer job run event arguments</param>
        public override void Preprocess(SiteInformation dbSiteRecord, WebInformation dbWebRecord, TimerJobRunEventArgs e)
        {
            var tenant = new Tenant(e.TenantClientContext);
            var site   = tenant.GetSiteByUrl(e.Url);
            var web    = e.Url == dbWebRecord.SiteUrl
                ? site.RootWeb
                : site.OpenWeb(e.Url.Substring(dbWebRecord.Name.IndexOf('/') + 1));

            // load additional properties could be used to optimize the permission checking process
            e.TenantClientContext.Load(web,
                                       w => w.HasUniqueRoleAssignments,
                                       w => w.Url,
                                       w => w.ServerRelativeUrl,
                                       w => w.ParentWeb.ServerRelativeUrl);

            var assignments = new List <PermissionAssignment>();
            var entries     = from groupLoginName in BroadAccessGroups.Keys
                              select new PermissionAssignment
            {
                Url        = e.Url,
                Group      = groupLoginName,
                Permission = web.GetUserEffectivePermissions(groupLoginName)
            };

            assignments.AddRange(entries);
            e.TenantClientContext.ExecuteQuery();

            var incompliantAssignments = assignments.Where(
                p => p.Permission.Value != null && (
                    p.Permission.Value.Has(PermissionKind.ViewPages) ||
                    p.Permission.Value.Has(PermissionKind.ViewListItems)));

            dbWebRecord.HasBroadAccess = incompliantAssignments.Any();
            if (dbWebRecord.HasBroadAccess)
            {
                dbWebRecord.BroadAccessGroups = string.Join(";",
                                                            (from a in incompliantAssignments select a.Group).ToArray());
                dbSiteRecord.HasBroadAccess = true;
            }
        }
예제 #24
0
        /// <summary>
        /// Event handler that's being executed by the threads processing the sites. Everything in here must be coded in a thread-safe manner
        /// </summary>
        private void AccessAppScanner_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            try
            {
                IEnumerable <string> expandedSites = null;
                if (!this.UseSearchQuery)
                {
                    // Get all the sub sites in the site we're processing
                    expandedSites = GetAllSubSites(e.SiteClientContext.Site);
                }
                else
                {
                    expandedSites = new List <string>();
                    (expandedSites as List <string>).Add(e.Url);
                }

                bool   isFirstSiteInList   = true;
                string siteCollectionUrl   = "";
                int    viewsRecent         = -1;
                int    viewsRecentUnique   = -1;
                int    viewsLifetime       = -1;
                int    viewsLifetimeUnique = -1;

                lock (scannedSitesLock)
                {
                    ScannedSites++;
                }

                // Manually iterate over the content
                foreach (string site in expandedSites)
                {
                    lock (scannedWebsLock)
                    {
                        ScannedWebs++;
                    }

                    // Create a client context using a AuthenticationManager per domain (without this you'll get access denied on the app domains)
                    using (ClientContext ccWeb = this.CreateClientContext(site))
                    {
                        ClientResult <ResultTableCollection> siteQueryResults = null;

                        Console.WriteLine("Processing site {0}...", site);
                        try
                        {
                            if (!firstSiteCollectionDone)
                            {
                                firstSiteCollectionDone = true;

                                // Telemetry
                                ccWeb.ClientTag = "SPDev:AccessAppScanner";
                                ccWeb.Load(ccWeb.Web, p => p.Description, p => p.Id);
                                ccWeb.ExecuteQuery();
                            }

                            if (isFirstSiteInList)
                            {
                                // Perf optimization: do one call per site to load all the needed properties
                                var spSite = (ccWeb as ClientContext).Site;
                                ccWeb.Load(spSite, p => p.RootWeb, p => p.Url); // Needed in IsSubSite
                                ccWeb.Load(spSite.RootWeb, p => p.Id);          // Needed in IsSubSite

                                isFirstSiteInList = false;
                            }

                            // Perf optimization: do one call per web to load all the needed properties
                            ccWeb.Load(ccWeb.Web, p => p.Id, p => p.WebTemplate, p => p.Configuration, p => p.Title, p => p.Created, p => p.AppInstanceId, p => p.ParentWeb, p => p.LastItemUserModifiedDate, p => p.SiteUsers, p => p.AssociatedOwnerGroup);
                            ccWeb.Load(ccWeb.Web.AssociatedOwnerGroup, p => p.Users);
                            ccWeb.ExecuteQueryRetry();

                            // Fill site collection url
                            if (string.IsNullOrEmpty(siteCollectionUrl))
                            {
                                siteCollectionUrl = ccWeb.Site.Url;
                            }

                            // Scanning to identify sites that can't show modern pages
                            if (Modes.Contains(Mode.Scan))
                            {
                                if (ccWeb.Web.WebTemplate.Equals("ACCSVC", StringComparison.InvariantCultureIgnoreCase) ||
                                    ccWeb.Web.WebTemplate.Equals("ACCSRV", StringComparison.InvariantCultureIgnoreCase))
                                {
                                    // Get the date when this Access App was last accessed
                                    var lastAccessedDate = ccWeb.Web.GetPropertyBagValueString("accsvcLastAccessedDate", "");
                                    var lastModifiedDate = ccWeb.Web.LastItemUserModifiedDate;

                                    // Query for usage on the actual site collection, do this only once per site collection
                                    if (viewsRecent == -1)
                                    {
                                        KeywordQuery siteQuery = new KeywordQuery(e.SiteClientContext);
                                        siteQuery.QueryText = string.Format("path:{0} NOT(contentclass:STS_Site) NOT(contentclass:STS_Web)", siteCollectionUrl);
                                        siteQuery.SelectProperties.Clear();
                                        siteQuery.SelectProperties.Add("ViewsRecent");
                                        siteQuery.SelectProperties.Add("ViewsRecentUniqueUsers");
                                        siteQuery.SelectProperties.Add("ViewsLifeTime");
                                        siteQuery.SelectProperties.Add("ViewsLifeTimeUniqueUsers");
                                        siteQuery.SortList.Add("ViewsRecent", SortDirection.Descending);
                                        siteQuery.TrimDuplicates = false;
                                        siteQuery.RowLimit       = 500;

                                        SearchExecutor seachExecutor = new SearchExecutor(e.SiteClientContext);
                                        siteQueryResults = seachExecutor.ExecuteQuery(siteQuery);

                                        // Load site collection site users
                                        e.SiteClientContext.Load(e.SiteClientContext.Web, p => p.SiteUsers);

                                        e.SiteClientContext.ExecuteQueryRetry();

                                        // Fill site usage information
                                        if (siteQueryResults != null)
                                        {
                                            viewsRecent         = 0;
                                            viewsRecentUnique   = 0;
                                            viewsLifetime       = 0;
                                            viewsLifetimeUnique = 0;

                                            if (siteQueryResults.Value != null)
                                            {
                                                foreach (ResultTable t in siteQueryResults.Value)
                                                {
                                                    foreach (IDictionary <string, object> r in t.ResultRows)
                                                    {
                                                        viewsRecent += r["ViewsRecent"] != null?int.Parse(r["ViewsRecent"].ToString()) : 0;

                                                        viewsRecentUnique += r["ViewsRecentUniqueUsers"] != null?int.Parse(r["ViewsRecentUniqueUsers"].ToString()) : 0;

                                                        viewsLifetime += r["ViewsLifeTime"] != null?int.Parse(r["ViewsLifeTime"].ToString()) : 0;

                                                        viewsLifetimeUnique += r["ViewsLifeTimeUniqueUsers"] != null?int.Parse(r["ViewsLifeTimeUniqueUsers"].ToString()) : 0;
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    var accessAppResult = new AccessAppScanData()
                                    {
                                        ViewsRecent         = viewsRecent,
                                        ViewsRecentUnique   = viewsRecentUnique,
                                        ViewsLifetime       = viewsLifetime,
                                        ViewsLifetimeUnique = viewsLifetimeUnique,
                                        SiteColUrl          = siteCollectionUrl,
                                        SiteUrl             = site,
                                        WebTitle            = ccWeb.Web.Title,
                                        WebCreatedDate      = ccWeb.Web.Created,
                                        WebTemplate         = $"{ccWeb.Web.WebTemplate}#{ccWeb.Web.Configuration}",
                                        AppInstanceId       = ccWeb.Web.AppInstanceId,
                                        WebId                  = ccWeb.Web.Id,
                                        LastAccessedDate       = lastAccessedDate,
                                        LastModifiedByUserDate = (ccWeb.Web.WebTemplate.Equals("ACCSRV", StringComparison.InvariantCultureIgnoreCase) ? lastModifiedDate.ToString() : "")
                                    };

                                    Console.WriteLine($"Access App found in {site}.");

                                    // grab the site collection administrators
                                    if (e.SiteClientContext.Web.SiteUsers != null)
                                    {
                                        try
                                        {
                                            var admins = e.SiteClientContext.Web.SiteUsers.Where(p => p.IsSiteAdmin);
                                            if (admins != null && admins.Count() > 0)
                                            {
                                                foreach (var admin in admins)
                                                {
                                                    if (!string.IsNullOrEmpty(admin.Email))
                                                    {
                                                        accessAppResult.SiteAdmins = AddSiteOwner(accessAppResult.SiteAdmins, admin.Email);
                                                    }
                                                }
                                            }
                                        }
                                        catch
                                        {
                                            //Eat exceptions...rather log all Access Apps i the main result list instead of dropping some due to error getting owners
                                        }
                                    }

                                    try
                                    {
                                        // grab folks from the Access Web App site owners group
                                        if (ccWeb.Web.AssociatedOwnerGroup != null && ccWeb.Web.AssociatedOwnerGroup.Users != null && ccWeb.Web.AssociatedOwnerGroup.Users.Count > 0)
                                        {
                                            foreach (var owner in ccWeb.Web.AssociatedOwnerGroup.Users)
                                            {
                                                if (!string.IsNullOrEmpty(owner.Email))
                                                {
                                                    accessAppResult.SiteAdmins = AddSiteOwner(accessAppResult.SiteAdmins, owner.Email);
                                                }
                                            }
                                        }
                                    }
                                    catch
                                    {
                                        //Eat exceptions...rather log all Access Apps i the main result list instead of dropping some due to error getting owners
                                    }

                                    if (!string.IsNullOrEmpty(ccWeb.Web.ParentWeb.ServerRelativeUrl))
                                    {
                                        Uri    siteCollectionUri = new Uri(siteCollectionUrl);
                                        string parentWebUrl      = $"{siteCollectionUri.Scheme}://{siteCollectionUri.DnsSafeHost}:{siteCollectionUri.Port}{ccWeb.Web.ParentWeb.ServerRelativeUrl}";
                                        accessAppResult.ParentSiteUrl = parentWebUrl;

                                        using (var ccParent = this.CreateClientContext(parentWebUrl))
                                        {
                                            try
                                            {
                                                // Get users from parent web site owners group
                                                ccParent.Load(ccParent.Web, p => p.AssociatedOwnerGroup);
                                                ccParent.Load(ccParent.Web.AssociatedOwnerGroup, p => p.Users);

                                                // Check app status
                                                AppInstance appInstance = null;
                                                if (accessAppResult.AppInstanceId != Guid.Empty)
                                                {
                                                    appInstance = ccParent.Web.GetAppInstanceById(accessAppResult.AppInstanceId);
                                                    ccParent.Load(appInstance);
                                                }

                                                ccParent.ExecuteQueryRetry();

                                                if (accessAppResult.AppInstanceId != Guid.Empty)
                                                {
                                                    accessAppResult.AppInstanceStatus = appInstance.Status.ToString();
                                                }

                                                try
                                                {
                                                    // Process parent web owners
                                                    if (ccParent.Web.AssociatedOwnerGroup != null && ccParent.Web.AssociatedOwnerGroup.Users != null && ccParent.Web.AssociatedOwnerGroup.Users.Count > 0)
                                                    {
                                                        foreach (var owner in ccParent.Web.AssociatedOwnerGroup.Users)
                                                        {
                                                            if (!string.IsNullOrEmpty(owner.Email))
                                                            {
                                                                accessAppResult.SiteAdmins = AddSiteOwner(accessAppResult.SiteAdmins, owner.Email);
                                                            }
                                                        }
                                                    }
                                                }
                                                catch
                                                {
                                                    //Eat exceptions...rather log all Access Apps i the main result list instead of dropping some due to error getting owners
                                                }
                                            }
                                            catch (Exception ex)
                                            {
                                                AccessAppScanError error = new AccessAppScanError()
                                                {
                                                    Error      = ex.Message,
                                                    SiteUrl    = site,
                                                    SiteColUrl = siteCollectionUrl
                                                };
                                                this.AccessAppScanErrors.Push(error);
                                                Console.WriteLine("Error while reading Access App status for {1}: {0}", ex.Message, site);
                                            }
                                        }
                                    }
                                    AccessAppResults.Push(accessAppResult);
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            AccessAppScanError error = new AccessAppScanError()
                            {
                                Error      = ex.ToDetailedString(),
                                SiteUrl    = site,
                                SiteColUrl = siteCollectionUrl
                            };
                            this.AccessAppScanErrors.Push(error);
                            Console.WriteLine("Error for site {1}: {0}", ex.Message, site);
                        }
                    }

                    try
                    {
                        TimeSpan ts = DateTime.Now.Subtract(this.StartTime);
                        Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId}. Processed {this.ScannedSites} of {this.SitesToScan} site collections ({Math.Round(((float)this.ScannedSites / (float)this.SitesToScan) * 100)}%). Process running for {ts.Days} days, {ts.Hours} hours, {ts.Minutes} minutes and {ts.Seconds} seconds.");
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"Error showing progress: {ex.ToDetailedString()}");
                    }
                }
            }
            catch (Exception ex)
            {
                AccessAppScanError error = new AccessAppScanError()
                {
                    Error      = ex.ToDetailedString(),
                    SiteUrl    = e.SiteClientContext.Site.Url,
                    SiteColUrl = e.SiteClientContext.Site.Url
                };
                this.AccessAppScanErrors.Push(error);
                Console.WriteLine("Error for site {1}: {0}", ex.Message, e.SiteClientContext.Site.Url);
            }
        }
예제 #25
0
        /// <summary>
        /// Event handler that's being executed by the threads processing the sites. Everything in here must be coded in a thread-safe manner
        /// </summary>
        private void UIExperienceScanner_TimerJobRun(object sender, TimerJobRunEventArgs e)
        {
            // Get all the sub sites in the site we're processing
            IEnumerable <string> expandedSites = GetAllSubSites(e.SiteClientContext.Site);

            bool   isFirstSiteInList = true;
            string siteCollectionUrl = "";

            lock (scannedSitesLock)
            {
                ScannedSites++;
            }

            // Manually iterate over the content
            foreach (string site in expandedSites)
            {
                lock (scannedWebsLock)
                {
                    ScannedWebs++;
                }

                // Clone the existing ClientContext for the sub web
                using (ClientContext ccWeb = e.SiteClientContext.Clone(site))
                {
                    Console.WriteLine("Processing site {0}...", site);
                    try
                    {
                        if (!firstSiteCollectionDone)
                        {
                            firstSiteCollectionDone = true;

                            // Telemetry
                            ccWeb.ClientTag = "SPDev:UIExperienceScanner";
                            ccWeb.Load(ccWeb.Web, p => p.Description, p => p.Id);
                            ccWeb.ExecuteQuery();
                        }

                        if (isFirstSiteInList)
                        {
                            // Perf optimization: do one call per site to load all the needed properties
                            var spSite = (ccWeb as ClientContext).Site;
                            ccWeb.Load(spSite, p => p.RootWeb, p => p.Url); // Needed in IsSubSite
                            ccWeb.Load(spSite.RootWeb, p => p.Id);          // Needed in IsSubSite

                            if (Modes.Contains(Mode.Scan) || Modes.Contains(Mode.IgnoredCustomizations))
                            {
                                ccWeb.Load(spSite, p => p.UserCustomActions); // User custom action site level
                            }
                            if (Modes.Contains(Mode.Scan) || Modes.Contains(Mode.BlockedLists))
                            {
                                ccWeb.Load(spSite, p => p.Features); // Features site level
                            }

                            isFirstSiteInList = false;
                        }

                        // Perf optimization: do one call per web to load all the needed properties
                        ccWeb.Load(ccWeb.Web, p => p.Id); // Needed in IsSubSite

                        if (Modes.Contains(Mode.Scan) || Modes.Contains(Mode.IgnoredCustomizations))
                        {
                            ccWeb.Load(ccWeb.Web, p => p.MasterUrl, p => p.CustomMasterUrl, // master page check
                                       p => p.AlternateCssUrl,                              // Alternate CSS
                                       p => p.UserCustomActions);                           // Web user custom actions
                        }
                        if (Modes.Contains(Mode.Scan) || Modes.Contains(Mode.BlockedPages) || Modes.Contains(Mode.BlockedLists))
                        {
                            ccWeb.Load(ccWeb.Web, p => p.Features); // Features web level
                        }

                        // WebTemplate and Lists is needed in all three scenarios, so we always prefetch them.
                        ccWeb.Load(ccWeb.Web, p => p.WebTemplate, p => p.Configuration);
                        ccWeb.Load(ccWeb.Web, p => p.Lists.Include(li => li.UserCustomActions, li => li.Title, li => li.Hidden, li => li.DefaultViewUrl, li => li.BaseTemplate, li => li.RootFolder, li => li.ListExperienceOptions)); // List check, includes list user custom actions
                        ccWeb.ExecuteQueryRetry();

                        // Fill site collection url
                        if (string.IsNullOrEmpty(siteCollectionUrl))
                        {
                            siteCollectionUrl = ccWeb.Site.Url;
                        }

                        // Scanning to identify sites that can't show modern pages
                        if (Modes.Contains(Mode.Scan) || Modes.Contains(Mode.BlockedPages))
                        {
                            PageScanner featureScanner    = new PageScanner(site, siteCollectionUrl);
                            var         featureScanResult = featureScanner.Analyze(ccWeb);
                            if (featureScanResult != null)
                            {
                                if (!this.PageResults.TryAdd(Guid.NewGuid().ToString() + featureScanResult.Url, featureScanResult))
                                {
                                    UIExperienceScanError error = new UIExperienceScanError()
                                    {
                                        Error      = $"Could not add page scan result for {featureScanResult.Url}",
                                        SiteColUrl = siteCollectionUrl,
                                        SiteURL    = site,
                                    };
                                    this.UIExpScanErrors.Push(error);
                                    Console.WriteLine($"Could not add page scan result for {featureScanResult.Url}");
                                }
                            }
                        }

                        // Scanning to identify lists which can't be shown in modern
                        if (Modes.Contains(Mode.Scan) || Modes.Contains(Mode.BlockedLists))
                        {
                            ListScanner listConfig   = new ListScanner(site, siteCollectionUrl, this.ExcludeListsOnlyBlockedByOobReasons);
                            var         scannedLists = listConfig.Analyze(ccWeb, ref this.ListResults, ref this.UIExpScanErrors);
                            lock (scannedListsLock)
                            {
                                this.ScannedLists = this.ScannedLists + scannedLists;
                            }
                        }

                        // Scanning for customizations which will be ignored in modern pages and lists
                        if (Modes.Contains(Mode.Scan) || Modes.Contains(Mode.IgnoredCustomizations))
                        {
                            // Custom CSS scanner
                            AlternateCSSScanner cssScanner = new AlternateCSSScanner(site, siteCollectionUrl);
                            var alternateCSSResult         = cssScanner.Analyze(ccWeb);
                            if (alternateCSSResult != null)
                            {
                                this.AlternateCSSResults.Push(alternateCSSResult);
                                if (this.CustomizationResults.ContainsKey(alternateCSSResult.Url))
                                {
                                    var customizationResult = this.CustomizationResults[alternateCSSResult.Url];
                                    customizationResult.IgnoredAlternateCSS = alternateCSSResult.AlternateCSS;
                                    if (!this.CustomizationResults.TryUpdate(alternateCSSResult.Url, customizationResult, customizationResult))
                                    {
                                        UIExperienceScanError error = new UIExperienceScanError()
                                        {
                                            Error      = $"Could not update CSS scan result for {customizationResult.Url}",
                                            SiteURL    = site,
                                            SiteColUrl = siteCollectionUrl
                                        };
                                        this.UIExpScanErrors.Push(error);
                                        Console.WriteLine($"Could not update CSS scan result for {customizationResult.Url}");
                                    }
                                }
                                else
                                {
                                    var customizationResult = new CustomizationResult()
                                    {
                                        SiteUrl             = alternateCSSResult.SiteUrl,
                                        Url                 = alternateCSSResult.Url,
                                        SiteColUrl          = alternateCSSResult.SiteColUrl,
                                        IgnoredAlternateCSS = alternateCSSResult.AlternateCSS
                                    };

                                    if (!this.CustomizationResults.TryAdd(customizationResult.Url, customizationResult))
                                    {
                                        UIExperienceScanError error = new UIExperienceScanError()
                                        {
                                            Error      = $"Could not add CSS scan result for {customizationResult.Url}",
                                            SiteURL    = site,
                                            SiteColUrl = siteCollectionUrl
                                        };
                                        this.UIExpScanErrors.Push(error);
                                        Console.WriteLine($"Could not add CSS scan result for {customizationResult.Url}");
                                    }
                                }
                            }

                            // Custom master page scanner
                            MasterPageScanner masterScanner = new MasterPageScanner(site, siteCollectionUrl);
                            var masterPageResult            = masterScanner.Analyze(ccWeb);
                            if (masterPageResult != null)
                            {
                                this.MasterPageResults.Push(masterPageResult);
                                if (this.CustomizationResults.ContainsKey(masterPageResult.Url))
                                {
                                    var customizationResult = this.CustomizationResults[masterPageResult.Url];
                                    customizationResult.IgnoredMasterPage       = masterPageResult.MasterPage;
                                    customizationResult.IgnoredCustomMasterPage = masterPageResult.CustomMasterPage;
                                    if (!this.CustomizationResults.TryUpdate(masterPageResult.Url, customizationResult, customizationResult))
                                    {
                                        UIExperienceScanError error = new UIExperienceScanError()
                                        {
                                            Error      = $"Could not update MasterPage scan result for {customizationResult.Url}",
                                            SiteURL    = site,
                                            SiteColUrl = siteCollectionUrl
                                        };
                                        this.UIExpScanErrors.Push(error);
                                        Console.WriteLine($"Could not update MasterPage scan result for {customizationResult.Url}");
                                    }
                                }
                                else
                                {
                                    var customizationResult = new CustomizationResult()
                                    {
                                        SiteUrl                 = masterPageResult.SiteUrl,
                                        Url                     = masterPageResult.Url,
                                        SiteColUrl              = masterPageResult.SiteColUrl,
                                        IgnoredMasterPage       = masterPageResult.MasterPage,
                                        IgnoredCustomMasterPage = masterPageResult.CustomMasterPage
                                    };

                                    if (!this.CustomizationResults.TryAdd(customizationResult.Url, customizationResult))
                                    {
                                        UIExperienceScanError error = new UIExperienceScanError()
                                        {
                                            Error      = $"Could not add MasterPage scan result for {customizationResult.Url}",
                                            SiteURL    = site,
                                            SiteColUrl = siteCollectionUrl
                                        };
                                        this.UIExpScanErrors.Push(error);
                                        Console.WriteLine($"Could not add MasterPage scan result for {customizationResult.Url}");
                                    }
                                }
                            }

                            // Custom action scanner
                            CustomActionScanner customActions = new CustomActionScanner(site, siteCollectionUrl);
                            customActions.Analyze(ccWeb, ref this.CustomActionScanResults, ref this.CustomizationResults, ref this.UIExpScanErrors);
                        }
                    }
                    catch (Exception ex)
                    {
                        UIExperienceScanError error = new UIExperienceScanError()
                        {
                            Error      = ex.Message,
                            SiteURL    = site,
                            SiteColUrl = siteCollectionUrl
                        };
                        this.UIExpScanErrors.Push(error);
                        Console.WriteLine("Error for site {1}: {0}", ex.Message, site);
                    }
                }

                try
                {
                    TimeSpan ts = DateTime.Now.Subtract(this.StartTime);
                    Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId}. Processed {this.ScannedSites} of {this.SitesToScan} site collections ({Math.Round(((float)this.ScannedSites / (float)this.SitesToScan) * 100)}%). Process running for {ts.Days} days, {ts.Hours} hours, {ts.Minutes} minutes and {ts.Seconds} seconds.");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error showing progress: {ex.ToString()}");
                }
            }
        }