private static bool ShouldCreateSynchronizationTask(BaseInfo classObj) { // Ignore any Url Slugs that should be ignored. // Only Url Slugs that are either made customized, the custom Url was modified, was Un-customized, or if this task was generated from the Staging Module in which case every Url Slug will be checked. // These staging tasks are manually handled since non-customized Url Slugs are dynamically generated based on the url Pattern of the page type. UrlSlugInfo UrlSlug = (UrlSlugInfo)classObj; if (UrlSlug.UrlSlugID <= 0) { return(false); } return(!DynamicRouteInternalHelper.ShouldIgnoreStagingTaskOfUrlSlug(UrlSlug.UrlSlugID)); }
/// <summary> /// Gets the Page's Url Slug based on the given NodeID and Culture. If Culture not found, then will prioritize the Site's Default Culture, then Cultures by alphabetical order. /// </summary> /// <param name="NodeID">The NodeID</param> /// <param name="DocumentCulture">The Document Culture, if not provided will use default Site's Culture.</param> /// <param name="SiteName">The Site Name, if not provided then will query the NodeID to find it's site.</param> /// <returns>The UrlSlug (with ~ prepended) or Null if page not found.</returns> public static string GetPageUrl(int NodeID, string DocumentCulture = null, string SiteName = null) { return(DynamicRouteInternalHelper.GetPageUrl(NodeID, DocumentCulture, SiteName)); }
/// <summary> /// Gets the CMS Page using Dynamic Routing, returning the culture variation that either matches the given culture or the Slug's culture, or the default site culture if not found. /// </summary> /// <param name="Url">The Url (part after the domain), if empty will use the Current Request</param> /// <param name="Culture">The Culture, not needed if the Url contains the culture that the UrlSlug has as part of it's generation.</param> /// <param name="SiteName">The Site Name, defaults to current site.</param> /// <param name="Columns">List of columns you wish to include in the data returned.</param> /// <returns>The Page that matches the Url Slug, for the given or matching culture (or default culture if one isn't found).</returns> public static ITreeNode GetPage(string Url = "", string Culture = "", string SiteName = "", IEnumerable <string> Columns = null) { // Load defaults SiteName = (!string.IsNullOrWhiteSpace(SiteName) ? SiteName : DynamicRouteInternalHelper.SiteContextSafe().SiteName); string DefaultCulture = DynamicRouteInternalHelper.SiteContextSafe().DefaultVisitorCulture; if (string.IsNullOrWhiteSpace(Url)) { Url = EnvironmentHelper.GetUrl(HttpContext.Current.Request.Url.AbsolutePath, HttpContext.Current.Request.ApplicationPath, SiteName); } // Handle Preview, during Route Config the Preview isn't available and isn't really needed, so ignore the thrown exception bool PreviewEnabled = false; try { PreviewEnabled = HttpContext.Current.Kentico().Preview().Enabled; } catch (InvalidOperationException ex) { } GetCultureEventArgs CultureArgs = new GetCultureEventArgs() { DefaultCulture = DefaultCulture, SiteName = SiteName, Request = HttpContext.Current.Request, PreviewEnabled = PreviewEnabled, Culture = Culture }; using (var DynamicRoutingGetCultureTaskHandler = DynamicRoutingEvents.GetCulture.StartEvent(CultureArgs)) { // If Preview is enabled, use the Kentico Preview CultureName if (PreviewEnabled && string.IsNullOrWhiteSpace(CultureArgs.Culture)) { try { CultureArgs.Culture = HttpContext.Current.Kentico().Preview().CultureName; } catch (Exception) { } } // If culture still not set, use the LocalizationContext.CurrentCulture if (string.IsNullOrWhiteSpace(CultureArgs.Culture)) { try { CultureArgs.Culture = LocalizationContext.CurrentCulture.CultureName; } catch (Exception) { } } // If that fails then use the System.Globalization.CultureInfo if (string.IsNullOrWhiteSpace(CultureArgs.Culture)) { try { CultureArgs.Culture = System.Globalization.CultureInfo.CurrentCulture.Name; } catch (Exception) { } } DynamicRoutingGetCultureTaskHandler.FinishEvent(); } // set the culture Culture = CultureArgs.Culture; // Convert Columns to string ColumnsVal = Columns != null?string.Join(",", Columns.Distinct()) : "*"; // Create GetPageEventArgs Event ARgs GetPageEventArgs Args = new GetPageEventArgs() { RelativeUrl = Url, Culture = Culture, DefaultCulture = DefaultCulture, SiteName = SiteName, PreviewEnabled = PreviewEnabled, ColumnsVal = ColumnsVal, Request = HttpContext.Current.Request }; // Run any GetPage Event hooks which allow the users to set the Found Page ITreeNode FoundPage = null; using (var DynamicRoutingGetPageTaskHandler = DynamicRoutingEvents.GetPage.StartEvent(Args)) { if (Args.FoundPage == null) { try { Args.FoundPage = CacheHelper.Cache <TreeNode>(cs => { // Using custom query as Kentico's API was not properly handling a Join and where. DataTable NodeTable = ConnectionHelper.ExecuteQuery("DynamicRouting.UrlSlug.GetDocumentsByUrlSlug", new QueryDataParameters() { { "@Url", Url }, { "@Culture", Culture }, { "@DefaultCulture", DefaultCulture }, { "@SiteName", SiteName } }, topN: 1, columns: "DocumentID, ClassName").Tables[0]; if (NodeTable.Rows.Count > 0) { int DocumentID = ValidationHelper.GetInteger(NodeTable.Rows[0]["DocumentID"], 0); string ClassName = ValidationHelper.GetString(NodeTable.Rows[0]["ClassName"], ""); DocumentQuery Query = DocumentHelper.GetDocuments(ClassName) .WhereEquals("DocumentID", DocumentID) .CombineWithAnyCulture(); // Handle Columns if (!string.IsNullOrWhiteSpace(ColumnsVal)) { Query.Columns(ColumnsVal); } // Handle Preview if (PreviewEnabled) { Query.LatestVersion(true) .Published(false); } else { Query.PublishedVersion(true); } TreeNode Page = Query.FirstOrDefault(); // Cache dependencies on the Url Slugs and also the DocumentID if available. if (cs.Cached) { if (Page != null) { cs.CacheDependency = CacheHelper.GetCacheDependency(new string[] { "dynamicrouting.urlslug|all", "documentid|" + Page.DocumentID, }); } else { cs.CacheDependency = CacheHelper.GetCacheDependency(new string[] { "dynamicrouting.urlslug|all" }); } } // Return Page Data return(Query.FirstOrDefault()); } else { return(null); } }, new CacheSettings((PreviewEnabled ? 0 : 1440), "DynamicRoutine.GetPage", Url, Culture, DefaultCulture, SiteName, PreviewEnabled, ColumnsVal)); } catch (Exception ex) { // Add exception so they can handle DynamicRoutingGetPageTaskHandler.EventArguments.ExceptionOnLookup = ex; } } // Finish event, this will trigger the After DynamicRoutingGetPageTaskHandler.FinishEvent(); // Return whatever Found Page FoundPage = DynamicRoutingGetPageTaskHandler.EventArguments.FoundPage; } return(FoundPage); }
/// <summary> /// Gets the Page's Url Slug based on the given NodeGuid and Culture. If Culture not found, then will prioritize the Site's Default Culture, then Cultures by alphabetical order. /// </summary> /// <param name="NodeGuid">The Node to find the Url Slug</param> /// <param name="DocumentCulture">The Document Culture, if not provided will use default Site's Culture.</param> /// <returns>The UrlSlug (with ~ prepended) or Null if page not found.</returns> public static string GetPageUrl(Guid NodeGuid, string DocumentCulture = null) { return(DynamicRouteInternalHelper.GetPageUrl(NodeGuid, DocumentCulture)); }
/// <summary> /// Gets the Page's Url Slug based on the given DocumentGuid and it's Culture. /// </summary> /// <param name="DocumentGuid">The Document Guid</param> /// <returns>The UrlSlug (with ~ prepended) or Null if page not found.</returns> public static string GetPageUrl(Guid DocumentGuid) { return(DynamicRouteInternalHelper.GetPageUrl(DocumentGuid)); }
/// <summary> /// Gets the Page's Url Slug based on the given DocumentID and it's Culture. /// </summary> /// <param name="DocumentID">The Document ID</param> /// <returns></returns> public static string GetPageUrl(int DocumentID) { return(DynamicRouteInternalHelper.GetPageUrl(DocumentID)); }
/// <summary> /// Saves any inserts, updates or deletes to the database /// </summary> /// <param name="SaveChildren">If the children should be saved as well.</param> public void SaveChanges(bool SaveChildren = true) { bool ShouldSaveChildren = SaveChildren || SavedAlready; // Add catch for uniqueness across url and site, no duplicates, how to handle? revert to node alias path with or without document culture? if (!SavedAlready) { // Check itself for changes and save, then children foreach (NodeUrlSlug UrlSlug in UrlSlugs) { // Handle Deletes if (UrlSlug.Delete) { UrlSlugInfoProvider.DeleteUrlSlugInfo(UrlSlug.ExistingNodeSlugGuid); continue; } if (UrlSlug.IsNewOrUpdated) { // Check for existing Url Slug that matches the new Url var MatchingUrlSlug = UrlSlugInfoProvider.GetUrlSlugs() .WhereEquals("UrlSlug", UrlSlug.UrlSlug) .WhereNotEquals("UrlSlugNodeID", NodeID) .Where($"UrlSlugNodeID in (Select NodeID from CMS_Tree where NodeSiteID = {UrlSlug.SiteID})") .FirstOrDefault(); if (MatchingUrlSlug != null) { if (DynamicRouteInternalHelper.AppendPostFixOnConflict()) { bool ExistingFound = true; int AppendCount = 0; // Loop till no match found while (ExistingFound) { string PreviousAppendex = $"-({AppendCount})"; AppendCount++; string NewAppendex = $"-({AppendCount})"; if (UrlSlug.UrlSlug.Contains(PreviousAppendex)) { UrlSlug.UrlSlug = UrlSlug.UrlSlug.Replace(PreviousAppendex, NewAppendex); } else { UrlSlug.UrlSlug += NewAppendex; } ExistingFound = UrlSlugInfoProvider.GetUrlSlugs() .WhereEquals("UrlSlug", UrlSlug.UrlSlug) .WhereNotEquals("UrlSlugNodeID", NodeID) .Where($"UrlSlugNodeID in (Select NodeID from CMS_Tree where NodeSiteID = {UrlSlug.SiteID})") .FirstOrDefault() != null; } } else { // Technically should never hit this as the action should have either been cancelled or logged with an error throw new Exception("Conflict Found on Save and Url Slug Conflict Behavior Setting is not 'Append Post Fix' so could not complete action."); } } if (ValidationHelper.GetGuid(UrlSlug.ExistingNodeSlugGuid, Guid.Empty) != Guid.Empty) { // Now Update the Url Slug if it's not custom var ExistingSlug = UrlSlugInfoProvider.GetUrlSlugInfo(UrlSlug.ExistingNodeSlugGuid); if (!ExistingSlug.UrlSlugIsCustom) { ExistingSlug.UrlSlug = UrlSlug.UrlSlug; UrlSlugInfoProvider.SetUrlSlugInfo(ExistingSlug); } } else { var newSlug = new UrlSlugInfo() { UrlSlug = UrlSlug.UrlSlug, UrlSlugNodeID = NodeID, UrlSlugCultureCode = UrlSlug.CultureCode, UrlSlugIsCustom = false }; UrlSlugInfoProvider.SetUrlSlugInfo(newSlug); } } } // Mark this as saved already SavedAlready = true; } // Now Call save children on children if they are built. if (ShouldSaveChildren) { foreach (NodeItem Child in Children) { Child.SaveChanges(); } } }
/// <summary> /// Generates the Url slugs for itself, first pulling any Custom Url Slugs, then rendering all culture codes outlined in the settings /// <paramref name="UseCurrentVersion">If true, then will look to the current version of the document, used only in Checking for conflicts to block publishing</paramref> /// </summary> public void BuildUrlSlugs(bool UseCurrentVersion = false) { // Temp replace Method version with Normal version string Pattern = Regex.Replace(DynamicRouteInternalHelper.GetClass(ClassName).ClassURLPattern, "ParentUrl\\(\\)", "ParentUrl", RegexOptions.IgnoreCase); // if no pattern, then default to node alias path, this way any child with a ParentUrl will still have a value. if (string.IsNullOrWhiteSpace(Pattern)) { Pattern = "{% NodeAliasPath %}"; } var SlugQuery = UrlSlugInfoProvider.GetUrlSlugs() .Source(x => x.Join(new QuerySourceTable("CMS_Tree", null, new string[] { }), new WhereCondition("NodeID = UrlSlugNodeID"), JoinTypeEnum.Inner)) .Columns(new string[] { "UrlSlugID", "UrlSlugGuid", "UrlSlugLastModified", "UrlSlug", "UrlSlugNodeID", "UrlSlugCultureCode", "UrlSlugIsCustom", "NodeSiteID" }) .WhereEquals("UrlSlugNodeID", NodeID); // If not checking for updates (rebuild), then the only ones we want to keep are the Custom Url Slugs. if (!Settings.CheckingForUpdates) { SlugQuery.WhereEquals("UrlSlugIsCustom", true); } // Import the existing Slugs (Custom if not checking for imports, or all of them) foreach (UrlSlugInfo ExistingSlug in SlugQuery) { UrlSlugs.Add(new NodeUrlSlug() { SiteID = ExistingSlug.GetIntegerValue("NodeSiteID", 1), IsCustom = ExistingSlug.UrlSlugIsCustom, IsDefault = ExistingSlug.UrlSlugCultureCode.Equals(Settings.DefaultCultureCode, StringComparison.InvariantCultureIgnoreCase), CultureCode = ExistingSlug.UrlSlugCultureCode, UrlSlug = ExistingSlug.UrlSlug, ExistingNodeSlugGuid = ExistingSlug.UrlSlugGuid }); } // Go through any cultures that do not have custom slugs already, these are the cultures that need to be rebuilt foreach (string CultureCode in Settings.CultureCodes.Where(culture => !UrlSlugs.Exists(slug => slug.IsCustom && slug.CultureCode.Equals(culture, StringComparison.InvariantCultureIgnoreCase)))) { var CultureResolver = Settings.BaseResolver.CreateChild(); CultureResolver.SetAnonymousSourceData(new object[] { DynamicRouteInternalHelper.GetCulture(CultureCode) }); bool IsDefaultCulture = CultureCode.Equals(Settings.DefaultCultureCode, StringComparison.InvariantCultureIgnoreCase); // Get actual Document, if it's the default culture, it MUST return some document, no matter what. var DocQuery = DocumentHelper.GetDocuments(ClassName) .WhereEquals("NodeID", NodeID) .Culture(CultureCode) .CombineWithDefaultCulture(IsDefaultCulture || Settings.GenerateIfCultureDoesntExist); if (UseCurrentVersion) { DocQuery.LatestVersion(true).Published(false); } else { DocQuery.PublishedVersion(true); } TreeNode Document = DocQuery.FirstOrDefault(); // If the Document is either Null because there is no Default Culture, then get any document // and set the culture code if the current Culture checking is either the default culture (required) // or is another culture but should be generated due to the GenerateIfCultureDoesntExist setting if (Document == null && (IsDefaultCulture || Settings.GenerateIfCultureDoesntExist)) { var AnyCultureDocQuery = DocumentHelper.GetDocuments(ClassName) .WhereEquals("NodeID", NodeID) .CombineWithAnyCulture(); if (UseCurrentVersion) { AnyCultureDocQuery.LatestVersion(true).Published(false); } else { AnyCultureDocQuery.PublishedVersion(true); } Document = AnyCultureDocQuery.FirstOrDefault(); Document.DocumentCulture = CultureCode; } if (Document != null) { // Add Document values and ParentUrl var DocResolver = CultureResolver.CreateChild(); DocResolver.SetAnonymousSourceData(new object[] { Document }); if (Parent == null) { // Look up parent Url Slug UrlSlugInfo ParentSlug = UrlSlugInfoProvider.GetUrlSlugs() .WhereEquals("UrlSlugNodeID", Document.NodeParentID) .OrderBy($"case when UrlSlugCultureCode = '{CultureCode}' then 0 else 1 end, case when UrlSlugCultureCode = '{Settings.DefaultCultureCode}' then 0 else 1 end, UrlSlugCultureCode") .FirstOrDefault(); if (ParentSlug != null) { DocResolver.SetNamedSourceData("ParentUrl", ParentSlug.UrlSlug); } else { DocResolver.SetNamedSourceData("ParentUrl", ""); } } else { DocResolver.SetNamedSourceData("ParentUrl", Parent.GetUrlSlug(CultureCode)); } var NodeSlug = new NodeUrlSlug() { SiteID = Document.NodeSiteID, CultureCode = CultureCode, IsCustom = false, IsDefault = IsDefaultCulture, UrlSlug = DynamicRouteInternalHelper.GetCleanUrl(DocResolver.ResolveMacros(Pattern), Settings.SiteName), }; // If checking for updates, need to flag that an update was found if (Settings.CheckingForUpdates) { var ExistingSlug = UrlSlugs.Where(x => x.CultureCode.Equals(CultureCode, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); if (ExistingSlug != null) { // Update the existing UrlSlug only if it is different and not custom if (!ExistingSlug.UrlSlug.Equals(NodeSlug.UrlSlug)) { ExistingSlug.IsNewOrUpdated = true; ExistingSlug.PreviousUrlSlug = ExistingSlug.UrlSlug; ExistingSlug.UrlSlug = NodeSlug.UrlSlug; } } else { // No Slug exists for this culture, add. NodeSlug.IsNewOrUpdated = true; UrlSlugs.Add(NodeSlug); } } else { // Not checking for updates to just adding node slug. NodeSlug.IsNewOrUpdated = true; UrlSlugs.Add(NodeSlug); } } else if (!Settings.GenerateIfCultureDoesntExist && !IsDefaultCulture) { // If document no longer exists but a Url slug exists, set this slug to be deleted. var SlugToDelete = UrlSlugs.Where(x => x.ExistingNodeSlugGuid != null && x.CultureCode.Equals(CultureCode, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); if (SlugToDelete != null) { SlugToDelete.Delete = true; } } } }