import { Injectable } from '@angular/core';
import { Router, ResolveStart } from '@angular/router';
import { ClientSideCacheService } from '../../client-side-cache/client-side-cache.service';
@Injectable()
export class PublicationService {
private readonly multilingualPublications: string[] = [];
private readonly urlSeparator: string = '/';
private siteLanguage = '';
private publicationId = '';
private publicationPath = '';
constructor(
public router: Router,
private cacheService: ClientSideCacheService
) {
// Extract publication path and publication ID from URL.
router.events.subscribe(event => {
if (event instanceof ResolveStart) {
// If urlAfterRedirects isn't the same as url, a redirect is made and we should use the urlAfterRedirects.
const pubPath = (event.urlAfterRedirects === event.url) ? this.getPublicationPathFromURL(event.url) : this.getPublicationPathFromURL(event.urlAfterRedirects);
// Fetch ID for publication only if publication changes.
if (pubPath !== this.publicationPath) {
this.publicationPath = pubPath;
this.cacheService.getValue(`${pubPath}-pubId`, `content/api/publication?path=${pubPath}`)
.toPromise().then(id => {
if (id) {
this.publicationId = id;
}
}
);
}
}
});
}
public getPublicationPath(): string {
return this.publicationPath;
}
/**
* Return publication path from URL
* @param url
*/
public getPublicationPathFromURL(url: string): string {
let pubPath = '';
if (url != null && url !== this.urlSeparator) {
// Check if it is multilingual publication
for (const multilingualPublication of this.multilingualPublications) {
if (url.startsWith(multilingualPublication)) {
return multilingualPublication;
}
}
const urlSplit = url.split(this.urlSeparator);
if (urlSplit[0] !== '') {
pubPath = this.urlSeparator + urlSplit[0];
} else {
pubPath = this.urlSeparator + urlSplit[1];
}
}
return pubPath;
}
public setLanguage(lang: string) {
this.siteLanguage = lang;
}
public getLanguage() {
return this.siteLanguage;
}
public getPublicationId() {
return this.publicationId;
}
// Return the SG that matches the url, undefined if it fails to find it
// If looking for roles placed on a stucture group ( Ex Distibutor-hub ) send in true for 'excludeVisible'
public findSG(currentSG, url: string, excludeVisible?: boolean): any {
if (!url) { return undefined; }
// Remove the index part since it is not present in the navigation json
url = this.stripIndexPartOfUrl(url);
if (url === currentSG.Url) { return currentSG; }
const parts = url.split('/');
// If first element is empty string remove it, this is needed since url starts with a '/'
if (parts[0] === '') {
parts.splice(0, 1);
}
/* This loop find the structre group that matches the url by each intetartion finding the strucutre groups that match
a longer and longer prefix.
Example: If parts is ["a", "b", "c", "d"] it first looks at /a/b and find that SG and assign it to currentSG, next iteration it
will look for the a child SG in currentSG with the url /a/b/c. When it reaches the end of the url it will return that SG that matches the
full url /a/b/c/d.
It will also filter out all structure groups that do not contain and index page
If it fails to find a matching SG on any level it will return undefined
depth starts at two since we would otherwise try and find a SG that matches just /publication_path,
that would always fail since the loop starts searchinh the nested SGs,
we should instead start by looking at /publication_path/product for example
*/
let depth = 2;
// Url is to shorts. Would not pass loop condition
if (!(depth <= parts.length)) {
return undefined;
}
for (; depth <= parts.length; depth++) {
// Construct the url for the current depth
const currentUrlParts = parts.slice(0, depth);
const currentUrl = '/' + currentUrlParts.join('/');
const nestedSgs = currentSG.Items || [];
currentSG = nestedSgs.find(sg => this.isValidSg(sg, excludeVisible) && (this.isTopLevelSg(sg) || this.hasMetadataSet(sg)) && sg.Url === currentUrl);
// If we end up with a non-valid SG break loop
if (!currentSG) {
break;
}
}
return currentSG;
}
public isValidSg(navigationElement: any, excludeVisible?: boolean): boolean {
const hasIndexPage = navigationElement.Items.find(e => e.Type === 'Page' && e.Url === navigationElement.Url);
return navigationElement.Type === 'StructureGroup' && !!hasIndexPage && (excludeVisible || navigationElement.Visible);
}
public hasMetadataSet(structureGroup): boolean {
return structureGroup.Metadata.inclPageInHierarchyCards === 'Yes';
}
public isTopLevelSg(structureGroup): boolean {
const urlParts = structureGroup.Url.split('/');
// If first element is empty string remove it, this is needed if url starts with a '/'
if (urlParts[0] === '') {
urlParts.splice(0, 1);
}
return urlParts.length <= 2;
}
public stripIndexPartOfUrl(url: string): string {
if (!url) { return ''; }
const parts: string[] = url.split('/');
if (parts[parts.length - 1] === 'index') {
parts.pop();
}
return parts.join('/');
}
public getParentUrl(url: string): string {
url = this.stripIndexPartOfUrl(url);
const urlParts = url.split('/');
const parentUrlParts = urlParts.slice(0, urlParts.length - 1);
const parentUrl = parentUrlParts.join('/');
return parentUrl;
}
public allChildren(structureGroups: any, url: string): any[] {
const Sg = this.findSG(structureGroups, url);
let children = [];
if (Sg) {
children = Sg.Items.filter(sg => this.isValidSg(sg) && this.hasMetadataSet(sg));
}
return children;
}
public allSiblings(structureGroups: any, url: string, includeSelf?: boolean): any[] {
/* Check if the passed in Sg matches the given url,
this is mainly in the case a url in the form of '/a' since
the proccess of picking out the parent part of the url would strip away the
entire url and children would not return the correct SG
*/
if (this.stripIndexPartOfUrl(url) === structureGroups.Url) {
return includeSelf ? [structureGroups] : [];
}
const parentUrl = this.getParentUrl(url);
// This will include itself
const allSiblings = this.allChildren(structureGroups, parentUrl);
// Remove the current sg (you are not your own sibling) unless flag is set
const filterCondition = sg => includeSelf || sg.Url !== url;
// If the url does not correspond to a strucutre group no siblings
return allSiblings.filter(filterCondition);
}
public allParents(structureGroups: any, url: string): any[] {
const parentUrl = this.getParentUrl(url);
const allParents = this.allSiblings(structureGroups, parentUrl, true);
// The direct parent of the url that was passed in should contain a structure group with the same url that was passed in
const directParent = allParents.find(sg => sg.Url === parentUrl) || {Items: []};
const urlExists = directParent.Items.some(sg => sg.Url === url );
return urlExists ? allParents : [];
}
public directParent(structureGroups: any, url: string): any[] {
const parentUrl = this.getParentUrl(url);
const allParents = this.allSiblings(structureGroups, parentUrl, true);
// The direct parent of the url that was passed in should contain a structure group with the same url that was passed in
const directParent = allParents.find(sg => sg.Url === parentUrl);
let parent = [];
if (directParent) {
// Check if the given URL exists in the parent SG.
const urlExists = directParent.Items.some(sg => sg.Url === url );
if (urlExists) {
parent.push(directParent);
}
}
return parent;
}
}