Theme-Component مقابل Plugin: ما الفرق؟

هل يمكن لأحد توضيح الفرق بين مفاهيم Discourse الثلاثة التالية: مكون السمة، الإضافة، و واجهة برمجة التطبيقات للإضافات (pluginAPI)؟

خاصة بين مكون السمة والإضافة. إذا أردت تخصيص منتداك، فكيف أعرف أيًا منهما يجب أن أبنيه؟

(عذرًا إذا فاتني هذا التمييز في المقدمة، لكنني لم أجد ذلك هناك)

أنا لست مبتدئًا في استخدام Discourse، لكنني أيضًا لست خبيرًا.

  1. تستخدم مكون السمة (Theme-component) تقنيات HTML وCSS وJavaScript لتحسين سمة أساسية.
    أشير إلى السمة الأساسية لأنها تُسمى عادةً “سمة”، وأحيانًا لا يميز الناس الفرق، مما يضطرك إلى استنتاج المعنى. يمكن للمدير تثبيت سمة أو مكون سمة دون إيقاف الموقع، وإذا كنت عميلًا لـ Discourse، فيمكنك أيضًا إضافة هذه المكونات. (قائمة) انظر أيضًا: دليل المبتدئين لاستخدام سمات Discourse

  2. يستخدم الإضافة (plugin) لغة Ruby ويمكنه فعل تقريبًا أي شيء ممكن. إذا كنت عميلًا لـ Discourse، فإن لديك مجموعات محدودة من الإضافات المسموح بتفعيلها، أما إذا كنت تستضيف الموقع بنفسك، فيمكنك إضافة ما تشاء، لكن انتبه: أرى العديد من المنشورات التي تشير إلى أن الإضافات المخصصة قد تعطل الموقع أثناء الترقية. كما أن هذه الإضافات لا تتطلب إعادة تشغيل عند التفعيل؛ وأظن أن إعادة التشغيل قد تكون مطلوبة عند التثبيت لأول مرة. يمكن لآخرين التوضيح أكثر، لأن خبرتي الوحيدة مع الإضافات تقتصر على تفعيلها من قوائم الإدارة. (قائمة) انظر أيضًا: دليل المبتدئين لإنشاء إضافات Discourse - الجزء 1

  3. لم أقم بتطوير إضافة، لذا أعتقد أنك تقصد مكتبة Ruby API الخاصة بـ Discourse. انظر: Use the Discourse API ruby gem

  4. يوجد أيضًا واجهة برمجة التطبيقات (API) التي تعتمد على Webhooks وتُستخدم عادةً مع curl أو لغة برمجة أخرى. وهذا أمر جيد لأنه يحررك من استخدام Ruby.

  5. وعلى الرغم من أنني لم أغامر بهذا المجال أيضًا، فيمكنك البرمجة على مستوى قاعدة بيانات PostgreSQL، لكنني لا أنصح بذلك إلا إذا كنت ماهرًا جدًا وواثقًا تمامًا من قدراتك.

أتمنى أن يكون هذا مفيدًا.


تعديل

جولة إضافية إذا كنت ترغب في الانغماس كليًا ك مطور لـ Discourse

انظر: كيف تبدأ في بناء أشياء لـ Discourse إذا كنت مبتدئًا (مثلي)

لإكمال إجابة @EricGT التي تشرح الأمر بشكل جيد بالفعل –

  • المظهر (Theme) أو مكون المظهر هو في الأساس طريقة لتعديل أي جزء من واجهة تطبيق Discourse الأمامي المبنية بـ EmberJS. يمكن أن يكون ذلك بسيطًا مثل تخصيص HTML أو CSS، أو معقدًا مثل إضافة وظائف جديدة. تتميز المظاهر بمرونة أكبر في حال حدوث خلل، مما يعني أن موقعك بالكامل لن يتعطل بالضرورة إذا لم يعمل شيء ما بشكل صحيح.
  • تؤثر الإضافة (Plugin) بشكل رئيسي على تطبيق Rails من جانب الخادم، ولكنها تتضمن أيضًا كل قدرات المظهر والتأثير على تطبيق EmberJS، رغم أنها أكثر تعقيدًا. تميل حالات فشل الإضافات إلى أن تكون أقل مرونة، لذا إذا كان بإمكانك بناء شيء ما باستخدام مظهر، فابدأ من هناك. ومع ذلك، فإن الإضافة مطلوبة إذا كنت بحاجة إلى مسار مخصص أو لتخزين البيانات.
  • pluginAPI هي واجهة برمجة تطبيقات (API) على جانب العميل يمكن لمكونات المظاهر استخدامها لتعديل أجزاء محددة من عميل Discourse بسهولة أكبر.

أفضل مكان للبدء في تخصيص موقعك هو باستخدام مظهر. إليك بعض الموارد:

دليل المصممين لمظاهر Discourse
دليل المطورين لمظاهر Discourse
دليل المبتدئين لاستخدام منشئ المظاهر وواجهة سطر الأوامر الخاصة بها (Theme CLI) للبدء في بناء مظهر لـ Discourse

شكرًا لكم يا أصدقاء. هذا مفيد. أعتقد أن التمييز الرئيسي الذي أدركه هو:
– إذا كنت تريد تغيير شيء يتعلق فقط بما يظهر في الواجهة الأمامية، فأنشئ سمة (theme).
– إذا كنت تريد تغيير شيء يتطلب تفاعلاً مع الخلفية، فأنشئ إضافة (plugin).

هل يبدو ذلك صحيحًا؟

إليك مثالًا ملموسًا لديّ في ذهني أحاول فهمه: أريد تمكين أي مشرف قسم من تثبيت المواضيع في ذلك القسم. أعتقد أن الخطوات العامة هي:

  1. تقييم ما إذا كان المستخدم مشرفًا على القسم (يتطلب هذا الوصول إلى الخلفية للحصول على معلومات حول المستخدم والقسم).

  2. إذا كان المستخدم مشرفًا، أظهر زر التثبيت (هذا في الواجهة الأمامية).

  3. إذا نقر المستخدم على زر التثبيت، انقل ذلك الموضوع إلى الأعلى (غير متأكد تمامًا من مكان حدوث هذا في كود Discourse—ربما في الواجهة الأمامية والخلفية معًا؟).

هنا، لأنني بحاجة إلى التواصل مع الخلفية (ربما في النقطة 1، وربما أيضًا في النقطة 3؟)، سأضطر لاستخدام إضافة. هل يبدو ذلك صحيحًا؟

قد يكون الجزء الظاهري هو ما يُظهر ذلك، لكنه سيتطلب مشاركة الواجهة الخلفية لأنه يتعلق بالحقوق والأمان. لا يمكنك ترك الواجهة الأمامية تتخذ قرارًا بشأن الصلاحيات التي يتمتع بها أي شخص.

هل تقصد أن الإجابة برمجياً على سؤال “هل هذا المستخدم مشرف على هذه الفئة؟” ستتضمن ملفات متعددة عبر الواجهة الأمامية والخلفية؟ Hmm…

في السمة، يجب أن تتمكن من معرفة ما إذا كان المستخدم مشرفًا، وإجراء مكالمات للخلفية إذا كانت واجهة برمجة تطبيقات Discourse تعرض نقطة نهاية يمكنك استدعاؤها من الواجهة الأمامية (بعد كل شيء، يمكن للسمة استخدام JavaScript)، لذا ربما لا تحتاج إلى إضافة لـ [1]. ستحتاج إليها فقط إذا كنت بحاجة إلى تغيير سلوك الخلفية أو عرض واجهة برمجة تطبيقات.

لكنك ربما تحتاج إليها لـ [3]، لأنه، كما قال @merefield، يتعلق بالحقوق والأمان (إذا منع الخلفية المشرف من تثبيت موضوع، فستحتاج إلى تغييره للسماح بذلك).

كما قلت أعلاه، ربما لن يتطلب ذلك إضافة مجردًا للتحقق من ذلك (لمعرفة ما إذا كان المستخدم مشرفًا أم لا)، لكنه ربما سيتطلب ذلك بسبب الإجراء المتمثل في السماح له بتثبيت الفئة. إذا كانت Discourse لديها خيار يسمح لأي مشرف بتثبيت موضوع (لا أعرف ما إذا كانت لديها)، فلن تحتاج إلى إضافة (ولكنك ربما لن تحتاج إلى سمة أيضًا، ما لم يكن زر التثبيت غير ظاهر للمشرفين، وتستخدم سمة فقط لعرضه للمشرفين واستدعاء نقطة النهاية في JavaScript عند النقر على زر التثبيت).

هذا مفيد جداً فيما يتعلق بالموضوعات مقابل الإضافات، وكذلك فيما يتعلق بالمثال المحدد الذي ذكرته. شكراً لك.

حتى الآن، كان نهجي في إدخال التغييرات يتمثل في فرز تفاصيل كود Discourse (أين يتم تعريف هذا الكائن، وما الذي يتحكم في هذا القالب، وأين يوجد المنطق الذي يتعامل مع هذا الإجراء، إلخ…). لكن هذا كان بطيئاً.

أعتقد أن المسار الأكثر كفاءة على الأرجح هو التركيز على واجهة برمجة التطبيقات (API). بهذه الطريقة، لن أحتاج إلى فرز جميع تفاصيل كود Discourse الناضج، ويمكنني أيضاً التركيز على بناء سمة بدلاً من إضافة - أو ربما مجرد إدخال تغييرات في لوحة تحكم “التخصيص”.

بشكل أساسي، يبدو أن فهم كيفية عمل واجهة برمجة التطبيقات (API) أكثر قابلية للإدارة بكثير من فهم قاعدة كود Discourse.

لنلتزم بالمثال الذي ذكرته: إذا كان المستخدم مشرفاً على فئة معينة، فاسمح لهذا المستخدم بتثبيت المواضيع في صفحة الفئة.

هل يمكنني فعل ذلك دون إضافة؟ دعني أحاول رسم مخطط لذلك:

1. هل المستخدم مشرف على فئة معينة؟

لا أرى حالياً أي شيء في واجهة برمجة التطبيقات (API) يخبرني بما إذا كان المستخدم مشرفاً على فئة معينة. أتوقع أن يكون ذلك موجوداً في استدعاء GET لاسترجاع فئة، لكنني لا أرى مثل هذا الاستدعاء هناك. أو ربما في استدعاء GET لاسترجاع مستخدم، لكنني هناك لا أرى قائمة بالفئات التي يكون فيها المستخدم مشرفاً.

هل يمكنني إضافة هذه البيانات؟

أو، بديلاً عن ذلك، ربما يمكنني إنشاء حقل مخصص (custom_field) للمستخدم أو للفئة لتحديد حالة المشرف، ثم إجراء استدعاء لواجهة برمجة التطبيقات (API) لذلك الحقل المخصص عند تحميل صفحة الفئة.

2. إذا كان المستخدم مشرفاً، اعرض زر التثبيت.

إذا تمكنت من الإجابة على السؤال (1)، فأفترض أنه يمكنني ببساطة إضافة هذا الزر باستخدام جافا سكريبت و CSS في الواجهة الأمامية، وإظهاره إذا كان المستخدم مشرفاً.

3. ينقر المستخدم (المشرف) على الزر، مما يؤدي إلى تثبيت الموضوع

في واجهة برمجة التطبيقات (API)، يبدو أن المواضيع تمتلك سمة “مُثبت” (pinned) (قيمة منطقية boolean). أفترض أن هذا يتوافق مع ما إذا كانت مثبتة في فئتها، حيث يبدو أن كل موضوع ينتمي إلى فئة واحدة فقط.

لذا هنا، عندما يضغط المشرف على زر “تثبيت”، يمكنني على الأرجح تحديث حالة “التثبيت” للموضوع إلى True. إذا لم ينجح ذلك، فقد تكون الحقول المخصصة حلاً هنا أيضاً (على الرغم من أنني لا أرى كيفية إضافة حقول مخصصة إلى موضوع).


لذا، مع هذا، أو شيء مشابه له، يبدو أنه يمكنني إنجاز هذه المهمة باستخدام واجهة برمجة التطبيقات (API) التي، لو قمت بها عبر إضافة، ستتطلب فرزاً كبيراً لملفات قاعدة كود Discourse.

هل يبدو ذلك صحيحاً؟

هل وجدت: كيفية عكس هندسة واجهة برمجة تطبيقات Discourse

لقد رأيت ذلك من قبل، لكنني أراجع الأمر الآن عن كثب. شكراً لتذكيرك.

أحاول فرز الأمور التالية:
–أين في واجهة برمجة التطبيقات (API) يمكنني الحصول على معلومات حول من هو مشرف فئة معينة (لا أرى ذلك في المعلومات المسترجعة حول الفئات أو المستخدمين—من الواضح أنه يجب أن يكون في مكان ما)

–هل يمكن استخدام واجهة برمجة التطبيقات لإضافة حقول جديدة إلى إدخال؟ على سبيل المثال، هل يمكنني استخدام واجهة برمجة التطبيقات لإضافة حقل مخصص إلى موضوع؟ (لا أعتقد أن المواضيع تأتي عادةً مع حقول مخصصة)

سأبحث في قاعدة البيانات.

تحتوي قاعدة البيانات على جدول posts ويمكنك إضافة حقل هناك، ولكن حينها ستخرج عن نطاق الدعم ولن تحصل على أي مساعدة.

شكرًا لك—كل هذا مفيد جدًا.

أي قاعدة بيانات تقصد؟


لتأكيد ذلك، فكرتي هنا هي استخدام واجهة برمجة التطبيقات (API) للقيام ببعض هذه المهام التي قد تتطلب عادةً إضافة (plugin). على سبيل المثال، موقعي نفسه سيقوم باستدعاء واجهة برمجة التطبيقات لتحديد ما إذا كان المستخدم (أو المجموعة، حسب الحالة) مشرفًا على فئة معينة. سيكون هذا الاستدعاء مدمجًا في لوحة التخصيص، أو في سمة (theme) أو إضافة.

لإجراء مثل هذه الاستدعاءات، سأحتاج إلى المصادقة، وهو ما أعتقد أنه يتطلب إنشاء مفتاح واجهة برمجة التطبيقات من لوحة الإدارة. تتطلب عملية إنشاء مفتاح واجهة برمجة التطبيقات في لوحة الإدارة “وصفًا” و"مستوى مستخدم"—لست متأكدًا من كيفية تطبيق ذلك هنا. في حالتي، أريد أن يقوم تطبيقي بإجراء استدعاء واجهة برمجة التطبيقات. لا يتم إجراؤه لمستخدم معين. لذا قد أكون أسيء الفهم.

هل تعرف ما هو مستوى المستخدم المناسب لمثل هذا النوع من استدعاءات واجهة برمجة التطبيقات أو ما الذي يجب إدخاله هناك؟

عند تثبيت Discourse وفقًا لـ الإرشادات القياسية، يتم تشغيله داخل حاوية Docker. يتم تحقيق استمرارية البيانات باستخدام قاعدة بيانات PostgreSQL.

انظر: إضافة مستكشف البيانات

إذا كانت لديك صلاحيات المسؤول، يمكنك الحصول على إحدى النسخ الاحتياطية، وهي ملف SQL مضغوط داخل ملف tar.gz. يمكنك استخدام SQL لإعادة تحميل البيانات إلى قاعدة بيانات PostgreSQL أخرى والقيام بمزيد من الأمور.

لم قمت أبدًا بإنشاء سمة (Theme) أو إضافة (Plugin)، ولم أستخدم واجهة برمجة التطبيقات من Ruby أو بأي لغة برمجة أخرى. لدي حق الوصول كمسؤول على موقع إنتاجي، وهو ما مكّنني من الوصول إلى النسخة الاحتياطية. كما أنني أبرمج باستخدام Prolog، وهو ما أستخدمه لـ الوصول إلى البيانات واستخدامها في تحليل المنشورات باستخدام DCGs. إذا كنت تعرف BNF، فإن DCGs ليست بعيدة عنها كثيرًا، لكنك تحتاج إلى فهم التوحيد النحوي (syntactic unification) والربط الخلفي (backward chaining) للأجزاء الأكثر تعقيدًا.

شكرًا لك. سأتعامل مع هذا البند بشكل منفصل.

لا، لأن حقوق الوصول لكل إجراء تُفرض من قبل الخادم.

ستحتاج إلى النظر في تجاوز الدوال في lib/guardian/ و lib/guardian.rb للسماح للمشرفين المحددين حسب الفئة بتثبيت المواضيع، ثم استخدام نفس الآليات المستخدمة في JavaScript للموضوعات (إلا أنها ستكون داخل الإضافة) لتغيير واجهة المستخدم بحيث تظهر خيار “تثبيت الموضوع” عند اللزوم.

أهـ، هذا منطقي. إذن يبدو أن استخدام الـ API لهذا الغرض لن ينجح (ردك قد يوفر عليّ الكثير من الوقت).

ربما أجرب ذلك بطريقة تختلف قليلاً عن سلوك التثبيت المعتاد. بدلاً من ذلك، سأمنح مستخدمين معينين حقوق “ملكية” لفئة معينة، مما يسمح لهم بإبراز مواضيع محددة في تلك الفئة.

الطريقة التي سأستخدمها مع واجهة برمجة التطبيقات JSON هي: من لوحة التحكم المخصصة، سأمنح المستخدمين المعنيين حقلاً مخصصاً (مثل category-name: owner) أو ما شابه ذلك. ثم، عند تحميل صفحة الفئة، سأستدعي الـ API لتقييم المستخدم؛ فإذا كان يملك ذلك الحقل المخصص، سأظهر زر “الإبراز”، وعند الضغط على زر “الإبراز” لموضوع معين، سأخصص هذا الموضوع لمجموعة الإبراز الخاصة بالفئة (وهو أيضاً حقل مخصص لتلك الفئة).

هذه مسودة أولية - لا داعي للتحقق خطوة بخطوة من هذه الخطوات (عندما أكتب الكود قد أحتاج إلى تعديلها على أي حال). لكن سؤالي الآن: هل يمكن أن تعمل هذه الطريقة في استخدام واجهة برمجة التطبيقات JSON - خاصة لإنشاء واسترجاع الحقول المخصصة من داخل تطبيق Discourse الخاص بي؟

عزيزي @JQ331،

لكن سؤالي في الوقت الحالي: هل يمكن أن تعمل هذه الطريقة في استخدام واجهة برمجة تطبيقات JSON – خاصةً لإنشاء حقول مخصصة واسترجاعها من داخل تطبيق Discourse الخاص بي؟

تلقّيت ردودًا ممتازة على أسئلتك حول الفروقات بين مكون السمة (theme component) والإضافة (plugin) وواجهة برمجة تطبيقات Discourse، وأعتقد أنني لا أستطيع إضافة الكثير من القيمة؛ ولكن إليك طريقة أخرى للنظر في الأمر، قد تجدها مفيدة أو لا:

تستخدم كل من مكونات السمة والإضافات خطافات القوالب (plugin hooks) لتقييم الكود ضمن دورة حياة Ember.js (وهو أمر يستحق التعلم، بالمناسبة).

بالإضافة إلى ذلك، فإن واجهة برمجة تطبيقات Discourse متاحة أيضًا لكل من مكونات السمة والإضافات.

تعرض واجهة برمجة التطبيقات بشكل أساسي مجموعة فرعية من البيانات الموجودة في قاعدة بيانات PostgreSQL الأساسية، وليس جميعها.

عند تطوير وظيفة جديدة، من الجيد البدء بواجهة برمجة التطبيقات والتحقق مما إذا كانت البيانات التي تحتاجها متاحة من خلالها.

إذا كانت هناك بعض البيانات التي تحتاجها غير موجودة في واجهة برمجة التطبيقات، فيجب عليك فحص قاعدة بيانات PostgreSQL لمعرفة ما إذا كانت هذه البيانات موجودة فيها.

إذا كانت البيانات الإضافية التي تحتاجها موجودة في قاعدة البيانات، فعليك حينها تعريض هذه البيانات، وعادةً ما يعني ذلك إضافة بيانات إلى مُسلسل بيانات Discourse وتوسيع واجهة برمجة التطبيقات.

مُسلسل البيانات هو ببساطة عملية إنشاء كائن JSON الذي يتم تعريضه عبر واجهة برمجة التطبيقات. ويمكن توسيعه لإضافة كائنات أكثر.

لفحص قاعدة بيانات PostgreSQL، أفترض أن هناك طرقًا عديدة للقيام بذلك (مثل قراءة الكود على GitHub)، لكنني أقوم بذلك بتسجيل الدخول إلى قاعدة البيانات مباشرةً ومراجعة الجداول فيها، ودراسة هيكل هذه الجداول والحقول الموجودة في كل جدول (باستخدام SQL الأساسي).

لذا، وبشكل موجز (وللحفاظ على الإيجاز)، نحتاج إلى وجود كل من واجهة برمجة التطبيقات وجداول قاعدة البيانات كمرجع، وعمومًا، عندما ترغب في “توسيع واجهة برمجة التطبيقات” بإضافة بيانات غير متوفرة فيها افتراضيًا (OOTB)، فإننا ننشئ إضافة (plugin) لذلك؛ وبذلك يتم تعريض هذه البيانات مع واجهة برمجة التطبيقات (الموسعة)، ويمكننا بعد ذلك استخدام هذه البيانات في كل من مكونات السمة والإضافات.

أتمنى أن تكون هذه النظرة قد كانت مفيدة أو ذات فائدة ما، ولو بشكل بسيط.

شرح رائع. شكرًا جزيلاً على هذه الإجابة. لقد سلطت الضوء على شيء لم أكن أدركه من قبل:

من هذه الردود، أستنتج (كما تقول) أن التفاعل مع واجهة برمجة التطبيقات (JSON API) يمكن أن يكون نقطة انطلاق جيدة في العديد من الحالات، مما قد يغني عن الحاجة إلى برمجة سمة (theme) أو إضافة (plugin) جديدة. لكن هناك أنواعًا معينة من البيانات لا يتم كشفها عبر الـ API. وللاستفادة من هذه الأنواع من البيانات والتعامل معها، ستحتاج إلى استخدام مُسلسل بيانات Discourse لكشفها؛ ولتنفيذ هذا التسلسل، ستحتاج إلى استخدام إضافة.

يبدو أن أحد الأمثلة الجيدة على البيانات غير المتاحة عبر الـ API هو مالكي المجموعة. أقول ذلك لأنه (بخصوص الوصول إلى مالكي المجموعة):

نقطة إرباك واحدة: في واجهة برمجة تطبيقات Discourse، عندما تحصل على مجموعة معينة، تُدرج إحدى السمات المُرجعة باسم "is_group_owner": true، لذا لا أعرف ما المقصود بذلك…

لكن يبدو أنه للحصول على مالك المجموعة، سأحتاج إلى تسلسل سمة مالك المجموعة.


هل توجد أمثلة جيدة على استخدام مُسلسل بيانات Discourse؟ لقد رأيت هذا، لكن نظرًا لأهميته، فإن دليلًا تعليميًا مع بعض الأمثلة سيكون مفيدًا للغاية.

أقرب مثال لدي هو:

هذا مفيد، لكنه ليس دقيقًا تمامًا (على الأقل يعطيني أخطاءً تقول “إضافة غير صالحة”). لست متأكدًا من كيفية تعديله بحيث يمكنني الوصول إلى مالكي المجموعة لكل مجموعة في صفحة فهرس المجموعات.

لا أعرف كيف حاولت استخدام مثال الإضافة، لكنني نجحت في تشغيلها على بيئة تطوير عند استخدام هيكل الملفات والكود الذي نشرته في موضوعك الآخر.

يُستخدم is_group_owner في سياق عرض المستخدم الحالي للمجموعات.

يمكنك الحصول على معلومات المالك عبر استدعاء Ajax، لكن حسب علمي سيتعين تنفيذ ذلك لكل مجموعة على حدة في القائمة، مما قد يؤدي إلى عدد كبير من الطلبات. أعتقد أن ذلك بشكل عام سيكون تحديًا لجعله يعمل باستخدام هذه الطريقة. على أي حال، إذا كنت ترغب في التجربة، يمكنك تجربة مقتطف كود إثبات المفهوم التالي في سمة (Theme). فقط استبدل GROUP_NAME باسم إحدى مجموعتك. (تحرير: إليك أيضًا مكون سمة يحتوي على مثال حول كيفية استخدام استدعاء Ajax: discourse-featured-topics/common/head_tag.html at master · awesomerobot/discourse-featured-topics · GitHub)

<script type="text/discourse-plugin" version="0.8.40">
  const { ajax } = require("discourse/lib/ajax");
  ajax(`/groups/GROUP_NAME/members.json`).then(response => {
    console.log(response.owners.map(owner => owner.username))
  });
</script>

مع كل ما سبق، فإن استخدام إضافة سيكون بالتأكيد الحل الأسهل والأكثر نظافة.