/// <summary>
        /// Constructor to create a BaseBinaryPathProvider
        /// </summary>
        /// <param name="engine">The SDL Web publish engine</param>
        /// <param name="package">The SDL Web publish package</param>
        public BaseBinaryPathProvider(Engine engine, Package package)
        {
            Engine  = engine;
            Package = package;

            String targetStructureGroupParam = package.GetValue("sg_PublishBinariesTargetStructureGroup");

            if (targetStructureGroupParam != null)
            {
                if (!TcmUri.IsValid(targetStructureGroupParam))
                {
                    log.Error(String.Format("TargetStructureGroup '{0}' is not a valid TCMURI. Exiting template.", targetStructureGroupParam));
                    return;
                }

                Publication publication = TridionUtils.GetPublicationFromContext(package, engine);
                TcmUri      localTargetStructureGroupTcmUri = TridionUtils.GetLocalUri(new TcmUri(publication.Id), new TcmUri(targetStructureGroupParam));
                targetStructureGroupUri = new TcmUri(localTargetStructureGroupTcmUri);
                log.Debug($"targetStructureGroupUri = {targetStructureGroupUri.ToString()}");
            }

            String stripTcmUrisFromBinaryUrlsParam = package.GetValue("stripTcmUrisFromBinaryUrls");

            if (stripTcmUrisFromBinaryUrlsParam != null)
            {
                stripTcmUrisFromBinaryUrls = stripTcmUrisFromBinaryUrlsParam.ToLower() == "yes" || stripTcmUrisFromBinaryUrlsParam.ToLower() == "true";
            }
            log.Debug($"stripTcmUrisFromBinaryUrls = {stripTcmUrisFromBinaryUrls}");
        }
예제 #2
0
        private string GetFieldValueAsString(XmlElement xmlElement)
        {
            string xlinkHref = xmlElement.GetAttribute("href", Constants.XlinkNamespace);

            if (!string.IsNullOrEmpty(xlinkHref))
            {
                if (!TcmUri.IsValid(xlinkHref))
                {
                    // External link field
                    return(xlinkHref);
                }

                IdentifiableObject linkedItem = Pipeline.Session.GetObject(xmlElement);
                Keyword            keyword    = linkedItem as Keyword;
                if (keyword == null)
                {
                    // Component link field or some other linked item (except Keyword)
                    return(xlinkHref);
                }

                // Keyword link field
                return(string.IsNullOrEmpty(keyword.Description) ? keyword.Title : keyword.Description);
            }

            if (xmlElement.SelectSingleElement("xhtml:*") != null)
            {
                // XHTML field
                RichTextData richText = BuildRichTextModel(xmlElement);
                return(string.Join("", richText.Fragments.Select(f => f.ToString())));
            }

            // Text, number or date field
            // Multi-line text field may use CR+LF to separate lines, but JSON.NET expects LF only.
            return(xmlElement.InnerText.Replace("\r\n", "\n"));
        }
예제 #3
0
        /// <summary>
        /// Returns a list of Components representing all encountered configurations that were found according to the locations in the .config file
        /// </summary>
        private List <Component> GetConfigurationComponents(RepositoryLocalObject repositoryLocalObject)
        {
            List <Component> results = new List <Component>();
            Component        configurationComponent = null;

            foreach (string location in SystemComponentLocation.Split(','))
            {
                try
                {
                    if (location == PUBLICATION) // read from Publication metadata
                    {
                        Repository publication = repositoryLocalObject.ContextRepository;
                        results.AddRange(GetConfigurationsFromMetadata(publication.Metadata, publication.MetadataSchema));
                    }
                    else if (location == CURRENT) // read from parent metadata
                    {
                        OrganizationalItem organizationalItem = repositoryLocalObject.OrganizationalItem;
                        results.AddRange(GetConfigurationsFromStructureGroup(organizationalItem, SystemComponentRecursive));
                    }
                    else if (location.StartsWith("/webdav") || TcmUri.IsValid(location))
                    {
                        configurationComponent = (Component)repositoryLocalObject.Session.GetObject(location);
                        results.Add(configurationComponent);
                    }
                }
                catch
                {
                    // Log error
                }
            }

            return(results);
        }
예제 #4
0
        public string GetSynchronized(SyncParams syncParams)
        {
            string tcmURI = syncParams.Tcm;
            string status = string.Empty;

            try
            {
                if (!string.IsNullOrEmpty(tcmURI) && TcmUri.IsValid(tcmURI))
                {
                    SynchronizeOptions syncOptions = GetSyncOptions(syncParams.Flag);
                    TcmUri             uri         = new TcmUri(tcmURI);
                    switch (uri.ItemType)
                    {
                    case ItemType.Schema:
                        status = SyncAllComponentsBasedOnSchema(tcmURI, uri.GetContextRepositoryUri(), syncOptions);
                        break;

                    case ItemType.Component:
                        SynchronizationResult result = Client.SynchronizeWithSchemaAndUpdate(tcmURI, syncOptions);
                        status = string.Format("Component Synchronization is successfull for Component: {0} ({1})", result.SynchronizedItem.Title, result.SynchronizedItem.Id);
                        break;
                    }
                    return(status);
                }
                else
                {
                    return("Component Synchronization Failed - Invalid TCM URI");
                }
            }
            catch (Exception ex)
            {
                string errorMessage = "An error occured while synchronizing components. Error Message: " + ex.Message + ex.StackTrace;
                return(errorMessage);
            }
        }
예제 #5
0
        public void AddValue(string value = null)
        {
            XmlElement newElement = fields.AddFieldElement(definition);

            if (value != null)
            {
                if (IsLinkField)
                {
                    if (TcmUri.IsValid(value))
                    {
                        TcmUri id = new TcmUri(value);
                        if (!id.IsVersionless)
                        {
                            id    = new TcmUri(id.ItemId, id.ItemType, id.PublicationId);
                            value = id.ToString();
                        }
                    }

                    XmlDocument  doc  = newElement.OwnerDocument;
                    XmlAttribute href = doc.CreateAttribute("xlink", "href", "http://www.w3.org/1999/xlink");
                    href.Value = value;
                    XmlAttribute type = doc.CreateAttribute("xlink", "type", "http://www.w3.org/1999/xlink");
                    type.Value = "simple";
                    newElement.Attributes.Append(href);
                    newElement.Attributes.Append(type);
                }
                else
                {
                    newElement.InnerText = value;
                }
            }
        }
예제 #6
0
        public static IdentifiableObject GetObjectByUriOrDefault(this Engine engine, string itemUriOrWebDavUrl)
        {
            if (!TcmUri.IsValid(itemUriOrWebDavUrl) && IsPublicationRelativeUrl(itemUriOrWebDavUrl))
            {
                var publication = engine.GetContextPublication();
                itemUriOrWebDavUrl = GetFullWebDavUrl(publication.Title, itemUriOrWebDavUrl);
            }

            return(engine.GetObject(itemUriOrWebDavUrl));
        }
예제 #7
0
 /// <summary>
 /// Retrieves a list of <see cref="T:Tridion.ContentManager.ContentManagement.Component" /> from a
 /// <see cref="T:Tridion.ContentManager.ContentManagement.OrganizationalItem" />
 /// </summary>
 /// <param name="organizationalItem"><see cref="T:Tridion.ContentManager.ContentManagement.OrganizationalItem" /></param>
 /// <param name="Schema">Schema <see cref="T:Tridion.ContentManager.TcmUri" /> for filtering.</param>
 /// <returns>List of <see cref="T:Tridion.ContentManager.ContentManagement.Component" /></returns>
 public static List <Component> Components(this OrganizationalItem organizationalItem, TcmUri schema)
 {
     if (schema != null && TcmUri.IsValid(schema))
     {
         return(organizationalItem.Components(new Schema(schema, organizationalItem.Session)));
     }
     else
     {
         return(organizationalItem.Components(null as Schema));
     }
 }
예제 #8
0
 /// <summary>
 /// Retrieves a list of <see cref="T:Tridion.ContentManager.ContentManagement.Component" /> from a
 /// <see cref="T:Tridion.ContentManager.ContentManagement.OrganizationalItem" />
 /// </summary>
 /// <param name="organizationalItem"><see cref="T:Tridion.ContentManager.ContentManagement.OrganizationalItem" /></param>
 /// <param name="schema">Schema TCM uri for filtering.</param>
 /// <returns>List of <see cref="T:Tridion.ContentManager.ContentManagement.Component" /></returns>
 public static List <Component> Components(this OrganizationalItem organizationalItem, String schema)
 {
     if (!String.IsNullOrEmpty(schema) && TcmUri.IsValid(schema))
     {
         return(organizationalItem.Components(new TcmUri(schema)));
     }
     else
     {
         return(organizationalItem.Components(null as Schema));
     }
 }
        /// <summary>
        /// Extracts binaries from the template and adds them to the package for processing.
        /// </summary>
        /// <param name="output"></param>
        /// <param name="engine"></param>
        /// <param name="package"></param>
        /// <returns></returns>
        private string ExtractBinaries(string output, Engine engine, Package package)
        {
            IExtractBinariesContentWrapper contentWrapper = new ExtractBinariesFromText(output);

            foreach (LinkReferenceWrapper linkAttribute in contentWrapper.GetLinkAttributes())
            {
                string pathAttributeValue = contentWrapper.GetAttributeValue(linkAttribute);
                // _logger.Debug("Path: " + pathAttributeValue);
                TcmUri targetItemUri = null;
                if (TcmUri.IsValid(pathAttributeValue))
                {
                    // Attribute value is TCM URI, now localize to currect context publication.
                    targetItemUri = engine.LocalizeUri(new TcmUri(pathAttributeValue));
                }

                if (targetItemUri != null || pathAttributeValue.StartsWith("/webdav/"))
                {
                    Component targetItem = engine.GetObject(targetItemUri ?? pathAttributeValue) as Component;
                    if (targetItemUri == null && targetItem != null)
                    {
                        targetItemUri = engine.LocalizeUri(targetItem.Id);
                    }

                    if ((targetItem != null) && (targetItem.ComponentType == ComponentType.Multimedia))
                    {
                        Item   binaryItem = package.CreateMultimediaItem(targetItemUri);
                        string itemName;
                        binaryItem.Properties.TryGetValue(Item.ItemPropertyFileName, out itemName);

                        Item existingItem = package.GetByName(itemName);
                        if (
                            existingItem == null ||
                            !existingItem.Properties[Item.ItemPropertyTcmUri].Equals(targetItemUri) ||
                            !existingItem.Equals(binaryItem) // Ensure that a transformed item is not considered the same
                            )
                        {
                            // _logger.Debug(string.Format("Image {0} ({1}) unique, adding to package", itemName, targetItemUri));
                            package.PushItem(itemName, binaryItem);
                        }
                        else
                        {
                            // _logger.Debug(string.Format("Image {0} ({1}) already present in package, not adding again", itemName, targetItemUri));
                        }

                        contentWrapper.ProcessLinkChange(linkAttribute, targetItemUri);
                    }
                }
            }

            return(contentWrapper.Content.ToString());
        }
예제 #10
0
        /// <summary>
        /// Obtains the requested property of the given package item
        /// </summary>
        /// <param name="item">Package item.</param>
        /// <param name="key">Property key.</param>
        /// <returns>Property Value as <see cref="T:Tridion.ContentManager.TcmUri" /></returns>
        public static TcmUri PropertyAsUri(this Item item, String key)
        {
            if (item != null)
            {
                String value = item.Property(key);

                if (!String.IsNullOrEmpty(value) && TcmUri.IsValid(value))
                {
                    return(new TcmUri(value));
                }
            }

            return(TcmUri.UriNull);
        }
예제 #11
0
        /// <summary>
        /// Gets a <see cref="T:Tridion.ContentManager.Templating.Package" /> as <see cref="T:Tridion.ContentManager.TcmUri" />
        /// </summary>
        /// <param name="package"><see cref="T:Tridion.ContentManager.Templating.Package" /></param>
        /// <param name="name">Name.</param>
        /// <returns>Value as <see cref="T:Tridion.ContentManager.TcmUri"/></returns>
        public static TcmUri ItemAsUri(this Package package, String name)
        {
            if (package != null)
            {
                String value = package.ItemAsString(name);

                if (!String.IsNullOrEmpty(value) && TcmUri.IsValid(value))
                {
                    return(new TcmUri(value));
                }
            }

            return(TcmUri.UriNull);
        }
        private string GetPublishPath(string pageId)
        {
            string result;

            if (TcmUri.IsValid(pageId) || pageId.StartsWith("/webdav/"))
            {
                Page page = (Page)Session.GetObject(pageId);
                result = page.PublishLocationUrl.Substring(1);
            }
            else
            {
                result = pageId;
            }
            return(result);
        }
예제 #13
0
        /// <summary>
        /// Extarcts the references to Tridion items from the template content. It is allowed to have values in the result that are not valid Tridion references.
        /// </summary>
        /// <returns></returns>
        public override string[] PerformExtractReferences()
        {
            TemplatingLogger log = TemplatingLogger.GetLogger(this.GetType());

            string[] dwReferences = _dwHandler.PerformExtractReferences();

            RazorHandler handler = new RazorHandler(TemplateId.ToString(), WebDavUrl, Content);

            handler.Initialize();

            List <string> imports    = handler.GetImportReferences();
            List <string> references = dwReferences.ToList();

            foreach (string path in imports)
            {
                if (!path.ToLower().StartsWith("tcm:") && !path.ToLower().StartsWith("/webdav/"))
                {
                    references.Add(GetRelativeImportPath(path));
                }
                else
                {
                    if (path.StartsWith("/webdav/"))
                    {
                        string[] pathParts   = path.Split('/');
                        string[] webDavParts = WebDavUrl.Split('/');

                        if (pathParts[2] != webDavParts[2])
                        {
                            pathParts[2] = webDavParts[2];
                        }

                        references.Add(String.Join("/", pathParts));
                    }
                    else if (TcmUri.IsValid(path))
                    {
                        TcmUri uri = new TcmUri(path);
                        if (uri.PublicationId != TemplateId.PublicationId)
                        {
                            uri = new TcmUri(uri.ItemId, uri.ItemType, TemplateId.PublicationId);
                        }
                        references.Add(uri.ToString());
                    }
                }
            }

            return(references.ToArray());
        }
        private object GetFieldValue(XmlElement xmlElement, int expandLinkDepth)
        {
            string xlinkHref = xmlElement.GetAttribute("href", Constants.XlinkNamespace);

            if (!string.IsNullOrEmpty(xlinkHref))
            {
                if (!TcmUri.IsValid(xlinkHref))
                {
                    // External link field
                    return(xlinkHref);
                }

                IdentifiableObject linkedItem = Pipeline.Session.GetObject(xmlElement);
                Logger.Debug($"Encountered XLink '{xmlElement.GetPath()}' -> {linkedItem}");

                ViewModelData embeddedViewModel = GetLinkFieldValue(linkedItem, expandLinkDepth);
                if (embeddedViewModel == null)
                {
                    // XLink is not a Component or Keyword link; return the raw URI.
                    return(xlinkHref);
                }
                return(embeddedViewModel);
            }

            if (xmlElement.SelectSingleElement("xhtml:*") != null)
            {
                // XHTML field
                return(BuildRichTextModel(xmlElement));
            }

            if (xmlElement.SelectSingleElement("*") != null)
            {
                // Embedded field
                return(BuildContentModel(xmlElement, expandLinkDepth));
            }

            // Text, number or date field
            return(xmlElement.InnerText);
        }
예제 #15
0
        /// <summary>
        /// Returns the XML for the requested transaction and removes any state files associated with it.
        /// </summary>
        /// <param name="response"><see cref="T:System.Web.HttpResponse"/></param>
        /// <param name="transaction">Transaction TcmUri</param>
        private void HandleTransactionRequest(HttpResponse response, String transaction)
        {
            Logger.Info("FileTransaction - Requested '{0}'.", transaction);

            if (TcmUri.IsValid(transaction))
            {
                String mappedFile = Path.Combine(mIncomingFolder, transaction + ".xml");

                if (File.Exists(mappedFile))
                {
                    response.ContentType = "text/xml";
                    response.WriteFile(mappedFile);

                    try
                    {
                        String stateFile = Path.Combine(mIncomingFolder, transaction + ".state.xml");
                        Logger.Debug("Removing transaction state xml '{0}'.", stateFile);

                        if (File.Exists(stateFile))
                        {
                            File.Delete(stateFile);
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Error("FileTransaction - Removing '{0}'.", ex, ex.Message);
                    }

                    return;
                }

                Logger.Warning("FileTransaction - Not found transaction file '{0}'.", mappedFile);
            }

            Logger.Warning("FileTransaction - Invalid transaction '{0}'.", transaction);

            response.StatusCode = (int)HttpStatusCode.NoContent;
            response.Write("No Content");
        }
        public BinaryPublisher(Package package, Engine engine)
        {
            this.package = package;
            this.engine  = engine;

            currentTemplate = engine.PublishingContext.ResolvedItem.Template;

            // Determine (optional) structure group parameter
            String targetStructureGroupParam = package.GetValue("PublishBinariesTargetStructureGroup");

            if (targetStructureGroupParam != null)
            {
                if (!TcmUri.IsValid(targetStructureGroupParam))
                {
                    log.Error(String.Format("TargetStructureGroup '{0}' is not a valid TCMURI. Exiting template.", targetStructureGroupParam));
                    return;
                }

                Publication publication = TridionUtils.GetPublicationFromContext(package, engine);
                TcmUri      localTargetStructureGroupTcmUri = TridionUtils.GetLocalUri(new TcmUri(publication.Id), new TcmUri(targetStructureGroupParam));
                targetStructureGroup = new TcmUri(localTargetStructureGroupTcmUri);
            }
        }
예제 #17
0
        public void Transform_PageIncludes_Success()
        {
            Component    inputItem = (Component)TestSession.GetObject(CoreComponentWebDavUrl);
            RenderedItem testRenderedItem;
            Package      testPackage = RunTemplate(typeof(PublishMappings), inputItem, out testRenderedItem);

            Item includesItem = testPackage.GetByName("/Preview/system/mappings/includes.json");

            Assert.IsNotNull(includesItem, "includesItem");
            string includesJson = includesItem.GetAsString();

            Console.WriteLine(includesJson);

            Dictionary <string, string[]> includes = JsonConvert.DeserializeObject <Dictionary <string, string[]> >(includesJson);

            PageTemplate testPageTemplate = (PageTemplate)TestSession.GetObject(ContentPageTemplateWebDavUrl);
            ItemFields   ptMetadataFields = new ItemFields(testPageTemplate.Metadata, testPageTemplate.MetadataSchema);
            TextField    includesField    = (TextField)ptMetadataFields["includes"];
            string       testKey          = testPageTemplate.Id.ItemId.ToString();

            string[] includePaths;
            Assert.IsTrue(includes.TryGetValue(testKey, out includePaths), $"includes.TryGetValue['{testKey}']");
            Assert.AreEqual(includesField.Values.Count, includePaths.Length, "includePaths.Length");
            int i = 0;

            foreach (string include in includesField.Values)
            {
                string expectedIncludePath = include;
                if (TcmUri.IsValid(include) || include.StartsWith("/webdav/"))
                {
                    Page includePage = (Page)TestSession.GetObject(include);
                    expectedIncludePath = includePage.PublishLocationUrl.Substring(1);
                }
                Assert.AreEqual(expectedIncludePath, includePaths[i], $"includePaths[{i}]");
                i++;
            }
        }
예제 #18
0
        private static void Main(string[] args)
        {
            // Default execution options
            Options options = new Options()
            {
                Mode                 = EngineMode.Debugger,
                ItemURI              = null,
                TemplateURI          = null,
                PublicationTargetURI = null
            };

            InitializeConsole();

            if (Parser.Default.ParseArguments(args, options))
            {
                LegacyInterface legacyInterface = null;

                try
                {
                    using (new PreviewServer())
                    {
                        // Initialize TcmDebugger.COM
                        legacyInterface = new LegacyInterface();
                        legacyInterface.SetProvider(new LegacyProvider());

                        switch (options.Mode)
                        {
                        case EngineMode.LocalServer:
                            using (new CompoundTemplateService2011())
                            {
                                Logger.Log(System.Diagnostics.TraceEventType.Information, "Press any key to exit.");
                                Console.ReadKey();
                            }
                            break;

                        case EngineMode.Debugger:
                        case EngineMode.Publisher:
                            if (String.IsNullOrEmpty(options.ItemURI))
                            {
                                throw new Exception("ItemUri is required for Debugger or Publisher engines");
                            }

                            if (!TcmUri.IsValid(options.ItemURI))
                            {
                                throw new Exception(String.Format("Invalid tcm uri {0}", options.ItemURI));
                            }

                            TcmUri tcmItemUri = new TcmUri(options.ItemURI);

                            if (tcmItemUri.ItemType != ItemType.Page || options.Mode != EngineMode.Publisher)
                            {
                                if (String.IsNullOrEmpty(options.TemplateURI))
                                {
                                    throw new Exception("Template tcm uri is required for non-page type objects or debug engine");
                                }

                                if (!TcmUri.IsValid(options.TemplateURI))
                                {
                                    throw new Exception(String.Format("Invalid template tcm uri {0}", options.TemplateURI));
                                }
                            }

                            if (!String.IsNullOrEmpty(options.PublicationTargetURI) && !TcmUri.IsValid(options.PublicationTargetURI))
                            {
                                throw new Exception(String.Format("Invalid publication target tcm uri {0}", options.TemplateURI));
                            }

                            Engine engine = null;

                            switch (options.Mode)
                            {
                            case EngineMode.Debugger:
                                engine = new Engines.DebugEngine();
                                break;

                            case EngineMode.Publisher:
                                engine = new RenderEngine();
                                break;
                            }

                            String output = engine.Execute(options.ItemURI, options.TemplateURI, options.PublicationTargetURI);
                            output += "";

                            Console.WriteLine();
                            Console.WriteLine("{0} [!] {1}", DateTime.Now.ToString("HH:mm:ss"), "Execution finished. Press any key to exit.");
                            Console.ReadKey();
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.Log(System.Diagnostics.TraceEventType.Error, ex.Message);
                }
                finally
                {
                    if (legacyInterface != null)
                    {
                        Marshal.ReleaseComObject(legacyInterface);
                    }
                }
            }
        }
예제 #19
0
        /// <summary>
        /// Handles "fileName=" requests from the cd_transport service
        /// </summary>
        /// <param name="response"><see cref="T:System.Web.HttpResponse"/></param>
        /// <param name="fileName">fileName</param>
        /// <param name="action">action to take, either empty or "remove"</param>
        private void HandleFileRequest(HttpResponse response, String fileName, String action)
        {
            String mappedFile = TcmUri.IsValid(fileName) ? Path.Combine(mIncomingFolder, fileName + ".state.xml") : Path.Combine(mIncomingFolder, fileName);

            Logger.Info("FileRequest - fileName '{0}', action '{1}'.", fileName, action);

            if (File.Exists(mappedFile))
            {
                if (String.Equals(action, "remove", StringComparison.OrdinalIgnoreCase))
                {
                    try
                    {
                        Logger.Info("FileRequest - removing file '{0}'.", fileName);
                        File.Delete(mappedFile);
                    }
                    catch (Exception ex)
                    {
                        Logger.Error("FileRequest - removing file", ex, ex.Message);
                    }

                    response.Write("File removed");

                    return;
                }
                else
                {
                    if (String.Equals(Path.GetExtension(mappedFile), ".xml", StringComparison.OrdinalIgnoreCase))
                    {
                        response.ContentType = "text/xml";

                        if (String.Equals(fileName, META_XML, StringComparison.OrdinalIgnoreCase))
                        {
                            CleanTransactions.Execute();

                            Logger.Debug("FileRequest - meta.xml requested");

                            String metaXml = HttpContext.Current.Cache[META_XML] as String;

                            if (String.IsNullOrEmpty(metaXml))
                            {
                                metaXml = File.ReadAllText(mappedFile, Encoding.UTF8);
                                HttpContext.Current.Cache.Insert(META_XML, metaXml, new CacheDependency(mappedFile));
                            }
                            else
                            {
                                Logger.Info("FileRequest - Sending cached meta.xml");
                            }

                            response.Write(metaXml);
                            return;
                        }
                    }

                    Logger.Info("FileRequest - sending file {0}", fileName);

                    response.WriteFile(mappedFile);
                    return;
                }
            }

            Logger.Warning("FileRequest - Not found or invalid '{0}'.", mappedFile);

            response.StatusCode = (int)HttpStatusCode.NoContent;
            response.Write("No Content");
        }
예제 #20
0
        private void AddIncludePageRegions(IDictionary <string, RegionModelData> regionModels, PageTemplate pageTemplate)
        {
            IEnumerable <string> includes = pageTemplate.Metadata.GetTextFieldValues("includes"); // TODO: use external link field (?)

            if (includes == null)
            {
                Logger.Debug($"No include Pages found in {pageTemplate.FormatIdentifier()}");
                return;
            }

            Publication contextPub = (Publication)pageTemplate.ContextRepository;

            foreach (string include in includes)
            {
                string includePageId;
                if (TcmUri.IsValid(include) || include.StartsWith("/webdav/"))
                {
                    // TCM URI or WebDAV URL
                    includePageId = include;
                }
                else
                {
                    // Legacy include: publish path. Convert to WebDAV URL.
                    includePageId = ConvertPublishPathToWebDavUrl(include, contextPub);
                }

                Page includePage;
                try
                {
                    includePage = (Page)Pipeline.Session.GetObject(includePageId);
                    includePage.Load(LoadFlags.None); // Force load the Page
                }
                catch (Exception ex)
                {
                    throw new DxaException($"Unable to load include page for '{include}'", ex);
                }

                string moduleName;
                string regionViewName = StripModuleName(includePage.Title, out moduleName);
                string regionName     = regionViewName;

                RegionModelData includePageRegion;
                if (regionModels.TryGetValue(regionName, out includePageRegion))
                {
                    Logger.Debug($"Promoting Region '{regionName}' to Include Page Region.");
                }
                else
                {
                    includePageRegion = new RegionModelData
                    {
                        Name    = regionName,
                        MvcData = new MvcData
                        {
                            ViewName = regionViewName,
                            AreaName = moduleName
                        }
                    };
                    regionModels.Add(regionName, includePageRegion);
                }
                includePageRegion.IncludePageId = GetDxaIdentifier(includePage);

                if (Pipeline.Settings.GenerateXpmMetadata)
                {
                    includePageRegion.XpmMetadata = new Dictionary <string, object>
                    {
                        { "IncludedFromPageID", GetTcmIdentifier(includePage) },
                        { "IncludedFromPageTitle", includePage.Title },
                        { "IncludedFromPageFileName", includePage.FileName }
                    };
                }
            }
        }