Merge branch 'dev' into issue/666

This commit is contained in:
Elio Struyf
2024-02-13 09:13:50 +01:00
68 changed files with 1200 additions and 339 deletions
+38 -6
View File
@@ -23,7 +23,17 @@ import {
} from '../constants';
import { DumpOptions } from 'js-yaml';
import { FrontMatterParser, ParsedFrontMatter } from '../parsers';
import { ContentType, Extension, Logger, Settings, SlugHelper, isValidFile, parseWinPath } from '.';
import {
ContentType,
Extension,
Logger,
Settings,
SlugHelper,
isValidFile,
parseWinPath,
processArticlePlaceholdersFromPath,
processTimePlaceholders
} from '.';
import { format, parse } from 'date-fns';
import { Notifications } from './Notifications';
import { Article } from '../commands';
@@ -37,7 +47,6 @@ import { DEFAULT_FILE_TYPES } from '../constants/DefaultFileTypes';
import { fromMarkdown } from 'mdast-util-from-markdown';
import { Link, Parent } from 'mdast-util-from-markdown/lib';
import { Content } from 'mdast';
import { processKnownPlaceholders } from './PlaceholderHelper';
import { CustomScript } from './CustomScript';
import { Folders } from '../commands/Folders';
import { existsAsync, readFileAsync } from '../utils';
@@ -57,6 +66,19 @@ export class ArticleHelper {
return ArticleHelper.getFrontMatterFromDocument(editor.document);
}
/**
* Retrieves the front matter from the current active document.
* @returns The front matter object if found, otherwise undefined.
*/
public static getFrontMatterFromCurrentDocument() {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
return ArticleHelper.getFrontMatterFromDocument(editor.document);
}
/**
* Get the contents of the specified document
*
@@ -524,7 +546,12 @@ export class ArticleHelper {
* @param title
* @returns
*/
public static async updatePlaceholders(data: any, title: string, filePath: string) {
public static async updatePlaceholders(
data: any,
title: string,
filePath: string,
slugTemplate?: string
) {
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
const fmData = Object.assign({}, data);
@@ -536,10 +563,11 @@ export class ArticleHelper {
}
if (fieldName === 'slug' && (fieldValue === null || fieldValue === '')) {
fmData[fieldName] = SlugHelper.createSlug(title);
fmData[fieldName] = SlugHelper.createSlug(title, fmData, slugTemplate);
}
fmData[fieldName] = processKnownPlaceholders(fmData[fieldName], title, dateFormat);
fmData[fieldName] = await processArticlePlaceholdersFromPath(fmData[fieldName], filePath);
fmData[fieldName] = processTimePlaceholders(fmData[fieldName], dateFormat);
fmData[fieldName] = await this.processCustomPlaceholders(fmData[fieldName], title, filePath);
}
@@ -597,7 +625,11 @@ export class ArticleHelper {
}
const regex = new RegExp(`{{${placeholder.id}}}`, 'g');
const updatedValue = processKnownPlaceholders(placeHolderValue, title, dateFormat);
let updatedValue = filePath
? await processArticlePlaceholdersFromPath(placeHolderValue, filePath)
: placeHolderValue;
updatedValue = processTimePlaceholders(updatedValue, dateFormat);
if (value === `{{${placeholder.id}}}`) {
value = updatedValue;
+71 -16
View File
@@ -1,6 +1,13 @@
import { ModeListener } from './../listeners/general/ModeListener';
import { PagesListener } from './../listeners/dashboard';
import { ArticleHelper, CustomScript, Logger, Settings } from '.';
import {
ArticleHelper,
CustomScript,
Logger,
Settings,
processArticlePlaceholdersFromData,
processTimePlaceholders
} from '.';
import {
DefaultFieldValues,
EXTENSION_NAME,
@@ -26,7 +33,6 @@ import { Questions } from './Questions';
import { Notifications } from './Notifications';
import { DEFAULT_CONTENT_TYPE_NAME } from '../constants/ContentType';
import { Telemetry } from './Telemetry';
import { processKnownPlaceholders } from './PlaceholderHelper';
import { basename } from 'path';
import { ParsedFrontMatter } from '../parsers';
import { encodeEmoji, existsAsync, fieldWhenClause, writeFileAsync } from '../utils';
@@ -299,17 +305,17 @@ export class ContentType {
Telemetry.send(TelemetryEvent.addMissingFields);
const content = ArticleHelper.getCurrent();
const article = ArticleHelper.getCurrent();
if (!content || !content.data) {
if (!article || !article.data) {
Notifications.warning(
l10n.t(LocalizationKey.helpersContentTypeAddMissingFieldsNoFrontMatterWarning)
);
return;
}
const contentType = ArticleHelper.getContentType(content);
const updatedFields = ContentType.generateFields(content.data, contentType.fields);
const contentType = ArticleHelper.getContentType(article);
const updatedFields = ContentType.generateFields(article.data, contentType.fields);
const contentTypes = ContentType.getAll() || [];
const index = contentTypes.findIndex((ct) => ct.name === contentType.name);
@@ -927,7 +933,8 @@ export class ContentType {
titleValue,
templateData?.data || {},
newFilePath,
!!contentType.clearEmpty
!!contentType.clearEmpty,
contentType
);
const article: ParsedFrontMatter = {
@@ -982,6 +989,7 @@ export class ContentType {
data: any,
filePath: string,
clearEmpty: boolean,
contentType: IContentType,
isRoot: boolean = true
): Promise<any> {
if (obj.fields) {
@@ -995,9 +1003,13 @@ export class ContentType {
if (field.name === 'title') {
if (field.default) {
data[field.name] = processKnownPlaceholders(
field.default,
titleValue,
data[field.name] = processArticlePlaceholdersFromData(
field.default as string,
data,
contentType
);
data[field.name] = processTimePlaceholders(
data[field.name],
field.dateFormat || dateFormat
);
data[field.name] = await ArticleHelper.processCustomPlaceholders(
@@ -1018,6 +1030,7 @@ export class ContentType {
{},
filePath,
clearEmpty,
contentType,
false
);
@@ -1026,18 +1039,33 @@ export class ContentType {
}
} else {
const defaultValue = field.default;
console.log(field.name, defaultValue, Array.isArray(defaultValue));
if (typeof defaultValue === 'string') {
data[field.name] = processKnownPlaceholders(
data[field.name] = await ContentType.processFieldPlaceholders(
defaultValue,
titleValue,
field.dateFormat || dateFormat
);
data[field.name] = await ArticleHelper.processCustomPlaceholders(
data[field.name],
data,
contentType,
field.dateFormat || dateFormat,
titleValue,
filePath
);
} else if (defaultValue && Array.isArray(defaultValue)) {
let defaultValues = [];
for (let value of defaultValue as string[]) {
if (typeof value === 'string') {
value = await ContentType.processFieldPlaceholders(
value,
data,
contentType,
field.dateFormat || dateFormat,
titleValue,
filePath
);
}
defaultValues.push(value);
}
data[field.name] = defaultValues;
} else if (typeof defaultValue !== 'undefined') {
data[field.name] = defaultValue;
} else {
@@ -1082,6 +1110,7 @@ export class ContentType {
}
break;
case 'string':
case 'slug':
case 'image':
case 'file':
default:
@@ -1100,6 +1129,32 @@ export class ContentType {
return data;
}
/**
* Processes the field placeholders in the given value.
*
* @param defaultValue - The default value for the field.
* @param data - The data object containing the field values.
* @param contentType - The content type object.
* @param dateFormat - The date format string.
* @param title - The title string.
* @param filePath - The file path string.
* @returns The processed value with field placeholders replaced.
*/
private static async processFieldPlaceholders(
defaultValue: string,
data: any,
contentType: IContentType,
dateFormat: string,
title: string,
filePath: string
) {
let value = processArticlePlaceholdersFromData(defaultValue, data, contentType);
value = processTimePlaceholders(value, dateFormat);
value = await ArticleHelper.processCustomPlaceholders(value, title, filePath);
return value;
}
/**
* Verify if the content type feature is enabled
* @returns
+1 -5
View File
@@ -88,16 +88,12 @@ export class DashboardSettings {
const ext = Extension.getInstance();
const wsFolder = Folders.getWorkspaceFolder();
const isInitialized = await Project.isInitialized();
const gitActions = Settings.get<boolean>(SETTING_GIT_ENABLED);
const pagination = Settings.get<boolean | number>(SETTING_DASHBOARD_CONTENT_PAGINATION);
const settings = {
projects: Settings.getProjects(),
project: Settings.getProject(),
git: {
isGitRepo: gitActions ? await GitListener.isGitRepository() : false,
actions: gitActions || false
},
git: await GitListener.getSettings(),
beta: ext.isBetaVersion(),
wsFolder: wsFolder ? wsFolder.fsPath : '',
staticFolder: Folders.getStaticFolderRelativePath(),
+1 -7
View File
@@ -31,7 +31,6 @@ import {
SETTING_SLUG_UPDATE_FILE_NAME,
SETTING_TAXONOMY_CUSTOM,
SETTING_TAXONOMY_FIELD_GROUPS,
SETTING_GIT_ENABLED,
SETTING_SEO_TITLE_FIELD
} from '../constants';
import { GitListener } from '../listeners/general';
@@ -49,14 +48,9 @@ import { Folders } from '../commands';
export class PanelSettings {
public static async get(): Promise<IPanelSettings> {
const gitActions = Settings.get<boolean>(SETTING_GIT_ENABLED);
return {
aiEnabled: Settings.get<boolean>(SETTING_SPONSORS_AI_ENABLED) || false,
git: {
isGitRepo: gitActions ? await GitListener.isGitRepository() : false,
actions: gitActions || false
},
git: await GitListener.getSettings(),
seo: {
title: (Settings.get(SETTING_SEO_TITLE_LENGTH) as number) || -1,
slug: (Settings.get(SETTING_SEO_SLUG_LENGTH) as number) || -1,
+35 -6
View File
@@ -1,4 +1,6 @@
import { stopWords, charMap } from '../constants';
import { Settings } from '.';
import { stopWords, charMap, SETTING_DATE_FORMAT, SETTING_SLUG_TEMPLATE } from '../constants';
import { processTimePlaceholders, processFmPlaceholders } from '.';
export class SlugHelper {
/**
@@ -6,13 +8,41 @@ export class SlugHelper {
*
* @param articleTitle
*/
public static createSlug(articleTitle: string): string | null {
public static createSlug(
articleTitle: string,
articleData: { [key: string]: any },
slugTemplate?: string
): string | null {
if (!articleTitle) {
return null;
}
// Remove punctuation from input string, and split it into words.
let cleanTitle = this.removePunctuation(articleTitle).trim();
if (!slugTemplate) {
slugTemplate = Settings.get<string>(SETTING_SLUG_TEMPLATE) || undefined;
}
if (slugTemplate) {
if (slugTemplate.includes('{{title}}')) {
const regex = new RegExp('{{title}}', 'g');
slugTemplate = slugTemplate.replace(regex, SlugHelper.slugify(articleTitle));
}
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
articleTitle = processTimePlaceholders(slugTemplate, dateFormat);
articleTitle = processFmPlaceholders(articleTitle, articleData);
return articleTitle;
}
return SlugHelper.slugify(articleTitle);
}
/**
* Converts a title into a slug by removing punctuation, stop words, and replacing characters.
* @param title - The title to be slugified.
* @returns The slugified version of the title.
*/
public static slugify(title: string): string {
let cleanTitle = this.removePunctuation(title).trim();
if (cleanTitle) {
cleanTitle = cleanTitle.toLowerCase();
// Split into words
@@ -23,8 +53,7 @@ export class SlugHelper {
cleanTitle = this.replaceCharacters(cleanTitle);
return cleanTitle;
}
return null;
return '';
}
/**
+2 -1
View File
@@ -15,7 +15,6 @@ export * from './MediaHelpers';
export * from './MediaLibrary';
export * from './Notifications';
export * from './PanelSettings';
export * from './PlaceholderHelper';
export * from './Questions';
export * from './Sanitize';
export * from './SeoHelper';
@@ -32,5 +31,7 @@ export * from './getTaxonomyField';
export * from './isValidFile';
export * from './openFileInEditor';
export * from './parseWinPath';
export * from './processArticlePlaceholders';
export * from './processFmPlaceholders';
export * from './processPathPlaceholders';
export * from './processTimePlaceholders';
+53
View File
@@ -0,0 +1,53 @@
import { ContentType } from '../models';
import { ArticleHelper } from './ArticleHelper';
import { SlugHelper } from './SlugHelper';
export const processArticlePlaceholdersFromData = (
value: string,
data: { [key: string]: any },
contentType: ContentType
): string => {
if (value.includes('{{title}}') && data.title) {
const regex = new RegExp('{{title}}', 'g');
value = value.replace(regex, data.title || '');
}
if (value.includes('{{slug}}')) {
const regex = new RegExp('{{slug}}', 'g');
value = value.replace(
regex,
SlugHelper.createSlug(data.title || '', data, contentType.slugTemplate) || ''
);
}
return value;
};
export const processArticlePlaceholdersFromPath = async (
value: string,
filePath: string
): Promise<string> => {
const article = await ArticleHelper.getFrontMatterByPath(filePath);
if (!article) {
return value;
}
if (value.includes('{{title}}')) {
const regex = new RegExp('{{title}}', 'g');
value = value.replace(regex, article.data.title || '');
}
if (value.includes('{{slug}}') && filePath) {
const contentType = article ? ArticleHelper.getContentType(article) : undefined;
if (contentType) {
const regex = new RegExp('{{slug}}', 'g');
value = value.replace(
regex,
SlugHelper.createSlug(article.data.title || '', article.data, contentType.slugTemplate) ||
''
);
}
}
return value;
};
+1 -1
View File
@@ -1,6 +1,6 @@
import { format } from 'date-fns';
export const processFmPlaceholders = (value: string, fmData: any) => {
export const processFmPlaceholders = (value: string, fmData: { [key: string]: any }) => {
// Example: {{fm.date}} or {{fm.date | dateFormat 'DD.MM.YYYY'}}
if (value && value.includes('{{fm.')) {
const regex = /{{fm.[^}]*}}/g;
@@ -1,29 +1,14 @@
import { format } from 'date-fns';
import { DateHelper } from './DateHelper';
import { SlugHelper } from './SlugHelper';
/**
* Replace the known placeholders
* Replace the time placeholders
* @param value
* @param title
* @returns
*/
export const processKnownPlaceholders = (
value: string,
title: string | undefined,
dateFormat: string
) => {
export const processTimePlaceholders = (value: string, dateFormat?: string) => {
if (value && typeof value === 'string') {
if (value.includes('{{title}}')) {
const regex = new RegExp('{{title}}', 'g');
value = value.replace(regex, title || '');
}
if (value.includes('{{slug}}')) {
const regex = new RegExp('{{slug}}', 'g');
value = value.replace(regex, SlugHelper.createSlug(title || '') || '');
}
if (value.includes('{{now}}')) {
const regex = new RegExp('{{now}}', 'g');