Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to use nested fields from relations #75

Merged
merged 16 commits into from
Apr 24, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions server/services/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ const getLanguageLinks = async (page, contentType, defaultURL, excludeDrafts) =>
const getSitemapPageData = async (page, contentType, excludeDrafts) => {
let locale = page.locale || 'und';
const config = await getService('settings').getConfig();

// Return when there is no pattern for the page.
if (
!config.contentTypes[contentType]['languages'][locale]
Expand Down Expand Up @@ -131,6 +130,16 @@ const createSitemapEntries = async () => {
// Collection entries.
await Promise.all(Object.keys(config.contentTypes).map(async (contentType) => {
const excludeDrafts = config.excludeDrafts && strapi.contentTypes[contentType].options.draftAndPublish;

const populate = ['localizations'].concat(Object.keys(strapi.contentTypes[contentType].attributes).reduce((prev, current) => {


if (strapi.contentTypes[contentType].attributes[current].type === 'relation') {
prev.push(current);
}
return prev;
}, []));

const pages = await noLimit(strapi.query(contentType), {
where: {
$or: [
Expand All @@ -149,17 +158,16 @@ const createSitemapEntries = async () => {
$notNull: excludeDrafts,
},
},
populate,
orderBy: 'id',
populate: ['localizations'],
});

// Add formatted sitemap page data to the array.
await Promise.all(pages.map(async (page) => {

const pageData = await getSitemapPageData(page, contentType, excludeDrafts);
if (pageData) sitemapEntries.push(pageData);
}));
}));

// Custom entries.
await Promise.all(Object.keys(config.customEntries).map(async (customEntry) => {
sitemapEntries.push({
Expand Down Expand Up @@ -226,6 +234,7 @@ const createSitemap = async () => {
});

const sitemapEntries = await createSitemapEntries();

if (isEmpty(sitemapEntries)) {
strapi.log.info(logMessage(`No sitemap XML was generated because there were 0 URLs configured.`));
return;
Expand Down
85 changes: 69 additions & 16 deletions server/services/pattern.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ const getAllowedFields = async (contentType) => {
if (field.type === fieldType) {
fields.push(fieldName);
}
if (field.type === 'relation' && field.target) {
const relation = strapi.contentTypes[field.target];
Object.entries(relation.attributes).map(([subFieldName, subField]) => {
if (subField.type === fieldType) {
fields.push(subFieldName);
}
});
}
kibblerz marked this conversation as resolved.
Show resolved Hide resolved
});
});

Expand All @@ -29,16 +37,29 @@ const getAllowedFields = async (contentType) => {
return fields;
};

const recursiveMatch = (fields) => {
return fields.reduce((result, o) => {
const field = RegExp(/\[([\w\d[\]]+)\]/g).exec(o)[1];
if (RegExp(/\[.*\]/g).test(field)) {
const fieldName = RegExp(/[\w\d]+/g).exec(field)[0];
result[fieldName] = recursiveMatch(field.match(/\[([\w\d[\]]+)\]/g));
} else {
result[field] = {};
}
return result;
}, {});
};

/**
* Get all fields from a pattern.
*
* @param {string} pattern - The pattern.
*
* @returns {array} The fields.
* @returns {array} The fields.\[([\w\d\[\]]+)\]
*/
const getFieldsFromPattern = (pattern) => {
let fields = pattern.match(/[[\w\d]+]/g); // Get all substrings between [] as array.
fields = fields.map((field) => RegExp(/(?<=\[)(.*?)(?=\])/).exec(field)[0]); // Strip [] from string.
let fields = pattern.match(/\[([\w\d[\]]+)\]/g); // Get all substrings between [] as array.
fields = recursiveMatch(fields); // Strip [] from string.
kibblerz marked this conversation as resolved.
Show resolved Hide resolved
return fields;
};

Expand All @@ -50,14 +71,30 @@ const getFieldsFromPattern = (pattern) => {
*
* @returns {string} The path.
*/

const resolvePattern = async (pattern, entity) => {
const fields = getFieldsFromPattern(pattern);

fields.map((field) => {
pattern = pattern.replace(`[${field}]`, entity[field] || '');
Object.keys(fields).map((field) => {
if (!Object.keys(fields[field]).length) {
pattern = pattern.replace(`[${field}]`, entity[field] || '');
} else {
const subField = Object.keys(fields[field])[0];
if (Array.isArray(entity[field]) && entity[field][0]) {
pattern = pattern.replace(
`[${field}[${subField}]]`,
entity[field][0][subField] || '',
);
} else {
pattern = pattern.replace(
`[${field}[${subField}]]`,
entity[field][subField] || '',
);
}
}
kibblerz marked this conversation as resolved.
Show resolved Hide resolved
});

pattern = pattern.replace(/([^:]\/)\/+/g, "$1"); // Remove duplicate forward slashes.
pattern = pattern.replace(/([^:]\/)\/+/g, '$1'); // Remove duplicate forward slashes.
pattern = pattern.startsWith('/') ? pattern : `/${pattern}`; // Add a starting slash.
return pattern;
};
Expand All @@ -76,42 +113,58 @@ const validatePattern = async (pattern, allowedFieldNames) => {
if (!pattern) {
return {
valid: false,
message: "Pattern can not be empty",
message: 'Pattern can not be empty',
};
}

const preCharCount = pattern.split("[").length - 1;
const postCharount = pattern.split("]").length - 1;
const preCharCount = pattern.split('[').length - 1;
const postCharount = pattern.split(']').length - 1;

if (preCharCount < 1 || postCharount < 1) {
return {
valid: false,
message: "Pattern should contain at least one field",
message: 'Pattern should contain at least one field',
};
}

if (preCharCount !== postCharount) {
return {
valid: false,
message: "Fields in the pattern are not escaped correctly",
message: 'Fields in the pattern are not escaped correctly',
};
}

let fieldsAreAllowed = true;
getFieldsFromPattern(pattern).map((field) => {
if (!allowedFieldNames.includes(field)) fieldsAreAllowed = false;
});
const allowedFieldsRecursive = (fields) => {
Object.keys(fields).map((field) => {
try {
if (
Object.keys(fields[field])
&& Object.keys(fields[field]).length > 0
) {
allowedFieldsRecursive(fields[field]);
}
} catch (e) {
console.log('Failed!');
console.log(e);
}

if (!allowedFieldNames.includes(field)) fieldsAreAllowed = false;
return true;
});
};
allowedFieldsRecursive(getFieldsFromPattern(pattern));
kibblerz marked this conversation as resolved.
Show resolved Hide resolved

if (!fieldsAreAllowed) {
return {
valid: false,
message: "Pattern contains forbidden fields",
message: 'Pattern contains forbidden fields',
};
}

return {
valid: true,
message: "Valid pattern",
message: 'Valid pattern',
};
};

Expand Down