Moodle

معمارية تطبيقات المصادر المفتوحة (المجلد الثاني)Moodle

معمارية تطبيقات المصادر المفتوحة (المجلد الثاني) Moodle

Tim Hunt

إذا استمتعت بهذه الكتب، فقد يعجبك أيضًا التصميم البرمجي بالأمثلة في بايثون (Software Design by Example in Python) والتصميم البرمجي بالأمثلة في جافاسكربت (Software Design by Example in JavaScript).

Moodle هو تطبيق ويب يُستخدم في البيئات التعليمية. وبينما يحاول هذا الفصل تقديم نظرة عامة على جميع جوانب كيفية عمل Moodle، فإن تركيزه يقع على المجالات التي يكون فيها تصميم Moodle مثيرًا للاهتمام بصفة خاصة:

  • طريقة تقسيم التطبيق إلى إضافات (plugins)؛
  • نظام الصلاحيات، الذي يتحكّم في أي المستخدمين يمكنهم تنفيذ أي الإجراءات في مختلف أنحاء النظام؛
  • طريقة توليد المخرجات، بحيث يمكن استخدام سمات (themes) مختلفة لإعطاء مظاهر مختلفة، وبذلك يمكن تعريب الواجهة.
  • طبقة تجريد قاعدة البيانات (database abstraction layer).

يوفّر Moodle مكانًا على الشبكة يلتقي فيه الطلاب والمعلّمون للتعليم والتعلّم. يُقسَّم موقع Moodle إلى مقرّرات (courses). ولكل مقرر مستخدمون (users) مسجَّلون فيه بأدوار مختلفة، مثل طالب (Student) أو معلّم (Teacher). ويضمّ كل مقرر عددًا من الموارد (resources) والأنشطة (activities). قد يكون المورد ملف PDF أو صفحة HTML داخل Moodle أو رابطًا إلى شيء آخر على الويب. وقد يكون النشاط منتدى (forum) أو اختبارًا (quiz) ويكي (wiki). وداخل المقرر تُنظَّم هذه الموارد والأنشطة بطريقة ما؛ فمثلًا قد تُجمَّع في مواضيع منطقية، أو في أسابيع على تقويم.

الشكل 13.1: مقرر في Moodle

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

الشكل 13.2: معمارية أنظمة جامعة نموذجية

  • مزوّد مصادقة/هوية (مثل LDAP) للتحكّم في حسابات المستخدمين عبر جميع أنظمتك.
  • نظام معلومات الطلاب؛ أي قاعدة بيانات تضمّ جميع طلابك، وبرنامج الدراسة الذي يلتحقون به، وبالتالي المقرّرات التي عليهم إكمالها؛ وسجلّهم الأكاديمي (transcript)—ملخّص عام لنتائج المقرّرات التي أتمّوها. كما يتولّى هذا النظام الوظائف الإدارية الأخرى، مثل تتبّع ما إذا كان قد سدّدوا الرسوم الجامعية.
  • مستودع مستندات (مثل Alfresco) لتخزين الملفات وتتبّع سير العمل عندما يتعاون المستخدمون على إنشاء الملفات.
  • ملف إنجاز إلكتروني (ePortfolio)؛ وهو مكان يجمّع فيه الطلاب الأصول والمواد، إمّا لبناء سيرة ذاتية (CV)، أو لتقديم دليل على استيفائهم متطلّبات مقرر قائم على التطبيق العملي.
  • أداة تقارير أو تحليلات (analytics) لتوليد معلومات عامة عمّا يجري في مؤسستك.

يركّز Moodle على توفير مساحة على الشبكة للتعليم والتعلّم، لا على أيٍّ من الأنظمة الأخرى التي قد تحتاجها مؤسسة تعليمية. ويوفّر Moodle تنفيذًا أساسيًا للوظائف الأخرى، بحيث يعمل إمّا كنظام مستقل أو مدمجًا مع أنظمة أخرى. أما الدور الذي يؤديه Moodle فيُعرف عادةً باسم بيئة التعلّم الافتراضية (virtual learning environment، VLE)، أو نظام إدارة التعلّم أو المقرّرات (learning or course management system، أي LMS أو CMS أو حتى LCMS).

Moodle برمجية مفتوحة المصدر أو برمجية حرة (GPL). وهي مكتوبة بـ PHP. وستعمل على معظم خوادم الويب الشائعة وعلى المنصّات الشائعة. وهي تتطلّب قاعدة بيانات، وتعمل مع MySQL أو PostgreSQL أو Microsoft SQL Server أو Oracle.

بدأ مشروع Moodle على يد Martin Dougiamas عام 1999، بينما كان يعمل في جامعة Curtin في أستراليا. صدر الإصدار 1.0 عام 2002، وفي ذلك الوقت كانت التقنيات المتاحة هي PHP4.2 و MySQL 3.23. وقد حدّ ذلك نوع المعمارية الممكنة في البداية، لكن الكثير قد تغيّر منذ ذلك الحين. والإصدار الحالي هو سلسلة Moodle 2.2.x.

13.1. نظرة عامة على كيفية عمل Moodle

يتألّف تثبيت Moodle من ثلاثة أجزاء:

  1. الشيفرة، عادةً في مجلد مثل /var/www/moodle أو ~/htdocs/moodle. ويجب ألّا يكون هذا المجلد قابلًا للكتابة من قِبل خادم الويب.
  2. قاعدة البيانات، التي يديرها أحد أنظمة إدارة قواعد البيانات المدعومة (RDMSs). وفي الواقع، يضيف Moodle بادئةً (prefix) إلى جميع أسماء الجداول، لذا يمكنه مشاركة قاعدة البيانات مع تطبيقات أخرى عند الحاجة.
  3. المجلد moodledata. وهو المجلد الذي يخزّن فيه Moodle الملفات المرفوعة والمُولَّدة، لذا يجب أن يكون قابلًا للكتابة من قِبل خادم الويب. ولأسباب أمنية، ينبغي أن يكون خارج جذر الويب (web root).

يمكن أن تكون هذه الأجزاء الثلاثة على خادم واحد. وبدلًا من ذلك، في إعداد موزون على الحِمل (load-balanced)، توجد نسخ متعددة من الشيفرة على كل خادم ويب، لكن توجد نسخة واحدة مشتركة فقط من قاعدة البيانات و moodledata، على خوادم أخرى غالبًا.

تُخزَّن معلومات الإعداد الخاصة بهذه الأجزاء الثلاثة في ملف اسمه config.php في جذر المجلد moodle عند تثبيت Moodle.

توجيه الطلبات

Moodle تطبيق ويب، لذا يتفاعل معه المستخدمون عبر متصفّح الويب ومن منظور Moodle يعني ذلك الاستجابة لطلبات HTTP. ومن ثم فإن أحد جوانب تصميم Moodle المهمة هو نطاق أسماء الروابط (URL namespace)، وكيف تُوجَّه الروابط إلى السكربتات المختلفة.

يستخدم Moodle الأسلوب القياسي في PHP لهذا الغرض. فلمشاهدة الصفحة الرئيسية لمقرر، يكون الرابط .../course/view.php?id=123، حيث 123 هو المعرّف الفريد للمقرر في قاعدة البيانات. ولمشاهدة نقاش في منتدى، يكون الرابط شيئًا مثل .../mod/forum/discuss.php?id=456789. أي أن هذين السكربتين بعينهما، course/view.php أو mod/forum/discuss.php، هما اللتان تتعاملان مع هذين الطلبين.

الأمر بسيط بالنسبة إلى المطوِّر. إذ لفهم كيفية تعامل Moodle مع طلب بعينه، تنظر إلى الرابط وتبدأ بقراءة الشيفرة من هناك. لكنه قبيح من وجهة نظر المستخدم. غير أن هذه الروابط دائمة. فهي لا تتغيّر إذا أُعيدت تسمية المقرر، أو إذا نقل مشرف نقاشًا إلى منتدى آخر. (وهذه خاصية جيّدة تُعدّ من مزايا الروابط، كما يشرحه مقال Tim Berners-Lee عناوين URI الجيّدة لا تتغيّر.)

أما الأسلوب البديل الذي يمكن اتّباعه فهو اعتماد نقطة دخول واحدة …/index.php/[extra-information-to-make-the-request-unique]، حيث يتولّى السكربت الواحد index.php توجيه الطلبات بطريقة ما. يضيف هذا الأسلوب طبقة تجريد غير مباشرة (indirection)، وهو شيء يحبّه مطوّرو البرمجيات دائمًا. ويبدو أن غياب هذه الطبقة غير المباشرة لا يضرّ Moodle.

الإضافات

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

هناك طرائق عدّة يمكن بها بناء نظام كأنّه نواة محاطة بإضافات. ولدى Moodle نواة سميكة نسبيًا (fat core)، والإضافات فيه مُنمَّطة الأنواع بقوة (strongly-typed). وحين أقول «نواة سميكة» فإنني أعني أن في النواة قدرًا كبيرًا من الوظائف. ويقابل ذلك ذلك النوع من المعمارية الذي يكون فيه كل شيء تقريبًا — باستثناء شفرة صغيرة محمِّلة للإضافات — إضافةً.

وحين أقول إن الإضافات مُنمَّطة الأنواع بقوة فأعني أنك، تبعًا لنوع الوظيفة التي تريد تنفيذها، مضطر لكتابة نوع مختلف من الإضافات، ولتنفيذ واجهة برمجية مختلفة. فمثلًا، تكون إضافة وحدة نشاط جديدة مختلفة كلّيًا عن إضافة مصادقة جديدة أو نوع سؤال جديد. وفي آخر إحصاء ثمّة نحو 35 نوعًا مختلفًا من الإضافات. (هناك قائمة كاملة بأنواع إضافات Moodle.) ويقابل ذلك ذلك النوع من المعمارية الذي تستخدم فيه جميع الإضافات الواجهة البرمجية نفسها في الجوهر، ثم تشترك —ربما— في المجموعة الجزئية من الخطّافات (hooks) أو الأحداث التي تهمّها.

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

تأخذ الإضافة في Moodle شكل مجلد يحتوي على ملفات. للإضافة نوع واسم، يُشكّلان معًا اسم المكوّن بصيغة «Frankenstyle». (ونشأ مصطلح «Frankenstyle» من جدل في قناة Jabber الخاصة بالمطوّرين، لكنّ الجميع أحبّه فبقي.) يحدّد نوع الإضافة واسمها المسار إلى مجلد الإضافة. فنوع الإضافة يعطي البادئة، واسم المجلد هو اسم الإضافة. وفيما يلي بعض الأمثلة:

نوع الإضافة اسم الإضافة Frankenstyle المجلد
mod (وحدة نشاط) forum mod_forum mod/forum
mod (وحدة نشاط) quiz mod_quiz mod/quiz
block (كتلة جانبية) navigation block_navigation blocks/navigation
qtype (نوع سؤال) shortanswer qtype_shortanswer question/type/shortanswer
quiz (تقرير اختبار) statistics quiz_statistics mod/quiz/report/statistics

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

إضافة نموذجية

سأشرح الكثير من تفاصيل معمارية Moodle بالاستعانة بإضافة بعينها. وكما جرت العادة، اخترت أن أنفّذ إضافة تطبع «Hello world».

هذه الإضافة لا تنتمي طبيعيًا إلى أيٍّ من أنواع إضافات Moodle المعيارية. فهي مجرّد سكربت، بلا أي صلة بشيء آخر، لذا سأقرّر تنفيذها كإضافة «local». وهذا نوع إضافات شامل (catch-all) مخصَّص للوظائف المتفرّقة التي لا تنتمي إلى أي مكان أفضل. وسأسمّي إضافتي greet، فيكون لها اسم Frankenstyle هو local_greet، ومسار مجلد هو local/greet. (شيفرة الإضافة يمكن تنزيلها.)

يجب أن تحتوي كل إضافة على ملف اسمه version.php يعرّف بعض البيانات الوصفية الأساسية عن الإضافة. ويستخدم هذا الملف نظام تثبيت الإضافات في Moodle لتثبيت الإضافة وترقيتها. فمثلًا، يحتوي local/greet/version.php على:

<?php
$plugin->component    = 'local_greet';
$plugin->version      = 2011102900;
$plugin->requires     = 2011102700;
$plugin->maturity     = MATURITY_STABLE;

قد يبدو تكرار اسم المكوّن زائدًا عن الحاجة، إذ يمكن استنتاجه من المسار، لكن المُثبِّت يستخدمه للتحقّق من أنّ الإضافة ثُبِّتت في المكان الصحيح. وحقل version هو إصدار هذه الإضافة. وmaturity إحدى القيم ALPHA أو BETA أو RC (مرشّح إصدار) أو STABLE. وrequires هو أدنى إصدار من Moodle متوافق مع هذه الإضافة. ويمكن، إن لزم الأمر، توثيق الإضافات الأخرى التي تعتمد عليها هذه الإضافة.

وفيما يلي السكربت الرئيسي لهذه الإضافة البسيطة (المخزَّن في local/greet/index.php):

<?php
require_once(dirname(__FILE__) . '/../../config.php');        // 1

require_login();                                              // 2
$context = context_system::instance();                        // 3
require_capability('local/greet:begreeted', $context);        // 4

$name = optional_param('name', '', PARAM_TEXT);               // 5
if (!$name) {
    $name = fullname($USER);                                  // 6
}

add_to_log(SITEID, 'local_greet', 'begreeted',
        'local/greet/index.php?name=' . urlencode($name));    // 7

$PAGE->set_context($context);                                 // 8
$PAGE->set_url(new moodle_url('/local/greet/index.php'),
        array('name' => $name));                              // 9
$PAGE->set_title(get_string('welcome', 'local_greet'));       // 10

echo $OUTPUT->header();                                       // 11
echo $OUTPUT->box(get_string('greet', 'local_greet',
        format_string($name)));                               // 12
echo $OUTPUT->footer();                                       // 13

السطر 1: إقلاع Moodle

require_once(dirname(__FILE__) . '/../../config.php');        // 1

السطر الوحيد في هذا السكربت الذي يقوم بأكبر قدر من العمل هو السطر الأول. فقد أوضحت أعلاه أنّ config.php يحتوي التفاصيل التي يحتاجها Moodle للاتصال بقاعدة البيانات والعثور على مجلد moodledata. غير أنّه ينتهي بالسطر require_once('lib/setup.php')، وهذا السطر:

  1. يحمّل جميع مكتبات Moodle القياسية باستخدام require_once؛
  2. يبدأ معالجة الجلسة (session)؛
  3. يتصل بقاعدة البيانات؛ و
  4. يهيّئ عددًا من المتغيّرات العامة التي سنلتقي بها لاحقًا.

السطر 2: التحقّق من أنّ المستخدم مسجَّل الدخول

require_login();                                              // 2

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

أما السكربت المدمج أكثر في Moodle فسيسمِّر وسائط إضافية هنا ليبيّن أي مقرر أو نشاط تنتمي إليه هذه الصفحة، ثم يتحقّق require_login أيضًا من أنّ المستخدم مسجَّل في هذا المقرر أو مسموح له بالوصول إليه بغير ذلك، وأنّه مسموح له برؤية هذا النشاط. وإن لم يكن الأمر كذلك، فسيُعرض خطأ مناسب.

13.2. نظام الأدوار والصلاحيات في Moodle

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

السطر 3: الحصول على السياق

$context = context_system::instance();                        // 3

في Moodle يمكن أن تكون للمستخدمين صلاحيات مختلفة في أماكن مختلفة. فمثلًا قد يكون المستخدم معلّمًا في مقرر وطالبًا في مقرر آخر، وبالتالي تكون له صلاحيات مختلفة في كل موضع. وتُسمّى هذه المواضع سياقات (contexts). أمّا سياقات Moodle فتشكّل تسلسلًا هرميًا (hierarchy) أشبه ما يكون بتسلسل مجلدات في نظام ملفات. وفي المستوى الأعلى يقع سياق النظام (System context)، وبما أنّ هذا السكربت غير مدمج كثيرًا في Moodle، فإنه يستخدم ذلك السياق.

داخل سياق النظام توجد عدّة سياقات للفئات المختلفة التي أُنشئت لتنظيم المقرّرات. ويمكن أن تكون هذه متداخلة، بحيث تحتوي فئةٌ على فئات أخرى. كما يمكن أن تحتوي سياقات الفئات على سياقات مقرّرات. وأخيرًا، لكل نشاط داخل مقرر سياق وحدة خاص به (Module context).

الشكل 13.3: السياقات

السطر 4: التحقّق من أنّ المستخدم يملك صلاحية استخدام هذا السكربت

require_capability('local/greet:begreeted', $context);        // 4

بعد الحصول على السياق — أي المنطقة المعنية من Moodle — يمكن التحقّق من الصلاحية. وتُسمّى كل وحدة وظيفية قد يملك المستخدم صلاحيتها أو لا يملكها قدرة (capability). ويوفّر التحقّق من قدرة تحكّمًا في الوصول أدقّ (finer-grained) من الفحوص الأساسية التي يجريها require_login. ولدى إضافتنا النموذجية البسيطة قدرة واحدة فقط: local/greet:begreeted.

يتمّ الفحص باستخدام الدالة require_capability التي تأخذ اسم القدرة والسياق. ومثل دوال require_&hellip; الأخرى، فلن تعود هذه الدالة إن لم تكن لدى المستخدم تلك القدرة، بل ستعرض خطأً بدلًا من ذلك. وفي مواضع أخرى تُستخدَم الدالة غير القاتلة has_capability التي تعيد قيمة منطقية (Boolean)، مثلًا لتحديد ما إذا كان ينبغي عرض رابط إلى هذا السكربت من صفحة أخرى.

كيف يضبط المدير أيّ المستخدمين يملك أيّ صلاحية؟ وفيما يلي الحساب الذي تجريه has_capability (على الأقل من حيث المفهوم):

  1. ابدأ من السياق الحالي.
  2. احصل على قائمة الأدوار التي يملكها المستخدم في هذا السياق.
  3. ثم استخرج ما هي صلاحية كل دور في هذا السياق.
  4. اجمع تلك الصلاحيات للحصول على جواب نهائي.

تعريف القدرات

كما يبيّن المثال، تستطيع الإضافة أن تعرّف قدرات جديدة تتعلّق بالوظيفة التي تقدّمها على وجه التحديد. وداخل كل إضافة في Moodle يوجد مجلد فرعي من الشيفرة اسمه db، ويحتوي على جميع المعلومات اللازمة لتثبيت الإضافة أو ترقيتها. ومن تلك المعلومات ملف اسمه access.php يعرّف القدرات. وفيما يلي ملف access.php الخاص بإضافتنا، وهو موجود في local/greet/db/access.php:

<?php
$capabilities = array('local/greet:begreeted' => array(
    'captype' => 'read',
    'contextlevel' => CONTEXT_SYSTEM,
    'archetypes' => array('guest' => CAP_ALLOW, 'user' => CAP_ALLOW)
));

وهذا يعطي بعض البيانات الوصفية عن كل قدرة تُستخدَم عند بناء واجهة المستخدم الخاصة بإدارة الصلاحيات. كما أنه يعطي صلاحيات افتراضية لأنواع الأدوار الشائعة.

الأدوار

الجزء التالي من نظام صلاحيات Moodle هو الأدوار. والدور (role) ليس سوى مجموعة صلاحيات مسمّاة. وحين تسجّل الدخول إلى Moodle، يكون لديك دور «المستخدم الموثَّق» (Authenticated user) في سياق النظام، وبما أنّ سياق النظام هو جذر التسلسل الهرمي، فإنّ ذلك الدور يسري في كل مكان.

داخل مقرر بعينه قد تكون طالبًا، وسيُطبَّق ذلك الإسناد للدور في سياق المقرر وفي كل سياقات الوحدات بداخله. غير أنّك في مقرر آخر قد يكون لك دور مختلف. فمثلًا قد يكون السيد Gradgrind معلّمًا في مقرر «Facts, Facts, Facts»، لكنّه طالب في مقرر التطوير المهني «Facts Aren't Everything». وأخيرًا، قد يُمنح المستخدم دور الإشراف (Moderator) في منتدى بعينه (سياق وحدة).

الصلاحيات

يعرّف كل دور صلاحية (permission) لكل قدرة. فمثلًا سيمنح دور المعلّم على الأرجح صلاحية ALLOW لـ moodle/course:manage، في حين لا يمنحها دور الطالب. لكنّ الطالب والمعلّم معًا يتيحان mod/forum:startdiscussion.

تُعرَّف الأدوار عادةً على المستوى العام، لكن يمكن إعادة تعريفها في كل سياق. فمثلًا يمكن جعل ويكي ما بعينه للقراءة فقط أمام الطلاب عبر تجاوز صلاحية قدرة mod/wiki:edit لدور الطالب في سياق تلك الويكي (سياق الوحدة) وتعيينها إلى PREVENT.

ثَمّة أربع صلاحيات:

  • NOT SET/INHERIT (الافتراضية)
  • ALLOW
  • PREVENT
  • PROHIBIT

في سياق بعينه، يملك كل دور إحدى هذه الصلاحيات الأربع لكل قدرة. ومن الفروق بين PROHIBIT و PREVENT أنّ PROHIBIT لا يمكن تجاوزها في السياقات الفرعية.

تجميع الصلاحيات

أخيرًا، تُجمَّع الصلاحيات لكل الأدوار التي يملكها المستخدم في هذا السياق.

  • إن منح أي دور صلاحية PROHIBIT لهذه القدرة، أعِد false.
  • وإلّا، إن منح أي دور صلاحية ALLOW لهذه القدرة، أعِد true.
  • وإلّا أعِد false.

ومثال على استخدام PROHIBIT هو التالي: لنفترض أنّ مستخدمًا ما كان ينشر مشاركات مسيئة في عدد من المنتديات، ونريد إيقافه فورًا. يمكننا إنشاء دور مستخدم «مشاغب» (Naughty) يضيف قدرات مثل mod/forum:post إلى PROHIBIT. ثم نُسند هذا الدور إلى ذلك المستخدم المسيء في سياق النظام. وبهذه الطريقة نتيقّن من أنّ المستخدم لن يستطيع النشر في أي منتدى بعد الآن. (ثم نتحدّث مع الطالب، وبعد الوصول إلى نتيجة مُرضية نزيل ذلك الإسناد من الدور كي يتمكّن من استعمال النظام من جديد.)

إذن، يمنح نظام صلاحيات Moodle المديرين قدرًا هائلًا من المرونة. فبإمكانهم تعريف الأدوار التي يريدونها كيفما شاءوا بصلاحيات مختلفة لكل قدرة؛ وبإمكانهم تعديل تعريفات الأدوار في السياقات الفرعية؛ ثم إسناد أدوار مختلفة للمستخدمين في سياقات مختلفة.

13.3. العودة إلى سكربتنا النموذجي

يوضّح الجزء التالي من السكربت بضع نقاط متفرّقة:

السطر 5: جلب البيانات من الطلب

$name = optional_param('name', '', PARAM_TEXT);               // 5

ومن بين ما يجب على كل تطبيق ويب أداؤه جلب البيانات من الطلب (متغيّرات GET أو POST) دون أن يكون عرضة لهجمات حقن SQL (SQL injection) أو البرمجة النصّية العابرة للمواقع (cross-site scripting). ويوفّر Moodle طريقتين للقيام بذلك.

الطريقة البسيطة هي المعروضة هنا. فهي تجلب متغيّرًا واحدًا بناءً على اسم الوسيط (وهو name هنا) وقيمة افتراضية والنوع المتوقَّع. ويُستخدَم النوع المتوقَّع لتنظيف المُدخل من جميع المحارف غير المتوقّعة. وهناك أنواع عديدة مثل PARAM_INT و PARAM_ALPHANUM و PARAM_EMAIL وغيرها.

وثمّة أيضًا دالة مماثلة اسمها required_param، وهي مثل دوال require_&hellip; الأخرى توقف التنفيذ وتعرض رسالة خطأ إن لم يُعثر على الوسيط المتوقَّع.

أما الآلية الأخرى التي يملكها Moodle لجلب البيانات من الطلب فهي مكتبة نماذج متكاملة. وهي غلاف حول مكتبة HTML QuickForm من PEAR. (وللمبرمجين غير المتخصّصين في PHP، تُعدّ PEAR ما يقابل CPAN في PHP.) بدا هذا خيارًا جيدًا عند اختياره، لكنه لم يعد قيد الصيانة اليوم. وفي وقت ما من المستقبل سيتعذّر علينا إلّا معالجة الانتقال إلى مكتبة نماذج جديدة، وهو ما ينتظره كثير منّا، إذ إنّ QuickForm يعاني عدّة مشكلات تصميمية مزعجة. لكنّها كافية في الوقت الحالي. ويمكن تعريف النماذج كمجموعة حقول من أنواع مختلفة (مثل مربع نص، أو قائمة اختيار منسدلة، أو منتقي تاريخ) مع تحقق في جانب العميل وجانب الخادم (بما في ذلك استخدام الأنواع PARAM_&hellip; ذاتها).

السطر 6: المتغيّرات العامة

if (!$name) {
    $name = fullname($USER);                                  // 6
}

يُظهر هذا المقطع أوّل متغيّر عام توفّره Moodle التي يوفّرها Moodle. فالمتغيّر $USER يتيح الوصول إلى معلومات عن المستخدم الذي يطّلع على هذا السكربت. ومن بين المتغيّرات العامة الأخرى:

  • $CFG: يحتفظ بإعدادات الإعداد المستخدَمة عادةً.
  • $DB: اتصال قاعدة البيانات.
  • $SESSION: غلاف حول جلسة PHP.
  • $COURSE: المقرر الذي يتعلّق به الطلب الحالي.

وغيرها من متغيّرات كثيرة، سنلتقي ببعضها أدناه.

وربما قريتَ عبارة «متغيّر عام» برعب. ولاحظ مع ذلك أنّ PHP يعالج طلبًا واحدًا في كل مرّة. لذلك فهذه المتغيّرات ليست «عالمية» إلى هذا الحدّ. وفي الواقع، يمكن رؤية متغيّرات PHP العامة بوصفها تطبيقًا لنمط سجلّ نطاق الخيط (thread-scoped registry pattern؛ انظر كتاب Martin Fowler أنماط معمارية تطبيقات المؤسسات [Patterns of Enterprise Application Architecture])، وهذه هي الطريقة التي يستعمل بها Moodle هذه المتغيّرات. وهي مريحة جدًّا لأنها تتيح الوصول إلى الكائنات المستخدَمة عادةً في أنحاء الشيفرة كلها، دون الحاجة إلى تمريرها إلى كل دالة وكل طريقة. وسوء استعمالها نادر.

لا شيء بسيط

يخدم هذا السطر أيضًا لطرح نقطة عن مجال المشكلة: لا شيء أبسط أبدًا. فعرض اسم المستخدم أعقد من مجرد دمج $USER->firstname و '~' و $USER->lastname. وقد تنصّ المؤسسة على سياسات تحدّ من إظهار أيّ من جزأي الاسم، ولكل ثقافة أعرافها الخاصة في ترتيب عرض الأسماء. لذلك توجد عدة إعدادات (configurations) ودالة تُجمِّع الاسم الكامل وفق القواعد.

والتواريخ مشكلة مماثلة. فقد يقع المستخدمون المختلفون في المناطق الزمنية المختلفة. يخزّن Moodle جميع التواريخ في صورة ختمات زمنية من نوع Unix (Unix time-stamps) وهي أعداد صحيحة، لذا تعمل في جميع قواعد البيانات. ثم توجد دالة userdate تعرض الختم الزمني للمستخدم وفق إعدادات المنطقة الزمنية والمحلّ الملائمة.

السطر 7: التسجيل

add_to_log(SITEID, 'local_greet', 'begreeted',
        'local/greet/index.php?name=' . urlencode($name));    // 7

جميع الإجراءات الجوهرية في Moodle مسجَّلة. وتُكتب السجلّات في جدول داخل قاعدة البيانات. وهذه مقايضة. فهي تجعل التحليل المتطوّر سهلًا إلى حدٍّ كبير، بل إنّ Moodle يتضمّن تقارير معدّة استنادًا إلى هذه السجلّات. لكنّها في موقع كبير ومزدحم تمثّل مشكلة في الأداء. إذ يكبر جدول السجلّات حجمًا بشكل هائل، مما يجعل نسخ قاعدة البيانات الاحتياطي أصعب، ويجعل الاستعلامات على جدول السجلّات بطيئة. وقد ينشأ كذلك تعارض في الكتابة على جدول السجلّات. ويمكن التخفيف من هذه المشكلات بطرق متعدّدة، مثل تجميع عمليات الكتابة في دفعات (batching)، أو أرشفة السجلّات القديمة أو حذفها لإخراجها من قاعدة البيانات الرئيسة.

13.4. توليد المخرجات

تُعالَج المخرجات أساسًا عبر كائنين عامّين.

السطر 8: المتغيّر العام $PAGE

$PAGE->set_context($context);                                 // 8

يخزّن $PAGE معلومات الصفحة التي ستُخرَج. ثم تصبح هذه المعلومات متاحة بسهولة أمام الشيفرة التي تولّد HTML. ويحتاج هذا السكربت إلى تحديد السياق الحالي صراحةً. (وفي مواقف أخرى، كان يمكن أن يُضبط ذلك تلقائيًا بواسطة require_login.) ويجب أيضًا ضبط رابط هذه الصفحة صراحةً. وقد يبدو ذلك زائدًا عن الحاجة، لكنّ سبب اشتراطه هو أنك قد تصل إلى صفحة بعينها عبر أيّ عدد من الروابط المختلفة، في حين ينبغي أن يكون الرابط الممرَّر إلى set_url هو الرابط القانوني (canonical) لتلك الصفحة—أي رابطًا دائمًا جيّدًا إن شئت. كما يُضبط عنوان الصفحة، وهو ما سيظهر في عنصر head الخاص بـ HTML.

السطر 9: رابط Moodle

$PAGE->set_url(new moodle_url('/local/greet/index.php'),
        array('name' => $name));                              // 9

إنّما أردت فقط أن ألفت الانتباه إلى صنف المساعد الصغير اللطيف الذي يجعل التعامل مع الروابط أسهل بكثير. ولعلّك تذكّر أنّ استدعاء دالة add_to_log أعلاه لم يستعمل صنف المساعد هذا. والحقيقة أنّ واجهة السجلّات البرمجية لا تقبل كائنات moodle_url. وهذا النوع من عدم الاتّساق من العلامات النموذجية على قاعدة شيفرة عريقة مثل قاعدة Moodle.

السطر 10: التعريب (Internationalisation)

$PAGE->set_title(get_string('welcome', 'local_greet'));       // 10

يستخدم Moodle نظامه الخاص كي يسمح بترجمة الواجهة إلى أيّ لغة. وقد تكون هناك اليوم مكتبات تعريب جيدة في PHP، لكنّه في عام 2002 حين نُفِّذ الأمر أوّل مرّة لم تكن هناك مكتبة متاحة وكافية. يقوم النظام على أساس الدالة get_string. وتُحدَّد النصوص بمفتاح وباسم المكوّن بصيغة Frankenstyle. وكما يتبيّن في السطر 12، من الممكن استبدال قيم داخل النص. (وتُعالَج القيم المتعدّدة باستخدام مصفوفات أو كائنات PHP.)

تُبحث النصوص في ملفات لغات ليست سوى مصفوفات PHP عادية. وفيما يلي ملف اللغة local/greet/lang/en/local_greet.php الخاص بإضافتنا:

<?php
$string['greet:begreeted'] = 'Be greeted by the hello world example';
$string['welcome'] = 'Welcome';
$string['greet'] = 'Hello, {$a}!';
$string['pluginname'] = 'Hello world example';

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

تُحدَّد اللغات المختلفة برمز الدولة المكوّن من حرفين (en هنا). وقد تستمدّ حزم اللغات من حزم لغات أخرى. فمثلًا تُعلن حزمة اللغة fr_ca (الفرنسية الكندية) اللغة fr (الفرنسية) لغةً أمًّا، فلا تكتفي بتعريف تلك النصوص التي تختلف عن الفرنسية. وبما أنّ Moodle بدأ في أستراليا، فإنّ en تعني الإنجليزية البريطانية، و en_us (الإنجليزية الأمريكية) مشتقّة منها.

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

ويُدار إنتاج ملفات حزم اللغات وتنسيق جهد الترجمة في http://lang.moodle.org/، وهو Moodle مع إضافة مخصّصة ([local_amos](http://docs.moodle.org/22/en/AMOS)). وهو يستعمل Git وقاعدة البيانات معًا كوحدة تخزينية لحفظ ملفات اللغات مع تاريخ إصدارات كامل.

السطر 11: بدء إخراج المخرجات

echo $OUTPUT->header();                                       // 11

وهذا سطر آخر يبدو بريئًا لكنه يفعل أكثر بكثير مما يبدو. والمقصد أنّه قبل إجراء أيّ مخرجات يجب تحديد السمة (skin) المنطبقة. وقد يعتمد ذلك على مزيج من سياق الصفحة وتفضيلات المستخدم. لكنّ $PAGE->context لم يُضبط إلّا في السطر 8، لذا لم يكن بإمكان المتغيّر العام $OUTPUT أن يكون قد تهيّأ في بداية السكربت. ولحلّ هذه المشكلة، يُستعمل بعض «السحر» في PHP لإنشاء كائن $OUTPUT المناسب بناءً على المعلومات الموجودة في $PAGE عند أول استدعاء لأيّ دالة مخرجات.

وهناك أمر آخر يستحقّ النظر هو أنّ كل صفحة في Moodle قد تحتوي على كتل (blocks). وهذه أجزاء إضافية قابلة للتهيئة من المحتوى تُعرض عادةً إلى يمين المحتوى الرئيس أو يساره. (وهي نوع من الإضافات.) ومرة أخرى، تعتمد المجموعة الدقيقة من الكتل المعروضة، بطريقة مرنة (يستطيع المدير التحكّم فيها)، على سياق الصفحة وبعض الجوانب الأخرى من هوية الصفحة. ومن ثمّ فإنّ جزءًا آخر من التحضير للإخراج هو استدعاء $PAGE->blocks->load_blocks().

وبعد استخراج جميع المعلومات الضرورية، تُستدعى إضافة السمة (التي تتحكّم في المظهر العام للصفحة) لتوليد تخطيط الصفحة الكلّي، بما في ذلك الترويسة والتذييل المعياريَّين المطلوبَين. وهذه الاستدعاءة مسؤولة أيضًا عن إضافة مخرجات الكتل في الموضع المناسب داخل HTML. وفي منتصف التخطيط سيكون هناك عنصر div توضع فيه المحتويات الخاصة بهذه الصفحة. يُولَّد HTML لهذا التخطيط، ثم يُقسَّم إلى نصفين بعد بداية div المحتوى الرئيس. ويُعاد النصف الأول، ويُخزَّن الباقي ليُعاد عبر $OUTPUT->footer().

السطر 12: إخراج جسم الصفحة

echo $OUTPUT->box(get_string('greet', 'local_greet',
        format_string($name)));                               // 12

يُخرج هذا السطر جسم الصفحة. وهو هنا يعرض مجرّد عبارة التحية داخل صندوق. والتحية بدورها نصّ معرَّب، وهذه المرّة استُبدلت قيمة في عنصر نائب. ويوفّر مُصيِّر النواة $OUTPUT طرائق راحة عديدة مثل box لوصف المخرجات المطلوبة بمصطلحات عالية المستوى إلى حدٍّ كبير. ويمكن للسمات المختلفة التحكّم في HTML الذي يُخرَج فعلًا لتشكيل الصندوق.

أما المحتوى الذي جاء أصلًا من المستخدم ($name) فيُخرَج عبر الدالة format_string. وهذا هو الجزء الآخر من توفير الحماية من XSS. كما أنّه يتيح استعمال مرشّحات النص (وهي نوع إضافات آخر). ومن أمثلة المرشّحات مرشّح LaTeX الذي يستبدل مُدخلات مثل $$x + 1$$ بصورة للمعادلة. وسأذكر، دون شرح، أنّ هناك في الواقع ثلاث دوال مختلفة (s، و format_string، و format_text) تبعًا لنوع المحتوى المحدّد الذي يُخرَج.

السطر 13: إنهاء إخراج المخرجات

echo $OUTPUT->footer();                                       // 13

وأخيرًا، يُخرَج تذييل الصفحة. ولا يعرض هذا المثال ذلك، لكنّ Moodle يتتبّع جميع شيفرة JavaScript التي تحتاجها الصفحة، ويخرَج وسوم السكربتات الضرورية في التذييل. وهذه ممارسة جيّدة معيارية. فهي تتيح للمستخدم رؤية الصفحة دون انتظار تحميل كل شيفرة JavaScript. ويودّ المطوِّر تضمين شيفرة JavaScript باستدعاءات واجهة برمجية مثل $PAGE->requires->js('/local/greet/cooleffect.js').

هل ينبغي أن يمزج هذا السكربت المنطق بالمخرجات؟

من الواضح أنّ وضع شيفرة الإخراج مباشرةً في index.php، حتى لو على مستوى عالٍ من التجريد، يحدّ من المرونة التي تملكها السمات للتحكّم في المخرجات. وهذه علامة أخرى على عُمر قاعدة شيفرة Moodle. فقد أُدخل المتغيّر العام $OUTPUT عام 2010 كخطوة تمهيدية في الطريق من الشيفرة القديمة، حيث كانت شيفرة الإخراج والمتحكّم موجودةً في الملف نفسه، إلى تصميم تُفصل فيه كل شيفرة العرض فصلًا تامًّا. ويشرح هذا أيضًا الطريقة القبيحة نوعًا ما التي يُولَّد بها تخطيط الصفحة كلّه ثم يُقسَّم إلى نصفين، بحيث يمكن وضع أيّ مخرجات من السكربت نفسه بين الترويسة والتذييل. وبعد فصل شيفرة العرض خارج السكربت — في ما سمّاه Moodle «مُصيِّرًا» (renderer) — يتسنّى للسمة أن تختار تجاوز شيفرة العرض لسكربت بعينه بشكل كامل (أو جزئي).

ويمكن لإعادة هيكلة (refactoring) صغيرة أن تُخرج كل شيفرة الإخراج من index.php الخاص بنا إلى مُصيِّر. وستصبح نهاية index.php (الأسطر 11 إلى 13) لتصبح:

$output = $PAGE->get_renderer('local_greet');
echo $output->greeting_page($name);

وسيكون هناك ملف جديد local/greet/renderer.php:

<?php
class local_greet_renderer extends plugin_renderer_base {
    public function greeting_page($name) {
        $output = '';
        $output .= $this->header();
        $output .= $this->box(get_string('greet', 'local_greet', $name));
        $output .= $this->footer();
        return $output;
    }
}

وإذا رغبت السمة في تغيير هذه المخرجات تغييرًا تامًّا، لكانت تعرّف صنفًا فرعيًا من هذا المُصيِّر يتجاوز الدالة greeting_page. ويحدّد $PAGE->get_renderer() صنف المُصيِّر المناسب للتهيئة تبعًا السمة الحالية. وهكذا تُفصل شيفرة المخرجات (العرض) فصلًا تامًّا عن شيفرة المتحكّم في index.php، وقد أُعيدت إعادة هيكلة الإضافة من شيفرة Moodle قديمة نمطية إلى معمارية MVC نظيفة.

13.5. التجريد فوق قواعد البيانات

كان سكربت «Hello world» بسيطًا إلى حدٍّ كافٍ فلم يكن بحاجة إلى الوصول إلى قاعدة البيانات، وإنّما كانت عدة استدعاءات من مكتبات Moodle المستخدمة تجري استعلامات على قاعدة البيانات. وسأصف الآن باختصار طبقة قواعد البيانات في Moodle.

كان Moodle يستخدم مكتبة ADOdb أساسًا لطبقة التجريد الخاصة بقاعدة البيانات، لكنّ ذلك أثار مشكلات بالنسبة إلينا، وكان للطبقة الإضافية من شيفرة المكتبة أثر ملحوظ على الأداء. لذلك، وفي Moodle 2.0، انتقلنا إلى طبقة تجريد خاصة بنا، وهي غلاف رقيق (thin wrapper) حول مكتبات PHP لقواعد البيانات المختلفة.

الصنف moodle_database

قلب المكتبة هو الصنف moodle_database. وهو يعرّف الواجهة التي يوفّرها المتغيّر العام $DB، وهو ما يتيح الوصول إلى اتصال قاعدة البيانات. وقد يبدو الاستعمال النموذجي كالتالي:

$course = $DB->get_record('course', array('id' => $courseid));

وهذا يُترجَم إلى لغة SQL التالية:

SELECT * FROM mdl_course WHERE id = $courseid;

ويُعيد البيانات بوصفها كائن PHP عاديًا ذو حقول عامة، تتيح لك الوصول إلى $course->id و $course->fullname وغيرها.

وتتعامل طرائق بسيطة كهذه مع الاستعلامات الأساسية والتحديثات والإدراجات البسيطة. وأحيانًا يلزم تنفيذ SQL أعقد، مثلًا لتشغيل التقارير. وفي تلك الحالة توجد طرائق لتنفيذ SQL عشوائي:

$courseswithactivitycounts = $DB->get_records_sql(
   'SELECT c.id, ' . $DB->sql_concat('shortname', "' '", 'fullname') . ' AS coursename,
        COUNT(1) AS activitycount
   FROM {course} c
   JOIN {course_modules} cm ON cm.course = c.id
   WHERE c.category = :categoryid
   GROUP BY c.id, c.shortname, c.fullname ORDER BY c.shortname, c.fullname',
   array('categoryid' => $category));

وفيما يلي بعض الأمور الواجب الانتباه إليها هناك:

  • تُلفّ أسماء الجداول بين {} كي تستطيع المكتبة العثور عليها وإضافة بادئة أسماء الجداول في مقدّمتها.
  • تستخدم المكتبة العناصر النائبة لإدراج القيم في SQL. وفي بعض الحالات يستخدم هذا إمكانات مشغّل قاعدة البيانات الأساسي. وفي حالات أخرى يجب تهريب القيم وإدراجها في SQL باستخدام معالجة النصوص. تدعم المكتبة كلٌّ من العناصر النائبة المسمّاة (كما في الأعلى) والعناصر النائبة مجهولة الاسم، باستخدام ? كعنصر نائب.
  • كي تعمل الاستعلامات على جميع قواعد البيانات التي ندعمها، يجب استعمال مجموعة آمنة من معايير SQL. فمثلًا يمكنك أن ترى أنّني استعملت الكلمة المفتاحية AS لأسماء الأعمدة المستعارة، لكن ليس لأسماء الجداول المستعارة. وقاعدتا الاستعمال هاتان ضروريتان.
  • ومع ذلك، هناك بعض المواقف التي لا تنجح فيها أيّ مجموعة من معايير SQL القياسية على جميع قواعد البيانات التي ندعمها؛ فمثلًا لكل قاعدة بيانات طريقة مختلفة في دمج النصوص. وفي هذه الحالات توجد دوال توافق (compatibility functions) لتوليد جملة SQL الصحيحة.

تعريف بنية قاعدة البيانات

مجال آخر تختلف فيه أنظمة إدارة قواعد البيانات اختلافًا كبيرًا هو صيغة SQL اللازمة لتعريف الجداول. وللتخلّص من هذه المشكلة، تعرّف كل إضافة في Moodle (وكذلك نواة Moodle) جداول قاعدة البيانات المطلوبة في ملف XML. يحلّل نظام تثبيت Moodle ملفات install.xml ويستخدم المعلومات التي تحتويها لإنشاء الجداول والفهارس المطلوبة. وهناك أداة مطوّر اسمها XMLDB مدمجة في Moodle تساعد في إنشاء ملفات التثبيت هذه وتحريرها.

وإذا كانت بنية قاعدة البيانات بحاجة إلى تغيير بين إصدارين من Moodle (أو من إضافة)، فعلى المطوِّر أن يكتب شيفرة (باستعمال كائن قاعدة بيانات إضافي يوفّر طرائق DDL) لتحديث بنية قاعدة البيانات، مع الحفاظ على جميع بيانات المستخدمين. وبذلك سيُحدِّث Moodle نفسه دائمًا من إصدار إلى الذي يليه، مما يبسّط الصيانة على المديرين.

وثمّة مسألة مثيرة للجدل، مصدرها أنّ Moodle بدأ باستخدام MySQL 3، وهي أنّ قاعدة بيانات Moodle لا تستعمل المفاتيح الأجنبية (foreign keys). ويتيح هذا بقاء بعض السلوكيات المعطوبة دون كشف، مع أنّ قواعد البيانات الحديثة قادرة على كشف المشكلة. والعُقدة أنّ الناس ظلّوا يشغّلون مواقع Moodle سنوات من دون مفاتيح أجنبية، لذا فثَمّة على الأرجح بيانات غير متّسقة موجودة فعلًا. وإضافة المفاتيح الآن ستكون مستحيلة من دون عملية تنظيف شديدة الصعوبة. ومع ذلك، ومنذ إضافة نظام XMLDB إلى Moodle 1.7 (عام 2006!)، تحتوي ملفات install.xml تعريفات المفاتيح الأجنبية التي ينبغي أن تكون موجودة، وما زلنا نأمل يومًا ما في إنجاز كل العمل الضروري كي نتمكّن من إنشائها أثناء عملية التثبيت.

13.6. ما لم يتناوله هذا الفصل

آمل أن أكون قد وفّرت لك نظرة عامة جيّدة على كيفية عمل Moodle. ولأنّ المساحة ضاقت، اضطررتُ إلى إغفال عدّة مواضيع مثيرة للاهتمام، منها كيف تتيح إضافات المصادقة والتسجيل والدرجات لـ Moodle أن يعمل بالتشابك مع أنظمة معلومات الطلاب، وكذلك الطريقة المثيرة للتخزين المعتمد على عنوان المحتوى (content-addressed) التي يخزّن بها Moodle الملفات المرفوعة. ويمكن العثور على تفاصيل هذه المواضيع، وغيرها من جوانب تصميم Moodle، في وثائق المطوّرين.

13.7. الدروس المستفادة

أحد الجوانب المثيرة للاهتمام في العمل على Moodle هو أنّه نتج عن مشروع بحثي. فـ Moodle يتيح — من دون أن يفرض — بيداغوجيا البنّاء الاجتماعي (social constructivist pedagogy). أي أنّنا نتعلّم على أفضل وجه حين نصنع شيئًا بالفعل، وأنّنا نتعلّم من بعضنا بعضًا بوصفنا مجتمعًا. ولم يسأل سؤال دكتوراه Martin Dougiamas ما إذا كان هذا نموذجًا فعّالًا في التعليم، بل ما إذا كان نموذجًا فعّالًا في تشغيل مشروع مفتوح المصدر. أي: هل يمكننا أن ننظر إلى مشروع Moodle بوصفه محاولةً لتعلّم كيفية بناء بيئة تعلّم افتراضية (VLE) واستخدامها، ومحاولةً لتعلّم ذلك عبر بناء Moodle واستخدامه بالفعل بوصفه مجتمعًا يعلّم فيه المعلمون والمطوّرون والمديرون والطلاب بعضهم بعضًا؟ أجد أنّ هذا نموذج جيّد للتفكير في مشروع تطوير برمجيات مفتوحة المصدر. والمكان الرئيس الذي يتعلّم فيه المطوّرون والمستخدمون من بعضهم هو النقاشات في منتديات مشروع Moodle، وفي قاعدة بيانات الأخطاء (bug database).

وربما كانت أهم نتيجة لهذا النهج التعليمي هي ألّا تخاف من البدء بتنفيذ أبسط حلّ ممكن أوّلًا. فمثلًا، كانت الإصدارات المبكّرة من Moodle لا تملك سوى بضعة أدوار مكتوبة بشكل صريح (hard-coded) مثل المعلّم والطالب والمدير. وهذا يكفي لسنوات عديدة، لكنّ حدودها اضطررنا في النهاية إلى معالجتها. وحين جاء وقت تصميم نظام الأدوار لـ Moodle 1.7، كانت في المجتمع خبرة وفيرة في كيفية استعمال الناس لـ Moodle، وكانت هناك كثير من طلبات الميزات الصغيرة التي أظهرت ما يحتاج الناس إلى ضبطه باستعمال نظام تحكّم في الوصول أكثر مرونة. وقد ساعد كلّ هذا في جعل نظام الأدوار أبسط ما يمكن، ومعقّدًا بقدر ما هو ضروري. (وفي الحقيقة، انتهى الإصدار الأول من نظام الأدوار إلى أن يكون معقّدًا أكثر بقليل من اللازم، ثم بُسّط قليلًا لاحقًا في Moodle 2.0.)

وإن أخذتَ بأنّ البرمجة تمرينٌ على حلّ المشكلات، فقد تظنّ أنّ Moodle أخطأ في التصميم أوّلًا ثم اضطرّ إلى إضاعة وقت في تصحيحه لاحقًا. وأقترح أنّ هذه نظرة غير مفيدة عند محاولة حلّ مشكلات واقعية معقّدة. فحين بدأ Moodle، لم يكن أحد يعرف ما يكفي لتصميم نظام الأدوار الذي لدينا اليوم. وإن أخذتَ زاوية التعلّم، فإنّ المراحل المختلفة التي مرّ بها Moodle للوصول إلى التصميم الحالي كانت ضرورية وحتمية.

ولكي تنجح هذه النظرة، يجب أن يكون من الممكن تغيير أيّ جانب تقريبًا من معمارية نظام ما بمجرد أن تتعلّم أكثر. وأظنّ أنّ Moodle يُظهر أنّ ذلك ممكن. فمثلًا، وجدنا طريقة لإعادة هيكلة الشيفرة تدريجيًا من سكربتات قديمة إلى معمارية MVC أنظف. ويتطلب ذلك جهدًا، لكنّ يبدو أنّه عند الضرورة يمكن للعشاريع مفتوحة المصدر أن تجد الموارد اللازمة لتنفيذ هذه التغييرات. ومن وجهة نظر المستخدم، يتطوّر النظام تدريجيًا مع كل إصدار رئيسي.