Discourse تستخدم محرك Markdown يُسمى Markdown-it.
فيما يلي بعض ملاحظات التطوير التي ستساعدك إما في إصلاح الأخطاء في النواة أو في إنشاء إضافاتك الجديدة.
الأساسيات
Discourse يحتوي على عدد قليل من الدوال المساعدة فوق المحرك، لذا فإن الغالبية العظمى من التعلم المطلوب هو فهم Markdown It.
يحتوي مجلد التوثيق على التوثيق الحالي.
أوصي بشدة بقراءة:
-
مستند البنية المعمارية لفهم كيفية عمل المحرك على مستوى عالٍ.
-
التطوير للحصول على إرشادات أساسية للتطوير
-
توثيق واجهة برمجة التطبيقات للحصول على مرجع مفصل جدًا
-
وأخيرًا، الكود المصدري وهو موثق جيدًا وواضح.
عندما أقوم بتطوير امتدادات للمحرك، فأنا عادةً ما أفتح محررًا ثانيًا للنظر في القواعد الموجودة. يتكون المحرك من قائمة طويلة من القواعد، وكل قاعدة في ملف مخصص يسهل متابعته بشكل معقول.
إذا كنت أعمل على قاعدة مضمّنة (inline)، فسأفكر في أي قاعدة مضمّنة موجودة تعمل بشكل مشابه تقريبًا وسأقوم ببناء عملي عليها.
تذكر أنه يمكنك أحيانًا الحصول على الوظيفة المطلوبة بمجرد تغيير المُصيغ (renderer)، وهو ما يكون عادةً أسهل بكثير.
كيفية هيكلة امتداد؟
عند تهيئة محرك markdown، يبحث في جميع الوحدات.
إذا كانت أي وحدة تُسمى /discourse-markdown\/|markdown-it\// (أي أنها تقع في دليل discourse-markdown أو markdown-it)، فستكون مرشحًا للتهيئة.
إذا كانت الوحدة تُصدّر دالة تسمى setup، فسيتم استدعاؤها بواسطة المحرك أثناء التهيئة.
بروتوكول التهيئة (setup)
/my-plugins/assets/javascripts/discourse-markdown/awesome-extension.js
export function setup(helper) {
// ... ضع كودك هنا
}
تحصل دالة setup على وصول إلى كائن مساعد يمكن استخدامه للتهيئة. يحتوي هذا الكائن على الدوال والمتغيرات التالية:
-
bool markdownIt: يتم تعيين هذه الخاصية علىtrueعندما يكون المحرك الجديد قيد الاستخدام. للتوافق الخلفي المناسب، تريد التحقق منها. -
registerOptions(cb(opts, siteSettings, state)): يتم استدعاء الدالة المقدمة قبل تهيئة محرك markdown، يمكنك استخدامه لتحديد ما إذا كنت ستفعّل المحرك أم تعطّله. -
allowList([spec, ...]): تُستخدم هذه الدالة للسماح بمرور HTML من خلال مُنقّي (sanitizer) الخاص بنا. -
registerPlugin(func(md)): تُستخدم هذه الدالة لتسجيل إضافة Markdown It.
دمج كل شيء معًا
function amazingMarkdownItInline(state, silent) {
// امتداد markdown it المضمّن القياسي يأتي هنا.
return false;
}
export function setup(helper) {
if(!helper.markdownIt) { return; }
helper.registerOptions((opts,siteSettings)=>{
opts.features.['my_extension'] = !!siteSettings.my_extension_enabled;
});
helper.allowList(['span.amazing', 'div.amazing']);
helper.registerPlugin(md=>{
md.inline.push('amazing', amazingMarkdownItInline);
});
}
امتدادات Discourse الخاصة
BBCode
تحتوي Discourse على قاعدتين (rulers) يمكنك استخدامهما لوسوم BBCode مخصصة. قاعدة على مستوى مضمّن (inline) وقاعدة على مستوى كتلة (block).
قواعد bbcode المضمّنة هي تلك التي تعيش في فقرة مضمّنة مثل [b]غامض[/b]
تُطبق قواعد مستوى الكتلة على عدة أسطر من النص مثل:
[poll]
- الخيار 1
- الخيار 2
[/poll]
md.inline.bbcode.ruler يحتفظ بقائمة من القواعد المضمّنة التي تُطبق بالترتيب.
md.block.bbcode.ruler يحتفظ بقائمة من قواعد مستوى الكتلة
هناك العديد من الأمثلة للقواعد المضمّنة في: bbcode-inline.js
الاقتباسات والاستطلاعات أمثلة جيدة لقواعد bbcode على مستوى الكتلة.
قواعد BBCode المضمّنة
قواعد BBCode المضمّنة هي كائن يحتوي على معلومات حول كيفية التعامل مع وسم.
على سبيل المثال:
md.inline.bbcode.ruler.push("underline", {
tag: "u",
wrap: "span.bbcode-u",
});
سيؤدي إلى
test [u]test[/u]
أن يُحوَّل إلى:
test <span class="bbcode-u">test</span>
يمكن للقواعد المضمّنة إما التفاف حول النص أو استبداله. عند التفاف، يمكنك أيضًا تمرير دالة للحصول على مرونة إضافية.
md.inline.bbcode.ruler.push("url", {
tag: "url",
wrap: function (startToken, endToken, tagInfo, content) {
const url = (tagInfo.attrs["_default"] || content).trim();
if (simpleUrlRegex.test(url)) {
startToken.type = "link_open";
startToken.tag = "a";
startToken.attrs = [
["href", url],
["data-bbcode", "true"],
];
startToken.content = "";
startToken.nesting = 1;
endToken.type = "link_close";
endToken.tag = "a";
endToken.content = "";
endToken.nesting = -1;
} else {
// مجرد إزالة وسم bbcode
endToken.content = "";
startToken.content = "";
// حالة حافة، لا نريد أن يُكتشف هذا كـ onebox إذا تم ربطه تلقائيًا
// هذا يضمن عدم إزالته
startToken.type = "html_inline";
}
return false;
},
});
توفر دالة التفاف وصولًا إلى:
-
tagInfo، وهو قاموس من أزواج المفاتيح والقيم المحددة عبر bbcode.
[test=testing]→{_default: "testing"}
[test a=1]→{a: "1"} -
الرمز (token) الذي يبدأ المضمّن
-
الرمز الذي ينهي المضمّن
-
محتوى bbcode المضمّن
باستخدام هذه المعلومات، يمكنك التعامل مع جميع أنواع احتياجات التفاف.
في بعض الأحيان قد ترغب في استبدال كتلة BBCode بأكملها، لهذا يمكنك استخدام replace
md.inline.bbcode.ruler.push("code", {
tag: "code",
replace: function (state, tagInfo, content) {
let token;
token = state.push("code_inline", "code", 0);
token.content = content;
return true;
},
});
في هذه الحالة، نحن نستبدل [code]كتلة كود[code] بأكملها برمز code_inline واحد.
قواعد BBCode على مستوى الكتلة
تسمح لك قواعد bbcode على مستوى الكتلة باستبدال كتلة بأكملها. واجهات برمجة التطبيقات (APIs) للكتل هي نفسها للحالات البسيطة:
md.block.bbcode.ruler.push("happy", {
tag: "happy",
wrap: "div.happy",
});
[happy]
hello
[/happy]
سيصبح
<div class="happy">hello</div>
تتميز دالة التفاف بواجهة برمجة تطبيقات مختلفة قليلاً بسبب عدم وجود رموز التفاف.
md.block.bbcode.ruler.push("money", {
tag: "money",
wrap: function (token, tagInfo) {
token.attrs = [["data-money", tagInfo.attrs["_default"]]];
return true;
},
});
[money=100]
**test**
[/money]
سيصبح
<div data-money="100">
<b>test</b>
</div>
يمكنك الحصول على تحكم كامل في عرض الكتلة باستخدام قاعدتي before و after، مما يسمح لك بأداء أشياء مثل التعشيق المزدوج لوسم وما إلى ذلك.
md.block.bbcode.ruler.push("ddiv", {
tag: "ddiv",
before: function (state, tagInfo) {
state.push("div_open", "div", 1);
state.push("div_open", "div", 1);
},
after: function (state) {
state.push("div_close", "div", -1);
state.push("div_close", "div", -1);
},
});
[ddiv]
test
[/ddiv]
سيصبح
<div>
<div>test</div>
</div>
معالجة استبدال النص
تأتي Discourse مع قاعدة نواة خاصة جدًا لتطبيق تعبيرات منتظمة على النص.
md.core.textPostProcess.ruler
للاستخدام:
md.core.textPostProcess.ruler.push("onlyfastcars", {
matcher: /(car)|(bus)/, // لا يتم دعم أعلام regex
onMatch: function (buffer, matches, state) {
let token = new state.Token("text", "", 0);
token.content = "fast " + matches[0];
buffer.push(token);
},
});
I like cars and buses
سيصبح
<p>I like fast cars and fast buses</p>
هذا المستند مُتحكم في إصداره - اقترح تغييرات على github.