اذهب إلى المحتوى

محمد أحمد العيل

الأعضاء
  • المساهمات

    308
  • تاريخ الانضمام

  • تاريخ آخر زيارة

  • عدد الأيام التي تصدر بها

    27

كل منشورات العضو محمد أحمد العيل

  1. يعد توفير الخدمات وإتاحة التطبيقات والموارد للمستخدمين الأهداف الأساسية لتجهيز الخواديم وإعدادها؛ إلا أن أي خادوم موصول بشبكة الإنترنت سيتعرض لا محالة لمستخدمين سيئي النيات يأملون استغلال الثغرات الأمنية للحصول على صلاحيات الدخول. توجد الجدران النارية Firewalls بهدف حجب المنافذ Ports غير المستخدمة؛ إلا أن السؤال يُطرح عن ما يجب فعله للخدمات التي تريد الوصول إليها دون أن تكون معروضة للجميع، تريد استخدامها عند الحاجة ومنع الوصول إليها في الأوقات الأخرى. الطرق على المنافذ Port knocking هو إحدى وسائل إخفاء الخدمات العاملة على جهازك؛ فتعمل على جعل الجدار الناري يحمي الخدمات إلى أن تطلب منه فتح منفذ عبر إرسال متتالية محدَّدة من البيانات. سنتحدث في هذا الدليل عن كيفية إعداد آلية للطرق على المنافذ من أجل إخفاء خدمة SSH باستخدام حزمة knockd على أوبنتو 14.04. ملحوظة: يغطي هذا الشرح الإصدار الرابع من بروتوكول IP. يفصل لينكس بين تأميني الإصدار الرابع والإصدار السادس. على سبيل المثال، لا تنطبق قواعد iptables إلا على حزم البيانات المنقولة عبر عناوين الإصدار الرابع، غير أنه يوجد مكافئ لها بالنسبة لعناوين الإصدار الرابع وهو ip6tables. إذا كان الخادوم لديك معدا لاستخدام عناوين الإصدار السادس فلا تنس تأمين كل من واجهات Interfaces كل من الإصدارين باستخدام الأدوات المناسبة. كيف يعمل الطرق على المنافذ؟تتمثل فكرة الطرق على المنفذ في إعداد خدمة لمراقبة سجلات Logs الجدار الناري أو واجهات Interfaces الشبكة بحثا عن محاولات اتصال. تغير الخدمةُ قواعدَ الجدار الناري، إذا جرت محاولات اتصال معرَّفة مسبقا (تعرف بالطرْقات Knocks)، من أجل السماح بالاتصالات عبر منفذ معيَّن. تسمح هذه الطريقة بالإبقاء على الخدمات مخفية إلى الوقت الذي تريد استخدمها فيه. لا يناسب هذا الإعداد خواديم الويب التي يُسمَح عادة لجميع الاتصالات بالوصول إليها؛ إلا أنها مفيدة للخدمات الموجهة فقط لمستخدمين معروفين وبصلاحيات محددة، على سبيل المثال خدمة SSH. لا ينبغي جعل الطرق على المنافذ التدبير الوحيد لتأمين الخدمات، رغم أن متتالية الطرق يمكن أن تكون معقدة جدا. تنفذ الخدمات عادة طرق تأمين واستيثاق خاصة بها تُعرَض للمستخدم بعد إرساله متتالية الطرق الصحيحة. يضيف الطرق على المنافذ، في هذا الإطار، طبقة جديدة يجب أن يمر بها المستخدم قبل أن يرى طريقة الاستيثاق المعتادة. يوفر الطرق على المنافذ ميزة أخرى مفيدة هي أنه لا يُصدِر أي ردود على محاولات الطرق. إذا حاول متسلل فحص المنافذ فسيرى أنها مغلقة وإن إحاول تخمين متتالية الطرق فيجب عليه التحقق بعد كل تخمين لمعرفة إن كان المنفذ فُتِح أم لا؛ الأمر الذي سيكون كافيا في أغلب الحالات ليمنع المهاجمين من الدخول ويصرف أنظارهم عن إعادة المحاولة. سنستخدم خلال هذا الشرح الجدار الناري المدمج افتراضيا مع أوبنتو (iptables) ونثبت برنامج knockd لتوفير وظيفة الطرق على المنافذ. إعداد IPTables لمنع أغلب الاتصالاتنحتاج، قبل أن نبدأ في إعداد آلية للطرق على المنافذ، إلى ضبط قاعدي للجدار الناري. سنمنع كل الاتصالات تقريبا. يأتي جدار IPTables الناري مضمنا في أوبنتو إلا أنه لا توجد افتراضيا أي قاعدة مما يعني أنه يُسمَح لجميع الاتصالات بالمرور. يمكنك تعلم كيفية إعداد جدار ناري باستخدام IPTables على Ubuntu 14.04 . نبدأ بالسماح بالاتصال عبر الجهاز المحلي. يعني هذا القبول بالبيانات المرسَلة من الخادوم نفسه وهو ما يسمح للخدمات العاملة على الخادوم بالتواصل في ما بينها. sudo iptables -A INPUT -i lo -j ACCEPTيضيف الأمر أعلاه قاعدة إلى سلسلة INPUT التي تتعامل مع جميع الاتصالات القادمة إلى الخادوم. تطلب القاعدة التي أضفناها من iptables قبول جميع البيانات القادمة من الواجهة المحلية lo التي تستخدم للاتصالات الداخلية. الخطوة التالية هي السماح لكل الاتصالات الجارية بالاستمرار؛ لذا ننفذ الأمر: sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTتطلب القاعدة الموجودة في الأمر أعلاه من iptables السماح للاتصالات الجارية والبيانات المتعلقة بها بالمرور. هذه القاعدة مهمة ليمكننا مواصلة الاتصال بالخادوم عن بعد باستخدام SSH حتى لا نفقد الاتصال به عند بدء حظر الاتصالات. ثم نضيف قواعد للسماح بالخدمات الموجهة للعموم، أي تلك التي لا يوجد شرط على مستخدميها مثل خادوم الويب (إن وُجد) الذي يعمل عادة على المنفذ رقم 80: sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPTاحذر أن تضيف قواعد للخدمات التي ستستخدم الطرق على المنافذ للوصول إليها. ستكون إضافةُ هذه القواعد مهمةَ خدمة الطرق التي ستعدل قواعد الجدار الناري حسب الطلب. لهذا السبب لن نضيف خادوم SSH إلى إعداد iptables. حتى هذه اللحظة لم نضف سوى قواعد تقبل الاتصال ولم نضف أي قواعد لحظر الاتصالات. وهو ما يعني أن الجدار الناري لا زال يقبل جميع الاتصالات؛ بعضها مذكور صراحة، وهي تلك التي أضفناها. سنمنع الآن جميع الاتصالات، ما عدا تلك التي حددناها في الأوامر السابقة: sudo iptables -A INPUT -j DROPيعني هذا أن جميع محاولات الاتصال التي لا توافق إحدى القواعد المذكورة سابقا ستُحظَر. تمكن معاينة القواعد بتنفيذ الأمر: sudo iptables -Sالنتيجة: -P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A INPUT -j DROPلاحظ أنه لا توجد حتى الآن قاعدة في إعدادات الجدار الناري تسمح باتصالات SSH. أنهينا الآن قواعد الجدار الناري، بقي أن نجعلها دائمة حتى لا يعاد تعيينها عند إعادة تشغيل الخادوم. نستخدم أداة iptables-persistent لهذا الغرض. نفذ الأمر التالي لتثبيتها: sudo apt-get install iptables-persistentثم شغل الخدمة بعد انتهاء التثبيت: sudo service iptables-persistent startتثبيت خدمة Knockdيدعى البرنامج الذي سنستخدمه لتمكين آلية للطرق عبر المنافذ knockd. ثبته بتنفيذ الأمر التالي: sudo apt-get install knockdلا يشغَّل البرنامج مباشرة بعد ثبيته وذلك حتى لا يحظُر بيانات كثيفة على الفور. يجب أن تضبط الخدمة وتفعلها يدويا. إعداد Knockd لاستخدام الطرق على المنافذيوجد ملف إعداد يجب تحريره لإعداد الخدمة: sudo nano /etc/knockd.confيجب أن يظهر لديك ملف بمحتوى شبيه بالتالي: [options] UseSyslog [openSSH] sequence = 7000,8000,9000 seq_timeout = 5 command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT tcpflags = syn [closeSSH] sequence = 9000,8000,7000 seq_timeout = 5 command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT tcpflags = synفي المقطع [options] توجد تعليمة باسم UseSyslog تخبر خدمة knockd أن عليها استخدام الوسائل الاعتيادية لحفظ السجلات. ينتج عن هذه التعليمة إدراج السجلات ضمن المجلد var/log/messages/. إذا أردت حفظ السجلات في مكان مغاير فيمكنك ذلك باستخدام التعليمة التالية بدلا من UseSyslog (حيث path/to/log/file/ مسار ملف السجلات): LogFile = /path/to/log/fileثم يأتي مقطعان يُستخدَمان لتجميع قواعد تُطابِق جميعها حدثا واحدا. لا يؤثر اسم المقطع على عمله إلا أنه يفيد لإعطاء نبذة لمن يفتح الملف عن عمل المقطع. يوجد في ملف الإعداد الافتراضي مقطع يفتح منفذ SSH وآخر لغلق نفس المنفذ. المعطى الذي يعيّن نمط الطرق هو التالي: sequence = 7000,8000,9000يعني النمط أعلاه أن مجموعة القواعد هذه ستبحث ما إذا كان نفس عنوان IP طلب الاتصال على المنفذ رقم 7000 متبوعا مباشرة بالمنفذ 8000 ثم أخيرا المنفذ 9000. سنغير متتالية الطرق لتصبح على النحو التالي: sequence = 2022,3022,4022يوجد معطيان آخران في المجموعة يراقبان النشاط: seq_timeout = 5 tcpflags = synيحدد المعطى الأول مدة زمنية يجب أن تكتمل فيها متتالية طلبات الاتصال المعرفة في المعطى السابق. أما المعطى الثاني فيحدد خيارا يجب أن يتواجد في حزم tcp حتى تكون صالحة (يستخدم هذا الخيار لمعرفة الطريقة التي يجب بها التعامل مع الحزم وحالتها). يشيع استخدام القيمة syn للتفريق بين الحزم التي نريدها وتلك التي تنشئها برامج مثل SSH في الخلفية. ثم نجد الأمر: command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPTيجب أن تتعرف على قاعدة من قواعد iptables. يمكن إذن تفسير المقطع بأنه عند إرسال متتالية الطرق في الوقت المحدد فسينفذ الأمر أعلاه من أجل السماح باتصالات SSH. إذا تفطنت لإعدادات iptables فسترى أن القاعدة الجديدة تستخدم الخيار A- لإلحاق القاعدة بنهاية سلسلة INPUT؛ وهو ما يعني أنها ستكون بعد القاعدة التي تحظر كافة الاتصالات. سنحتاج للتعديل على الأمر وإبدال القاعدة بأخرى تُدرَج في بداية الملف؛ لذا سنستخدم الخيار I- ونعلم القاعدة على أنها القاعدة رقم 1: command = /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPTستُضاف قاعدة جديدة للسماح بقبول اتصالات SSH من المسخدِم الذي أرسل متتالية الطرق. الجزء %IP% الموجود في القاعدة سيُبدَل بعنوان IP الذي أتت منه متتالية الطرق. المقطع الأخير يؤدي عملا مشابها بمتتالية مغايرة وأمر يعمد إلى حذف قاعدة فتح اتصالات SSH من iptables. سنستخدم المتتالية 4022,3022,2022 لإنهاء الاتصال الذي أنشأناه وغلق المنفذ المرتبط به (أي المنفذ رقم 22). يجب تغيير المتتاليات الموجودة افتراضيا في المقاطع وإبدالها بأخرى عشوائية. ترك المتتالية الموجودة افتراضيا في ملف الإعداد يجعل من تنفيذ آلية طرق المنافذ هذه بلا فائدة (الجميع يعرف المتتالية الافتراضية). أغلق الملف (CTRl + X) ثم احفظه (CTRL +O). تشغيل خدمة Knockdيمكننا الآن بعد أن أنهينا إعداد knockd للحصول على مجموعة قواعد صالحة اختبارُ الإعداد بتشغيل الخدمة. تذكر أن الإعداد الافتراضي صالح إلا أنه لن يكون آمنا إلا إذا غيرت متتالية الطرق الافتراضية لكل مقطع. يجب التعديل على ملف آخر لتفعيل الخدمة: sudo nano /etc/default/knockdنغير قيمة الخيار START_KNOCKD لتصبح 1: START_KNOCKD=1أغلق الملف (CTRl + X) ثم احفظه (CTRL +O). يمكننا الآن تشغيل الخدمة، لذا ننفذ الأمر: sudo service knockd startيشغل الأمر خدمة knockd ويسمح بتغيير قواعد الجدار الناري عند الطرق على المنافذ حسب المتتالية المعرَّفة في ملف الإعداد. اختبار الطرق على المنافذسنرى الآن ما إذا كان بإمكاننا التعديل على قواعد iptables بالطرق على المنافذ المعرفة في ملف الإعداد. أبق - احترازا - على اتصالك الجاري بالخادوم نشطا وافتح نافذة جديدة للطرفية. توجد الكثير من الأدوات للاستخدام في طرق المنافذ؛ ومن أشهرها nmap، netcat وعميل آخر صُمِّم خصيصا للطرق واسمه knock. سنستخدم أداة knock ولكن قبل ذلك فلنتأكد أولا من أن منفذ SSH مغلق فعليا. ssh root@server_ip_addressيجب أن تكون النتيجة على النحو التالي: ssh: connect to host server_ip_address port 22: Operation timed outيعني هذا أن الخادوم لم يُجِب وأن المهلة الممنوحة للاتصال انقضت. يعود السبب إلى أن خدمة SSH محظورة على الخادوم حسب قواعد iptables. اضغط على زري CTRL وC للعودة إلى الطرفية إن طالت مدة محاولة الاتصال. أداة Knock لطرق المنافذأداة knock وسيلة سهلة للطرق على المنافذ يطورها نفس فريق knockd. تُضَمَّن هذه الأداة في حزمة knockd؛ لذا يمكن تثبيتها على الجهاز العميل مثل ما فعلنا على الخادوم: sudo apt-get install knockdيمكن أيضا تنزيل الأداة من الموقع الرسمي الذي توجد به إصدارات لكل من Windows وOS X؛ وكذلك لأندرويد وiOS. تُستخدَم أداة knock على النحو التالي: knock server_ip_address sequenceحيث sequence متتالية الطرق وserver_ip_address عنوان IP الخادوم. بالنسبة لمثالنا سيكون الاستخدام على النحو التالي: knock server_ip_address 2022 3022 4022ولإغلاق المنفذ نرسل المتتالية التي حددناها في ملف الإعداد: knock server_ip_address 4022 3022 2022إعداد Knockd لغلق الاتصالات تلقائياتأكدنا الآن من أن خدمة الطرق تعمل. سنغير الإعدادات لجعلها أكثر صلابة. أعد فتح ملف الإعداد على الخادوم sudo nano /etc/knockd.confيتيح knockd إمكانية تحديد مدة زمنية يُنفَّذ أمر محدد بعد انقضائها؛ نستفيد من هذه الميزة للاكتفاء بقاعدة واحدة لفتح وإغلاق منفذ SSH؛ مما يعني أننا لن نحتاج لإرسال متتالية الطرق حتى يغلَق المنفذ. يمكن حذف مقاطع openSSH وcloseSSH ضمن ملف الإعداد لأننا سنبدلها بمقطع وحيد نسميه SSH: [options] UseSyslog [SSH]سنعرِّف داخل مقطع SSH متتالية للطرق، خيار tcpflags ومهلة زمنية للانتظار ينبغي الطرق على المنافذ خلالها؛ مثل ما فعلنا مع المقطعين السابقين. سنضيف أيضا الأمر المستخدَم لفتح منفذ SSH: [options] UseSyslog [SSH] sequence = 5438,3428,3280,4479 tcpflags = syn seq_timeout = 15 start_command = /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPTاختر متتالية منافذ فريدة. حددنا في هذا المثال أربعة منافذ. تمكن زيادتها حسب رغبتك ولكن انتبه إلى أنه يجب طرق المنافذ خلال المدة الزمنية (بالثواني) المحدَّدة ضمن معطى seq_timeout. معطى start_command هو نفسه معطىcommand الذي رأيناه في الأمثلة السابقة؛ الفرق الوحيد هو أن الاسم أكثر إسهابا. يأتي الآن دور المعطيات الجديدة التي ستساعدنا على غلق المنفذ: [options] UseSyslog [SSH] sequence = 5438,3428,3280,4479 tcpflags = syn seq_timeout = 15 start_command = /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPT cmd_timeout = 10 stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPTيحدد معطى cmd_timeout الثواني التي ستنتظرها خدمة knockd قبل تنفيذ الأمر الموجود ضمن معطى stop_command. النتيجة هي أنه عند إرسال المتتالية الصحيحة فإن المنفذ سيُفتح لمدة 10 ثوان ثم يُغلق من جديد. احفظ الملف ثم أغلقه. أعد تشغيل الخدمة لاعتماد القواعد الجديدة: sudo service knockd restartيمكن استخدام الأمر التالي للاتصال ضمن الوقت المحدد: knock server_ip_address 5438 3428 3280 4479 && ssh root@server_ip_addressسيغلق المنفذ بعد 10 ثوان من الاتصال. خاتمةيُنظَر إلى الطرق على المنافذ على أنه مجرد إخفاء للخدمات بدل تأمينها؛ على الرغم من ذلك يبقى وسيلة رائعة لإضافة طبقة أمان جديدة ضد الهجمات العشوائية. يجب دائما تأمين خدماتك عبر الوسائل الأمنية المتاحة في النظام إلا أن إضافة مِصيدة مثل الطرق على المنافذ أمام التدابير الأمنية المعتادة يمكن أن يقلل بقدر ملحوظ هجمات القوة القاسية ومحاولات التسلل التي يتعرض لها خادومك. ترجمة - بتصرف - لمقال How To Use Port Knocking to Hide your SSH Daemon from Attackers on Ubuntu.
  2. يتضمن توفير الخدمات للعموم عبر شبكة الإنترنت خطر التعرض لهجمات، إلا أن عرض الخدمات هو الغرض الأساسي - غالبا - لتجهيز خادوم. يمكن لأي منفَذ Port أو خدمة أن تكون عُرضة لأنواع كثيرة من التجسس ومحاولات الوصول من طرف مستخدمين سيئي النوايا أو سكربتات تعمل تلقائيا. في حين أن بعض الخدمات يجب أن تبقى متاحة نظرا لأنها موجهة للاستخدام من الجميع (خادوم ويب يستضيف موقعا على سبيل المثال)، إلا أن أخرى لا ينبغي لها ذلك، إذ يجب الاستيثاق من مستخدميها ومنع من لايُرخص له الدخول من الولوج إلى الخدمة (خدمة SSH على سبيل المثال). الوضعية المُثلى هي أن تكون هذه الخدمات مؤمنة جدا ولا يتاح الوصول إليها إلا لمن نرغب في منحه هذه الصلاحية. يتيح الاستيثاق فريد الحزمة Single Packet Authentication وسيلة تسمح للجدار الناري Firewall الإبقاء على حظر خدمة إلى حين إرسال حزمة خاصة مُعمَّاة Encrypted إلى خدمة في الاستماع. إذا صادقت خدمة الاستماع على الحزمة فإنها تغير فورا قواعد الجدار الناري من أجل عرض المنفذ المطلوب. توجد أداة اسمها fwknop (اختصار ل Firewall Knock Operator وتعني عامل الطرق على الجدار الناري) تستخدم لاعتراض الحزم المخصصة ثم تغيير قواعد الجدار الناري حسب المطلوب. سنعد في هذا الدليل خادوما لأداة fwknop وعميلا لها على جهازين يعملان بتوزيعة أوبنتو 14.04. يمكّننا هذا الإعداد من حماية خادوم SSH وحظر الدخول إليه ما لم تطلب إتاحته. تثبيت fwknop على خادوم أوبنتو الأولسنفترض في إعداداتنا وجود خادوميْ أوبنتو 14.04 وتوفر اسم نطاق لكل واحد من الخادومين؛ مع أن استخدام عناوين IP لن يمثل عائقا. نفترض أيضا تثبيت خدمة SSH. تثبت الأوامر التالية خادوم fwknop على الجهاز الأول والذي سيكون بمثابة خادوم للثاني: sudo apt-get update sudo apt-get install fwknop-serverتثبيت عميل fwknop على خادوم أوبنتو الثانيسنجعل من الخادوم الثاني عميلا؛ لذا سنحتاج لتثبيت عميل fwknop عليه. يتكفل هذا العنصر بإنشاء الحزمة المعماة لإرسالها إلى الخادوم الآخر. sudo apt-get update sudo apt-get install fwknop-clientإعداد مفاتيح GPGسنستخدم مفاتيح GPG لتوفير الاستيثاق أثناء نقل الحزم. نحتاج لتنفيذ الإجراءات التالية على كل من الجهاز الخادوم والجهاز العميل. تأتي أداة GPG مثبتة افتراضيا. ولِّد مفاتيح على كل من الجهازيْن: gpg --gen-keyستسأل بضعة أسئلة يكفي - في الغالب - اختيار الإجابات الافتراضية بالضغط على زر Enter. سيطلب منك أيضا إدخال ثم تأكيد عبارة سر Passphrase. سيأخذ إنشاء مفتاح عشوائي بعض الوقت؛ من الأفضل تنفيذ بعض الإجراءات على الخادوم للتسريع من العملية (مثلا افتح نافذة Shell جديدة ونفذ فيها الأمر التالي find / > /dev/null إضافة إلى أوامر أخرى). نحتاج بعد انتهاء توليد مفاتيح GPG إلى كتابة أو نسخ معرفات المفاتيح العمومية. لذا ننفذ الأمر التالي على كل من الجهازين (الخادوم والعميل) gpg --list-keys /home/zeine77/.gnupg/pubring.gpg ------------------------------- pub 1024D/FFEDEE15 2015-07-21 uid Mohamed Ahmed Eyil (Comment) <Email> sub 1024g/95B798A4 2015-07-21الجزء المعلَّم هو الذي نحتاجه هنا (مفتاح عمومي). انسخ المفتاح العمومي لكل من الجهازين واحتفظ به مع تعليمِه (خادوم SERVER أو عميل CLIENT للتفريق بينهما) على النحو التالي: SERVER: FFEDEE15 CLIENT: 1E6E6DC4نفذ الأمر التالي على الجهاز العميل لإنشاء نسخة client.asc من المفتاح عبر تصديرها إلى ملف. ستحتاج لكتابة معرف مفتاح العميل الذي نسخته للتو: gpg -a --export 1E6E6DC4 > client.ascنفس الشيء على الجهاز الخادوم مع استخدام المفتاح العمومي للخادوم وتسمية المف بserver.asc: gpg -a --export FFEDEE15 > server.asc1- نقل المفاتيح بين الأجهزةنحتاج الآن لنقل المفاتيح بين الجهازين بحيث يكون لدى كل منهما نسخة من الاثنين. نستخدم أداة scp لنسخ ملف client.asc من العميل إلى الخادوم. نفذ الأمر التالي على العميل scp client.asc server_domain_or_ip:/path/to/user/home/directoryحيث server_domain_or_ip اسم نطاق أو عنوان IP الخادوم و/path/to/user/home/directory المسار الذي سيُنقل إليه الملف (المجلد الشخصي للمستخدم). سيطلب منك بعد تنفيذ الأمر معلومات الدخول إلى الخادوم. ثم نكرر نفس الشيء مع ملف server.asc لنقله من الخادوم إلى العميل (تنفيذ الأمر يكون على العميل): scp server_domain_or_ip:/path/to/user/home/directory/server.asc .توجد الآن نسخة من كل ملف على الخادوم وعلى العميل. 2- استيراد وتوثيق المفاتيحيوجد الآن المفتاح العمومي لكل واحد من الجهازين على الآخر (المفتاح العمومي للخادوم على العميل، والمفتاح العمومي للعميل على الخادوم) مما يعني أنه يمكننا استيراد المفتاح العمومي إلى قاعدة بيانات GPG المحلية. نفذ الأمر التالي على الخادوم: gpg --import client.ascثم نفذ التالي على العميل: gpg --import server.ascيوجد لدى كل من الجهازين الآن مفتاح الآخر في قاعدة بياناته؛ ننتقل إلى توثيق المفاتيح. نفذ الأمر التالي عل كل من الجهازين، مع استخدام معرفات المفاتيح المناسبة. في المثال لدينا سننفذ ما يلي على العميل (نوثق المفتاح العمومي للخادوم): gpg --edit-key FFEDEE15وعلى الخادوم (نوثق المفتاح العمومي للعميل) gpg --edit-key 1E6E6DC4سيظهر سطر أوامر gpg؛ نطلب توثيق المفتاح عبر الأمر: signسيطلب منك التأكيد على اختيارك، اضغط زر y ثم Enter. ثم الخطوة قبل الأخيرة وهي كتابة عبارة السر (نفس عبارة السر التي أدخلتها عند توليد المفتاح). وأخيرا أمر save لحفظ الإعدادات والخروج من سطر أوامر gpg. نحصل باكتمال هذه الخطوات على نسخة موثَّقة لمفتاح gpg الخاص بكل جهاز على الآخر، مما يعني أننا نثق من صحة المفاتيح. إعداد الوصول في خادوم fwknopيجب علينا إعداد خدمة fwknop لاستخدام مفاتيح GPG والسماح للعميل بالاتصال بالخادوم والاستيثاق لديه. افتح ملف إعداد الأداة على الخادوم بصلاحيات إدارية: sudo nano /etc/fwknop/access.confتوجد افتراضيا بضعة أسطر نشطة، البقية تعليقات وشروح. SOURCE: ANY; KEY_BASE64 __CHANGEME__ HMAC_KEY_BASE64 __CHANGEME__سنضبط ملف الإعداد لاستخدام الاستيثاق عبر مفاتيح GPG التي أعددناها سابقا. لذا سنضيف تعليمات لإعطاء معلومات لخدمة fwknop عن المفاتيح التي نستخدمها. توجد هذه التعليمات ضمن ملف الإعداد، احذف علامة التعليق (#) ثم أعط القيم المناسبة للتعليمات. عدل الملف لتصبح الأسطر النشطة على النحو التالي: SOURCE: ANY; OPEN_PORTS: tcp/22; ## SSH لتمكين الاتصال عن طريق FW_ACCESS_TIMEOUT: 30; ## مدة بقاء الاتصال مفتوحا REQUIRE_SOURCE_ADDRESS: Y; ## طلب عنوان المصدَر GPG_REMOTE_ID: FFEDEE15; ## المفتاح العمومي للعميل GPG_DECRYPT_ID: 1E6E6DC4; ## المفتاح العمومي للخادوم GPG_DECRYPT_PW: your_GPG_passphrase_here; ## عبارة سر الخاصة بالخادوم GPG_HOME_DIR: /home/test/.gnupg; ## gunpg مسار مجلد ## يوجد عادة في المجلد الشخصي للمستخدِم تأكد من إعطاء معرف مفتاح العميل ضمن تعليمة GPG_REMOTE_ID ومعرف مفتاح الخادوم ضمن تعليمة GPG_DECRYPT_ID. يجب كذلك إدخال عبارة السر لمفتاح الخادوم ضمن التعليمة GPG_DECRYPT_PW ومسار مجلد gnupg. ضمن تعليمة GPG_HOME_DIR. احفظ (CTRL+O) الملف ثم أغلقه (CTRL+X). اضبط قواعد IPTablesننتقل الآن، بعد أن أنهينا إعداد خادوم fwknop، إلى ضبط قواعد IPTables. ستعدل خدمة fwknop هذه القواعد حسب الحاجة؛ ولكن قبل ذلك نحتاج إلى إغلاق المنفذ. نحتاج أولا إلى السماح للاتصال الجاري بالمتابعة قبل غلق المنفذ. تسمح القاعدة التالية للاتصال الموجودة بالمواصلة: sudo iptables -A INPUT -i eth0 -p tcp --dport 22 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTملحوظة: هذا الإجراء مهم جدا في حال كنت تعد خادوما بعيد تتصل به عن طريق SSH. ثم نقيّد مباشرة بعد تنفيذ الأمر أعلاه الولوجَ عبر منفذ SSH بمنع بقية الاتصالات كلها: sudo iptables -A INPUT -i eth0 -p tcp --dport 22 -j DROPيوجد لدينا الآن جدار ناري مبدئي لمنع الاتصال عبر المنفذ 22 ويمكن بالتالي تنفيذ إعدادات fwknop؛ لذا نعيد تشغيل خادوم fwknop: sudo service fwknop-server restartستبدأ خدمة fwknop بمراقبة الخادوم بحثا عن حزم بيانات توافق القواعد التي أعددناها. الاتصال بالخادوم من العميلسنجرب الآن الاتصال بالخادوم من العميل. إن جرى كل شيء على ما يرام فلن يمكننا الاتصال بسبب انتهاء مهلة الاتصال (Time out). ssh root@server_domain_or_ipسيحاول العميل الاتصال بالخادوم وبعد انتهاء مهلة الاتصال تظهر الرسالة التالية: ssh: connect to host server_domain_or_ip port 22: Connection timed outإن لم ترغب في انتظار اكتمال المهلة - التي قد تطول - يمكنك الضغط على مفتاحي CTRL وC معا. يمكننا الآن إرسال حزمة معماة للاستيثاق لدى الخادوم. نستخدم عميل fwknop لهذا الغرض؛ ونمرر له المعطيات التالية: A tcp/22-: يحدد هذا الخيار البروتوكول والمنفذ الذي نطلب فتحه. gpg-recip--: معرف مفتاح GPG الخاص بالخادوم. gpg-sign--: معرف مفتاح GPG الخاص بالعميل. a-: يخبر fwknop بعنوان IP الذي يسمح بالوصول منه؛ أي عنوان الجهاز العميل. D-: يخبر الأمر بوجهة الطلب. نحدد هنا عنوان IP الخاص بالخادوم أو نطاقه. ننشئ ثم نرسل الأمر انطلاقا من هذه المعطيات والمعطيات السابقة (مفاتيح GPG): fwknop -A tcp/22 --gpg-recip FFEDEE15 --gpg-sign 1E6E6DC4 -a client_ip_address -D server_domain_or_ipسيطالب منك إدخال عبارة سر العميل لفك تعمية المفاتيح ثم ترسل الحزمة المعماة إلى الخادوم. لديك الآن ثلاثون ثانية لمحاولة الاتصال عبر SSH. ملحوظة: لتغيير هذه المدة فعّل تعليمة FW_ACCESS_TIMEOUT في ملف إعداد خادوم fwknop ثم أعطها القيمة المرادة. ssh root@server_domain_or_ipإن جرت الأمور على النحو المخطط لها فسيمكنك الاتصال بنجاح. سيُغلق المنفذ المفتوح بعد ثلاثين ثانية غير أن الاتصال سيبقى نشطا. تستطيع رؤية قواعد IPTables المضافة على الجهاز الخادوم بعد تنفيذ أمر إنشاء الحزمة المعماة وإرسالها على العميل (وقبل انقضاء مهلة الثلاثين ثانية). استخدم الأمر التالي لهذا الغرض: sudo iptables -Sالنتيجة -P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT -N FWKNOP_INPUT -A INPUT -j FWKNOP_INPUT -A INPUT -i eth0 -p tcp -m tcp --dport 22 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -i eth0 -p tcp -m tcp --dport 22 -j DROP -A FWKNOP_INPUT -s client_ip_address/32 -p tcp -m tcp --dport 22 -j ACCEPT لاحظ إضافة سطر جديد إلى IPTables. يطبق القاعدة المضافة على جميع طلبات الاتصال، إن وافق عنوان مصدر الطلب الجهاز الذي أرسل الحزمة المعماة فإنه يسمح له بالوصول عبر المنفذ 22 وإلا يرفض الطلب. خاتمةيمكّن إعداد آلية للاستيثاق فريد الحزمة من إضافة طبقة أمان عند الاتصال بين الأجهزة. يوفر هذا الإجراء علاوة على حماية الخواديم من هجمات القوة القاسية Brute force والهجمات العشوائية، المساعدة في حال اكتشاف ثغرات أمنية ضمن الخدمات المحمية مما يتيح لك في وضعية آمنة إلى أن ترقع الثغرة. قد تمثل آلية الاستيثاق فريد الحزمة إزعاجا للمستخدمين؛ إلا أنها مفيدة أمنيا ويمكن استخدامها مع إجراءات أمنية أخرى للمزيد من الأمان. ترجمة -بتصرف- لمقال How To Use fwknop to Enable Single Packet Authentication on Ubuntu 12.04.
  3. يوجد درس في الأكاديمية يشرح أمرين يستخدمان في البحث عن الملفات على لينكس. راجع درس كيف تستخدِم أمرَي find و locate للبحث عن الملفّات على Linux.
  4. نفذ الأمر التالي (على فرض أنك توجد في المجلد الذي تريد عرض مساره): pwdيعرِض الأمر مسار مجلد العمل، أي المسار الذي تتواجد فيه عند تنفيذه. إن لم تكن تعرف كيف تنتقل إلى المجلد فيتوجب عليك حينها البحث عنه باستخدام أحد الأمرين locate أو find .
  5. تمكن معرفة مواصفات وحدة المعالجة المركزية (عدد الأنوية، المعالجات في كل نواة، المُصنِّع، ... إلخ) عن طريق تنفيذ الأمر: cat /proc/cpuinfoبطريقة مشابهة تمكن معرفة خصائص الذاكرة العشواشية: cat /proc/meminfoفي ما يخص الأوامر على لينكس لا أعرف مرجعا عربيا يجمعها. توجد شروحات عن لينكس هنا على الأكاديمية كما توجد شروحات عن بعض أوامر لينكس من هنا أو من هنا
  6. يجري التخاطب بين الشبكات عبر عناوين IP والتي هي مجموعة من الأرقام تخضع لقيود معروفة، مثلا 145.45.67.89 عنوان IP لجهاز موجود على شبكة ما. دور نظام أسماء النطاقات هو توفير آلية تجعل من الممكن استخدام أسماء يسهل على الإنسان تذكرها. لولا وجود DNS لوجب عليك تذكر العنوان 52.16.48.7 بدلا من academy.hsoub.com. راجع مقال مقدّمة إلى مُصطَلحات وعناصر ومفاهيم نظام أسماء النطاقات لتفصيل أكثر عن آلية عمل النظام.
  7. تعد شبكة الخلايا المنزلِقة Slidable grid وسيلة رائعة وجذابة لعرض أجزاء عدة من المعلومات في نفس المساحة؛ فتنزلق كل خلية من الشبكة عند النقر أو الحومان Hover وتعرِض محتوى إضافيا. سنتطرق خلال هذا الدرس إلى عملية إنشاء شبكة خلايا منزلقة ابتداءً من وسوم HTML الضرورية، التنسيق وجعل الشبكة متجاوبة Responsive؛ مع إضافة خطوط أيقونات الويب. سنعرِض أيضا لكيفيّة استخدام jQuery لإضافة بعض التأثيرات على شبكة الخلايا. هكذا ستبدو شبكة الخلايا المنزلِقة بعد اكتمال الدّرس. إنشاء الشبكة في HTMLفي ما يلي وسوم HTML المستعملة لإنشاء الشبكة. سنشرح عملها بالمختصر. نحتاج لعنصر تغليف Wrapping يحيط بالشبكة، ثم أجزاء مستقلة بتموضع نسبي Relative positioning داخل عنصر التغليف. تمثل هذه الأجزاء الخلايا المرئية المكونة للشبكة. توجد داخل كل خليّة مساحتان بتموضع مطلق Absolute تملأ كل واحدة منهما طول وعرض الخلية تماما. سنضيف تأثيرات إلى كل من المساحتيْن بحيث تكون كل مساحة إما خارج مجال الرؤية في الصفحة أو مرئية. <div id="services" class="cf"> <section class="service"> <div class="service-icon"><span class="icon-web"></span><br/>التصميم للويب</div> <div class="service-description"><p>تقديم لطيف حول التصميم للويب وما نقدمه من خِدْمات في هذا المجال</p></div> </section> <section class="service"> <div class="service-icon"><span class="icon-graphic"></span><br/>التصميم الغرافيكي</div> <div class="service-description"><p> ... </p></div> </section> <section class="service"> <div class="service-icon"><span class="icon-logo"></span><br/>تصميم الشعارات</div> <div class="service-description"><p> ... </p></div> </section> <section class="service"> <div class="service-icon"><span class="icon-dev"></span><br/>التطوير للويب</div> <div class="service-description"><p> ... </p></div> </section> <section class="service"> <div class="service-icon"><span class="icon-3d"></span><br/>التصميم ثلاثي الأبعاد</div> <div class="service-description"><p> ... </p></div> </section> <section class="service"> <div class="service-icon"><span class="icon-illustration"></span><br/>إيضاحات</div> <div class="service-description"><p> ... </p></div> </section> </div> <!-- END #services -->يوجد div بمعرّف services وصنف Class اسمُه cf؛ توجد داخله ستة عناصر section وبداخل كل section عنصرا div، على النحو الذي ذكرناه سابقا. يوجد بداخل الأول من عنصري div عنصر span لنضع داخله أيقونة وعنوان. في عنصر div الآخر يمكن إدراج المحتوى الإضافي (وصف مثلا). حرصنا على جعل السّكربت يظهر في حالة عدم وجود Javascript لدى الزائر، فيظهر لديه المحتوى دون التأثيرات. ننتقل الآن إلى تنسيق المحتوى عن طريق CSS وتهيئته لعمل تأثيرات عليه بواسطة jQuery. تنسيق الشبكة عن طريق CSSيتكوّن ملف CSS من ثلاثة أجزاء: الأساسي للعمل مع jQuery، شفرة خط الأيقونة، والأخير تنسيقات لمظهر أجمل. في ما يلي الجزء الأول. #services .service { width: 33%; float: left; padding: 0.5em; min-height: 200px; overflow: hidden; position: relative; border: 1px solid #eee; } @media screen and (max-width: 600px) { #services .service { width: 50%; } } @media screen and (max-width: 320px) { #services .service { width: 100%; } } #services .service .service-icon, #services .service .service-description { position: absolute; width: 100%; height: 100%; top: 0; left: 0; background: #fff; padding: 50px 0; color: #222; } #services .service .service-description { left: 100%; background: #249EC2; color: white; padding: 50px; } #services .service .service-description:hover { cursor: pointer; }نشرح عمل الأسطر السابقة. نستهدف أولا الخلايا (service.) لترتيبها داخل الشبكة بإعطائها عرضا مائعا Fluid وحدا أدنى للارتفاع، وجعلها تطفو إلى اليسار (لهذا السبب سنجعل - في ما بعد - عنصر التغليف يمنع المحتوى من الانسياب باستخدام خاصية clear. راجع درس أساسيات الطوفان Float في أوراق الأنماط المُتتالية CSS بهذا الخصوص). ثم - وهذا مهم جدا - نعطي القيمة hidden لخاصية overflow (لو لم نفعل ذلك لعُرِض المحتوى الإضافي طول الوقت) مع تحديد التموضع النسبي position: relative. نستخدم بعدها استعلامات الوسائط Media queries لأخذ عدة شاشات في الحسبان وبالتالي جعل الشبكة متجاوبة. تعني هذه الشيفرة أن تصميم الشبكة سيتكون من ثلاثة أعمدة بالنسبة لسطح المكتب (عرض الشاشة أكبر من 600px)، ثم يتحول إلى عموديْن إذا نقُص عرض الشاشة وفي الشاشات الصغيرة يصبح عمودا واحدا. نستهدف الآن - بعد الانتهاء من تجهيز الخلايا - العناصِر الداخلية، service-icon. وservice-description.؛ ونعطيها تموضعا مطلقا (لهذا السبب أعطينا حدا أدنى للارتفاع في النمط السابق) ثم نضعها في يسار الجزء العلوي. بالنسبة لموضع الوصف service-description. فسنغيره بعد قليل. نجعل العنصريْن الداخليّيْن يملآن كامل العنصر الأب. البقية للتأثير المرئي. أخيرا نستهدف عنصُر الوصف لوحده فنعطي القيمة %100 لخاصية left مما يجعله بالكامل يندفع إلى اليمين خارج مجال الرؤية نظرا لضبط خاصية overflow على hidden ضمن service.. سنستهدغ قيمة left هذه في jQuery، وهو ما يجعل من المهم تعريفها الآن. خط الأيقونةننتقل الآن إلى الخطوة التالية وهي تعريف أسماء الأصناف المُستخدمة ضمن HTML لتُعرَض الأيقونات الصحيحة؛ نستخدم font-face@ للحصول على خطوط الأيقونات. نبحث أولا عن وسيلة لإيجاد خط أيقونات مناسب لاحتياجاتنا. توجد عدة خيّارات اخترنا من بينها موقع Fontastic. تختار على الموقع الأيقونات التي ترغب في استخدامها، يعتمد الاختيار طبعا على نوعية المشروع الذي تعمل عليه. يمكنك تغيير بعض المعلومات مثل أسماء أصناف الأيقونات وأسماء الخطوط على النحو المبيَّن أدناه. اخترنا نفس الأسماء الموجودة في سكربت HTML من أجل تطابق دون مشاكل. يعطيك الموقع مجلدا لتنزيله مع شفرة للاستخدام. ضَع مجلد الخطوط في مجلد CSS (أو أي مجلد آخر يناسبك) ثم خذ الشفرة وضعها في ملف CSS. إليك ما تحتاجه: @font-face { font-family: "slidable-grid"; src:url("fonts/slidable-grid.eot"); src:url("fonts/slidable-grid.eot?#iefix") format("embedded-opentype"), url("fonts/slidable-grid.woff") format("woff"), url("fonts/slidable-grid.ttf") format("truetype"), url("fonts/slidable-grid.svg#slidable-grid") format("svg"); font-weight: normal; font-style: normal; } [class^="icon-"]:before, [class*=" icon-"]:before { font-family: "slidable-grid" !important; font-style: normal !important; font-weight: normal !important; font-variant: normal !important; text-transform: none !important; speak: none; font-size: 4em; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .icon-web:before { content: "a"; } .icon-graphic:before { content: "b"; } .icon-logo:before { content: "c"; } .icon-dev:before { content: "d"; } .icon-3d:before { content: "e"; } .icon-illustration:before { content: "f"; }إن أعدت تحميل المشروع فستظهر الأيقونات. لم يتبقَّ لنا سوى إكمال التنسيق. تنسيقات نهائيةنستكمل ما بقي من تنسيقات. في ما يلي الشفرة التي تضع الشبكة في الوسط وتعطيها العرض الأقصى. نضيف تأثيرا للحوم على الأيقونات: @import url(reset.css); * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } .cf:before, .cf:after { content: " "; /* 1 */ display: table; /* 2 */ } .cf:after { clear: both; } .cf { *zoom: 1; } body { font-family: 'Exo 2', sans-serif; /* Google Font <a href="http://google.com/fonts">http://google.com/fonts</a> */ text-align: center; color: #999; background: #444; -webkit-font-smoothing: antialiased; } #services { max-width: 850px; margin: 0 auto; } #services .service .service-icon:hover { cursor: pointer; color: #249EC2; } #services .service .service-icon span { display: block; -webkit-transition: all 0.1s linear; -moz-transition: all 0.1s linear; transition: all 0.1s linear; } #services .service .service-icon:hover span { position: relative; bottom: 5px; }إضافة jQueryنهدف من خلال استخدام jQuery إلى إعادة استخدام نفس الشفرة البرمجية للشبكة كاملة. ننتظر النقر على إحدى خلايا الشبكة (صنف service) وعند حدوثه نحرك مكان الأيقونة ونزيحها خارج الإطار المرئي ثم نجلب الوصف لعرضه. نضيف أيضا صنفا جديدا (open) لمساعدتنا في معرفة العنصر المرئي. اجلب jQuery إلى الصفحة ثم أضف الشفرة التالية في ملف أو في وسم <script>: $(document).ready(function() { $('.service').click(function() { var $this = $(this); if ($this.hasClass("open")) { $this.find('.service-icon').animate({left: "0"}); $this.find('.service-description').animate({left: "100%"}); $this.removeClass("open"); } else { $this.find('.service-icon').animate({left: "-100%"}); $this.find('.service-description').animate({left: "0"}); $this.addClass("open"); } }); });بعد أن تجهز الصفحة وعند النقر على كتلة service. نختبر هل لدى هذه الكتلة صنف open أم لا. في البداية لا يوجد قسم صنف لدى العناصر service مما يعني أن الاختبار سيتجاوز مباشرة إلى تنفيذ الأوامر الموجودة في else، فنبحث عن عنصر div الذي توجد به الأيقونة (service-icon.) ونحركه إلى اليسار ب%100-؛ ثم نعثر على الوصف ونحركه إلى اليسار عند 0 مما يجعله مرئيا. في الأخير نضيف صنف open إلى service. يعني هذا أنه سيكون لدى service بعد النقر عليه وإظهار المحتوى الإضافي صنفٌ باسم open على النحو الذي يظهر في الصورة التالية. يمكننا بالعودة إلى تعليمة if في الشفرة السابقة أن نرى أنه بالنقر على كتلة service. لديها صنف باسم open فإن الأيقونة تعود إلى الوضعية 0 والوصف ينزاح ب%100. نزيل بعدها الصنف open لتمكن إعادة استخدامه والنقر عليه أكثر من مرة. خاتمة وأفكارلدينا الآن نظام بشبكة لدى كل خلية منها وجهان. يمكن التعديل على الفكرة ومواءمتها حسب طبيعة الاستخدام. يوجد في الصفحة التجريبية مثال آخر يُبدَل فيه وجه الخلية عند الحوم فوقها وليس عند النقر. يمكن الحصول على هذا التأثير عبر إبدال دالة ()click. ب()hover. في شفرة jQuery. يعود الخيار لك. يمكنك أيضا تغيير اتجاه التحريك بإبدال left بالقيمة المناسبة. سرعة الحركة يمكن التعديل عليها هي الأخرى. ابتعد عن الإكثار من استخدام شبكة الخلايا المنزلقة بدون داع، ولا تستعملها إلا إذا كانت تضيف إلى تجربة المستخدم. يمكن تنزيل نتيجة هذا الدرس demo.zip. ترجمة بتصرف لمقال How to create a slidable grid with jQuery لصاحبه Harry Atkins.
  8. يوفر Laravel 5 ميزة جديدة تسمى استعلامات الاستمارة Form Requests تساعد في معالجة بيانات الاستمارة والتحقق من صحتها. ستتعلم في هذا الدرس كيفية إنشاء استمارة اتصال في Laravel 5 باستخدام الميزة المذكورة وإمكانيات Laravel في إرسال البريد الإلكتروني. سنعرض طوال الدرس لميزات وحيل تقنية تتعلق بالتطوير على Laravel. تتكون استمارة الاتصال من ثلاثة حقول هي اسم المستخدم، وعنوان بريده الإلكتروني والرسالة التي يود إرسالها. ملحوظة: يفترض الدرس أن Laravel مثبت وجاهز للعمل. نبدأ بإنشاء مشروع Laravel جديد (أسميته academy) ثم الدخول إلى المجلد: laravel new academy cd academyيمكننا إنشاء متحكِّم Controller خاص بمعالجة استمارة الاتصال وعرضها إلا أننا سندمج هذه الوظيفة - للتسهيل - في المتحكِّم الذي يتولى الأمور الإدارية المتعلقة بالتطبيق. سننشئ لهذا الغرض متحكما باسم AboutController ونستخدم إجراءيْ create وstore لعرض استمارة الاتصال والتعامل معها (يقدم create الاستمارة عبر GET فيما يقدمها store عبر POST). سيستخدم AboutController إجراءات create ،index وstore فقط فسنكتفي بإنشاء متحكِّم بسيط عن طريق أمر artisan make:controller على النحو التالي: $ php artisan make:controller --plain AboutController Controller created successfullyنضيف كُنْيتيْن Aliases ضمن ملف app/Http/routes.php لتمكين الوصول إلى الاستمارة عبر المسار contact/: Route::get('contact', ['as' => 'contact', 'uses' => 'AboutController@create']); Route::post('contact', ['as' => 'contact_store', 'uses' => 'AboutController@store']);ينشئ أمر artisan make:controller السابق متحكما خاويا، نتيجة استخدام خيار plain-- مع artisan. أي أنه يتوجب علينا إضافة دالتَيْ create وstore في متحكم AboutController. عدل على المتحكِّم ليبدو على النحو التالي: <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller; class AboutController extends Controller { public function create() { return view('about.contact'); } public function store() { } } ملحوظة: يوجد المتحكم ضمن المسار app/Http/Controllers الموجود ضمن مجلد المشروع. كل المسارات المذكورة في الدرس تُنسَب إلى مجلد المشروع. ضبطنا إجراء create ليقدم عرضا View باسم contact.blade.php يوجد ضمن المسار resources/views/about. العرض contact.blade.php غير موجود لحد الساعة. فلننشئه. إنشاء استمارة الاتصاليوفر Laravel ميزات عدة لتصْيِير Rendering استمارات HTML. سنستخدم نظام Blade للقَوْلَبة Templating، المضمن في Laravel، لإنشاء الاستمارة. أنشئ مجلدا باسم about في المسار resources/views/ ثم أنشئ داخله ملفا باسم contact.blade.php. وألصق المحتوى التالي: <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> {!! Html::style('css/form.css') !!} <title>Academy Contact</title> </head> <body> <div id="page-wrap"> <h1>Academy contact</h1> <ul> @foreach($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> <div id="contact-area"> {!! Form::open(array('route' => 'contact_store', 'class' => 'form')) !!} {!! Form::label('Your Name') !!} {!! Form::text('name', null, array('required', 'placeholder'=>'Your name')) !!} {!! Form::label('Your E-mail Address') !!} {!! Form::text('email', null,array('required', 'placeholder'=>'Your e-mail address')) !!} {!! Form::label('Message') !!} {!! Form::textarea('message', null, array('required', 'id'=>'Message', 'cols'=>'20', 'rows'=>'20','placeholder'=>'Your message')) !!} {!! Form::submit('Contact Us!', array('class'=>'submit-button')) !!} {!! Form::close() !!} </div> </div> </body> </html>سنعود لشرح هذه الشفرة بعد قليل. 1- تفعيل مكون HTMLفي الاستمارة أعلاه استخدمنا مُنشِئ الاستمارات Form builder الموجود في مكون HTML؛ هذا المكون غير مفعل افتراضيا في Laravel 5. لتفعيله نفذ الخطوات التالية. عدل على ملف composer.json الموجود في مجلد المشروع وأضف التعليمة التالية ضمن خانة require: "illuminate/html": "~5.0"تصبح خانة require على الهيئة التالية بعد إضافة السطر (انتبه للفاصلة , في نهاية السطر الذي يسبق التعليمة التي أضفناها): "type": "project", "require": { "php": ">=5.5.9", "laravel/framework": "5.1.*", "illuminate/html": "~5.0" },حدث اعتماديات Dependencies المشروع عبر الأمر: composer update بعد انتهاء التحديث عدل على ملف config/app.php وأضف السطر التالي ضمن مصفوفة Providers (انتبه للفواصل): 'Illuminate\Html\HtmlServiceProvider',في نفس الملف أضف السطرين التاليين لمصفوفة Aliases: 'Form' => 'Illuminate\Html\FormFacade', 'Html' => 'Illuminate\Html\HtmlFacade',2- شرح آلية تصيير الاستمارةنعود لشرح طريقة إنشاء الاستمارة الموجودة عبر نظام Blade. نبدأ بأول تعليمة: {!! Html::style('css/form.css') !!}نطلب من نظام القوالب تحميل ملف CSS لاستخدامه في الصفحة. سيحمَّل ملف CSS في المكان الذي استُدعِيت فيه دالة style التابعة لصنف Html الذي عرَّفناه في الخطوة السابقة ضمن مصفوفة Aliases، هو وصنف Form. انتبه لكتابة اسم الصنف بنفس طريقة تعريفه في المصفوفة (Html وليس HTML). ملف form.css يوجد ضمن مجلد فرعي (باسم css) من مجلد Public. توضع الملفات المتاحة للعموم (مثل CSS وJavaScript) داخل هذا المجلد. ننتقل للتعليمتين: {!! Form::open(array('route' => 'contact_store', 'class' => 'form')) !!} ... {!! Form::close() !!}تعمل الدالتان Form::open وForm::close() معا من أجل إنشاء وسوم Tags فتح وإغلاق الاستمارة على التوالي. تستقبل دالة Form::open مصفوفة تمثل معطيات إعداد مختلفة؛ مثل كنية route(الوِجهة) التي تشير إلى دالة store ضمن المتحكم AboutController، وclass التي تشير إلى صنف CSS المستخدَم لتنسيق الاستمارة. يُستخدَم إجراء POST افتراضيا إلا أنه يمكن إبداله بGET عبر تمريره في المعطى method على النحو التالي 'method' => 'post'. تضيف دالة Form::open حقلا مخفِيّا عبارة عن رمز _token للحماية من هجمات تزوير الطلب عبر الموقع Cross-site request forgery, CSRF. ثم يأتي الدور على التعليمات التالية: <ul> @foreach($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul>تستخدم هذه التعليمات لإظهار رسائل تحقق إذا كانت مُدخَلات المستخدم غير مناسبة لطبيعة الحقول (سنعود لها لاحقا، لا تنسها). تظهر في ما بعد مجموعة من الدوال من أجل إنشاء حقول الاستمارة: Form::label لإنشاء لصائق Labels الحقول، Form::text لإنشاء حقول نصية، Form::textarea لإنشاء حقول نصية متعددة الأسطر، وForm::submit لإنشاء زر الإرسال. لاحظ أن دالتيْ Form::text وForm::text تحويان معطى باسم خاصية name الخاصة بالحقل (email، name وmessage على التوالي). أضفنا أيضا لكل حقل تشكيلة من الخيارات مثل أسماء أصناف التنسيقات وخصائص HTML. يمكنك بعد حفظ الملف تنفيذ الأمر التالي (من مجلد المشروع) لتشغيل تشغيل خادوم الويب المضمن في Laravel وتجربة النتيجة: php artisan serveأدخل العنوان http://localhost:8000/contact في المتصفح لعرض صفحة الاستمارة. ننتقل الآن، بعد إنشاء الاستمارة، لإعداد آلية التعامل مع محتوى الاستمارة وتمكين إرسال المقترحات عبرها. إنشاء استعلام استمارة الاتصاليضيف الإصدار الخامس من Laravel ميزة جديدة تعرف باستعلامات الاستمارة Form Requests؛ تهدف إلى وضع التصريح Authorization والتحقق من البيانات المرسَلة خارج المتحكِّمات وتغليفها ضمن صنف منفصل. نستخدم artisan لإنشاء استعلام Form request على النحو التالي: $ php artisan make:request ContactFormRequestينشئ الأمر ملفا باسم ContactFormRequest.php مساره app/Http/Requests/ContactFormRequest.php. يبدو هيكل الصنف، بعد حذف التعليقات، كما يلي: <?php namespace App\Http\Requests; use App\Http\Requests\Request; class ContactFormRequest extends Request { public function authorize() { return false; } public function rules() { return [ // ]; } }تحدد دالة authorize ما إذا كان يُسمح للمستخدم الحالي بالتفاعل مع الاستمارة. نريد أن يكون أي مستخدم قادرا على التفاعل مع الاستمارة، لذا نعدل الدالة لتكون القيمة المُرجَعة true بدلا من false. public function authorize() { return true; }تعرِّف دالة rules قواعد التحقق المرتبطة بحقول الاستمارة. الحقول الثلاثة، email، name وmessage جميعها مطلوبة (required)؛ كما أن حقل البريد الإلكتروني يجب أن يحوي عنوانا بريديا صالحا. نعدل دالة rules لإدراج هذه القواعد. public function rules() { return [ 'name' => 'required', 'email' => 'required|email', 'message' => 'required', ]; }توجد مدقِّقات Validators أخرى غير required وemail في Laravel للاستخدام عند الحاجة. لاحظ استخدام أكثر من مدقق في نفس الحقل مع الفصل بينها ب| مثل ما فعلنا مع حقل البريد الإلكتروني. احفظ ملف ContactFormRequest.php ثم افتح المتحكِّم AboutController (الموجود في المسار app/Http/Controllers/AboutController.php) وعدل دالة store لتصبح على النحو التالي: … use App\Http\Requests\ContactFormRequest; class AboutController extends Controller { public function store(ContactFormRequest $request) { return \Redirect::route('contact') ->with('message', 'Thanks for contacting us!'); } }لا تنس إضافة ;use App\Http\Requests\ContactFormRequest إلى المتحكم ليمكنه استخدام قواعد المصادقة المعرَّفة في الصنف ContactFormRequest.php الذي سيتولى التحقق من موافقة مُدخَلات المستخدم للقواعد الموضوعة ويظهر رسالة خطأ في حال أخلَّ المستخدم بإحداها. على سبيل المثال، إذا أدخل المستخدم عنوانا بريديا غير صالح فستظهر رسالة الخطأ التالية. طبعا لن تظهر رسائل الخطأ من العدم. تتولى مصفوفة errors$ المستخدمة في ملف contact.blade.php إظهار هذه الرسائل. هل تذكر هذه الشفرة؟ <ul> @foreach($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> نعود إلى ContactFormRequest.php. قد ترغب في إظهار رسالة (شكر مثلا) للمستخدم عند نجاح إرسال الاستمارة. تمكن إضافة رسالة سريعة ضمن دالة store. يضاف المتغير المُمرَّر للدالة تلقائيا إلى متغيرات الجلسة Session في Laravel مما يمكّن من استخدامها عبر الدالة Session::get. يمكِن على سبيل المثال استخدام الشفرة التالية لإظهار محتوى المتغير message في أي عرض (View) أثناء الجلسة: @if(Session::has('message')) <div class="alert alert-info"> {{Session::get('message')}} </div> @endifبقيت خطوة واحدة لتكون الاستمارة جاهزة لإرسال بريد إلكتروني. نحتاج لإعداد مكوِّن البريد في Laravel وإدماج وظيفة تسليم البريد إلى دالة store. إعداد مكوِّن البريد في Laravelيستخدم Laravel حزمة SwiftMailer لتوفير إمكانية إرسال بريد إلكتروني بيسر. كل ما عليك فعله هو ضبط الإعدادات الموجودة في ملف config/mail.php. نذكر في ما يلي بعض الإعدادات الموجودة في الملف. driver: يدعم Laravel عدة تعريفات بريد مثل SMTP، دالة بريد PHP (mail)، خادوم بريد Sendmail وخدمتَيْ Mailgun وMandrill لتسليم البريد. يجب أن تضبط الإعدادات على التعريف المرغوب بالاختيار بين mailgun، sendmail، mail،smtp و mandrill. host: تعيين اسم المستضيف لخادوم البريد الإلكتروني (عند استخدام التعريف smtp).port: تعيين اسم منفَذ خادوم البريد الإلكتروني (عند استخدام التعريف smtp).from: إذا رغبت في أن تستخدم كل الرسائل المرسَلة نفس العنوان البريدي والاسم للمرسِل فبإمكانك ضبط إعدادات from وaddress المعرَّفة في هذه المصفوفة.encryption: يحدد بروتوكول التعميّة Encryption المستخدَم لإرسال البريد الإلكتروني.username: يعين اسم المستخدم في حال كان التعريف smtp.password: كلمة سر المستخدم في حال كان التعريف smtp.sendmail: مسار خادوم Sendmail إذا كانت قيمة driver هي sendmail.pretend: يطلب هذا الإعداد من Laravl تجاهل التعريف المضبوط وإرسال البريد إلى سجل التطبيق بدلا منه. يناسب التطبيقات التي مازالت قيد التطوير والتجربة.نأخذ مثالا لإعداد إرسال البريد في Laravel لاستخدام حساب بريد Google. غير الإعدادات التالية مثل ماهو مبيَّن. أعط القيمة smtp للتعريف driver. هذه هي القيمة الافتراضية.أبدل قيمة host إلى smtp.google.comاستخدم المنفذ port رقم 465.أبدل التعميّة encryption إلى ssl.استخدم حساب البريد الإلكتروني ضمن إعداد username.اكتب كلمة سر حساب البريد الإلكتروني في إعداد password. تذكر أنه يجب، ضمن بيئة عمل فعلية، حفظ هذه المعلومات ومعلومات أخرى حساسة ضمن ملف env. في مشروعك أو في متغير البيئة في الخادوم.احفظ التعديلات على ملف config/mail.php ثم عدل المتحكم AboutController ليبدو على النحو التالي: public function store(ContactFormRequest $request) { \Mail::send('emails.contact', array( 'name' => $request->get('name'), 'email' => $request->get('email'), 'user_message' => $request->get('message') ), function($message) { $message->from('wj@wjgilmore.com'); $message->to('wj@wjgilmore.com', 'Admin')->subject('Academy Feedback'); }); return \Redirect::route('contact')->with('message', 'Thanks for contacting us!'); }الدالة Mail::send هي المسؤولة عن ابتداء عملية تسليم البريد، وتستقبل ثلاثة معطيات. يحدد المعطى الأول اسم العرض (View) المستخدم لقالب جسم (محتوى) البريد الإلكتروني. المعطى الثاني هو مصفوفة بالبيانات المتاحة للاستخدام في جسم البريد الإلكتروني. البيانات المتاحة في حالتنا هي تلك القادمة من استمارة الاتصال عبر الكائن request$. يمنح المعطى الثالث إمكانية تحديد خيارات إضافية للبريد الإلكتروني مثل المرسِل، المستقبِل والموضوع. سنحتاج لإنشاء العرض الذي توجد به محتويات البريد. لذا سننشئ ملفا باسم contact.blade.php في المسار resources/views/emails. طبعا يمكنك تغيير اسم ومجلد العرض؛ إن فعلت فلا تنس التعديل على المتحكم AboutController بما يوافقك تغييراتك. You received a message from academy.hsoub.com: <p> Name: {{ $name }} </p> <p> {{ $email }} </p> <p> {{ $user_message }} </p>ملحوظة: يوجد سجل التطبيق في المسار storage/logs/laravel.log. عند إعطاء القيمة true لpretend ستحاكي إرسال بريد دون أن ترسله فعليا وسيظهر سطر يشبه التالي في السجل: [2015-07-12 11:34:51] local.INFO: Pretending to mail message to: wj@wjgilmore.comعُد، بعد حفظ التعديلات، إلى استمارة الاتصال؛ اكتب بيانات صحيحة ضمن حقول الاستمارة وأرسلها. يجب أن تصل الرسالة إلى البريد الموجود في دالة to (أي wj@wjgilmore.com في المثال). قد تواجه مشاكل مع إعدادات Gmail بسبب التقييدات على تطبيقات الطرف الثالث في Gmail. راجع إعدادات حساب Google في هذه الحالة. خاتمةاخترنا عرض جميع المخرجات المذكورة في هذا الدرس باللغة الإنجليزية للتركيز على الخاصية المشروحة. إن أردت توطين Localization المثال واستخدام اللغة العربية فيمكنك كتابة العبارات العربية مباشرة في ملفات القوالب لكن ستبقى رسائل الخطأ باللغة الإنجليزية ويجب عليك إدراج رسائل خطأ خاصة بالعربية. على العموم يُنصَح، لمن أراد توفير قابلية التوطين بسهولة، بعدم كتابة المُخرجات مباشرة في الشفرة Hard code بل إنشاء ملف بسلاسل المحارف Strings المستخدمة واستدعاء ملف اللغة المناسبة عن طريق دالة الترجمة trans. يُنشَأ مجلد لكل لغة مدعومة ضمن المسار resources/lang الذي يوجد فيه افتراضيا مجلد للغة الإنجليزية en به ملفات بالعبارات المستخدمة افتراضيا. ترجمة بتصرف لمقال Creating a Contact Form in Laravel 5 Using the Form Request Feature لصاحبه Jason Gilmore
  9. تعد مكتبة jQuery واحدة من أكثر المكتبات استخداما للإضافة على الصفحات؛ إذ أنها تجعل من التعامل مع نموذج كائن المستند Document object model, DOM أمرا في منتهى اليسر. لا شك أن السهولة في التعامل سبب أساسي في شعبية jQuery، إذ يبدو بالإمكان فعل أي شيء نريده عن طريق هذه المكتبة. توجد، من بين الخيارات المتاحة أمامنا، مقاطع Snippets تجنح إلى الظهور المرة تلو الأخرى. سنعرِض في هذا المقال إلى عشرة مقاطِع سيستخدمها الجميع، من المبتدئ إلى المتمكن، مرارا وتكرارا. زر العودة إلى الأعلى// عد إلى الأعلى $('a.top').click(function(){ $(document.body).animate({scrollTop : 0}, 800); return false; }); // Anchor tag أنشئ وسما للمربط <a class="top" href="#">عد إلى الأعلى</a>يتضح أننا لا نحتاج إلى إضافة Plugin لـ jQuery من أجل الحصول على تحريك سهل إلى الأعلى؛ يكفي استخدام دالتي animate و scrollTop. يمكن تغيير المكان الذي يحُط فيه شريط التمرير عبر تغيير قيمة scrollTop. في المثال أعلاه استخدمنا القيمة 0 لأننا نريد العودة إلى أعلى الصفحة، لكن لو أردنا زَيحانًا offset بقيمة 100px فيمكن إدراج هذه القيمة. يتلخص ما فعلناه في تحريك جسم المستند Document body طوال 800 ملي ثانيّة حتى يصل إلى القمّة. التحقق من تحميل الصور$('img').load(function() { console.log('image load successful'); });تحتاج أحيانا إلى التأكد من تحميل كل الصور قبل إكمال السكربت؛ تؤدي الأسطر الثلاثة أعلاه هذه المهمة بكل يُسر. يمكن أيضا التحقق من تحميل صورة معينة عبر إبدال وسم tag بمعرِّف ID أو صنف Class. تصحيح الصور المعطوبة تلقائيا$('img').error(function(){ $(this).attr('src', 'img/broken.png'); });تحصل أحيانا أعطاب في روابط الصور على الموقع يكون معها إبدال الروابط يدويا أمرا صعبا. يعمل المقطع أعلاه على إبدال الصور المعطوبة تلقائيا مما ينقِذ من الكثير من المشاكل. تبديل الصنف عند الحومان Hover$('.btn').hover(function(){ $(this).addClass('hover'); }, function(){ $(this).removeClass('hover'); } );نرغب عادة في تغيير مظهر العناصر القابلة للنقر في صفحة الويب عندما يحوم حولها المؤشر وهو بالضبط ما تفعله الأسطر في المقطع أعلاه؛ فتضيف صنفا للعنصر عند الحوم حوله ثم تزيل الصنف عندما يكُفّ المستخدِم. كل ما عليك فعله هو إضافة التنسيق المرغوب ضمن ملف CSS. تعطيل حقول الإدخال$('input[type="submit"]').attr("disabled", true);قد تريد تعطيل زر الإرسال أو حقل إدخال إلى أن يؤدي المستخدم إجراء معينا (التأشير على صندوق “قرأتُ الشروط”، مثلا). يضيف المقطع خاصية disabled (مُعطَّل) إلى الحقل مما يتيح لك تفعيله عندما تريد. كل ما عليك فعله لتفعيل الحقل هو تنفيذ الدالة removeAttr مع تمرير المُعطى disabled على النحو التالي: $('input[type="submit"]').removeAttr("disabled");إيقاف تحميل الروابط$('a.no-link').click(function(e){ e.preventDefault(); });قد نود أن تؤدي الروابط أعمالا أخرى غير الانتقال إلى صفحة أو حتى إعادة تحميلها، وهو ما تعمل عليه الأسطر أعلاه عبر تعطيل الإجراء الافتراضي Default action. قد يكون السبب - على سبيل المثال - تنشيطَ سكربت آخر. التبديل بين تأثيريْ التلاشي Fade والانزلاق Slide// تلاش $(".btn").click(function() { $(".element").fadeToggle("slow"); }); // تبديل $(".btn").click(function() { $(".element").slideToggle("slow"); });تأثيرا التّلاشي والانزلاق من أكثر التّأثيرات في jQuery استخداما. عندما نريد فقط عرض عنصر عند النقر فإن دالتي fadeIn وslideDown ملائمتان تماما. لكن إن أردنا أن يظهر العنصر بعد النقرة الأولى ثم يختفي بعد الثانية فهذا المقطع يؤدي المهمة بنجاح. تأثير الطّيّ// أغلق كل اللوحات $('#accordion').find(‘.content').hide(); // تأثير الطّيّ $('#accordion').find('.accordion-header').click(function(){ var next = $(this).next(); next.slideToggle('fast'); $('.content').not(next).slideUp('fast'); return false; });كل ما تحتاجه إلى جانب هذا المقطع هو شفرة HTML المناسبة للتأثير. أولا نغلق كل اللوحات ثم عند حدث النقر click ينزلق المحتوى المربوط بالترويسة accordion-header بالتتابع. هذه طريقة سهلة للحصول على تأثير طي بسرعة. تحديد ارتفاع عنصر div اعتمادا على آخر$('.div').css('min-height', $('.main-div').height());تمكِّن هذه الطريقة من تحديد نفس الارتفاع لعنصريْ div بغض النظر عن محتوى كل منهما. في السطر أعلاه عيّنا ارتفاع عناصر div بحيث يكون لديها على الأقل ارتفاع العنصر main-div. لائحة بألوان مختلفة حسب العناصِر الزوجية والفردية$('li:odd').css('background', '#E8E8E8');يمنح هذا السطر خلفية باللون المحدد للعناصر الفردية من اللائحة؛ مما يمكنك من الحصول على لائحة مخطَّطة - مثل هيئة الحمار الوحشي - عبر إضافة لون خلفية افتراضي في ملف CSS تأخذه العناصر الزوجية من اللائحة. لا يقتصر استخدام هذه الطريقة على اللوائح بل يتعداها إلى الجداول وعناصر div وغيرها. ترجمة بتصرّف لمقال 10 jQuery snippets every designer should know لصاحبته Sara Vieira.
  10. نستخدِم المحدِّدات Selectors في كلّ مرّة نستخدم فيها CSS. تبقى محدِّدات CSS - رغم كثرة الاستخدام - أحد الأجزاء الأكثر إهمالًا في ملفّ المواصفة Specification. نتحدّث كثيرًا عن التّحويلات Transformations الكبرى في CSS3 لكنّنا دائمًا ننسى الأساسيّات. يجعل الاستخدام الجيّد للمحدّدات من كتابة الشّفرة أيسر وأكثر أناقة. سنغطّي في هذا المقال عشرة محدّدات يقلّ الانتباه إليها في الغالب، إلّا أنّها تجمع بين الفائدة العاليّة والفعاليّة. *ربّما يكون محدِّد * الأسهل تذكّرا إلّا أنّه في الغالب لا يُستخدَم بكثرة. يتمثّل عمل هذا المحدِّد في تنسيق جميع العناصر الموجودة في الصّفحة؛ ومن الجيّد استخدامُه لإعادة العناصر للوضع الابتدائيّ Reset وإنشاء بعض التّنسيقات الافتراضيّة للصّفحة مثل مجموعة الخطوط Font family والحجم Size. * { margin: 0; padding: 0; font-family: helvetica, arial, sans-serif; font-size: 16px; }A + Bيُسمَّى محدّد المُجاوِر Adjacent ويعمل على تحديد العنصُر (B) الّذي يتبع العُنصُرَ الأوَّل (A) مباشرةً. العنصران A وB يوجدان في نفس المستوى. نحدّد في المثال التّالي أوَّلَ عنصر div بعد التّرويسة Header: header + div { margin-top: 50px; }A > Bيُشبه عملُه عملَ المحدِّد A B. يكمن الاختلاف في أنّ A B يحدّد كلّ العناصر المتفرّعة من العنصُر A مهما كان المستوى الّذي توجد به ؛ أما A > B فيكتفي بالعناصر الفرعيّة ذات المستوى الأوّل. يُنصَح باستخدام المحدِّد A > B عند التّعامل مع الأبناء المباشرين للعنصُر الأب. لاحِظ المثال التّالي: <h1> <em>هامّ:</em> هذا <span> العنوان <em>مهمّ</em> جدًّا </span> </h1>التّنسيق التّاليّ يلوّن عنصُر em المتفرّع عن h1 مباشرةً بالأحمر: h1 > em { color: red; }لو استخدمنا المحدّد A B لأعطى اللّون الأحمر لكلّ عناصر em في المثال السّابق. ["A[href*="exampleيُستخدَم لتنسيق رابط خاصّ بطريقة مغايرة. يُقارن ما يوجد بين ظفريْن بعنوان الرّابط فإن وُجدت مطابقة نُفِّذ التّنسيق. المثال التّاليّ يجعل كلّ الّروابط الّتي تُحيل إلى فيسبوك تبدو زرقاء: a[href*="facebook"] { color: blue; }يُمكن استخدام المحدِّد دون علامة *؛ يجب في هذه الحالة أن يكون التّطابق مع الرّابط تامًّا. (A:not(Bيستخدم عبارة النّفي not لاستبعاد ما لا يُطابق B. المثال التّاليّ يحدّد كلّ عناصر div الموجودة في الصّفحة ما عدا التّذييل Footer: div:not(.footer) { margin-bottom: 40px; }A:first-child / A:last-childيسمح بتحديد أوّل أو آخر عنصُر فرعيّ من العنصُر A (على التّوالي A:first-child وA:last-child). يُمكن أن يكون المحدِّد مفيدًا عند التّعامل مع عناصر لائحة، لحذف هامش أو إطار على سبيل المثال. في ما يلي نحذف الإطار عن أوّل عنصُر من اللّائحة والهامش عن آخر عنصُر: ul li:first-child { border: none; } ul li:last-child { margin-right: 0px; }(A:nth-child(nيوفّر المحدِّد nth-child وسيلةً سهلة لاختيّار أيّ عنصُر فرعيّ حسب ترتيبه. يحدّد المثال التّالي العنصُر الثّالث ضمن لائحة غير مرتَّبة ul ويطبِّق عليه التّنسيق. ul li:nth-child(3) { background: #ccc; }يُمكن استخدام هذا المحدّد أيضًا لاختيّار العناصِر الّتي ترتيبها مُضاعِف لعدد معيَّن عبر إضافة n بعد العدد. المثال التّالي يحدِّد العناصر ذات التّرتيب 3، 6، 9، 12 وهكذا. ul li:nth-child(3n) { background: #ccc; }(A:nth-last-child(nيُشبِه عمله عمل المحدِّد nth-child ويُستعمل على نفس المنوال؛ لكنه يبدأ حساب التّرتيب من آخر عنصُر، بدلًا من أوّل عنصُر. يعني هذا أنّك عند إعطاء العدد 2 لتحديد عنصُر من لائحة فسيختار العنصُر قبل الأخير في التّرتيب. ul li:nth-last-child(2) { background: #ccc; }(A:nth-of-type(nيحدّد العنصُر من نوع A الّذي يظهر للمرّة n. نختار في المثال التّاليّ ثالث فقرة (عنصُر p) ضمن div. div p:nth-of-type(3) { font-style: italic; }A:visitedهل لاحظت في ما مضى أنّ الصّفحات الّتي سبقت لك زيّارتها تظهر بلون مختلف في نتائج Google؟ هذا بالضّبط هو عمل المحدِّد visited. يمثّل هذا التّنسيق إضافةً كبيرة للمستخدِم؛ إلّا أنّه يُهمَل في بعض الأحيان. a:visited { color: blue; }خاتمةيظهر لي - حسب خبرتي - أنّ هذا النّوع من المحدِّدات يُمكن أن يختصر الكثير من الوقت ويجنِّب تكدّس الكثير من المعرّفات ID داخل الوسوم. ليست هذه سوى البداية، توجد الكثير من محدِّدات CSS الأخرى المفيدة الّتي تُهمَل أحيانًا. هل تستخدِم محدِّدات CSS؟ هل استخدامها أيسر من المعرِّفات والأصناف Classes؟ شاركنا تجربتك عبر التّعليقات. ترجمة بتصرّف لمقال 10 CSS selectors you shouldn’t code without لصاحبته Sara Vieira.
  11. تمنح تقنيّة Ajax مواقع الويب إمكانيّةَ تحميل محتوى إلى عناصر مختلفة ضمن الصّفحة دون الحاجة لإنعاش Refresh الصّفحة. قد لا تبدو - للوهلة الأولى - ميزةً مؤثّرة، إلّا أنّها تمكّن من فعل الكثير من الأمور. تستخدم الكثير من الإجراءات مثل التّصويت، إبداء الإعجاب، التّعليقات على Disqus والتّغريدات على تويتر؛ وإجراءات أخرى كثيرة Ajax للحصول على تجربة مستخدم User experience لا مثيل لها. ذاع صيت Ajax سنة 2005 عندما استخدمته Google في ميزة اقتراحات البحث، الّتي تُظهر اقتراحات أثناء الكتابة على محرّك بحث Google. لم تكن هذه الميزة لترى النّور لولا وجود تقنيّة Ajax. سنعرض في هذا المقال لكيفيّة استخدام Ajax في ووردبريس. سيكون الأمر أكثر تعقيدًا قليلًا ممّا تعوّدت عليه، إلّا أنّ الأمور ستجري على ما يُرام إن كان لديك فهم جيّد CSS، HTML وPHP؛ إضافةً لفهم لأساسيّات Javascript. كيف يعمل Ajaxليس Ajax في الواقع سوى Javascript. تأتي كلمة Ajax اختصارًا لAsynchronous Javascript And XML (أي: Javascript وXML لا تزامنيّان). تهدف تقنيّة Ajax إلى أن تكون جسرًا بين موقع الويب والخادوم، وهو ما يعني أنّك ستسخدمها بالتّزامن مع Javascript، CSS، HTML وربّما PHP أيضًا؛ الأمر الّذي قد يعقّد الأمور قليلًا. يعدّ الكائن XMLHttpRequest الّذي يتبادل البيانات مع الخادوم الأساس الّذي تقوم عليه تقنيّة Ajax. يشبه عمل Ajax عمومًا إرسال استمارة في صفحة ويب. في ما يلي الخطوات الأساسيّة: تحدّد البيانات الّتي تريد إرسالهاتضبُط نداء Ajaxتستخدم كائن XMLHttpRequest لإرسال البيانات إلى الخادومتحصُل على إجابة من الخادوم. يُمكن استخدام ردّ الخادوم ضمن شفرة Javascript.يكثُر استخدام Ajax عبر دوالّ التّغليف Wrapper functions في jQuery، لذا سنركّز على هذه الطّريقة في هذا المقال. أوّل نداء Ajaxسنحتاج قبل البدء في كتابة شفرة Ajax إلى قالب يُمكن التّعديل عليه، قالب فرعيّ أو إضافة. أنصح باستخدام إضافة. إنشاء إضافة سهل: أنشئ مجلَّدًا فرعيًّا ضمن wp-content/plugins (اخترتُ اسم ajax-text). أنشئ ملفّ PHP ضمن المجلَّد وأعطِه نفس اسم المجلَّد (ajax-text.php) وألصِق الشفرة التّاليّة في الملفّ: <?php /** * Plugin Name: Ajax Test * Plugin URI: <a href="http://danielpataki.com">http://danielpataki.com</a> * Description: This is a plugin that allows us to test Ajax functionality in WordPress * Version: 1.0.0 * Author: Daniel Pataki * Author URI: <a href="http://danielpataki.com">http://danielpataki.com</a> * License: GPL2 */ ستظهر الإضافة، بعد حفظ الملفّ، في ركن الإضافات في لوحة التّحكّم بووردبريس. فعّلها. للمزيد حول تطوير إضافات ووردبريس راجع دليل مدخل إلى برمجة إضافات ووردبريس. 1- صفّ Enqueueing ملفّ Javascriptسنحتاج لماستخدام ملفّ JavaScript في الإضافة، لذا سنصُفّ الملفّ. نستخدم الشّفرة التّاليّة لهذا الغرض: add_action( 'wp_enqueue_scripts', 'ajax_test_enqueue_scripts' ); function ajax_test_enqueue_scripts() { wp_enqueue_script( 'test', plugins_url( '/test.js', __FILE__ ), array('jquery'), '1.0', true ); }تُضيف الشّفرة أعلاه ملفّ test.js إلى الموقع. أخبرنا ووردبريس أنّ jQuery اعتماديّة يجب تحميلها في التّذييل Footer. الخطوة التّاليّة هي إنشاء نداء Ajax بسيط داخل ملفّ Javascript. 2- إنشاء نداء Ajaxيحوي نداء Ajax على العديد من المعطيات، سنتعرّف على الأساسيّة منها. jQuery(document).ready( function($) { $.ajax({ url: "http://yourwebsite.com", success: function( data ) { alert('يبلُغ عدد عناصر div في الصّفحة الرّئيسيّة لديك:' + $(data).find('div').length); } }) })كلّ ما فعلناه هو استخدام دالّة ()ajax.$ وتحديد بعض الخيّارات. يحدّد المُعطى url لنداء Ajax أين يجب أن يُرسل الطّلب الّذي تستقبل الدّالةُ الموجودة في المعطى success الإجابةَ عليه. يجب أن يُحيل المُعطى url إلى رابط الصّفحة الرّئيسيّة لموقعك. ستظهر لك، إذا نفّذت الخطوات بطريقة صحيحة، نافذةٌ منبثقة بعدد عناصر div الموجودة في الصّفحة الرئيسيّة فورَ إنعاش الصّفحة (ﻷخذ التّغييرات على الإضافة في الحسبان). لا يبدو المظهر أنيقًا؛ إلّا أنّ ما فعلناه في الواقع هو تحميل صفحة مختلفة تمامًا دون إعادة تحميل تلك الّتي نوجد فيها. مثال كامل لاستخدام Ajax في ووردبريسيوفّر ووردبريس دعمًا أكبر للمساعدة في استخدام نداءات Ajax ويقدّم آليّة معياريّة لتأديّتها. سنُبدِل عنوان url بآخر أكثر ديناميكيّة وننشئ طريقةً أفضل للتّخاطب مع الخادوم. سنعرض في المثال التّالي كيفيّةَ إضافة ميزة “أعجبني هذا المقال” إلى موقعنا. أنشأتُ إضافةً جديدة لهذا الغرض باسم “post-love”، يمكن الملفّ المضغوط للإضافة في آخر الدّرس. ملحوظة: يجب تعطيل وحذف الإضافة السّابقة ajax-text حتى يمكن للإضافة الجديدة post-love العمل بطريقة صحيحة. 1- تخزين وعرض البياناتسنستخدم حقلًا مُخصَّصًا Custom field لحفظ عدد الإعجابات الّتي تلقّاها المنشور. سنسمّي هذا الحقل post_love وسنعرض قيمته تحت كلّ مقال في صفحة منفردة Single page. في ما يلي كيفيّة ذلك (ملفّ content-filter.php): add_filter( 'the_content', 'post_love_display', 99 ); function post_love_display( $content ) { $love_text = ''; if ( is_single() ) { $love = get_post_meta( get_the_ID(), 'post_love', true ); $love = ( empty( $love ) ) ? 0 : $love; $love_text = '<p class="love-received"><a class="love-button" href="#" data-id="' . get_the_ID() . '">أبدِ(ي) إعجابك</a><span id="love-count">' . $love . '</span></p>'; } return $content . $love_text; } يمكننا عبر استخدام content-filter.php عرض أيّ شيء نُريده أسفل محتوى المقال مباشرةً. نستخدم دالّة ()is_singl للتّأكّد من أنّ الإعجابات تظهر على الصّفحات المنفردة. نحصُل على عدد الإعجابات من الحقل المخصَّص post_love ونتأكّد من أنّ العدد يساوي صفرًا إن لم توجد إعجابات حتى اللّحظة. استخدمنا HTML لعرض رابط لإبداء الإعجاب وعدد الإعجابات. نحصُل على معرّف المقال عبر الدّالّة ()get_the_ID ونمرّره لدالّة get_post_meta الّتي نحصُل من خلالها على قيمة الحقل المخصَّص post_love؛ ثمّ في اﻷخير نُرجِع المحتوى متبوعًا بالنّص الّذي أنشأناه للتّو. نستخدم CSS لإضافة بعض التّحسينات على طريقة العرض عبر صفّ ملفّ CSSكان تفهم جيّدًا على النّحو التّاليّ: add_action( 'wp_enqueue_scripts', 'post_love_assets' ); function post_love_assets() { if( is_single() ) { wp_enqueue_style( 'love', plugins_url( '/love.css', __FILE__ ) ); }لاحظ استخدام الشّرط ()is_single قبل تحميل ملفّ CSS، يعود السّبب في ذلك إلى أنّه لا معنى لتحميل النّمط في صفحات غير منفردة، حيثُ إنّه لا يُستخدَم إلّا فيها؛ الأمر الّذي يُساعد في التّقليل من تأثير الإضافة على زمن تحميل الموقع. أنشئ ملفّ love.css في مجلَّد الإضافة وضمّن فيه النّمط التّالي: .entry-content .love-button { background: #f14864; color: #fff; padding:11px 22px; display:inline-block; border:0px; text-decoration: none; box-shadow: 0px 6px #d2234c; position:relative; } .entry-content .love-button:hover{ top:3px; box-shadow: 0px 3px #d2234c; } .entry-content .love-button:active{ top:6px; box-shadow: none; } #love-count { background: #eee; box-shadow: 0px 6px #ddd; color: #666; padding:11px 22px; display:inline-block; }تظهر النّتيجة بعد إكمال الخطوات السّابقة على هيئة زرّ بتأثير ثلاثيّ الأبعاد يوجد أمامه عدّاد. 2- نداء Ajax الخطوة التّاليّة هي تجميع نداء Ajax. يستدعي ذلك كتابة السكربت وصَفَّه. سنناقش، قبل البدْء في كتابة السّكربت، معطَى url الموجود في نداء Ajax. في المثال السّابق (ajax-text) كتبنا عنوان الموقع مباشرةً ضمن الشّفرة البرمجيّة، وهي طريقة غير صحيحة أثناء إنشاء الإضافات؛ فكلّ موقع لديه عنوانه الخاصّ. ملحوظة: سنحذف الشّفرة التّاليّة ونعيد كتابتها من جديد مع إضافة نداء Ajax: add_action( 'wp_enqueue_scripts', 'post_love_assets' ); function post_love_assets() { if( is_single() ) { wp_enqueue_style( 'love', plugins_url( '/love.css', __FILE__ ) ); }يوفّر ووردبريس ملفًّا موّحدًا للاستخدام (wp-admin/admin-ajax.php) ووسائلَ للحصول على مسار الملفّ كامِلًا. يستدعي الملفّ بعض الحيّل أثناء صفّه، نعرض لها في الشّفرة التّاليّة: add_action( 'wp_enqueue_scripts', 'ajax_test_enqueue_scripts' ); function ajax_test_enqueue_scripts() { if( is_single() ) { wp_enqueue_style( 'love', plugins_url( '/love.css', __FILE__ ) ); } wp_enqueue_script( 'love', plugins_url( '/love.js', __FILE__ ), array('jquery'), '1.0', true ); wp_localize_script( 'love', 'postlove', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) )); }تُسجّل الشّفرة أعلاه ملفّ Javascript بالطّريقة المُعتادة، ثمّ تمرّر سلسلة محارف String إلى السّكربت عبر الدّالّة ()wp_localize_script (تُستخدَم هذه الدّالّة عادةً لترجمة سلاسل المحارف الموجودة في Javascript). يُمكنك إضافة سلاسل محارف إلى المصفوفة Array بحسب حاجتك. يمكننا الآن استخدام المتغيّر postlove.ajax_url في ملفّ love.js للحصول على مسار admin-ajax.php، على النّحو التّاليّ: jQuery( document ).on( 'click', '.love-button', function() { var post_id = jQuery(this).data('id'); jQuery.ajax({ url : postlove.ajax_url, type : 'post', data : { action : 'post_love_add_love', post_id : post_id }, success : function( response ) { alert(response) } }); })ليست هذه النّسخةَ النّهائيّة، لكنّنا وضعنا أساس التّخاطب مع الخادوم. ننفّذ دالّة في كلّ مرة يُضغَط فيها على الزّرّ أبد(ي) إعجابك، ثمّ نأخذ معرّف المقال ونخزّنه في المتغيّر post_id. يُنشئ سكربت love.js نداء Ajax يأخذ في معطى url قيمةَ المتغيّر postlove.ajax_url. بالنّسبة لمعطى type فقد أعطيناه القيمة post أيّ أنّ التّخاطب مع الخادوم سيكون عبر طلبات Post وهي ما يعني أنّنا سنستخدِم المتغيّر POST_$، من جانب الخادوم، للحصول على البيانات. توجد في قسم data كلّ المعطيات الّتي تودّ إرسالها. نحتاج لإرسال معرّف المقال (post_id) لنعرِف المقال الّذي أبدى الزّائر إعجابه به. يطلُب ووردبريس تحديد إجراء Action عند استخدام الملفّ admin-ajax.php. حدّدنا الإجراء post_love_add_love. أخيرًا نعرض النّتيجة في نافذة منبثقة. إذا جربّت التّعديلات الجديدة وضغط على زرّ إبداء الإعجاب فستظهر رسالة منبثقة تحوي عدد مرّات إبداء الإعجاب (0). لم نعرّف خطّافًا Hook للإجراء post_love_add_love في الشّفرة أعلاه وهو ما يؤدّي بadmin-ajax إلى إرجاع القيمة 0. سنعرّف في الفقرة المواليّة الخطّاف النّاقص. 3- المعالجة من جانب الخادوموصلنا في هذه المرحلة إلى إرسال بيانات إلى الخادوم دون أن نخبره مالّذي يجب عليه فعله بها. يجب، عند الضّغط على الزّرّ، زيّادة عدد الإعجابات ب1 وإرجاع القيمة الجديدة. سنحتاج لإنشاء خطّافَين لهذا الغرض. add_action( 'wp_ajax_nopriv_post_love_add_love', 'post_love_add_love' ); add_action( 'wp_ajax_post_love_add_love', 'post_love_add_love' ); function post_love_add_love() { }يُنفَّذ الخطّاف الأوّل بالنّسبة للزّوّار الضّيوف، والثّاني لمُسجَّلي الدّخول. بالمناسبة، هذه طريقة جيّدة للتّحكّم في الوصول. الاتّفاق هو كالتّالي: هل تتذكّر كيف عرّفنا معطى الإجراء في نداء Ajax؟ نحتاج إلى إلحاق هذا المعطى ب_wp_ajax و/أو _wp_ajax_nopriv. بالنّسبة للدّالّة يُمكن استخدام أيّ تسميّة، استخدمنا نفس سلسلة المحارف الموجودة في اسم الإجراء للحفاظ على التّجانس. نُضيف الآن وظيفة الزّيّادة بالتّدرّج Incrementing add_action( 'wp_ajax_nopriv_post_love_add_love', 'post_love_add_love' ); add_action( 'wp_ajax_post_love_add_love', 'post_love_add_love' ); function post_love_add_love() { $love = get_post_meta( $_POST['post_id'], 'post_love', true ); $love++; update_post_meta( $_POST['post_id'], 'post_love', $love ); echo $love; die(); }نعثُر على القيمة الحاليّة، نزيدها بواحد ثمّ نخزّن القيمة الجديدة ونعرضها. يجب تنفيذ ()die في نهاية الدّالّة وإلّا فإنّ ملفّ admin-ajax.php سينفّذ (0)die الخاصّة به ممّا ينتج عنه عرض 0 إضافيّ في الإجابة. تظهرعند الضّغط على زر إبداء الإعجاب الآن نافذة منبثقة بها عددُ الإعجابات، وعند إنعاش الصّفحة يظهر العدد إلى جانب الزّرّ المذكور. لم يتبقّ لنا سوى جعل العدد يتغيّر دون الحاجة لإنعاش الصّفحة. تتكفّل شفرة Javascript التّاليّة بهذه المهمّة: jQuery( document ).on( 'click', '.love-button', function() { var post_id = jQuery(this).data('id'); jQuery.ajax({ url : postlove.ajax_url, type : 'post', data : { action : 'post_love_add_love', post_id : post_id }, success : function( response ) { jQuery('#love-count').html( response ); } }); })يؤدّي الضّغط على الزّرّ الآن إلى زيّادة عدد الإعجابات دون الحاجة لإنعاش الصّفحة. 4- لمسات أخيرةربّما لاحظت أنّنا أضفنا في زرّ الإعجاب رابطًا إلى #: <a class="love-button" href="#" data-id="' . get_the_ID() . '">أبدِ(ي) إعجابك</a>يعود السّبب في ذلك إلى أخذ الحالة الّتي يكون Javascript فيها غير مفعَّل لدى الزّائر. في هذه الحالة يؤدّي الضّغط على الزّرّ إلى إعادة تنزيل الصّفحة. إلّا أنّ استخدام # وحده ضمن وسم Tag الرّابط يجعل هذا الأخير غير صالح Invalid. فلنعالج هذا الأمر. ملحوظة: يُستخدَم ماسك المكان Placeholder # في خاصيّة href ضمن وسم <a> للدّلالة على أنّ الرّابط يُحيل إلى عنصر من الصّفحة يُعطَى معرّفه بعد ماسك المكان (مثلًا:"a href="#id5). استخدام # لوحده يعني أنّ الرّابط يُحيل إلى معرّف غير موجود في الصّفحة. في المحصّلة يُحيل الرّابط إلى الصّفحة لكنّه يبقى غير صالح دلاليًّا Semantic. سنعدّل على الشّفرة بحيث يُحيل الرّابط إلى نداء Ajax، مثلًا: http://yourwebsite.com/wp-admin/admin-ajax.php?action=post_love_add_love&post_id=23 بالنّسبة للزّوّار الّذين يفعّلون Ajax. add_filter( 'the_content', 'post_love_display', 99 ); function post_love_display( $content ) { $love_text = ''; if ( is_single() ) { $love = get_post_meta( get_the_ID(), 'post_love', true ); $love = ( empty( $love ) ) ? 0 : $love; $love_text = '<p class="love-received"><a class="love-button" href="' . admin_url( 'admin-ajax.php?action=post_love_add_love&post_id=' . get_the_ID() ) . '" data-id="' . get_the_ID() . '">give love</a><span id="love-count">' . $love . '</span></p>'; } return $content . $love_text; }إذا كان Javascript مفعَّلًا في المتصفّح فيجب تعطيل إمكانيّة تتبّع رابط URL. لذا نُضيف return false إلى الدّالّة في حدث click كما يلي: jQuery( document ).on( 'click', '.love-button', function() { var post_id = jQuery(this).data('id'); jQuery.ajax({ url : postlove.ajax_url, type : 'post', data : { action : 'post_love_add_love', post_id : post_id }, success : function( response ) { jQuery('#love-count').html( response ); } }); // نُرجع القيمة false هنا return false; })الخطوة الأخيرة هيّ التّفريق بين عمليّات Ajax والعمليّات الأخرى ضمن دالّة ()post_love_add_love. عند استخدام Ajax نعرض القيمة الجديدة، ثمّ يتوقّف السكربت. إن لم يُستخدَم Ajax (يعني هذا أنّ الزّائر سيُوجَّه إلى admin-ajax) فكلّ ما نفعله هو إعادة توجيه المتصفّح إلى المقال بعد تنفيذ السّكربت. في ما يلي النّسخة النّهائيّة لدالّة ()post_love_add_love: function post_love_add_love() { $love = get_post_meta( $_REQUEST['post_id'], 'post_love', true ); $love++; update_post_meta( $_REQUEST['post_id'], 'post_love', $love ); if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { echo $love; die(); } else { wp_redirect( get_permalink( $_REQUEST['post_id'] ) ); exit(); } }أبدلنا المتغيّر POST_$ بREQUEST_$ إذ أنّ الزوّارّ بAjax مفعَّل سيستخدمون طلب POST في ما يستخدم بقيّة الزّوّار طلب GET. يمكّن متغيّر REQUEST_$ من الحصول على البيانات في الحالتيْن. جوانب إضافية تجب مراعاتها عند استخدام AjaxAjax ليس صعبًا، إلّا أنّه يوجد الكثير من الأمور السّهلة الّتي تجب عليك معرفتها. في ما يلي أهمّ جانبيْن ينبغي عليك الاهتمام بهما. 1- الأمانيُمكن أن تؤدّي قلّة الحذر أثناء استخدام Ajax إلى مشاكل أمنيّة عديدة. لا تتحقّق الإضافة التّي أنشأناها أعلاه من الضّغطات المتعدّدة الّتي يُمكن أن تؤدّي - فضلًا عن تشويه النّتائج - إلى زيّادة الحمل على الخادوم، خصوصًا إن واصل الكثيرون الضّغط على الزّرّ في نفس الوقت. علاوةً على ذلك فإنّ تغيير العدّاد لا يحتاج للضّغط على الزّر؛ مجرّد الدّخول إلى الرّابط (مثلًا http://yourwebsite.com/wp-admin/admin-ajax.php?action=post_love_add_love&post_id=23) يفي بالمهمّة. خذ في الحسبان أنّ بعض الإضافات تسمح بحذف المقالات من الواجهة اﻷماميّة دون الحاجة للدّخول إلى لوحة التّحكّم؛ ماذا لو وُجِد رابط تؤدّي زيّارته إلى نفس التّأثير؟ ليست وضعيّة مثاليّة. استخدام الأرقام الخاصّة Nonces هو إحدى وسائل تأمين الاستمارات والرّوابط ضدّ المحاولات الخبيثة. 2- تجربة المستخدميعود انتشار استخدام Ajax إلى أنّ المستخدم يحصُل على تفاعليّة أكبر مع التّقليل من وقت التّحميل. يعني هذا أيضًا أنّه يجب عليك العمل جاهدًا للتّأكّد من أنّ استخدامك لAjax يرفع حقًا من نوعيّة تجربة المستخدم: يجب أن يعرف الزّائر دائمًا مالّذي يحدُث ولماذا. لو كانت إضافة post-love موجَّهةً للنّشر ﻷضفتُ أيقونة لحالة التّحميل تعطّل زرّ الإعجاب عند الضّغط عليه، ثمّ بعد نجاح العمليّة يعود الزّرّ لحالته الطّبيعيّة. تُمكن إضافة هذه الميزة عن طريق معطى beforeSend ضمن دالّة ()ajax.$. يُتيح beforeSend إمكانيّة تنفيذ بعض التّعليمات قبل إرسال البيانات إلى الخادوم. إضافة إشارة مرئيّة أثناء تغيّر عدد الإعجابات فكرة جيّدة؛ إن لم يكن المستخدم يعرف مالّذي ينتظره عند الّضغط على الزّر فربّما لا يُلاحظ تغيّر عدد الإعجابات. في المقابل، لا تبالِغ باستخدام التّأثيرات المرئيّة أثناء التّعامل مع نداءات Ajax، كما حصل للكثيرين مع انتشار هذه التّقنيّة فجعلوا كلّ شيء يتحرّك وينشط في كلّ اتّجاه. لا تضف تأثيرات تلفت النّظر إلّا إذا كان لذلك معنى، ولا تجعل المستخدم ينتظر حتّى نهاية التّحريكة Animation؛ وإلّا فإنّ جودة تجربة المستخدم ستنقُص ولن تزيد. خاتمةتعدّ تقنيّة Ajax من الأدوات القويّة المتاحة للمطوّرين من أجل إضافة التّفاعليّة والتّقليل من الضّغط على الخادوم. لن يُمكن إحصاء الميزات الممكن تقديمها باستخدام Ajax: عرض التّعليقات دون الحاجة لإعادة تنزيل الصّفحة، التّمرير اللّامتناهيّ Infinite scrolling للمقالات، التّحميل الخامل Lazy-loading للصوّر، وغيرها الكثير. سيجعل Ajax تطبيقك أفضل بكثير شرطَ الانتباه إلى تفاصيل تجربة المستخدم وعدم استخدامه لغير ضرورة. رابط تنزيل إضافة `post-love` في صورتها النّهائيّة. post-love.zip ترجمة بتصرّف لمقال Using AJAX With PHP on Your WordPress Site Without a Plugin لكاتبه Daniel Pataki.
  12. استخدم الأمر التّالي: sudo update-alternatives --config editor ستظهر قائمة ببرامج تحرير نصوص للاختيّار بينها، أدخل رقم المحرّر الّذي تُريد. إن لم يظهر المحرّر في القائمة استخدم الأمر التّالي لإضافته إليها (في المثال إضافة محرّر Geany، يجب أن يكون مثبَّتًا مسبقًا): sudo update-alternatives --install /usr/bin/editor editor /usr/bin/geany 10 أبدِل usr/bin/geany/ بمسار المحرّر الّذي تُريده.
  13. توجد الكثير من المقالات التّتي تتحدّث عن ترجمة إضافات Plugins ووردبريس، إلّا أنّ القليل منها يتطرّق لتفاصيل عمليّة التّرجمة عندما تكون مهمَّة يوميّة. مالّذي يحدُث عند تغيير جزء من الشّفرة البرمجيّة Code؟ مالّذي يحدُث عند إضافة سلاسل محارف Strings جديدة؟ مالّذي يحدُث عند تحديث إضافة إلى إصدار جديد؟ هذه هي نوعيّة الأسئلة الّتي سيُجيب عليها هذا المقال. سنتطرَّق، خطوة بخطوة، كيف نعمل من أجل أن تكون الإضافات الجديدة أو المُحدَّثة جاهزةً للتّرجمة إلى أيّ لغة. يتوفّر ووردبريس بلغات عدّة: تُضَمَّن في نظام ووردبريس لإدارة المحتوى قابليّةُ الاستخدام بأيّ لغة، على الرّغم من أنّ لغة الواجهة الافتراضيّة هي الإنجليزية الأميركية (التّرميز en_US). إنشاء إضافةأوّل ما يجب تنفيذه هو إنشاء إضافة. سننشئ إضافةً للتّطبيق عليها. يُمكنك متابعة درس مُقدّمة إلى برمجة إضافات Wordpress للمزيد حول برمجة إضافات ووردبريس. ستعمل الإضافة على كتابة نصّ تحفيزي في التّرويسة Header. أولًا أنشئ مجلَّدًا جديدًا ضمن مجلَّد الإضافات وسمِّه wp-admin-motivation. ثمّ أنشئ ملفًّا ضمن هذا المجلّد وسمِّه wp-admin-motivation.php. في ما يلي الشّفرة الّتي يجب وضعها في الملفّ: <?php /* Plugin Name: Admin Motivation Plugin URI: http://danielpataki.com Description: Shows motivational messages in the admoin bar Author: Daniel Pataki Version: 1.0 Author URI: http://danielpataki.com Text Domain: wp-admin-motivation */ function get_motivation_text() { $motivation = array( 'You are awesome', 'This website is boss', 'You look great today', 'Your earlobes are well rounded, good job!' ); shuffle( $motivation ); return $motivation[0]; } add_action( 'admin_notices', 'show_motivation_text' ); function show_motivation_text() { $text = get_motivation_text(); echo "<p id='wp-admin-motivation'>$text</p>"; } add_action( 'admin_enqueue_scripts', 'motivation_assets' ); function motivation_assets($hook) { wp_enqueue_style( 'motivation-styles', plugin_dir_url( __FILE__ ) . 'styles.css' ); }تخلِط الدّالّة الأولى ()get_motivation_text مصفوفة Array من العبارات التّحفيزيّة باستخدام دالّة shuffle الموجودة في PHP، ثمّ بعد الخلط تختار العبارة الأولى من المصفوفة. يُمكن عدّ دالّة ()get_motivation_text وسيلةً لتوزيع العبارات التّحفيزيّة عشوائيًّا. نربُط في الخطوة التّاليّة بين دالّة ()show_motivation_text والخطّاف Hook المسمَّى admin_notices. تحصُل دالّة ()show_motivation_text على عبارة تحفيزيّة عشوائيّة عن طريق ()get_motivation_text ثمّ تطبع العبارة داخل فقرة ذات معرّف wp-admin-motivation. تكون العبارة التّحفيزيّة، بالوصول إلى هذه النّقطة، مرئيّة؛ ولكنّنا سنغيّر موضعها لتكون إلى جانب تبويب خيّارات/مساعدة (Options/help). نستخدم CSS لهذا الغرض. تكتفي بعض الإضافات بوضع بضعة أسطر CSS في التّرويسة، إلّا أنّ الطّريقة الصّحيحة للتّعامل مع CSS هيّ إدراج الأسطُر ضمن الطّابور Queue؛ وذلك عن طريق ربط دالّة ()motivation_assets بالخطّاف admin_enqueue_scripts وإدراج CSS في الطّابور هناك. يحدّد النّمط مواضع النّصّ مع أخذ اتّجاه الكتابة من اليمين إلى اليسار Right-to-left, RTL في الحسبان. #wp-admin-motivation { float: right; padding-right: 15px; padding-top: 7px; margin: 0; font-size: 11px; } .rtl #wp-admin-motivation { float: left; padding-left: 15px; }الإضافة جاهزة الآن للعمل. المشكل الوحيد هو أنّ العبارات التّحفيزيّة تستخدم الإنجليزيّة فقط. إن أردت استخدام لغة أخرى فسيتطلّب الأمر إنشاء إضافة مماثلة وإبدال العبارات الإنجليزيّة بعبارات اللّغة المستهدفة. تخيّل أنّك تُريد استهداف عشرين لغةً، يعني هذا أنّك ستحتاج لعشرين إضافة لا يوجد فرق بينها سوى في العبارات المعروضة. الكثير من الجهد المهدور، لا بدّ من وجود وسيلة أخرى. تدويل Internationalizing إضافةيهدف تدويل الإضافات في الأساس إلى جعلها جاهزةً لتُترجَم. يتطلّب التّدويل تغليفَ سلاسل المحارف Strings في دوالّ خاصّة تُسخدَم لتحميل التّرجمات الصّحيحة. توجد العديد من دوالّ التّدويل إلّا أنّنا سنأخذ فقط واحدةً منها، وهي دالّة ()__، ونترك البقيّة إلى أن نفهم المثال. تأخذ الدّالّة ()__ معطييْن: النّص الّذي نُريد ترجمته ونطاق النّصّ Text domain. في ما يلي الصّيغة العامّة لاستخدام هذه الدّالّة: <?php $greeting = __( 'Hello There!', 'text-domain' ); ?>نحصُل بعد استخدام دالّة ()__ على ترجمة النّصّ إن كانت موجودة، وإلّا فإنّ الدّالّة تُعيد النّصّ الأصليّ. تستخدم الدّالّةُ اللّغةَ الحاليّة لتحديد التّرجمة المطلوبة. يُمكنك الانتقال بين لغات ووردبريس في الإعدادات. تُطبَّق اللّغة المُختارة في الإعدادات على جميع التّرجمات. نأتي الآن لنطاق النّصّ، حتّى تكتمل عندنا الصّورة. نطاق النّصّ هو طريقة لتجميع التّرجمات مع بعض. لدى القالب Theme الّذي تستخدمه نطاق نصّ خاصّ به، نفس الشّيء بالنّسبة لجميع الإضافات. تجعل نطاقات النّصوص من التّفريق بين التّرجمات عمليّةً أيسر. يجب أن يكون نطاق النّصّ، بالنّسبة للإضافات، مطابقًا لاسم مجلّد الإضافة. يعني هذا، في مثالنا، أنّ نطاق النّصّ يجب أن يكون wp-admin-motivation. كلّ ما ياوجّب علينا فعله، من أجل تدويل الإضافة، هو تغليف سلاسل المحارف بالدّالّة ()__. $motivation = array( __( 'You are awesome', 'wp-admin-motivation'), __( 'This website is boss', 'wp-admin-motivation'), __( 'You look great today', 'wp-admin-motivation'), __( 'Your earlobes are well rounded, good job!, 'wp-admin-motivation')' );تنفيذ التّرجماتجعلنا في الخطوة السّابقة سلاسل المحارف جاهزةً للتّرجمة، لكن لحدّ الآن لا توجد أيّ ترجمات على الموقع. يجب أن نخبر ووردبريس أين توجد ترجمات الإضافة. الطّريقة المعياريّة لذلك هي إنشاء مجلّد فرعيّ باسم lang ضمن مجلَّد الإضافة. أنشئ مجلّد lang الآن وأضف الشّفرة التّاليّة في الملفّ الرّئيس للإضافة حتى يعرف ووردبريس أين توجد التّرجمات: add_action('plugins_loaded', 'wan_load_textdomain'); function wan_load_textdomain() { load_plugin_textdomain( 'wp-admin-motivation', false, dirname( plugin_basename(__FILE__) ) . '/lang/' );تعدّ دالة ()load_plugin_textdomain أساسيّةً هنا ويجب أن تُستدعى من دالّة مربوطة بالخطّاف plugins_loaded. أوّل معطى لدالّة ()load_plugin_textdomain هو اسم نطاق النّصّ الّذي تحدّثنا عنه سابقًا، الثّاني مهجور Deprecated، والثّالث هو مجلّّد اللّغة. ستكون أسماء ملفّات التّرجمات على صيغة plugin_name-locale.mo. حيثُ plugin_name اسم الإضافة وlocale المحلّيّة. سنرى في ما بعد كيفيّة إنشاء هذه التّرجمات. تذكّر، في الوقت الحاليّ، أنّك يجب أن تضع ملفّات التّرجمات هنا. ملحوظة: المحليّة Locale هي مجموعة من المُعطيات تحدّد لغة المستخدم، دولته، وأيّ تفضيلات خاصّة قد يودّ المستخدم رؤيتها في لغة الواجهة. سنختار ترجمة العبارات التّحفيزيّة الموجودة في الإضافة إلى اللّغة العربيّة؛ لذا سيكون المفّ باسم wp-admin-motivation-ar.mo. بالوصول إلى هذه النّقطة سيعمل كلّ شيء بالطريقة المرجوَّة، ينقُصُنا فقط إنشاء التّرجمات. إنشاء التّرجماتتوجد طُرُق متعدّدة لإنشاء ترجمات إلّا أنّ النّتيجة في النّهاية متطابقة. ستحصُل على ملفّين، أحدهما بامتداد po. والآخر بامتداد mo. الأوّل يُمكن للأشخاص قراءته، ويُمكنك فتحه بأيّ محرّر نصوص والتّعديل عليه. أمّا الملفّ ذو الامتداد mo. فهو نتاج تحويل ملفّ po. إلى صيغة مقروءة بالنّسبة للآلة، ذات حجم أصغر بكثير إلّا أنّه لا يمكن للإنسان قراءتها. في ما يلي مثال على ملفّ PO غير مُترجَم. توجد في أعلى الملفّ معلومات أساسيّة عن التّرجمة، المترجِم والمشروع؛ تليها سلاسل محارف تُمكن ترجمتها متبوعة بأماكن التّرجمات المنتظَرة. msgid "" msgstr "" "Project-Id-Version: WP Admin Motivation 1.0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-04-27 13:09+0100\n" "PO-Revision-Date: 2015-04-27 13:09+0100\n" "Last-Translator: Daniel Pataki <contact@tastique.org>\n" "Language-Team: Daniel Pataki <hello@danielpataki.com>\n" "Language: Hungarian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-KeywordsList: __;_e\n" "X-Poedit-Basepath: .\n" "X-Poedit-SearchPath-0: ..\n" #: ../wp-admin-motivation.php:20 msgid "You are awesome" msgstr "" #: ../wp-admin-motivation.php:21 msgid "This website is boss" msgstr "" #: ../wp-admin-motivation.php:22 msgid "You look great today" msgstr "" #: ../wp-admin-motivation.php:23 msgid "Your earlobes are well rounded, good job!" msgstr ""الصّيغة العامّة للأسطُر في ملفّ التّرجمة هي التّاليّة: #: مسار الملفّ الّذي توجد به السّلسلة:رقم السّطر حيثُ توجد السّلسلة msgid "السّلسة المطلوبة ترجمتها" msgstr "التّرجمة"قد تظنّ أنّ كون أماكن تواجد السّلاسل مذكورة مباشرةً بعد ذكر رقم السّطر الّذي تتواجد به يجعل من إنشاء وصيّانة ملفّات التّرجمة أمرًا شاقًّا. لهذا السّبب توجد أدوات مخصًّصة للمساعدة. تُساعد أدوات المدير Admin tools في توليد ملفّات po. للإضافات الموجودة في مستودع ووردبريس. كما يُمكن أيضًا إنشاء ملفّات po. عبر البرنامج الرّائع Poedit. نزّل Poedi وشغّله؛ انقُر على File (ملفّ) ثمّ New catalog (مسرَد جديد). أدخل المعلومات المطلوبة في النّافذة المنبثقة. لا تهتّم بطقم المحارف (Charset) وصيّغ الجمع (Plural forms)؛ سنكتفي الآن بالمعلومات الأساسيّة. انتقل إلى تبويب Sources paths (مسارات المصادر). يُخبر مربَّع Paths (المسارات) Poedit أين يجب عليه البحث عن السّلاسل القابلة للتّرجمة ذات العلاقة بالمسار القاعديّ (Base path). سنضع التّرجمات ضمن مجلَّد فرعيّ في الإضافة؛ يعني هذا أنّ لدينا خيّارين لإعداد المسارات في Poedit: ضبط المسار القاعديّ Base path على القيمة /.. (المجلَّد الأب) وإعطاء القيمة . (المجلَّد الحاليّ) لمربَّع المسارات Paths.ضبط قيمة المسار القاعديّ Base path على قيمة المجلَّد الحاليّ . وقيمة مربَّع المسارات Paths على المجلَّد الأب /...يُخبر الخيّار الأوّل Poedit أنّ الشّفرة البرمجيّة للإضافة موجودة في المجلَّد الأب لمجلَّد اللّغة lang؛ أي أنّ البرنامج سيذهب للمجلَّد الأب ويبحث في ذلك المجلَّد. أمّا الخيّار الثّاني فيقول إنّ المجلَّد الّذي يوجد به الملفّ الحاليّ هو مجلَّد اللّغة؛ وأنّ الملفّات الّتي يجب التّحقّق منها توجد في المجلَّد الأعلى، لذلك استخدمنا /.. في مربَّع المسارات Paths. سنعتمد في هذا الدّرس الخيّار الثّانيّ. الخطوة التّاليّة هي ضبط الكلمات المفتاحيّة في تبويب Sources keywords (الكلمات المفتاحيّة للمصادر). نحدّد في هذا التّبويب أسماء دوالّ التّرجمة المستخدَمة. في المثال هنا نحتاج فقط لإضافة دالّة __. في أغلب الحالات ستحتاج لإدراج دالتّيْن إلى ثلاث؛ راجع التّرجمة المتقدّمة أدناه. لم يبق إلّا الموافقة على البيانات المُدخَلة. سيطلُب منك البرنامج مكان حفظ الملفّ؛ اختر مسار المجلَّد lang إن كنت اعتمدت الخيّار الثّاني في إعداد المسارات أعلاه. سيبحث Poedit في ملفّات الإضافة ويعثُر على السّلاسل الجاهزة للتّرجمة. يُمكنك بعدها ترجمة هذه السّلاسل إلى اللّغة الّتي تُريد. احفَظ الملفّ بعد الانتهاء من التّرجمة وسيولّد Poedit ملفّ MO في مجلَّد lang. تجب إعادة تسميّة ملفّ MO (اسم الملفّ في الإعداد الافتراضيّ هو default.mo) ليوافق الصّيغة الّتي ناقشناها سابقًا؛ أي أن اسم الملفّ، في المثال لدينا، هو wp-admin-motivation-ar.mo. بالنّسبة لملفّ PO فلايهمّ اسمه كثيرًا، إلّا أنّ من الأفضل تسميّته بنفس الطّريقة لكي تعرف بسهولة أيّ ملفّ PO يوافق ملفّ MO إذا كانت توجد الكثير من الملفّات في مجلَّد اللّغات. ستظهر العبارات التّحفيزيّة باللّغة العربيّة؛ بعد وضع ملفّ اللّغة في مجلَّد اللّغات ضمن الإضافة وتفعيل اللّغة العربيّة في إعدادات ووردبريس. صيّانة التّرجماتيبدو تحديث التّرجمات صعبًا للوهلة الأولى، إلّا أنّه ليس كذلك. صحيحٌ أنّ ملفّ PO يحوي رقم السّطر الّذي توجد به السّلسلة المُترجمَة، لكن ووردبريس لا يعتمد على أرقام الأسطُر عند استخدام ملفّ PO. الهدف من استخدام الملفّ والسّكر الّذيْن توجد بهما سلسلة المحارف المُترجَمة هو مساعدة المترجِم بحيث يُمكن النّظر إلى مكان وجود العبارة ومن ثمّ ترجمتها حسب السّيّاق الّذي وردت فيه. لن تحتاج إلى ملفّات التّرجمة إن كانت التّعديلات الّتي تُجريها تقتصر على الشّفرة البرمجيّة الّتي توجد بها سلاسل المحارف الجاهزة للتّرجمة دون أن تعدّل على هذه الأخيرة. ما أفعله عادةً هو إنشاء ملفّ POT ثمّ أحدّثه في كلّ مرة أحدّث فيها الإضافة، حتى ولو لم تتغيَّر سلاسل المحارف. تُشبه ملفّات POT ملفّات PO؛ إلّا أنّها لا تحتوي على أيّ ترجمات، فكلّ ما تحتويه هو الجمل والعبارات الجاهزة للتّرجمة؛ أيّ أنّها قوالب فقط. تكون التّرجمات سهلة ما دام ملفّ القالب محدَّثًا ويحوي رقم السّطر الصّحيح لكلّ سلسلة محارف. إذا احتجت إلى تحديث ملفّ لغة فكلّ ما عليك فعله هو فتحه عن طريق Poedit ثمّ النّقر على أيقونة التّحديث Update وسيُشعرك البرنامج بالسّلاسل الجديدة والمُعدَّلة ويمكنك بعدها التّعديل عليها. قبول التّرجماتمن الأمور الّتي يحدُث فيها الارتباك كثيرًا، على الأقلّ هذا ما حدث لي أولَ وهلة، هو كيفيّة إنشاء ترجمات جديدة لإضافة. كيف يُمكنني إضافة ترجماتي الخاصّة؟ كنتُ أظنّ أنّ هذه العمليّة تحدُث آليًّا، لكنّها ليست كذلك. عندما يُعجَب أحدهم بإضافة فإنّه يترجمها إلى لغته باستخدام ملفّ POT المضمَّن في الإضافة ثمّ يُرسل التّرجمة (ملفّات MO وPO) إلى مطوّر الإضافة الّذي يضعها في ملفّ lang، يُنشئ إصدارًا جديدًا ويدفعها إلى مستودع ووردبريس. عندما يتلقّى مستخدم الإضافة التّحديث تظهر الإضافة بلغة واجهة ووردبريس لديه، إن كانت ضمن اللّغات الموجوة في ملفّ lang. أستضيف كلّ مستودعاتي على Github وهو ما يسهّل على المترجمين إرسال ملفّات التّرجمة لي باستخدام طلبات السّحب Pull requests. كلّ ما عليّ فعله بعدها هو الضّغط على زرّ وإنشاء إصدار جديد من مستودع ووردبريس. التّرجمات المتقدّمةتعرّفنا في ما سبق على طريقة ترجمة سلاسل محارف قاعديّة عن طريق دالّة ()__. توجد 14 دالة ترجمة في ووردبريس يُمكن استخدامها لتدقيق ضبط ترجماتك. على الأرجح لن تحتاج إلّا إلى 3 أو 4 منها. نبدأ بالأشهر من بينها. 1- ()__هذه هي الدّالّة الأساسيّة للتّرجمة. تأخذ سلسلة محارف وتُعيد التّرجمة إن كانت موجودة. 2- ()e_نفس عمل الدّالّة السّابقة تقريبًا، إلّا أنّها بدلًا من إرجاع التّرجمة تعرضها مباشرةً. 3- ()n_تتيح هذه الدّالة وسيلة أنيقة للتّعامل مع الجموع. تأخذ الدّالّة أربعة معطيات: صيغة المفرد، صيغة الجمع، العدد الّذي على أساسه ستعرض الصّيغة (صيغة المفرد أو الجمع) ونطاق النّصّ. في ما يلي مثال سريع: <?php $messages = get_message_count(); // الاستعمال المبَسَّط: لا تُظهر الدّالة العدد الفعليّ $text = _n( 'You have one message', 'You have lots of messages', $messages, 'my-message-app' ); // في طريقة الاستعمال هذه نُظهر العدد الفعليّ للرّسائل $text = sprintf( _n( 'You have one message', 'You have %s messages', $messages, 'my-message-app' ), $messages ) ?>في هذا المثال نحصُل على عدد الرّسائل ضمن متغيّر messages ثمّ نستخدم هذا المتغيّر في دالّة ()n_ الّتي تعتمد على قيمة المتغيّر لاختيّار سلسلة المحارف الّتي تعرضها (بصيغة المفرد أو الجمع). في طريقة الاستعمال المبسَّط للدّالة لا يظهر عدد الرّسائل ضمن سلسلة المحارف المعروضة حيثُ توجد عبارتان، الأولى للعرض إذا كانت قيمة messages مفردة وتُعرَض الثّانيّة في حالة الجمع. إذا أردنا عرض العدد نستخدم عبارة s% في المكان الّذي نُريد أن يُعرض فيه العدد. بالنّسبة لملفّ PO فينبغي أخذ اللّغة الّتي نريد التّرجمة إليها في الحسبان. في أغلب اللّغات الأوروبيّة لا توجد سوى صيغة واحدة للجمع: إذا كان المعدود أكبر من أو يُساوي اثنين يُعدّ جمعا، أي أنّ هناك حالتين فقط (واحدة للمفرَد والأخرى للجمع). في اللّغة العربيّة يتغيّر المعدود حسب العدد في ما مجموعه خمس صيّغ (مفرَد، مثنَّى وثلاث حالات بالنّسبة للجمع)، إضافةً للصّيغة الّتي يكون العدد فيها يُساوي صفرا (أيّ أنّ المجموع يُساوي ستة). لمن يُريد التّفصيل في المسألة فليراجع فصل العدد والمعدود في النّحو :). لأخذ هذه التّغييرات في الحسبان عند إنشاء ملفّ التّرجمة نُخبر Poedit بعدد صيّغ الجمع الموجودة لدينا، بالنّسبة للعربيّة: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5; قبل البدء في البحث عن سلاسل المحارف نُضيف الدّالة n_ في تبويب Sources keywords (الكلمات المفتاحيّة للمصادر) حتى يتعرّف عليها البرنامج؛ إضافةً للدّالّة __ السّابقة. ملحوظة: من المهمّ جدًّا كتابة الدّالّة بالصّيغة n:1,2_ الّتي تعني أنّ المُعطى الأوّل (الرّقم 1) يعبّر عن صيغة المفرد وأنّ المعطى الثّاني لصيغة الجمع. يمكننا الآن إنشاء ملفّ اللّغة وترجمة العبارات الواردة فيه مع أخذ الجمع بالحسبان. ملحوظة: أبقِ عبارة %s في التّرجمات الّتي تحتاج ذلك، أي الّتي يكون العدد فيها اثنان فما فوق. ستُبدَل هذه العبارة بالعدد الفعليّ أثناء تنفيذ البرنامج. مثلًا إذا كان العدد بين 3 و 10 (التّبويب الرّابع في الصّورة، الّذي يحمل العنوان Form 3) فيمكن أن تكون التّرجمة “توجد لديك 3 رسائل” أو “توجد لديك 8 رسائل”. القيمة (3 و 8) ستُعرف أثناء عمل الإضافة الّتي ستضعها مكان %s. 4- ()x_تهدف هذه الدّالّة إلى الوقاية من الخلط بين الكلمات مختلفة المعنى الّتي تُكتَب بنفس الطّريقة. مثلًا إذا كنت تستخدم كلمة pair بمعنى “زوج أشخاص” وبمعنى “ربط بجهاز بلوتوث” فيُمكن أن يشتبه المعنى. لمنع الاشتباه ولمساعدة المترجم على اختيّار العبارة المناسبة في لغته استخدم دالّة ()x_. _x( 'Pair', 'A pair of people', 'my-plugin-textomaind' ); _x( 'Pair', 'As in: pairing devices', 'my-plugin-textomaind' );5- ()ex و ()nxيُشبه عمل هاتيْن الدّالتيْن عمل دالّة ()x_ أعلاه. الفرق هو أنّ ()ex_ تعرض النّتيجة مباشرةً في حين أنّ nx_ تُضيف إمكانيّة تحديد صيغة للجمع. تخليص Escaping التّرجماتتتيح الدّوال السّتّ التّاليّة إمكانيّة تخليص التّرجمة من أجل الاستخدام الآمن في نصوص HTML (الثّلاث الأخيرة) أو خاصيّاته attributes (الثّلاث الأولى). ()esc_attr__()esc_attr_e()esc_attr_x()esc_html__()esc_html_e()esc_html_xترجمات Noopتسجّل دوالّ Noop سلاسل المحارف بصيغة الجمع في ملفّ POT دون أن تترجمها. يعود السّبب في ذلك إلى أنّ العدد غير معروف سلفًا، وهو ما يعني أنّنا سنحتاج لدالّة أخرى للحصول على العدد وبالتّالي اختيّار صيغة المعدود (الجمع). لهذا السّبب نستخدم دالّة Noop ثمّ نترجم القيمة الحقيقيّة لاحقًا بعد الحصول على العدد. راجع المقال التّالي للمزيد. تستخدم الدّوال التّاليّة لهذا الغرض: ()n_noop_()nx_noop_()translate_nooped_pluralترجمة Javascriptربّما ننساءل ماذا عن سلاسل المحارف الموجودة ضمن شفرة Javascript؟ يقدّم ووردبريس الحلّ عبر دالّة ()wp_localize_script. تأخذ هذه الدّالّة ثلاثة معطيات: مِقبض Handle (معرّف) السّكريبت الّذي نُريد ترجمته، اسم كائن Object للاستخدام داخل السّكريبت ومصفوفة التّرجمة. في ما يلي مثال على ترجمة سلاسل محارف ضمن Javascript: add_action( 'admin_enqueue_scripts', 'motivation_assets' ); function motivation_assets($hook) { wp_enqueue_script( 'motivation-scripts', plugin_dir_url( __FILE__ ) . 'scripts' ); wp_localize_script( 'motivation-scripts', 'wma', array( 'section_title' => __( 'Today's motivation', 'wp-admin-motivation' ) )); }بعد إضافة التّرجمات يُمكنك استخدامها في ملفّ Javascript المُسجَّل. يجب عليك استخدام اسم الكائن ومفاتيح مصفوفة التّرجمة: jQuery.html( "<h2>" + wma.section_title + "</h2>" );خاتمةيستخدم ملايين الأشخاص لغاتِهم للتّعامل مع ووردبريس، فلمَ لا تساعدهم في عمل نفس الشّيء مع منتجاتك؟ فلا جعل الإضافات جاهزة للتّرجمة أمر صعب ولا صيّانتها تُمثّل كبير إزعاج. يُساعد تجهيز سلاسل المحارف للتّرجمة وإنشاء ملفّات po وmo الجميع كما أنّه يزيد من جودة المنتج. ترجمة بتصرّف لمقال Everything You Need to Know About Translating WordPress Plugins لكاتبه Daniel Pataki.
  14. يجب التنبيه أوّلًا أنّ أمر adduser يوجد في التّوزيعات الدّبيانيّة فقط. في حال كتابة سكربتات عابرة للتّوزيعات يجب استخدام useradd بدلًا منه. يوفّر أمر adduser سهولة أكبر من useradd فهو يجمع وظائف أوامر عدّة معًا. مثلًا الأمر التّالي: adduser usernameيؤدّي الوظائف التّاليّة: إنشاء اسم مستخدم باسم usernameإنشاء مجلَّد شخصيّ للمستخدِمإنشاء مجموعة بنفس الاسم وإضافة المستخدم إليها.طلب كلمة سرّ من المستخدمطلب معلومات إضافيّة اختيّاريّة من المستخدم يمكن لأمر useradd تأديّة الكثير من هذه المهامّ، لكنّه يحتاج لخيّارات إضافيّة. بعض المهامّ السّابقة يحتاج تنفيذ أوامر أخرى: للحصول على نفس الوظائف السّابقة يجب تنفيذ الأوامر التّاليّة: useradd -m -U username passwd username chfn username إضافةً لذلك يتأكّد الأمر adduser من أنّ معرّفات المستخدمين تتبع سيّاسة دبيان بهذا الخصوص.
  15. ملحوظة: يوجد عنوان MAC لكلّ واجهة في الجهاز. يُمكن أن تكون في الجهاز الواحد أكثر من واجهة، وبالتّالي أكثر من عنوان MAC. لمعرفة عنوان MAC واجهة معيَّنة يُمكن استخدام الأمر التّاليّ: cat /sys/class/net/NIC/address حيثُ NIC اسم واجهة، مثلا لواجهة eth0 يكون الأمر: cat /sys/class/net/eth0/address نفّذ الأمر التّالي للحصول على لائحة بالواجهات المتوفّرة: ls /sys/class/net
  16. هذا تحذير سببه عدم ذكر قيمة لتعليمة ServerName في إعدادات Apache. للتخلّص من رسالة الخطأ نفّذ الأمر التّالي الّذي يُنشئ ملفًّا للإعداد ويُضيف إليه سطرًا يحوي قيمة لتعليمة ServerName: echo "ServerName localhost" | sudo tee /etc/apache2/conf-available/fqdn.conf اعتمد ملفّ الإعداد: sudo a2enconf fqdn ثمّ أعد تحميل Apache لأخذ التّغييرات في الحسبان: sudo service apache2 reload
  17. تدلّ كتابة علامة التّعجّب !! مرّتين على طلب إعادة تنفيذ آخر أمر. جرّب التّالي مثلًا: echo "Abderrzak" !! سيُستعاض في المثال أعلاه عن !! بالأمر echo المنفَّذ قبلها. بالعودة إلى الأمر : sudo apt-get install !! فربّما يكون المستخدم حاول تنفيذ أمر مّا ولكنّه وجد أنّه غير مثبَّت على النّظام فثبّته مع الاختصار؛ على النّحو التّالي مثلًا: program بما أنّ البرنامج غير مثبَّت تظهر رسالة بالهيئة التّاليّة: The program 'program' is currently not installed. You can install it by typing: sudo apt-get install program فيثبّته عن طريق الأمر: sudo apt-get install !!
  18. لتثبيت حزمة بصيغة deb استخدم الأمر التالي: sudo dpkg -i package.deb حيثُ package.deb اسم الحزمة. قد تحتاج الحزمة لاعتماديّات (حزم أخرى يجب تثبيتها قبل إمكانيّة تثبيت هذه الحزمة) في هذه الحالة ستظهر رسالة خطأ بالأمر، يُمكن تنفيذ الأمر التّالي لتنزيل الاعتماديّات ثمّ تثبيتها: sudo apt-get install -fيُمكن أن تظهر رسائل خطأ بعد تنفيذ الأمر أعلاه، في هذه الحالة تجب عليك معرفة السّبب وإصلاحُه قبل تثبيت البرنامج.
  19. أوّلًا الخادمان وسيطان Proxy، أيّ أنّ الخادوم في كلتا الحالتيْن يؤدّي وظيفة لحاسوب آخر. يوجد نوعان من الخواديم الوسيطة: خادوم وسيط لإعادة التّوجيه Forwarding proxy server: ويُطلَق عليه عادةً خاجوم وسيط دون ذكر إعادة التّوجيه. من جهة يوجد العميل (A) وفي الجهة الأخرى يوجد الخادوم (C) الّذي يُريد العميل الوصول إليه، والخادوم الوسيط بينهما (B). يتّصل A ب B لكي يصِل إلى C. في حالة وسيط لإعادة التّوجيه فإنّ العميل A يجب أن يُعدَّ للاتّصال بالخادوم الوسيط B ويطلُب منه الاتّصال ب C وإرسال نتيجة الطّلب إليه. يُستخدَم هذا النّوع من الوسطاء كثيرًا في المؤسّسات لمنع الموظّفين من زيارة مواقع محدّدة. كما يُستخدَم لتجاوز الحظر أيضًا (موقع ممنوع في الدّولة X، تستخدم خادومًا وسيطًا في دولة Y لا تمنع هذا الموقع لتجاوز الحظر).خادوم وسيط عكسيّ Reverse proxy server: يوجد لدينا نفس المخطَّط في الحالة السّابقة: من جهة يوجد العميل (A) وفي الجهة الأخرى يوجد الخادوم (C) الّذي يُريد العميل الوصول إليه، والخادوم الوسيط بينهما (B). لكن الفرق هنا أنّ العميل A لا يحسّ بوجود C، بالنّسبة له يوجد فقط B (الخادوم الوسيط). يُرسل العميل طلباته إلى الخادوم الوسيط (دون أن يكون على علم بأنّ هذا الخادوم وسيط) الّذي يُحيلها إلى الخادوم C؛ ثمّ يُعيد الخادوم الوسيط إرسال الإجابة على الطّلب إلى العميل بعد تلقّيها من الخادوم C. تُستخدَم الخواديم الوسيطة العكسيّة للوصول إلى خواديم موجودة خلف جدار ناريّ أو لتوزيع الحمل بين خواديم عديدة.
  20. تُقسَّم أوامر Shell إلى نوعين: أوامر داخليّة (مضمّنة): توجد هذه الأوامر(أو الدّوالّ) ضمن Shell نفسه الّذي يُنفّذها مياشرةً.أوامر خارجيّة: وهي برامج (سكربتات) توجد خارج Shell. عند استدعاء أمر من هذه الأوامر فإنّ Shell يبحث عن البرنامج ثمّ ينفّذه. يُحدّد متغيّرالبيئة PATH المسارات الّتي يبحث فيها Shell عن الأوامر الخارجيّة.أمر cd مثال على الأوامر الدّاخليّة وecho مثال على الأوامر الخارجيّة. لا توجد فقرات بالنّسبة للأوامر الدّاخليّة ضمن الدّليل (Manual) وعليه تظهر رسالة الخطأ المذكورة عند تنفيذ أمر: man cdيُمكن الحصول على المساعدة في استعمال cd عبر اﻷمر: help cd
  21. هذه إشارة لإعادة توجيه التّدفّق. ما يلي اقتباس من درس مقدّمة إلى إعادة توجيه الإدخال/الإخراج : بالعودة إلى السّؤال، تًستخدَم الإشارة التّالية: >&لإعادة توجيه تدفّق إلى آخر. في الحالة التّاليّة توجّه الإخراج المعياريّ stdout إلى الخطأ المعياري stderr: 1>&2أو العكس (الخطأ المعياريّ إلى الإخراج المعياريّ) 2>&1 راجع الدّرس المذكور أعلاه لمعلومات أكثر عن وحدات الإدخال والإخراج.
  22. تحدّث كلّ من upgrade وdist-upgrade الحزم انطلاقًا من المصادر الموجودة في الملفّ /etc/apt/sources.listيمكن الفرق بين الاثنين أنّ عمل upgrade يقتصر على إصدارات جديدة من الحزم المثبَّتة، أيّ أنّه لن يُضيف حزما غير موجودة. أمّا dist-upgrade فيُمكن أن يُضيف أو يحذف حزمًا جديدة. فلنفرض أنّ الإصدار الجديد من برنامج مثبَّت يتطلّب اعتماديّات (حزم) جديدة؛ في هذه الحالة لن يؤدّي تنفيذ أمر sudo apt-get upgrade إلى تثبيت الإصدار الجديد من البرنامج لأنّ ذلك يستدعي إضافة حزم جديدة؛ أمّا أمر sudo apt-get dist-upgradeفسيُثبّت الإصدار الجديد واعتماديّاته. ملحوظة: من الأفضل قبل تثبيت الحزم عبر أحد الأمرين السّابقين، تحديثُ لائحة الحزم وعناوين مستودعاتها عبر الأمر: sudo apt-get update
  23. تناول الجزء الأوّل من هذا الدّليل الخوارزميّة الّتي يتبعها خادوم ويب Nginx لاختيّار كتلة Server للإجابة على طلب العميل. سنتحدَّث في هذا المقال عن كتل Location وكيف يقرّر Nginx الكتلة الّتي ستُجيب على الطّلب. تحليل كُتَل Locationتوجد لدى Nginx آليّة لتقرير كتلة Location (الموقع) الّتي ستتولّى التّعامل مع الطّلب. تُشبه هذه الخوارزميّة في عملها خوارزميّة اختيّار كتلة Server المشروحة في الجزء الأوّل من هذا الدّليل. 1- صيّاغة Syntax كتلة Locationنبدأ، قبل شرح خوارزميّة اختيّار كتلة Location الّتي ستتولّى الإجابة على الطّلب، بشرح الصّيّاغة الّتي يستخدمها Nginx في تعريفات كتل الموقع. تُستخدَم كُتل Location الّتي تقبع ضمن كتلة Server (أو ضمن كتلة Location أخرى)، لتقرير كيف يُتعامل مع المعرّفات الكليّة للموارد Universal Resource Identifier, URI (الجزء الّذي يأتي بعد اسم النّطاق أو عنوان IP/المنفذ). تأخذ كتلة Location عمومًا الهيئة التّاليّة: location optional_modifier location_match { . . . }تُحدّد تعليمة location_match مالّذي سيُقارن به معرّف المورد الموجود في الطّلب. يُحيل optional_modifier إلى مُغيِّر اختيّاري يؤثّر على الطّريقة الّتي يبحث بها Nginx عن تطابق في كتلة Location. نميّز الحالات التّاليّة، حسب قيمة optional_modifier: لا يوجد متغيّر: إن لم تُذكَر قيمة للمغيِّر الاختيّاريّ فإنّ الموقع سيُحلّل حسب التّطابق المُبدَأ Prefix match. يعني هذا أنّ الموقع سيُقارَن ببداية معرّف المورِد (URI) في الطّلب، بحثًا عن تطابقات.=: إذا استُخدمت علامة التّساوي فإنّ الكُتلة لن تُأخذ في الحسبان إلّا إذا كانت مطابقة تمامًا لمعرّف المورِد في الطّلب.~: في هذه الحالة يُستخدَم تعبير نمطيّ Regular expression يتأثّر بحالة الأحرف (كبيرة Upper case أو صغيرة Lower case).*~: تختلف عن الحالة السّابقة في أنّ التّعبير النّمطيّ لا يتأثر بحالة الأحرف.~^: تُشير هذه القيمة إلى أنّه إن اختيرت هذه الكتلة في بوصفها أفضل تطابق دون الاعتماد على التّعابير النّمطيّة فإنّ هذه الأخيرة (أي التّعابير النّمطيّة) لن تُأخَذ بالحسبان.2- أمثلة توضّح صيّاغة كتلة Locationكتلة Location التّاليّة مثال على التّطابق المُبدَأ. ستُختار هذه الكتلة للإجابة على الطّلبات الّتي تتضمّن معرّفات الموارِد مثل site/page1/index.html/، site/ أو site/index.html/ (كلّها تبدأ ب site/): location /site { . . . }لتوضيح التّطابق التّام نأخذ الكتلة التّاليّة الّتي ستُستخدَم للإجابة على الطّالبات ذات معرّف المورد page1/. لن تُختار هذه كتلة Location هذه للإجابة على طلب بمعرّف مورد page1/index.html/. انتبه إلى أنّه في حال اختيّار هذه الكتلة وكان الطّلب يُنفَّذ باستخدام صفحة فهرس (Index page) فإنّه ستُجرى إعادة توجيه داخليّة Internal redirect إلى كتلة موقع أخرى تكون هيّ المُداول Handler الفعليّ للطّلب: location = /page1 { . . . }في الإعداد أدناه تعبيرٌ نمطيّ يتأثّر بحالة الأحرف. يُمكن أن تُستخدَم كتلة الموقع في هذا المثال للتّعامل مع الطّلبات على tortoise.jpg/ ولكنّها لا يُمكن أن تلبّي الطّلبات على FLOWER.PNG/. location ~ \.(jpe?g|png|gif|ico)$ { . . . }المثال التّالي لا يختلف عن المثال السّابق سوى في المغيّر الاختيّاريّ (~*) حيثُ إنّ التّعبير النّمطي هنا لا يتأثّر بحالة الأحرف؛ لذا يُمكن استخدام الكتلة للإجابة على كلّ من tortoise.jpg/ و FLOWER.PNG/. location ~* \.(jpe?g|png|gif|ico)$ { . . . }المثال الأخير للحالة الّتي تمنع فيها كتلة Location البحث عن تطابق عبر التّعابير النّمطيّة. تُحدّد الكتلة التّالية على أنّها أفضل تطابق لا يعتمد على التّعابير النّمطيّة للطّلبات على costumes/ninja.html/. location ^~ /costumes { . . . }رأينا أنّ المغيّرات تُحدّد كيف يجب أن تُفسَّر كتلة Location؛ إلّا أنّها لا تخبرنا عن ماهيّة الخوارزميّة الّتي يستخدمها Nginx لتقرير كتلة الموقع الّتي سيُرسِل إليها الطّلب، وهو ما سنتعرّض لها في الفقرات التّاليّة. 3- كيف يختار Nginx كتلة Location الّتي ستتعامل مع الطّلبات؟يستخدم Nginx آليّةً لاختيّار كتلة الموقِع الّتي ستُجيب على الطّلبات مُشابهةً لكيفيّة اختيّاره لكتلة الخادوم.فهم الإجراءات الّتي يتبعها Nginx لاختيّار الكتلة المناسبة أساسيّ جدًّا لتكون قادرًا على إعداد Nginx بوثوق ودقّة. يُقارن Nginx بين معرّف المورد في الطّلب وكتلة Location، مع احتساب المغيّرات الّتي ذكرناه في الفقرة السّابقة، من أجل تحديد أنسب كتلة موقع للإجابة على الطّلب؛ وفقًا للخوارزميّة التّاليّة: يبدأ Nginx بالتحقّق من كلّ المواقع الّتي لا تستعمل تعابير نمطيّة؛ فيُقارن كلّ كتلة موقع بمعرّف مورد الطّلب كاملًا. يبحث Nginx أوّلًا عن تطابق كامل. إن وُجدت كتلة Location تستخدم المغيِّر = وتُطابق تمامًا معرّف المورد في الطّلب؛ فإنّ كتلة الخادوم هذه سيقع عليها الاختيّار فورًا. إن لم يعثُر على تطابق كامل (باستخدام المغيّر =)، ينتقل Nginx إلى تقويم السّابقات (Prefixes) غير المُطابقة تمامًا؛ فيبحث عن أطول تطابق في الموقع مع معرّف المورد ثمّ يقوّمه بالطّريقة التّاليّة: إذا كانت كتلة الموقع الّتي يوجد بها أطول تطابق تستخدِم المغيّر ~^ فسيُنهي Nginx فورًا بحثَه ويختار هذه الكتلة للإجابة على الطّلب. إن لم تكُن الكتلة الّتي يوجد بها أطول تطابق تستخدِم المغيّر ~^ فسيحتفظ بها Nginx دون أن يُنهي البحث، بحيث يُمكن له اختيّارها عند الحاجة. ينتقل Nginx، بعد تحديد الكتلة ذات التّطابق الأطول والاحتفاظ بها، إلى تقويم كتل المواقع الّتي تستخدِم التّعابير النّمطيّة، سواء كانت تتأثّر بحالة الأحرف أم لا. يُقوِّم Nginx التّعابير النمطيّة بالتّسلسل، ويختار فورًا أوّل كتلة موقع يُوافق تعبيرها النّمطيّ معرّفَ مورد الطّلب. إن لم يعثُر خادوم الويب على موقع ذي تعبير نمطيّ يُوافق معرّف المورد المطلوب فإنّه يختار الكتلة المُحتفَظ بها سابقًا للإجابة على الطّلب. من المهمّ هنا أن نفهم أنّ Nginx، مبدئيًّا، يُفضّل كتل الموقع الّتي تُطابق الطّلب عبر تعابير نمطيّة؛ إلّا أنّه يبدأ بالبحث عن تطابق في كتل المواقع الّتي لا تستعمل تعابير نمطيّة، وهو ما يسمح لمسؤول خادوم الويب بتجاوز ميل Nginx إلى تفضيل التّعابير النّمطيّة عبر تحديد المغيِّرات = و~^. من المهمّ أيضًا الانتباه إلى أنّ الاختيّار في كتل المواقع الّتي تستخدِم التّطابق المُبدَأ يكون حسب التّطابق الأطول والأكثر تحديدًا غالبًا. في حين أنّ تقويم التّعابير النمطيّة (وبالتّالي الاختيّار) يتوقّف فور الحصور على تطابق وهو ما يعني أنّ ترتيب كتل المواقع الّتي تستخدم التّعابير النّمطيّة له تأثير كبير على الاختيّار. 4- متى يتجاوز تقويم كتلة الموقع إلى مواقع أخرى؟عند اختيّار كتلة Location للإجابة على طلب فإنّ التّعامل معه، ابتداءً من هذه النّقطة، يحدُث بالكامل ضمن إطار الكتلة المُختارة. فقط هذه الكتلة والتّعليمات المتفرّعة عنها هي من يُحدّد كيف يُتعامل مع الطّلب، دون تدخّل من كتل Location من نفس المستوى في البنية الشّجريّة. تسمح هذه القاعدة العامّة بتصميم كتل Location يُمكن التّنبّؤ بعملها. على الرّغم من ذلك، يجب الانتباه إلى وجود حالات تتسبّب تعليمات داخل الكتلة المُختارة في البحث من جديد عن موقع. يُمكن لهذا الاستثناء من القاعدة العامّة “كتلة موقع واحدة فقط” أن يُحدِث تأثيرات على كيفيّة الإجابة على الطّلب، بحيث يُخالف التّوقّعات الّتي كانت عندك أثناء تصميم كتل المواقع. في ما يلي بعض التّعليمات الّتي قد تؤدّي إلى إعادة توجيه من داخل الكتلة المختارة: indextry_filesrewriteserror_pageنعرض باختصار لكلّ واحدة من هذه التّعليمات. تتسبّب تعليمة index دائمًا في إعادة توجيه إن استُخدِمت للتّعامل مع الطّلب. يُستخدَم التّطابق التّام غالبًا لتسريع اختيّار كتلة الموقع عبر إنهاء الخوارزميّة فور حدوث هذا التّطابق. مع ذلك، إن استخدمت التّطابق التّامّ في حال الطّلب على مجلَّد فإنّه توجد إمكانيّة كبيرة لإعادة توجيه الطّلب إلى موقع آخر للإجابة الفعليّة عليه. تُطابق أوّل كتلة في المثال التّالي معرّف المورد exact/، إلّا أنّ تعليمة index الّتي ترثها هذه الكتلة تتسبّب في إعادة توجيه إلى الكتلة الثّانيّة: index index.html; location = /exact { . . . } location / { . . . }إذا احتجت أن يبقى تنفيذ الطّلب داخل الكتلة الأولى، في المثال أعلاه؛ فسيتطلّب ذلك منك استخدام وسيلة أخرى لتلبيّة الطّلب على المجلّد. يُمكنك مثلًا استخدامُ فهرس غير صالح لهذه الكتلة ثمّ تفعيل تعليمة autoindex: location = /exact { index nothing_will_match; autoindex on; } location / { . . . }هذه إحدى الوسائل لمنع index من تبديل السّيّاق، ولكنّها وسيلة غير ناجعة في أغلب الإعدادات. يُستخدَم التّطابق التّامّ في المجلّدات كثيرًا في بعض الأمور مثل إعادة كتابة الطّلب Request rewriting (الّذي ينتُج عنه البحث من جديد عن الموقع). قد تتسبّب تعليمة try_files هي الأخرى في إعادة تقويم عمليّة اختيّار الموقع. تطلُب هذه التّعليمة من Nginx التحقّق من وجود مجموعة مسمّاة من الملفّات أو المجلّدات؛ قد يكون المُعطى الأخير لتعليمة try_files معرّفَ مورد يُعيد Nginx التّوجيه إليه. فلنأخذ الإعداد التّالي مثالًا للشّرح: root /var/www/main; location / { try_files $uri $uri.html $uri/ /fallback/index.html; } location /fallback { root /var/www/another; }سيختار Nginx الكتلة الأولى، في المثال أعلاه، في حال الطّلب على blahblah/. تبحث الكتلة عن ملفّ blahblah في المجلّد var/www/main/، وإن لم تعثُر عليه تبحث عن ملفّ blahblah.html؛ فإن لم تجد هذا الأخير تبحث عن مجلَّد blahblah/ ضمن var/www/main/. إذا فشلت كلّ هذه المحاولات يُعيد Nginx توجيه الطّلب إلى fallback/index.html/ وهو ما يتسبّب في استدعاء كتلة موقع الأخرى، الثّانيّة في المثال. في المحصّلة فإنّ الملفّ var/www/another/fallback/index.html/ سيكون الإجابة على الطّلب. تعليمة rewrite أيضًا قد تتسبّب في تجاوز كتلة الموقع المختارة عبر الخوارزميّة المشروحة أعلاه. يبحث Nginx عند استخدام المعطى الأخير مع تعليمة rewrite أو في حالة عدم ذكر أيّ معطى على الإطلاق، يبحث عن تطابق جديد اعتمادًا على نتيجة rewrite. نُعيد استخدام المثال الأخير مع إضافة تعليمة rewrite للكتلة الأولى؛ يُمكن أن نلاحظ أنّ الطّلب يُمرَّر في بعض الحالات ماباشرةً إلى كتلة Location الثّانيّة دون تنفيذ تعليمة try_files: root /var/www/main; location / { rewrite ^/rewriteme/(.*)$ /$1 last; try_files $uri $uri.html $uri/ /fallback/index.html; } location /fallback { root /var/www/another; }تُجيب كتلة الموقع الأولى ابتداءً في المثال أعلاه على الطّلب rewriteme/hello/؛ ثمّ يأتي الطّلب لتعليمة rewrite الّتي تُعيد كتابته ليُصبح hello/ فيبدأ Nginx البحث من جديد عن موقع للإجابة على الطّلب ويعثُر من جديد على تطابق مع الكتلة الأولى ثمّ تُنفَّذ عليه تعليمة try_files وفقًا للآليّة المشروحة في مثال استخدام تعليمة try_files. أمّا إذا كان الطّلب على rewriteme/fallback/hello/ فستُطابق كتلة الموقع الأولى الطّلب ثمّ تنفَّذ عليه تعليمة rewrite الّتي تعيد كتابته فيصبح fallback/hello/ ممّا ينتُج عنه مطابقة مع كتلة الموقع الثّانيّة وبالتّالي تُجيب الطّلب. تحدُث حالة مُشابهة مع تعليمة return عند إرسال رمز الحالة Status code رقم 301 أو 302. الفرق في هذه الحالة هي أنّ النتيجة عبارة عن طلب جديد كلّيًّا على هيئة إعادة توجيه خارجيّة External redirect مرئيّة. نفس الشيء يُمكن أن يحدُث مع تعليمة rewrite عند استخدام عَلَميْ redirect و permanent. إلّا أنّ هذه الحالات يجب ألا تكون غير متوقّعة، فإعادة التّوجيه الخارجيّة المرئيّة ينتُج عنها دومًا طلب جديد. ملحوظة 1: الفرق بين إعادة التّوجيه الدّاخليّة والخارجيّة هو أنّ الأولى لا تُنهي طلب العميل الحاليّ بل تُحيله إلى مسار آخر ضمن خادوم الويب؛ أمّا الخارجيّة فتُنهي طلب العميل مع إخباره بمعرّف المورد الجديد الّذي يجب عليه إرسال طلب آخر للحصول عليه. ملحوظة 2: رموز الحالة هي أعداد يستخدمها بروتوكول HTTP ليُشعِر بالحالة الّتي انتهى عليها تنفيذ الطّلب. تُقسَّم إجابات HTTP إلى خمس مجموعات: إجابات بمعلومات، إجابات بنجاح الطّلب، إعادات توجيه، أخطاء من جانب العميل و أخطاء من جانب الخادوم. يُمكن أن تؤدّي تعليمة error_page إلى إعادة توجيه داخليّة مشابهة لتلك الّتي تُنشئها تعليمة try_files. تُستخدَم تعليمة error_page لتحديد ما يجب أن يحدُث عند تلقّي رموز حالات معيَّنة. على الأرجح لن تُنفَّذ تعليمة error_page إذا كانت تعليمة try_filesمضبوطة، فهذه الأخيرة تتعامل مع كامل دورة حياة الطّلب. فلنأخذ المثال التّاليّ: root /var/www/main; location / { error_page 404 /another/whoops.html; } location /another { root /var/www; }تستجيب الكتلة الأولى الّتي تقدّم ملفّات من المجلَّد var/www/main/ لجميع الطّلبات، ما عدا تلك الّتي تبدأ بanother/. إن لم يُعثَر على الملفّ المطلوب ضمن المجلَّد المذكور (رمز الحالة 404) فسيُعاد توجيه الطّلب إلى الملفّ another/whoops.html/ ممّا ينتُج عنه البحث عن كتلة موقع تتعامل مع الطّلب الجديد. يرسو البحث على كتلة الموقع الثّانيّة الّتي تُجيب بالملفّ var/www/another/whoops.html/. يُساعد فهمُ الظّروف الّتي تؤثّر على Nginx وتجعله يبحث عن كتلة موقع جديدة تلبّي الطّلب على توقّع سلوك Nginx عند إنشاء طلبات. خاتمةيُسهّل فهمُ الإجراءات الّتي يتبعها Nginx في الاستجابة لطلبات العملاء كثيرًا من عمل مسؤول الموقع، فيمكنك معرفة كتلة الخادوم الّتي سيختارها Nginx للإجابة حسب كلّ طلب على حدة. كما ستكون لديك القدرة على معرفة كتلة الموقع الّتي سيختارها خادوم الويب اعتمادًا على معرّف المورد المذكور في الطّلب. على العموم تُمكنّك معرفة الطّريقة الّتي يختار Nginx وفقها الكتل المختلفة من تتبّع السّيّاقات الّتي يطبّقها Nginx من أجل الإجابة على كلّ طلب. ترجمة بتصرّف لمقال Understanding Nginx Server and Location Block Selection Algorithms.
  24. توجد طريقة سهلة، استخدم الأمر: mysql -u root -e 'use db_test'إذا كانت قاعدة البيانات موجدة فلن يظهر أي خطأ، إن لم تكن موجودة فستحصُل أخطاء. انطلاقًا من هذه الملاحظة يُمكنك اختبار رمز العودة Return code إذا كان 0 فهذا يعني ألّا أخطاء؛ أو على النّحو التّالي مثلًا: if ! mysql -u root -e 'use db_test'; then ## إن لم تكن قاعدة البيانات موجودة فسيُنفَّذ هذا الجزء من البرنامج fi
  25. يمرّ الطّلب بعدّة مراحل: - ترجمة اسم النّطاق إلى عنوان IP - يُرسل العميل (المتصفّح مثلا) طلبًا إلى عنوان IP الخادوم الّذي يوجد عليه الموقع. - يستخدم خادوم ويب Apache مستضيفات افتراضيّة، كلّ مستضيف افتراضيّ يُمكن أن يكون موقعا. في طلب HTTP يوجد ما يُعرف بالتّرويسات Headers والّتي تتضمّن معلومات عن الطّلب، منها المستضيف Host (اسم النّطاق مثلا) ومعرّف المورد URI (الصّفحة المطلوبة) وغيرها. يستخدِم خادوم الويب هذه المعلومات لمعرفة الموقع المطلوب ثمّ يمرّر الطّلب إلى المستضيف الافتراضيّ الّذي يتولّى الموقع. توجد آليّة مشابهة على خادوم ويب Nginx. راجع المقالين التّاليين: كيفية ضبط المستضيفات الافتراضية في خادوم وب Apache على أوبنتو 14.04 فهم آلية عمل خوارزميّة الاختيار في كُتل Server لإعدادات خادوم Nginx والسّؤال التّالي: استضافة عدة نطاقات بنفس IP على Nginx
×
×
  • أضف...