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

عبد اللطيف ايمش

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

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

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

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

    63

كل منشورات العضو عبد اللطيف ايمش

  1. بعد تعلّم المعلومات الرئيسية عن الفضاء ثلاثي الأبعاد والإضاءة، تعجّلنا في نهاية الدرس السابق لكي نحصل على صورة ثلاثية الأبعاد، إلا أنني سأعود قليلًا إلى الوراء وأشرح كائنات threeJS بالتفصيل قبل إعادة عرض الناتج مجددًا. التفاصيل أنشأنا في نهاية الدرس السابق جسمًا كرويًا نمثّل به لكوكب المريخ: let marsGeo = new THREE.SphereGeometry (500, 32, 32); تدعم مكتبة threeJS أشكالا هندسية مختلفة، من بينها الشكل الكروي الذي يُتحصَّل عليه بالدالة SphereGeometry. نمرّر للدالة في المعطى الأول قيمة نصف قطر الكرة (500)، عدد القطع الأفقية المكونة للكرة (32) وعدد القطع العموديّة (32). يُنشَأ كل جسمٍ افتراضيًا في نقطة المبدأ (‎0, 0, 0)، لذا لا حاجة إلى نقل الجسم. ماذا يحدث لو قلّلنا عدد القطع إلى 4 في كل اتجاه من الكرة؟ الصورة الآتية تعرض أثر اختلاف عدد القطع (من اليسار إلى اليمين: 4 ثم 8 ثم 16 ثم 32 قطعة أفقيًا وشاقوليًا لكل جسم). يمكنك أن تلاحظ أنَّ حافة الجسم تصبح «خشنةً» كلما قلّ عدد القطع؛ ويبدو الجزء الأمامي من الجسم كرويًا بسبب الأضواء والمُظلِّلات (shaders، سأتحدث عن المُظلِّلات بعد قليل). كقاعدة عامة: كلما ازدادت عدد القطع الموجودة في أحد الأجسام كان «أنعم»؛ لكن زيادة عدد القطع له سلبياته، فالعدد الكبير من القطع يؤدي إلى زيادة وقت المعالجة اللازم لإظهار المشهد إضافةً إلى زيادة حجم الذاكرة اللازمة لعرضه. عليك الموازنة بين الأمرين وذلك بتحديد دقة التفاصيل التي تحتاج لها في الجسم؛ فلو كانت الكرة بعيدةً جدًا أو صغيرةً جدًا، فلن تحتاج إلى قطعٍ كثيرة لكي تجعلها تبدو كرويةً، لكن إذا أردتَ إنشاء مشهدٍ يصوِّر الكوكب عن قرب، فعليك زيادة عدد القطع لتضمن أنَّ حواف الكرة ستبقى ناعمةً حتى لو اقتربنا كثيرًا من الكوكب. تركيب «المواد» طبّقنا في آخر مثال مادة Phong ‏(Phong material) إلى الكرة: marsMaterial = new THREE.MeshPhongMaterial( { color: 0xff6600 } ), Phong هي خوارزمية تظليل (Shader algorithm) كتبها أحد باحثي رسوميات الحاسوب الفيتناميين. الغرض من المُظلِّل هو تعريف كيفية تفاعل الضوء مع السطح ثلاثي الأبعاد: وكانت خوارزمية Phong سريعةً وكفاءتها عالية، ومثاليةً لعرض السطوح الناعمة. يجب أن تُدمَج المادة مع كائن ثلاثي الأبعاد في threeJS لإنشاء mesh: marsMesh = new THREE.Mesh(marsGeo, marsMaterial); الناتج المعروض في المثال السابق بسيطٌ جدًا ولا يناسب غرضنا، فنحن نريد إنشاء كوكب، وليس كرةً برتقاليةً لامعة. ولإنشاء هذا الشعور، فعلينا تضمين المزيد من المواد ووضعها على الكرة، وذلك عبر Texture Loader: let loader = new THREE.TextureLoader(); هنالك الكثير من الجوانب التي تتعلق بالمواد المُشكِّلة للجسم، وأبرزها – في حالة كوكب المريخ – هو اللون، لكن سطح كوكب المريخ متنوع جدًا ولا يمكن تمثيله بلونٍ واحد. فالحل هو أخذ إحدى الخرائط التي أنشأتها المكوكات الفضائية واستعمالها على الكرة. استعملتُ في هذا المثال صورًا من Celestia ثم ضبطُها لتكوِّن خريطةً المواد لسطح الكوكب: marsMaterial.map = loader.load(imgLoc+"mars-map.jpg"); ربما تتذكر من أوّل درس في هذه السلسلة أننا عرّفنا المتغير imgLoc الذي يحتوي على مسار تخزين الصور. قياس الخريطة المستعملة مع WebGL على النقيض من عدد القطع التي تُشكِّل الجسم، من المستحسن استخدام خرائط ذات دقة عالية، لأنها تعطي شعورًا بدقة تفاصيل الجسم؛ لكن ذلك يؤدي إلى نفس المشاكل: استخدام ذاكرة أكثر، وتقليل الأداء. أجرتَ WebGL موازنة بين ما سبق عبر وضع شرطين للخرائط المستعملة: يجب أن يكون عرض وارتفاع الخريطة من قوى العدد 2: أي 1 أو 2 أو 4 أو 8 أو 16 أو 32 أو 64 أو 128 أو 256 أو 512 أو 1024 أو 2048 أو 4096 أو 8192 بكسل طولًا أو عرضًا (يمكن أن تختلف الأرقام. فمن الممكن أن تكون الخريطة بعرض 1024 وبارتفاع 512). هنالك حدٌّ أقصى لما تستطيع متصفحات الهاتف استعماله لخرائط الأجسام، وهذا الحد يرتفع مع مرور الوقت (نظام iOS 10 يدعم خريطةً بأبعاد 4096×4096، ويمكنك تجربة مختلف الأجهزة والأنظمة في webglreport.com). سأعيد تحجيم الخريطة التي سأستعملها إلى 4096‎×2048 بكسل: ‎من المهم أن تكون الخريطة سلسة الحواف (seamless): أي عندما توضع على كرة، فلن تكون حدودها واضحةً. العرض لإنتاج أيّ مشهد فعلينا «عرضه» (render)، وذلك باستخدام شفرة ذات خمسة أسطر، على النحو التالي: let renderer = new THREE.WebGLRenderer({antialiasing : true}); marsloc.appendChild(renderer.domElement); function render(){ renderer.setSize(window.innerWidth, window.innerHeight); renderer.clear(); renderer.render(scene, camera); } استعملنا الخيار antialiasing لتنعيم شكل كوكب المريخ عند تعريف المتغير renderer (الذي يملك خياراتٍ كثيرة أخرى). المتغير marsLoc هو العنصر الذي سيحتوي على مشهد WebGL والذي أنشأناه في الجزء الأول، حيث أضفنا renderer إليه. أما داخل الدالة render فكانت أبعاد المشهد مساويةً لأبعاد نافذة المتصفح؛ ثم مسحنا كل ما كان موجودًا في لوح الرسم، ثم عرضنا المشهد باستخدام الكاميرا التي عرَّفناها سابقًا. ستلاحظ الآن أنَّ النتيجة تبدو مسطحةً ولامعةً، وثابتةً في الفضاء، وغير متجاوبة؛ وسنتخذ التدابير اللازمة في الدرس القادم من هذه السلسلة لحلّ تلك المشاكل. ترجمة –وبتصرّف– للمقال A WebGL Tour of the Solar System: Mars, Part ThreeلصاحبهDudley Storey
  2. تمهيد بشكل عام، نستعمل حاويات Docker لمرة وحيدة فقط، وهي تعمل إلى أن ينتهي تنفيذ الأمر الموكل إليها. لكن في بعض الأحيان تحتاج التطبيقات إلى الوصول إلى بيانات أو حفظها بعد حذف الحاوية. أمثلة عن البيانات التي تحتاج التطبيقات إلى الوصول إليها تتضمن قواعد البيانات، والمحتوى المولّد من قِبل المستخدم لمواقع الويب، وملفات السجلات. يمكن الوصول إلى تلك البيانات بشكلٍ دائم باستخدام حجوم Docker (أي Docker Volumes). المتطلبات المسبقة خادوم أوبنتو 16.04 (أو 14.04) مضبوطٌ كما في درس «الإعداد الابتدائي لخادوم أوبنتو 14.04»، بما في ذلك إعداد حساب مستخدم عادي لكنه يملك امتيازات الجذر (root) عبر الأداة sudo. برمجية Docker مثبّتة على حاسوبك ملاحظة: صحيحٌ أننا نفترض أنَّنا سنستعمل Docker على أوبنتو 16.04، لكن أوامر docker التي تتعامل مع حجوم Docker والمذكورة في هذا الدرس يجب أن تعمل عملًا صحيحًا على بقية الأنظمة لطالما كانت Docker مثبّتةً عليها وأُضيف المستخدم sudo إلى المجموعة docker. يمكن إنشاء حجوم Docker ووصلها بأمرٍ واحد ألا وهو الأمر المُستخدَم لإنشاء الحاوية، أو يمكن إنشاء الحجوم بشكل مستقلٍ عن أيّة حاوية ثم وصلها إليها لاحقًا. سنناقش في هذا الدرس أربع طرائق مختلفة لمشاركة البيانات بين الحاويات. أولًا: إنشاء حجم مستقل أتى الأمر docker volume create مع إصدار 1.9 من Docker والذي يسمح لك بإنشاء حجم دون ربطه بأية حاوية. سنستخدم هذا الأمر لإنشاء حجم باسم DataVolume1: docker volume create --name DataVolume1 إذا عُرِض الاسم، فذلك يُشير إلى نجاح تنفيذ الأمر السابق: DataVolume1 لكي نستعمل هذا الحجم، علينا إنشاء حاوية جديدة مبنية على صورة ubuntu مستعملين الخيار ‎--‎rm لحذف الحاوية تلقائيًا بعد خروجنا منها، وسنستخدم الخيار ‎-v لوصل الحجم الجديد. الخيار ‎-v يتطلب ذكر اسم الحجم متبوعًا بنقطتين رأسيتين : ثم المسار المطلق لمكان وصل الحجم داخل الحاوية. إذا لم يكن المجلد المذكور في المسار السابق موجودًا في صورة التوزيعة، فسيُنشَأ ثم سيُنفَّذ الأمر السابق؛ وفي حال كان المجلد موجودًا، فسيؤدي وصل الحجم إليه إلى إخفاء المحتوى الموجود في الصورة الأصلية. docker run -ti --rm -v DataVolume1:/datavolume1 ubuntu لنكتب بعض البيانات إلى الحجم قبل إغلاق الحاوية: root@802b0a78f2ef:/# echo "Example1" > /datavolume1/Example1.txt ولاستعمالنا الخيار ‎--rm عند إنشاء الحاوية، فستُحذَف الحاوية تلقائيًا عند خروجنا منها. لكن الحجم سيبقى متاحًا للوصول. root@802b0a78f2ef:/# exit يمكننا أن نتحقق أنَّ الحجم ما يزال موجودًا في النظام عبر الأمر docker volume inspect: docker volume inspect DataVolume1 الناتج: [ { "Name": "DataVolume1", "Driver": "local", "Mountpoint": "/var/lib/docker/volumes/datavolume1/_data", "Labels": null, "Scope": "local" } ] ملاحظة: يمكننا أيضًا تفحص البيانات الموجودة في المسار المذكور في سطر Mountpoint لكن لا يُستحسَن تعديلها، لأنَّ ذلك قد يؤدي إلى حدوث عطب في البيانات إن كانت الحاوية تعمل وتجري تعديلات على تلك الملفات. لنبدأ الآن حاويةً جديدةً ونربط الحجم DataVolume1 إليها: docker run --rm -ti -v DataVolume1:/datavolume1 ubuntu سنُنفِّذ الأمر الآتي داخل الحاوية: root@d73eca0365fc:/# cat /datavolume1/Example1.txt الناتج: Example1 لنخرج الآن من الحاوية بالأمر exit. ثانيًا: إنشاء حجم مرتبط بحاوية لكن سيبقى موجودًا بعد حذفها سنُنشِئ في المثال الآتي حجمًا وحاويةً في آنٍ واحد، ومن ثم سنحذف الحاوية، ثم سنربط الحجم إلى حاويةٍ جديدة. سنستخدم الأمر docker run لإنشاء حاوية جديدة بناءً على صورة ubuntu، وسيؤدي استخدام الخيار ‎-t إلى منحنا وصولًا إلى الطرفية، والخيار ‎-i سيسمح لنا بالتفاعل مع الحاوية. ولغرض التوضيح، سأستخدم الخيار ‎--name لتحديد اسم للحاوية، ثم سأستخدم الخيار ‎-v لإنشاء حجم جديد، وسنسميّه DataVolume2، وسنستخدم النقطتين الرأسيتين لفصل الاسم عن مسار وصل الحجم في الحاوية. وفي النهاية سنطلب إنشاء الحاوية لتبدأ تنفيذها بالأمر الافتراضي الموجود في ملف Docker لصورة Ubuntu (الذي هو الأمر bash) لكي نحصل على وصول إلى الصدفة (shell): docker run -ti --name=Container2 -v DataVolume2:/datavolume2 ubuntu ملاحظة: الخيار ‎-v هو خيارٌ مرنٌ جدًا، إذ يستطيع إنشاء وصل ترابطي، أو يمكنه إنشاء حجم جديد بتعديلٍ بسيط جدًا في شكله. فلو بدأ أوّل وسيطٍ بشرطة مائلة / أو ‎~/‎ فسيُنشِئ وصلًا ترابطيًا، لكن إن حذفت الشرطة في أوّل وسيط، فسيُنشِئ حجمًا. ‎-v /path:/path/in/container وصل المسار ‎/path في الجهاز المضيف إلى ‎/path/in/container في الحاوية. ‎-v path:/path/in/container إنشاء حجم باسم path دون علاقة بالمضيف. ولمّا كنا داخل الحاوية، فلنكتب بعض البيانات إلى الحجم: root@87c33b5ae18a:/# echo "Example2" > /datavolume2/Example2.txt root@87c33b5ae18a:/# cat /datavolume2/Example2.txt الناتج: Example2 سنخرج من الحاوية باستخدام الأمر exit. سنُعيد الآن تشغيل الحاوية مرةً أخرى، وسيتم وصل الحجم تلقائيًا. docker start -ai Container2 لنتأكد من أنَّ الحجم قد وصِلَ إلى الحاوية وما تزال البيانات موجودةً فيه: root@87c33b5ae18a:/# cat /datavolume2/Example2.txt الناتج: Example2 لنخرج مرةً أخرى من الحاوية بالأمر exit. لن تسمح لنا Docker بحذف الحجم إذا كان مرتبطًا بحاوية، لننظر ماذا سيحدث لو جربنا ذلك: docker volume rm DataVolume2 ستخبرنا الرسالة الآتية أنَّ الحجم ما يزال مستخدم وستعطينا المُعرِّف الكامل للحاوية: Error response from daemon: Unable to remove volume, volume still in use: remove DataVolume2: volume is in use - [719f98391ecf1d6f0f053ffea1bbd84cd2dc9cf6d31d5a4f348c60d98392804c] يمكننا استخدام المُعرِّف السابق لحذف الحاوية: docker rm 719f98391ecf1d6f0f053ffea1bbd84cd2dc9cf6d31d5a4f348c60d98392804c لن يؤثر حذف الحاوية على الحجم المرتبط بها، وما زال موجودًا في النظام، ونستطيع التحقق من ذلك باستخدام الأمر docker volume ls: docker volume ls الناتج: DRIVER VOLUME NAME local DataVolume2 نستطيع الآن استخدام الأمر docker volume rm لحذف الحجم بتمرير اسمه إلى الأمر السابق: docker volume rm DataVolume2 أنشأنا في هذا المثال حجمًا فارغًا مع الحاوية الذي يرتبط بها في آنٍ واحد. أما في المثال القادم فسنرى ماذا سيحدث لو أنشأنا حجمًا ووصلناه إلى مجلدٍ فيه بيانات موجودة مسبقًا في الحاوية. ثالثًا: إنشاء حجم وربطه بمجلدٍ فيه بيانات عمومًا، لا يكون هنالك فرقٌ بين إنشاء حجم باستخدام الأمر docker volume create وبين إنشائه عند تشغيل الحاوية، لكن هنالك استثناءٌ وحيدٌ لهذه القاعدة، ويحدث عندما نُنشِئ حجمًا في نفس وقت إنشاء الحاوية وكان المسار الذي نريد ربط الحجم فيه مجلدًا موجودًا في الصورة الأصلية وفيه بيانات، وفي هذه الحالة ستُنسَخ البيانات الموجودة في ذاك المجلد إلى الحجم. كمثال عن الحالة السابقة، سنُنشِئ حاويةً ونصل الحجم الجديد إلى المجلد ‎/var الموجود مسبقًا في صورة التوزيعة والذي يحتوي على بيانات: docker run -ti --rm -v DataVolume3:/var ubuntu جميع المحتوى الموجود في مجلد ‎/var في الصورة الأصلية سيُنسَخ إلى الحجم، ثم يمكننا وصل الحجم إلى الحاوية الجديدة. وفي هذه المرة، سنُنفِّذ الأمر ls بدلًا من الأمر الافتراضي bash، والذي سيُظهِر محتويات الحجم دون الحاجة إلى الدخول إلى الصدفة: docker run --rm -v DataVolume3:/datavolume3 ubuntu ls DataVolume3 كما توقعنا، سيحتوي الحجم DataVolume3 على نسخةٍ من محتويات مجلد ‎/var في الصورة الأصلية: backups cache lib local lock log mail opt run spool tmp من غير المرجّح أن نصل المجلد ‎/var بهذه الطريقة، لكن قد تستفيد من المعلومات السابقة إذا أردت إنشاء صورة خاصة بك وتريد طريقةً سهلةً للحفاظ على البيانات. سنشرح في المثال الآتي كيفية مشاركة الحجم مع أكثر من حاوية. رابعًا: مشاركة البيانات بين أكثر من حاوية Docker كل ما فعلناه منذ بداية هذا الدرس هو وصل الحجم إلى حاوية واحدة فقط بنفس الوقت، لكننا نرغب عادةً بالسماح بمشاركة البيانات بين أكثر من حاوية عبر وصل الحجم إلى عدِّة حاوية معًا. وهذا سهلٌ جدًا، لكن هنالك مشكلة محورية: لا تدعم Docker حاليًا ميزة التعامل مع قفل الملفات (file locking)، فلو احتاجت أكثر من حاوية إلى الكتابة على الحجم، فيجب أن تكون البرمجيات الموجودة في تلك الحاويات مصممةً لكي تكتب إلى أماكن تخزين البيانات المشتركة، لتجنّب تلف البيانات. إنشاء الحاوية Container4 والحجم DataVolume4 سنستخدم الأمر docker run لإنشاء حاوية جديدة باسم Container4 ووصل حجم جديد إليها: docker run -ti --name=Container4 -v DataVolume4:/datavolume4 ubuntu سنُنشِئ الآن ملفًا ونضع فيه بعض المحتوى النصي: root@db6aaead532b:/# echo "This file is shared between containers" > /datavolume4/Example4.txt ثم سنخرج من الحاوية بالأمر exit. سنعود الآن إلى سطر الأوامر في الجهاز المضيف، حيث سنُنشِئ حاويةً جديدةً ونصل فيها الحجم DataVolume4. إنشاء الحاوية Container5 ووصل الحجم الذي كان موصولًا في Container4 سنُنشِئ حاويةً جديدةً باسم Container5 ونصل فيها الحجم الذي كان موصولًا في Container4: docker run -ti --name=Container5 --volumes-from Container4 ubuntu root@81e7a6153d28:/# cat /datavolume4/Example4.txt This file is shared between containers لنضف بعض النصوص إلى أحد الملفات من الحاوية الثانية: root@81e7a6153d28:/# echo "Both containers can write to DataVolume4" >> /datavolume4/Example4.txt ثم سنخرج من الحاوية بالأمر exit. سنتأكد الآن أنَّ البيانات الجديدة أصبحت متاحةً للحاوية Container4. رؤية التغييرات التي أجريناها في Container5 لنتحقق من أنَّ الحاوية Container5 قد كتبت البيانات على حجم DataVolume4 بإعادة تشغيل الحاوية Container4: docker start -ai Container4 root@db6aaead532b:/# cat /datavolume4/Example4.txt This file is shared between containers Both containers can write to DataVolume4 بعد أن تأكدنا من إمكانية القراءة والكتابة إلى الحجم من كلا الحاويتين، فسنخرج من الحاوية بالأمر exit. أكرِّر أنَّ Docker لا تملك ميزةً لقفل الملفات، لذا يجب أن تقفل التطبيقاتُ الملفاتَ بنفسها. لكن من الممكن وصل حجم Docker للقراءة فقط لكي نضمن عدم حدوث تلف في البيانات نتيجةً لخطأٍ ما، وذلك عبر إضافة الخيار ‎:ro. تشغيل الحاوية Container6 ووصل الحجم للقراءة فقط نستطيع –بعد وصل الحجم إلى الحاوية– أن نفصله ثم نصله مرةً أخرى للقراءة فقط، لكننا نستطيع أيضًا إنشاء حاوية تصل الحجم كما نشاء مباشرةً. وذلك بإضافة ‎:ro بعد اسم الحاوية التي سنحاول وصل الحجم المرتبط بها: docker run -ti --name=Container6 --volumes-from Container4:ro ubuntu سنتحقق من أنَّ الحجم موصولٌ للقراءة فقط بمحاولتنا حذف الملف الذي أنشأناه سابقًا: root@ab63aebe534c:/# rm /datavolume4/Example4.txt rm: cannot remove '/datavolume4/Example4.txt': Read-only file system بعد إغلاق هذه الحاوية (كالمعتاد، بالأمر exit) فيمكننا حذف الحاويات التي أنشأناها إضافةً إلى الحجم: docker rm Container4 Container5 Container6 docker volume rm DataVolume4 لقد رأينا في هذا المثال كيفية مشاركة البيانات بين حاويتين باستخدام الحجوم، بالإضافة إلى طريقة وصل الحجوم للقراءة فقط. الخلاصة أنشأنا في هذا الدرس حجم بيانات الذي يسمح لنا بالاحتفاظ بالبيانات حتى بعد حذفنا للحاوية. وشاركنا حجمًا بين عدِّة حاويات، ونوهنا إلى أهمية أن تكون التطبيقات المستعملة في الحاويات قادرةً على قفل الملفات لمنع تلف البيانات. وفي النهاية شاهدنا كيف يمكن وصل الحجم للقراءة فقط. إذا كنتَ مهتمًا بكيفية مشاركة البيانات بين الحاويات والنظام المضيف، فأنصحك بقراءة درس «كيفية مشاركة البيانات بين حاوية Docker والمضيف». ترجمة -وبتصرّف- للمقال How To Share Data between the Docker Container and the Hostلصاحبته Melissa Anderson
  3. قالب Twenty Seventeen هو أكثر قالب افتراضي تعددًا في الاستخدامات شهدته ووردبريس. لكن الخيارات التي يُتيحها هذا القالب ليس شاملة، وهنالك بعض الأمور التي قد نرغب بفعلها والتي لا يسمح القالب بها. سأريك في هذا الدرس خمسة تعديلات على قالب Twenty Seventeen لإضفاء طابعك الشخصي عليه، وسنبدأ بالتخصيصات الأساسية والبسيطة ثم سننتقل إلى الأصعب والأكثر تعقيدًا. لكن قبل أن نبدأ بشرح الشيفرات البرمجية، فلننظر أولًا إلى قائمة التخصيصات التي سنغطيها في هذا الدرس. وإذا أرعى أحدها انتباهك فانتقل واقرأ القسم الموافق له: التعديل الأول: «كيفية إنشاء قائمة تحتوي روابط للمواقع الاجتماعية». التعديل الثاني: «كيفية تغيير عبارة “Proudly powered by WordPress” (أو مثيلتها بالعربية: “مدعوم بواسطة WordPress”)». التعديل الثالث: «إنشاء قائمة تنقّل بين أقسام الصفحة الرئيسية». التعديل الرابع: «إضافة شريط جانبي مخصص لكل قسم من أقسام الصفحة الرئيسية». التعديل الخامس: «كيفية إضافة صورة بارزة كبيرة جدًا لكل صفحة أو منشور». بعد انتهائنا من إجراء التعديلات السابقة، فسنحوِّل قالب Twenty Seventeen إلى موقعٍ جميلٍ وعصريٍ مناسب تمامًا للشركات أو للمدونات. ألا تصدقني؟ ألقِ نظرةً إلى الصورة الآتية، فهذه هي النتيجة المرجوة إذا اتبعت التعديلات التي سنوردها جميعها (صورة متحركة): التعديل الأول: إنشاء قائمة تحتوي روابط للمواقع الاجتماعية حسنًا، هذا ليس تعديلًا وإنما ملاحظة أو تنويه إلى هذه الميزة؛ لكنه يؤدي إلى تخصيص قالب Twenty Seventeen ليلائم موقعك، لذا سنشرحه هنا. يتضمن قالب Twenty Seventeen قائمةً تحتوي روابط لمواقع التواصل الاجتماعي ذات مظهرٍ جذاب في تذييل الصفحة. المشكلة هي أنَّ العديد من المستخدمين قد اشتكوا من عدم قدرتهم على اكتشاف طريقة إضافة روابط الشبكات الاجتماعية إلى هذه القائمة، لكن لا تقلق فالأمر بسيطٌ جدًا بعد أن تتعلم طريقة فعله. كل ما عليك فعله هو إضافة قائمة فيها روابط إلى شبكات التواصل الاجتماعي وإسنادها إلى موضع «Social Links Menu» (أو «قائمة الروابط الاجتماعية» باللغة العربية). هذه هي الخطوات بالتفصيل: ابدأ بفتح المخصِّص من «Appearance > Customize» (مظهر > تخصيص) اضغط على خيار «Menus» (قوائم) ثم اضغط على زر «Add a Menu» (أضف قائمة) سمِّ القائمة باسمٍ واضحٍ دالٍ على وظيفتها مثل «Social Links Menu» ثم اضغط على زر «Create Menu» (إنشاء قائمة) فعِّل مربع الاختيار المجاور لعبارة «Social Links Menu» ( قائمة الروابط الاجتماعية) في قسم «Display Location» (عرض الموقع) بعد أن تنتهي من ضبط القائمة، فيجب أن تبدو كما في الصورة الآتية: آخر خطوة هي الضغط على زر «Save & Publish» (حفظ ونشر) في المُخصِّص لحفظ القائمة الجديدة. يجب أن تظهر قائمة الروابط الاجتماعية الآن في تذييل الصفحة. أنا متأكد أنَّ ما سبق ليس صعبًا أبدًا، لذا لننتقل إلى أمرٍ أكثر صعوبةً! التعديل الثاني: تغيير عبارة «Proudly powered by WordPress» هنالك سببان يدفعانك إلى تغيير النص الموجود في تذييل الصفحة الذي يقول «Proudly powered by WordPress» أو «مدعوم بواسطة WordPress»: ربما تريد الإضافة إلى الجملة السابقة لتقول «مدعوم بواسطة WordPress، ومُصمَّم من شركة WebPress Designs، ومُستضاف من شركة LAMPress!». ربما تريد وضع عبارة أخرى لتصرِّح عن حقوق النشر الخاصة بموقعك مثل «جميع الحقوق محفوظة 2016، شركة MyBiz». وبغض النظر عن السبب الذي دفعك لتغيير هذه العبارة، فسأخبرك طريقة تعديلها في هذا القسم. أوّل شيءٍ عليك فعله هو إنشاء وتفعيل قالب ابن (Child theme) (ملاحظة: إذا أردت حلًا سهلًا جدًا، فكل ما عليك فعله هو تنزيل القالب الابن الذي سنعطيك إياه في نهاية هذا الدرس). بعد إنشائك وتفعيلك للقالب الابن، فانسخ ملف footer.php من قالب Twenty Seventeen إلى مجلد القالب الابن، ثم افتح ملف footer.php المنسوخ وابحث عن السطر get_template_part( 'template-parts/footer/site', 'info' );‎. لديك خياران الآن: إما أن تجعل السطر السابق تعليقًا (بإضافة // قبل الشيفرة) مما يؤدي إلى حذف تلك العبارة فقط، وإما أن تجعله تعليقًا ثم تضيف النص الخاص بك. هذه هي الشيفرة التي استخدمتها لإنشاء التذييل المعروض في الصورة السابقة: التعديل الثالث: إنشاء قائمة تنقّل بين أقسام الصفحة الرئيسية في أحد الدروس السابقة «تخصيص قالب Twenty Seventeen في ووردبريس ليناسب الشركات» أضفتُ سكربت jQuery بسيط لبناء قائمة تنقل ديناميكية ضمن الصفحة الرئيسية والتي يوجد فيها روابط لمختلف أقسام الصفحة الرئيسية. ولقد أدى السكربت السابق عمله كما ينبغي، لكنه كان يحتاج إلى بعض التحسينات. سأريك في هذا الدرس كيفية تحسينه؛ فبالإضافة إلى إنشاء قائمة تنقل لمختلف أقسام الصفحة الرئيسية، فسنحاول أيضًا حلّ مشكلة تأثير المرور فوق عناصر القائمة، وإضافة تأثير التمرير السلس (smooth scrolling) لتحسين تجربة المستخدم. لنبدأ أولًا بشيفرة jQuery التي عليك إضافتها إلى موقعك: /* One page nav code */ jQuery( document ).ready(function(){ /* Add padding and id's to each front page section */ jQuery( "h2.entry-title" ).each( function() { var panelId = jQuery( this ).html().toLowerCase().replace(/\s+/g, "-"); jQuery( this ).wrapInner(function() { return "<span style='padding-top:96px;' id='" + panelId + "'></span>"; }) }) /* Remove navigation link highlighting */ jQuery('#top-menu li').removeClass('current-menu-item current_page_item '); /* Add highlighting on click */ jQuery('#top-menu li a').on('click', function(event) { jQuery(this).parent().parent().find('li').removeClass('current-menu-item'); jQuery(this).parent().addClass('current-menu-item'); }); /* Check current URL and highlight nav for current page */ jQuery('#top-menu li a').each( function() { var pageUrl = jQuery( location ).attr( 'href' ); var navUrl = jQuery( this ).attr( 'href' ); if ( navUrl == pageUrl ) { jQuery( this ).parent().addClass('current-menu-item'); } }) }) يمكنك إضافة هذه الشيفرة إلى ملف JavaScript الذي يُحمِّله القالب الابن (وهذا ما فعلتُ) أو يمكنك استخدام إضافة تسمح لك بإضافة شيفرات JavaScript إلى موقعك (وهذا ما نصحتك بفعله في درسي السابق)؛ لكن احرص على تحميل الشيفرة بعد انتهاء تحميل مكتبة jQuery. لم يتغيّر أوّل جزء من الشيفرة وبقي كما هو مذكور في الدرس السابق، أما بقية الشيفرة فتحل مشكلة تأثير المرور فوق عناصر القائمة. ألقِ نظرةً على التعليقات الموجودة في الشيفرة السابقة لتأخذ فكرةً عمّا يجري. إضافةً إلى ما سبق، سنجعل حركة الانتقال إلى كل قسم من أقسام الصفحة أكثر سلاسةً بتثبيت وتفعيل إضافة مجانية باسم jQuery Smooth Scroll. طبعًا ما يزال عليك بناء قائمة التنقل بإضافة روابط مخصصة إلى كل قسم من أقسام الصفحة عبر استعمال اسم الصفحة المعروضة في ذاك القسم. فمثلًا، لإضافة رابط إلى قسم «About» فعليك إنشاء رابط مخصص وجعله يشير إلى ‎#about. لتفاصيل كاملة رجاءً ارجع إلى درس «تخصيص قالب Twenty Seventeen في ووردبريس ليناسب الشركات». أعلم أنَّ إنشاء هذه القائمة يتطلب وقتًا، لكن هذا الجهد سيؤتي أكله. لم تقتنع بكلامي؟ انظر إلى النتيجة النهائية (صورة متحركة): التعديل الرابع: إضافة شريط جانبي مخصص لكل قسم من أقسام الصفحة الرئيسية يستخدم قالب Twenty Seventeen الفراغات البيضاء استعمالًا كثيرًا، لكنني أعلم بما تفكر به، إذ تظن أنَّ هنالك الكثير من المساحات البيضاء، ألا هل أدلك على طريقةٍ للاستفادة منها؟ تستطيع إضافة ودجات مختلفة للشريط الجانبي لكل قسمٍ من أقسام الصفحة الرئيسية، ويمكنك أيضًا إضافة شريط جانبي إلى الصفحات. لنقل -على سبيل المثال- أنَّك تريد إضافة حقل للبحث، وقائمةً منسدلةً فيها تصنيفات موقعك، وبضعة ودجات إلى قسم المدونة في صفحتك الرئيسية. من المؤكد أنَّ القائمة ستبدو جميلة إذا استطعنا جعلها شبيهةً بتلك الموجودة في هذه الصورة: وبالطبع لا ترغب بإظهار نفس الودجات في قسم «About»؛ وإنما تريد إضافة شيءٍ آخر في ذاك القسم، ومن المحبَّذ إظهار شريط جانبي في قسم About في الصفحة الرئيسية وإظهار شريط جانبي آخر في صفحة ‎/about نفسها! أود أن أبشِّرَك بإمكانية فعل ذلك، حيث تستطيع إضافة شريط جانبي مخصص لكل قسم من أقسام الصفحة الرئيسية ثم إعادة استخدام تلك الأشرطة الجانبية –أو إنشاء أشرطة جانبية مختلفة تمامًا– في صفحات موقعك. لكن هذا التعديل ليس سهلًا، حيث سنحتاج إلى تعديل القالب الابن وسيلزمنا استخدام إضافة. افتراضيًا، لا يُضيف قالب Twenty Seventeen أيّة أشرطة جانبية إلى أقسام الصفحة الرئيسية أو إلى أيّة صفحات. وإنما سيعرض شريطًا جانبيًا في المنشورات فقط، لكن الصفحات ستبدو وكأنها خاويةٌ لكثرة المسافات البيضاء. لكي نغيّر ذلك ولإضافة أشرطة جانبية إلى الصفحات، فعليك القيام بعدِّة أمور: نسخ ملفات القالب التي يستعملها Twenty Seventeen لعرض الصفحات وأقسام الصفحة الرئيسية. إضافة شيفرة الشريط الجانبي إلى قالب الصفحات وإلى كل قسم من أقسامٍ الصفحة الرئيسية عبر تعديل ملفات القالب. تثبيت إضافة Custom Sidebars. إنشاء شريط جانبي مخصص لكل قسم من أقسام الصفحة الرئيسية، ولكل صفحة نريد عرض الشريط الجانبي فيها. إضافة شريط جانبي لكل صفحة تريد عرضه فيها. دعني أريك كل خطوة على حدة. - الخطوة الأولى: نسخ ملفات content-page.php و content-front-page.php و content-front-page-panels.php إلى مجلد القالب الابن. ستجد الملفات السابقة في مجلد ‎\wp-content\themes\twentyseventeen\template-parts\page وعليك نسخها جميعها. ستحتاج إلى إعادة إنشاء نفس بنية المجلدات السابقة في مجلد القالب الابن بإنشاء مجلد جديد باسم template-parts ثم إنشاء مجلد جديد آخر داخله باسم page. ثم لصق الملفات الثلاثة السابقة في مجلد page. بعد انتهاء العملية السابقة، فيجب أن تبدو بنية مجلدات القالب الابن لديك كما يلي: الخطوة الثانية: إضافة شيفرة الشريط الجانبي إلى كل ملف من الملفات السابقة. افتح بدايةً ملف content-page.php وأضف ‎<?php get_sidebar(); ?>‎ قبل وسم إغلاق العنصر header مباشرةً كما يلي: ثانيًا، افتح الملفين الآخرين (content-front-page.php و content-front-page-panels.php) وأضف الشيفرة الآتية قبل وسم إغلاق العنصر header مباشرةً في كلاهما: <hr> <div class="front-page-sidebar"> <aside id="secondary" class="widget-area" role="complementary"><?php $title = get_the_title(); dynamic_sidebar( $title ); ?></aside> </div> بعد إضافة الشيفرة السابقة، فيجب أن يبدو كل من الملفين content-front-page.php و content-front-page-panels.php كما يلي (بعد إضافة الشيفرة الضرورية): الخطوة الثالثة: تثبيت إضافة Custom Sidebars إضافة Custom Sidebars المجانية متاحةٌ في موقع WordPress.org، لذا يمكنك البحث عن الكلمة المفتاحية «Custom Sidebars» في صفحة «Plugins > Add New» (إضافات > أضف جديد). الخطوة الرابعة: إنشاء شريط جانبي مخصص لكل قسم من أقسام الصفحة الرئيسية. اذهب إلى «Appearance > Widgets» (مظهر > ودجات) لإنشاء شريط جانبي جديد لكل قسم من أقسام الصفحة الرئيسية. انتبه الآن جيدًا لما سنفعله، لأنَّ من المهم جدًا أن تقوم بهذه العملية على أتم وجه لكي تُعرَض صفحتك الرئيسية كما يجب. اتبع الخطوات الآتية لإنشاء الأشرطة الجانبية: اضغط على زر «Create a new sidebar» (للأسف، لا توفر إضافة Custom Sidebars ترجمةً عربيةً لها)، وسمِّ كل شريط جانبي بنفس اسم الصفحة تمامًا؛ فمثلًا، لو كان أحد أقسام الصفحة الرئيسية يَعرِض صفحةً عنوانها «About» فعليك حينئذٍ أن تُنشِئ شريطًا جانبيًا بنفس الاسم: «About». الشيفرة التي أضفناها في الخطوة الثانية إلى قوالب الصفحة الرئيسية ستبحث عن مطابقة بين اسم القسم وبين اسم الشريط الجانبي لكي تعلم ما هو الشريط الجانبي الذي يجب عرضه. اضغط على «Advanced – Edit custom wrapper code» وأضف الشيفرة الآتية: في الحقل الأول «Before Title» أضف <h2 class="widget-title"‎> في الحقل الثاني «After Title» أضف <‎/h2> في الحقل الثالث «Before Widget» أضف <section id="%1$s" class="widget %2$s"‎> في الحقل الرابع «After Widget» أضف <‎/section> هذه الشيفرة تماثل الشيفرة التي يضيفها قالب Twenty Seventeen قبل وبعد كل ودجت، وقبل وبعد كل عنوان للودجت. وبتغليف محتوى الشريط المخصص بهذه الشيفرة فأنت تضمن أنَّ التنسيق الافتراضي لقالب Twenty Seventeen سيُطبَّق على محتوى الشريط الجانبي المخصص لأقسام الصفحة الرئيسية. 3. أصبحت جاهزًا الآن لإضافة محتوى إلى الأشرطة الجانبية، لذا اختر ما تشاء من الودجات المتاحة وابنِ الأشرطة الجانبية التي تريدها لعرضها لكل قسم. بعد أن تنتهي، فيجب أن تبدو قائمة الأشرطة الجانبية كالآتية، والتي يُطابِق اسمها اسم القسم الذي ستُعرَض بجواره: الخطوة الخامسة: إضافة شريط جانبي لكل الصفحات التي تريد عرض شريط جانبي فيها. اذهب الآن إلى أيّة صفحات تريد عرض شريط جانبي فيها، وافتح المحرر، ومرِّر إلى الأسفل لكي ترى مربعًا بعنوان «Sidebars» ثم اختر الشريط الجانبي الذي تريد عرضه من القائمة المنسدلة. مرِّر إلى الأعلى واضغط على زر «Update» (تحديث) ثم اعرض الصفحة، وستلاحظ أنَّ الشريط الجانبي معروضٌ تحت عنوان الصفحة: التعديل الخامس: إضافة صورة بارزة كبيرة جدًا لكل صفحة تعجبني طريقة عرض الترويسة التي تعرض فيها صورة أو مقطع فيديو في الصفحة الرئيسية لقالب Twenty Seventeen؛ لكنني لا أحب طريقة عرض الصور البارزة حيث تظهر كغيرها من الصور؛ لذا أود عرض الصورة البارزة لكل صفحة أو منشور بنفس أبعاد الصور الموجودة في الصفحة الرئيسية. هذا أكثر التعديلات تخصيصًا للقالب، ولن أكذب عليك وأقول أنَّ الشيفرة التي ستراها بعد قليل هي شيفرةٌ بسيطةٌ وجميلة! لكنها تؤدي عملها دون مشاكل. أوّل شيء عليك فعله هو نسخ ملف header.php من قالب Twenty Seventeen إلى القالب الابن؛ ثم عليك وضع الشيفرة الآتية بدلًا من كامل عنصر header: <header id="masthead" class="site-header" role="banner"> <?php if ( has_post_thumbnail() && ( is_single() || ( is_home() || ( is_page() && ! twentyseventeen_is_frontpage() ) ) ) ) : ?> <span class="has-header-image twentyseventeen-front-page home"> <div id="page-header" class="custom-header"> <div id="custom-header-media" class="custom-header-media" > <div id="wp-custom-header" class="wp-custom-header"> <?php if ( is_home() && ! twentyseventeen_is_frontpage() ) { $page_for_posts = get_option( 'page_for_posts' ); echo get_the_post_thumbnail( $page_for_posts ); } else { the_post_thumbnail( 'twentyseventeen-featured-image' ); } ?> </div> </div> <div class="site-branding"> <div class="wrap"> <div class="site-branding-text"> <h1 class="site-title"> <?php if ( is_home() && ! twentyseventeen_is_frontpage() ) { $page_for_posts = get_option( 'page_for_posts' ); echo get_the_title( $page_for_posts ); } else { the_title();; } ?> </h1> </div> </div> </div> <a href="#content" class="menu-scroll-down"><?php echo twentyseventeen_get_svg( array( 'icon' => 'arrow-right' ) ); ?><span class="screen-reader-text"><?php _e( 'Scroll down to content', 'twentyseventeen' ); ?></span></a> </div> </span> <?php else : get_template_part( 'template-parts/header/header', 'image' ); endif;?> <?php if ( has_nav_menu( 'top' ) ) : ?> <div class="navigation-top"> <div class="wrap"> <?php get_template_part( 'template-parts/navigation/navigation', 'top' ); ?> </div><!-- .wrap --> </div><!-- .navigation-top --> <?php endif; ?> </header> ثم علينا إضافة بعض شيفرات CSS إلى القالب الابن لكي تُنسَّق الصور البارزة مثل الصور الموجودة في الصفحة الرئيسية: /* Force sticky navigation into position */ #page-header { margin-bottom: 0 !important; } /* Force header image to full height */ #custom-header-media { height: 100vh; max-height: 100%; overflow: hidden; position: relative; } انسخ الشيفرة السابقة وألصقها في ملف الأنماط style.css في القالب الابن. الشيفرة التي وضعناها في ملف header.php والتنسيق الذي أضفناه إلى ملف style.css سيُجبِر الصورة لأن تكون بكامل الارتفاع؛ لكن بسبب الطريقة الديناميكية التي تُضاف فيها الفئات (classes) وتُحذَف؛ فإنَّ شريط التنقل الثابت (sticky) لن يعمل مباشرةً؛ وإنما علينا إضافة شيفرة jQuery إلى قالبنا لفعل ذلك. يمكنك إضافة الشيفرة الآتية إلى ملف JavaScript ضمن القالب الابن أو إلى إضافة، لكن احرص على أن يُحمَّل الملف بعد تحميل مكتبة jQuery. // Sticky nav on pages and posts var body = jQuery( 'body' ); var navigation = body.find( '.navigation-top' ); var customHeader = body.find( '.custom-header' ); var navigationOuterHeight = navigation.outerHeight(); if ( body.hasClass( 'has-header-image' ) ) { var headerOffset = customHeader.innerHeight() - navigationOuterHeight; } jQuery( window ).on( 'scroll', function() { if ( jQuery( window ).scrollTop() >= headerOffset ) { navigation.addClass( 'site-navigation-fixed' ); } else { navigation.removeClass( 'site-navigation-fixed' ); } }); رائع! عندما تضيف الآن صورةً بارزةً إلى صفحةٍ أو منشور، فستُعرَض بكامل ارتفاع الشاشة، كما هي الترويسات الموجودة في الصفحة الرئيسية. جميع التعديلات السابقة في قالب ابن وحيد لقد أجرينا الكثير من التعديلات في هذا الدرس وقد يصعب على البعض تطبيقها، ولتسهيل الأمر عليك، فقد أجريتُ هذه التعديلات على قالب ابن ورفعته على GitHub؛ لذا إذا لم تجد نفسك متفرغًا للقيام بكل التعديلات السابقة ولا ترغب بإنشاء قالب ابن يدويًا لكن التعديلات أعجبتك وتريد تطبيقها على موقعك، فكل ما عليك فعله هو تنزيل الملف المضغوط ثم رفعه إلى موقعك وتفعيل القالب. إضافةً إلى ما سبق، عليك تثبيت إضافة Custom Sidebars وإضافة jQuery Smooth Scroll لكي تتمكن من الوصول إلى جميع التخصيصات التي أجريناها في هذا الدرس. بعد ذلك، عليك إنشاء أقسام الصفحة الرئيسية وقائمة مخصصة للتنقل فيها والمشروحة بالتفصيل في درسنا السابق «تخصيص قالب Twenty Seventeen في ووردبريس ليناسب الشركات» ثم خصِّص قائمة روابط مواقع التواصل الاجتماعي، واكتب النص الذي تريده في التذييل، وأضف الأشرطة الجانبية لأقسام الصفحة الرئيسية، وستظهر عندك النتيجة النهائية نفسها. ترجمة -وبتصرّف- للمقال ‎5 Excellent Ways to Hack the Twenty Seventeen WordPress Theme لصاحبه Jon Penland.
  4. شرحنا في أوّل جزء من هذه السلسلة أساسيات استخدام إطار عمل threeJS، بما في ذلك ضبط إعدادات الكاميرا ودمج مشهد WebGL مع محتويات صفحة الوِب. أما في هذا الدرس فسنُموضع الكاميرا في الفضاء ثلاثي الأبعاد ونجعلها تُشير إلى كائن ونُضيف إضاءةً إلى المشهد. ولكن قبل فعل أيّ مما سبق، علينا أن نفهم طبيعة الفضاء ثلاثي الأبعاد. ملاحظة: هذا الدرس يكمل من حيث انتهى الدرس السابق، لذا عليك أن تفهمه وتحصل على شِفرته قبل الاستمرار بقراءة هذا الدرس. التنقل في الفضاء ثلاثي الأبعاد أتوقع أنَّك تعرف محاور الإحداثيات من دراستك الثانوية: يُرسَم المحور x أفقيًا، والمحور y شاقوليًا (عموديّا). وبضبط الوحدات التي تُمثلها تلك المحاور، فسنتمكن من تحديد مكان أيّ عنصرٍ في الجزء الموجب من الفضاء ثنائي الأبعاد. أما إذا أردنا تضمين الأماكن السالبة فعلينا تمديد المحاور إلى اليسار وإلى الأسفل: نقطة تقاطع المحورين هي النقطة ‎0, 0 وتسمى «المبدأ» (Origin). أما لتحديد المواقع في فضاء ثلاثي الأبعاد، فعلينا إضافة محور ثالث عمودي على المحورين السابقين ويتجه بعيدًا عن المشاهد. نستطيع تخيّل المحور z أيضًا بإمالة المنظور قليلًا كما في الرسم التوضيحي الآتي: لاحظ أنَّ الجزء السالب من المحور z يتجه مبتعدًا عنّا، أما الجزء الموجب فهو أقرب. يمكن الآن تحديد مكان أيّة نقطة في الفضاء بتقاطع ثلاثة قيم x وy وz. ملاحظة: لاحظ عدم أهمية ما الذي تمثِّله تلك الأرقام، فقد تكون بوحدة المليمتر أو القدم أو السنوات الضوئية! المهم هو كيف تتفاعل تلك الأرقام مع بعضها: فالقيمة 2x هي ضعف المسافة x. تتواجد الكاميرات التي أنشأناها في الدرس السابق في مبدأ الإحداثيات افتراضيًا، لكننا نريد وضعها بعيدًا عن المنتصف: camera.position.set(1380, 0, 0); المتغير scene يحتوي على الكائنات التي سنُنشِئها، والذي يُمكن تهيئته بالسطر الآتي: scene = new THREE.Scene(); يوضع المشهد تلقائيًا في مبدأ الإحداثيات (‎0, 0, 0) وسنضع فيه أيضًا العنصر الرئيسي الذي سنُنشِئه (كوكب المريخ). لذا نريد أن نوجِّه الكاميرا إلى تلك النقطة: camera.lookAt(scene.position); ملاحظة: يمكن وضع الكاميرا – في هذا المثال– على المحور z أيضًا لكن يجب أن تبعد بنفس القيمة (1380)، ويمكن أيضًا وضعها في الجزء السالب من المحور x أوz؛ وذلك لأنَّ الكاميرا ستكون موجهةً نحو المبدأ دومًا. لنشغِّل الأضواء المشهد الذي أنشأناه هو فضاءٌ مظلمٌ لا متناهٍ، مما يعني أنَّ أي كائن سيوضع فيه سيكون غيرَ مرئي، لعدم وجود مصدر للضوء. لنضف واحدًا: light = new THREE.PointLight(0xFFFFFF, 2, 5000); light.position.set(2000, 2000, 1500); scene.add(light); ملاحظة: لاحظ أنَّك لن ترى شيئًا في هذه المرحلة، ليس لأنَّ مصدر الضوء يكون مخفيًّا افتراضيًا، وإنما لأننا لم نعرض المشهد بعد. تتوافر ستة أنواع من الأضواء في إطار عمل threeJS: ضوء نقطي (PointLight): وهو الضوء الذي يكون له موضعٌ معيّن، ويُشعّ الضوء في كل الاتجاهات بقدر متساوٍ، مما يجعل هذا النوع من الإضاءة مثاليًا لمحاكاة الضوء الآتي من النجوم أو المصابيح. ضوء ذو اتجاه (DirectionalLight): وهو الضوء الذي له اتجاهٌ معيّن، لكنه ليس حزمةً ضوئيةً (Beam، على النقيض من «بقعة الضوء» Spotlight أدناه)، وهو مفيدٌ لمحاكاة طريقة رؤية أشعة الشمس من سطح الأرض. بقعة ضوء (SpotLight): الضوء الذي يُشير إلى اتجاهٍ معيّن يشبه حزمةً ضوئيةً واسعةً مَثَلُهُ كَمَثَلِ الأضواء المسرحية. الضوء الآتي من منطقة مضيئة مستطيلة (RectAreaLight) وهو مفيدٌ لمحاكاة الضوء المار عبر النافذة أو من لوحٍ من الفلورسنت. الإضاءة المحيطية (AmbientLight): هذا ضوءٌ عامٌ كالذي تراه آتيًا من جميع الاتجاهات. تخيل ليلةً غيرَ مقمرةٍ فيها ضوء محيطي. للإضاءة المحيطية القدرة على إضفاء لونٍ معيّن على المشهد: فكلما كان الضوء المحيطي فاتحًا كلما ظهر المشهد بلونٍ باهت. الإضاءة نصف الكروية (HemisphereLight): يدعى هذا النوع من الإضاءة في برامج التصميم ثلاثي الأبعاد بالمصطلح «Skylight». وهي تحاكي الضوء القادم من الأعلى (السماء) إلى الأسفل (أيّ نوع من الانعكاس أو الضوء من الأرض). يمكنك تخيّل هذا النوع من الإضاءة كما لو أنَّ الإضاءة المحيطية مقسومة إلى نصفين. سأشرح أنواع الإضاءة السابقة تفصيليًا في مقالاتٍ قادمة، لكن علينا أن نركِّز الآن على الضوء النقطي والذي يأخذ ثلاثة وسائط: اللون (عبر قيمة بالنظام الست عشري، يسبقها ‎0x)، والشدة (Intensity)، والنقطة التي سيبدأ الضوء بالانتشار منها. وكذلك الأمر فيما يتعلق بالظلال، فجميع الأضواء تلقي بظلالٍ في الواقع، أما في الرسومات ثلاثية الأبعاد فقد يُسبِّب ذلك مشاكل، لذا يكون إلقاء الظلال الناتجة من الضوء اختياريًا. ربما تُفضِّل التجربة مع شدة الضوء لترى تأثيرها، حيث ستجد أنَّ الشدة العالية للضوء ستجعل الألوان قويةً جدًا، مثل استخدام ضوء فلاش قوي جدًا بالقرب من الجسم الذي تصوِّره بكاميرتك العادية. نموذج أولي بسيط يؤسفني أن نخوض في كل المعلومات السابقة دون أن تتاح لنا فرصةٌ لرؤية أيّة أشكال في صفحتنا، لذا أنشأتُ نسخةً مبسطةً من الناتج وعرضتها لك. الشيفرة الآتية تضم جميع التعليمات البرمجية التي كتبناها منذ بداية هذه السلسلة وحتى هذه المرحلة (دون الشفرة المسؤولة عن تضمين إطار threeJS): let camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000), light = new THREE.PointLight(0xFFFFFF, 2, 5000), scene = new THREE.Scene(); light.position.set(2000, 2000, 1500); camera.position.set(1500, 0, 0); camera.lookAt(scene.position); scene.add(light); let marsGeo = new THREE.SphereGeometry (500, 32, 32), marsMaterial = new THREE.MeshPhongMaterial( { color: 0xff6600 } ), marsMesh = new THREE.Mesh(marsGeo, marsMaterial); scene.add(marsMesh); let renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight) marsloc.appendChild(renderer.domElement); renderer.render(scene, camera); بوضع الشيفرة السابقة في صفحة الويب التي أنشأناها في الدرس السابق، فسترى النتيجة الآتية؛ لكن ما يزال علينا إضافة الكثير من الأمور إليها، وشرحها بالتفصيل، وهذا ما سنفعله في الدرس القادم. ترجمة –وبتصرّف– للمقال A WebGL Tour of the Solar System: Mars, Part Two لصاحبه Dudley Storey.
  5. تمهيد بايثون هي أداةٌ رائعةٌ لمعالجة البيانات. ومن المحتمل أنَّ البرامج التي تكتبها ستتضمن قراءة البيانات أو كتابتها أو تعديلها، ولهذا السبب ستستفيد جدًا من قدرة بايثون على التعامل مع مختلف صيغ الملفات التي تُخزِّن أصنافًا متنوعةً من البيانات. ليكن لديك -على سبيل المثال- برنامج بايثون الذي يتحقق من امتيازات الوصول لقائمة من المستخدمين؛ فيكون من المرجح في هذه الحالة أن تُخزَّن قائمة المستخدمين في ملفٍ نصيٍ بسيط. أو ربما لم تكن تتعامل مع النصوص، وإنما مهمة برنامجك هي التحليل الاقتصادي؛ ومن المرجح أنَّك ستأخذ الأرقام التي ستجري عملياتك عليها من ورقة عمل في أحد البرامج مثل Excel أو Calc. وأرى أنَّه من المرجح أن يتطلب تطبيقك إدخال أو إخراج البيانات بغض النظر عن الهدف منه. سيشرح هذا الدرس باختصار بعض الصيغ التي تستطيع بايثون التعامل معها. سنستعرض أولًا لمحة عن بعض أنواع الملفات، ثم سنكمل الدرس بكيفية فتح وقراءة الملفات والكتابة إليها في بايثون 3. يجب أن تُصبح قادرًا بعد نهاية هذا الدرس على التعامل مع أيّة ملفات نصية في بايثون. المتطلبات المسبقة ستحتاج في هذا الدرس إلى توافر بايثون 3 على حاسوبك بالإضافة إلى بيئة برمجية محلية مضبوطة عليه. لغة بايثون هي لغة متكاملة وتستطيع التعامل بسهولة (نسبيًا) مع مختلف صيغ الملفات، بما فيها الصيغ الآتية: txt: ملف نصي بسيط يُخزِّن البيانات التي تُمثِّل المحارف (أو السلاسل النصية) ولا تضم أيّة بيانات وصفية. CSV: ملف يحتوي قيمًا مفصولٌ بينها بفاصلة (أو غيرها من المحارف التي تستعمل لفصل الحقول) لتنظيم بُنية هيكلية للبيانات، مما يسمح بحفظ المعلومات بصيغةٍ مجدولة. HTML: لغة توصيفية تُخزِّن البيانات الهيكلية وتستعمل عادةً لبناء صفحات الويب. JSON: صيغة بسيطة وعملية، مما يجعلها أشهر الصيغ المستعملة لتخزين ونقل البيانات. سنُركِّز في هذا الدرس على صيغة txt. الخطوة الأولى: إنشاء ملف نصي قبل أن نبدأ بإنشاء برامج بايثون، سنحتاج إلى ملفٍ نصيٍ لنعمل عليه؛ لذا افتح محررك النصي المُفضَّل وأنشِئ ملف txt جديد واجعل اسمه days.txt. أدخِل في الملف الجديد بضعة أسطر، ولتكن مثلًا أيام الأسبوع: Monday Tuesday Wednesday Thursday Friday Saturday Sunday احفظ الآن الملف وانتبه إلى مكان تخزينه. فمثلًا، كان اسم المستخدم في نظامي هو sammy وحفظتُ الملف في المسار ‎/users/sammy/days.txt. هذا المسار مهمٌ جدًا في الخطوات القادمة، لأننا سنحاول فتحه باستخدام بايثون. بعد أن حصلنا على ملفٍ نصيٍ لنعمل عليه، فلنبدأ عملية كتابة البرنامج. الخطوة الثانية: فتح الملف قبل أن نبدأ بكتابة البرنامج علينا إنشاء ملف لحفظه، لذا سنُنشِئ الملف files.py في المحرر النصي. وللتبسيط سنضعه في نفس المجلد الذي يحتوي الملف days.txt: أي ‎/users/sammy/‎. علينا لنفتح ملف في بايثون أن نعثر على طريقةٍ لربط الملف الموجود على القرص الصلب بأحد المتغيرات. تدعى هذه العملية «بفتح» الملف. سنبدأ بإخبار بايثون أين يوجد الملف، ويُشار عادةً إلى مكان تخزين الملف بالمصطلح «المسار» (path). ولكي تستطيع لغة بايثون أن تفتح لك الملف، فستحتاج إلى مساره. ومسار الملف days.txt كما ذكرنا في القسم السابق هو ‎/users/sammy/days.txt، وسنُنشِئ متغيرًا لتخزين هذا المسار باسم path في ملف files.py وضبط قيمته إلى مسار الملف days.txt: path = '/users/sammy/days.txt' سنستخدم بعد ذلك الدالة open()‎ الموجودة في بايثون لفتح الملف days.txt. تتطلب الدالة open()‎ تمرير مسار الملف كأوّل وسيط (argument) لها، لكنها تقبل عددًا كبيرًا من المعاملات (parameters)؛ لكن أهم معامل منها هو المعامل الاختياري الذي يُحدِّد «نمط» فتح الملف (opening mode)، فالنمط هو سلسلةٌ نصيةٌ تُحدِّد ماذا تستطيع أن تفعله مع الملف. هذه قائمة ببعض الأنماط المتوافرة: 'r': فتح الملف للقراءة 'w': فتح الملف للكتابة 'x': إنشاء الملف وفتحه للكتابة 'a': الإضافة إلى نهاية الملف 'r+‎': فتح الملف للقراءة والكتابة معًا أريد أن أقرأ من الملف فقط في المثال الآتي، لذا سأستخدم النمط r. سنستعمل الدالة open()‎ لفتح الملف days.txt وإسناده إلى المتغير days_file. days_file = open(path,'r') نستطيع الآن بعد فتح الملف أن نقرأ منه، وهذا ما سنناقشه في القسم التالي. الخطوة الثالثة: قراءة الملف نستطيع الآن بعد فتح الملف أن نُجري عمليات عليه (مثلًا: أن نقرأ منه) عبر المتغير الذي أسندناه إليه. توفِّر بايثون ثلاثة عمليات متعلقة بقراءة المعلومات من ملف، وسأريك إياها كلها في أمثلةٍ لتفهم كيف تعمل. أوّل عملية هي ‎<file>.read()‎ التي تُعيد كامل محتويات الملف كسلسلةٍ نصيةٍ وحيدة. days_file.read() الناتج: 'Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday\n' العملية الثانية ‎<file>.readline()‎ تُعيد السطر التالي من الملف، حيث تُعيد السطر بأكمله بالإضافة إلى محرف السطر الجديد. ببساطة: هذه العملية ستؤدي إلى قراءة الملف سطرًا بسطر. days_file.readline() الناتج: 'Monday\n' وبعد أن تقرأ سطرًا عبر readline()‎ فسيتم الانتقال إلى السطر الذي يليه، فلو استدعيتَ هذه العملية مرةً أخرى، فستُعيد السطر الذي يلي السطر السابق في الملف كما في الناتج الآتي: 'Tuesday\n' آخر عملية هي ‎<file>.readlines()‎ التي تُعيد قائمةً (list) بجميع الأسطر الموجودة في الملف، حيث يُمثَّل كل سطر في الملف بعنصرٍ في القائمة. days_file.readlines() الناتج: ['Monday\n', 'Tuesday\n', 'Wednesday\n', 'Thursday\n', 'Friday\n', 'Saturday\n', 'Sunday\n'] أحد الأمور التي يجب عليك أن تبقيها في ذهنك عند القراءة من الملفات هو عدم قدرتك على إعادة قراءة الملف بعد استخدام إحدى عمليات القراءة السابقة. فمثلًا لو استدعيتَ days_file.read()‎ متبوعةً بالدالة days_file.readlines()‎ فستُعيد العملية الثانية سلسلةً نصيةً فارغةً، وبالتالي ستحتاج إلى إنشاء متغير جديد مرتبط بالملف في كل مرة ترغب فيها بقراءته. بعد أن تعلمنا طرائق القراءة من ملف فحان الوقت الآن إلى تعلم كيفية الكتابة إلى ملفٍ جديد. الخطوة الرابعة: الكتابة إلى ملف سنكتب الآن السلسلة النصية «Days of the Week» إلى ملفٍ جديد متبوعةً بأيام الأسبوع. سنُنشِئ بادئ الأمر المتغير title: title = 'Days of the Week\n' سنحتاج أيضًا إلى تخزين أيام الأسبوع في متغيرٍ الذي سنسميه days. ولتبسيط ذلك سنضع نفس الشيفرة التي استخدمناها في القسم أعلاه؛ حيث سنفتح الملف بنمط القراءة ثم نقرأه ونُخزِّن ناتج عملية القراءة في المتغير الجديد days: path = '/users/sammy/days.txt' days_file = open(path,'r') days = days_file.read() أصبح لدينا الآن متغيران أولهما للعنوان والآخر لأيام الأسبوع، لذا نستطيع البدء بالكتابة إلى الملف الجديد. لكننا سنحتاج أولًا إلى تحديد مسار الملف، وسنستخدم المجلد ‎/users/sammy مجددًا، ثم سنُحدِّد اسم الملف الجديد الذي نود إنشاءه؛ لذا سيكون المسار النهائي هو ‎/users/sammy/new_days.txt، وسنضع هذا المسار في المتغير new_path، ثم سنفتح الملف الجديد بنمط الكتابة، وذلك باستخدام الدالة open()‎ مع النمط w: new_path = '/users/sammy/new_days.txt' new_days = open(new_path,'w') من المهم أن تعلم أنَّه لو كان الملف new_days.txt موجودًا من قبل «فتح» الملف، فستُحذف جميع محتوياته السابق، لذا توخى الحذر عند استخدام النمط w. بعد أن فتحنا الملف للقراءة، سنستطيع الآن أن نكتب البيانات فيه وذلك باستخدام الدالة ‎<file>.write()‎. تأخذ دالة الكتابة معاملًا وحيدًا الذي يجب أن يكون سلسلةً نصية وتكتبه إلى الملف. إذا أردتَ أن تبدأ سطرًا جديدًا في الملف، فعليك توفير محرف بداية السطر (newline) يدويًا. سنكتب أولًا العنوان إلى الملف متبوعًا بأيام الأسبوع. ولنضف أيضًا بعض تعبيرات print لنعرف ما الذي سيُكتَب إلى الملف، وهذا أمرٌ مستحسنٌ لكي تتتبع مسار تنفيذ برنامجك. new_days.write(title) print(title) new_days.write(days) print(days) بعد أن تنتهي من التعامل مع ملفٍ ما، فاحرص على أن تغلقه، وهذا ما سنفعله في الخطوة الأخيرة. الخطوة الخامسة: إغلاق الملف عملية إغلاق الملف تعني إغلاق قناة التواصل بين الملف على القرص وبين المتغير الموجود في برنامجك. إغلاق الملفات يعني أنَّ البرامج الأخرى ستتمكن من الوصول إلى الملف وستُبقي بياناتك بأمان؛ لذا احرص دومًا على إغلاق الملفات التي تفتحها؛ وهذا ما سنفعله في الأسطر الآتية عبر استعمال الدالة ‎<file>.close()‎: days_file.close() new_days.close() أنهينا عملية معالجة الملفات في بايثون، ولننظر الآن إلى الشيفرة النهائية. الخطوة السادسة: التحقق من سلامة الشيفرة قبل أن تجرِّب الشيفرة تأكَّد أنَّ كل شيء يبدو سليمًا. يجب أن تكون الشيفرة النهائية شبيهةً بما يلي: path = '/users/sammy/days.txt' days_file = open(path,'r') days = days_file.read() new_path = '/users/sammy/new_days.txt' new_days = open(new_path,'w') title = 'Days of the Week\n' new_days.write(title) print(title) new_days.write(days) print(days) days_file.close() new_days.close() بعد أن تحفظ الشيفرة في ملفها، فافتح الطرفية (terminal) وشغِّل سكربت بايثون: python files.py يجب أن يبدو الناتج كما يلي: Days of the Week Monday Tuesday Wednesday Thursday Friday Saturday Sunday لنتحقق الآن من أنَّ الشيفرة تعمل كما ينبغي لها وذلك بفتح الملف الجديد المُنشَأ new_days.txt فلو نُفِّذ البرنامج تنفيذًا صحيحًا فيجب أن تكون مخرجاته كالآتي: Days of the Week Monday Tuesday Wednesday Thursday Friday Saturday Sunday إذا ظهر معك نفس الناتج السابق فأود أن أهنِّئك على إتمامك لهذا الدرس بنجاح. الخلاصة تعلمنا في هذا الدرس كيفية التعامل مع الملفات النصية البسيطة في بايثون 3 ومعالجتها. يمكنك الآن أن تفتح الملفات وتقرأها وتكتب إليها ثم تغلقها. جرِّب ما تعلمته في هذا الدرس على ملفات التي تريد معالجتها واسأل إن واجهتَ صعوبةً في ذلك. ترجمة -وبتصرّف- للمقال How To Handle Plain Text Files in Python 3 لصاحبته Michelle Morales
  6. يأتي قالب Twenty Seventeen كقالب افتراضي مع إصدار ووردبريس 4.7، وهذا القالب الموجّه للشركات والأعمال يُظهِر انحرافًا واضحًا في القوالب الافتراضي التي كانت تأتي مع ووردبريس والتي كانت متمحورة حول المدونات، والذي يُبيّن تحوّل ووردبريس من منصة تدوين إلى منصةٍ قادرةٍ على إنشاء جميع أنواع مواقع الويب. إذا كنتَ تخطط لتجربة قالب Twenty Seventeen فستلاحظ أنَّ هذا القالب يختلف جذريًا عن القوالب التي سبقته، فالقوالب الافتراضية السابقة لا تتطلب إعدادًا مطولًا وكان الغرض منها هو استعمالها لإنشاء مدونات؛ لكن الأمر مختلفٌ مع قالب Twenty Seventeen. نعم، تستطيع استعمال هذا القالب كقالبٍ لمدونتك، لكن هذا ليس هو الغرض الذي صُمِّمَ هذا القالب لأجله، فهو مصممٌ لإنشاء مواقع لشركات باستخدام صفحة رئيسية تُمثِّل صفحة هبوط (landing page) مُقسمَّةٍ إلى أقسام، ويمكن أن ترى ذلك بوضوح بالنظر إلى الموقع التجريبي الرسمي لهذا القالب. النتيجة التي أدى لها هذا التغيير في الهدف من القالب هي جعل إعداد قالب Twenty Seventeen يأخذ وقتًا أطول من بقية القوالب الافتراضية. سنستكشف في هذا الدرس قالب Twenty Seventeen وما الذي يستطيع تقديمه، وسأريك كيفية إعداد القالب لضبط صفحة هبوط احترافية بسهولة. ملاحظة: يجب أن تعلم أنَّ هذا الدرس مبنيٌ على نسخة قالب Twenty Seventeen المُضمَّنة بإصدار ووردبريس 4.7 RC، ويُفتَرَض أنَّ هذه النسخة مُطابِقة للنسخة النهائية من القالب التي ستوضَع في إصدار 4.7، لكن يُحتَمَل وجود بعض التعديلات الصغيرة. لمحة عن قالب Twenty Seventeen انتشرت في الآونة الأخيرة فكرة استخدام الصفحة الرئيسية كصفحة هبوط لمواقع الشركات، ويسمح لنا قالب Twenty Seventeen –المُضمَّن افتراضيًا في ووردبريس–بإنشاء صفحة هبوط جذابة. لكي تأخذ فكرةً عن إمكانيات هذا القالب، فانظر إلى الموقع التجريبي الرسمي. ستلاحظ مباشرةً مقطع الفيديو المعروض في الترويسة. مرِّر إلى الأسفل قليلًا وسترى التصميم -الذي يُقسِّم الصفحة إلى أقسام- جليًا أمامك، فكلُّ قسمٍ مفصولٍ عن غيره بصورة خلفية مُطبّقٌ عليها تأثير اختلاف المنظور (parallax) والتي تحتل كامل طول وعرض نافذة المتصفح. شريط التنقل بسيطٌ جدًا وهو ثابتٌ في أعلى الصفحة. ويُستعمَل خطٌ وحيدٌ ألا وهو Libre Franklin في عموم القالب مع تنوعٍ في أوزانه (weights) وألوانه وتنسيقه. بعد ضبط القالب ضبطًا سليمًا، فيُمثِّل قالب Twenty Seventeen قالبًا عصريًا ذا مظهرٍ احترافي، مبنيًا على خطٍ سهل القراءة، واستخدامٍ قويٍ للعناصر البصرية اللافتة للنظر مع استخدام المسافات البيضاء استعمالًا صحيحًا. كيفية إنشاء أقسام في الصفحة الرئيسية بأخذ كمية العمل التي أجراها Matt Mullenweg على المُخصِّص (Customizer) في الآونة الأخير بالحسبان، فلن نُفاجأ أنَّ كمًّا كبيرًا من إعداد قالب Twenty Seventeen سنجريه في المُخصِّص. تُبيّن الصورة الآتية الخيارات المتاحة في المُخصِّص بعد تفعيل قالب Twenty Seventeen: إضافةً إلى الميزات الافتراضية مثل ضبط القوائم والودجات، يمكننا أن نضبط من المُخصِّص صورةَ (أو فيديو) الترويسةِ، ونُبدِّل الألوان إلى نظام ألوانٍ مختلف، ونُسنِد المحتوى إلى أقسام الصفحة الرئيسية. يتضمن إصدار ووردبريس 4.7 ميزةً جديدةً اسمها «visible edit shortcuts» ، حيث تظهر هذه الميزة على شكل أيقونات زرقاء كما في الصورة أعلاه، والضغط على أي اختصارٍ من هذه الاختصارات سيؤدي إلى فتح قائمة المخصص المسؤولة عن تعديل هذا الجزء من الموقع. هذا سيُسهِّل تعديل الكثير من الميزات في قالب Twenty Seventeen مثل صورة الترويسة، وعنوان الموقع والشعار اللفظي له، والمحتوى الذي يظهر في كل قسم من أقسام الصفحة. فكل ما عليك فعله هو العثور على المحتوى الذي تشاء تعديله ثم الضغط على الاختصار الظاهر بجواره، ثم تخصِّصَه كما تشاء. تأتي ووردبريس 4.7 بميزةٍ أخرى في المُخصِّص التي يمكنك أن تراها في خيار «Additional CSS» (بالعربية: «تنسيقات CSS الإضافية»)، وهذه الميزة أصبحت متوافرةً لكامل القوالب المثبّتة في ووردبريس 4.7 وليست خاصةً بقالب Twenty Seventeen. لشرح كيفية ضبط قالب Twenty Seventeen فسأحاول إعداده كما لو كنتُ سأستخدمه لموقعي الشخصي. الخطوة الأولى: إنشاء صفحة لكل قسم من أقسام الصفحة الرئيسية علينا أولًا إنشاء بضع صفحات: إنشاء صفحة لنستعملها كصفحة رئيسية ثابتة. إنشاء صفحة لنستعلمها للمدونة أو لعرض المنشورات. إنشاء أربع صفحات إضافية تتضمن المحتوى الذي نريد عرضه في أقسام الصفحةالرئيسية. أنشِئ ثلاث صفحات إضافية إذا كنتَ تُخطِّط لاستعمال مدونتك أو صفحة المنشورات كقسمٍ من أقسام الصفحة الرئيسية. أما للموقع الذي سنضبطه في هذا الدرس، فسأنشِئ صفحةً رئيسيةً، وصفحة للمدونة، وصفحة about، وصفحة services، وصفحة contact. الخطوة الثانية: إضافة صورة بارزة لكل صفحة للاستفادة من تأثير اختلاف المنظور (parallax) فسنحتاج إلى صورةٍ بارزةٍ (featured image) كبيرة والتي ستُشكِّل جزءًا من أقسام الصفحة الرئيسية. لن تحتاج إلى إضافة صورة بارزة لصفحة Home التي أنشأتها في الخطوة السابقة، لكن يجب أن تُضيف صورةً بارزةً لجميع الصفحات الأخرى. الصور التي تستعملها النسخة التجريبية من Twenty Seventeen بقياس 2000×1200 بكسل، استعمل الصور التي تكون أبعادها قريبةً من الرقم السابق؛ أما الصور الصغيرة فستعطي نتيجةً سيئةً. استعملتُ في المثال صورًا مجانيةً من StockSnap وأعدتُ تحجيمهم جميعًا إلى حوالي 2000 بكسل بالعرض و 1200 بكسل بالطول (± 10%). الخطوة الثالثة: إسناد صفحة رئيسية ثابتة وصفحة للمنشورات حان الوقت الآن لبدء عملية التخصيص، افتح المُخصِّص بالذهاب إلى «Appearance > Customize» (مظهر > تخصيص) في لوحة التحكم أو عبر الضغط على زر «Customize» (تخصيص) في شريط الإدارة الظاهر في الواجهة الأمامية لموقعك. اختر عنصر «Static Front Page» (صفحة رئيسية ثابتة) من القائمة وأجرِ التعديلات الآتية: تحت عنوان «Front page displays» (تعرض الصفحة الرئيسية)، انتقِ «Astatic page» (صفحة ثابتة). في قائمة «Front page» (الصفحة الرئيسية) المنسدلة اختر الصفحة الرئيسيةالتي أنشأتها سابقًا. في قائمة «Posts page» (صفحة المقالات) المنسدلة اختر صفحة المنشورات أوالمدونة. لا تنسَ الضغط على زر «Save & Publish» (حفظ ونشر)، ثم انتقل إلى الخطوة التالية. الخطوة الرابعة: إسناد كل صفحة إلى قسمٍ من أقسام الصفحة الرئيسية لإسناد مختلف الصفحات التي أنشأتَها مسبقًا إلى أقسام الصفحة الرئيسية فاضغط على عنصر «Theme Options» (خيارات القالب) في قائمة المُخصِّص. ثم استخدم القوائم المنسدلة لإسناد كل صفحة إلى القسم الملائم لها والذي تريد إظهارها فيه. الخطوة الخامسة: إنشاء قائمة التنقل الرئيسية يمكنك إنشاء قائمة التنقل عبر الخيار «Menus» (قوائم) من داخل قائمة المُخصِّص أو عبر الذهاب إلى صفحة «Appearance > Menus» (مظهر > قوائم) في لوحة التحكم. وبغض النظر عن مكان إنشائك للقائمة، فأحب أن أنوِّه إلى أنَّ إنشاء القوائم لم يتغير في إصدار 4.7، أي أنشِئ القائمة كما فعلت سابقًا ثم أسندها إلى المكان المُسمى «Top Menu» (القائمة العلوية). الخطوة السادسة: إضافة فيديو كترويسة للصفحة لنضع مقطع فيديو بدلًا من صورة الترويسة الافتراضية «المملة». اذهب إلى القسم الملائم في المُخصِّص عبر الضغط على أيقونة التعديل الزرقاء أو ابحث عن خيار «Header Media» (وسائط الترويسة) في المُخصِّص. ارفع أو اختر مقطع الفيديو الذي تريد استخدامه، ويمكنك أيضًا وضع رابط URL لفيديو على موقع يوتيوب إذا أردتَ ذلك. إذا أردتَ رفع مقطع فيديو فاعلم أنَّ من الأفضل أن تكون أبعاد الفيديو 2000×1200 بكسل. اخترتُ مقطع فيديو مجاني لأستعمله في موقعي، وهو بأبعاد 1920×1080 بكسل، ولا يتوافق تمامًا مع الأبعاد المُستحسنة، لكنه يبدو بشكلٍ جيد في الموقع. أنهينا الآن إنشاء الصفحة الرئيسية مع إسناد الأقسام المختلفة إليها. هذه هي الصفحة التي أنشأتُها في موقعي باتباع الخطوات التي ذكرناها أعلاه (هذه الصورة متحركة): إنشاء قائمة تنقّل بين أقسام الصفحة الرئيسية إحدى الميزات غير الموجودة في قالب Twenty Seventeen هي دعم التنقل بين أقسام الصفحة الرئيسية. فمن الجميل أن تتمكن من الانتقال إلى القسم الذي تريده في الصفحة الرئيسية مباشرةً؛ لكن لحسن الحظ، يمكن إضافة هذه الميزة المُغفَلة بسهولة. أوّل ما علينا فعله هو إنشاء قائمة التي تُشير روابطها إلى خاصية id لأقسام الصفحة الرئيسية بدلًا من إشارتها إلى صفحات مختلفة في موقعك. ولفعل ذلك، علينا إنشاء قائمة مخصصة التي تبدو روابطها كما في الصورة: عند كتابة مُعرِّفات id في القائمة السابقة، فاستعمل أسماء الصفحات المُعروضة في أقسام الصفحة الرئيسية، لكن اجعل حالة الأحرف صغيرة وضع شرطة - بدلًا من الفراغات. إذا ثبّتتَ ووردبريس في مجلدٍ فرعي (وهذا شائعٌ جدًا خصوصًا للمواقع المطوَّرة في بيئات التطوير المحلية) فعليك تضمين المجلد الفرعي في الروابط التي تُدخِلها في القائمة التي أنشأتها. فمثلًا، كان الموقع التجريبي الذي أعمل عليه موجودًا في الرابط http://localhost/wp4point7 وهذا يعني أنَّ ووردبريس مثبتةٌ في مجلدٍ فرعي (‎/wp4point7) ولكي تعمل الروابط عملًا سليمًا، فسأحتاج إلى تضمين اسم المجلد الفرعي في رابط URL كما يلي: ‎/wp4point7/#about. أما إذا لم تُثبِّت ووردبريس في مجلدٍ فرعي، فلا حاجة إلى وضع اسم المجلد واستعمل البُنية الظاهرة في الصورة أعلاه. بعد إنشاء قائمة التنقل فستحتاج إلى إضافة مُعرِّفات id لكل قسم من أقسام الصفحة. يمكنك فعل ذلك بطريقتين. الحل الأسهل هو إضافة مُعرِّفات id مباشرةً إلى عناوين الصفحة باستخدام مُحرِّر الصفحات في ووردبريس، وذلك بإضافة شيفرة HTML مباشرةً إلى حقل العنوان. لاحظ أنَّه لإضافة مُعرِّف id كان علي وضع العنوان ضمن عنصر <span>، وكان المُعرِّف مكتوبًا بأحرفٍ صغيرة مع وضع شرطات - بدلًا من الفراغات. وهذا يعني أنَّ المُعرِّفات التي نضعها هنا ستُطابِق تلك التي وضعناها في روابط قائمة التنقل. إضافة إلى أنني أضفتُ حاشيةً علوية (top padding) بمقدار 96 بكسل. وعندما يُضغَط على الروابط فستؤدي قيمة الحاشية السابقة إلى دفع المحتوى باتجاه الأسفل لكي نتمكن من رؤية العنوان تحت شريط التنقل الثابت في أعلى الصفحة. لكي أكون صادقًا معك، أرى أنَّ هذه الطريقة غير عملية ولا أحبِّذ استعمالها. والطريقة التي أُفضِّلها هي إضافة عنصر span ومُعرِّف id والحاشية باستخدام jQuery. وبهذا لن أحتاج إلى تعديل عناوين الصفحات وإضافة شيفرات إليها، وسأحصل على نفس النتيجة في نفس الوقت. هذه هي الشيفرة التي كتبتُها والتي تفعل ما نرغب به: jQuery( "h2.entry-title" ).each( function() { var panelId = jQuery( this ).html().toLowerCase().replace(/\s+/g, "-"); jQuery( this ).wrapInner(function() { return "<span style='padding-top:96px;' id='" + panelId + "'></span>"; }) }) ما تفعله الشيفرة السابقة هو العثور على عنوان كل قسم ثم وضعه في عنصر span مع تنسيقٍ مناسب؛ وستُنشِئ الشيفرة مُعرِّف id بقيمةٍ مناسبة بأخذ عنوان القسم وجعله بحالةٍ صغيرة وبإبدال الشرطات بالفرغات. فلو كان عندما قسمٌ بعنوان My Blog (كما في الصورة السابقة)، فالشيفرة السابقة تُضيف خاصية id الآتية: id='my-blog'‎ وهي نفس الصيغة التي استعملتها عند إنشاء روابط قائمة التنقل. لتحميل الشيفرة السابقة إلى موقعك فيمكنك إضافتها في أحد المكانين الآتيين: ملف JavaScript الذي يُشكِّل جزءًا من قالب ابن لقالب Twenty Seventeen، أو إلى إضافةٍ التي تُضيف شيفرات JavaScript مخصصة. اتخذتُ الطريقة الثانية في موقعي واستعملتُ إضافةً لتحميل شيفرات JavaScript و CSS إلى الموقع والمشروحة في مقالة How to Turn Any WordPress Theme Modification into a Simple Plugin، لكن يمكنك استخدام إضافة أخرى مثل Simple Custom CSS and JS لتحميل شيفرة jQuery السابقة. بعد إضافة شيفرة jQuery وإنشاء قائمة التنقل، فسأريك كيف أصبحت قائمة التنقل في موقعي (صورة متحركة): رائع، لقد أصبح قالب Twenty Seventeen جاهزًا كصفحة هبوط، وأضفنا إليه شريطًا للتنقل بين أقسام الصفحة. إذا أردتَ إضافة أشياء أخرى إلى ما أنشأناه، فخذ هذه التحسينات بالحسبان: إضافة قواعد إعادة توجيه لكي يتمكن أيُّ شخصٍ يزور أحد صفحاتك المعروضة كقسمٍ في صفحتك الرئيسية أن يُعاد توجيهه إلى القسم المناسب في الصفحة الرئيسية. فمثلًا، أعد توجيه الصفحة http://example.com/contact إلى القسم الآتي في الصفحة الرئيسية http://example.com/#contact. إذا قررتَ استخدام jQuery لتفعيل ميزة التنقل بين أقسام الصفحة الرئيسية، فضع وسومًا شرطيةً (conditional tags) لتحميل الشيفرة في الصفحة الرئيسية فقط. إذا تضمن موقعك صفحاتٍ إضافيةً لا تمثِّل أقسامًا في صفحتك الرئيسية، فأضف تلك الصفحات إلى قائمة التنقل، وأنقل جمع روابط التنقل الخاصة بأقسام الصفحة الرئيسية إلى قائمة فرعية تظهر عند الضغط على كلمة «Home» كما في الصورة أدناه. الخلاصة قالب Twenty Seventeen بدأ عصرًا جديدًا من قوالب ووردبريس (القوالب الافتراضية على الأقل)، ومن الواضح أنَّ مطوري ووردبريس الذين يقفون خلف قالب Twenty Seventeen ملتزمون بدفع ووردبريس لتصبح أكثر من منصة تدوين ولتدخل عالم مواقع الشركات والتجارة الإلكترونية. يوفِّر قالب Twenty Seventeen تصميمًا عصريًا للصفحة الرئيسية التي تُقسَّم إلى أقسام؛ لكن ضبطه يحتاج وقتًا أطول وجهدًا أكثر مقارنةً بما سبقه من القوالب الافتراضية. رأيتَ في هذا الدرس كيف أنَّ الجهد المبذول لإعداد القالب قد آتى أُكله، وأنَّ النتيجة كانت موقعًا احترافيًا وجذابًا والذي لم نكن لنحلم بتحقيقه باستخدام القوالب الافتراضية السابقة. ترجمة -وبتصرّف- للمقال How to Customize the Free Twenty Seventeen WordPress Theme for Businessلصاحبه Jon Penland.
  7. لقد أنشأتَ شركتك لتلبي احتياجات مستخدميك، أليس كذلك؟ سواءً كنتَ ستساعدهم شخصيًا أو على الهاتف أو عبر البريد الإلكتروني، وتستطيع مساعدتهم أيضًا –دون تدخلٍ منك– عبر موقعك الإلكتروني. وسواءً أكانوا زبائن محتملين يستكشفون الموقع، أو زوارًا مشتركين في موقعك لكنهم لم يشتروا شيئًا بعد، أو أنَّهم زبائن يدفعون لقاء خدماتك؛ لكن الأمر ليس مهمًا، وإنما المهم أن تتواصل معهم دومًا. وما لم تكن تسأل زوارك عمّا يفعلون، فكيف ستعرف شعورهم تجاه شركتك؟ هذا الأمر مهمٌ جدًا خصوصًا لموقعك الإلكتروني، الذي يُشكِّل دعامةً أساسيةً لشركتك والذي أنفقته عليه مالًا ووقتًا كثيرًا لبنائه. أقتبس المقولة الآتية من Esteban Kolsky: العبرة من الكلام السابق هي: لو شعر الزبون بعدم الرضى بأيِّ حالٍ من الأحوال، فلا تظن أنه سيأتي إليك شاكيًا؛ ما لم يكن زبونًا أمضى وقتًا طويلًا في شراء منتجاتك وكان مخلصًا لك (لكن مع ذلك، لا أضمن لك أنَّه سيشتكي)، فلماذا يضيعوا أوقاتهم لكي يُعلِموك بأنَّ الموقع لا يفتح بسرعة أو أنَّ هنالك تحذيرًا أمنيًا يظهر في متصفحهم؟ عليك أن تكتشف ذلك بنفسك. إذا لم تكن تحاول اكتشاف سلوك زوار موقعك بنفسك، فحان الوقت لفعل ذلك؛ وستساعدك القائمة الآتية التي تضم 14 أداةً «تتجسس» على زوار موقعك لمحاولة فهم ما الذي تفعله بشكلٍ جيد في موقعك، وما الذي لا تجيده… الأدوات التي تساعدك في فهم سلوك زوارك واحتياجاتهم أيُّ شخصٍ يأخذ وقتًا ليتفاعل مع موقعك يتوقع أن يحصل على شيءٍ ما في المقابل، وصحيحٌ أنَّ المقابل يختلف من موقعٍ لآخر، لكن هنالك قاعدة ثابتة: زوار موقعك (وزبائنك المحتملون والقدامى) لا يريدون وجود أمرٍ يعكِّر عليهم زيارتهم لموقعك. لكن دون استعمال الأدوات الصحيحة، فقد لا تعرف ما الذي يزعج زوارك. صحيحٌ أنَّ Google Analytics يمكن أن يُشير إلى وجود مشكلة ما، لكنه ليس كافيًا في أغلب الأوقات. فلا تركن إلى Google Analytics وحده ليخبرك كامل القصة. فمعدلات الارتداد العالية (High bounce rates) سيئة جدًا، لكن هل يمكنك أن تعرف السبب؟ هنالك عدِّة طرائق يمكنك استعمالها لتعرف ما الذي يفعله زوارك، وأدوات التحليل مثل أداة Google Analytics لا تكفي أبدًا، لذا اطلع على كامل القائمة. أدوات التحليل لنبدأ بمراجعة الأرقام! Google Analytics حسنًا، أعلمُ أنني قلتُ منذ قليل أنَّ Google Analytics ليس «كافيًا»، لكنه ما يزال جزءًا مهمًا من ترسانة الأدوات «التجسسية» على زوارك، حيث يتيح لك معرفة: من هم زوار موقعك، بما في ذلك الموقع الجغرافي، والتوزع الديموغرافي، والأجهزة والمتصفحات التي يستعملونها، وهلم جرًا. كيف وجدوا موقعك، هل هو عبر وسائل التواصل الاجتماعي، أو عبر البحث العادي، أو البحث المدفوع، وهكذا. أين ذهبوا أوّلَ ما زاروا موقعك، وكم مَكِثوا فيه. ما هي الأهداف التي حققوها من زيارة موقعك، بما في ذلك ملء نماذج HTML، أو قراءة عدد معيّن من المقالات، أو شراء بعض البضائع …إلخ. هنالك الكثير من المعلومات التي نستطيع الحصول عليه من Google Analytics، لذا اعتمد على نظام التقارير الخاص بهم لتبسيط الأمر، وذلك بإعداد تقارير تَتَتبّع معدلات الارتداد وخروج الزوار من موقعك وغير ذلك. يمكنك أيضًا الانتباه إلى توجّه زوارك في الوقت الحقيقي. اقرأ أيضًا: سلسلة مدخل إلى Google Analytics إضافة Google Analytics Dashboard for WP بعد أن تضبط Google Analytics في موقعك، فيمكنك أن تُضيفه إلى لوحة تحكم ووردبريس، وبهذا ستضمن أنَّه في كل مرة تدخل فيها إلى لوحة تحكم ووردبريس فستتذكر أن تتحقق من نشاط الموقع. هنالك الكثير من الإضافات المتوافرة لفعل ذلك، لكنِّ إضافة «Google Analytics Dashboard for WP» هي أشهرها وأعلاها تقييمًا؛ وبالإضافة إلى إظهارها للقطة شاشة لنشاط موقعك في الوقت الحقيقي، فهي تخبرك أيضًا معلوماتٍ عن مدى تحقيق هدفك، ومعدلات الارتداد، وأخطاء 404 (الناجمة عن محاولة الزائر الدخول إلى صفحة غير موجودة)، والكثير غير ذلك. Kissmetrics لنتحدث عن مسار التحويل الخاص بموقعك، والذي هو المسار المثالي الذي يملكه كل موقع والذي يؤدي إلى تحويل الزائر إلى زبون عندما يصل إلى نهاية هذا المسار؛ وربما بنيتَ موقعك بناءً استراتيجيًا اعتمادًا على هذا المفهوم. وأحد الأشياء التي لا تستطيع أدوات التحليل إخبارك بها هي ما الذي يحدث في ذاك المسار: فماذا يفعل زوارك؟ وما الذي يدفعهم لمغادرة الموقع؟ وهكذا. خدمة Kissmetrics هي خدمةٌ مدفوعة، لكنها تعطيك رؤية واضحة عن سلوك زوارك وكيف تتجنب أيّة عثرات في هذا المسار. Chartbeat إذا كان في موقعك محتوىً كثير وكنت تريد معرفة إن كان ذلك المحتوى ناجحًا أم فاشلًا اعتمادًا على سلوك زوارك، فأداة Chartbeat هي أداةٌ رائعةٌ تستحق التجربة. حيث تستعمل هذه الأداة نهجًا متعدد المسارات لتتبع سلوك زوارك (بما في ذلك مدة التركيز على المحتوى، ومعدّلات التوقف عن القراءة …إلخ.) أثناء قراءتهم لمحتوى موقعًا. ثم تعطيك القدرة على تعديل وتحسين محتواك في الوقت الحقيقي اعتمادًا على سلوكهم. أدوات تتبع نشاط الزوار عبر خرائط التفاعل تحليل البيانات هو الخطوة الأولى لمعرفة إذا كانت هنالك مشكلة في موقعك أم لا، أمّا خرائط التفاعل (heat maps) فهي الخطوة الثانية التي يجب أن تتخذها لتساعدك في إصلاح الخلل الذي يحدث مع زوارك. Heatmap هذه الأداة التي تُنشِئ خرائط التفاعل هي خيارٌ جيد لنبدأ به، ليس لأنها متوافرة مجانًا، بل لأن هنالك إضافةً يمكنك استعمالها في ووردبريس. صحيحٌ أنَّ أدوات إنشاء خرائط التفاعل التي سنذكرها في هذا الدرس تُسجِّل نفس نوع المعلومات تقريبًا، وكل واحدة منها لها ميزات خاصة. وميزة هذه الأداة أنَّها تُسجِّل النشاط على مختلف الأنظمة والأجهزة، مما يعطيك نظرةً أفضل على نقاط ضعف التصميم المتجاوب لموقعك. SeeVolution تقنية خرائط التفاعل التي توفرها أداة SeeVolution تحاول عرض ما يلي إلى مستخدميها: أكثر المناطق التي يُضغَط عليها في الموقع. أكثر الأماكن التي تتحرك عليها الفأرة (وبالتالي، أكثر المناطق التي تلفت انتباه زوارك). نسبة الزوار الذين يُمرِّرون إلى أسفل الصفحة. إذا كنتَ مهتمًا بمعرفة ما الذي يُعرَض من موقعك وفيما إذا كانت هنالك نقاط ضعف لا تجذب انتباه الزوار، فهذه الأداة ستوجِّهك للطريق الصحيح. CrazyEgg أداة CrazyEgg شبيهةً بتقنية خرائط التفاعل التي توفرها أداة SeeVolution، الفرق الرئيسي بينهما هو وجود أداة «Confetti». هذه الأداة لا تعرض ما هي أكثر المناطق التي ينقر عليها زوار موقعك فحسب، لكنها توفر أيضًا إحصائيات عن الزوار بناءً على طريقة تعرفهم على موقعك أو الذين وصلوا إلى موقعك عبر كلمات بحث معيّنة. لنقل مثلًا أنَّه يأتيك عددٌ كبيرٌ من الزوار الذين بحثوا عن كلمةٍ مفتاحيةٍ معيّنة، لكن أغلبية الزوار قد خرجوا من الموقع دون اتخاذ أيّة إجراءات، وهذا دليلٌ قويٌ على وجود مشكلة في استخدامك لتقنية SEO. أي أنَّ هذه الأداة مفيدةٌ للتعرف على توجهات زوارك. إضافة Hotspots Analytics هل تبحث عن أداةٍ مجانيةٍ توفِّر لك خرائط التفاعل وتريد أن تعمل على ووردبريس؟ انظر إذًا إلى إضافة «Hotspots Analytics»، حيث ستريك هذه الإضافة المواضع الموجودة في موقعك والتي ينقر عليها (أو يلمسها) زوارك كثيرًا. توجد في هذه الإضافة ميزةٌ تريك توزّع النقرات على فترةٍ من الزمن، وستستفيد من هذه الميزة إذا عدَّلتَ في تصميم موقعك وأردتَ أن تعرف كيف يمكن أن تغيّر هذه التعديلات في سلوك زوارك. أدوات المحادثة الحية هل توفِّر ميزة المحادثة الحية في موقعك؟ إذا كان جوابك هو النفي، فأنصحك بأن تستعمل أداةً بسيطةً لتعرف وجهات نظر زبائنك. GoSquared قد تظن أنَّ استعمال أدوات المحاثة الحية مقتصرٌ على شركات التجارة الإلكتروني التي تريد التفاعل مع الزبائن لزيادة احتمال بيع منتجاتها. لكنك ستُفاجأ من كثرة الاستخدامات التي ستجدها لبرمجيات المحادثة الحية؛ إحدى ميزاتها غير المتوقعة هي معرفة إن كان زوارك على وشك مغادرة الموقع. فباستخدام أدوات مثل GoSquared، يمكنك أن تُنشَأ تواصلًا مع زوارك، وتتبعهم أثناء رحلتهم في موقعك، وتوفر لهم اهتمامًا ومساعدةً عندما يتحدثون معك. إضافة Live Chat by Formilla أحد أفضل الأمور التي تستطيع توفيرها لزوار موقعك هو أن يكون دعمك وإجابتك لأسئلتهم تفاعليًا؛ وعند استخدام نافذة محادثة صغيرة في الطرف السفلي من صفحتك، فأنت تخبرهم أنَّك موجودٌ إذا ما احتاجوا لك. هذه الإضافة تطلب من الزوار مشاركة بريدهم الإلكتروني أولًا قبل بدء المحادثة، وتوفِّر لهم «حسابًا» لكن تعلم مَن هم (وفي أي منطقةٍ يتواجدون، ومن أين أتوا إلى موقعك … إلخ.). وهذه فرصةٌ إضافيةٌ لكي تفهم فيها سلوك زوار موقعك، ولكي تعلم إذا كانوا يواجهون مشاكل أثناء تواجدهم في الموقع. أدوات التتبع الشاملة إذا كنتَ تظن أنَّ خرائط التفاعل جميلة، فقد كان الوقت الآن لترى أدواتٍ متكاملة التي تتضمن تتبعًا عبر خرائط التفاعل، وتسجيلًا لسلوك الزوار في الموقع، وتحليلًا لمسار التحويل (conversion funnel analysis)، والمزيد. Clicktale إذا كان وقتك ضيقًا وأردتَ أن تعرف مباشرةً ما الذي يُضغَط وبأي تواتر ومِن مَن، فأنصحك باستعمال خرائط التفاعل، لكن إذا أردتَ أن تُشاهد بالتفصيل ما الذي يفعله الزائر في موقعك، فانظر إلى التسجيل الذي توفره أداة Clicktale. هذه الأداة تُسجِّل جلسة مستخدم سطح المكتب أو الهاتف في موقعك، وتريك كيف حرَّكوا الفأرة في موقعك، ومتى مرَّروا إلى الأسفل، أو كيف ضغطوا على قائمة التنقل، وأداة Clicktale تتضمن خرائط تفاعل وتحليلًا لمسار التحويل، لكي تستطيع إنشاء دراسة تفصيلية عن زوار موقعك وسلوكهم. hotjar أداة hotjar هي أداةٌ شاملةٌ لتتبع الزوار وتعطيك كل التفاصيل التي تحتاج لها لكي تفهم سلوكهم ولكي تقيّم أداء موقعك. هذه الأداة تملك عدِّة ميزات تستحق الاستكشاف إذا أردتَ أن تحثّ زوارك على التواصل معك عبر الاستفتاءات والاستبيانات. LuckyOrange يجب أن تُدرِك في هذه اللحظة أنَّ كل الأدوات الشاملة تمتلك نفس الميزات. وإذا كنتَ تبحث عن أداةٍ تُسجِّل كل الأمور السابقة (إضافةً إلى غيرها) فانظر إلى أداة LuckyOrange. من السهل البدء باستخدام هذه الأداة؛ وهذه قائمةٌ بميزات تتبع الزوار التي تأتي معها: صفحة التحليل تسجيلات الموقع خرائط تفاعل ديناميكية محادثة حية تحليل لمسار التحويل نموذج لكتابة مراجعة أو للتواصل استفتاءات لأخذ رأي الزبائن الأدوات السلوكية أخيرًا وليس آخرًا، لنتحدث عن أهمية اختبار A/B للتجسس احترافيًا على زوار الموقع. اختبار A/B صحيحٌ أنَّ اختبار A/B ليس ضروريًا للتجسس على سلوك زوار موقعك، لكنه يساعدك في فهم ما الذي يجعلهم مستائين. لذا، إذا قمتَ بكل إجراءات التحليل، وتتبعتَ الزوار عبر خرائط التفاعل، وفعّلتَ إمكانية المحادثة الحية مع زوارك، ولكنك ما زلتَ لا تعرف ما السبب وراء مغادرة زوارك للموقع، فاستخدام اختبار A/B للتأكد من نظريتك. فهذا الاختبار هو أسهل طريقة للتأكد من جودة اتخاذ منهج مختلف لمحتوى موقعك أو لتصميمه أو لمسار التحويل، والعديد من أدوات اختبار A/B مبنيةٌ في ووردبريس ولا تحتاج إلى استخدام أدوات خارجية. الخلاصة هل تريد أن يقل معدّل ارتداد الزوار وأن يزداد زمن بقائهم في موقعك؟ وتريد أن ترى المسار الذي اتبعه كل زائر في موقعك؟ وتريد أن تمنح زوارك تجربةً خاصةً والتي لن يحصلوا عليها من موقعٍ منافسٍ لك؟ لا تنتظر أن يظهر الزوار من العدم (لأنك قد تنتظر إلى الأبد حتى يحدث ذلك)، وابدأ بالتجسس عليهم وانظر كيف تستطيع تحسين موقعك. ترجمة -وبتصرّف- للمقال‎14 Sneaky Online Tools to Help You Spy on Your Site’s Visitors لصاحبته Brenda Barron.
  8. كانت هنالك الكثير من المحاولات الباكرة لابتكار تقنية لإنشاء رسوميات ثلاثية الأبعاد 3D تفاعلية في صفحات الوِب، بما فيها VRML وFlash، لكن لم يظهر معيارٌ متكاملٌ حتى عام 2013 ألا وهو WebGL المبني على OpenGL ES 2.0، لذا أصبح بإمكاننا تضمين رسوميات 3D تفاعلية في صفحات الوِب دون الحاجة إلى أيّة إضافات أو ملحقات للمتصفح. ما هي تقنية WebGL؟ تسمح تقنية WebGL بكتابة رسوميات 3D في صفحات الويب باستخدام JavaScript عبر العنصر <canvas>، لكن تقنية WebGL لا تُنشِئ «عناصر» على الصفحة، إذ تتعامل مباشرةً مع البكسلات؛ وبالتالي نقول عن تقنية WebGL أنّها تقنيةٌ منخفضة المستوى: حيث توفِّر تحكمًا دقيقًا بالفضاء ثلاثي الأبعاد. وعلى النقيض من أغلبية تطبيقات 3D، لا تتضمن WebGL «مشاهد» (Scenes) أو «كائنات» (Objects) أو «نماذج» (Models). وهذا ما يجعلها تقنيةً قويةً جدًا، لكن في الوقت نفسه تُصعِّب عملية التعلم بالنسبة للمطورين، ولا يمكن إخراج النتائج بواسطتها بسرعة. صحيحٌ أنَّ من الممكن تقنيّا كتابة شِفرات WebGL عبر JavaScript مباشرة، لكن أغلبية المطورين يستعملون إطار عمل Framework، وأشهر إطار عمل لتقنية WebGL هو threeJS، لكن هنالك خياراتٌ أخرى عداه. الحاجة إلى إطار عمل أنصح دومًا بالابتعاد عن أطر العمل: فأنا أعتقد أنَّ المطورين سيتعلمون أمورًا تفيدهم في المستقبل إذا تمكنوا من فهم الأساسيات التي تُبنَى عليها تقنيةٌ ما، بدلًا من سَلك «طرق مختصرة». لكنّ الرسم ثلاثي الأبعاد موضوعٌ معقدٌ جدًا، ومحاولة بناء خبرة عملية باستخدام WebGL إضافةً إلى تعلم تقنيات الوِب الأخرى أمرٌ صعبٌ للكثيرين، وتعلّم أساسيات WebGL يعني إضاعة الكثير من الوقت في التعلم دون القدرة على إنشاء أيّة رسومات ولو كانت بسيطةً. لهذا السبب، سنستعمل في شرحنا لهذه التقنية إطار threeJS، ولا أفترض عند كتابتي لهذه السلسلة أنَّ القارئ على دراية بمبادئ الرسم ثلاثي الأبعاد، لكن يجب أن يكون ذا درايةٍ جيدة بتقنيات HTML وCSS وJavaScript. سنستخدم في هذه السلسلة آخر إصدار متوافر من إطار threeJS. ما الذي يجعل هذه السلسلة مختلفةً عن غيرها أغلبية الدروس التعليمية التي تتحدث عن threeJS والمتوافرة على الوِب هي دروسٌ ليست بالمستوى المطلوب، حيث لا تعمل الأمثلة الحية لعدم تحديث الشفرة منذ فترةٍ طويلةٍ (تأتي التحديثات والتحسينات على إطار عمل threeJS بين الحين والآخر)، وهنالك خطواتٌ كاملةٌ غيرُ مشروحةٍ. لذا سأبذل جهدي لتفادي ذلك في هذه السلسلة، حيث سأضع المعلومات بتسلسلٍ منطقيٍ شارحا باستفاضة، مستعملًا أمثلةً عمليةً مسليةً ألا وهي نمذجة الكواكب الموجودة في نظامنا الشمسي كرسومات 3D. بناء كوكب المريخ باستخدام WebGL بعد أن تعرفنا على WebGL وتحدثنا أننا سنستعمل إطار العمل threeJS للاستفادة من إمكانية WebGL لإنشاء رسوميات 3D في صفحات الويب التي نُنشِئها، فسنكمل تعلمنا في بقية هذا الدرس (وفي الدروس القادمة) بإنشاء نموذج لكوكب المريخ؛ وسنستهل الأمر بتعلم كيفية وضع كاميرا ثلاثية الأبعاد، والتحكم بتفاعل محتوى WebGL مع بقية صفحة HTML الموجود داخلها. ملاحظة: سنشرح كل خطوة بالتفصيل أثناء بنائنا للنموذج، لذا لن يكون لدينا مشهدٌ جاهزٌ في نهاية هذا الدرس، وإنما سنفعل ذلك في المقالات اللاحقة المبنية على هذه المقالة (والمستقلة عنها)، أما لو كنتَ متحمسًا لرؤية الناتج النهائي، فاطلع على هذه التجربة الحية. تهيئة الصفحة على الرغم من إمكانية استخدام عناصر WebGL بمفردها في الصفحة، إلا أنها تُصنَّف كتقنية وِب معيارية، مما يعني أنَّها تندمج اندماجًا كاملًا مع محتوى HTML، ولعدم امتلاك العنصر <canvas> ميزات تتعلق بقابلية الوصول وتحسين أرشفة محركات البحث للصفحة، فمن المنطقي أن نُنشِئ محتوى WebGL ضمن صفحة وِب فيها معلوماتٌ تشرح محتواه، وذلك بوضع النص ضمن شيفرات HTML، ثم إضافة محتوى WebGL عند الحاجة. وسنؤسِّس لصفحتنا –التي تتحدث عن المريخ– باستعمال الشيفرة الآتية: <div id="marsloc"></div> <article id="marsinfo"> <h1>Mars</h1> <div> <p>Home to both the Solar System’s highest mountain… </div> </article> سيوضع محتوى WebGL ضمن العنصر marsloc، وسيوضَع النص الموجود في عنصر <div> فوق جميع محتويات الصفحة. هذه هي شِفرة Sass التي تتحكم بمظهر الصفحة: body { background: black; margin: 0; min-height: 100vh; color: #fff; } #marsloc { cursor: grab; } #marsinfo { position: absolute; top: 0; width: 100%; padding: 2rem; } #marsinfo h1 { font-size: 8vw; margin-top: 0; font-weight: 100; line-height: 1; position: absolute; } #marsinfo div { width: 40%; position: absolute; background-color: rgba(0,0,0,0.3); right: 0; padding: 1.3rem; line-height: 1.6; font-size: 1.2rem; pointer-events: none; @media all and (max-width: 540px) { width: 100%; left: 0; top: 40vw; } } تغيير شكل مؤشر الفأرة في العنصر ‎#marsloc سيُدلُّ على محتوى WebGL، أما العنصر div الموجود في ‎#marsloc فله الخاصية pointer-events: none لذا لن يتأثر النص الموجود أعلى محتوى WebGL بالتعديلات التي نجريها على الكوكب. إنشاء الكوكب بعد كتابة شفرات HTML و CSS، فلنحمِّل آخر إصدار من threeJS من CDN في أسفل الصفحة: <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.js"> </script> ملاحظة: علينا أن نضمن وجود إطار عمل threeJS قبل كتابة أيّة شفرات تتعلق به. سيبدأ السكربت بضبط مجموعة من المتغيرات والثوابت التي سنستعملها في شيفرتنا: var container, controls, camera, renderer, scene, light, marsMesh, clock = new THREE.Clock(); const imgLoc = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/"; سنستخدم المتغير clock للمساعدة في الحركة الافتراضية لكوكب المريخ، أما imgLoc فهو ثابتٌ يُشير إلى موقع جميع الصور التي سنستعملها وعرّفناه في البداية كي لا نُكرِّر المسار لاحقًا. مجال الرؤية أوّل عنصر سنُنشِئه هو الكاميرا، وهذا أمرٌ ضروريٌ لنتمكن من «رؤية» المشهد الذي سنُنشِئه. يوجد نوعان من الكاميرات في إطار threeJS: المنظور (perspectiveCamera): الخطوط المتوازية الموجودة في المشهد ستتقارب من بعضها إذا امتدت لمسافة طويلة (تخيل نفسك واقفًا على سكةٍ حديديةٍ وتنظر إلى أبعد جزء ينتهي إليه بصرك)؛ وستبدو الأجسام البعيدة من الكاميرا صغيرةً. وهذه مشابه لآلية عمل بصرنا، وسيبدو المشهد من هذا النوع من الكاميرات واقعيًا. الإسقاط العمودي (OrthographicCamera): الخطوط المتوازية ستبقى متوازية بغض النظر عن مدى امتدادها؛ ولنفس السبب، لن تبدو الأجسام أصغر كلما ابتعدت عن الكاميرات. نستفيد من هذا النوع من الكاميرات عند رسم عناصر الواجهة الرسومية أو لبعض المناظير المعمارية. إذا كنّا سنستخدم كاميرا ذات منظور (perspectiveCamera) فعلينا أن نُعرِّف مجال الرؤية (يُشار إليه عادةً بالاختصار FOV، الذي يرمز إلى Field of view). يُحدِّد مجال الرؤية مدى اتساع «رؤية» الكاميرا. فمجال الرؤية الضيق (الذي يشبه الغِماء Blinker الذي يضعونه على أعين الخيول التي تجر العربات، كيلا تجزع الأحصنة مما حولها) يؤدي إلى «تركيز» الكاميرا ugn جزءٍ مُحدّد من المشهد، مما يؤدي إلى عدم إظهار بعض العناصر لأنها ستقع خارج مجال الرؤية. أما مجال الرؤية العريض فيؤدي إلى إظهار المزيد من الأجسام، لكنه يُسبِّب في جعلها تبدو أصغر وأبعد. علينا أيضًا ضبط نسبة العرض إلى الارتفاع Aspect ratio: أي ما هو عرض المشهد نسبةً إلى ارتفاعه. من المرجّح أنَّك قد سمعتَ بنسبة العرض إلى الارتفاع في الأفلام، فالأفلام ذات المشاهد «العريضة» تجنح لأن تكون أفلام «ملحمية»، أما الأفلام التي نسبة العرض إلى الارتفاع قليلة (تكاد أن تصبح مربعةً) فستشعر أنها أفلامٌ قديمة. نريد غالب الأوقات أن تتشابه نسبة العرض إلى الارتفاع في المشهد مع نسبة العرض إلى الارتفاع التابعة لنافذة المتصفح للزائر. آخر قيمتين علينا ضبطهما للكاميرا هما قيمتا لوحي القص Clipping plane البعيد والقريب. مبدئيا، تستطيع الكاميرات ثلاثية الأبعاد أن «ترى» إلى مسافة لا متناهية (على النقيض من المشاهد الواقعية)، أي لا توجد محدوديات في العدسات المستعملة في الكاميرات ثلاثية الأبعاد ولا توجد جزئيات في طبقات الجو تعرقل رؤيتها. يُشار إلى ذلك في الألعاب بالمصطلح «مسافة الرؤية» (أو مسافة الرسم، Draw distance)، وهذا هو أحد أسباب استخدام الضباب في الألعاب ثلاثية الأبعاد القديمة: فكلما قلَّ عدد العناصر التي يجب تحميلها كانت اللعبة أسرع. وفي حالتنا، نريد أن يكون لوح القص القريب Near clipping plane قريبًا جدًا إلى «عدسة» الكاميرا، ولوح القص البعيد Far clipping plane على مسافةٍ معقولة، وبالتالي ستحتوى العناصر التي نريد عرضها بين هذين اللوحين. الشفرة التي سنستعملها لفعل ذلك هي: camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000); أصبحتَ لدينا الآن كاميرا ثلاثية الأبعاد، لكنها موجودة في فضاء أسود لا متناهي، ولا تُشير إلى أيّ شيءٍ بعد؛ لكننا سنشرح في الدرس القادم طبيعة الفضاء ثلاثي الأبعاد، وكيفية إضاءته، وطريقة إضافة العناصر إلى المشهد. ترجمة –وبتصرّف– للمقال Introduction to WebGL وللمقال A WebGL Tour of the Solar System: Mars, Part One لصاحبهما Dudley Storey.
  9. تأتيني عادةً أسئلةٌ عمّا إذا كانت شبكةٌ متعددة المواقع تعمل على ووردبريس هي خيارٌ مناسبٌ لمواقع الشركات التي لديها أقسامٌ متنوعةٌ ذات طابعٍ مختلف. ويكون جوابي عادةً هو «نعم»، يمكن لشبكةٍ متعددة المواقع فعل ذلك. لكن ليس من الضروري إنشاء موقعٍ منفصلٍ لكل قسمٍ من أقسام شركتك (حتى لو استعملتَ شبكةً متعددة المواقع تعتمد على ووردبريس)؛ فإذا أردتَ مشاركة المحتوى غير الخاص بقسمٍ معيّن، أو كانت «هوية» (identity) أقسام شركتك تتشابه كثيرًا، أو إذا كان عندك موقعٌ مستقل ولا ترغب بتحويله إلى شبكةٍ متعددة المواقع، فهنالك دومًا طريقةٌ للالتفاف على هذه الإشكالية دون تفعيل الشبكة متعددة المواقع. سأريك في هذا المنشور طريقة فعل ذلك باستخدام «التصنيفات المخصصة» (custom taxonomies). حيث سنُنشِئ تصنيفًا باسم «division» الذي يمكن إسناد المنشورات والصفحات إليه، لذا ستتمكن من تحديد ما هي الأقسام التي يجب وضع كل منشور أو صفحة فيها. ثم سنُنشِئ ملف قالب لذاك التصنيف، مما يعني إمكانية عرض المحتوى الموجود في كل قسم بالشكل الذي تريده. سنحتاج إلى كتابة بعض الشيفرات لإنشاء إضافة (plugin) وإنشاء ملف في قالبك، لكن الشيفرة لن تكون صعبةً، وسنستخدم «قالب ابن» تابع للقالب الافتراضي Twenty Sixteen لكن يمكنك استخدام هذه الطريقة على أيّ قالب تشاء. سنُنشِئ أيضًا ملفًا لتسجيل (register) التصنيف المخصص، ومن الأفضل فعل ذلك عبر إضافة، لكيلا تخسر الشيفرة التي تُنشِئ التصنيف المخصص في حال تغيّر قالب مستقبلًا. لنبدأ بالعمل. تجهيز القالب الابن قبل أن نبدأ، يجب أن نملك: موقعًا تطويريًا أو مخصصًا للتجريب يعمل على ووردبريس، والذي يُمثِّل نسخةً من موقع الشركة الذي لديك. لكن أحذِّرك أن تفعل ذلك على موقعٍ إنتاجي، وإنما ارفع الملفات إلى الموقع الإنتاجي بعد أن تتأكد أنَّها تعمل دون مشاكل. محرِّر شيفرات. أحد القوالب، الذي يمكن أن يكون قالبًا خاصًا بشركتك، أو قالب ابن للقالب Twenty Sixteen (كالذي أستعمله)، إذا كنتَ ستستخدم قالب ابن فتأكَّد أنَّ قالب Twenty Sixteen مثبّتٌ لديك. ابدأ بإنشاء قالب ابن وتفعيله، إلا أنّي لن أشرح هذا بالتفصيل، وإنما سأحيلك إلى درسٍ آخر. هذه هي الشيفرة التي سأستخدمها في ملف style.css في القالب الابن: /* Theme Name: WPMU DEV Taxonomy for Company Website Divisions Theme URI: https://github.com/rachelmccollin/wpmu-taxonomy-company-divisions Description: Theme to support WPMU DEV post on custom menu items Author: Rachel McCollin Author URI: http://rachelmccollin.co.uk/ Template: twentysixteen Version: 1.0 */ @import url("../twentysixteen/style.css"); ربما تلاحظ من الشيفرة السابقة أنني رفعتُ الملفات الخاصة بهذا الدرس على GitHub، يمكنك أن تنزلها من هناك وتستعملها بدلًا من إنشائها يدويًا. أنشَأتُ أيضًا موقعًا تجريبيًا. بعد أن تفعِّل القالب الابن، فستلاحظ أنَّه يبدو كقالب Twenty Sixteen الافتراضي: تسجيل التصنيف المخصص في ووردبريس الخطوة التالية هي تسجيل التصنيف المخصص عبر إنشاء إضافة. إلا أنَّك تستطيع إضافة الشيفرة إلى ملف functions.php في قالبك، لكن المشكلة هي أنَّ هذه الشيفرة ستُحذف (وسيختفي التصنيف المخصص الجديد) في حال حدَّثتَ قالبك في المستقبل. أنشِئ ملفًا جديدًا في مجلد الإضافات plugins (الموجود في مجلد wp-content) وأعطهِ اسمًا مناسبًا، وافتحه وأضف إليه التعليقات الآتية: <?php /** * Plugin Name: WPMU DEV Register Division Taxonomy * Plugin URI: https://github.com/rachelmccollin/wpmu-taxonomy-company-divisions * Description: Registers a 'division' taxomy for use in your copmany website. * Version: 1.0 * Author: Rachel McCollin * Author URI: http://rachelmccollin.co.uk */ أضف الآن هذه الدالة لتسجيل التصنيف المخصص: function wpmudev_register_taxonomy() { $labels = array( 'name' => __( 'Divisions' ), 'singular_name' => __( 'Division' ), 'search_items' => __( 'Search Divisions' ), 'all_items' => __( 'All Divisions' ), 'edit_item' => __( 'Edit Divisions' ), 'update_item' => __( 'Update Divisions' ), 'add_new_item' => __( 'Add New Division' ), 'new_item_name' => __( 'New Division Name' ), 'menu_name' => __( 'Divisions' ), ); $args = array( 'labels' => $labels, 'hierarchical' => true, 'sort' => true, 'args' => array( 'orderby' => 'term_order' ), 'show_admin_column' => true ); register_taxonomy( 'division', array( 'post', 'page' ), $args); } add_action( 'init', 'wpmudev_register_taxonomy' ); الشيفرة السابقة ستُسجِّل تصنيفًا باسم division وتعطي لافتاتٍ مناسبةً له وتضبط عنوانًا سيظهر في القائمة… وتحدِّد الشيفرة السابقة أيضًا أنَّنا نستطيع إضافة هذا التصنيف إلى المنشورات والمقالات وذلك عبر تمرير الوسيط array( 'post', 'page' ) إلى دالة register_taxonomy()‎. فعِّل الإضافة في موقعك، ويجب أن تملك الآن وصولًا إلى تصنيفٍ جديد عندما تحاول تحرير الصفحات والمنشورات: يمكنك الآن إنشاء عناصر ضمن التصنيف الجديد تُمثِّل أقسام شركتك، ثم تُسنِد إليها المنشورات والصفحات. عرض أقسام شركتك في الموقع لقد أنشأتُ بعض المحتوى من منشوراتٍ وصفحاتٍ وأسندته إلى أقسامٍ مختلفة. هذه بضعة منشوراتٍ من التي أضفتُها: أنشأتُ أيضًا بعض الصفحات للأقسام، وأنشأتُ أيضًا صفحاتٍ عامةً: يمكنك الآن إضافة جميع صفحاتك وأقسامك إلى قائمة التنقل الأساسية في موقعك، لكن لا تُضف المنشورات، لأنها ستُعرضَ في صفحات الأرشيف. لقد فعلتُ ما سبق في صفحة «Menus» (القوائم) في لوحة التحكم: ستُنشِئ ووردبريس صفحة أرشيف تلقائيًا لكل قسم من أقسام شركتك، وستعرض تلك الصفحات باستعمال أكثر صفحات القالب تحديدًا والموجودة في قالبك (وفقًا لهيكلية ملفات القوالب). هذه هي صفحة الأرشيف لتصنيف «Research and Development»: قامت ووردبريس بعملها على أتم وجه، فوضعت المنشورات والصفحات المُسنَدَة إلى التصنيف. لكنها تعرضها بترتيبٍ زمني كما قد تتوقع، لكنني أفضِّل عرض جميع الصفحات أولًا متبوعةً بالمنشورات، ولفعل ذلك سأُنشِئ صفحة أرشيف للتصنيف المُخصص وأستعمل فيها نسخةً مخصصةً من حلقة الدوران في ووردبريس (يسمونها the loop). إنشاء صفحة أرشيف للتصنيفات المخصصة لعرض صفحة أرشيف التصنيفات المخصصة كما نشاء، فعلينا إنشاء ملف قالب باسم taxonomy-divsion.php ولنفعل ذلك بنسخ ملف قالب موجود مسبقًا ثم تعديله. إذا كان في قالب ملفٌ باسم archive.php فأنشِئ نسخةً منه وسمِّها taxonomy-divsion.php، أما إذا لم يكن في قالبك الملف السابق، فانسخ الملف index.php. ولأنني أعمل مع قالب Twenty Sixteen فأُنشِئ نسخةً من ملف archive.php وأضعه في مجلد القالب الابن باسم taxonomy-divsion.php. علينا أولّا تعديل التعليقات في بداية الملف لكي تصف بدقة محتويات هذا الملف: <?php /** * The template for displaying division archive pages */ اعثر الآن على الشيفرة المسؤولة عن حلقة التكرار، وهي تبدو كالآتي في حال نسختَها من قالب Twenty Sixteen: <?php // Start the Loop. while ( have_posts() ) : the_post(); /* * Include the Post-Format-specific template for the content. * If you want to override this in a child theme, then include a file * called content-___.php (where ___ is the Post Format name) and that will be used instead. */ get_template_part( 'template-parts/content', get_post_format() ); // End the loop. Endwhile; الشيفرة السابقة تستدعي ملفًا من Twenty Sixteen باسم content.php، سنبقي على الشيفرة السابقة، لكننا سنُشغِّلها مرتين: مرة للمنشورات ومرة للصفحات. عدِّل حلقة التكرار في ملف القالب لتصبح كما يلي: <?php // Start the first Loop - pages. while ( have_posts() ) : the_post(); $posttype = get_post_type( get_the_ID() ); if ( $posttype == 'page' ) { get_template_part( 'template-parts/content', get_post_format() ); } // End the loop. endwhile; rewind_posts(); // Start the second Loop - posts. while ( have_posts() ) : the_post(); $posttype = get_post_type( get_the_ID() ); if ( $posttype == 'post' ) { get_template_part( 'template-parts/content', get_post_format() ); } // End the loop. Endwhile; الشيفرة السابقة ستؤدي إلى استدعاء حلقة التكرار مرتين، لكنها ستطلب الصفحات والمنشورات من قاعدة البيانات مرةً واحدةً فقط، وهذا أفضل من استخدام WP-Query لإنشاء طلبية جديدة. سنتحقق في بداية الحلقة فيما إذا كان «نوع المنشور» (post type) مساوٍ للقيمة page وفي حال تحقق الشرط فسنعرض الصفحة، ثم سنُعيد الكرّة مرةً أخرى للمنشورات العادية ذات النوع post. احفظ الملف السابق وستجد أنَّ صفحة الأرشيف أصبحت تعرض الصفحات أولًا ثم المنشورات: هذه هي شيفرة ملف القالب كاملةً: <?php /** * The template for displaying division archive pages */ get_header(); ?> <div id="primary" class="content-area"> <main id="main" class="site-main" role="main"> <?php if ( have_posts() ) : ?> <header class="page-header"> <?php the_archive_title( '<h1 class="page-title">', '</h1>' ); the_archive_description( '<div class="taxonomy-description">', '</div>' ); ?> </header><!-- .page-header --> <?php // Start the first Loop - pages. while ( have_posts() ) : the_post(); $posttype = get_post_type( get_the_ID() ); if ( $posttype == 'page' ) { get_template_part( 'template-parts/content', get_post_format() ); } // End the loop. endwhile; rewind_posts(); // Start the second Loop - posts. while ( have_posts() ) : the_post(); $posttype = get_post_type( get_the_ID() ); if ( $posttype == 'post' ) { get_template_part( 'template-parts/content', get_post_format() ); } // End the loop. endwhile; // Previous/next page navigation. the_posts_pagination( array( 'prev_text' => __( 'Previous page', 'twentysixteen' ), 'next_text' => __( 'Next page', 'twentysixteen' ), 'before_page_number' => '<span class="meta-nav screen-reader-text">' . __( 'Page', 'twentysixteen' ) . ' </span>', ) ); // If no content, include the "No posts found" template. else : get_template_part( 'template-parts/content', 'none' ); endif; ?> </main><!-- .site-main --> </div><!-- .content-area --> <?php get_sidebar(); ?> <?php get_footer(); ?> تنسيق أقسام الشركة بشكلٍ مختلف أصبح لدينا الآن ملف قالب يعرض صفحات ومنشورات أقسم الشركة بالطريقة التي نرغب بها، وحان الوقت الآن لجعل صفحات الأقسام تختلف بصريًا عن بعضها بإضافة بعض التنسيق إليها. يمكنك أن تُنسِّق أقسام موقعك كما تشاء، وربما ستُنسِّقها أكثر مما سأريك إياه في هذا الدرس، إلا أنني سأريك كيف تغيّر ألوان بعض أجزاء الصفحات والمنشورات وصفحات الأرشيف لكل قسم، لجعلها تتميز عن بعضها. يمكنك أن تفعل ذلك في صفحة الأنماط التابعة للقالب الابن (أو في صفحة الأنماط في القالب الأساسي إن لم تكن تستعمل قالب ابن). افتح ملف style.css وأضف القواعد الآتية إليه: .term-research-development .page-header { border-top: 4px solid #d8a518; } .term-research-development .page-title { color: #d8a518; } .term-operations .page-header { border-top: 4px solid #5674A0; } .term-operations .page-title { color: #5674A0; } .term-sales .page-header { border-top: 4px solid #FE6763; } .term-sales .page-title { color: #FE6763; } .term-sales .page-header { border-top: 4px solid #78D400; } .term-sales .page-title { color: #78D400; } هذا سيُضيف تنسيقًا لترويسات صفحات الأرشيف باستهداف الأنماط (classes) المُطبَّقة على العنصر body وذلك اعتمادًا على التصنيفات التي استخدمتها. ستحتاج إلى تعديل القيم السابقة بما يتوافق مع أسماء أقسام شركتك، يمكنك أن ترى أسماء الأصناف المولّدة تلقائيًا عبر تفحص شيفرة HTML في صفحة أرشيف القسم في موقعك، وستكون مرتبطةً بالعنصر body. استعملتُ اللون الأصفر في صفحة تصنيف «Research and Development» لتمييزها: يمكنك إضافة المزيد إلى صفحة الأنماط، فربما تريد تنسيق المنشورات التابعة لمدونتك العامة بشكلٍ يختلف عن تنسيق منشورات بقية الأقسام الأخرى، ويمكنك أيضًا تنسيق الصفحات والمنشورات التابعة لمختلف الأقسام بأشكالٍ متنوعة. يمكنك أيضًا تنسيق قائمة التنقل الرئيسية لكي تُطابِق تنسيق بقية الصفحة. أي أنَّ خياراتك ليست محدودة، ومن الممكن أن تجعل مظهر صفحات أقسام شركتك مختلفةً عن بعضها تمامًا. الخلاصة باستخدام التقنيات التي شرحناها في هذا الدرس، يمكننا إنشاء موقع لشركة فيه أقسامٌ مختلفة، وتعرض تلك الأقسام المحتوى المتعلق بها فقط، ونستطيع استخدام فئات CSS التي تُضيفها ووردبريس إلى عنصر body لتنسيق تلك الأقسام بشكلٍ مختلف. يمكنك أن تكمل المشوار في تنسيق الصفحات، وذلك بإضافة صور أو خلفيات أو يمكنك تغيير الخطوط وذلك عبر صفحة الأنماط. وتستطيع أيضًا إنشاء ملف قالب مختلف لكل قسم، وذلك باستعمال هيكلية ملفات القوالب استعمالًا صحيحًا لتعرف طريقة تسمية الملفات. كل ما سبق يعني أنَّك إذا كنتَ تعمل على موقعٍ موجودٍ مسبقًا ولم تشأ إنشاء مواقع مخصصة لكل قسم من أقسام شركتك، فيمكن لموقع ووردبريس وحيد أن يضفي طابعًا خاصًا لكل قسم من أقسام الشركة. ترجمة -وبتصرّف- للمقال How to Create a Custom Taxonomy for Departments or Divisions on a Company WordPress Siteلصاحبته Rachel McCollin.
  10. تمهيد عمومًا، نستعمل حاويات Docker لمرة وحيدة فقط، وهي تعمل إلى أن ينتهي تنفيذ الأمر الموكل إليها. وافتراضيًا تكون البيانات المُنشَأة داخل الحاوية متاحةً فقط إلى الحاوية وقابلة للوصول عند تشغيل الحاوية فقط. حجوم Docker (أي Docker volumes) يمكن أن تُستخدَم لمشاركة الملفات بين النظام المضيف (host system) وحاوية Docker. لنقل –على سبيل المثال– أننا نريد استخدام صورة Nginx الرسمية ونحتفظ بسجل دائم لخادوم Nginx لكي نحلل معلوماته لاحقًا. وافتراضيًا، ستكتب صورة Niginx الرسمية سجلاتها إلى ‎/var/log/nginx داخل الحاوية، والتي لا نستطيع في الحالة العادية الوصول إلى ذاك الملف من النظام المضيف. سنشرح في هذا الدرس كيفية جعل البيانات الموجودة داخل الحاوية متاحةً للجهاز المضيف. المتطلبات المسبقة خادوم أوبنتو 16.04 (أو 14.04) مضبوطٌ كما في درس «الإعداد الابتدائي لخادوم أوبنتو 14.04»، بما في ذلك إعداد حساب مستخدم عادي لكنه يملك امتيازات الجذر (root) عبر الأداة sudo. برمجية Docker مثبّتة على حاسوبك إذا كنتَ جديدًا على Docker، فسلسلة «docker ecosystem» المنشورة على الأكاديمية تعطيك لمحةً عن طريقة استعمالها. الخطوة الأولى: الوصل الترابطي لحجم التخزين الأمر الآتي سيُنشِئ مجلدًا باسم nginxlogs في مجلد المنزل (Home) للمستخدم الحالي ويصله وصلًا ترابطيًا (bindmount) إلى ‎/var/log/nginx في الحاوية: docker run --name=nginx -d -v ~/nginxlogs:/var/log/nginx -p 5000:80 nginx لنأخذ دقيقةً من الزمن لتفحص الأمر بالتفصيل: ‎--name=nginx: تسمية الحاوية لكي نستطيع الإشارة إليها لاحقًا بسهولة. ‎-d: فصل العملية الحالية من الطرفية وتشغيلها في الخلفية، وإلا فسنرى مِحَث Nginx فارغ ولن نتمكن من استعمال جلسة الطرفية إلا بعد انتهاء تنفيذ Nginx. ‎-v ~/nginxlogs:/var/log/nginx: ضبط حجم الوصل الترابطي الذي يربِط بين مجلد ‎/var/log/nginx داخل حاوية Nginx إلى مجلد ‎‎~/nginxlogs في الجهاز المضيف. تَستعمِل Docker النقطتين الرأسيتين : لفصل مسار المضيف عن مسار الحاوية، ويجب ذكر مسار المضيف أولًا. ‎-p 5000:80: ضبط تمرير المنافذ؛ حيث تستمع خدمة Nginx داخل الحاوية إلى المنفذ 80 افتراضيًا، وهذا الخيار يؤدي إلى ربط المنفذ 80 في الحاوية إلى المنفذ 5000 في النظام المضيف. nginx: تحديد أنَّ هذه الحاوية يجب بناؤها على صورة Nginx، والتي تُنفِّذ الأمر nginx -g "deamon off"‎ لتشغيل Nginx. ‎-v /path:/path/in/container وصل المسار ‎/path في الجهاز المضيف إلى ‎/path/in/container في الحاوية. ‎-v path:/path/in/container إنشاء حجم باسم path دون علاقة بالمضيف. الخطوة الثانية: الوصول إلى البيانات من المضيف لدينا الآن خادوم Nginx يعمل داخل حاوية موجودة في جهازنا المحلي، ولدينا المنفذ 5000 الموجود في جهاز المضيف مرتبطٌ بالمنفذ 80 للحاوية. افتح عنوان خادوم الويب في متصفحك، مستخدمًا عنوان IP لجهازك المحلي ورقم المنفذ 5000، مثلًا: http://203.0.113.0:5000 ويجب أن تشاهد الرسالة الآتية: وإذا نظرنا الآن في مجلد ‎~/nginxlogs الموجود في المضيف، فسنرى الملف access.log المُنشَأ من حاوية nginx والذي تظهر فيه الطلبية التي أجريناها: cat ~/nginxlogs/access.log يجب أن يحتوي شيئًا شبيهًا بما يلي: 203.0.113.0 - - [11/Nov/2016:00:59:11 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36" "-" إذا أجريتَ أيّة تعديلات إلى مجلد ‎~/nginxlogs فيجب أن تراها من داخل الحاوية مباشرةً. الخلاصة شرحنا في هذا الدرس كيفية إنشاء حجم Docker لمشاركة المعلومات بين الحاوين والنظام المضيف، مما يفيدنا كثيرًا في بيئات التطوير، حيث من الضروري الوصول إلى السجلات لتحليلها. ترجمة -وبتصرّف- للمقال How To Share Data between the Docker Container and the Host لصاحبته Melissa Anderson
  11. هل جميع الزيارات التي تأتي موقعك هي زياراتٌ «جيدة»؟ إذا نظرتَ إلى الأمر بسطحية فقد تظن ذلك، لكن من الأفضل أن تُمعِن النظر في الزيارات التي تأتي إلى موقعك، فليست جميع الزيارات لها أغراضٌ حسنة! هنالك الكثير من الأسباب وراء رغبتك بالتحكم بالأماكن الجغرافية المسموح لها بالوصول إلى موقعك: إذا كانت عندك شركةٌ محلية ولم ترغب بإضاعة التّدفّق bandwith الخاص بخادومك على زياراتٍ لن تستفيد منها آتيةٍ من دولٍ أخرى. ففي النهاية لن يهتم بسلعك إلا السكان المحليون، ولن تلقي لها العائلات الموجودة في أقصاع الأرض بالًا. إذا كنت منزعجًا من التحليل الشهري لموقعك الذي يخرِّبه أولئك المزعجون الذين يلمؤن موقعك بالرسائل عديمة المعنى… إذا كانت شركتك تستهدف الزبائن الموجودين في منطقة جغرافية مُحدَّدة لأنَّ خدماتك مطلوبة جدًا في تلك المناطق دون وجود منافسة حقيقية من شركاتٍ أخرى. إذا كنتَ تخاف على موقعك من المخترقين من الدول الأخرى، وتريد أن تجعله أكثر أمانًا. لن يكون للموقع الجغرافي لزوار بعض المواقع أثرًا فيها، لكنه قد يُشكِّل عاملًا مهمًا في بعضها الآخر. فالسبب الوحيد لبنائك موقعًا (باستخدام ووردبريس) هو لكي تصل إلى الجمهور الصحيح، وهذا هو السبب وراء أهمية تحسين أداء موقعك، وأمانه. لكن إن كنت تظن أنَّ استهداف منطقة جغرافية معيّنة سيفيدك في إيصال رسالتك، فأكمل قراءة هذا الدرس. موارد تساعدك في تحديد المنطقة الجغرافية التي تأتي الزيارات منها يمكن أن تخبرك أداة Google Analytics من أين تأتي زيارات موقعك جغرافيًا. فكل مرة يزور أحدهم موقعك، فسيخبر عنوان IP للزائر برمجيةَ Google Analytics (أو أيّة برمجية أخرى تُسجِّل هذه المعلومات) عن المكان الجغرافي الذي يزور منه هذا الشخص موقعك. التصنيف عبر القارات: أو عبر الدول: أو عبر المدن: أظنَّ أنَّ الفكرة قد وصلتك. وصحيحٌ أنَّ أداة Google Analytics مفيدةٌ في معرفة من أين تأتي زيارات موقعك، لكن هذا الأداة لن تساعدك في زيادة الزيارات من مناطق جغرافية معيّنة إلى موقعك (أو بالعكس، تخفيض عدد الزيارات من مناطق أخرى). لكن إذا كنتَ تريد أن تُحدِّد من أين يأتي زوارك، فانظر إلى إحصائيات Google Analytics أولًا. بعد أن تأخذ فكرةً لا بأس بها عن أماكن قدوم زوار موقعك، فحان الوقت لكي تُقيّم إن كان هؤلاء الزوار مناسبين لموقعك أم لا. هنالك الكثير من الأدوات الخارجية التي يمكنك استخدامها لتحسين موقعك من ناحية توجيه المحتوى لمناطق جغرافية معيّنة. CDNs هي أداةٌ رائعة لكي تستعملها لتوجيه محتواك لجميع أنحاء العالم، لكن لا توجد خواديم لموقعك في جميع أنحاء العالم. يمكنك أيضًا استخدام Google Webmaster Tools لتحديد دولة لتكون هدفًا لموقعك. إذا كنتَ تتساءل في نفسك عن ووردبريس، فللأسف لا توجد ميزة مُضمَّنة في ووردبريس لمساعدة المطورين بتحسين المواقع التي يطورنها جغرافيًا. لكن هنالك عددٌ من الإضافات التي تساعدك في تلبية احتياجاتك بهذا الخصوص. الاستخدامات المختلفة لاستهداف الزوار جغرافيًا إذا أردتَ حجب الوصول إلى موقعك من دولة معيّنة، أو تستهدف أخرى، أو تُظهِر منتجات معيّنة اعتمادًا على الدولة التي يزور منها الزبون موقعك، أو حتى توفير محتوى مختلف بناءً على الموقع الجغرافي؛ فستحتاج حينئذٍ إلى إضافةٍ لكي تستطيع فعل ذلك. هذه بعض الإضافات التي قد تساعدك في استهداف زوار معيّين بناءً على عناوين IP الخاصة بهم. إضافات لتوفير محتوى مخصص اعتمادًا على الموقع الجغرافي هذه الإضافات مناسبة لتوفير محتوى اعتمادًا على الدولة. GeoTargeting Lite هذه هي النسخة المجانية من إضافة GeoTargeting Pro الشاملة والتي سنذكرها أدناه. الفرق الوحيد بين هذه الإضافة وإضافة GeoTargeting Pro هو أنَّ هذه الإضافة تسمح لك بجعل المحتوى يستهدف دولةً معيّنةً فقط، وإذا أردتَ ميزاتٍ متقدمة أو قدرةً على حجب الوصول لدول معيّنة، فعليك استخدام إضافة لهذا الغرض، أو شراء إضافة GeoTargeting Pro الكاملة. Geo Targeting add-on for Icegram إذا كنتَ تستخدم إضافة Icegram لوردبريس (الإضافة المدفوعة، وليست المجانية)، فربما تكون مهتمًا في معرفة أنَّ هنالك إمكانية تثبيت إضافة لتوجيه المحتوى جغرافيًا. فيمكن توسعة الإضافة الرائعة لإنشاء حملة تسويقية لكي تستهدف مناطق جغرافية معيّنة. إضافات لعرض إعلانات اعتمادًا على الموقع الجغرافي AdRotate إذا كنتَ تُخطِّط للكسب من وراء موقعك وكنت تريد تقديم إعلانات مختلفة اعتمادًا على الدولة أو الولاية أو المدينة، فانظر إلى إضافة AdRotate. تتوافر نسخة مجانية من هذه الإضافة في موقع ووردبريس، لكن النسخة المدفوعة توفِّر إمكانية توجيه الإعلانات إلى مناطق جغرافية. Advanced Ads هذه إضافةٌ أخرى للإعلانات التي توفر إمكانية توجيه الإعلانات إلى مناطق جغرافية معيّنة. وعلى الرغم من أنَّ هذه الإضافة مجانية، لكن عليك تثبيت إضافات geotargeting و geoblocking لها وتلك الإضافات غير مجانية. أما لو أردتَ تخصيص قواعد استهداف المناطق الجغرافية، فهذه هي الإضافة التي ستحتاج لها. إضافات الحجب حسب المناطق الجغرافية استعمل هذه الإضافات عندما تريد حجب دول أو مناطق كاملة من الوصول إلى موقعك. IP Geo Block جعل المحتوى يستهدف مناطق معيّنة يؤدي إلى توفير محتوى متنوع للزوار من مناطق مختلفة، أما الهدف من الحجب هو منع الوصول تمامًا من مناطق معيّنة، وهذا لا يعني منع الزوار فقط، وإنما حجب الأشخاص الذين يحاولون اختراق موقعك لأغراضٍ خبيثة. هذه الإضافة يمكنها أن تحجب الزوار عبر عناوين IP أو يمكن حجب دول كاملة من الوصول إلى موقعك عبر: صفحة تسجيل الدخول نموذج التعليقات جميع المداخل الموجودة في السند الخلفي (back-end) لموقعك. iQ Block Country إذا كنت مهتمًا بتقليل عدد زوار موقعك، فيمكنك استخدام هذه الإضافة، وهنالك عددٌ من الأسباب يدفعك لذلك: محاولة إصلاح مصدر المشكلة التي تخرِّب إحصائيات موقعك. توفير محتوى خاص للمستخدمين القادمين من دول أخرى. منع المخترقين من العثور على موقعك. إضافة شاملة لتوجيه المحتوى ولحجبه اعتمادًا على الموقع الجغرافي أما إذا أردتَ تحكمًا كاملًا بتوجيه محتواك أو إعادة توجهيه أو حجب الزوار بناءً على موقعهم الجغرافي، فانظر إلى الإضافة الآتية. GeoTargeting Pro هذه هي إضافةٌ مدفوعةٌ التي تساعد المواقع الكبيرة (أو شبكةً متعددة المواقع) التي تحتاج إلى توفير محتواها اعتمادًا على الموقع الجغرافي للزوار من كل الدول حول العالم. ستملك تحكمًا كاملًا بأنواع المحتوى الذي سيُخدِّم إلى مختلف الزوار وما هي الصفحات التي سيُعاد توجيه الزوار إليها. المحتوى القابل للتوجيه حسب الموقع الجغرافي يتضمن: منشورات المدونة، والصفحات الثابتة، وقوائم التنقل، والودجات… الخلاصة قد تجد أنَّ توجيه المحتوى أو حجبه اعتمادًا على الموقع الجغرافي مفيدٌ جدًا عندما تحاول الوصول إلى الجمهور المناسب لموقعك، وتُبعد الزوار غير المفيدين، وتُحسِّن من أداء موقعك، وتزيد من حمايته. إذا كنت تتساءل إن كان موقعك سيستفيد من توجيه المحتوى اعتمادًا على الموقع الجغرافي، وكان في ذهنك جمهورٌ يقطنُ في منطقةٍ معيّنة، فأنصحك بإلقاء نظرةٍ على الإضافات والأدوات السابقة. ترجمة -وبتصرّف- للمقال A Guide to Geotargeting and Geographic-Specific Content for WordPress لصاحبته Brenda Barron.
  12. لا يجب أن تبدأ دائمًا بالتطوير باستعمال إطار عمل JavaScript، لكن هنالك حالات يكون فيها استعمال إطار العمل أمرًا منطقيًا. منذ فترةٍ كتبتُ مقالةً بعنوان «لماذا يدفعنا التطوير بلغة JavaScript إلى حافة الجنون!» والتي زارها الكثيرون، وظهر التساؤلان الآتيان ردًا عليها: - متى يجب أن نستخدم إطار عمل؟ - إذا لم نستعمل إطار عمل، فكيف نبدأ؟ سأحاول في هذه المقالة الإجابة على التساؤل الأول، أما سؤال كيفية البدء إذا لم نستعمل إطار عمل فهو سؤالٌ أكبر، ويحتاج تفصيلًا أكثر. بدأتُ العمل منذ عدِّة سنوات في شركة (لن أسميها هنا)، والتي تستطيع أن تطلب منها إنشاء أي شيء غريب يمكن تخيله، وستصنعه لك خلال فترةٍ وجيزة. كان للشركة هامش ربحٍ كبير، وتجني أموالًا طائلة، وتعلمتُ الكثير عن إدارة الأعمال منها، لكنني تعلمتُ أيضًا بعض الأمور عن الأنظمة. كانت واجهة موقع الشركة الإلكتروني معقدةً! ولهذا السبب بدأ موقع الشركة في بدايات 2000 بالتحول إلى تطبيقٍ ذي صفحةٍ واحدة، حتى قبل انتشار فكرة التطبيقات ذات الصفحة الواحدة (single page application). كانت شيفرات JavaScript التي تشغل موقعهم معقدةً جدًا، وكانوا نادرًا ما يوظفون مبرمجين، واختاروا إطار عمل YUI بدلًا من jQuery لأنه كان شائعًا في تلك الفترة، ثم اشتقوا (fork) شيفرة YUI مما جعل التعامل معها وصيانتها أمرًا صعبًا جدًا. ولسببٍ أجهله، كانوا يكتبون شيفرات JavaScript بشكلٍ شبيهٍ بشيفرات Visual Basic. كانوا أيضًا يستعملون نظامًا لإدارة الإصدارات (version control system) من الثمانينات الذي كانت آلية حماية الملفات من التعديل فيه هي قفلها (file locks) والتي تستطيع بسهولة تجاوزها. سكربت البناء الذي كانوا يستعملونه هو شخصٌ اسمه Ed، فلتحويل الشيفرة إلى الخادوم الإنتاجي سيكون عليك أن تطبع قائمةً بالملفات التي عدَّلتَها وتسلمها لذاك الشخص، ثم سيبحث عن تلك الملفات وينسخها يدويًا إلى كل خادوم على حدة. كان السند الخلفي (backend) للموقع هو حاسوبٌ قديمٌ يُشغِّل برمجيات مكتوبة في سبعينيات القرن الماضي، وكانت تُحدَّث بيئة التطوير المشتركة يدويًا من قِبل المطورين، متى أرادوا ذلك. إذا كنتَ تضحك الآن على حالهم وتظن أن هؤلاء الأشخاص أغبياء، فأظن أنَّ الوقتَ مناسبٌ لأشير إلى أنهم لم يعانوا من انقطاعاتٍ في الخدمة، وكانوا يُصدرون ميزاتٍ جديدة دوريًّا، ويجنون أموالًا طائلة تزداد عامًا بعد عامًا. لذا أظن أنهم يضحكون الآن خلال طريقهم إلى البنك ليستلموا أرباحهم. هل تتلقى أجرًا لقاء كتابتك للشيفرات؟ إذًا أنت تعمل لدى شركة. وعندما تقرر الشركة كيف تُنفِق ميزانية المطورين، فلن يكون منطقيًا أن تنفق مالًا لكتابة البرمجيات «بالطريقة الصحيحة». الفكرة التي أحاول إيصالها هنا هي أنَّ حال الشركة السابقة قد يبدو بائسًا بالنسبة إلى مطوري البرمجيات، لكنه منطقيٌ جدًا وجيد بالنسبة إلى أصحاب تلك الشركة الذين يهتمون بالربح. استخدام إطار عمل JavaScript قد لا يكون خيارًا استراتيجيًا للشركات حتى لو كان حالها سيئًا كالشركة التي نتحدث عنها. إذًا، كيف يجب أن تتابع الشركة السابقة بتطوير برمجياتها؟ كيف ستقرر الشركة أنها بحاجة إلى استخدام إطار عمل؟ شرحتُ سابقًا في مقالة «لماذا يدفعنا التطوير بلغة JavaScript إلى حافة الجنون!» أنَّ من غير المنطقي البدء بتطوير البرمجيات انطلاقًا من إطار عمل. لنفترض وجود شيءٍ شبيهٍ بإطار Angular عند نشأة الشركة السابقة؛ فهل كان عليهم استخدام إطار عمل؟ هل يجب عليهم استخدام إطار عمل في هذه الفترة؟ عندما تختار الشركة إطار عمل لتستعمله لتطوير برمجياتها، فهنالك كلف ومخاطر: - ماذا لو اختفى دعم إطار العمل خلال خمس سنوات؟ هل يمكن أن تتحمل الشركة عبء صيانة إطار العمل بنفسها؟ هل يتوافر في فريق العمل أشخاصٌ لهم خبرات في هذا المجال؟ الجواب هو النفي لأغلبية الشركات، ولا نُغفِل أنَّ الكثير من أطر العمل Frameworks تختفي فجأةً. عليك أن تجري مع التيار وتخمِّن إن كانت الزيادة في الإنتاجية الآتية من اختيار إطار عمل تستحق الوقت والكلفة اللازمة للتحويل إلى إطارٍ آخر لاحقًا، أو أنَّها تستحق كلفة التعامل مع برمجيات غير مدعومة لسنوات في نفس الوقت الذي تحاول فيه توفير ميزانية كافية للتحويل إلى إطارٍ جديد. إذا كانت شيفرات برمجيتك قليلة نسبيًا وكنت تعمل في شركة وتحذف كميةً لا بأس بها من الشيفرات وتُعيد كتابتها (كثير منا يفعل ذلك لكنهم لا يعترفون)، فأرى أنَّ من المناسب استعمال إطار عمل. أما لو كانت شيفرات برمجيتك كثيرة جدًا، وكنت تنظر إلى خطتك التي وضعتها للسنوات الخمسة القادمة ورأيتها مليئةً بالميزات الجديدة التي ستكوِّن جزءًا أساسيًا من شركتك، فإن اختيار إطار عمل الذي سينتهي دعمه خلال خمسة سنوات هو رهانٌ خاسر، والطريقة الوحيدة التي ستجعلك تختار واحدًا هي التفكير بأجوبةٍ للأسئلة المتبقية … - كم ستخسر من إنتاجية مطوريك حتى يتعلموا إطار العمل الجديد؟ إذا كنت تنتقل إلى إطارٍ وليكن Angular (وما لم توظِّف مطورين يعرفون Angular من قبل ويلمّون بأحدث المعلومات) فستنفق مالًا لتدريب فريق التطوير وسيضيّع المطورون الأشهر القادمة يسألون «كيف أفعل ذلك في Angular؟» حتى لو كانوا يفعلون نفس الأمر تمامًا باستخدام jQuery وشيفرات JavaScript البسيطة دون أدنى تفكير. - هل يمكننا بناء هذه الميزة التي تجعلنا نجني مالًا أكثر بنفس مقدار الوقت اللازم لتعلم إطار العمل؟ الجواب لأغلبية الشركات الربحية هو «نعم». - هل سيسمح لنا إطار العمل بجني المزيد من المال بجعلنا نُنشِئ الميزات الجديدة بوتيرة أسرع في المستقبل؟ نظريًا: هذه هي الفائدة المرجوة من إطار العمل. إذ ستحصل على فائدة أكبر من إطار العمل عندما يكون فريقك كبيرًا، وشيفرات مشروعك كثيرة، وتريد تسريع وتيرة تطوير الميزات الجديدة. لكن لاحظ أنَّك تستطيع تطوير بعض البرمجيات بسهولة ولن تستفيد فيها من التحول إلى إطار عمل. حسنًا، ربما تظن أنَّ هذا قرارٌ صعبٌ، إلا أنه في الواقع عكس ذلك لأغلبية الشركات. - الشركات الصغيرة وسريعة التغيرات تقع في الزاوية العليا اليسرى من المخطط السابق، فإذا كانت شيفرات برنامجك تتغير بسرعة فلن يكون من الخطر التغيير إلى إطار عمل آخر. فلو كنت تضيف الميزات يمنةً ويسرةً لأنك لم تعلم أيها يجني مالًا بعد، فلن تهتم إن «مات» إطار العمل الذي تستخدمه، لأنك ستحذف تلك الميزات والأجزاء من الشيفرات على أيّة حال. إذا كانت إنتاجيتك أكبر عندما تستعمل إطار عمل، وكنت تعمل لدى شركةٍ ناشئةٍ فسأقول أنَّ من الصواب استعمال إطار عمل، مع الأخذ بالحسبان أنَّ أغلبية الأشخاص تقل إنتاجيتهم عندما يستعملون إطار عمل لكنهم لا يعترفون بذلك. - تتواجد الشركات الصغيرة وذات معدل التغيير البطيء في الطرف السفلي اليساري من المخطط، وتكون إما شركات صغيرة تقليدية التي لا تُمثِّل البرمجيات مكوِّنًا أساسيًا من طريقة عملها، أو شركات البرمجيات الصغيرة. إذا كنت مبرمجًا تعمل لدى شركة صغيرة، فلا تستعمل إطار Angular أو React أو غيرهما. وإنما استخدم المكتبات المستقرة والثابتة والتي تعمل على جميع المتصفحات. فلو كنت تعمل في شركةٍ صغيرةٍ، فأرى أنَّ مخاطر استعمال إطار عمل تفوق ميزاته. - الشركات الكبيرة وذات معدل التغيير السريع تتواجد في القسم العلوي الأيمن من المخطط، وهي الشركات التي تكون البرمجيات مكوِّنًا أساسيًا فيها، أي أنهم يضيفون الميزات كثيرًا ويحذفون أجزاءً كبيرةً من الشيفرات ويعيدون كتابتها (حتى لو لم يعترفوا بذلك)، ولديهم فرق برمجية كبيرة، والميزات الجديدة التي يُضيفونها ستدر عليهم مالًا. لذا من المنطقي أن يدرِّبوا فِرَق البرمجة عندهم لاستعمال إطار عمل، لكي يبدأ الجميع من نفس المكان ولتُكتَب الميزات بوتيرة أسرع. إذا كانت شركتك ذات دخلٍ كبير، فستقل مخاطر فقدان دعم إطار العمل مع مرور الزمن، لأنك عندما كنت تختار ما هو إطار العمل الذي ستعمل عليه فستأخذ بالحسبان الدعم الطويل له. أما لو كنت تعمل في شركةٍ كبيرةٍ وتجني أموالًا طائلة وكانت البرمجيات من أساس عملك، فمن المنطقي أن تكتب إطار عمل خاص بك مثل Facebook و Google … - الشركات الكبيرة وذات معدل التغيير البطيء تقع في الركن السفلي اليميني من المخطط. وتنتمي الشركة التي تحدثنا عنها في بداية المقالة إلى هذا النوع، ولن يكون الخيار الصائب واضحًا هنا. فليس مجال عمل الشركة هو البرمجيات، لكن البرمجيات هي جزءٌ أساسيٌ نظام عمل الشركة. لن تتغير الشركة بسرعة، لكن الميزات الجديدة ستساعد الزبائن بشراء المزيد من المنتجات. مثاليًا، يمكنك أن تبني على إطار عمل الذي سيبقى حوالي 50 سنة، وهذا أمرٌ معقولٌ بالنسبة إلى شركةٍ تستعمل برمجيات وشيفرات عمرها 40 سنة. لكن هذا ليس موجودًا، لذا قد تفكر ببناء إطار عمل خاص بك، لأنك ستتأكد أنك لن تقع في فخ أطر العمل الميتة بعد 10 سنوات. لذا يجب أن تنشئ كل شيء من الصفر، وسترى أنَّ نفقات إنشاء ذلك قليلةٌ مقارنةً بالربح خلال 50 سنة … إذًا، هل يجب أن تنتقل الشركة إلى إطار عمل؟ لا يوجد جواب سهل! فعندما كنتُ مطورًا أعمل عندهم وكنت أفكر عن البرمجيات فقط، فكان الجواب الجلي بالنسبة لي هو: بالطبع يجب أن ينتقلوا لاستخدام Angular! لكن عندما بدأتُ شركتي الخاصة، فأصبحتُ أرى البرمجيات من زاويةٍ أخرى، لذا لن يكون الخيار سهلًا. توقف برهةً وفكِّر بعمق قبل الالتزام بإطار عمل، وإذا كنتَ صاحب قرار التحول إلى إطار عمل في حال استعملتك الشركة كمستشار، فأسدِ خدمةٍ إلى الشركة، وانزع عنك نظارات المبرمج، وضع نظارات مدير الأعمال. ترجمة -وبتصرّف- للمقال Should You Ever Use a JS Framework?‎ لصاحبه Sean Fioritto
  13. قائمة التنقل هي جزءٌ مهمٌ من موقع ووردبريس وتساعدك زوار موقعك ليتنقلوا بسهولة في موقعك وتسهِّل إظهار البُنية الهيكلية لموقعك؛ وهنالك مكانٌ مخصصٌ لها في مكانٍ ما في أعلى الصفحات في أغلبية القوالب. لكن هل استعمال قوائم التنقل مقتصرٌ على ترويسة الموقع؟ الحقيقة هي أنَّك تستطيع القيام بالكثير معها. سأريك في هذا الدرس بعض الخيارات التي بمقدورك استعمالها لتوظيف قوائم التنقل بشكلٍ أفضل. وسأريك كيف يمكنك إنشاء عدِّة قوائم تنقل لموقعك. سأريك أيضًا طريقتين لوضع القوائم الإضافية في الواجهة الأمامية لموقعك، إحداها باستخدام ودجت والأخرى عبر كتابة شيفرات. استخدام ودجت هو الطريقة الأسهل والمناسبة إذا لم تشأ كتابة الشيفرات، لكنها تعتمد على وجود منطقة ودجات في القالب الذي تستخدمه، فلو لم يكن يحتوي القالب المُفعَّل على منطقة لإضافة ودجات في المكان الذي تريد وضع القائمة فيه، فسأريك كيفية كتابة شيفرة لإضافة القائمة في قالب. لنبدأ بإنشاء بضع قوائم. إنشاء عدِّة قوائم للتنقل يملك موقعي قائمة تنقل رئيسية أنشأتُها مسبقًا، يمكنك أن ترى من الصورة الآتية وجود عدِّة مستويات فيها، حيث توجد العناصر الفرعية داخل قائمة «Services». لقد فعّلتُ خيار «Primary Menu» (القائمة الأساسية) وهذا يعني أنَّ القالب الذي أستعمله (وهو Twenty Sixteen) سيُعرِض القائمة في المكان الرئيسي لها: لكنني لستُ محدودًا بهذه القائمة فقط، وأستطيع إنشاء واحدة أخرى، وسأرجع إلى صفحة «Menus» (قوائم) في لوحة التحكم وأضغط على رابط «create a new menu» (أنشئ قائمة جديدة): هنا يمكنك إضافة قائمةٍ جديدة، القائمة التي سأُنشِئها ستكون لصفحات الخدمات، لذا سأُطلِق عليها اسم «Services» ثم سأضغط على زر «Create Menu» (إنشاء قائمة) لإنشائها، ثم سأضيف تلك الصفحات إليها: بعد إضافة الصفحات إلى قائمتك، فاضغط على زر «Save Menu» (حفظ القائمة) لحفظها. ملاحظة: لا تُفعِّل خيار «Primary Menu» (القائمة الأساسية)، لأنَّك إذا فعلتَ ذلك، فستوضع هذه القائمة بدلًا من القائمة التي أنشأتها ووضعتها في ترويسة الموقع؛ لكننا لا نريد فعل ذلك، بل نريد إضافة هذه القائمة إلى مكانٍ آخر في الموقع. يمكنك فعل كل ذلك عبر صفحة تخصيص القالب، وذلك بالضغط على الخيار «Menus» (قوائم) على جانب الشاشة (الأيسر لو كانت لوحة التحكم عندك بالإنكليزية، والأيمن لو كانت بالعربية) وسترى كل القوائم التي أنشأتها معروضةً: من هنا يمكنك إضافة قائمة جديدة باستخدام زر «Add a Menu» (أضف قائمة)، أو يمكنك اختيار قائمة موجودة مسبقًا لتعدلها. إضافة القوائم باستخدام ودجت بعد أن ضبطتَ القائمة الجديدة، فحان الوقت لإضافتها إلى موقعك. من المفيد أن تُضيف القائمة إلى تذييل الموقع، وهذا يعني أنَّه عندما يُمرِّر الزوار إلى أسفل الصفحة، فليسوا بحاجةٍ إلى التمرير إلى أعلى الصفحة مرةً أخرى لكي يستطيعوا الانتقال إلى صفحةٍ أخرى في الموقع، وهذا مفيدٌ جدًا خصوصًا في الشاشات الصغيرة للهواتف المحمولة. لا يملك القالب الذي أستخدمه منطقة ودجات في التذييل، لكنه يملك منطقة ودجات باسم «Content Bottom» (أسفل المحتوى) الموجودة تحت المحتوى، وسأستخدمها. يمكنك استخدام أيّة منطقة ودجات موجودة في القالب تشاء، وذلك لملائمة احتياجاتك. لإضافة القائمة إلى ودجت، فيمكنك إما أن تستخدم صفحة التخصيص أو صفحة الودجات في لوحة التحكم من «Appearance > Widgets» (مظهر > ودجات)، لكن دعنا نستخدم صفحة التخصيص هذه المرّة. افتح صفحة التخصيص ثم اضغط على خيار «Widgets» (ودجات) الموجود في القائمة الظاهرة على طرف الشاشة: اختر منطقة الودجات التي تريد إضافة القائمة إليها، واضغط على زر «Add a Widget» (أضف ودجت)، وهنا سيُتاح لك الخيار لإضافة الودجات التي تريد: اختر منها «Custom Menu» (قائمة مخصصة)، ثم اختر القائمة التي تريد إضافتها، وأعطها عنوانًا: بعد أن تنتهي من فعل ذلك، فاضغط على زر «Save and Publish» (حفظ ونشر) لتحفظ تعديلاتك، ثم تحقق من ظهور القائمة في واجهة موقعك: إذا أردتَ إضافة قوائم أخرى –سواءً إلى نفس منطقة الودجات أو إلى منطقةٍ أخرى– فاتبع نفس الخطوات السابقة لإنشاء أي عدد من القوائم تحتاج له، ثم أضفها إلى منطقة الودجات التي تشاء. إضافة قائمة إلى قالبك باستخدام الشيفرات إذا لم يملك قالبك منطقةً ودجات مناسبةً لإضافة القائمة التي أنشأتها إليها، فيمكنك كتابة شيفرة لوضع القائمة أينما تشاء. فبدلًا من استخدام منطقة الودجات المسماة «Below Content» (أسفل المحتوى) فسأكتب شيفرةً لإضافة قائمة الخدمات إلى تذييل القالب يدويًا. لاحظ أنني أستعمل قالب Twenty Sixteen، وسأُنشِئ «قالب ابن» (child theme) جديد لفعل ذلك، لأنَّ أيّة تعديلات سأجريها على القالب الأصلي ستختفي عندما أُحدِّث القالب في المستقبل؛ أما إذا كنت تعمل على قالبٍ أنشأتَه أنت (وهو خاصٌ بموقعك) فيمكنك تعديل ملفات القالب مباشرةً. ملاحظة: يمكنك أن تجد الملفات المصدرية لهذا الدرس على GitHub. سأنُشِئ القالب الجديد في مجلد wp-content/themes وفيه ملفين، أحدهما باسم style.css الذي يُخبر ووردبريس أنَّ هذا القالب هو «قالب ابن»، وملف التذييل footer.php الذي سينوب عن الملف الأصلي الموجود في القالب الرئيسي. وستستعمل ووردبريس الملفات الأخرى من مجلد القالب الأب. إنشاء قالب ابن أولًا، عليك إضافة ما يلي إلى ملف style.css لتخبر ووردبريس أنَّ هذا القالب هو قالب ابن: /* Theme Name: WPMU DEV Navigation Menus for Power Users Theme URI: https://github.com/rachelmccollin/wpmudev-nav-menus Description: Theme to support WPMU DEV post on navigation menus Author: Rachel McCollin Author URI: http://rachelmccollin.co.uk/ Template: twentysixteen Version: 1.0 */ @import url("../twentysixteen/style.css"); احفظ الملف. إضافة القائمة إلى التذييل عليك –لإضافة القائمة إلى التذييل– تعديل الملف footer.php؛ وإذا كنتَ تعمل على قالب ابن، فعليك نسخ ملف footer.php من القالب الأساسي إلى مجلد القالب الابن، ثم عليك تعديل الملف المنسوخ. افتح ملف التذييل وأضف ما يلي بعد وسم بداية العنصر <footer>: <aside class="footer-menu widget"> <h3>Services</h3> <nav class="footer-navigation" role="navigation" aria-label="<?php esc_attr_e( 'Footer Menu', 'wpmudev' ); ?>"> <?php wp_nav_menu( array( 'menu' => 'services', 'menu_class' => 'footer-menu' ) ); ?> </nav> </aside> لاحظ أنني أضفت اسم القائمة (services) كأحد المعاملات التي مررتها إلى الدالة wp_nav_menu()‎، لذا لو سمّيتَ قائمتك باسمٍ مختلف، فاحرص على استعمال ذاك الاسم هنا. أضفتُ أيضًا الفئة widget لتنسيق القائمة، وهذا يعني أنَّ القائمة ستبدو شبيهةً بالودجات في واجهة الموقع: حسنًا، مظهرها ليس جميلًا أبدًا، فهي تقع ملتصقةً بحقوق النشر، لذا لنضف بعض التنسيق عليها. تنسيق القائمة احفظ ملف footer.php وافتح ملف style.css مرةً أخرى، ثم أضف إليه الشيفرة الآتية: .footer-menu { width: 100%; } الشيفرة السابقة تحلّ مشكلة العرض، وستظهر القائمة على يسار الشاشة مع وجود فراغ كبير حولها: لنحل هذه المشكلة بجعل عناصر القائمة تظهر بسطرٍ وحيد، وذلك عبر إضافة الشيفرة الآتية إلى ملف الأنماط: .footer-menu ul { margin-left: 0; } .footer-menu li { float: left; list-style-type: none; margin-left: 0; margin-right: 20px; font-size: 1.5em; } هذا أفضل. يجب أن تبدو القائمة كما هو ظاهر في الصورة الآتية في الشاشات الكبيرة: لكنها لا تظهر بشكلٍ جميلٍ في الشاشات الصغيرة: لنضف سطرًا لكي نفصل بين القائمة والعنصر الذي يليها، وذلك بإضافة الشيفرة الآتية إلى صفحة الأنماط: .site-info { clear: both; } كيف تبدو الآن؟ حسنًا، أصبحت أجمل. أصبح لدينا الآن قائمة الخدمات في تذييل الموقع، ونستطيع إضافة ما نشاء من روابط إليها دون الحاجة إلى تعديل ملفات القوالب مجددًا، إذ كل ما عليك فعله هو تعديل القائمة في لوحة التحكم. أليس هذا بسيطًا؟ الخلاصة يمكن الاستفادة من قوائم التنقل في أمكان كثيرة بخلاف ترويسة الصفحة. يمكنك إنشاء قوائم جديدة وإضافتها إلى مختلف الأماكن مثل التذييل. يمكنك أيضًا إنشاء قائمة للشريط الجانبي وأخرى للتذييل (أو أي مكانٍ آخر في قالبك)، أو يمكنك إضافة عدِّة قوائم في نفس منطقة الودجات؛ فالخيار عائدٌ إليك تمامًا! ترجمة -وبتصرّف- للمقال The Power Users Guide to WordPress Navigation Menus لصاحبته Rachel McCollin.
  14. حسنًا، لقد تعرفتَ على ووردبريس حديثًا وأصبحتَ جاهزًا لنقل موقعك الساكن (static) القديم المبني باستخدام HTML فقط إلى نظام إدارة محتوى حديث ومتطور. لكن انتظر برهةً، كيف ستفعل ذلك دون أن تفقد موقعك القديم أثناء بنائك للموقع الجديد؟ وكيف تضمن أنَّ الزوار لن يكتشفوا موقعك الجديد ببحثهم في Google قبل أن تنتهي من ضبطه كاملًا؟ سأريك في هذا الدرس طريقةً حلّ هذه الإشكالية، وسننظر إلى كيفية العمل على الموقع الجديد خلف الستار، مبقيًا موقعك القديم مكانه كي يبقى متاحًا للزوار أثناء تطوير موقعك الجديد. الأمر أبسط بكثير مما تتخيل، لذا لنبدأ! ما الذي ستحتاج إليه لكي تُكمِل معنا في هذا الدرس، فستحتاج إلى: موقعٍ ساكن (static) موجود مسبقًا في نفس المكان الذي تريد وضع موقعك الجديد فيه. المقدرة على تثبيت ووردبريس على خادومك، إما يدويًا أو تلقائيًا عبر سكربت. وصول عبر FTP أو لوحة تحكم cPanel إلى الخادوم. إذا لم تكن متأكدًا من أحد البنود السابقة، فاسأل مزود الاستضافة، وأخبرهم أنَّك ستحتاج إلى تعديل ونقل بضعة ملفات، ويجب أن يعطوك طريقةً للوصول إلى تلك الملفات، وإن لم يعطوك ذلك فأنصحك بالبحث عن مزود استضافة آخر! أين يجب تطوير الموقع الجديد؟ إذا كنتَ مطوِّر ووردبريس خبير، فلا أظن أنَّه توجد مشكلة في نقل مواقع ووردبريس، إذ أنَّ من المرجح أنَّك ستطوِّر الموقع على حاسوبك المحلي أو على خادوم تخزين صغير خاص بك، ثم ستنقل الموقع النهائي إلى الخادوم الإنتاجي. أما لو كنتَ جديدًا على ووردبريس، فقد تَحسَبُ أنَّ المهمة شاقة، فكيف ستُثبِّت ووردبريس محليًا؟ ناهيك عن الرعب الناجم عن التفكير بطريقة نقل موقعك إلى الخادوم الإنتاجي بعد انتهاء العمل عليه! لا حاجة لكي تقلق من الأمور السابقة، إذ يمكنك تثبيت ووردبريس على خادومك الإنتاجي وتعمل عليها دون معرفة الزوار؛ وفي نفس الوقت يكون موقعك الثابت القديم في مكانه. وبعد أن ينتهي ضبط ونقل المحتوى إلى الموقع الجديد، فليس عليك نقل الملفات أو تصدير قاعدة البيانات، وإنما كل ما عليك فعله هو نسخ وتعديل ملف أو ملفين، وتغيير بعض إعدادات الضبط. يبدو أنَّ ذلك سهلٌ جدًا ويمكنك –حتى لو كنتَ مبتدئًا باستعمال ووردبريس– أن تفعله. رائع! الخطوة الأولى: تثبيت ووردبريس في مجلدٍ فرعي أوّل خطوة هي تثبيت ووردبريس، لكن بدلًا من تثبيتها في المجلد الجذر لموقعك، فعليك تثبيتها في مجلدٍ فرعي، فلو كان موقعك موجودًا على الرابط http://mysite.com فعليك تثبيتها في مجلد باسم http://mysite.com/wordpress. لكن دعني أحذرك بادئ الأمر أنني لا أثبت ووردبريس في مجلدٍ باسم wordpress، إذ عليك إعطاء المجلد اسمًا أكثر غموضًا وأصعب تخمينًا، لكي لا يعثر الزوار (أو المخترقون) عليها صدفةً. باختصار: يمكنك أن تسمِّ المجلد بأيِّ اسمٍ تشاء لطالما كان يختلف عن أسماء الملفات أو المجلدات الموجودة في الموقع القديم، وإلا فقد يصدف وأن يزور أحدهم تلك الصفحة في موقعك القديم مما يأخذهم إلى الموقع الجديد. لذا إذا كنتَ تريد تثبيت ووردبريس، فثبِّتها في مجلدٍ جديد. وإذا كنتَ تستعمل سكربتًا توفره شركة الاستضافة (مثل Softaculous أو Fantastico) فيمكنك تحديد ذلك أثناء التثبيت. أو إذا كنتَ تثبت ووردبريس يدويًا، فعليك وضع الملفات في مجلدٍ فرعي. ملاحظة: إذا كان موقعك الجديد سيُشِّكل شبكةً متعددة المواقع، فما يزال بإمكانك فعل ما سبق ذكره، لكنك لن تستطيع استخدام النطاقات الفرعية (subdomains) كبنيةٍ لشبكتك، فلن تستطيع استخدام سوى المجلدات الفرعية (subdirectories) فقط. الخطوة الثانية: إخفاء الموقع من المؤكد أنَّك لن ترغب أن يعثر الناس على موقعك الجديد أثناء إنشائه في محركات البحث، لذا ستحتاج إلى إخفائه. تُسهِّل ووردبريس من ذلك كثيرًا، فكل ما عليك فعله هو الذهاب إلى صفحة «Settings > Reading» (الإعدادات > قراءة) في لوحة التحكم التابعة للموقع الجديد، وستجد مربع اختيار لإخفاء موقعك عن محركات البحث: اختر حقل «Discourage search engines from indexing this site» (منع محركات البحث من أرشفة هذا الموقع) ثم اضغط على زر «Save Changes» (حفظ التغييرات)، وهذا سيُنشِئ ملف robots.txt في موقعك الذي سيطلب من محركات البحث ألّا تُفهِرس الموقع الجديد. لا تقلق بخصوص هذا الملف، ستعتني به ووردبريس وتتكفل بأمره. الخطوة الثالثة: إعداد موقعك الجديد هذا هو الجزء الذي يأتي فيه المرح! أمضِ بعض الوقت في موقعك الجديد، واختر قالبًا، وثبِّت الإضافات، وأنشِئ بعض المحتوى، وارفع الصور والوسائط الأخرى… الخطوة الرابعة: تحضّر لنقل الموقع الجديد ليصبح موقعك الرئيسي عندما يصبح موقعك الجديد جاهزًا، فحان الوقت لتحضير نقله ليصبح موقعك الرئيسي. ستحتاج إلى نسخ بضعة ملفات وتعديل أحدها، ثم تغيير بعض الإعدادات. لنشرح طريقة فعل ذلك خطوةً بخطوة. نسخ موقعك القديم احتياطيًا ثم حذفه خذ أولًا نسخةً احتياطيةً من ملفات موقعك القديم بنسخها إلى جهازك المحلي، وذلك عبر عميل FTP أو عبر خاصية إدارة الملفات File Manager في لوحة cPanel. بعد تحميل كامل ملفات موقعك القديم، فاحذفها. نسخ وتعديل ملفين باستخدام FTP أو File Manager، انسخ (ولا تنقل) ملفين من المجلد الفرعي الذي ثبتت فيه ووردبريس إلى المجلد الجذر (أي مجلد public_html): index.php ‎htaccess إذا لم تكن قادرًا على رؤية ملف ‎.htaccess فعليك تفعيل إظهار الملفات المخفية في cPanel أو في عميل FTP. يمكنك فعل ذلك في cPanel عبر العودة إلى الصفحة الرئيسية والضغط على File Manager ثم تفعيل الخيار «Show Hidden Files». أما عملاء FTP فكلٌ له طريقته لإظهار الملفات المخفية، لذا انظر في توثيق البرنامج. افتح الآن الملف index.php الموجود في المجلد الجذر لموقعك (والذي أُنشِئ عندما نسخته من المجلد الفرعي) وابحث عن هذا السطر: require('./wp-blog-header.php') وعدِّله ليصبح كما يلي: require('./subdirectory/wp-blog-header.php') ضع اسم المجلد الفرعي الذي ثبتت فيه ووردبريس بدلًا من subdirectory في الشيفرة السابقة. فلو ثبتتُ ووردبريس في http://rachelmccollin.co.uk/new-site/‎ فستبدو الشيفرة كما يلي: require('./new-site/wp-blog-header.php') احفظ الملف الآن واخرج منه. الخطوة الخامسة: تعديل ضبط الموقع اذهب إلى صفحة «Settings > General» (الإعدادات > عام) في لوحة التحكم، وأضبط عنوان موقعك الجديد، واسمح لمحركات البحث بفهرسة موقعك. تعديل رابط URL للموقع اذهب إلى صفحة «Settings > General» (الإعدادات > عام) في لوحة التحكم، وسترى حقلين يمكنك إدخال رابط URL فيهما، واحدٌ لرابط ووردبريس، والآخر لرابط الموقع. يُشير رابط ووردبريس إلى مكان تثبيت ووردبريس، بينما يجب أن يُشير رابط الموقع إلى الرابط الذي سيعثر الناس على موقعك فيه. هذا هو الضبط الخاص بي قبل التعديل: عدِّل حقل «Site Address» (عنوان الموقع) واحذف المسار الذي يُشير إلى المجلد الفرعي، فبدلًا من أن يكون http://rachelmccollin.co.uk/new-site يجب أن يصبح http://rachelmccollin.co.uk. احفظ تعديلاتك بالضغط على زر «Save Changes» (حفظ التغييرات). السماح لمحركات البحث بالوصول إلى موقعك هل تذكر عندما كنتَ تضبط موقعك الجديد أنَّك أخفيته عن محركات البحث؟ يجب ألّا تغفل عن التراجع عن هذه الخطوة. اذهب إلى صفحة «Settings > Reading» (الإعدادات > قراءة) وألغِ تفعيل الخيار «Discourage search engines from indexing this site box» (منع محركات البحث من أرشفة هذا الموقع) ثم اضغط على زر «Save Changes» (حفظ التغييرات). يجب أن تكون محركات البحث قادرةً على فهرسة موقعك الآن. ملاحظة: لتسريع هذه العملية، فأنصحك بتسجيل موقعك في أدوات «Google webmaster tools» وتخبر Google عن موقعك. وسترى أيضًا تواتر فهرسة Google لموقعك ويمكنك أن تختار إخفاء أجزاءٍ منه. مشاكل؟ أحيانًا بعد فعلك لمثل هذه العمليات فقد تحاول زيارة الموقع ولا تجده يعمل، فقد تتم إعادة توجيهك عدِّة مرات، أو ربما تحصل على رسالة خطأ 404. إذا حدث ذلك، فاحذف الملفات المؤقتة في متصفحك؛ فلربما كان يتذكر متصفحك موقعك القديم ويُظهِر رسالة الخطأ. بعدا أن تفعل ذلك فيجب أن يعمل الموقع عملًا سليمًا. ويمكنك أيضًا أن تجرِّب متصفحًا أو جهازًا آخر. الخلاصة ربما افترضتَ أنَّ عليك حذف موقعك الثابت في أثناء إنشائك وإعدادك لموقع ووربريس، لكن ذلك ليس صحيحًا؛ ولو اتبعتَ ما ذكرناه في هذا الدرس فستتمكن من الإبقاء على موقعك القديم أثناء تطويرك للجديد في الخفاء، والذي لن يظهر للزوار أو لمحركات البحث إلا بعد أن يجهز تمامًا. ترجمة -وبتصرّف- للمقال How to Keep Your Static Site Running While Building a New WordPress Site لصاحبته Rachel McCollin.
  15. سنستعرض في هذا الدرس أمثلةً عن مختلف حالات استعمال flexbox في CSS3، سنجعل الشرح مختصرًا قدر الإمكان وستُوضَّح الفكرة عبر قراءة الشيفرة؛ لذا تمعّن فيها كثيرًا، وانظر إلى ناتجها (لا تغفل أهميّة تجربتها عندك). لمزيدٍ من المعلومات حول flexbox، فانظر درس «تعرف على CSS Flexbox وأساسيات استعماله لهيكلة صفحات الويب». إنشاء حاوية flex أول خطوة لإنشاء تخطيط صفحة يعتمد على flexbox هو إنشاء حاوية flex، وذلك بضبط الخاصية display إلى flex؛ يجدر بالذكر أنَّك ما زلت تحتاج إلى استخدام السابقة ‎-webkit في متصفح Safari: .flexcontainer { display: -webkit-flex; display: flex; } ترتيب عناصر flex في صف عناصر flex هي العناصر الأبناء لحاوية flex، والتي يمكن وضعها على المحور الرئيسي والمحور العمودي عليه. افتراضيًا، المحور الرئيسي هو المحور الأفقي، لذا ستوضع العناصر في صف (row)، يمكنك قلب المحور الرئيسي بضبط الخاصية flex-direction إلى column، حيث أنها مضبوطةٌ افتراضيًا إلى row: /* ضبط الحاوية */ .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: row; flex-direction: row; } ترتيب عناصر flex على شكل عمود يمكننا ترتيب عناصر flex على شكل عمود بضبط الخاصية flex-dirextion إلى column. .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: column; flex-direction: column; } تحريك عناصر flex إلى الأعلى كيفية نقل لعناصر flex إلى الأعلى (top) يعتمد على اتجاه المحور الرئيسي. فإن كان رأسيًا (vertical) فيمكنك ضبط justify-content وإذا كان أفقيًا فاضبط align-items. .flexcontainer { -webkit-flex-direction: column; flex-direction: column; -webkit-justify-content: flex-start; justify-content: flex-start; } أما الشيفرة الآتية: .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: row; flex-direction: row; -webkit-align-items: flex-start; align-items: flex-start; } فتُنتِج: تحريك عناصر flex إلى بداية الحاوية تحريك العناصر إلى يمين أو يسار الحاوية يعتمد على أمرين، أولهما هو المحور الرئيسي، فلو كانت الخاصية flex-direction مضبوطةً إلى row فاستعمل حينها الخاصية justify-content، وإن كانت مضبوطةٌ إلى column فاستعمل align-items؛ وثانيهما هو اتجاه الصفحة، فلو استعملتَ القيمة flex-start (كما في أمثلتنا) وكان اتجاه صفحتك من اليسار إلى اليمين فستتحرك العناصر إلى اليسار، أما لو كان اتجاه صفحتك من اليمين إلى اليسار فستتحرك العناصر إلى اليمين. .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: row; flex-direction: row; -webkit-justify-content: flex-start; justify-content: flex-start; } أما الشيفرة: .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: column; flex-direction: column; -webkit-align-items: flex-start; align-items: flex-start; } فتنتج: تحريك عناصر flex إلى نهاية الحاوية وكما ذكرنا في القسم السابق، ستتحرك العناصر إلى اليمين عند استخدام القيمة flex-end في حال كانت الصفحة من اليسار إلى اليمين، وستتحرك إلى اليسار عندما تكون الصفحة من اليمين إلى اليسار. .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: row; flex-direction: row; -webkit-justify-content: flex-end; justify-content: flex-end; } أما الشيفرة: .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: column; flex-direction: column; -webkit-align-items: flex-end; align-items: flex-end; } فتنتج: توسيط العناصر التوسيط الأفقي والرأسي للعناصر الموجودة في حاوية flex هو أمرٌ هيّن، فكل ما علينا فعله هو ضبط القيمة center إلى الخاصية justify-content و align-items. لا اختلاف فيما لو كانت الخاصية flex-direction مضبوطةً إلى row أو column. .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: row /* works with row or column */ flex-direction: row; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; } والعناصر المرتبة عموديًا: تكبير عنصر flex عددًا معيّنًا من المرات يمكنك أن تُحدِّد كيف يكبر أو يصغر حجم عنصر flex نسبةً إلى عناصر flex الأخرى الموجودة في الحاوية. يمكنك فعل ذلك بضبط الخاصية flex لكل عنصر تريد تكبيره أو تصغيره: .bigitem { /* This will be twice as big as the small item. */ -webkit-flex: 2 0 0; flex: 2 0 0; } .smallitem { -webkit-flex: 1 0 0; flex: 1 0 0; } التفاف عناصر flex لتصبح عدِّة أسطر دعم المتصفحات لالتفاف (wrapping) عناصر flex ما يزال محدودًا إلى متصفحات webkit و IE11، فللأسف لا يدعم firefox هذه الميزة بعد. /* On the flex container */ .flexcontainer { display: -webkit-flex; display: flex; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; /* You can set flex-wrap and flex-direction individually */ -webkit-flex-direction: row; flex-direction: row; -webkit-flex-wrap: wrap; flex-wrap: wrap; /* Or do it all in one line with flex flow */ -webkit-flex-flow: row wrap; flex-flow: row wrap; /* tweak the where items line up on the row */ /* valid values are: flex-start, flex-end, space-between, space-around, stretch */ -webkit-align-content: flex-end; align-content: flex-end; } التفاف عناصر flex لتُشكِّل عدة أعمدة لاحظ إسناد القيمة column wrap إلى الخاصية flex-flow في الشيفرة الآتية: .flexcontainer { display: -webkit-flex; display: flex; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; -webkit-flex-flow: column wrap; flex-flow: column wrap; -webkit-align-content: stretch; align-content: stretch; } إزالة المسافة بين عناصر flex التي تشكل أسطرًا وأعمدةً تسمَح لك الخاصية align-content بضبط قيم للتحكم بكيفية توزيع المسافة بين الأسطر أو الأعمدة. الخيارات المتاحة لك هي flex-start و flex-end و space-between و space-around و stretch. فلإزالة المسافة بين الأعمدة الملتفة اضبط الخاصية align-content إلى center: .flexcontainer { display: -webkit-flex; display: flex; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; -webkit-flex-flow: column wrap; flex-flow: column wrap; -webkit-align-content: center; align-content: center; } تثبيت أحد العناصر إلى أحد أطراف حاوية flex يمكنك التحكم بقيمة align-items لمختلف العناصر الموجودة في حاوية flex عبر الخاصية align-self. يمكنك أيضًا ضبط هوامش (margins) بقيم معيّنة لتحريك العناصر إلى الأعلى أو الأسفل أو إلى اليمين أو اليسار؛ فلو كانت عندك حاوية تُرتَّب فيها العناصر على شكل عمود، وأردتَ نقل أحد العناصر إلى يسار الحاوية، فاضبط الخاصية margin-right إلى auto: /* On the flex item to pin */ .left { -webkit-align-self: flex-start; align-self: flex-start; } .right { margin-left: auto; } ترجمة -وبتصرّف- للمقال The Ultimate Flexbox Cheat Sheet لصاحبه Sean Fioritto
  16. هل وجدتَ موقعك الذي يعتمد على ووردبريس معطلًا وكل ما يمكنك قوله هو «لم أفعل شيئًا! صدقًا!» ومع ذلك سترى رسائل الخطأ التي تقول أنَّ ملفاتك ناقصة أو قاعدة بياناتك تالفة… لا تقلق في حال اختفت المنشورات والتصنيفات فجأةً، أو ظهرت رسائل خطأ مثل: “Warning: require_once(path/to/file.php) [function.require-once]: failed to open stream: No such file or directory in…” أو “Cannot establish database connection.” فسأشرح كيف تحدث مختلف أنواع رسائل الخطأ السابقة، وكيفية حل تلك المشاكل؛ وسأبيّن لك كيف أنَّه من غير المرجّح أن تكون أنت سبب المشكلة. حسنًا، ما هي الفكرة الرئيسية هنا؟ يصعب عادةً معرفة المُسبِّب الرئيسي لأيٍّ من الأخطاء السابقة لوجود عدد كبير (نسبيًا) من المسببات. هذه هي الأسباب الرئيسية التي تؤدي إلى تلف في قاعدة البيانات، أو فقدانها: لم توضَع معلومات الدخول بشكلٍ صحيحٍ في ملف wp-confing.php. حدثت مشكلة في نظام التشغيل. تعطلت إحدى القطع العتادية في الخادوم. حدثت علّة في خادوم MySQL أو مفسر PHP أو في القوالب والإضافات التي تستعملها. استهلاكك للموارد أصبح كبيرًا مما أدى إلى إيقاف محاولة الوصول إلى قاعدة البيانات (مثل PHP memory limit). ربما تعرضتَ إلى هجومٍ إلكتروني، وتم اختراق موقعك. هنالك قائمةٌ أطول للأسباب المحتملة لفقدان ملفاتك: أذونات الملفات غير مضبوطة ضبطًا صحيحًا. الجدار الناري (أو برمجيات الحماية الأخرى) لخادومك يمنع الوصول إلى بعض أجزاء موقعك. أدخلتَ رابط URL للموقع بشكلٍ خاطئ في صفحة «Settings > General» (الإعدادات > عام). الإضافات الخارجية حذفت أحد الملفات أو سببت مشكلةً في المسارات. ثبّتت شهادة SSL ونسيت استبدال روابط الصور. الروابط الدائمة غير مضبوطة ضبطًا صحيحًا. هنالك أخطاء في ملف ‎.htaccess. رُفعِتَ الصور في مجلدٍ خاطئ. تمت مقاطعة عملية رفع المحتوى. هنالك علّة في إضافة أو سكربت أو قالب تستعمله في موقعك. ربما حدثت مشكلة في الخادوم. ربما تعرضتَ إلى هجومٍ إلكتروني، وتم اختراق موقعك. بغض النظر عن المسبب الرئيسي، فهنالك بضع طرائق لإصلاح مشاكل الملفات وقاعدة البيانات. النسخ الاحتياطي والاستعادة منه أسهل طريقة لحل مشكلة تتعلق بقواعد البيانات أو فقدان الملفات هي استعادة نسخة احتياطية من موقعك؛ أما لو لم يكن لديك نسخةٌ احتياطيةٌ لتستعيدها، فهنالك حلولٌ أخرى يمكنك تجربتها. لكن قبل أن تجرِّب الحلول الأخرى، فأنصحك بأخذ نسخة احتياطية لكامل موقعك، حتى لو لم يكن يعمل، لأنَّه قد تسوء بعض الأمور ومن الأفضل أن تكون لديك نسخةٌ أساسيةٌ (حتى لو كانت معطلةً) عوضًا أن تكون خالي الوفاض. ألق نظرة على هذا المقال للمزيد من التفاصيل عن النسخ الاحتياطي: الحماية وإضافات النسخ الاحتياطي في ووردبريس وبعد أن تأخذ نسخةً احتياطيةً من موقعك لتستعملها في حال وقوع كارثة، فأنت جاهزٌ لتبدأ بإصلاح الموقع. إصلاح مشكلة في قاعدة البيانات هنالك عدِّة طرائق يمكنك فيها إصلاح قاعدة البيانات. يمكنك أن تجرب الطريقة الآتية التي تحل أغلبية المشاكل التي تواجه قواعد البيانات. يمكنك إضافة السطر الآتي إلى ملف wp-config.php والموجود في المجلد الجذر لموقعك: define( 'WP_ALLOW_REPAIR', true ); يمكنك إضافة السطر السابق في أي مكان في الملف لكن لا تضفه داخل دالة (على سبيل المثال). أنصحك بوضعه قبل السطر الآتي مباشرةً: /* That's all, stop editing! Happy blogging. */ لتفاصيلٍ إضافيةٍ حول طريقة تعديل ملف wp-config.php فراجع درس «كيف تستخدم FTP لنقل ملفات مدونة ووردبريس الخاص بك؟». بعد أن أضفتَ السطر السابق، فيمكنك زيادة صفحة إصلاح قاعدة البيانات من الرابط الآتي: http://your-site.com/wp-admin/maint/repair.php أو عليك زيارة https://your-site.com/wp-admin/maint/repair.php إذا كانت عندك شهادة SSL. لا تنسَ وضع اسم نطاق موقعك بدلًا من your-site.com. ابقِ في ذهنك أنَّه ليس من الضروري أن تسجِّل دخولك لرؤية هذه الصفحة، لذا بعد انتهاءك من إصلاح قاعدة البيانات، فاحرص على حذف السطر السابق، وإلا فسيستطيع أيُّ شخصٍ الوصولَ إلى تلك الصفحة. عندما تزور الصفحة السابقة، تستطيع أن تضغط على زر «Repair Database» لإصلاح قاعدة البيانات، أو يمكنك إصلاح قاعدة البيانات وتحسينها عبر الضغط على زر «Repair and Optimize Database». بعد إنهاء العملية السابقة، يجب أن يكون موقعك قد أُصلِحَ وأصبحَ جاهزًا للاستعمال. لكن إذا لم تفلح محاولتنا السابقة، فيمكننا إصلاح قاعدة البيانات عبر phpMyAdmin. بعد تسجيل الدخول إلى phpMyAdmin، فاضغط على اسم قاعدة البيانات الموجود في القائمة على يسار الشاشة، ثم بعد ظهور جداول قاعدة البيانات في الصفحة، فمرِّر إلى الأسفل ثم اضغط على مربع الاختيار المُعَنوَن Check All، ثم اختر «Repair table» من القائمة المنسدلة الموجودة بجوار مربع الاختيار. سيتم إصلاح جداول قاعدة البيانات تلقائيًا ويجب أن يعمل موقعك مجددًا. إصلاح الملفات التالفة أو المفقودة لكن ماذا لو كانت ملفاتك هي الناقصة أو التالفة؟ يمكنك إصلاحها أيضًا. إذا ثبّتتَ شهادة SSL في الآونة الأخيرة في موقعك، فقد يبدو أنَّ الصور أمست مفقودةً، ولحلّ هذه المشكلة عليك استبدال روابط الصور لكي تُضمِّن فيها السابقة https. لتفاصيل عن كيفية فعل ذلك، فراجع الدرس «استبدال روابط الصور على مواقع ووردبريس بعد تثبيت شهادة SSL». أما للغالبية العظمى من المشاكل الأخرى، فوضع نسخة جديدة من الملف المعطوب يجب أن يحل المشكلة. يمكنك استخدام FTP للوصول إلى موقعك واستبدال الملفات المعطوبة ووضع أخرى جديدة بدلًا منها، التي يمكنك أن تعثر عليها بتنزيل نسخة من برمجية ووردبريس من موقع WordPress.org. بعد فك ضغط ملف ZIP، يمكنك أن تنقل الملفات الجديدة إلى خادومك، لكن احرص على ترك المجلد ‎/wp-content/‎ دون تعديل، أو قد تعرِّض نفسك لخسارة جميع ملفات الوسائط والصور التي رفعتها إلى موقعك. يمكنك تطبيق ما سبق على الإضافات والقوالب، لكن الفرق الوحيد هو أنَّ عليك تنزيل نسخة حديثة من الإضافات أو القوالب بدلًا من ووردبريس نفسها. إذا فشل كل ما سبق… إذا جرّبتَ كل ما سبق، لكن لم يحالفك الحظ في تشغيل موقعك، فيمكنك أن تجرّب تفعيل نمط التنقيح في ووردبريس، لتفاصيل عن كيفية فعل ذلك، فارجع إلى درس «Debugging WordPress: How to Use WP_DEBUG». ربما تنظر أيضًا في سجل الأخطاء فلربما استطعت التعرف على المشكلة من هناك، ومن ثم ترسلها إلى شركة الاستضافة لتحليل الخطأ. ابحث عن ملفٍ باسم debug.log داخل مجلد ‎/wp-content/‎. إذا لم يحّل أيٌّ مما سبق مشكلتك، فحان الوقت لكي تراسل شركة الاستضافة، فقد تكون المشكلة من ضبط الخادوم، أو ربما أصابته مشكلة. والحل الوحيد لهذه المشكلة هو التواصل مع شركة الاستضافة وإعلامها بالأمر. حسنًا، هنالك حلٌّ أخير لا أحبذه: إذا لم تكن لديك نسخةٌ احتياطيةٌ وليست لديك مشكلة مع فقدان بيانات موقعك بالكامل، فيمكنك حذف الموقع وإعادة تثبيت ووردبريس. الخلاصة إذا طبّقتَ ما ذكرناه في هذا الدرس وحالفك الحظ فيجب أن يعمل موقعك عملًا سليمًا دون أن ترى أيّة رسائل خطأ تُشير إلى ملف ناقص أو قاعدة بيانات معطوبة. ولتجنب الصداع الناجم عن محاولة إصلاح الموقع (إذا صَدَفَ وتعطّل) فأنصحك بأخذ نسخ احتياطية دوريًّا. ترجمة -وبتصرّف- للمقال Repairing Corrupted, Broken or Missing Files and Databases in WordPress لصاحبته Jenni McKinnon.
  17. تمهيد عندما تُنشِئ حاوية Docker فسيُسنَد إليها مُعرِّف عالمي فريد (UUID) وهو ضروريٌ لتفادي التضاربات في الأسماء ولتسهيل أتمتة إنشاء الحاويات دون تدخل البشر. وهو مفيدٌ لكي يتعرف الحاسوب المضيف (والشبكة) على المضيف. لكن يصعب التفريق بين الحاويات بالنسبة للبشر، سواءً كنّا نقصد الاسم الطويل (64 محرف) أو المُعرِّف القصير (12 محرف) الذي يبدو كالآتي 285c9f0f9d3d. توفِّر Docker أسماءً مولّدةً تلقائيًا للحاويات تأتي من جمع كلمتين عشوائيتين بشرطة سفلية، مثلًا: evil_ptolemy، مما يُسهِّل التفريق بين الحاويات، لكن الأسماء العشوائية لا تعطينا أيّة فكرة عن وظيفة الحاوية مَثَلُهَا كمثل مُعرِّف UUID. سأخبرك بثلاث نصائح التي ستُسهِّل من التعرف على الحاويات. أولًا: سمِّ الحاوية عندما تُنشِئها وذلك بإضافة الخيار ‎--name=meaningful_name إلى الأمر docker run، وبهذا سيصبح اسم الحاوية ذا معنى عند استخدام الجلسات التفاعلية وفي ناتج بعض الأوامر مثل docker ps. لكن هنالك بعض المحدوديات، لأن أسماء الحاويات يجب أن تكون فريدةً، لهذا لا يمكن استخدام نفس الاسم لأكثر من حاوية. ضع ما يلي في سطر الأوامر أو في ملف Docker: docker run –name=meaningful_name على سبيل المثال، إذا أردنا إنشاء حاوية مبنيةً على صورة nginx فسنُنفِّذ الأمر: docker run --name nginx -d nginx وسيظهر الاسم في قائمة الحاويات التي تعمل حاليًا: docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 08f333ef7216 nginx "nginx -g 'daemon off" 15 seconds ago Up 14 seconds 80/tcp, 443/tcp nginx صحيحٌ أنَّ الاسم سيظهر في ناتج الأمر docker ps ويمكن استخدامه لإدارة الحاوية، لكنه لن يظهر في مِحَث (prompt) سطر الأوامر إذا فتحتَه، أو في السجلات. ولفعل ذلك عليك أن تُسنِد «اسم مضيف» (hostname) إلى الحاوية. ثانيًا: إسناد اسم مضيف إلى الحاوية القيمة المُمرَّرة إلى الخيار ‎--hostname ستُضبِط اسم المضيف داخل الحاوية في الملفين ‎/etc/hostname و ‎/etc/hosts. وبناءً على ذلك، سيظهر اسم المضيف في مِحَث سطر الأوامر، وسيلعب اسم المضيف دورًا في ضبط خدمة DNS للحاوية وقد يكون مفيدًا عند تعلم التعامل مع أكثر من حاوية. لكنه ليس ضروريًا للوصول إلى الحاوية من خارجها، إلا أنَّه سيظهر في ملفات السجل التابعة للحاوية، وعندما تُكتَب تلك الملفات إلى جهاز تخزين مستقل عن الحاسوب المضيف، فسيسهل التعرف على الحاوية بناءً على اسم المضيف الخاص بها. ضع ما يلي في سطر الأوامر أو في ملف Docker: docker run –hostname=value أو docker run -h value وصحيحٌ أنَّ ‎--name و ‎--hostname مفيدان للتعرف على الحاويات، لكن الأمر ليس متعلقًا بإطلاق اسم على الحاوية فحسب، وإنما ستستفيد منه (في السجلات مثلًا) إذا ضبطتَ الحاوية لكي تُحذَف تلقائيًا بعد انتهاءك منها. ثالثًا: حذف الحاويات تلقائيًا بعد انتهاء تنفيذها عندما تجرّب مع حاويات Docker، فقد تستفيد في بعض الأحيان من الحفاظ على الحاوية بعد انتهاء تنفيذها. يمكنك حفظ البيانات مثل ملفات السجلات وتتفحص آخر حالة للحاوية. لكن في بعض الأحيان لا ترغب في بقاء الحاوية بعد انتهاء التجربة معها، وفي هذه الحالة يمكنك استخدام الخيار ‎--rm لحذفها عند انتهاء تنفيذها. وهذا سيُسهِّل من «تنظيف» بيئة التجارب. لكن انتبه! إذا كنتَ تستخدم حجوم Docker ‏(Docker volumes) فالخيار ‎--rm سيؤدي إلى حذف أيّة حجوم مرتبطة بالحاوية. ضع ما يلي في سطر الأوامر أو في ملف Docker: docker run –rm ستستفيد من هذا الخيار كثيرًا عند إنشائك لصورة Docker وتحتاج إلى الوصول إلى حاوية للتجربة عليها، لكنك لا تريد أن تملأ كامل قرصك بحاويات لن تستعملها مرةً أخرى. الخلاصة هنالك ثلاثة خيارات للأمر docker run: الخيار ‎--name و ‎--hostname و ‎--rm التي تُسهِّل من تعاملك مع حاويات Docker عندما تحاول تعلم التعامل معها. يمكنك العثور على مزيدٍ من المعلومات حول الأمر docker run في درس «التعامل مع حاويات Docker». ترجمة -وبتصرّف- للمقال Naming Docker Containers: 3 Tips for Beginners لصاحبته Melissa Anderson
  18. قبل البدء بتوضيح كيفية استخدام Codeception مع شيفرات PHP، فيجب أن نغطي الأساسيات عبر شرحنا لماذا نحتاج إلى اختبار تطبيقاتنا. ربما يمكننا إكمال المشروع الذي نعمل عليه دون تضييع الوقت بكتابة الاختبارات، أليس ذلك؟ بلى، إذ لن تحتاج إلى كتابة اختبارات لكل شيء (كإنشاء صفحة رئيسية لموقعك). لن تحتاج غالبًا إلى كتابة اختبارات عندما يحتوي مشروعك على صفحات ثابتة مرتبطة بصفحة توجيه وحيدة. لكنك ستحتاج إلى إجراء اختبارات عندما: يستخدم فريقك أسلوب «التطوير الموجّه بالاختبارات» (TDD) أو «التطوير الموجّه بالسلوك» (BDD). تُجرى عدِّة عمليات إيداع في مستودع Git التابع لمشروعك يوميًا. أنت مطوِّرٌ محترف، وتعمل على مشروع جاد. يمكنك أن تعذر نفسك بقولك أنَّ هنالك فريق كامل مخصص للاختبارات، وهم مجموعةٌ من الأشخاص الذين يجرون اختبارات ويكتبونها عند الحاجة. لكن لك أن تتخيل كم سيستغرق حلّ العلّة من الوقت بعد أن تضيف وظائف جديدة إلى مشروعك. ما المشاكل التي تحلها الاختبارات؟ لنتعرّف أولًا على المشاكل التي يمكن حلها عبر الاختبارات. لن تتمكن من التخلص من جميع الأخطاء الظاهرة عند الاختبار، لكنك ستستطيع توصيف السلوك المتوقع في حالات الاختبار (test cases). انتبه إلى أنَّه قد توجد أخطاء ضمن حالات الاختبار التي تكتبها. لكن كن على يقين أنَّ شيفرتك ستتغير فيما بعد (إما بتصحيح العلل، أو إضافة ميزات جديدة، حتى تصبح شيفرتك خاليةً من الأخطاء الموصوفة في حالة الاختبار). يمكن -إضافةً إلى ما سبق- استعمال حالات الاختبار المكتوبة بشكلٍ جيد في التوثيق لأنك سترى من خلالها ما هو السلوك المتوقع للبرنامج في حالات معيّنة. خلاصة القول أنَّ كتابة الاختبارات هي استثمارٌ مهمٌ تستفيد منه في المستقبل. إذًا، ما هي الاختبارات التي يمكننا استعمالها في مشروعنا؟ اختبارات الوحدات (unit tests): هي اختباراتٌ منخفضة المستوى (low-level) التي تتحقق من سلامة أجزاء صغيرة من شيفرتك، والتي تكون عادةً الدوال الموجودة في الأصناف (classes) الموجودة في مشروعك والمعزولة عن بعضها. اختبارات التكامل (integration tests): تتحقق اختبارات التكامل من سلامة أداء جزءٍ من برنامجك والذي قد يحتوي على عدِّة أصناف أو دوال، إلا أنها تؤدي جميعًا غرضًا وحيدًا ألا وهو توفير ميزة معيّنة. يجب أن تتحقق هذه الاختبارات من كيفية تفاعل الأصناف مع بعضها. الاختبارات الوظيفية (function tests): اختبار استجابة التطبيق لمختلف الطلبيات، كالتغيرات الحاصلة في قاعدة البيانات ...إلخ. اختبارات القبول (acceptance tests): الغرض من هذه الاختبارات في أغلبية الحالات هو معرفة إن كان التطبيق يلبّي جميع متطلبات العميل. للتوضيح، لنحاول شرح هذه العملية بتمثيلها على شيءٍ ملموس مثل المباني. تتألف بعض أنواع المباني من لبنات صغيرة تُشكِّل جدرانًا، ويجب أن تتحقق اشتراطاتٌ معيّنةٌ فيها، إذ يجب أن تستطيع أن تقاوم الحِمل المُطبَّق عليها، ويجب أن تكون لها أبعاد وحجم معيّن وهلم جرًا… تلك الأمور تسمى «اختبارات الوحدات» (unit tests). أما الفكرة من اختبارات الاندماج هي معرفة متانة وقوة الارتباط بين اللّبنات، وكيف تُشكِّل مع بعضها عنصرًا من عناصر المبنى (مثل الجدران). أما الاختبارات الوظيفية فيمكن ربطها بالاختبارات التي تُجرى على جدارٍ من جدران المبنى، مثل اختبار جودة العزل الحراري، ومعرفة إن كان بالإمكان مرور أشعة الشمس من النوافذ الموجودة في هذا الجدار. وأخيرًا، تتضمن اختبارات القبول إجراء فحص لكامل المبنى: كفتح الباب، والدخول إلى الغرفة، وإغلاق الباب، وتشغيل الضوء، والصعود إلى الطابق الثاني والنظر إلى الحديقة عبر النافذة… تعرّف على Codeception التقسيم السابق مشروطٌ بعدِّة عوامل، ويصعب مقاومة النزعة إلى خلط عدِّة أنواع من الاختبارات. يستعمل الكثير من المبرمجين اختبارات الوحدات ويدّعون أنها كافية؛ وكنتُ منهم، إلا أنني وجدتُ أنَّ استخدام عدِّة أنظمة لمختلف أنواع الاختبارات هو أمرٌ مرهقٌ ويستهلك وقتًا طويلًا. لذا قررتُ منذ فترة أن أعثر على شيءٍ أكثر فائدةً من مكتبة PHPUnit؛ أردتُ أن يتطور استخدامي لاختبارات الشيفرات، إلا أنني لم أرغب بقراءة وتعلم الكثير من صفحات التوثيق وأن أبحث عن الحلول الالتفافية للمشاكل التي تواجهني. ومن ثم اكتشفت وجود Codeception. كنتُ بادئ الأمر متشككًا ومرتابًا منه، وهذه طبيعتنا عندما نرى شيئًا جديدًا (الحق يقال: هذا المشروع موجودٌ منذ خمس سنوات، لذا لا يمكننا اعتباره «جديدًا»)، لكن بعد تجربته لعدة أيام، خَلِصتُ إلى أنَّ Codeception هو نظامٌ مفيدٌ جدًا. قد تتساءل كيف تستطيع تثبيت Codeception؟ الأمر أبسط مما تتخيل: $ composer require "codeception/codeception" $ php vendor/bin/codecept bootstrap بعد التثبيت ستجد مجلدًا جديدًا باسم test في مشروعك، وستجد داخله مجلداتٍ فرعيةً باسم acceptance و functional و unit، يبدو أننا نستطيع البدء بكتابة اختباراتنا، إذًا ما هي الخطوة القادمة؟ لنحاول الآن إضافة اختبار قبول لمثالٍ بسيط: $ php vendor/bin/codecept generate:cept acceptance HelloWorld يمكننا الآن فتح ملف اختبار القبول ذي المسار tests/acceptance/HelloWorldCept.php ونضع فيه المحتوى الآتي: <?php $I = new AcceptanceTester($scenario); $I->wantTo('perform actions and see result'); المتغير الافتراضي المسمى ‎$I ليس مجرد حرف، وإنما هو ضمير متحدث يُشير إلى «الشخص» الذي يجري الاختبار: الذي يفتح صفحة تطبيقك أو أحد أصنافه ويفعل شيئًا ما ويريك النتيجة النهائية لأفعاله. إذ سترى ماذا فعل وما هو الخطأ الذي حدث. هذا هو سبب تسمية ذاك الكائن بالاسم ‎$I ولماذا تدعى الدوال الموجودة فيه wantTo()‎ و see()‎ و amOnPage()‎. لنفكر كشخصٍ يريد إجراء اختبارات للتحقق من عمل إحدى الصفحات. أوّل ما يخطر ببالنا هو فتح الصفحة والبحث عن عبارة معينة فيها، مما يعني أنَّ الصفحة متوافرة للزوار. يسهل كثيرًا فعل ذلك: <?php $I->amOnPage('/'); $I->see('Welcome'); ثم نستعمل الأمر الآتي لتشغيل اختبارات Codeception: $ php vendor/bin/codecept run سنلاحظ مباشرةً أنَّ شيئًا ما ليس صحيحًا، وسنرى من أوّل وهلة أنَّ رسالة الخطأ طويلة وغير واضحة، لكن عندما ننظر إليها عن قرب، فسنجد أنَّ معناها أصبح جليًا. لدينا اختبارٌ وحيدٌ وقد وَجَدَ خطأً: Acceptance Tests (1) Perform actions and see result (HelloWorldCept) Error ---------- 1) Failed to perform actions and see result in HelloWorldCept (tests/acceptance/HelloWorldCept .php) [GuzzleHttp\Exception\ConnectException] cURL error 6: Could not resolve host: localhost (see http://curl.haxx.se/libcurl/c/libcurl-errors.html) خلاصة الرسالة السابقة هي أنَّ الموقع في localhost غير متوافر. انظر إلى أوّل سطر من ملف الاختبار: 1. $I->amOnPage("/") لنفتح ملف tests/acceptance.suite.yml ونعدّل قيمة url: http://localhost/‎ إلى رابطٍ آخر تتوافر عليه الصفحة. يتوافر على جهازي المحلي خادومٌ يحتوي على الصفحة التي نريد اختبارها url: https://local.codeception-article.com/‎. شغِّل الاختبار مرةً أخرى، ويجب أن تظهر عندك النتيجة الآتية: Acceptance Tests (1) --------------------------------------------------------------------------------------- Perform actions and result (HelloWorldCept) Ok تهانينا! لقد نجح اختبارنا. تتوافر العديد من الدوال للاختبار بجانب الدالة amOnPage()‎، لكنني عرضتُها لسهولة استعمالها. يمكن تقسيم دوال الاختبار الموجودة في Codeception إلى المجموعات الآتية: دوال التفاعل مع الصفحة: fillField()‎ و selectOption()‎ و submitForm()‎ و click()‎. دوال التحقق من وجود أشياء معيّنة في الصفحة: see()‎ و dontSee()‎ و seeElement()‎ و seeInCurrentUrl()‎ و seeCheckboxIsChecked()‎ و seeInField()‎ و seeLink()‎. يمكنك إضافة لاحقة (suffix) إلى الدوال السابقة عندما تحتاج استعمالها دون أن توقف الاختبار إن لم يُعثَر على العنصر الذي تختبر وجوده. دوال التعامل مع الكعكات (cookies): setCookie()‎ و grabCookie()‎ و seeCookie()‎. التعليقات وشرح حالة الاختبار: amGoingTo()‎ و wantTo()‎ و expect()‎، استخدم هذه الدوال لكي تشرح ما الذي يفعله الاختبار، مما يذكرك بالهدف منه. فلو أردنا مثلًا أن نختبر صفحةً لاستعادة كلمة المرور عبر البريد الإلكتروني، فسنكتب ملفًا شبيهًا بالملف الآتي: <?php $I = new AcceptanceTester($scenario); $I->wantTo('Test forgotten password functionality'); $I->amOnPage('/forgotten') $I->see('Enter email'); $I->fillField('email', 'incorrect@email.com'); $I->click('Continue'); $I->expect('Reset password link not sent for incorrect email'); $I->see('Email is incorrect, try again'); $I->amGoingTo('Fill correct email and get link'); $I->see('Enter email'); $I->fillField('email', 'correct@email.com'); $I->click('Continue'); $I->expect('Reset password link sent for correct email'); $I->see('Please check your email for next instructions'); يبدو أنَّ الملف السابق يكفي لاختبار الصفحة، لكن ماذا لو كانت هنالك بعض الأقسام في الصفحة التي تُحمَّل عبر Ajax؟ كيف نستطيع أن نختبر تلك الصفحة؟ أجيبك أنَّ Codeception يستعمل مكتبة PhpBrowser المبنية على Symfony BrowserKit و Guzzle. أي لا تقلق، فالأمر سهلٌ وبسيط، ولن تحتاج إلا إلى استعمال curl لحل هذه المشكلة. يمكنك أيضًا استخدام Selenium واختبار الصفحات عبر المتصفحات الحقيقية. وصحيحٌ أنَّ ذلك أبطأ، إلا أنك ستتمكن من اختبار سكربتات JavaScript أيضًا. لكن أولًا عليك تثبيت إضافة Selenium، وتعديل ملف acceptance.suite.yml وإعادة بناء الصنف AcceptanceTester، ويمكنك بعد ذلك استخدام الدوال wait()‎ و waitForElement()‎؛ وستتمكن أيضًا من الحفاظ على وقتك ومواردك باستخدام الدالتين saveSessionSnapshot()‎ و loadSessionSnapshot()‎ اللتين تسمحان لك بحفظ حالة الجلسة الراهنة، وإجراء اختبارات جديدة على جلسات محفوظة مسبقًا، وقد تستفيد من هذا في بعض الحالات مثل اختبار عملية الاستيثاق من المستخدم. الخلاصة هي أننا نستطيع اختبار العديد من الوظائف بسهولة وسرعة وكفاءة. الاختبار الوظيفي لننتقل الآن إلى إجراء اختبارات وظيفية على تطبيقنا: $ php vendor/bin/codecept generate:cept functional HelloWorld ومحتوى الملف: <?php $I = new FunctionalTester($scenario); $I->amOnPage('/'); $I->see('Welcome'); انتظر قليلًا، ماذا يحدث! لا يوجد خطأ في الشيفرة السابقة، حيث تُكتَب الاختبارات الوظيفية بنفس طريقة كتابة اختبارات التكامل، الفرق بينهما هو أنَّ الاختبارات الوظيفية تتفاعل مباشرةً مع تطبيقك، مما يعني أنَّك لن تحتاج إلى خادوم ويب لتشغيل اختبار وظيفي، وستستطيع اختبار أجزاء مختلفة من تطبيقك. وصحيحٌ أنَّه لا يتوافر دعمٌ لجميع إطارات العمل، لكن قائمة الإطارات المدعومة كبيرة (Symfony، و Silex، و Phalcon، و Yii، و Zend Framework، و Lumen، و Laravel) ويجب أن تكون كافيةً لأغلبية الحالات ولمعظم المطورين. رجاءً راجع توثيق Codeception للحصول على قائمة بجميع الدوال المتوافرة وكيفية تفعيلها في ملف functional.suite.yml. قبل أن ننتقل إلى اختبارات الوحدات، فدعني أنحرف قليلًا عن الموضوع وأحدثك قليلًا عن شيءٍ ربما لاحظتَه عند إنشاء الاختبارات: $ php vendor/bin/codecept generate: cept acceptance HelloWorld لاحظ الكلمة cept، واعلم أنَّ ما سبق ليس الطريقة الوحيدة لإنشاء الاختبارات، فهنالك أيضًا «اختبارات cest»، والفرق بينهما هو أنَّك تستطيع هيكلة عدِّة حالات وسيناروهات في نفس الصنف: $ php vendor/bin/codecept generate:cest acceptance HelloWorld <?php class HelloWorldCest { public function _before(AcceptanceTester $I) { $I->amOnPage('/forgotten') } public function _after(AcceptanceTester $I) { } // tests public function testEmailField(AcceptanceTester $I) { $I->see('Enter email'); } public function testIncorrectEmail(AcceptanceTester $I) { $I->fillField('email', 'incorrect@email.com'); $I->click('Continue'); $I->see('Email is incorrect, try again'); } public function testCorrectEmail(AcceptanceTester $I) { $I->fillField('email', 'correct@email.com'); $I->click('Continue'); $I->see('Please check your email for next instructions'); } } ستُشغَّل الدالتان ‎_before()‎ و ‎_after()‎ قبل وبعد كل اختبار. وستُمرَّر نسخةٌ من الصنف AcceptanceTester إلى كل اختبار، لذا يمكنك استخدامه بنفس الآلية المتبعة في اختبارات cest. قد يكون هذا النوع من الاختبارات مفيدًا في بعض الأحيان، لذا من المفيد تعلمه ومعرفة وجوده. اختبارات الوحدات أخيرًا: اختبارات الوحدات. بُني Codeception على PHPUnit، أي أنَّك تستطيع استخدام الاختبارات المكتوبة لمكتبة PHPUnit، وذلك باستخدام الأمر الآتي: $ php vendor/bin/codecept generate:phpunit unit HelloWorld أو يمكنك ببساطة أن «ترث» (inherit) الاختبارات في ‎\PHPUnit_Framework_TestCase. لكن إن أردتَ شيئًا إضافيًا، فيمكنك تجربة اختبارات الوحدات التي يوفرها Codeception: $ php vendor/bin/codecept generate:test unit HelloWorld <?php class HelloWorldTest extends \Codeception\TestCase\Test { /** * @var \UnitTester */ protected $tester; protected function _before() { } protected function _after() { } // tests public function testUserSave() { $user = User::find(1); $user->setEmail('correct@email.com'); $user->save(); $user = User::find(1); $this->assertEquals('correct@email.com', $user->getEmail()); } } لا شيء يختلف عمّا عهدتَه من قبل، فالدالتان ‎_before()‎ و ‎_after()‎ هما بديلتا setUp()‎ و tearDown()‎، وستعملان قبل وبعد كل اختبار. الميزة الأساسية لهذا الاختبار هي القدرة على توسعة عملية الاختبار بتضمين وحدات (modules) التي يمكن تفعيلها في ملف unit.suite.yml: الوصول إلى memcache وقواعد البيانات لتتبع التغيرات (قواعد MySQL و SQLite و PostgreSQL و MongoDB مدعومة). اختبار تطبيقات REST/SOAP. Queues كل وحدة (module) لها ميزاتها، لذا من الأفضل أن تنظر إلى التوثيق وتجمع المعلومات الضرورية عنها قبل الانتقال إلى كتابة اختبارات تستعملها. يمكنك أيضًا استعمال حزمة Codeception/Specify (والتي يجب إضافتها إلى ملف composer.json) وتكتب وصفًا كالآتي: <?php class HelloWorldTest extends \Codeception\TestCase\Test { use \Codeception\Specify; private $user; protected function _before() { $this->user = User::find(1); } public function testUserEmailSave() { $this->specify("email can be stored", function() { $this->user->setEmail('correct@email.com'); $this->user->save(); $user = User::find(1); $this->assertEquals('correct@email.com', $user->getEmail()); }); } } شيفرة PHP الموجودة ضمن الدوال المغلقة (closure functions) السابقة معزولةٌ، لذا لن تؤثر التعديلات داخلها على بقية شيفراتك. ستساعد عبارات الشرح بجعل الغاية من الاختبار واضحةً وتسهيل التعرف على الاختبارات التي فشلت. إضافةً إلى ما سبق، يمكنك استعمال الحزمة Codeception\Verify لكتابة اختبارات بشيفرة شبيهة بأسلوب BDD: <?php public function testUserEmailSave() { verify($map->getEmail())->equals('correct@email.com'); } ويمكنك بالطبع استعمال stub: <?php public function testUserEmailSave() { $user = Stub::make('User', ['getEmail' => 'correct@email.com']); $this->assertEquals('correct@email.com', $user->getEmail()); } الخلاصة: يساعد Codeception على توفير الوقت والجهد ما الذي تتوقع الحصول عليه من Codeception؟ من هم الأشخاص المناسبون لاستعماله؟ برأيي الشخصي، Codeception مناسب لجميع أنواع الفرق: الكبيرة والصغيرة، والمبتدئين والمحترفين، والذين يستعملون أطر عمل والذين يكتبون برمجياتهم من الصفر، أي أنَّ Codeception صالحٌ لكل زمان ومكان (تقريبًا). Codeception هو إطار عمل ناضج وموثّق جيدًا ويمكن توسعته باستعمال إضافات. وهو حديثٌ، ومبنيٌ على المكتبة الشهيرة PHPUnit، مما يطمئن المطورين الذين لا يريدون تجربة الكثير من المكتبات قبل الاستقرار. أداءُ هذه المكتبة جيدٌ، وهذا يعني أنها سريعة ولا تحتاج وقتًا ولا جهدًا كثيرًا، ومن السهل تعلمها، والتوثيق الرائع يجعل عملية التعلم تمر دون مشاكل. من السهل جدًا تثبيت وضبط Codeception، مع أنه يحتوي على الكثير من الميزات المتقدمة. وصحيحٌ أنَّ أغلبية المستخدمين لا يحتاجون جميع (أو حتى أغلب) الميزات الموجودة فيه، إلا أنَّ ذلك يعتمد على ما تشاء فعله معه. يمكنك البدء بالأساسيات، ثم ستبدأ بالتعامل مع الميزات الإضافية عاجلًا أو آجلًا. ترجمة -وبتصرّف- للمقال Jumpstart Your PHP Testing with Codeception لصاحبه Vasily Koval
  19. تُسهِّل برمجية ووردبريس إنشاءَ صفحاتٍ ومنشوراتٍ محميةٍ بكلمة مرور، وهذه ميزةٌ رائعة إذا أردتَ تقييد الوصول إلى محتوى معيّن، لكن المشكلة الوحيدة هي أنَّ أيّة صور أومقاطع فيديو تَعرِضُها في تلك الصفحات ستبقى متاحةً للوصول من صفحات الوسائط المرفقة. يمكن أن يتحول الأمر إلى إشكالية رئيسية إذا كانت تحتوي ملفات الوسائط على معلومات مهمة التي تريد أن تبقى محميةً بكلمة مرور. أحدحلول هذه المشكلة هو إلغاء دعم صفحات الوسائط (attachment pages) في ووردبريس كليًّا، مما يعني أنَّ على زوار موقعك الوصول إلى المنشور (أوالصفحة) المحمي بكلمة مرور لرؤية المحتوى. سننظر في هذا الدرس إلى طريقتين لحل هذه المشكلة.فأولًا سنحلّ المشكلة بإنشاء «قالب ابن» (child theme) وكتابة بعض الشيفرات؛ ثم سنشرح كيف نستخدم إضافةً مجانيةً لفعل ذلك تلقائيًا. متى نخفي صفحات الوسائط؟ لنقل أنك كتبتَ درسًا تعليميًا رائعًا، وكان معروضًا في صفحةٍ وحيدةٍ، وبلغ طوله بضعة آلاف من الكلمات، إضافةً إلى مقطع فيديو أو أكثر، وعدد لا بأس به من الصور التوضيحية. سنفترض أنَّك لا تريد إنشاء متجر لبيع الدورات التعليمية وإنما توفِّر في بعض الأحيان محتوى مدفوع، لذا لن تكون مهتمًا بتثبيت نظام إدارة دورات تعليمية كامل مثلCoursePressPro. حيث اخترتَ حماية الصفحة بكلمة مرور التي ستُرسِلها بطريقةٍ مؤتمتة إلى البريد الإلكتروني للشخص الذي سيدفع لقاء الوصول إلى الدرس. سأفترض أنَّك وجدتَ حلًّا بسيطًا الذي يساعدك في ما سبق دون أن يتطلب جهدًا أو وقتًا طويلًا منك. لكن بعد عدِّة أسابيع أدركتَ وجود مشكلة، حيث شارك أحدهم صورةً من مقالتك على وسائل التواصل الاجتماعي مستعملًا رابط URL لصفحة الوسائط المرفقة، وتلك الصفحة يمكن الوصول إليها مباشرةً دون إدخال كلمة مرور. وما يزيد الطين بلّةً أنَّ القالب الذي تستعمله يُضيف روابط (للصورالتالية والسابقة) إلى صفحة المرفقات مما يسمح للزوار بالتنقل بسهولة بين جميع الصور التي أرفقتها بالمنشور المخفي! حسنًا، دعني أخبرك أنَّ هذه المشكلة سهلة الحل جدًا… إخفاء صفحات المرفقات باستخدام الشيفرات إذا توفر لديك عميل FTP ومحرر نصي، فيمكنك حلّ هذه المشكلة بسرعة عبر إنشاء «قالب ابن». كل ما عليك فعله هو إضافة سطرٌ واحدٌ إلى ملف image.php ويمكنك–اختياريًّا– إضافته إلى ملف video.php. إذا ألقيتَ نظرةً إلى البنية الهيكلية لقوالب ووردبريس فستلاحظ أنَّ أكثر ملف تخصيصًا يمكن إنشاؤه لصفحة المرفقات هو ملفٌ باسم MIME_type.php وهذا يعني أنَّه لو كانت لديك صورٌ ومقاطع فيديو في صفحةٍ محميةٍ بكلمة مرور، فستحتاج إلى إخفاء صفحة المرفقات لكلا النوعين، وستحتاج إلى تعديل ملفٍ مختلفٍ لكل نوع من الوسائط. لنبدأ بملف image.php،لكن قبل أن تبدأ بتعديل أيّة ملفات، فتأكد أنَّك تستعمل «قالب ابن». بعد إنشاء وتفعيل القالب الابن، أنشِئ ملفًا باسم image.php على حاسوبك، وافتحه باستخدام المحرر النصي المفضَّل لديك، وأضف إليه السطر البرمجي الآتي: <?php wp_redirect(get_permalink($post->post_parent)) ; ?> احفظ الملف وأغلقه، ثم ارفعه إلى مجلد الجذر للقالب الابن الذي أنشأتَه منذ قليل. عندما يحاول أحدهم الوصول إلى صفحة المرفقات للصور، فسيتم تحميل ملف image.php وسيؤدي إلى إعادة توجيه المتصفح إلى المنشور المرتبط بتلك الصورة، وهذا هو السلوك الذي نرغب به؛ فأيُّ شخصٍ يحاول الوصول إلى صفحة المرفقات الخاصة بالصور التابعة للمنشور المحمي بكلمة المرور سيُعاد توجيهه إلى الصفحة المحمية بكملة المرور، وعليه عندئذٍ إدخال كلمة المرور للوصول إلى تلك الصفحة. لإضافة حماية إلى صفحات المرفقات الخاصة بمقاطع الفيديو، أنشِئ ملفًا باسم video.php وأضف إليه نفس السطر السابق. هذاحلٌ بسيطٌ جدًا لإخفاء صفحات المرفقات باستخدام الشيفرات؛ لكن إذا كنتَ تُفضِّل إتمام الأمر دون استعمال محرر نصي وعميلFTP، فأود أن أبشِّرَك بوجود حلٍ آخر… إخفاء صفحات المرفقات باستخدام إضافة إذا أردتَ تفادي إنشاء قالب ابن وكتابة (أونسخ ولصق) السطر البرمجي السابق، فيمكنك استخدام إضافة لإعادة توجيه كل صفحات المرفقات. إضافة AttachmentPages Redirect هي إضافةٌ مجانيةٌ متاحةٌ على WordPress.org التي تؤدي الغرض الواضح من اسمها: إعادة توجيه جميع صفحات المرفقات إلى صفحة المنشور الأصلي الموجودةُ فيه (إعادة توجيه دائمة 301)،أو إلى الصفحة الرئيسية (إعادة توجيه مؤقتة 302) إذا لم تكن تلك الوسائط مرفقةً إلى أحد المنشورات. هنالك أكثر من 10000 موقع يستعمل هذه الإضافة، ولها تقييم 4.7 من 5 نجوم. للبدء باستخدام إضافة Attachment Pages Redirect اذهب إلى«Plugins > Add New» (الإضافات> أضف جديد) في لوحة تحكم ووردبريس وابحث عن العبارة «Attachment Pages Redirect»، ثم اضغط Install (تثبيت) ثم Activate (تفعيل). لا تحتاج إلى ضبط أيّ شيءٍ آخر. افتح متصفحك وحاول عرض صفحة أحد المرفقات،وستجد أنَّه قد تمت إعادة توجيهك إلى صفحة المنشور الأصلي أو إلى الصفحة الرئيسية للموقع. لا تظن أنَّ هنالك حماية كاملة لمرفقاتك لا تضمن الطريقتان اللتان شرحناهما في هذا الدرس أنَّ الزوار يجب أن يستعملوا كلمة مرور للوصول إلى الصور أو مقاطع الفيديو التي تُضيفها إلى منشوراتك المحمية؛ لكن دعني أوضِّح لك أنَّه لا توجد أيّة طريقة لحماية المرفقات حمايةً تامةً. فمثلًا، يستطيع المستخدمون الذين يملكون وصولًا إلى المرفقات (عبر إدخالهم لكلمة مرور صحيحة) أن ينسخوا محتواك بكل سهولة عبر: تفحص شيفرة الصفحة المصدرية والعثور على روابط URL مباشرة لملفات الوسائط أو عبر استخدام برمجيات لأخذ لقطات شاشة للصور أو تسجيل محتوى الفيديو عند تشغيله. هنالك خطواتٌ إضافية يمكنها التصدي لهذه المحاولات مثل إعادة التوجيه من طرف الخادوم (sever-level redirects) مما يمنع الوصول المباشر إلى ملفات الوسائط،وعبر استخدام JavaScript لمنع نسخ محتواك. لكن إذا كان المستخدم عاكفًا على نسخ مقالتك والوصول إلى ملفات الوسائط، فسيجد طريقةً للالتفاف على وسائل الوقاية التي استعملتها. في النهاية، أريد أن أُذكِّرك أنَّه لا توجد طريقةٌ لحماية ملفات الوسائط، ففي اللحظة التي تسمح بها لأيّ شخص بالوصول إليها،فلن تستطيع الاحتفاظ بالقدرة على التحكم بالمحتوى، لذا أبقِ هذا في بالك في كل مرة تُضيف فيها المحتوى في موقعك، حتى لو كنتَ «تحمي»المنشورات عبر كلمة مرور (يجب أن تكون مدركًا بعد قراءتك لما سبق أنَّه لا توجد حماية مطلقة للمحتوى). إذا لم تكن تريد رؤية الصور أو مقاطع الفيديو التي ترفعها إلى موقعك مسروقةً ومنشورةً في أحد المنتديات، فلا ترفعها إلى موقعك من الأساس. الخلاصة الميزة المُضمَّنة في ووردبريس لحماية المنشورات بكلمة مرور تُسهِّل من إنشاء محتوى خاص ومحمي، لكنها لا تحمي المرفقات والوسائط المُضافة إلى الصفحات والمنشورات التي لا تستطيع الوصول إليها إلا بكلمة مرور. الحل بسيطٌ جدًا. إذ يمكنك إعادة توجيه صفحات المرفقات إلى المنشور الأصلي الذي أُرفِقَت الوسائط به عبر سطرٍ برمجيٍ وحيد، وإذا لم تكن تريد أن تُضيع وقتك بكتابة ذاك السطر، فإضافة Attachment Pages Redirect ستساعدك في ذلك. ترجمة-وبتصرّف-للمقال [Howto Hide Media Attachment Pages in WordPress] لصاحبهJon Penland
  20. Docker هي أداةٌ لإنشاء الحاويات تُستخدَم لتوفير برمجيات مع نظام ملفات الذي يحتوي كل شيءٍ تحتاج له تلك البرمجيات لتعمل. استخدام حاويات Docker يضمن أنَّ البرمجيات ستعمل عملًا صحيحًا بغض النظر عن النظام الذي ستعمل داخله، لأنَّ بيئة التشغيل ستكون متماثلة في جميع الحالات. سنوفِّر في هذا الدرس لمحةً عن العلاقة بين صور Docker (أي Docker images) وحاويات Docker (أي Docker containers). ثم سنتعلم كيفية تشغيل وإيقاف وحذف الحاويات. لمحة عن حاويات Docker يمكننا أن نتخيل «صورة Docker» على أنها قالبٌ (template) يُستخدم لإنشاء حاويات Docker. تبدأ الصور عادةً بنظام ملفات رئيسي، وتُضاف التعديلات على نظام الملفات (والمعاملات المترافقة معها) على شكل طبقات متتالية. وعلى النقيض من توزيعات لينكس الاعتيادية، تحتوي صورة Docker على المكوِّنات الأساسية اللازمة لتشغيل التطبيق (ولا تحتوي على جميع الأدوات الموجودة في التوزيعات التقليدية). ولا تتغير صور Docker، وإنما تكوِّن نقطة انطلاق لإنشاء حاويات Docker. هذا المزيج بين الطبقات المتاحة للقراءة فقط والتي تعلوها طبقةٌ قابلةٌ للكتابة والقراءة يُشكِّل ما يُعرَف بنظام الملفات الاتحادي (union file system). فعند حدوث تغيير إلى ملفٍ موجودٍ في حاوية فسيُنسَخ الملف من الفضاء المتاح للقراءة فقط إلى الطبقة التي يُسمَح فيها بالقراءة والكتابة، ثم ستُطبَّق التغييرات، والنسخة الموجودة في الطبقة المتاحة للقراءة والكتابة تؤدي إلى «إخفاء» الملف الأصلي لكنها لا تحذفه. التغييرات التي تحدث في الطبقة المتاحة للقراءة والكتابة تتواجد في الحاويات المستقلة فقط، وعندما تُحذَف إحدى الحاويات فستضيع جميع التغييرات إلا إن اتخذنا إجراءات لحفظها. التعامل مع الحاويات في كل مرة تستعمل فيها الأمر docker run سيؤدي إلى إنشاء حاوية جديدة من الصورة التي حدَّدتَها. ما سبق قد يُسبِّب بعض الالتباس، لذا لنأخذ بعض الأمثلة للتوضيح. الخطوة الأولى: إنشاء حاويتَين الأمر docker run الآتي سيُنشِئ حاويةً جديدةً تستعمل الصورة ubuntu، ويمنحنا الخيار ‎-t وصولًا إلى الطرفية، والخيار ‎-i يسمح لنا بالتفاعل معها. سنعتمد على الأمر الافتراضي الموجود في ملف Docker لتوزيعة أوبنتو (وهو bash) لكي نحصل على وصولٍ للصدفة (shell): $ docker run -ti ubuntu سيتغير مِحَث سطر الأوامر (prompt) ليُشير إلى أننا نعمل داخل الحاوية بحساب الجذر، متبوعًا بمُعرِّفٍ ذي 12 محرفًا للحاوية، ويبدو المِحث كالآتي: root@11cc47339ee1:/# سنُجري تعديلًا على الحاوية عبر كتابة جملة نصيّة إلى ملفٍ داخل مجلد ‎/tmp، ثم سنستخدم الأمر cat للتأكد من أنَّ الملف قد تعدّل: echo "Example1" > /tmp/Example1.txt cat /tmp/Example1.txt الناتج: Example1 لنخرج الآن من الحاوية بالأمر exit. سيوقف تشغيل حاويات Docker عند انتهاء تنفيذ الأمر الذي بدأت به، لذا ستتوقف حاويتنا عن العمل عند خروجنا من الصدفة bash. ولو نفَّذنا الأمر docker ps الذي يعرض الحاويات التي تعمل حاليًا، فلن نشاهد حاويتنا مذكورةً بينها: docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES أما إذا استعملنا الخيار ‎-a الذي يعرض جميع الحاويات، سواءً كانت متوقفةً أم تعمل حاليًا، فستظهر عندئذٍ حاويتنا في القائمة: docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 11cc47339ee1 ubuntu "/bin/bash" 6 minutes ago Exited (127) 8 seconds ago small_sinoussi عندما أُنشِئَت الحاوية، أُعطيَتْ مُعرَّفًا واسمٍ مولِّدٍ تلقائيًا. وكان مُعرِّف الحاوية في حالتنا هو 11cc47339ee1 والاسم المولَّد تلقائيًا هو small_sinoussi. الخيار ps -a يُظهِر هذه القيمة بالإضافة إلى اسم الصورة التي بُنيَت الحاوية عليها (ubuntu)، ومتى أُنشِئَت الحاوية (‎6 minutes ago)، وما الأمر الذي بدأت الحاوية به (‎/bin/bash). والناتج يعرض أيضًا حالة الحاوية (Exited) ومتى دخلت الحاوية بهذه الحالة (‎8 seconds ago). إذا ما زالت الحاوية تعمل عند تنفيذ الأمر، فستظهر الحالة Up مع بيان مدّة تشغيلها. إذا أعدنا تنفيذ الأمر السابق، فستُنشَأ حاويةٌ جديدة: docker run -ti ubuntu سنعرف أنَّ هذه حاويةٌ جديدة لأنَّ مُعرِّف الحاوية الظاهر في المِحَث مختلف، وإذا حاولنا عرض الملف Example1 فلن نجده: root@6e4341887b69:/# cat /tmp/Example1 الناتج: cat: /tmp/Example1: No such file or directory قد تظن أنَّ الناتج السابق يعني أنَّ البيانات قد اختفت، لكن ذلك ليس صحيحًا. سنخرج الآن من الحاوية الثانية لنتأكد من أنَّ كلا الحاويتين ما زالتا موجودتين في نظام الملفات، ولم تُحذَف الحاوية الأولى التي أنشأنا فيها الملف. docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6e4341887b69 ubuntu "/bin/bash" About a minute ago Exited (1) 6 seconds ago kickass_borg 11cc47339ee1 ubuntu "/bin/bash" 13 minutes ago Exited (127) 6 minutes ago small_sinoussi الخطوة الثانية: إعادة تشغيل الحاوية الأولى لإعادة تشغيل حاوية موجودة مسبقًا، فسنستخدم الأمر start مع الخيار ‎-a و ‎-i، متبوعًا باسم الحاوية أو مُعرِّفها. احرص على أن تضع مُعرِّف الحاوية التي أنشأتها في نظامك في الأمر الآتي: docker start -ai 11cc47339ee1 سنجد أنفسنا داخل صدفة bash مرةً أخرى، وعندما نستخدم الأمر cat على الملف الذي أنشأناه مسبقًا فسنرى أنَّه ما يزال موجودًا: root@11cc47339ee1:/# cat /tmp/Example1.txt Example1 يمكننا الآن أن نخرج من الحاوية بالأمر exit. الناتج السابق يُظهِر أنَّ التعديلات التي أُجرِيَت داخل الحاوية ستبقى حتى لو أوقفناها ثم أعدنا تشغيلها مرةً أخرى. ولن يُحذَف محتواها إلا عند إزالة الحاوية. وهذا المثال يبيّن أنَّ التعديلات ستبقى محصورةً في الحاوية، ولن تؤثر على ما سواها، وعند بدء حاوية أخرى فستأخذ بنية نظام الملفات من الصورة الأصلية. الخطوة الثالثة: حذف كلا الحاويتين لقد أنشأنا في هذا الدرس حاويتين، وسنختمه بتوضيح كيف نحذفهما. يسمح لنا الأمر docker rm –الذي يعمل على الحاويات المتوقفة فقط– بحذف حاوية أو أكثر عبر تحديد اسمها أو مُعرِّفها كما يلي: docker rm 11cc47339ee1 kickass_borg 11cc47339ee1 kickass_borg لقد حُذِفَت الحاويتان مع جميع محتوياتها. الخلاصة لقد ألقينا نظرةً مفصّلةً على الأمر docker run ورأينا كيف يُنشِئ حاويةً جديدةً في كل مرّة يُنفَّذ فيها، ورأينا أيضًا كيفية الحصول على معلومات عن حاوية متوقفة وكيفية تشغيلها والوصول إليها. ترجمة -وبتصرّف- للمقال Working with Docker Containers لصاحبته Melissa Anderson
  21. تمهيد من المهم أن تراقب دومًا أداء الخادوم، ولمساعدتك في ذلك يوفِّر DigitalOcean مخططات Droplet Graphs التي تعرض أداء خادومك بشكلٍ بصري. سننظر في هذا الدرس إلى كيفية الاستفادة من المخططات المتوافرة افتراضيًا، إضافةً إلى المخططات التي سنحصل عليها بعد تثبيت عميل ‏DigitalOcean، الذي هو أداةٌ صغيرةٌ تجمع معلوماتٍ عن الذاكرة واستهلاك القرص وعن أكثر العمليات استعمالًا للمعالج وللذاكرة في النظام. المخططات الافتراضية لعرض مخططات أحد الخواديم، فاضغط على اسمه في صفحة Droplet: عند الضغط على اسم الخادوم، فستنتقل مباشرةً إلى صفحة Graphs. وإذا خرجتَ من صفحة Graphs فيمكنك العودة إليها بالضغط على رابط Graphs في شريط التنقل الظاهر على اليسار: يمكن عرض إحصائيات Droplet Graphs لآخر 6 ساعات، أو 24 ساعة، أو 7 أيام، أو 30 يوم. وتتوافر ثلاثة مخططات لأي خادوم: مخطط «Bandwidth public» (التراسل الشبكي الخارجي) يُظهِر استهلاك التراسل الشبكي للاتصالات الخارجية بواحدة ميغابت في الثانية. يظهر التراسل الشبكي للاتصالات الواردة باللون البنفسجي الداكن، أما التراسل الشبكي للاتصالات الصادرة فسيظهر باللون البنفسجي الفاتح. مخطط «CPU» (وحدة المعالجة المركزية) يُظهِر نسبةً مئويةً لمقدار استهلاك طاقة المعالجة، وتظهر عمليات المستخدم باللون الأزرق الفاتح، بينما تظهر عمليات النظام باللون الأزرق الداكن. مخطط «Disk I/O» (الكتابة والقراءة على القرص) يُظهِر عمليات القراءة والكتابة بواحدة ميغابايت في الثانية، وتُمثَّل عمليات القراءة باللون الأخضر الداكن، أما عمليات الكتابة فتظهر باللون الأخضر الفاتح. أما إذا فعّلتَ الشبكات الخاصة، فستستطيع الوصول إلى مخططٍ رابع لتتبع التراسل الشبكي الخاص. وكما في مخطط «Bandwidth public»، لن يظهر هذا المخطط إلا إذا كان هنالك تراسل شبكي فعلي، وعندئذٍ سيُعرَض مخططٌ باسم «Bandwidth private» التي يُظهِر استهلاك التراسل الشبكي الخاص بواحدة ميغابت في الثانية؛ يظهر التراسل الشبكي للاتصالات الواردة باللون البنفسجي الداكن، أما التراسل الشبكي للاتصالات الصادرة فسيظهر باللون البنفسجي الفاتح. عندما تضع الفأرة فوق أحد المخططات، فسيُعرَض خطٌ عموديٌ في جميع المخططات إضافةً إلى مربع صغير يحتوي على القيم المرتبطة بهذه اللحظة الزمنية. يمكنك أن تلحظ في الصورة الآتية أنَّ المربع سيعرض حالة النظام في الساعة 12:12 مساءً في تاريخ November 21, 2016: المخططات الإضافية التي تظهر بعد تثبيت العميل المخططات الافتراضية تُقاس وتُحسَب باستخدام أدوات خارجية، ولا تتطلب أي شيءٍ خاص في الخادوم نفسه. أما الإحصائيات الأخرى (كاستهلاك الذاكرة والقرص) تتطلب جمعًا للبيانات من داخل الخادوم. يوفِّر عميل DigitalOcean (الذي هو أداةٌ صغيرةٌ تعمل على الخادوم) المعلومات الإضافية حول استهلاك الذاكرة والقرص. يمكنك تعلم المزيد من المعلومات حول عميل DigitalOcean وكيفية تثبيته في درس «كيفية تثبيت واستخدام عميل DigitalOcean لإظهار المزيد من المخططات للخادوم». يمكن تثبيت العميل تلقائيًا أثناء إنشاء الخادوم عبر تفعيل الحقل «Monitoring» كما هو موضَّح في هذه الصورة: يمكن أيضًا تثبيت العميل يدويًا باتباع التعليمات الواردة في الصفحة الموجودُ رابطها أعلى المخططات: بعد تفعيل العميل، فستملك وصولًا إلى مخططاتٍ إضافيةٍ التي تتضمن: مخطط «Memory» الذي يُظهِر نسبة استهلاك الذاكرة الفيزيائية RAM. مخطط «Disk Usage» الذي يُظهِر نسبة المساحة المستخدمة في القرص الصلب المرتبط بالخادوم. مخطط «Top processes» يُظهِر أكثر العمليات التي تعمل على الخادوم استهلاكًا للموارد، والمرتّبة عبر استهلاك المعالج أو الذاكرة. وهذا المخطط لا يُظهِر أيّة بيانات من الماضي، وإنما سيُظهِر البيانات اللحظية فقط. المرور بالفأرة على أيّ مخطط سيؤدي إلى إظهار مربع يحتوي على تفاصيل استهلاك الموارد في اللحظة الحالية: أخيرًا، إذا كنتَ قد فعّلتَ الشبكة الخاصة واستخدمتها، فستُعرَض سبعة مخططات في الصفحة السابقة: الخلاصة رأينا ما هي المخططات المتوافرة لخواديم DigitalOcean. إذا دلّت تلك المخططات على مشاكل في الأداء، فيتوجّب عليك تتبّع الخلل ومُحاولة إصلاحه. ألق نظرة على دروسنا السّابقة لتجد فيها أمثلة عن تحسين أداء مُختلف جوانب الخادوم. ترجمة -وبتصرّف- للمقال How To Track Droplet Performance with DigitalOcean Droplet Graphs لصاحبته Melissa Anderson
  22. أداة البناء Grunt أو Gulp، مكتبة require.js، ‏browserify، الإصدار السادس من ES، المفسرات، أطر عمل React و Angular و Amber، التعابير المغلقة (closures)، سلسلة prototype. ارتفاع في ضغط الدم يؤدي إلى سكتة دماغية. حسنًا، تطوير الويب أمرٌ ممتع جدًا، لكن JavaScript مروعة! تجد نفسك منسجمًا تمامًا مع جميع جوانب تطوير الويب، لكن عندما يأتي الأمر إلى JavaScript فستشعر أنَّ جزءًا كبيرًا من المعلومات الأساسية ينقصك بينما يعرفه الآخرون، والذي سيؤدي إلى جعلهم يفهمون سكربتات JavaScript. نعم، الحقيقة هي أنَّك تفتقد بالفعل إلى بعض القطع؛ لكن هذا لا يعني أنَّ التوجه الحالي لتطوير الواجهات الأمامية ليس مجنونًا! اطمئن، فأنت لستَ بمفردك، لذا اسحب كرسيًا واجلس، وجهِّز نفسك لكتابة تطبيق JavaScript. أوّل خطوة هي ضبط بيئة التطوير المحلية، لذا اتخذ قرارك: هل ستستخدم Gulp، أم Grunt، لا! سأستعمل سكربتات NPM. هل أستعمل Webpack أم Browserify أم Require.js؟ هل أتخذ قرارًا مصيريًا بالانتقال إلى الإصدار السادس من ES؟ أليس ضروريًا أن أضع مرجعًا عن أمراض القلب بجواري؟ كيف سأنظِّم اختبار الشيفرات؟ هل من إطارِ عملٍ تنصحني به؟ أليس من الأفضل تشغيل الاختبارات من سطر الأوامر، لنستعمل إذًا PhantomJS؟ مع أي إطارٍ أذهب: Angular أم React؟ ربما Ember؟ ماذا عن Backbone؟ ربما قرأتَ بعض صفحات توثيق React ووجدتَ فيها أنَّ «Redux هو حاويةٌ ذاتُ حالةٍ قابلةٍ للتوقع لتطبيقات JavaScript» وبدت على وجهك أمارات الرضى، فمن المؤكد أنَّك ستحتاج إلى هذه الميزة العظيمة، بغض النظر عن أنَّك لم تفهم حرفًا من شرحها. السؤال الآن هو: لماذا أصبح تطوير تطبيقات JavaScript أمرًا يدفع إلى الجنون؟! دعني أساعدك لفهم سبب ذلك. لنبدأ بمثالٍ بسيطٍ ثم سنستعرض صورًا جميلةً توضِّح وجهة نظري. هذا تطبيق «Hello, World!‎» مكتوبٌ باستخدام React: // main.js var React = require('react'); var ReactDOM = require('react-dom'); ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') ); لم ننتهِ منه بعد: $ npm install --save react react-dom babelify babel-preset-react $ browserify -t [ babelify --presets [ react ] ] main.js -o bundle.js هنالك عدِّة خطوات ناقصة هنا، مثل تثبيت مكتبة browserify أو ما الذي عليك فعله لتشغيل الصفحة في المتصفح، إذ لا يبدو أنَّ ما سبق سيُنشِئ صفحة ويب قادرة على فعل أيّ شيء! بعد أن تنتهي من إنجاز ما سبق، فستجد ملفًا يدعى bundle.js يحتوي على تطبيق «Hello, World!‎» السابق المكتوب بمكتبة React والذي يضم حوالي 19374 سطرًا برمجيًا، وكل ما فعلتَه هو تثبيت browserify و babelify و react-dom، التي «تزن» آلاف الأسطر البرمجية. هذه صورة تعبيرية عن برنامج «Hello, World!‎» في React: حسنًا، هذا تطبيق «Hello, World!‎» باستخدام JavaScript دون مكتبات: <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>Hello World</title> </head> <body> <div id="container"></div> <script> document.body.onload = function(){ var container = document.getElementById("container"); container.innerHTML = '<h1>"Hello, world!"</h1>'; } </script> </body> </html> هذا كل ما في الأمر! 18 سطر برمجي (يمكن اختصارها إلى أقل من ذلك)، التي تستطيع نسخها ولصقها في ملفٍ باسم index.html وتنقر نقرتين عليه مما يفتحه في متصفحك. يا للبساطة! إذا كنتَ تفكِّر في هذه اللحظة «أليس إطار React يفعل أكثر من ذاك المثال البسيط الذي كتبته، والذي لا يرقى أن يكون تطبيق JavaScript» فأنت مصيبٌ (تقريبًا)، وعلى بعد خطوة واحدة من فهمك لماذا كل هذا التعقيد. انظر إلى هذه الصورة: أغلبية تطبيقات JavaScript التي ستعمل عليها ستقع في مكانٍ ما في منتصف المنحني الجرسي (bell curve) السابق. وإذا كنتَ في منتصف المنحني السابق وبدأت تطبيقًا بالاعتماد على React فسوف ينتهي بك المطاف بهندسة تطبيقك زيادةً عن اللزوم من بدايته. وهذا هو سبب تعقيد تطوير تطبيقات JavaScript، لأنَّ غرض أغلبية الأدوات التي تظن أنَّك بحاجةٍ إليها هو حلّ المشاكل التي لن تتعرض إليها بتاتًا. أصبحت حالة تطوير تطبيقات JavaScript في الآونة الأخيرة معقدةً ومربكةً لأنَّ الجميع يبالغون في هندسة تطبيقاتهم دون أن يدركوا ذلك. إذًا، كيف يجب أن نبدأ بتطوير تطبيق JavaScript؟ هل علينا استخدام مكتبة شبيهة بمكتبة React أو Angular؟ هل يجب أن نستخدم مدير للحزم؟ ماذا يفترض علينا أن نفعل إذا لم نستخدمهما؟ هل كتابة الاختبارات ضرورية؟ هل علينا أصلًا توليد شيفرات HTML عبر JavaScript؟ هذه هي الأسئلة التي يجب أن تسألها لنفسك قبل أن تبدأ بمجموعة ضخمة من أدوات التطوير. عندما تبدأ بتطوير تطبيق JavaScript فمن المهم أن تختار نقطةً في المنحني الجرسي في المكان الذي تظن أنَّ من المرجح أن يصله تطبيقك في المستقبل من ناحية التعقيد. لن أكذب عليك، فعل ذلك ليس سهلًا ويحتاج خبرةً، لكن هنالك منطقةٌ كبيرة يمكنك أن تبدأ منها أغلبية تطبيقات JavaScript: استعمل مكتبة jQuery مع قوالب لصفحات الواجهة الأمامية مع أداة بناء بسيطة لجمع الملفات وتصغيرها (بفرض أنَّ إطار العمل الذي تستعمله لتطوير السند الخلفي [backend] لا يفعل ذلك تلقائيًا). إذا أردتَ أن تتعلم كيفية هيكلة تطبيق JavaScript بطريقةٍ صحيحة، فعليك أن تبدأ بفهم كيف ومتى ولماذا تستخدم إطار عمل أو حزمة npm أو إصدار ES6 أو متى تكتب اختبارات أو هل عليك جعل الاختبارات تعمل محليًا أو في متصفح، ثم سيأتي دور بقية الأسئلة وحلّ بقية المشاكل. إن أردتَ أن تملأ الفجوات الموجودة في معلوماتك حول تطوير JavaScript وأن تتجنّب الشعور بأنّك تبالغ في تصميم تطبيق JavaScript فحاول أن تتابع ما نطرحه هنا في قسم البرمجة في أكاديمية حسوب. ترجمة -وبتصرّف- للمقال Why JavaScript Development is Crazy لصاحبه Sean Fioritto
  23. تمهيد تَتَبَّعُ مخططات الخادوم (Droplet Graphs) استخدامَ موارد خادومك مع مرور الزمن. يمكن قياس بعض الأمور مثل التراسل الشبكي والقراءة والكتابة إلى القرص من أدواتٍ خارجية. لكن للحصول على معلوماتٍ إضافية فيجب تثبيت عميل DigitalOcean على الخادوم لتوفير إمكانية قياس استخدام الذاكرة والقرص الصلب والحصول على معلومات عن أكثر البرمجيات استهلاكًا للمعالج أو الذاكرة في النظام. سنتعرّف في هذا الدرس على عميل DigitalOcean وكيف يعمل، وسنشرح كيفية تثبيته للحصول على المعلومات السابقة. وسنريك أيضًا كيفية حذف العميل تمامًا في حال لم تعد تريد تلك الإحصائيات والبيانات. ما هو عميل DigitalOcean؟ عميل DigitalOcean هو أداةٌ مفتوحة المصدر مكتوبةٌ بلغة Go التي توفِّر قياسات إضافية لكي تُعرَض كمخططات تُظهِر أداء خادومك. دون تثبيت العميل، ستُعرَض لك عبر Droplet Graphs المعلومات الآتية: استهلاك التراسل الشبكي الداخلي والخارجي نشاط المعالج القراءة والكتابة إلى القرص أما مع تثبيت العميل، فسيُعرَض لك –إضافةً إلى ما سبق–: استهلاك الذاكرة استهلاك القرص الصلب أكثر العمليات استهلاكًا للمعالج وللذاكرة. يمكن تشغيل العميل على توزيعة أوبنتو 14.04 أو ما بعدها، وتوزيعة CentOS 6 وما بعدها، ودبيان 8. زر صفحة مستودع عميل DigitalOcean على GitHub لتنظر إلى شيفرته. ما هي المجلدات التي يستطيع العميل الوصول إليها تعمل خدمة العميل كمستخدمٍ دون امتيازات الذي يملك وصولًا إلى المجلدات الثلاثة الآتية: ‎/proc: المكان الذي يجمع فيه العميل معلوماتٍ عن حالة النظام ‎/var/opt: مكان كتابة العميل لمعلومات الاستيثاق (authentication information) الخاصة به ‎/opt/digitalocean: مكان تخزين الملفات الثنائية يبلِّغ العميل عن أسماء أكثر العمليات استهلاكًا لموارد النظام، لكنه لا يُرسِل أيّة متغيرات متعلقة بالبيئة، ولا أيّة وسائط مُمرَّرة إلى العمليات تفاديًا لكشف أيّة معلومات حساسة. كيف يُرسِل العميل البيانات المُقاسة من النظام؟ يستعمل عميل DigitalOcean المنفذين 80 و 443 لإرسال البيانات، ولن يحتاج إلى استقبل أيّة بيانات. يُستعمَل المنفذ 80 للتواصل مع خدمة DigitalOcean metadata للحصول على بيانات الاستيثاق، ثم سيستعمل العميل تلك البيانات للاستيثاق مع خدمة معالجة الإحصائيات وستُشفّر جميع البيانات. كيفية تفعيل العميل عليك تثبيت العميل على خادومك للحصول على معلومات إضافية عن خادومك في Droplet Graphs. يمكن فعل ذلك تلقائيًا أثناء عملية إنشاء الخادوم، أو يدويًا في أيّ وقت. تفعيل العميل أثناء عملية إنشاء الخادوم لتثبيت العميل أثناء إنشاء الخادوم، فاختر حقل Monitoring في قسم «additional options» في صفحة الإنشاء: سيُثبّت العميل تلقائيًا وسيُفعّل أثناء مرحلة إنشاء الخادوم. تثبيت العميل يدويًا يتوافر سكربت للتثبيت لتثبيت العميل يدويًا. سيُضيف السكربت مستودعًا إلى نظامك ويستعمل مدير الحزم لتثبيت العميل. وهذا سيُبسِّط إجراء عمليات إدارة الحزم مثل تحديث العميل أو حذفه. سجِّل دخولك إلى خادومك بالمستخدم root أو بأي مستخدم يملك امتيازات الجذر عبر الأمر sudo: ssh root@droplet_IP_address بعد أن تتصل بخادومك، يمكنك تنزيل وتنفيذ سكربت التثبيت مباشرةً لو شئت تثبيت العميل من فورك: curl -sSL https://agent.digitalocean.com/install.sh | sh قد يُطلَب منك إدخال كلمة مرورك إذا كنتَ تستعمل مستخدمًا يملك امتيازات الجذر عبر الأمر sudo. ملاحظة: لو أردتَ تفحص السكربت قبل تثبيت، فيمكنك كتابته إلى القرص أولًا: curl -sSL https://agent.digitalocean.com/install.sh -o /tmp/install.sh ثم تنظر في محتويات الملف بتنفيذ الأمر: less /tmp/install.sh بعد أن تتطلع على محتوى الملف وتتأكد منه، فيمكنك إكمال عملية التثبيت وتشغيل السكربت كما يلي: sh /tmp/install.sh يجب أن يكون العميل مثبتًا على نظامك الآن. كيفية عرض المخططات الجديدة زر قسم «Graphs» في صفحة تفاصيل الخادوم بعد تثبيت العميل وتفعيله: يجب أن تصبح المخططات الجديدة ظاهرةً لك بعد برهةٍ من تفعيل العميل: استخدم القائمة المنسدلة لتعديل المدة الزمنية الظاهرة في المخططات، وضع الفأرة فوق أحد المخططات لعرض معلومات تفصيلية. كيفية حذف العميل إذا لم تعد تريد استخدام العميل، فيمكنك حذف الحزمة باستخدام مدير الحزم الموجود في توزيعتك. لخواديم أوبنتو ودبيان، اكتب: sudo apt-get purge do-agent أما لخواديم CentOS: sudo yum remove do-agent سيتم إيقاف الخدمة وستُزال الحزمة من نظامك. وإذا أردتَ إعادة تثبيت العميل لاحقًا فيمكنك فعل ذلك عبر مدير الحزم. كيفية إزالة مستودع عميل DigitalOcean لحذف مستودع العميل من خادومك، فكل ما عليك فعله هو حذف ملف ضبط المستودع. لخواديم أوبنتو ودبيان، اكتب: sudo rm /etc/apt/sources.list.d/digitalocean-agent.list أما لخواديم CentOS: sudo rm /etc/yum.repo.d/digitalocean-agent.repo سيُحذَف ملف ضبط مستودع عميل DigitalOcean من خادومك. الخلاصة يزيد عميل DigitalOcean من البيانات المعروضة في Droplet Graphs ليوفر لك معلوماتٍ إضافيةٍ عن أداء خادومك واستعماله للموارد. ترجمة -وبتصرّف- للمقال How To Install and Use the DigitalOcean Agent for Additional Droplet Graphs لصاحبه Justin Ellingwood
  24. تمهيد تتوسع قواعد البيانات بسرعة مع مرور الزمن، وتكاد في بعض الأحيان أن تملأ المساحة التخزينية المتاحة في نظام الملفات كلها. وقد تتعرض أيضًا إلى مشاكل في الإدخال والإخراج نتيجةً لمحاولة عدِّة خدمات الكتابة على (أو القراءة من) نفس القسم معًا. هذا الدرس سيفيدك لو كنتَ تريد إضافة المزيد من المساحة التخزينية، أو استخدام خصائص جهاز التخزين لزيادة الأداء (ربما عبر استخدام RAID)، أو تتطلّع إلى استعمال ميزات أخرى للتخزين. سيعلِّمُك هذا الدرس طريقة تغيير مجلد تخزين بيانات MySQL. التعليمات المذكورة هنا تناسب الخواديم التي تُشغِّل نسخةً وحيدةً من MySQL، أمّا لو كانت عندك أكثر من نسخة، فسيساعدك درس «How To Move a MySQL Data Directory to a New Location on Ubuntu 16.04» في ذلك، لأنَّه يحتوي معلومات عن كيفية تغيير مكان التخزين عبر تعديل الضبط. المتطلبات المسبقة خادوم أوبنتو 16.04 (أو 14.04) مضبوطٌ كما في درس «الإعداد الابتدائي لخادوم أوبنتو 14.04»، بما في ذلك إعداد حساب مستخدم عادي لكنه يملك امتيازات الجذر (root) عبر الأداة sudo. خادوم MySQL. لو لم يكن عندك خادوم MySQL مضبوطٌ مسبقًا، فسيساعدك الدرس «تثبيت وإعداد نظامي إدارة قواعد البياناتMySQL وPostgreSQL على أوبنتو». نسخة احتياطية من قواعد بياناتك. ما لم تكن تتعامل مع نسخةٍ حديثة التثبيت من MySQL، فاحرص على أخذ نسخة احتياطية من بياناتك. سيساعدك درس «كيف تقوم بالنسخ الاحتياطي لقواعد بيانات MySQL على Ubuntu» على فعل ذلك. سننقل البيانات من جهاز تخزينٍ موصولٍ (mounted) في نقطة الوصل ‎/mnt/volume-nyc1-01.. سيعلّمك هذا الدرس طريقة نقل مجلد تخزين بيانات MySQL إلى مكانٍ جديد بغض النظر عن وسيط التخزين الذي تستخدمه (قرص صلب، أو مصفوفة RAID، أو تخزين شبكي). الخطوة الأولى: نقل مجلد بيانات MySQL لكي نضمن سلامة البيانات، علينا أولًا إيقاف خادوم MySQL: sudo systemctl stop mysql الأمر systemctl لا يُظهِر نتيجة تنفيذ أوامر إدارة الخدمات، لذا إذا أردتَ التحقق أنَّ الخادوم قد أُغلِق بنجاح، فنفِّذ الأمر الآتي: sudo systemctl status mysql انظر إلى آخر سطر من ناتج الأمر السابق الذي يجب أن يخبرك أنَّ الخادوم قد توقف عن العمل: . . . Jul 18 11:24:20 ubuntu-512mb-nyc1-01 systemd[1]: Stopped MySQL Community Server. نستطيع الآن –بعد إغلاق الخادوم– نقل مجلد قواعد البيانات إلى مكانٍ آخر: sudo mv /var/lib/mysql /mnt/volume-nyc1-01/mysql ثم سنُنشِئ وصلةً رمزيةً (symbolic link): sudo ln -s /mnt/volume-nyc1-01/mysql /var/lib/mysql يبدو أنَّنا نستطيع تشغيل خادوم MySQL بعد إنشاء الوصلة الرمزية، لكن هنالك أمرٌ إضافيٌ يجب ضبطه. الخطوة الثانية: ضبط قواعد الوصول في AppArmor بعد أن نقلتَ مجلد MySQL إلى مكانٍ آخر في نظام الملفات، فعليك أن تُعدِّل في ضبط AppArmor، وذلك بتعديل ملف alias التابع لبرمجية AppArmor: sudo nano /etc/apparmor.d/tunables/alias أضف الآن التعليمة الآتية في نهاية الملف: . . . alias /var/lib/mysql/ -> /mnt/volume-nyc1-01/mysql/, لكي تأخذ التعديلات مفعولها، فيجب إعادة تشغيل AppArmor: sudo systemctl restart apparmor ملاحظة: إذا تخطيت خطوة ضبط AppArmor وحاولت تشغيل mysql مباشرةً، فستظهر لك رسالة الخطأ الآتية: Job for mysql.service failed because the control process exited with error code. See "systemctl status mysql.service" and "journalctl -xe" for details. يمكن تلخيص الناتج الظاهر من الأمرَين systemctl و journalctl بما يلي: Jul 18 11:03:24 ubuntu-512mb-nyc1-01 systemd[1]: mysql.service: Main process exited, code=exited, status=1/FAILURE ولمّا كانت رسائل الخطأ لا تُظهِر ربطًا مباشرًا بين AppArmor ومجلد التخزين، فقد يصعب عليك أن تعرف لماذا يحدث هذا الخطأ. لكن إن نظرنا إلى ملف syslog فسنرى ما هي المشكلة: sudo tail /var/log/syslog الناتج: Nov 24 00:03:40 digitalocean kernel: [ 437.735748] audit: type=1400 audit(1479945820.037:20): apparmor="DENIED" operation="mknod" profile="/usr/sbin/mysqld" name="/mnt/volume-nyc1-01/mysql/mysql.lower-test" pid=4228 comm="mysqld" requested_mask="c" denied_mask="c" fsuid=112 ouid=112 يمكننا الآن أن نُشغِّل خدمة MySQL: sudo systemctl start mysql sudo systemctl status mysql بعد أن تُعيد تشغيل MySQL، فتحقق أنَّ بياناتك سليمة وأنَّ خادوم MySQL يعمل عملًا سليمًا دون مشاكل. الخلاصة نقلنا في هذا الدرس مجلد تخزين بيانات MySQL واستعملها وصلةً رمزيةً لكي نخبر MySQL ما هو مكان التخزين الجديد؛ ثم حدّثنا ضبط برمجية AppArmor (الموجودة في توزيعة أوبنتو وغيرها) لكي يتوافق مع ما عدّلناه. وصحيحٌ أننا استعملنا جهاز تخزين مستقل، إلا أنَّك تستطيع اتباع تعليمات هذا الدرس لإعادة تعريف مكان تخزين البيانات بغض النظر عن تقنية التخزين المستعملة. ترجمة -وبتصرّف- للمقال How to Change a MySQL Data Directory to a New Location Using a Symlink لصاحبته Melissa Anderson
  25. تمهيد كلما كانت صفحات الموقع أسرع تحميلًا كلما ازداد احتمال بقاء الزائر متصفحًا للموقع، وعندما تمتلئ صفحات مواقع الويب بالصور والمحتوى التفاعلي الظاهر عبر سكربتات تُحمَّل في الخلفية، فلن تكون عملية فتح «صفحة» ويب أمرًا هينًا، إذ تتضمن طلب ملفاتٍ عدِّة من خادوم الويب ملفًا ملفًا، وعملية تقليل تلك الطلبيات هي إحدى سُبُل تسريع موقعك. يمكن فعل ذلك بطرائق عدِّة، لكن إحدى أهم الخطوات التي يجب إجراؤها هي ضبط التخزين المؤقت في المتصفح (browser caching)؛ وهذا يعني إخبار المتصفح بإمكانية استخدام نسخ محلية من الملفات التي حُمِّلَت في إحدى المرات بدلًا من طلبها من الخادوم مرارًا وتكرارًا؛ ولفعل ذلك يجب إضافة ترويسات لرد HTTP ‏(HTTP response headers) تخبر المتصفح بما عليك فعله. هذا هو دور وحدة header (‏header module) في خادوم Nginx، التي يمكن استعمالها لإضافة الترويسات إلى رد HTTP، لكن دورها الأساسي يمكن في ضبط الترويسات المسؤولة عن التخزين المؤقت. سننظر في هذا الدرس إلى كيفية استخدام وحدة header لتطبيق الفكرة السابقة. المتطلبات المسبقة ستحتاج قبل إكمال قراءة هذا الدرس إلى ما يلي: خادوم أوبنتو 16.04 (أو 14.04) مضبوطٌ كما في درس «الإعداد الابتدائي لخادوم أوبنتو 14.04»، بما في ذلك إعداد حساب مستخدم عادي لكنه يملك امتيازات الجذر (root) عبر الأداة sudo. خدمة Nginx مثبّتة على خادومك باتباعك لهذا الدرس «تنصيب، إعداد واستخدام nginx كخادوم ويب». سنحتاج بالإضافة إلى وحدة header إلى وحدة map التابعة لخادوم Nginx؛ لمزيدٍ من المعلومات حول وحدة map، فاقرأ درس كيفية استخدام وحدة map على خادوم أوبنتو 16.04. الخطوة الأولى: إنشاء ملفات للتجربة سنُنشِئ في هذه الخطوة عدِّة ملفات في مجلد Nginx، إذ سنستخدم هذه الملفات لاحقًا للتحقق من سلوك خادوم Nginx الافتراضي لاختبار أنَّ التخزين المؤقت في المتصفح يعمل عملًا صحيحًا. لتقرير ما هو نوع الملفات المُخدَّمة عبر الشبكة، فلن يُحلِّل Nginx محتويات الملف لأن ذلك يستهلك وقتًا كثيرًا؛ وإنما سينظر إلى لاحقة (أو امتداد) الملف لتحديد ما هو نوع MIME التابع له، والذي سيُصرِّح عن الهدف أو الغاية من الملف. ونتيجةً لهذا السلوك، فلن يكون لمحتوى الملفات الاختبارية أيّة أهمية؛ إذ سنستطيع خداع خادوم Nginx بتسمية الملفات تسميةً صحيحةً، فقد يظن خادوم Nginx أنَّ ملفًا فارغًا يُمثِّل صورةً وآخر يُمثِّل سكربت JavaScript. لنُنشِئ ملفًا باسم test.html في مجلد Nginx الافتراضي عبر الأداة truncate (يمكنك استخدام أيّة أداة أو أمر آخر وليكن touch مثلًا). لاحقة الملف تُشير إلى أنَّه مستند HTML: sudo truncate -s 1k /var/www/html/test.html لنُنشِئ المزيد من الملفات الاختبارية بنفس الطريقة السابقة: صورة jpg وملف css وسكربت js: sudo truncate -s 1k /var/www/html/test.jpg sudo truncate -s 1k /var/www/html/test.css sudo truncate -s 1k /var/www/html/test.js الخطوة التالية هي التحقق من سلوك خادوم Nginx فيما يتعلق بإرسال ترويسات للتحكم بالتخزين المؤقت للمتصفح وذلك بتخديم الملفات التي أنشأناها أخيرًا مع الضبط الافتراضي له. الخطوة الثانية: التحقق من السلوك الافتراضي لخادوم Nginx يكون لجميع الملفات نفس سلوك التخزين المؤقت افتراضيًا. ولرؤية ذلك سنستخدم ملف HTML الذي أنشأناه في الخطوة الأولى، إلا أنَّك تستطيع إجراء هذا الاختبار على أيّ ملف من الملفات التي أنشأناها سابقًا. لنتحقق من أنَّ الملف test.html يُخدَّم ومعه المعلومات التي لها علاقة بالمدة الواجب تخزين المتصفح للملف فيها مؤقتًا. سيؤدي الأمر الآتي إلى طلب الملف من خادوم Nginx المحلي ويُظهِر ترويسات رد HTTP: curl -I http://localhost/test.html يجب أن ترى عدِّة ترويسات HTTP: HTTP/1.1 200 OK Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:12:26 GMT Content-Type: text/html Content-Length: 1024 Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT Connection: keep-alive ETag: "57d40685-400" Accept-Ranges: bytes يمكنك أن تلاحظ السطر قبل الأخير الذي يبدأ بالكلمة ETag الذي يتضمن مُعرِّفًا فريدًا للنسخة الحالية من الملف المطلوب. فإذا حاولت تنفيذ أمر curl السابق أكثر من مرة فستجد نفس قيمة ETag. أما عند استخدام متصفح ويب، فستُخزَّن قيمة ETag ثم تُرسَل إلى الخادوم عبر ترويسة If-None-Match عندما يريد المتصفح أن يطلب نفس الملف مرةً أخرى (عند تحديث الصفحة على سبيل المثال). يمكنك محاكاة ذلك في سطر الأوامر بالأمر الآتي. احرص على تغيير قيمة الترويسة If-None-Match في الأمر لتُطابِق قيمة ETag في ناتج الأمر السابق: curl -I -H 'If-None-Match: "57d40685-400"' http://localhost/test.html ستجد أنَّ الرد أصبح مختلفًا الآن: HTTP/1.1 304 Not Modified Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:20:31 GMT Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT Connection: keep-alive ETag: "57d40685-400" ردّ خادوم Nginx بحالة «‎304 Not Modified»، ولن يُرسِل الملف عبر الشبكة مجددًا، وإنما يخبر المتصفح أنَّه يستطيع إعادة استخدام الملف المُخزَّن محليًا والذي نزَّله سابقًا. ما سبق مفيدٌ لأنه يُقلِّل من التراسل الشبكي، لكنه ليس مثاليًا لتحقيق أداءٍ عالٍ نتيجةٍ للتخزين المؤقت. المشكلة مع ترويسة ETag أنَّ المتصفح سيرسل الطلبية إلى الخادوم دائمًا ويسأله إن كان بإمكانه استخدام الملف المُخزَّن مؤقتًا، وحتى لو ردّ الخادوم على المتصفح بالإيجاب بحالة 304 بدلًا من إعادة إرسال الملف مجددًا، إلا أنَّ إرسال الطلبية واستلام الرد سيستهلك وقتًا. سنستخدم في الخطوة التالية وحدة headers لإضافة معلومات للتحكم بالتخزين المؤقت. وهذا سيجعل المتصفح يُخزِّن الملفات محليًا دون أخذ إذن الخادوم أولًا. الخطوة الثالثة: ضبط ترويستَي Cache-Control و Expires إضافةً إلى ترويسة التحقق من تغيّر الملف (ETag)، هنالك ترويستان للتحكم بالتخزين المؤقت هما Cache-Control و Expires. ترويسة Cache-Control هي نسخةٌ أحدث، وفيها خياراتٌ أكثر مقارنةً بترويسة Expires وهي أفيد إن شئت التحكم بسلوك التخزين المؤقت تحكمًا دقيقًا. إذا ضُبِطَت تلك الترويسات، فهي تخبر المتصفح أنَّ الملف المطلوب يمكن أن يُخزَّن محليًا لفترةٍ زمنيةٍ معيّنة (تستطيع أن تجعل صلاحيته «للأبد»!) دون طلبه مرةً أخرى. أما إن لم تُضبَط تلك الترويسات، فسيطلب المتصفحُ الملفَ دومًا من الخادوم، متوقعًا أن يحصل على الحالة «‎200 OK» أو «‎304 Not Modified». يمكننا استخدام وحدة header لإرسال ترويسات HTTP آنفة الذكر. وحدة header هي وحدةٌ من أساس خادوم Nginx لذا لن تحتاج إلى تثبيت أيّ شيءٍ لاستخدامها. لإضافة وحدة header، افتح ملف ضبط Nginx في محرر nano أو أيّ محررٍ نصيٍ تشاء: sudo nano /etc/nginx/sites-available/default اعثر على القسم المُعَنوَنَ server، والذي يبدو كما يلي: . . . # Default server configuration # server { listen 80 default_server; listen [::]:80 default_server; . . . أضف القسمَين الآتيين: أولهما قبل قسم server لتعريف المدة الزمنية لتخزين مختلف أنواع الملفات مؤقتًا، وثانيهما داخل كتلة server الذي يضبط ترويسات التخزين المؤقت ضبطًا سليمًا: . . . # Default server configuration # # Expires map map $sent_http_content_type $expires { default off; text/html epoch; text/css max; application/javascript max; ~image/ max; } server { listen 80 default_server; listen [::]:80 default_server; expires $expires; . . . القسم الموجود قبل قسم server هو قسم map الذي يربط بين أنواع الملفات ومدة التخزين المؤقت لهذا النوع من الملفات. استعملنا عدِّة خيارات ضبط فيه: القيمة الافتراضية هي off، والتي لن تُضيف أيّة ترويسات للتحكم بالتخزين المؤقت، وهذا خيارٌ جيدٌ للمحتوى الذي لا يتطلب تخزينًا مؤقتًا. لنوع text/html سنضبط القيمة إلى epoch وهي قيمةٌ خاصةٌ التي تعني عدم التخزين المؤقت نهائيًا، مما يجبر المتصفح على السؤال إن كانت الصفحة مُحدَّثة. لنوع text/css و application/javascript -والتي هي ملفات الأنماط CSS وسكربتات JavaScript- سنضبط القيمة إلى max وهذا يعني أنَّ المتصفح سيُخزِّن هذه الملفات مؤقتًا أطول مدة يستطيعها، مما يُقلِّل عدد الطلبيات لوجود عدد كبير من هذه الملفات التي ترتبط بصفحة HTML. آخر خيار لنوع ‎~image/‎ وهو تعبيرٌ نمطيٌ (regular expression) الذي يُطابِق جميع الملفات ذات النوع الذي يحتوي على العبارة image/‎ فيه (مثل image/jpg و image/png). وكما في ملفات CSS و JS، تتواجد عادةً صورٌ كثير في مواقع الويب، ومن الجيد تخزينها محليًا، لذا ضبطنا القيمة إلى max أيضًا. التعليمة expires داخل قسم server (التي هي جزءٌ من وحدة headers) تضبط ترويسات التحكم بالتخزين المؤقت؛ حيث تستخدم القيمة المأخوذة من المتغير ‎$expires الذي يربط بين أنواع الملفات ومدة صلاحيتها. وبهذه الطريقة ستختلف الترويسات المُرسَلة اعتمادًا على نوع الملف. احفظ الملف واخرج من المُحرِّر النصي، وأعد تشغيل خادوم Nginx لتفعيل الضبط الجديد: sudo systemctl restart nginx سنتحقق في الخطوة التالية من أنَّ الضبط الجديد يعمل عملًا سليمًا. الخطوة الرابعة: اختبار التخزين المؤقت في المتصفح لنُنفِّذ نفس الأمر الذي طلبنا فيه ملف HTML في القسم السابق: curl -I http://localhost/test.html سيكون الرد مختلفًا هذه المرة، إذ يجب أن ترى ترويستين جديدتين: HTTP/1.1 200 OK Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:48:53 GMT Content-Type: text/html Content-Length: 1024 Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT Connection: keep-alive ETag: "57d40685-400" Expires: Thu, 01 Jan 1970 00:00:01 GMT Cache-Control: no-cache Accept-Ranges: bytes ترويسة Expires مرتبطة بتاريخٍ في الماضي وترويسة Cache-Control مضبوطةٌ إلى no-cache، مما يجعل المتصفح يسأل الخادوم إن توفرت نسخةٌ جديدةٌ من الملف (باستخدام ترويسة ETag كما سبق). ستجد ردًا مختلفًا عندما تُجرِّب على صورة: curl -I http://localhost/test.jpg ناتج الأمر السابق: HTTP/1.1 200 OK Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:50:41 GMT Content-Type: image/jpeg Content-Length: 1024 Last-Modified: Sat, 10 Sep 2016 13:11:36 GMT Connection: keep-alive ETag: "57d40688-400" Expires: Thu, 31 Dec 2037 23:55:55 GMT Cache-Control: max-age=315360000 Accept-Ranges: bytes ارتبطت الترويسة Expires في هذه المرة بتاريخٍ في المستقبل البعيد، واحتوت ترويسة Cache-Control على max-age التي تخبر المتصفح كم ثانية يجب أن يُبقي على الملف. وهذا يُخبِر المتصفح أن يُخزِّن الصورة المُنزَّلة مؤقتًا لأطول مدة ممكنة، وبالتالي إذا ظهرت الصورة مرةً أخرى فستُستعمَل النسخة المحلية ولن تُرسَل الطلبية إلى الخادوم بتاتًا. يجب أن تكون النتيجة مشابهةً لملفَي test.js و test.css لأنهما ملفا JavaScript و CSS ويُضبَط لهما ترويسات التخزين المؤقت أيضًا، مَثَلُهُما كَمَثل الصور. إن ظهر عندك مثلما عرضنا في أمثلتنا، فاعلم أنَّ ترويسات التحكم بالتخزين المؤقت قد ضُبِطَت ضبطًا صحيحًا وسيستفيد موقعك من ناحية الأداء، وسيقل الحِمل على خادومك نتيجةً لتخزين المتصفح للملفات محليًا وعدم طلبها من الخادوم. عليك الآن أن تُخصِّص إعدادات التخزين المؤقت اعتمادًا على محتوى موقعك، لكن قد تجد أنَّ الضبط الذي وفرناه في هذا الدرس مناسبًا لتبدأ منه. الخلاصة يمكن أن تُستعمَل وحدة headers لإضافة أي نوع من الترويسات إلى رد HTTP، لكن من أفضل تطبيقات هذه الوحدة هو ضبط ترويسات التحكم بالتخزين المؤقت. إذ سيساعد ذلك في تحسين أداء مواقع الويب المُستضافة على خادومك، خصوصًا في الشبكات ذات زمن التأخير المرتفع (نسبيًا) مثل شبكات الهواتف المحمولة. يمكن أن يؤدي ذلك إلى تحسين ظهور موقعك في محركات البحث التي تأخذ عامل سرعة الموقع بالحسبان. حيث يُعتَبَر ضبط ترويسات التخزين المؤقت من أبزر نصائح أدوات اختبار سرعة الصفحات من Google (أقصد Google PageSpeed). لمزيدٍ من المعلومات التفصيلية عن وحدة headers، فارجع إلى توثيق Nginx الرسمي. ترجمة -وبتصرّف- للمقال How to Implement Browser Caching with Nginx’s header Module on Ubuntu 16.04 لصاحبه Mateusz Papiernik.
×
×
  • أضف...