const StoreShortcode = require("../models/StoreShortcode"); /** * خدمة معالجة الأكواد المختصرة * @version 2.0.0 */ class ShortcodeService { /** * معالجة النص واستبدال جميع الـ Shortcodes * @param {string} content - المحتوى الأصلي * @param {string} storeId - معرف المتجر * @param {Object} options - خيارات إضافية * @returns {Promise} - المحتوى بعد المعالجة */ static async parseShortcodes(content, storeId, options = {}) { if (!content || typeof content !== 'string') return content; // التعبير النمطي للـ Shortcodes const shortcodeRegex = /\[(\w+)(?:\s+([^\]]*))?\]/g; let parsedContent = content; let match; // تجميع جميع الـ Shortcodes الفريدة const shortcodesToProcess = new Map(); while ((match = shortcodeRegex.exec(content)) !== null) { const name = match[1]; const attrsStr = match[2] || ''; if (!shortcodesToProcess.has(name)) { shortcodesToProcess.set(name, []); } shortcodesToProcess.get(name).push({ fullMatch: match[0], attrs: this.parseAttributes(attrsStr) }); } // معالجة كل Shortcode for (const [name, occurrences] of shortcodesToProcess) { const shortcode = await StoreShortcode.findOne({ userId: storeId, name: name, isActive: true }); if (!shortcode) continue; for (const occ of occurrences) { try { const html = await shortcode.render(occ.attrs, { storeId, userToken: options.userToken }); parsedContent = parsedContent.replace(occ.fullMatch, html); } catch (error) { console.error(`Error processing shortcode ${name}:`, error); parsedContent = parsedContent.replace(occ.fullMatch, `
Error: ${error.message}
` ); } } } return parsedContent; } /** * تحليل معاملات الـ Shortcode * @param {string} attrsStr - نص المعاملات * @returns {Object} - كائن المعاملات */ static parseAttributes(attrsStr) { const attrs = {}; if (!attrsStr) return attrs; // دعم كل من name="value" و name=value const attrRegex = /(\w+)(?:="([^"]*)"|='([^']*)'|="([^"]*)"|="([^"]*)"|="([^"]*)"|=([^\s]+))/g; let match; while ((match = attrRegex.exec(attrsStr)) !== null) { const name = match[1]; let value = match[2] || match[3] || match[4] || match[5] || match[6] || match[7] || ''; // تحويل القيم إلى الأنواع المناسبة if (value === 'true') value = true; else if (value === 'false') value = false; else if (!isNaN(value) && value !== '') value = Number(value); attrs[name] = value; } return attrs; } /** * الحصول على قائمة جميع الـ Shortcodes المتاحة لمستخدم * @param {string} userId - معرف المستخدم * @returns {Promise} - قائمة الـ Shortcodes */ static async getAllShortcodes(userId) { const builtins = StoreShortcode.getBuiltinShortcodes(); const custom = await StoreShortcode.find({ userId, type: 'custom', isActive: true }); return [ ...builtins.map(b => ({ ...b, isBuiltin: true })), ...custom.map(c => ({ ...c.toObject(), isBuiltin: false })) ]; } /** * إنشاء Shortcode مخصص جديد * @param {string} userId - معرف المستخدم * @param {Object} data - بيانات الـ Shortcode * @returns {Promise} - الـ Shortcode المنشأ */ static async createCustomShortcode(userId, data) { const { name, displayName, description, template, parameters, settings } = data; // التحقق من عدم وجود اسم مكرر const existing = await StoreShortcode.findOne({ userId, name }); if (existing) { throw new Error(`Shortcode "${name}" already exists`); } const shortcode = new StoreShortcode({ userId, name: name.toLowerCase().replace(/[^a-z0-9_-]/g, ''), displayName, description, template, parameters: parameters || [], settings: settings || {}, type: 'custom' }); await shortcode.save(); return shortcode; } /** * تحديث Shortcode مخصص * @param {string} shortcodeId - معرف الـ Shortcode * @param {string} userId - معرف المستخدم (للتحقق) * @param {Object} data - بيانات التحديث * @returns {Promise} - الـ Shortcode المحدث */ static async updateCustomShortcode(shortcodeId, userId, data) { const shortcode = await StoreShortcode.findOne({ _id: shortcodeId, userId }); if (!shortcode) { throw new Error('Shortcode not found'); } Object.assign(shortcode, data); shortcode.cachedOutput = null; // إبطال التخزين المؤقت await shortcode.save(); return shortcode; } /** * حذف Shortcode مخصص * @param {string} shortcodeId - معرف الـ Shortcode * @param {string} userId - معرف المستخدم * @returns {Promise} */ static async deleteCustomShortcode(shortcodeId, userId) { const result = await StoreShortcode.deleteOne({ _id: shortcodeId, userId, type: 'custom' }); return result.deletedCount > 0; } /** * معاينة Shortcode (دون حفظ) * @param {Object} data - بيانات الـ Shortcode * @param {Object} context - السياق * @returns {Promise} - HTML المعاينة */ static async previewShortcode(data, context) { const { name, displayName, template, parameters, attrs = {} } = data; // دمج المعاملات مع القيم الافتراضية const mergedAttrs = {}; if (parameters) { for (const param of parameters) { mergedAttrs[param.name] = attrs[param.name] !== undefined ? attrs[param.name] : param.defaultValue; } } // تنفيذ القالب let html = template || ''; for (const [key, value] of Object.entries(mergedAttrs)) { const regex = new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, 'g'); html = html.replace(regex, String(value ?? '')); } return html || `
Preview: ${displayName} (no content)
`; } /** * تهيئة الـ Shortcodes لمستخدم جديد * @param {string} userId - معرف المستخدم */ static async initializeUserShortcodes(userId) { await StoreShortcode.initializeBuiltins(userId); } } module.exports = ShortcodeService;