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

المساهمون
  • المساهمات

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

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

  • Days Won

    12

السُّمعة بالموقع

81 Excellent

9 متابعين

آخر الزُوّار

4,015 زيارة للملف الشّخصي
  1. سنستعمل في هذا الدرس تحريكات CSS بسيطة إضافةً إلى بعض شفرات JavaScript لإنشاء ساعة متحركة. سنُنشِئ هذه الساعة باستخدام HTML و CSS وخلفية SVG إضافةً إلى بعض أسطر JavaScript. سنستعمل التحريكات Animations أو الانتقالات Transitions في CSS، وسنعتمد على JavaScript لضبط التوقيت الابتدائي. ملحوظة: يتضمّن الملف المرفق شفرة الأمثلة المذكورة في هذا الدرس. شفرات HTML سنبدأ بإضافة بعض شفرات HTML. فكَّرتُ بداية الأمر باستخدام ثلاثة عناصر لكل عقرب من عقارب الساعة، ومن ثم وضعتُ تلك العناصر ضمن حاوية؛ وصحيحٌ أننا نستطيع استخدام العناصر الثلاثة بمفردها مع تحريكات CSS لكننا سنحتاج إلى الحاويات (Containers) لوضع العقارب في مكانها المناسب وتحريكها. <article class="clock"> <div class="hours-container"> <div class="hours"></div> </div> <div class="minutes-container"> <div class="minutes"></div> </div> <div class="seconds-container"> <div class="seconds"></div> </div> </article> لن نستخدم الشفرة أعلاه الآن، لكننا سنكتبها خطوة خطوة. بالنسبة للتنسيقات فسنعتمد على ملف التنسيقات assets/clocks.css في المرفق مع شرح العناصر الأساسية فيه. إن أردتَ رؤية النتيجة التي نريد الوصول إليها بنهاية الدرس فتمكنك معاينة المستند examples/1.html الموجود في الملف المرفق. واجهة الساعة نستخدم الشفرة التالية في ملف التنسيقات لإعطاء الساعة شكلها الدائري. .clock { border-radius: 50%; background: #fff url(ios_clock.svg) no-repeat center; background-size: 88%; height: 20em; padding-bottom: 31%; position: relative; width: 20em; } .clock.simple:after { background: #000; border-radius: 50%; content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 5%; height: 5%; z-index: 10; } أضفنا أيضًا دائرةً سوادء اللون في المنتصف وجعلناها «أعلى» من غيرها (عبر خاصية z-index) لكي تقع عقارب الساعة خلفها. نضع المحتوى التالي في ملف HTML: <link rel="stylesheet" href="assets/clocks.css"> <div class="demo-container clocks single"> <article class="clock simple show"></article> </div> إن فتحت الملف الناتج عن هذه الخطوة (examples\2.html) في المتصفح، والذي يحوي شفرة HTML التي تحصلنا عليها لحدّ الساعة، فستجد أنه يرسم شكل الساعة ولكن من دون عقارب. تضبط الشفرة التاليّة في ملف CSS موضع الحاويات قبل إضافة العقارب: .minutes-container, .hours-container, .seconds-container { position: absolute; top: 0; right: 0; bottom: 0; left: 0; } عقرب الساعات نضيف الحاويّة الخاصّة بعقرب الساعات، والعنصر الخاص بالعقرب نفسه إلى ملف HTML: <link rel="stylesheet" href="assets/clocks.css"> <div class="demo-container clocks single"> <article class="clock simple show"> <div class="hours-container"> <div class="hours"></div> </div> </article> </div> نحصُل على الملف examples/3.html. إن نظرت في ملف تنسيقات CSS فستجد أننا نستعمل القيمة absolute للخاصية position لجعل مكان العقرب مطلقًا. .hours { background: #000; height: 20%; left: 48.75%; position: absolute; top: 30%; transform-origin: 50% 100%; width: 2.5%; } كما أننا استخدمنا النسب المئوية لتسهيل إعادة تحجيم الساعات، مما يجعل من السهل ملء الشاشة بالساعة أو إظهارها على شاشات الهواتف المحمولة. ضبطنا أيضًا قيمة الخاصية transform-origin إلى أسفل العقرب لتمكين تدويره لاحقًا. عقرب الدقائق وهو يشبه عقرب الساعات، لكنه أطول وأضيق. نضيفه أولا إلى ملف HTML: <link rel="stylesheet" href="assets/clocks.css"> <div class="demo-container clocks single"> <article class="clock simple show"> <div class="hours-container"> <div class="hours angled"></div> </div> <div class="minutes-container" style="transform: rotateZ(6deg);"> <div class="minutes"></div> </div> </article> </div> نحصُل على المستند examples/4.html. يُنسَّق الصنف في CSS على النحو التالي: .minutes { background: #000; height: 40%; left: 49%; position: absolute; top: 10%; transform-origin: 50% 100%; width: 2%; } عقرب الثواني أما عقرب الثواني، فهو أكثر ضيقًا من سابقه، لكنه يمتد إلى ما بعد منتصف الساعة. نضيف عنصر العقرب ضمن حاويتّه فنحصُل على examples/5.html: <link rel="stylesheet" href="assets/clocks.css"> <div class="demo-container clocks single"> <article class="clock simple show"> <div class="hours-container"> <div class="hours angled"></div> </div> <div class="minutes-container" style="transform: rotateZ(6deg);"> <div class="minutes angled"></div> </div> <div class="seconds-container"> <div class="seconds"></div> </div> </article> </div> لتنسيق طول عقرب الثواني وضيقه ضبطنا قيمة الخاصية transform-origin إلى 80%، مما يبقي 20% من العقرب ممتدةً بعيدًا عن المنتصف. .seconds { background: #000; height: 45%; left: 49.5%; position: absolute; top: 14%; transform-origin: 50% 80%; width: 1%; z-index: 8; } لدينا الآن ساعة دائرية بالعقارب المطلوبة: الساعات، الدقائق والثواني؛ إلا أنّ هذه العقارب ثابتة ولا تتحرك كما يجدر بها أن تفعل. سنضبُط هذا الأمر في الفقرات التالية. الحركة تنتقل عقارب بعض أنواع الساعات كل ثانية محدثةً صوتًا، وأخرى تتحرك عقاربها بسلاسة. لنفعل كلا الأمرين ولنبدأ بتحريك العقارب بسلاسة أولًا. استخدمنا في كلتا الحالتين التعليمة keyframes لتحريك العقارب 360 درجة: @keyframes rotate { 100% { transform: rotateZ(360deg); } } عقرب الساعات يتحرك بخطوات متصلة ستطلب الشِفرة السابقة من العنصر أن يدور 360 درجة إذا استخدمنا الخاصية animation في صنف CSS المطبّق على عنصر HTML. يتيح استخدام دالة الزمن linear أن تكون الحركة خطيّةً وسلسةً: .hours-container { animation: rotate 43200s infinite linear; } .minutes-container { animation: rotate 3600s infinite linear; } .seconds-container { animation: rotate 60s infinite linear; } سندوِّر عقرب الساعات hours مرةً كل 12 ساعة (أي 43200 ثانية)، وسيدور عقرب الدقائق دورةً كاملةً كل ساعة (3600 ثانية)، أما عقرب الثواني فيكمل دورته كل دقيقة (60 ثانية). ينتُج لدينا مستند examples/6.htmlوالذي يحوي شفرة HTML التالية: <link rel="stylesheet" href="assets/clocks.css"> <div class="demo-container clocks single linear"> <article class="clock simple show"> <div class="hours-container"> <div class="hours angled"></div> </div> <div class="minutes-container" style="transform: rotateZ(6deg);"> <div class="minutes angled"></div> </div> <div class="seconds-container"> <div class="seconds"></div> </div> </article> </div> إذا كنتَ ذا بصرٍ ثاقب، فستلاحظ حركة عقرب الدقائق البطيئة جدا؛ حيث سيحتاج إلى ساعةٍ كاملةٍ لإكمال دورانه؛ أما عقرب الثواني فسيحتاج إلى 60 ثانية فقط مما يُسهِّل ملاحظته. عقرب الثواني يتحرك بخطوات منفصلة يمكننا جعل حركة العقارب كما في الساعات التقليدية بتحريك عقرب الثواني 60 حركة منفصلة في الدقيقة. إحدى أبسط الطرائق لفعل ذلك هي استخدام دالة التوقيت steps، أي ستصبح الخاصية animation كما يلي: .minutes-container { animation: rotate 3600s infinite steps(60); } .seconds-container { animation: rotate 60s infinite steps(60); } ونعدّل ملف HTML لنحصُل على مستند examples/7.html ذي المحتوى التالي (لاحظ استخدام الصنف steps مكان الصنف linear): <link rel="stylesheet" href="assets/clocks.css"> <div class="demo-container clocks single steps"> <article class="clock simple show"> <div class="hours-container"> <div class="hours angled"></div> </div> <div class="minutes-container" style="transform: rotateZ(6deg);"> <div class="minutes angled"></div> </div> <div class="seconds-container"> <div class="seconds"></div> </div> </article> </div> أصبح عقربا الساعة يتحركان 60 حركة منفصلة لإتمام الدورة، وسيحسب المتصفح تلقائيًا ما هي المسافة المقطوعة في كل خطوة. إظهار الوقت الصحيح صحيحٌ أنَّ الساعات السابقة جميلة، لكنها لا تعرض الوقت الحالي. يمكننا عرض الوقت الصحيح باستخدام بعض شِفرات JavaScript: /* * البدء بالوقت المضبوط في جهاز المستخدم * From: cssanimation.rocks/clocks */ function initLocalClocks() { // Get the local time using JS var date = new Date; var seconds = date.getSeconds(); var minutes = date.getMinutes(); var hours = date.getHours(); // إنشاء كائن لعقارب الساعة لثلاثة: الساعات، الدقائق والثواني var hands = [ { hand: 'hours', angle: (hours * 30) + (minutes / 2) }, { hand: 'minutes', angle: (minutes * 6) }, { hand: 'seconds', angle: (seconds * 6) } ]; // ضبط زوايا العقارب for (var j = 0; j < hands.length; j++) { var elements = document.querySelectorAll('.' + hands[j].hand); for (var k = 0; k < elements.length; k++) { elements[k].style.webkitTransform = 'rotateZ('+ hands[j].angle +'deg)'; elements[k].style.transform = 'rotateZ('+ hands[j].angle +'deg)'; // إذا تعلق الأمر بعقرب الدقائق فإننا نحفظ الثواني لحساب موضع عقرب الدقائق في ما بعد if (hands[j].hand === 'minutes') { elements[k].parentNode.setAttribute('data-second-angle', hands[j + 1].angle); } } } } هذه الدالة تحوِّل الوقت الحالي (الساعات والدقائق والثواني) إلى الزاوية الموافقة له لكل عقربٍ من عقارب الساعة، ومن ثم ستُطبِّق الزاوية الصحيحة لكل عقرب على حدة باستخدام القيمة rotateZ على الخاصية style.transform. نضيف إلى مستند HTML قيما مبدئيّة للخاصيّتين style.transform وdata-second-angle (التي تحفظ الثواني لحساب موضع عقرب الدقائق). بدون هذه القيم لن تجد شفرة جافاسكربت السابقة العناصر لتنفيذ التعليمات عليها. نحصُل في ختام هذه الخطوة على المستند examples/8.html (الذي يستدعي ملف جافاسكريبت clocks.js حيث توجد دوال جافاسكربت السابقة). سيعمل ما سبق في أغلبية المتصفحات دون مشاكل، عدا متصفح Safari الذي علينا استعمال سابقة (prefix) خاصة معه، ولهذا السبب سنستعمل الخاصية style.webkitTrasform أيضًا. مزامنة حركة عقرب الثواني والدقائق علينا الحرص على تحريك عقرب الدقائق عندما يبلغ عقرب الثواني الساعة الثانية عشرة، كما في الصورة المتحركة الآتية. عندما تُرسَم الساعة على الشاشة لأوّل مرة، فيجب تحريك عقرب الدقائق بعد أقل من دقيقة واحدة، ولفعل ذلك علينا حساب متى ستنتهي أوّل دقيقة ومن ثم تحريك العقرب يدويًا. ولمّا كنّا نستعمل JavaScript لضبط الحركة الأوليّة، فسنستمر باستخدامها عبر تحريك العقرب بمقدار ست درجات كل دقيقة باستخدام الدالة setInterval. وقبل تحريك عقرب الدقائق، علينا حساب كم تبقى من الدقيقة الحالية. ربما لاحظتَ هذه الأسطر في الشِفرة السابقة: if (hands[j].hand === 'minutes') { elements[k].parentNode.setAttribute('data-second-angle', hands[j + 1].angle); } الأسطر السابقة تختبر إن كان العقرب هو عقرب الدقائق، وإذا تحقق ذلك فسنضبط خاصيةً باسم data-second-angle تحتوي على الزاوية الحالية لعقرب الثواني. يمكننا الاستفادة من هذه المعلومات لتحريك عقرب الدقائق. /* ضبط المدة المتبقية من الدقيقة عند ظهور الساعة لأول مرة، ثم تحريك العقرب كل دقيقة بعد انتهاء الدقيقة التي ظهرت فيها الساعة على الصفحة. */ function setUpMinuteHands() { // إيجاد القدر المتبقي من الدقيقة عند ظهور الساعة var containers = document.querySelectorAll('.minutes-container'); var secondAngle = containers[0].getAttribute("data-second-angle"); if (secondAngle > 0) { // ضبط عدّاد ينتهي مع الدقيقة الحالية من أجر تحريك عقرب الدقائق var delay = (((360 - secondAngle) / 6) + 0.1) * 1000; setTimeout(function() { moveMinuteHands(containers); }, delay); } } /* * تدوير العقرب بالنسبة للدقيقة الأولى */ function moveMinuteHands(containers) { for (var i = 0; i < containers.length; i++) { containers[i].style.webkitTransform = 'rotateZ(6deg)'; containers[i].style.transform = 'rotateZ(6deg)'; } // الاستمرار بعد الدقيقة الأولى في تدوير عقرب الدقائق كل 60 ثانية setInterval(function() { for (var i = 0; i < containers.length; i++) { if (containers[i].angle === undefined) { containers[i].angle = 12; } else { containers[i].angle += 6; } containers[i].style.webkitTransform = 'rotateZ('+ containers[i].angle +'deg)'; containers[i].style.transform = 'rotateZ('+ containers[i].angle +'deg)'; } }, 60000); } إضافة ارتداد للعقارب علينا حذف خاصية الحركة في CSS لاستخدامنا JavaScript لضبط مختلف جوانب الحركة؛ لكن بدلًا من حذفها فقط، سنضع بدلًا منها انتقالًا (transition)؛ وهذه فرصةٌ ملائمةٌ لإضافة خصائص أخرى للحركة. عندما نضبط زاويةً جديدةً في JavaScript للعقرب، فسيُطبّق انتقالٌ (transition) في CSS الذي سيخبر المتصفح عن الحركة، وهذا يعني أنَّ شِفرة JavaScript ستتعامل مع تغيرات بسيطة في الزاوية وسيتكفّل المتصفح بعملية التحريك. قبل فعل ذلك، علينا تحديث الشِفرة لاستخدام JavaScript لتحريك عقرب الثواني أيضًا، حيث سنستخدم الشِفرة الآتية لتحريك حاوية عقرب الثواني ستين مرة في الدقيقة: /* * تحريك حاويات الثواني */ function moveSecondHands() { var containers = document.querySelectorAll('.seconds-container'); setInterval(function() { for (var i = 0; i < containers.length; i++) { if (containers[i].angle === undefined) { containers[i].angle = 6; } else { containers[i].angle += 6; } containers[i].style.webkitTransform = 'rotateZ('+ containers[i].angle +'deg)'; containers[i].style.transform = 'rotateZ('+ containers[i].angle +'deg)'; } }, 1000); } بعد أن أصبحت حركة عقارب الثواني والدقائق مدارةً من JavaScript، فعلينا تحديث شِفرة CSS لنستبدل الخاصية transitions بالخاصية animation: .minutes-container { transition: transform 0.3s cubic-bezier(.4,2.08,.55,.44); } .seconds-container { transition: transform 0.2s cubic-bezier(.4,2.08,.55,.44); } هذه الانتقال الذي طبّقناه على خاصية transform يستعمل دالة التوقيت cubic-bezier، وهذه الدالة ستعطي العقرب حركةً ارتداديةً إلى الخلف قبل الدوران باتجاه عقارب الساعة. يمكنك تجربة التأثيرات المضافة في هذه الفقرة بمعاينة الملف examples/9.html. محاكاة الساعة الموجودة في نظام iOS 7 أنا أحد معجبي بساطة الساعة المستعملة في نظام iOS 7؛ وبإعادة تنسيق العقارب وبإضافة صورة خلفية مع الأرقام، فسنتمكن بسهولة من إنشاء مثل هذه الساعة، كما في المثال examples/10.html. هذا التصميم مبنيٌ على تصميم ساعة Swiss railway clock الشهيرة، يمكننا تعديل ساعتنا لتشبهها عبر تغيير الأنماط المستعملة واستعمال صورة خلفية جديدة، كما في المثال examples/11.html. توافق المتصفح يمكن للمتصفحات الحديثة التعامل مع الانتقالات والحركات في CSS بما في ذلك متصفح IE 10+‎، والإصدارات الحديثة من متصفحَي Chrome و Firefox اللذين يدعمانهما دون سابقة (prefix)، أما Safari فعلينا استعمال السابقة ‎-webkit معه. رأينا في هذا الدرس كيفية إنشاء ساعة والتحكم في عقاربها باستخدام تأثيرات CSS وتنفيذ تعليمات جافاسكريبت. يمكنك الاستفادة من الأمثلة المذكورة في الدرس لإنشاء ساعات مخصّصة لأغراضك. إن كنت تفكّر ساعات بمناطق زمنيّة مختلفة مثل تلك التي نراها في الفنادق فيمكنك الاستعانة بمكتبتي Moment.js وMoment.js Timezone. ترجمة –وبتصرّف– للمقال Clocks لصاحبه Donovan Hutchinson. حقوق الصورة البارزة محفوظة لـ Freepik
  2. وجدتُ العمل كمطوّر على ووردبريس قليل المتاعب، فإنتاج مواقع وِب جميلة هو خدمةٌ رائعةٌ نقدمها للآخرين وأمرٌ لا يؤدي إلى كارثة إذا لم تتقنه تمامًا بادئ الأمر؛ حيث لا يُشابه العمل طيارا حربيا أو في حرس الحدود! لكن مع ذلك، هنالك استثناءات: ففي بعض الأحيان سبَّب لي عملي مشاكل وكان شعوري بالذنب وتأنيب الضمير سيؤدي بي إلى ترك عملي. ولاحظتُ أنَّ أسباب ذلك هي نفسها، ولقد وضعتها في رسمٍ توضيحي، لذا سأترك المخطط ليتحدث عنها: رغبتُ بترك عملي مرات عدّة عندما أثرّتُ على تقييم موقع العميل في محركات البحث؛ فأخطاء SEO لها وقعٌ كبيرٌ نظرا لأسباب كثيرة منها: يمكنها أن تكلِّف عميلك قدرا كبيرا من المال، فلو كان موقع عميلك هو مصدر دخله الرئيسي، فأخطاء SEO ستؤثر على دخله الشهري، أو تدمره (وبالتالي نمط حياته). يسهل كثيرًا الوقوع بهذه الأخطاء، خصوصًا لو كنتَ على درايةً بتطوير مواقع ووردبريس، لكن معلوماتك عن بنية الإنترنت ومحركات البحث محدودة. إذا ارتكبتَ خطأً كبيرًا، فمن شبه المستحيل إصلاحه! فعلى عميلك أن ينفق أشهرًا من وقته في إعادة بناء موقعه بمساعدة خبير SEO بعد خسارته لتقييمه العالي، ولا يوجد شيءٌ يمكنك أن تفعله للمساعدة. سنناقش في هذا الدرس إحدى الأمور التي أرى المطورين غير المتمرسين يفعلونها بعملائهم، ألا وهي عدم استخدام إعادة التوجيه للروابط التي جرى تغييرها، خصوصًا عند تحويل موقع إلى ووردبريس من تقنية أخرى. المفاهيم الأساسية عند استخدام إعادة التوجيه هذا القسم ليس دليلًا شاملًا لإعادة التوجيه، وإنما يشرح الأفكار الأساسية التي يتعامل Google بناء عليها مع صفحات الويب وروابطها، ولماذا علينا أن نسعى محاولين إرضاءه عند تغيير روابط الصفحات. تقييمات البحث يعمل محرِّك Google للبحث داخليًا عبر تقييم الصفحات التي يمكن البحث عنها، وقد يُظهِر محرِّك Google إحدى الصفحات كأوّل نتيجة عند البحث اعتمادًا على معايير كثيرة، ويُظهِر صفحةً أخرى كثاني نتيجة، وهلم جرًا. ولأنَّ محرِّك البحث Google يستقبل شهريًا مليار عملية بحث، فتقييم إحدى صفحات الموقع في Google تقييمًا مرتفعًا يعني الحصول على عدِّد هائل من الزيارات إلى موقعك، والذي بدوره يعني إمكانية بيع الكثير من المنتجات، أو الضغط على الإعلانات، أو توقيع العرائض، أو أيّ أمر آخر يجعلك تجني المال أو تُحقِّق هدفك من الموقع. التقنية المستعملة لمساعدة مواقع معيّنة لتحسين تقييمها في محرِّك Google (وغيره من محركات البحث الأقل شهرة) تسمى «Search Engine Optimization» (التهيئة لمحركات البحث) اختصارًا SEO، وإذا قلنا «تقييم SEO لإحدى الصفحات» فنحن نشير إلى تقييم الصفحة عند محرِّك Google. الروابط الدائمة لا يُقيّم Google الأداء بناءً على كامل صفحات الموقع، وإنما لكل صفحةٍ على حدة. فلنقل مثلا أنّ لدينا صفحة رابطها example.com/great-optimized-page ذات أداءٍ جيد (لسببٍ من الأسباب) وتظهر في أعلى قائمة النتائج في إحدى عبارات البحث الشهيرة؛ مما يؤدي إلى توجيه زيارات كثيرة إلى تلك الصفحة، وقد تكون تلك الصفحة بمفردها مسؤولةً عن أغلبية زيارات (وبالتالي دخل) الموقع، حتى لو لم تكن بقية صفحات الموقع ذات أداء جيد في محرِّكات البحث. الرابط الدائم(paramlink وهي كلمةٌ مركّبةٌ أصلها «permanent link») هو عنوان URL الذي يُفترَض أنَّه الرابط الدائم لإحدى الصفحات. ففي مثالنا السابق، كان الرابط الدائم للصفحة هو example.com/great-optimized-page. يُرسِل محرِّك Google الزوار إلى صفحتك عبر الرابط الدائم، فلو كان يوصي Google بأحد المطاعم إلى كثير من زواره، لكن رابط الصفحة لم يعد يعمل فجأة، ورَجَعَ الزوار إلى Google مشتكين من عدم وجود تلك الصفحة، فسيتوقف Google عن إظهار تلك الصفحة في نتائج البحث. ما هي إعادة التوجيه؟ يشرح Google لنا معنى إعادة التوجيه شرحًا جيدًا، لذا لنبدأ باقتباسٍ من عندهم: علاقة إعادة التوجيه بالتهيئة لمحركات البحث وكما في مكاتب البريد، سيُسعَد Google بتحديث سجلاته عندما تُرسِل له استمارة «تغيير العنوان» (وفي حالتنا، هذه «الاستمارة» هي إعادة التوجيه ذات الرمز 301)، وبعد فترةٍ قصيرةٍ من تغييرك للرابط الدائم لصفحتك وضبط إعادة توجيه من الرابط القديم إلى الجديد، فسيبدأ Google بربط نتائج البحث إلى عنوان صفحتك الجديدة، وهذا ما يسمح للصفحة ذات الرابط الجديد بالاحتفاظ بتقييم الرابط القديم نفسه. ماذا يحدث إذا لم تضبط إعادة التوجيه؟ حسنًا لو لم يُغيّر أحدنا عنوان منزله بعد انتقاله، فستذهب الكثير من «المعلومات» المهمة (مثل الدعوات، واستمارات الضرائب، والرسائل البريدية …) إلى عنوانٍ قديم، وستُعاد الكثير من الطرود إلى مكتب البريد، ولن تحصل على أيٍّ منها! هذا ينطبق أيضًا على عناوين الويب، فلو تغيّر عنوان example.com/great-optimized-page دون تمرير الزيارات إلى رابطٍ آخر، فإنّ جميع الزيارات إلى ذاك العنوان لن تكتمل، فلم تعد الصفحة متوفرة في ذاك العنوان، لذا ستظهر رسالة الخطأ الشهيرة «404» عند محاولة زيارة الصفحة، وهذا الرمز يعني «لا توجد صفحة مرتبطة بهذا العنوان». لا يحب Google إرسال الزوار إلى روابط «ميتة»، لذا سيحذف الصفحة example.com/great-optimized-page من سجلاته، ويسمح لبقية الصفحات التي تناقش الموضوع نفسه بأخذ مكان هذه الصفحة؛ وهذا يعني أنَّ السنوات الطوال التي أمضيناها في تطوير الصفحة للحصول على تقييم عال في Google قد ضاعت، ومن المرجح أنها ضاعت للأبد! كيف يبدو الضرر الذي أُحدِثَ في تقييم SEO لموقع العميل إذا نقلتَ لتوِّك الموقع إلى ووردبريس، وكان مبنيا على تقنية قديمة منذ عقود متضمنًا نظام تجارة إلكترونية لا يعمل تمامًا، لكن لمّا كان الموقع قديما ومتخصصا في تقديم خدماتٍ ممتازة في مجالٍ معيّن، فتقييمه أصبح مرتفعًا جدًا في Google. أما موقع ووردبريس فهو جميل، ويُحمَّل بسرعة، وإدارته أسهل، ويبدو بمظهرٍ رائعٍ على الهواتف؛ لكن بعد نقلك لجميع منتجات الموقع إلى WooCommerce فلم تُعِد توجيه الروابط الدائمة لصفحات الموقع القديمة. مرّت ستة أسابيع على إطلاق الموقع الجديد، ثم أتتك رسالةٌ عبر البريد الإلكتروني من عميلك قائلًا فيها: «لقد أحببتُ الموقع الجديد، لكنني نظرتُ في تحليل Google Analytics ووجدتُ أنَّ الموقع لا تأتيه زيارات من محرِّك البحث؛ إضافةً إلى انخفاض مبيعاتي من 10 آلاف دولار شهريًا قبل الانتقال إلى الموقع الجديد إلى حوالي 500 دولار بعد الانتقال إلى الموقع الجديد. هلّا أخبرتني ماذا يحدث؟». لقد أدركتَ خطأك هنا! فصفحات الموقع القديمة ذات التقييم العالي أصبحت تعرض الخطأ 404 (الصفحة غير موجودة) ولم تُجرِ عملية إعادة توجيه إلى الروابط الدائمة في موقعك الجديد الذي يستعمل ووردبريس؛ لكن للأسف لقد فات الأوان، فذهبت التقييمات العالية لموقع العميل، وحذفه Google من سجلاته، وأصبح عميلك يخسر آلاف الدولارات شهريًا بسبب خطئك، وأمسيتَ تفكِّر بإلقاء حاسوبك من النافذة والانضمام إلى مدرسة تدريب الطيارين الحربيين. لا تحسبنّ قصة الرعب السابقة غير حقيقية، بل هي شائعةٌ أكثر مما تتوقع. وفقدان تقييم SEO لصفحات موقع العميل هو أخطر الأمور التي قد تحدث عند تطوير المواقع. سنتحدث في الجزء الثاني من هذه المثال عن طرائق تفادي حدوث مثل هكذا قصص، بمختلف حالات استخدام ووردبريس. ترجمة –وبتصرّف– للمقال Understanding 301 Redirects in WordPress لصاحبه Fred Meyer.
  3. عندما يتعلق الأمر ببناء نوع معيّنٍ من تطبيقات الويب، فأوّل ما يخطر ببالي هو استخدام ووردبريس! ومن بين المشاريع البرمجية التي عملتُ عليها في السنتين الماضيتين، تطلّب نصفها - على الأقل - إمكانية إدارة حسابات المستخدمين، وهو ما يعني عادةً السماح للمستخدمين بإنشاء حساباتهم، بإدخال بعض البيانات في حقلَي الاسم وعنوان البريد الإلكتروني، ومن ثم ستُرسَل إليهم رسالةٌ بريديةٌ لتفعيل حسابهم بعد إرسال النموذج. توفِّر ووردبريس طريقةً سهلةً لإدارة المستخدمين عبر لوحة التحكم، وإذا كان موقعك تعليميًا أو كنتَ تستعمله مدونةً لك، فلا حاجة إلى إنشاء آليةٍ أخرى لإدارة المستخدمين، لكن إذا كنتَ تبني تطبيقًا متكاملًا، فهنالك طرائق أخرى للتعامل مع حسابات المستخدمين. لنقل مثلًا أنَّ المُصمِّم قد وضع طريقةً معينةً لإدارة الحسابات في الموقع، وإذا أجبرتَ المستخدمين على استخدام لوحة التحكم فستتسبَّب بتخريب تجربة المستخدم، أليس كذلك؟ هنالك طرائقٌ أفضل لتسجيل حسابات المستخدمين وإدارتها بدلًا من استخدام لوحة التحكم التابعة لووردبريس، وسنناقش في هذا الدرس كيفية إنشاء حسابات المستخدمين برمجيًا، وسنلقي نظرةً على النقاط الآتية: الحصول على معلومات المستخدم بصيغةٍ معيّنة، التعرّف على المعلومات اللازمة لإنشاء حساب مستخدم، إنشاء الحساب، التأكد أنَّ شيفرتنا تخضع لمعايير كتابة الشفرات القياسية، وأنها سهلة القراءة والصيانة. لا تقلق، لن يكون هذا الدرس طويلا ومملًّا، أعدك بذلك! إنشاء حسابات مستخدمي ووردبريس يحتاج أحدنا إلى المعلومات الآتية –على الأقل– لإنشاء حساب مستخدم جديد: اسم المستخدم كلمة المرور عنوان البريد الإلكتروني عندما يتعلق الأمر بإنشاء اسم المستخدم، فأنا أفضِّل استخدام البريد الإلكتروني اسمًا للمستخدم لأنني أضمن أنَّه فريد ولن يتكرر مرةً أخرى؛ ولهذا سنستخدمه في هذا الدرس؛ وسنتحدث عن توليد كلمة المرور لاحقًا. أضف إلى ذلك ضرورة افتراض أنَّ البيانات ستأتيك بأيّ صيغة: فربما تكون بصيغة JSON، أو تكون حقولًا مفصولةً بفواصل كما في CSV، وقد تأتي بصيغة XML. بغض النظر عن الصيغة المستعملة، فيجدر بك كتابة الشفرات اللازمة لتفسير تلك المعلومات باستخدام PHP لكي تستطيع التعامل معها لاحقًا. ولتبسيط الشيفرات المعروضة في هذا الدرس، سنفترض وجود تسجيلة Record لمستخدمٍ وحيدٍ مخزنةٌ معلوماته في مصفوفة. هذا لا يعني أنَّ المعلومات ستأتيك مباشرةً على شكل مصفوفة جاهزة، وإنما عليك تحويلها برمجيًا إلى واحدة. أولًا: معلومات المستخدم لنفترض أنَّنا سنضيف شخصًا باسم Meghan إلى نظامنا، ولدينا عنوان بريده الإلكتروني. لكن لا بأس بذلك، فلدينا المعلومات الأساسية التي نريدها: <?php $user_info = array( 'email' => 'meghan@emaildomain.com', 'first_name' => 'Meghan', 'last_name' => 'McFarlin', ); ولنحوِّلها إلى شيءٍ نستطيع من خلاله إنشاء حسابٍ له. ثانيًا: إنشاء الحساب أوّل ما علينا فعله هو التحقق من عدم وجود مستخدم مرتبط بعنوان البريد الإلكتروني نفسه، وإذا حدث ذلك فسنستعمل التعليمة return فقط، لكنك قد ترغب بإظهار رسالة خطأ أو أي نوع من التنبيهات لتخبر المستخدم أنَّك لا تستطيع إنشاء هذا الحساب لوجوده من قبل؛ لكن ذلك خارجٌ عن نطاق هذا الدرس: <?php // التحقق من صحة البريد الإلكتروني الذي أدخله المستخدم وعدم وجوده مسبقًا في قاعدة البيانات $email = $user_info['email']; if ( ! filter_var( $email, FILTER_VALIDATE_EMAIL ) || username_exists( $email ) ) { return; } لنفترض في هذا الدرس أنَّ المستخدم غير موجود، وعلينا إنشاء حساب له؛ ولفعل ذلك سنحتاج إلى عنوان البريد الإلكتروني إضافةً إلى كلمة مرور؛ ولحسن الحظ، توليد كلمات المرور سهلٌ جدًا: <?php $password = wp_generate_password( 16, false ); لنأخذ البريد الإلكتروني الذي أدخله المستخدم وكلمة المرور التي ولّدناها ولنُنشِئ الحساب: <?php // الحصول على البريد الإلكتروني وتوليد كلمة المرور $email = $user_info['email']; $password = wp_generate_password( 12, false ); // إنشاء حساب المستخدم $user_id = wp_create_user( $email, $password, $email ); // ضبط امتيازات أو دور المستخدم $user = new \WP_User( $user_id ); $user->set_role( 'author' ); لاحظ في الشيفرة السابقة أننا ضبطنا دور المستخدم (role)، وذلك عبر الحصول على نسخةٍ من الصنف Wp_User ثم استخدام مُعرِّف المستخدم المُخزَّن في المتغير ‎$user_id لضبط الدور الخاص به. استخدمتُ دور الكاتب Author في المثال السابق، لكن هنالك أدوار ووردبريس أخرى يمكنك اختيار أحدها. ثالثًا: ألا توجد خطواتٌ أخرى؟! أصبح حساب المستخدم جاهزًا عند هذه المرحلة؛ دعني أذكِّرُكَ أنَّ جزءًا كبيرًا من عملية إنشاء المستخدم متعلقٌ بكيفية استقبال البيانات، ومن ثم معالجتها، والطريقة التي تسمح بها لمدير الموقع بإنشاء الحسابات. هذه الأمور مهمة، لكنني وعدتُكَ أن أبقي هذا الدرس قصيرًا ومركَّزًا، وتلك الأمور ترتبط ارتباطًا وثيقًا بشكل موقعك وتجربة المستخدم فيه، أي أنها تختلف من موقعٍ لآخر. الخلاصة لقد تعلمنا في هذا الدرس طريقة إنشاء مستخدمي ووردبريس برمجيًا، عبر تجربتنا لذلك على حساب مستخدمٍ واحد. إذا أردتَ إنشاء مستخدمين عدّة، فعليك إنشاء مصفوفات فيها معلومات المستخدمين، والتي تستطيع الدوران عليها عبر حلقة تكرار وتُنشِئ الحسابات كما فعلنا في هذا الدرس. ترجمة –وبتصرّف– للمقال Programmatically Creating WordPress Users لصاحبه Tom McFrlin. حقوق الصورة البارزة محفوظة لـ Freepik
  4. تمهيد الدالة str.format()‎ المتوافرة للسلاسل النصية تسمح لك باستبدال المتغيرات وتنسيق القيم. مما يمنحك القدرة على تجميع العناصر مع بعضها عبر إدخالها في مواضع معينة. سيشرح لك هذا الدرس أشهر الاستخدامات لآلية تنسيق السلاسل النصية في بايثون، والتي ستساعدك في جعل شيفرتك وبرنامجك أسهل قراءةً واستخدامًا. استخدام «المُنسِّقات» تعمل المُنسِّقات (formatters) بوضع حقول قابلة للاستبدال تُعرَّف عبر وضع قوسين معقوفين {} في السلسلة النصية ثم استدعاء الدالة str.format()‎، إذ ستُمرَّر القيمة التي تريد وضعها ضمن السلسلة النصية إلى الدالة format()‎ وستوضع هذه القيمة في نفس مكان الحقل القابل للاستبدال الموجود في السلسلة الأصلية عندما تُشغِّل برنامجك. لنطبع سلسلةً نصيةً تستخدم «مُنسِّقًا» (formatter): print("Sammy has {} balloons.".format(5)) الناتج: Sammy has 5 balloons. أنشأنا في المثال السابق سلسلةً نصيةً تحتوي على قوسين معقوفين: "Sammy has {} balloons." ثم أضفنا الدالة str.format()‎ ومررنا إليها القيمة الرقمية 5 وهذا يعني أنَّ القيمة 5 ستوضع مكان القوسين المعقوفين: Sammy has 5 balloons. يمكننا أيضًا إسناد السلسلة النصية الأصلية التي تحتوي مُنسِّقًا إلى متغير: open_string = "Sammy loves {}." print(open_string.format("open source")) الناتج: Sammy loves open source. أضفنا في المثال السابق السلسلة النصية "open source" إلى سلسلةٍ نصيةٍ أكبر باستبدالها للقوسين المعقوفين الموجودَين في السلسلة الأصلية. تسمح لك المُنسِّقات في بايثون باستخدام الأقواس المعقوفة لحجز أماكن للقيم التي ستمررها مستقبلًا عبر الدالة str.format()‎. استخدام المُنسِّقات لحجز أكثر من مكان يمكنك استخدام أكثر من زوج من الأقواس المعقوفة عند استعمال المُنسِّقات؛ فيمكنك أن تضيف سلسلةً نصيةً أخرى إلى المثال السابق وذلك بإضافة زوج آخر من الأقواس المعقوفة وتمرير قيمة ثانية إلى الدالة كما يلي: new_open_string = "Sammy loves {} {}." # {} مكانين محجوزين عبر print(new_open_string.format("open-source", "software")) # تمرير قيمتين إلى الدالة مفصولٌ بينهما بفاصلة الناتج: Sammy loves open-source software. أضفنا زوجًا آخر من الأقواس المعقوفة إلى السلسلة النصية للسماح بوضع قيمة ثانية، ثم مررنا سلسلتين نصيتين إلى الدالة str.format()‎ مفصولٌ بينهما بفاصلة. سنضيف عمليات استبدال أخرى عبر اتباع نفس الآلية التي شرحناها أعلاه: sammy_string = "Sammy loves {} {}, and has {} {}." print(sammy_string.format("open-source", "software", 5, "balloons")) الناتج: Sammy loves open-source software, and has 5 balloons. إعادة ترتيب المنسقات عبر المعاملات الموضعية عندما نترك الأقواس المعقوفة دون معاملات (parameters) ممررة إليها، فستضع بايثون القيم المُمرَّرة إلى الدالة str.format()‎ بالترتيب. هذا تعبيرٌ فيه زوجين من الأقواس المعقوفة يوضع مكانهما سلسلتان نصيتان شبيهٌ بما رأيناه سابقًا في هذا الدرس: print("Sammy the {} has a pet {}!".format("shark", "pilot fish")) الناتج: Sammy the shark has a pet pilot fish! اُستبدِل أوّل زوجٍ من الأقواس المعقوفة ووضعت مكانه القيمة "shark"، ووضعت القيمة "pilot fish" مكان الزوج الثاني من الأقواس. القيم التي مررناها إلى الدالة str.format()‎ كانت بهذا الترتيب: ("shark", "pilot fish") إن سبق لك دراسة أنواع البيانات المُختلفة على بايثون فقد تلاحظ أنَّ القيمة السابقة هي من النوع tuple، ويمكن الوصول إلى كل قيمة موجودة فيها عبر فهرسٍ رقميٍ تابعٍ لها، والذي يبدأ من الفهرس 0. يمكننا تمرير أرقام الفهارس إلى داخل القوسين المعقوفين: print("Sammy the {0} has a pet {1}!".format("shark", "pilot fish")) سنحصل بعد تنفيذ المثال السابق على نفس الناتج التي ظهر دون تحديد أرقام الفهارس يدويًا، وذلك لأننا استدعينا القيم بالترتيب: Sammy the shark has a pet pilot fish! لكن إن عكسنا أرقام الفهارس في معاملات الأقواس المعقوفة فسنتمكن من عكس ترتيب القيم المُمرَّرة إلى السلسلة النصية الأصلية: print("Sammy the {1} has a pet {0}!".format("shark", "pilot fish")) الناتج: Sammy the pilot fish has a pet shark! لكن إن حاولت استخدام الفهرس ذي الرقم 2 ولم تكن لديك إلا قيمتين موجودتين في الفهرسين 0 و 1، فأنت تستدعي قيمةً خارج المجال المسموح، ولهذا السبب ستظهر رسالة خطأ: print("Sammy the {2} has a pet {1}!".format("shark", "pilot fish")) الناتج: IndexError: tuple index out of range تُشير رسالة الخطأ إلى وجود قيمتين فقط ومكانهما هو 0 و1، لذا كان الفهرس 2 غير مرتبطٍ بقيمةٍ وكان خارج المجال المسموح. لنضف الآن مكانين محجوزين إلى السلسلة النصية ولنمرر بضع قيم إلى الدالة str.format()‎ لكي نفهم آلية إعادة الترتيب فهمًا تامًا. هذه هي السلسلة النصية الجديدة التي فيها أربعة أزواج من الأقواس المعقوفة: print("Sammy is a {}, {}, and {} {}!".format("happy", "smiling", "blue", "shark")) الناتج: Sammy is a happy, smiling and blue shark! ستوضع القيم المُمرَّرة إلى الدالة str.format()‎ بنفس ترتيب ورودها في حال لم نستعمل المعاملات داخل الأقواس المعقوفة. تملك السلاسل النصية المُمرَّرة إلى الدالة str.format()‎ الفهارس الآتية المرتبطة بها: “happy” “smiling” “blue” “shark” 0 1 2 3 لنستخدم الآن أرقام الفهارس لتغيير ترتيب ظهور القيم المرتبطة بها في السلسلة النصية: print("Sammy is a {3}, {2}, and {1} {0}!".format("happy", "smiling", "blue", "shark")) الناتج: Sammy is a shark, blue, and smiling happy! ولمّا كنّا قد بدأنا بالفهرس ذي الرقم 3، فستظهر القيمة "shark" أولًا. أي أنَّ وضع رقم الفهرس بين القوسين كمعامل سيؤدي إلى تغيير ترتيب ظهور القيم في السلسلة النصية الأصلية. نستطيع -بالإضافة إلى المعاملات الموضعية الرقمية- أن نربط بين القيم وبين كلمات محجوزة مخصصة ومن ثم نستدعيها عبر وضع الكلمة المحجوزة بين القوسين المعقوفين كما يلي: print("Sammy the {0} {1} a {pr}.".format("shark", "made", pr = "pull request")) الناتج: Sammy the shark made a pull request. أظهر المثال السابق استخدام كلمة محجوزة كوسيط بالإضافة إلى المعاملات الموضعية، يمكنك استخدام الكلمة المحجوزة pr كوسيط بالإضافة إلى أرقام الفهارس، وتستطيع أيضًا إعادة ترتيب تلك الوسائط كيفما شئت: print("Sammy the {pr} {1} a {0}.".format("shark", "made", pr = "pull request")) الناتج: Sammy the pull request made a shark. استخدام المعاملات الموضعية والكلمات المحجوزة سيمنحنا تحكمًا أكبر بكيفية معالجة السلسلة النصية الأصلية عبر إعادة ترتيب القيم المُمرَّرة إليها. تحديد نوع القيمة يمكنك وضع معاملات أخرى ضمن القوسين المعقوفين، سنستخدم الصيغة الآتية {field_name:conversion} حيث field_name هو الفهرس الرقمي للوسيط الممرَّر إلى الدالة str.format()‎ والذي شرحناه تفصيليًا في القسم السابق، و conversion هو الرمز المستعمل للتحويل إلى نوع البيانات الذي تريده. «رمز التحويل» يعني رمزًا من حرفٍ وحيد الذي تستخدمه بايثون لمعرفة نوع القيمة المُراد «تنسيقها». الرموز التي سنستخدمها في أمثلتنا هي s للسلاسل النصية و d لإظهار الأرقام بنظام العد العشري (ذي الأساس 10) و f لإظهار الأعداد ذات الفاصلة. يمكنك قراءة المزيد من التفاصيل عن رموز التنسيق في بايثون 3 (وغير ذلك من المواضيع المرتبطة بهذا المجال) في التوثيق الرسمي. لننظر إلى مثالٍ نُمرِّر فيه رقمًا صحيحًا عبر الدالة format()‎ لكننا نريد إظهاره كعددٍ ذي فاصلة عبر رمز التحويل f: print("Sammy ate {0:f} percent of a {1}!".format(75, "pizza")) الناتج: Sammy ate 75.000000 percent of a pizza! وضعت القيمة -مكان أوّل ورود للصيغة {field_name:conversion}- كعددٍ ذي فاصلة، أما ثاني ورود للقوسين المعقوفين فكان بالصيغة {field_name}. لاحظ في المثال السابق وجود عدد كبير من الأرقام الظاهرة بعد الفاصلة العشرية، لكنك تستطيع تقليل عددها. فعندما نستخدم الرمز f للقيم ذات الفاصلة نستطيع أيضًا تحديد دقة القيمة الناتجة بتضمين رمز النقطة . متبوعًا بعدد الأرقام بعد الفاصلة التي نود عرضها. حتى لو أكل سامي 75.765367% من قطعة البيتزا فلن نحتاج إلى هذا القدر الكبير من الدقة، إذ يمكننا مثلًا أن نجعل عدد المنازل العشرية ثلاث منازل بعد الفاصلة بوضعنا ‎.3 قبل رمز التحويل f: print("Sammy ate {0:.3f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 75.765 percent of a pizza! أما إذا أردنا عرض منزلة عشرية وحيدة، فيمكننا إعادة كتابة السلسلة السابقة كالآتي: print("Sammy ate {0:.1f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 75.8 percent of a pizza! لاحظ كيف أدى تعديل دقة الأرقام العشرية إلى تقريب الرقم (وفق قواعد التقريب الاعتيادية). وصحيحٌ أننا عرضنا رقمًا دون منازل عشرية كعددٍ ذي فاصلة، إلا أننا إذا حاولنا تحويل عدد عشري إلى عدد صحيح باستخدام رمز التحويل d فسنحصل على خطأ: print("Sammy ate {0:.d} percent of a pizza!".format(75.765367)) الناتج: ValueError: Unknown format code 'd' for object of type 'float' إذا لم ترغب بعرض أيّة منازل عشرية، فيمكنك كتابة تعبير كالآتي: print("Sammy ate {0:.0f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 76 percent of a pizza! لن يؤدي ما سبق إلى تحويل العدد العشري إلى عددٍ صحيح، وإنما سيؤدي إلى تقليل عدد المنازل العشرية الظاهرة بعد الفاصلة. إضافة حواشي لمّا كانت الأماكن المحجوزة عبر القوسين المعقوفين هي حقول قابلة للاستبدال (أي ليست قيمًا فعليةً) فيمكنك إضافة حاشية (padding) أو إضافة فراغ حول العنصر بزيادة حجم الحقل عبر معاملات إضافية، قد تستفيد من هذا الأمر عندما تحتاج إلى تنظيم البيانات بصريًا. يمكننا إضافة حقلٍ بحجمٍ معيّن (مُقاسًا بعدد المحارف) بتحديد ذاك الحجم بعد النقطتين الرأسيتين : كما في المثال الآتي: print("Sammy has {0:4} red {1:16}!".format(5, "balloons")) الناتج: Sammy has 5 red balloons ! أعطينا في المثال السابق حقلًا بحجم 4 محارف للعدد 5، وأعطينا حقلًا بحجم 16 محرفًا للسلسلة النصية balloons (لأنها سلسلة طويلة نسبيًا). وكما رأينا من ناتج المثال السابق، يتم محاذاة السلاسل النصية افتراضيًا إلى اليسار والأعداد إلى اليمين، يمكنك أن تُغيّر من هذا بوضع رمز خاص للمحاذاة بعد النقطتين الرأسيتين مباشرةً. إذ سيؤدي الرمز > إلى محاذاة النص إلى يسار الحقل، أما الرمز ^ فسيوسِّط النص في الحقل، والرمز < سيؤدي إلى محاذاته إلى اليمين. لنجعل محاذاة العدد إلى اليسار ونوسِّط السلسلة النصية: print("Sammy has {0:<4} red {1:^16}!".format(5, "balloons")) الناتج: Sammy has 5 red balloons ! نلاحظ الآن أنَّ محاذاة العدد 5 إلى اليسار، مما يعطي مساحة فارغةً في الحقل قبل الكلمة red، وستظهر السلسلة النصية balloons في منتصف الحقل وتوجد مسافة فارغة على يمينها ويسارها. عندما نُنسِّق الحقل لنجعله أكبر من حجمه الطبيعي فستملأ بايثون الحقل افتراضيًا بالفراغات، إلا أننا نستطيع تغيير محرف الملء إلى محرفٍ آخر بوضعه مباشرةً بعد النقطتين الرأسيتين: print("{:*^20s}".format("Sammy")) الناتج: *******Sammy******** ستضع بايثون السلسلة النصية المُمرَّرة إلى الدالة str.format()‎ ذات الفهرس 0 مكان القوسين المعقوفين لأننا لم نطلب منها عكس ذلك، ومن ثم سنضع النقطتين الرأسيتين ثم سنُحدِّد أننا سنستعمل المحرف * بدلًا من الفراغات لملء الحقل، ثم سنوسِّط السلسلة النصية عبر استعمال الرمز ^ مُحدِّدين أنَّ حجم الحقل هو 20 محرف، ومُشيرين في نفس الوقت إلى أنَّ الحقل هو حقلٌ نصيٌ عبر وضع الرمز s. يمكننا استخدام هذه المعاملات مع المعاملات التي استخدمناها وشرحناها في الأقسام السابقة: print("Sammy ate {0:5.0f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 76 percent of a pizza! حدِّدنا داخل القوسين المعقوفين رقم فهرس القيمة العددية ثم وضعنا النقطتين الرأسيتين ثم اخترنا حجم الحقل ثم وضعنا نقطةً . لنضبط عدد المنازل العشرية الظاهرة، ثم اخترنا نوع الحقل عبر رمز التحويل f. استخدام المتغيرات مرَّرنا منذ بداية هذا الدرس وإلى الآن الأعداد والسلاسل النصية إلى الدالة str.format()‎ مباشرةً، لكننا نستطيع تمرير المتغيرات أيضًا، وذلك بنفس الآلية المعتادة: nBalloons = 8 print("Sammy has {} balloons today!".format(nBalloons)) الناتج: Sammy has 8 balloons today! يمكننا أيضًا استخدام المتغيرات لتخزين السلسلة النصية الأصلية بالإضافة إلى القيم التي ستُمرَّر إلى الدالة: sammy = "Sammy has {} balloons today!" nBalloons = 8 print(sammy.format(nBalloons)) الناتج: Sammy has 8 balloons today! ستُسهِّل المتغيرات من التعامل مع تعبيرات التنسيق وتُبسِّط عملية إسناد مدخلات المستخدم وإظهارها مُنسَّقةً في السلسلة النصية النهائية. استخدام المُنسِّقات لتنظيم البيانات يسطع نجم آلية التنسيق التي نشرحها في هذا الدرس عندما تُستخدَم لتنظيم البيانات بصريًا، فلو أردنا إظهار نتائج قاعدة البيانات إلى المستخدمين، فيمكننا استعمال المُنسِّقات لزيادة حجم الحقل وتعديل المحاذاة لجعل الناتج أسهل قراءةً. لننظر إلى حلقة تكرار تقليدية في بايثون التي تطبع i و i*i و i*i*i لمجالٍ من الأعداد من 3 إلى 13: for i in range(3,13): print(i, i*i, i*i*i) الناتج: 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 11 121 1331 12 144 1728 صحيحٌ أنَّ الناتج مُنظَّمٌ قليلًا، إلا أنَّ الأعداد تتداخل مع بعضها بصريًا مما يُصعِّب قراءة الأسطر الأخيرة من الناتج، وإذا كنتَ تتعامل مع مجموعة أكبر من البيانات التي يتواجد فيها أعداد أكبر (أو أصغر) مما عرضناه في مثالنا، فقد تبدو لك المشكلة جليةً حينها. لنحاول تنسيق الناتج السابق لإعطاء مساحة أكبر لإظهار الأعداد: for i in range(3,13): print("{:3d} {:4d} {:5d}".format(i, i*i, i*i*i)) لم نُحدِّد في المثال السابق ترتيب الحقل وبدأنا مباشرةً بكتابة النقطتين الرأسيتين متبوعةً بحجم الحقل ورمز التحويل d (لأننا نتعامل مع أعداد صحيحة). أعطينا في المثال السابق حجمًا للحقل مساويًا لعدد أرقام العدد الذي نتوقع طباعته في الحقل المعني مضافًا إليه 2، لذا سيبدو الناتج كالآتي: 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 11 121 1331 12 144 1728 يمكننا أيضًا تحديد حجم ثابت للحقل لنحصل على أعمدة متساوية العرض، مما يضمن إظهار الأعداد الكبيرة بصورة صحيحة: for i in range(3,13): print("{:6d} {:6d} {:6d}".format(i, i*i, i*i*i)) الناتج: 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 11 121 1331 12 144 1728 يمكننا أيضًا تعديل محاذاة النص الموجود في الأعمدة باستخدام الرموز > و ^ و <، وتبديل d إلى f لإظهار منازل عشرية، وغير ذلك مما تعلمناه في هذا الدرس لإظهار البيانات الناتجة كما نرغب. الخلاصة قد يكون استخدام المنسقات لوضع القيم في سلسلةٍ نصيةٍ أمرًا مفيدًا لتسهيل تنظيم البيانات والقيم، فالمنسقات هي آليةٌ تساعد في جعل الناتج مقروءًا للمستخدم. ترجمة -وبتصرّف- للمقال How To Use String Formatters in Python 3 لصاحبته Lisa Tagliaferri.
  5. تمهيد المُحرِّر التدفقي sed‏ (Stream EDitor) هو محرِّر نصيّ يُجري عمليات التعديل على المعلومات الآتية من مجرى الإدخال القياسي (Standard Input) أو من ملف. يجري المُحرِّر sed عملياته على النصوص سطرًا بسطر بطريقةٍ غير تفاعلية،. هذا يعني إمكانية تحديد التعديلات التي تريد إجراءها أثناء كتابتك للأمر ومن ثم سيُنفِّذها sed تلقائيًا، دون أن يستشيرك في كلّ مرة؛ ربما تجد هذا الموضوع مُربِكًا وغريبًا، لكن اعلم أنَّ هذه الطريقة غير التفاعلية فعّالةٌ جدًا وسريعةٌ لتعديل النصوص. سنشرح في هذا الدرس بعض العمليات الأساسية وسنتعرّف على البنية العامة للتعامل مع هذا المُحرِّر. أنا واثقٌ تمامًا أنَّك لن تستبدل المحرر sed بمحررك النصي العادي، لكن من المرجَّح أن تضيف هذه الأداة الفعّالة إلى جعبة الأدوات التي تستعملها للتعامل مع النصوص. أساسيات استخدام sed في الحالة العامة، يُجري المُحِّرر sed عملياته على مجرى نصي (text steam) آتٍ من مجرى الإدخال القياسي أو من ملف. هذا يعني أنَّك تستطيع إرسال ناتج أحد الأوامر إلى الأمر sed مباشرةً لتعديله، أو يمكنك استعمال sed على ملفٍ موجودٍ مسبقا. عليك أن تتنبّه إلى طباعة الأمر sed لجميع مخرجاته إلى مجرى الإخراج القياسي (Standard output) مبدئيا؛ وهذا يعني طباعة النتائج إلى الشاشة بدلًا من حفظها في ملفٍ ما، إلا إذا أجريت عملية إعادة توجيه (Output redirection). الصيغة العامّة لاستخدام هذا المُحرِّر هي: sed [options] commands [file-to-edit] لننسخ بعض الملفات النصية إلى مجلد المنزل الخاص بنا للتدرّب على عمليات التعديل: cd cp /usr/share/common-licenses/BSD . cp /usr/share/common-licenses/GPL-3 . لنستخدم الأمر sed لعرض محتويات ملف رخصة BSD الذي نسخناه آنفًا. ولعلمنا أنَّ الأمر sed يُرسِل الناتج إلى الشاشة مبدئيًّا، فيمكننا استخدامه لقراءة الملف دون تعديل، وذلك إذا لم نوفِّر أيّة تعليمات له. لنجرِّب ذلك: sed '' BSD الناتج: Copyright (c) The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. ... … يعمل الأمر السابق كما توقعنا لعدم وجود أيّة تعليمات تعديل ضمن علامتَيْ الاقتباس ''، مما أدى إلى طباعة كل سطر كما هو إلى مجرى الإخراج القياسي (أي إلى الشاشة). سنشرح كيف يستطيع الأمر sed الحصول على النصوص عبر مجرى الإدخال القياسي بتمرير ناتج الأمر cat عبر أنبوب (pipe، باستعمال الرمز |) إلى الأمر sed، وسنلاحظ ظهور الناتج السابق نفسه: cat BSD | sed '' المخرجات: Copyright (c) The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . . . . . . كما لاحظنا، يمكننا إجراء العمليات بسهولة على الملفات أو على مجرى من النص (آتٍ من أحد الأوامر). طباعة أسطر معيّنة رأينا في المثال السابق أنَّ تمرير النص إلى الأمر sed دون استخدام أيّة تعليمات تعديل سيؤدي إلى طباعة النتائج مباشرةً إلى مجرى الإخراج القياسي. سننظر الآن إلى تعليمة طباعة الأسطر الموجودة في sed، والتي نستطيع استخدامها عبر وضع المحرف p ضمن علامتَي الاقتباس. sed 'p' BSD الناتج: Copyright (c) The Regents of the University of California. Copyright (c) The Regents of the University of California. All rights reserved. All rights reserved. Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions are met: are met: . . . . . . نلاحظ أنَّ sed قد طبع كل سطر مرتين، وهذا بسبب طباعته لكل سطر تلقائيًا، ولأننا طلبنا ذلك مرةً أخرى عبر استعمال تعليمة الطباعة p. إذا أمعنتَ النظر في الناتج السابق، فستلاحظ أنَّ sed قد طبع أوّل سطر مرتين، متبوعًا بالسطر الثاني مرتين …إلخ. وسترى أنَّ المُحرِّر sed يُجرى عملياته على الأسطر فرادى. حيث يقرأ سطرًا من النص، ثم يجري عمليات التعديل عليه، ثم يخرج النص الناتج قبل تكرار نفس العملية على السطر الذي يليه. يمكننا إصلاح الناتج السابق عبر تمرير الخيار ‎-n إلى sed، والذي يمنعه من طباعة الأسطر تلقائيًا: sed -n 'p' BSD الناتج: Copyright (c) The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . . . . . . نلاحظ الآن أنَّ كل سطر طُبِعَ مرةً واحدةً فقط. المجالات لا أظن أنَّ الأمثلة السابقة تُحسَب على أنها «تعديلات» (إلا إذا كنت تريد طباعة كل سطر مرتين…)؛ لنعدِّل الآن الناتج بجعل sed يطبع أوّل سطر فقط: sed -n '1p' BSD الناتج: Copyright (c) The Regents of the University of California. بوضع الرقم 1 قبل تعليمة الطباعة، فسنخبر sed ما هو السطر الذي نريد إجراء عمليات التعديل عليه. يمكننا أيضًا طباعة أوّل خمسة أسطر (لا تنسَ استخدام الخيار ‎-n): sed -n '1,5p' BSD الناتج: Copyright (c) The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions أعطينا مجالًا من الأسطر للأمر sed السابق. وإذا أعطيت sed مجالًا، فهذا يعني رغبتك بإجراء عمليات التعديل على تلك الأسطر فقط. وأخبرنا –في المثال السابق– الأمر sed أن يطبع الأسطر 1 إلى 5. يمكننا تحديد المجال السابق بطريقةٍ أخرى، وذلك بتحديد بداية مجال الأسطر ومن ثم استخدام معامل إزاحة لكي نخبر sed كم سطرًا عليه أن يتنقل للوصول إلى نهاية المجال: sed -n '1,+4p' BSD سيُنتِج الأمر السابق الناتج نفسه، وذلك لأننا أخبرنا sed أن يبدأ المجال من السطر الأول وينتهي بعده بأربعة أسطر. إذا أردنا أن نتخطى سطرًا بين كل سطرين، فيمكننا تحديد «الخطوة» بعد استخدام المحرف ~. سنطبع في المثال الآتي سطرًا كل سطرين وذلك بدءًا من السطر 1: sed -n '1~2p' BSD الناتج: Copyright (c) The Regents of the University of California. modification, are permitted provided that the following conditions 1. Redistributions of source code must retain the above copyright 2. Redistributions in binary form must reproduce the above copyright documentation and/or other materials provided with the distribution. may be used to endorse or promote products derived from this software . . . . . . حذف الأسطر يمكننا بسهولة إجراء عمليات حذف للأسطُر بدلًا من طباعتها، وذلك بتبديل الأمر p إلى الأمر d. لم نعد نحتاج الخيار ‎-n لأنَّ الأمر sed – عند استخدام تعليمة الحذف معه – سيطبع كل شيء لم يُحذَف، مما يساعدنا في معرفة ماذا عُدِّل. يمكننا تعديل آخر أمر من القسم السابق ليحذف سطرًا كل سطرين بدءًا من أوّل سطر. يجب أن يظهر في ناتج تنفيذ هذا الأمر كلُّ سطرٍ لم يظهر في ناتج الأمر الذي طبّقناه في القسم السابق: sed '1~2d' BSD المخرجات: All rights reserved. Redistribution and use in source and binary forms, with or without are met: notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer in the 3. Neither the name of the University nor the names of its contributors without specific prior written permission. . . . . . . من المهم أن تلاحظ أنَّ الملف الأصلي لم يتغيّر قط، فما يزال على حاله، لكن التعديلات التي حدثت قد ظهرت على شاشتنا. إذا أردنا حفظ تعديلاتنا، فيمكننا إعادة توجيه مجرى الإخراج القياسي إلى ملفٍ كما في الأمر الآتي: sed '1~2d' BSD > everyother.txt إذا اطلعتَ على محتويات الملف مستعملًا الأمر cat، فسترى الناتج السابق نفسه. أذكِّرُكَ أنَّ الأمر sed لا يُعدِّل الملف المصدري مبدئيًّا منعًا لحدوث أخطاء. يمكننا تغيير هذا السلوك الافتراضي باستعمال الخيار ‎-i، الذي يؤدي إلى إجراء التعديلات على الملف الأصلي. لنجِّرب هذا الخيار على الملف everyother.txt الذي أنشأناه منذ قليل. لنحاول حذف المزيد من الأسطر من هذا الملف: sed -i '1~2d' everyother.txt إذا استعملتَ الأمر cat مرةً أخرى، فسترى أنَّ الملف قد عُدِّل. وكما ذكرنا سابقًا، الخيار ‎-i خطيرٌ ويجب توخي الحذر عند استعماله؛ ولحسن الحظ، يعطينا sed القدرة على إنشاء نسخةٍ احتياطيةٍ مباشرةً قبل التعديل؛ وذلك بوضع لاحقة الملف الاحتياطي مباشرةً بعد الخيار ‎-i كما يلي: sed -i.bak '1~2d' everyother.txt الأمر السابق سيؤدي إلى إنشاء ملفٍ احتياطيٍ باللاحقة ‎.bak، ثم تعديل الملف الأصلي مباشرةً. استبدال النصوص أحد أشهر استخدامات المُحرِّر sed هو استبدال النصوص. يملك المُحرِّر sed القدرة على البحث عن أنماطٍ نصيةٍ عبر التعابير النمطية (Regular expressions) ومن ثم استبدال النص المُطابِق. يمكنك مراجعة هذه المقالة لمزيدٍ من المعلومات حول التعابير النمطية. يمكنك استبدال كلمة بأخرى عبر الصيغة البسيطة الآتية: 's/old_word/new_word/' المحرف s يُمثِّل عملية الاستبدال (تذكّر أنَّ كلمة «استبدال» بالإنكليزية هي «Substitute»)، وتُستعمَل الخطوط المائلة / لفصل مختلف الحقول؛ لكن يمكنك استخدام محارف أخرى للفصل بين الحقول إذا ارتأيتَ فائدةً من ذلك. على سبيل المثال، إذا كنتَ تحاول تغيير اسم الصفحة في رابط URL، فيمكنك استخدام حرف آخر للفصل بين الحقول (مثلا _) لأنَّ عناوين URL تحتوي على خطوط مائلة / ضمنها. سنفعل ذلك في المثال الآتي الذي نستعمل الأمر echo فيه لتمرير رابط URL عبر أنبوب إلى مجرى الإدخال القياسي للأمر sed: echo "http://www.example.com/index.html" | sed 's_com/index_org/home_' الناتج: http://www.example.org/home.html لا تنسَ وضع آخر فاصل (المحرف _) أو سيشتكي الأمر sed: echo "http://www.example.com/index.html" | sed 's_com/index_org/home' ستظهر رسالة الخطأ الآتية: sed: -e expression #1, char 22: unterminated `s' command لنُنشِئ ملفًا للتدرّب على استبدال النصوص (تأكد من كتابة النص على7 أسطُر): echo "this is the song that never ends yes, it goes on and on, my friend some people started singing it not knowing what it was and they'll continue singing it forever just because..." > annoying.txt لنستبدل الآن التعبير forward بالتعبير on: sed 's/on/forward/' annoying.txt الناتج: this is the sforwardg that never ends yes, it goes forward and on, my friend some people started singing it not knowing what it was and they'll cforwardtinue singing it forever just because… يمكنك أن تلاحظ بعض الإشكاليات في الناتج السابق. أولها هو أننا نستبدل تعابير وليس كلمات، هذا يعني أنَّ التعبير on الموجود ضمن الكلمة song سيصبح forward. تظهر الإشكالية الثانية في السطر الثاني، حيث لم يُبدّل تعبير on الثاني إلى forward؛ وهذا لأنَّ الأمر s سيُعدِّل أوّل مطابقة في السطر مبدئيًّا، ثم سينتقل إلى السطر الذي يليه؛ ولجعل الأمر sed يُبدِّل كل التعابير الموجودة في السطر بدلًا من أوّل مطابقة، فعلينا تمرير «راية» (flag) اختيارية إلى تعليمة الاستبدال. سنوفِّر الراية g إلى تعليمة الاستبدال بوضعها بعد آخر محرف فصل، كما في المثال الآتي: sed 's/on/forward/g' annoying.txt الناتج: this is the sforwardg that never ends yes, it goes forward and forward, my friend some people started singing it not knowing what it was and they'll cforwardtinue singing it forever just because… أصبحت تعليمة الاستبدال تُغيّر كل مُطابَقة في السطر. لكن إذا أردتَ تغيّر ثاني مطابقة للتعبير on في كل سطر، فيمكنك استخدام الرقم 2 بدلًا من الراية g: sed 's/on/forward/2' annoying.txt الناتج: this is the song that never ends yes, it goes on and forward, my friend some people started singing it not knowing what it was and they'll continue singing it forever just because… إذا أردت أن ترى ما هي الأسطر التي استبدِلَت، فيمكنك استخدام الخيار ‎-n لإخفاء الأسطر غير المُعدَّلة؛ ثم يمكننا بعد ذلك تمرير الخيار p إلى تعليمة الاستبدال لطباعة الأسطر التي أجريت عليها تلك العملية: sed -n 's/on/forward/2p' annoying.text الناتج: yes, it goes on and forward, my friend وكما لاحظتَ، يمكننا دمج رايات عدة معًا في نهاية التعليمة. إذا أردنا البحث مع إهمال اختلاف حالة الأحرف، فيمكننا استعمال الراية i كما في الأمر الآتي: sed 's/SINGING/saying/i' annoying.txt الناتج: this is the song that never ends yes, it goes on and on, my friend some people started saying it not knowing what it was and they'll continue saying it forever just because… إعادة استخدام التعابير التي تمت مطابقتها إذا استخدمنا التعابير النمطية لمطابقة النصوص، فلدينا طرائق مختلفة نستطيع استخدامها للإشارة إلى التعبير الذي تمت مطابقته في النص الذي سنضعه بدلًا من النص الأصلي. على سبيل المثال، إذا أردنا أن نطابق من بداية السطر إلى التعبير at، فيمكننا استخدام الأمر الآتي: sed 's/^.*at/REPLACED/' annoying.txt الناتج: REPLACED never ends yes, it goes on and on, my friend some people started singing it REPLACED it was and they'll continue singing it forever just because… لاحظ أنَّ المحرف البديل * أدى إلى المطابقة من بداية السطر ^ إلى آخر مُطابَقة للتعبير at. ولعدم معرفتك ما هي العبارة التي ستُطابَق في تعبير البحث، فيمكنك استخدام الرمز & في تعبير الاستبدال لتمثيل النص الذي جرت مُطابقته. هذا المثال يُظهِر كيف نستطيع وضع قوسين حول النص المُطابَق: sed 's/^.*at/(&)/' annoying.txt الناتج: (this is the song that) never ends yes, it goes on and on, my friend some people started singing it (not knowing what) it was and they'll continue singing it forever just because… طريقة أخرى أكثر مرونةً للإشارة إلى النص المُطابَق هي استخدام أقواس هلاليّة () لتجميع أقسام من النص المُطابق. يمكن الإشارة إلى كل مجموعة من النص المُطابَق والمحاط بالأقواس الهلالية عبر استخدام الأرقام. فمثلًا، نُشير إلى أوّل مجموعة تعابير محاطة بقوسين هلاليين بالتعبير ‎\1، والمجموعة الثانية بالتعبير ‎\2 وهكذا. سنُبدِّل في هذا المثال بين أوّل كلمتين في كل سطر (لا تنسَ «تخليص» [escape] الأقواس الهلالية ضمن التعابير النمطية): sed 's/\([a-zA-Z0-9][a-zA-Z0-9]*\) \([a-zA-Z0-9][a-zA-Z0-9]*\)/\2 \1/' annoying.txt الناتج: is this the song that never ends yes, goes it on and on, my friend people some started singing it knowing not what it was they and'll continue singing it forever because just… يمكنك ملاحظة أنَّ الناتج ليس مثاليًا؛ فمثلًا، سنتجاهل أوّل كلمة لأنها تحتوي على محرف ليس مذكورًا ضمن مجموعة المحارف التي نريد مطابقتها. وستُعامَل they'll ككلمتين في السطر الخامس. لنحسِّن من التعبير النمطي السابق ليصبح أكثر دقةً: sed 's/\([^ ][^ ]*\) \([^ ][^ ]*\)/\2 \1/' annoying.txt الناتج: is this the song that never ends it yes, goes on and on, my friend people some started singing it knowing not what it was they'll and continue singing it forever because... just الناتج أفضل بكثير من الناتج السابق، حيث ستُجمَّع علامات الترقيم مع الكلمة التي تتبع لها. لاحظ كيف كرَّرنا التعبير داخل القوسين (مرةً دون استخدام المحرف *، ومرةً معه). وهذا لأنَّ المحرف * يؤدي إلى مطابقة مجموعة المحارف التي تسبقه عندما تقع 0 مرة أو أكثر. وهذا يعني أنَّ التعبير سيُطابَق حتى لو لم يُعثَر على النمط ضمن النص؛ وللتأكد من وجود التعبير مرةً واحدةً على الأقل، فعلينا مطابقته أولّا دون استخدام *. الخلاصة لقد شرحنا بعض أساسيات استخدام الأمر sed، أتوقع أنَّك عرفتَ الآن سهولة وسرعة تعديل النصوص باستخدام أوامر sed. سنشرح في الدرس القادم ميزاتٍ متقدّمة للمحرِّر sed. ترجمة –وبتصرّف– للمقال The Basics of Using the Sed Stream Editor to Manipulate Text in Linux لصاحبه Justin Ellingwood
  6. بعد تعلّم المعلومات الرئيسية عن الفضاء ثلاثي الأبعاد والإضاءة، تعجّلنا في نهاية الدرس السابق لكي نحصل على صورة ثلاثية الأبعاد، إلا أنني سأعود قليلًا إلى الوراء وأشرح كائنات 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
  7. تمهيد بشكل عام، نستعمل حاويات 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
  8. قالب 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.
  9. شرحنا في أوّل جزء من هذه السلسلة أساسيات استخدام إطار عمل 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.
  10. تمهيد بايثون هي أداةٌ رائعةٌ لمعالجة البيانات. ومن المحتمل أنَّ البرامج التي تكتبها ستتضمن قراءة البيانات أو كتابتها أو تعديلها، ولهذا السبب ستستفيد جدًا من قدرة بايثون على التعامل مع مختلف صيغ الملفات التي تُخزِّن أصنافًا متنوعةً من البيانات. ليكن لديك -على سبيل المثال- برنامج بايثون الذي يتحقق من امتيازات الوصول لقائمة من المستخدمين؛ فيكون من المرجح في هذه الحالة أن تُخزَّن قائمة المستخدمين في ملفٍ نصيٍ بسيط. أو ربما لم تكن تتعامل مع النصوص، وإنما مهمة برنامجك هي التحليل الاقتصادي؛ ومن المرجح أنَّك ستأخذ الأرقام التي ستجري عملياتك عليها من ورقة عمل في أحد البرامج مثل 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
  11. يأتي قالب 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.
  12. تمهيد Python هي لغةٌ سهلة القراءة للغاية ومتنوعة ومتعددة الاستخدامات، واسمها مستوحى من مجموعة كوميدية بريطانية باسم «Monty Python»، وكان أحد الأهداف الأساسية لفريق تطوير بايثون هو جعل اللغة مرحةً وسهلة الاستخدام، وإعدادها بسيطٌ، وطريقة كتابتها مباشرة وتعطيك تقريرًا مباشرًا عند حدوث أخطاء، وهي خيارٌ ممتازٌ للمبتدئين والوافدين الجدد على البرمجة. لغة بايثون هي لغة متعددة الاستعمالات، وتدعم مختلف أنماط البرمجة مثل كتابة السكربتات والبرمجة كائنية التوجه (object-oriented)، وهي مناسبةٌ للأغراض العامة، واستعمالها يتزايد في سوق العمل إذ تعتمدها منظماتٌ مثل «United Space Alliance» (شركة في مجال إرسال مركبات فضائية وتتعاقد معها ناسا) و «Industrial Light & Magic» (أستوديو للتأثيرات السينمائية وللرسوم المتحركة)، وتوفِّر بايثون قدراتٍ كثيرةٍ لمن يريد تعلم لغة برمجة جديدة. طوِّرَت اللغة في نهاية الثمانينات من القرن الماضي، ونُشِرَت أوّل مرة في عام 1991، طُوِّرَت بايثون من قِبل Guido van Rossum، وهو عضوٌ نشطٌ للغاية في المجتمع. وتعتبر بايثون على أنَّها بديلٌ عن لغة ABC، وأوّل إصدار منها كان يتضمن التعامل مع الاستثناءات (exception handling) والدوال والأصناف (classes) مع إمكانية الوراثة فيها. وعدما أُنشِئ منتدى محادثة في Usenet باسم comp.lang.python في 1994، فبدأت قاعدة مستخدمي بايثون بالنمو، مما مهّد الطريق لها لتصبح واحدة من أكثر لغات البرمجة شيوعًا وخصوصًا لتطوير البرمجيات مفتوحة المصدر. لمحة عامة قبل أن ننظر إلى إمكانيات إصدارَي بايثون 2 وبايثون 3 (مع الاختلافات البرمجية الرئيسية بينهما)، فلننظر إلى لمحة تاريخية عن الإصدارات الرئيسية الحديثة من بايثون. بايثون 2 نُشِرَ هذا الإصدار في أواخر عام 2000، وأصبحت بايثون 2 لغة برمجة شاملة مقارنةً بالإصدارات التي تسبقها وذلك بعد تطبيق اقتراح PEP ‏(Python Enhancement Proposal)، وهو مواصفةٌ (specification) تقنيةٌ التي توفِّر معلومات إلى أعضاء مجتمع بايثون أو تصف ميزاتٍ جديدة في اللغة. بالإضافة إلى ذلك، تضمنت بايثون 2 ميزاتٍ برمجية جديدة مثل «cycle-detecting garbage collector» لأتمتة عملية إدارة الذاكرة، وزيادة دعم يونيكود لتدعم اللغة جميع المحارف المعيارية …إلخ. وأثناء عملية تطوير بايثون 2 أضيفت ميزات جديدة بما في ذلك توحيد الأنواع والأصناف في بايثون في بنية هيكلية وحيدة (وذلك في إصدار 2.2 من بايثون). بايثون 3 تُعتَبر بايثون 3 هي مستقبل لغة بايثون وهي النسخة قيد التطوير من اللغة، وهذا إصدارٌ رئيسيٌ نُشِر في أواخر عام 2008 لإصلاح بعض المشاكل الجوهرية في تصميم الإصدارات السابقة من اللغة، وكان التركيز أثناء تطوير بايثون 3 هو تحسين الشيفرات التي تبنى عليها اللغة وحذف التكرارات، مما يعني أنَّ هنالك طريقة وحيدة فقط لإنجاز مهمّة معيّنة. التعديلات الأساسية التي حدثت في بايثون 3.0 تتضمن تغير العبارة print إلى دالة مُضمَّنة باللغة، وتحسين قسمة الأعداد الصحيحة، وتوفير دعم إضافي ليونيكود. في البداية، انتشرت بايثون 3 ببطء نتيجةً لعدم توافقيتها مع بايثون 2، مما يعني أنَّ على المستخدمين اختيار ما هو الإصدار الذي عليهم استخدامه. بالإضافة إلى ذلك، كانت الكثير من المكتبات البرمجية متاحةً فقط لبايثون 2، لكن بعد تقرير فريق تطوير بايثون 3 أنَّه يجب أن التخلي عن دعم بايثون 2، فبدأت عملية تحويل المكتبات إلى بايثون 3. يمكننا معرفة زيادة الاعتماد على بايثون 3 من خلال عدد الحزم البرمجية التي تدعم بايثون 3، والتي هي (في وقت كتابة هذا المقال) 339 من أصل 360 من أشهر الحزم. بايثون 2.7 بعد إصدار بايثون 3.0 في 2008، أُصدِرَت نسخة بايثون 2.7 في تموز 2010 وهي آخر إصدار من سلسلة ‎2.x، الغرض من إصدار بايثون 2.7 هو جعل الطريق ممهدًا أمام مستخدمي بايثون ‎2.x لتحويل برامجهم إلى بايثون 3 بتوفير بعض التوافقية بينهما. وهذه التوافقية تضمنت دعم بعض الوحدات المُحسّنة في 2.7 مثل unittest لأتمتة الاختبارات، و argparse لتفسير خيارات سطر الأوامر، وبعض الفئات في collections. ولخصوصية بايثون 2.7 ولكونها جسرًا واصلًا بين الإصدارات القديمة من بايثون 2 وبين بايثون 3.0، فأصبحت خيارًا شائعًا بين المبرمجين بسبب توافقيتها مع الكثير من المكتبات. عندما نتحدث اليوم عن بايثون 2، فنحن نشير عادةً إلى إصدار بايثون 2.7 لأنَّه أكثر إصدار مستخدم؛ لكنه يُعتَبَر أنَّه إصدارٌ قديم، وسيتوقف تطويره (التطوير الحالي هو إصلاح العلل فقط) تمامًا في 2020. الاختلافات الأساسية بغض النظر أنَّ بايثون 2.7 وبايثون 3 تتشاركان في الكثير من الأشياء، لكن لا يجدر بك أن تظن أنَّهما متماثلتان ويمكن تبديل الشيفرات بينهما. وعلى الرغم من أنَّك تستطيع كتابة شيفرات جيدة وبرامج مفيدة في أيّ إصدار منهما، لكن من المهم أن تفهم أنَّ هنالك بعض الاختلافات في بنية الشيفرات وفي طريقة تفسيرها. سأعرض هنا بعض الأمثلة، لكن عليك أن تعلم أنَّك ستواجه المزيد من الاختلافات أثناء مسيرة تعلمك لبايثون. print في بايثون 2، تُعامَل print كتعبيرٍ برمجيٍ بدلًا من كونها دالة، وهذا كان يثير ارتباكًا لأنَّ الكثير من الأمور داخل بايثون تتطلب تمرير وسائط (arguments) بين قوسين، إذا فتحتَ مُفسِّر بايثون 2 لطباعة «Sammy the Shark is my favorite sea creature»، فستكتب تعبير print الآتي: print "Sammy the Shark is my favorite sea creature" أما في بايثون 3، فستُعامَل print()‎ كدالة، لذا لطباعة السلسلة النصية السابقة، فيمكننا استخدام شكل استدعاء الدوال التقليدي كما يلي: print("Sammy the Shark is my favorite sea creature") هذا التعديل جعل من البنية اللغوية في بايثون موحدةً وسهَّلَ من التبديل بين مختلف دوال الطباعة فيها. يجدر بالذكر أنَّ الدالة print()‎ متوافقة مع بايثون 2.7، لذا ستعمل شيفرات بايثون التي تستعمل print()‎ بشكلٍ صحيحٍ في أيّ الإصدارَين. قسمة الأعداد الصحيحة في بايثون 2، أيُّ عددٍ تكتبه دون فواصل عشرية سيُعامَل على أنَّه من النوع integer، تأتي الإشكالية عندما تحاول قسمة الأعداد الصحيحة على بعضها، فتتوقع في بعض الأحيان حصولك على عددٍ عشري (تسمى أيضًا بالأعداد ذات الفاصلة العائمة float) كما في التعبير الرياضي: 5 / 2 = 2.5 لكن الأعداد الصحيحة في بايثون 2 لن تتحول إلى أعداد عشرية عندما تتطلب العملية التي تُجرى عليها ذلك. عندما يكون العددان الموجودان على جانبَي معامل القسمة / عددين صحيحين، فإن بايثون 2 ستجري عملية القسم وستُنتِج عددًا عشريًا إلا أنها ستُعيد العدد الصحيح الأصغر أو المساوي للناتج، وهذا يعني أنَّه لو كتبتَ ‎5 / 2 فستُعيد بايثون 2.7 العدد الصحيح الأصغر أو المساوي للعدد 2.5، وهو في هذه الحالة 2: a = 5 / 2 print a 2 لإعادة عدد عشري، فيجب إضافة فواصل عشرية إلى الأرقام التي ستُجري عليها عملية القسمة كما في ‎5.0 / 2.0 لكي تحصل على النتيجة المنطقية 2.5. أما في بايثون 3، فقسمة الأعداد الصحيحة أصبحت كما نتوقع: a = 5 / 2 print a 2.5 يمكنك استخدام ‎5.0 / 2.0 لإعادة 2.5، لكن إن أردتَ تقريب ناتج القسمة فاستخدم المعامل // الموجود في بايثون 3، كالآتي: a = 5 // 2 print a 2 هذا التعديل في بايثون 3 جعل من قسمة الأعداد الصحيحة أمرًا سهلًا، لكن هذه الميزة غير متوافقة مع بايثون 2.7. دعم محارف يونيكود عندما تتعامل لغات البرمجة مع السلاسل النصية (strings، والتي هي سلسلةٌ من المحارف)، فهي تفعل ذلك بطرائق مختلفة لكي تتمكن الحواسيب من تحويل الأعداد إلى أحرف ورموز. تستعمل بايثون 2 محارف ASCII افتراضيًا، لذا عندما تكتب "Hello, Sammy!‎" فستتعامل بايثون 2 مع السلسلة النصية كمجموعة من محارف ASCII، والتي هي محدودةٌ لحوالي مئتَي محرف، أي أنَّ محارف ASCII هي طريقة غير عملية لترميز المحارف خصوصًا المحارف غير اللاتينية (كالعربية مثلًا). لاستخدام ترميز محارف يونيكود (Unicode) الذي يدعم أكثر من 128000 محرف تابع للكثير من اللغات والرموز، فعليك أن تكتب u"Hello, Sammy!‎"‎ حيث تُشير السابقة u إلى Unicode. تستعمل بايثون 3 محارف يونيكود (Unicode) افتراضيًا، مما يوفِّر عليك بعض الوقت أثناء التطوير، ويمكنك كتابة وعرض عدد أكبر بكثير من المحارف في برنامجك بسهولة. يدعم يونيكود الكثير من المحارف بما في ذلك الوجوه التعبيرية (emojis)، واستعمالها كترميز محارف افتراضي يعني أنَّ الأجهزة المحمولة ستكون مدعومةً في مشاريعك تلقائيًا. إذا كنت تحب أنَّ تكون شيفرات بايثون 3 التي تكتبها متوافقةً مع بايثون 2، فأبقِ على حرف u قبل السلاسل النصية. استمرار التطوير الفارق الرئيسي بين بايثون 3 وبايثون 2 ليس في البنية اللغوية وإنما في أنَّ إصدار بايثون 2.7 سيتوقف دعمه في 2020، وسيستمر تطوير بايثون 3 بميزاتٍ جديدة وإصلاحٍ لمزيدٍ من العلل. التطويرات الأخيرة في اللغة تتضمن تخصيصًا أبسط لإنشاء الأصناف، وطريقةً أوضح للتعامل مع المصفوفات… الاستمرار بتطوير بايثون 3 يعني أنَّ المطورين يمكن أن يعتمدوا على اللغة، وسيطمئنون أنَّ المشاكل التي قد تحدث فيها ستُحَل في فترةٍ قريبة، ويمكن أن تصبح البرامج أكثر كفاءة بإضافة المزيد من الميزات للغة. نقاطٌ أخرى يجب أخذها بعين الاعتبار عليك أن تضع النقاط الآتية بعين الاعتبار عندما تبدأ مشوارك كمبرمج بلغة بايثون، أو عندما تبدأ بتعلم لغة بايثون بعد تعلمك لغيرها. إذا كنتَ تأمل بتعلم اللغة دون أن تفكِّر بمشروعٍ معيّن، فأنصحك بالتفكير بمستقبل بايثون، فسيستمر تطوير ودعم بايثون 3 بينما سيوقف دعم بايثون 2.7 عمّا قريب. أما إذا كنتَ تُخطِّط للانضمام لفريق تطوير أحد المشاريع، فعليك أن تنظر ما هو إصدار بايثون المستخدم فيه، وكيف يؤدي اختلاف الإصدار إلى اختلاف طريقة تعاملك مع الشيفرات، وإذا ما كانت المكتبات البرمجية المستعملة في المشروع مدعومةً في مختلف الإصدارات، وما هي تفاصيل المشروع نفسه… إذا كنت تُفكّر ببدء أحد المشاريع، فيجدر بك أن تنظر ما هي المكتبات المتوفرة وما هي إصدارات بايثون المدعومة. وكما قلنا سابقًا، الإصدارات الأوليّة من بايثون 3 لها توافقية أقل مع المكتبات المبنية لبايثون 2، لكن الكثير منها قد جرى تحويله إلى بايثون 3، وسيستمر ذلك في السنوات الأربع المقبلة. الخلاصة لغة بايثون كبيرة جدًا وموثقة توثيقًا ممتازًا وسهلة التعلم، ومهما كان اختيارك (بايثون 2 أو بايثون 3) فستتمكن من العمل على المشاريع الموجودة حاليًا. صحيحٌ أنّ هنالك بعض الاختلافات المحورية، لكن ليس من الصعب الانتقال من بايثون 3 إلى بايثون 2، وستجد عادةً أنَّ بايثون 2.7 قادرة على تشغيل شيفرات بايثون 3، خصوصًا في بدايات تعلمك للغة. من المهم أن تبقي ببالك أنَّ تركيز المطورين والمجتمع أصبح منصبًّا على بايثون 3، وسيصبح هذه اللغة رائدةً في المستقبل وستلبي الاحتياجات البرمجية المطلوبة، وأنَّ دعم بايثون 2.7 سيقل مع مرور الزمن إلى أن يزول في 2020. ترجمة -وبتصرّف- للمقال [Python 2 vs Python 3: Practical Considerations] لصاحبته Lisa Tagliaferri
  13. لقد أنشأتَ شركتك لتلبي احتياجات مستخدميك، أليس كذلك؟ سواءً كنتَ ستساعدهم شخصيًا أو على الهاتف أو عبر البريد الإلكتروني، وتستطيع مساعدتهم أيضًا –دون تدخلٍ منك– عبر موقعك الإلكتروني. وسواءً أكانوا زبائن محتملين يستكشفون الموقع، أو زوارًا مشتركين في موقعك لكنهم لم يشتروا شيئًا بعد، أو أنَّهم زبائن يدفعون لقاء خدماتك؛ لكن الأمر ليس مهمًا، وإنما المهم أن تتواصل معهم دومًا. وما لم تكن تسأل زوارك عمّا يفعلون، فكيف ستعرف شعورهم تجاه شركتك؟ هذا الأمر مهمٌ جدًا خصوصًا لموقعك الإلكتروني، الذي يُشكِّل دعامةً أساسيةً لشركتك والذي أنفقته عليه مالًا ووقتًا كثيرًا لبنائه. أقتبس المقولة الآتية من 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.
  14. webgl

    كانت هنالك الكثير من المحاولات الباكرة لابتكار تقنية لإنشاء رسوميات ثلاثية الأبعاد 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.
  15. تأتيني عادةً أسئلةٌ عمّا إذا كانت شبكةٌ متعددة المواقع تعمل على ووردبريس هي خيارٌ مناسبٌ لمواقع الشركات التي لديها أقسامٌ متنوعةٌ ذات طابعٍ مختلف. ويكون جوابي عادةً هو «نعم»، يمكن لشبكةٍ متعددة المواقع فعل ذلك. لكن ليس من الضروري إنشاء موقعٍ منفصلٍ لكل قسمٍ من أقسام شركتك (حتى لو استعملتَ شبكةً متعددة المواقع تعتمد على ووردبريس)؛ فإذا أردتَ مشاركة المحتوى غير الخاص بقسمٍ معيّن، أو كانت «هوية» (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.