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

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

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

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

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

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

    63

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

  1. تمهيد يعدّ ووردبريس أشهر نظام إدارة محتوى (CMS) موجود على الإنترنت، حيث تسمح بإنشاء مدونات ومواقع مرنة بسهولة، بالاعتماد على قواعد بيانات MySQL مع لغة PHP. أصبح ووردبريس منتشرًا انتشارًا واسعًا وهي خيارٌ ممتازٌ لإنشاء موقع إلكتروني بسرعة؛ إذ نستطيع إجراء أغلبية عمليات إدارة الموقع من لوحة التحكم الخاصة به. سنُركِّز في هذا الدرس على ضبط نسخة ووردبريس على خادوم LAMP (أي خادوم يستعمل نظام لينكس، مع خادوم وب Apache، قاعدة بيانات MySQL و لغة البرمجة PHP) على أوبنتو 16.04. المتطلبات المسبقة لكي تستطيع المتابعة مع هذا الدرس، فستحتاج وصولًا إلى خادوم أوبنتو 16.04. يجب عليك تنفيذ المهام الآتية قبل البدء باتباع تعليمات هذا الدرس: إنشاء مستخدم بصلاحيات sudo على الخادوم: إذ سنُطبِّق الأوامر المذكورة في هذا الدرس عبر مستخدمٍ ليس جذرًا لكن لديه امتيازات الجذر عبر الأمر sudo. يمكنك إنشاء مستخدم له امتيازات الجذر باستخدام الأمر sudo باتباع درس الإعداد الابتدائي لخادوم أوبنتو 14.04. تثبيت برمجيات LAMP: تحتاج ووردبريس إلى خادوم ويب وقاعدة بيانات ومُفسِّر PHP لكي تعمل عملًا سليمًا؛ وبالتالي إذا ثبَّتتَ برمجيات LAMP فستُحقق هذا المتطلب. اتبع التعليمات الموجودة في درس كيف تثبت حزم MySQL ،Apache ،Linux :LAMP و PHP على أوبنتو لتعلّم طريقة تثبيت وضبط تلك البرمجيات. تأمين الموقع باستخدام شهادة SSL: تقدّم ووردبريس محتوى ديناميكي وفيها خاصيات للاستيثاق من المستخدمين وتسجيل دخولهم. تسمح لنا تقنية TLS/SSL بتشفير المحتوى المقدَّم من موقعنا مما يعني أنَّ الاتصال أصبح آمنًا. إلا أنَّ الطريقة التي ستتبعها لضبط SSL تختلف فيما إذا كان لديك اسم نطاق لموقعك أم لا: إذا كان لديك اسم نطاق خاص بالموقع: أسهل طريقة لتشفير موقعك هي بالاستعانة بخدمة Let’s Encrypt والتي توفِّر شهادات مجانية وموثوقة. راجع الدرس تنصيب شهادة SSL مجانية عبر خدمة Let’sencrypt على خادوم لينكس لمزيدٍ من المعلومات. إذا لم يكن لديك اسم نطاق خاص بالموقع: وكنتَ تجرب ضبط ووردبريس للتعلّم أو لاستخدامك الشخصي، فيمكنك استخدام شهادة موقعة ذاتيًا. وهذا يوفِّر نوع التشفير نفسه، لكن دون التحقق من النطاق الخاص بك. راجع هذا الدرس لمزيدٍ من المعلومات حول ضبط الشهادات الموقعة ذاتيًا. إذا كانت المتطلبات المسبقة جاهزةً، فسجِّل دخولك إلى الخادوم عبر المستخدم الذي يملك امتيازات الجذر عبر الأمر sudo وتابع بقية هذا الدرس. الخطوة الأولى: إنشاء مستخدم وقاعدة بيانات MySQL لووردبريس أوّل خطوةٍ هي خطوةٌ تحضيريةٌ، إذ تستعمل ووردبريس قواعد MySQL لتخزين وإدارة معلومات الموقع والمستخدمين. ثبتنا مسبقًا قواعد بيانات MySQL على خادومنا، لكننا نحتاج إلى إنشاء قاعدة بيانات ومستخدم لكي تستعمله ووردبريس؛ وذلك بتسجيل الدخول بحساب root (حساب المدير) إلى خادوم MySQL بتنفيذ الأمر الآتي: mysql -u root -p ستُسأل عن كلمة المرور التي أدخلتها لحساب root عندما ثبّتتَ برمجية MySQL. علينا أولًا إنشاء قاعدة بيانات منفصلة لكي تستطيع ووردبريس التحكم بها كيفما تشاء؛ ويمكننا تسميتها بأيِّ اسمٍ يحلو لنا، لكننا سنستخدم الاسم wordpress في درسنا للسهولة. يمكنك إنشاء قاعدة بيانات في ووردبريس بكتابة تعليمة SQL الآتية: CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci; ملاحظة: يجب أن تنتهي كل تعليمات MySQL بفاصلة منقوطة ;، لذا تأكد من وجودها إن واجهتَ أيّة مشكلات. علينا بعد ذلك إنشاء مستخدم MySQL منفصل يحق له التعامل مع قاعدة البيانات التي أنشأناها آنفًا فقط؛ فمن المستحسن إنشاء قواعد بيانات مع حسابات مستخدمين مرتبطة بها ذات غرضٍ وحيد، وذلك لتسهيل الإدارة وتحسين الأمان. سنستخدم الاسم wordpressuser في هذا الدرس، لكن استعمل أي اسم يحلو لك. سنستعمل الأمر الآتي لإنشاء هذا الحساب وضبط كلمة مرور خاصة به ومنحه وصولًا إلى قاعدة البيانات التي أنشأناه، تذكَّر أن تختار كلمة مرور قوية لمستخدم قواعد البيانات: GRANT ALL ON wordpress.* TO 'wordpressuser'@'localhost' IDENTIFIED BY 'password'; أصبح لدينا مستخدم وقاعدة بيانات خاصَين بووردبريس، علينا الآن تحديث جدول الامتيازات لكي يعتمد MySQL التغييرات الأخيرة التي أجريناها: FLUSH PRIVILEGES; يمكننا الخروج من سطر أوامر MySQL بكتابة: EXIT; الخطوة الثانية: تثبيت إضافات PHP عندما ضبطنا برمجيات LAMP ثبّتنا الحد الأدنى اللازم من وِحْدات PHP لكي تتواصل مع MySQL تواصلًا سليمًا؛ لكن ووردبريس والإضافات التابعة لها تستعمل وحدات PHP أخرى. يمكننا تنزيل وتثبيت أشهر وحدات PHP لاستعمالها مع ووردبريس بتنفيذ الأمرين الآتيين: sudo apt-get update sudo apt-get install php-curl php-gd php-mbstring php-mcrypt php-xml php-xmlrpc ملاحظة: لكل إضافة من إضافات ووردبريس متطلبات خاصة بها، وبعضها يستدعي تثبيت حزم PHP إضافية. راجع توثيق الإضافة لتعرف ما هي المتطلبات المسبقة التي تحتاج لها؛ والتي يمكن تثبيتها عبر apt-get (أو apt) إذا كانت متوافرة في المستودعات. علينا الآن إعادة تشغيل خادوم أباتشي لكي نستطيع استخدام الوحدات التي ثبتناها، يمكننا فعل ذلك بتنفيذ الأمر الآتي: sudo systemctl restart apache2 الخطوة الثالثة: تعديل ضبط أباتشي للسماح باستعمال ملفات ‎.htaccess ولتفعيل وحدة Rewrite علينا الآن إجراء تعديلات بسيطة على ضبط أباتشي؛ فاستعمال ملفات ‎.htaccess مُعطّلٌ حاليًا، لكن ووردبريس (وبعض إضافاتها) تستعمل هذه الملفات لإجراء تعديلات على سلوك خادوم الويب في بعض المجلدات. علينا أيضًا تفعيل وحدة mod_rewrite في أباتشي والتي سنحتاج لها لكي تعمل ميزة الروابط الدائمة (permalinks) في ووردبريس عملًا سليمًا. تفعيل ملفات ‎.htaccess افتح ملف ضبط أباتشي الأساسي لإجراء أوّل تعديل: sudo nano /etc/apache2/apache2.conf للسماح باستخدام ملفات ‎.htaccess فعلينا ضبط التعليمة AllowOverride ضمن كتلة Directory التي تُشير إلى جذر الموقع. أضف الأسطر الآتية في أسفل الملف: <Directory /var/www/html/> AllowOverride All </Directory> أنهينا تعديل هذا الملف، لذا احفظه وأغلقه. تفعيل وحدة Rewrite علينا الآن تفعيل وحدة mod_rewrite لكي نستطيع الاستفادة من ميزة الروابط الدائمة في ووردبريس: sudo a2enmod rewrite تفعيل التعديلات التي أجريناها قبل تطبيق التعديلات على الخادوم، علينا أن نحرص أننا لم نرتكب أيّ أخطاء في بنية التعليمات: sudo apache2ctl configtest من المفترض أن يكون الناتج شبيهًا بالرسالة الآتية: AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message Syntax OK إذا أردت التخلص من أوّل سطر، فأضف التعليمة SeverName إلى ملف ‎/etc/apache2/apache2.conf التي تشير إلى اسم نطاق الخادوم أو إلى عنوان IP الخاص به، لكن اعلم أنَّ هذه الرسالة لا تؤثر على عمل موقعنا، فلطالما كان الناتج يحتوي على السطر Syntax OK سنستطيع أن نكمل عملية تفعيل التعديلات بإعادة تشغيل خادوم أباتشي: sudo systemctl restart apache2 الخطوة الرابعة: تنزيل ووردبريس بعد إكمالنا لضبط خادومنا، أصبحنا جاهزين لتنزيل وضبط ووردبريس. من المستحسن الحصول على آخر إصدار من ووردبريس من موقعها الرسمي وذلك حرصًا على الحصول على آخر التحديثات الأمنية. انتقل إلى مجلد تستطيع الكتابة عليه، ثم نزِّل الملف المضغوط من الإنترنت عبر كتابة الأمرين الآتيين: cd /tmp curl -O https://wordpress.org/latest.tar.gz استخرج محتويات الملف المضغوط: tar xzvf latest.tar.gz سننقل هذه الملفات إلى المجلد الجذر لموقعنا بعد لحظات، لكن قبل ذلك سنُنشِئ ملف ‎.htaccess فارغ ونضبط الأذونات الخاصة به لكي تتاح مستقبلًا لووردبريس لتستعملها. أنشِئ الملف واضبط أذوناته بتنفيذ الأمرين: touch /tmp/wordpress/.htaccess chmod 660 /tmp/wordpress/.htaccess سننسخ أيضًا الملف التي يحتوي على مثال عن ضبط ووردبريس إلى الملف الذي ستقرأه ووردبريس: cp /tmp/wordpress/wp-config-sample.php /tmp/wordpress/wp-config.php سنُنشِئ أيضًا مجلدًا باسم upgrade لكي لا تواجه ووردبريس مشاكل في الأذونات عندما تحاول تنزيل التحديثات من الإنترنت: mkdir /tmp/wordpress/wp-content/upgrade يمكننا الآن نسخ كامل محتويات المجلد إلى المجلد الجذر لموقعنا، وسنستخدم الخيار ‎-a لإبقاء الأذونات كما هي؛ وسنستخدم نقطة في نهاية مسار المجلد الذي سننسخه للإشارة إلى أننا نريد نسخ كل شيء في ذاك المجلد بما في ذلك المجلدات المخفية (مثل الملف ‎.htaccess الذي أنشأناها منذ قليل): sudo cp -a /tmp/wordpress/. /var/www/html الخطوة الخامسة: ضبط مجلد ووردبريس قبل أن نُشغِّل معالج التثبيت الخاص بووردبريس من المتصفح، علينا تعديل بعض الأشياء في مجلد ووردبريس. تعديل الملكية والأذونات أحد أهم الأمور التي علينا فعلها هو ضبط ملكية الملفات وأذوناتها، فسنحتاج إلى الكتابة إلى تلك الملفات كمستخدمٍ عادي، وعلينا السماح لخادوم الويب بالوصول إليها وتعديل ملفات ومجلدات معيّنة لكي تعمل ووردبريس كما ينبغي. سنبدأ بضبط ملكية كل الملفات الموجودة في جذر الموقع إلى المستخدم الحالي (الذي سنسميه sammy في هذا الدرس، لكن عليك تعديله ليُطابِق اسم المستخدم الذي تستعمله للوصول إلى امتيازات الجذر)، وسنضبط المجموعة المالكة إلى www-data: sudo chown -R sammy:www-data /var/www/html علينا الآن ضبط الأذن الخاص setgid على كل مجلد موجود ضمن مجلد الجذر للموقع، وهذا يؤدي إلى وراثة الملفات المُنشأة ضمن تلك المجلدات لمجموعة المجلد الأب (والتي ضبطناها إلى www-data) بدلًا من جعلها مملوكةً لمجموعة المستخدم المُنشِئ لها؛ وهذا يعني أننا لو أنشأنا ملفًا ضمن أحد تلك المجلدات باستخدام سطر الأوامر فسيملكه خادوم الويب (عبر المجموعة www-data). يمكننا ضبط الإذن setgid على كل مجلد من مجلدات ووردبريس بتنفيذ الأمر الآتي: sudo find /var/www/html -type d -exec chmod g+s {} \; هنالك أذونات أخرى علينا تعديلها؛ فعلينا إعطاء المجموعة المالكة إذن الكتابة إلى مجلد wp-content لكي نتمكن من تعديل القوالب والإضافات من واجهة الويب: sudo chmod g+w /var/www/html/wp-content وعلينا إعطاء خادوم الويب إذن الكتابة على جميع محتويات المجلدين themes و plugins كما يلي: sudo chmod -R g+w /var/www/html/wp-content/themes sudo chmod -R g+w /var/www/html/wp-content/plugins الأذونات الحالية مناسبة لأغلبية حالات الاستخدام، لكن قد تتطلّب بعض الإضافات تعديلاتٍ أخرى للأذونات. إعداد ملف ضبط ووردبريس سنحتاج الآن إلى إجراء بعض التعديلات على ملف ضبط ووردبريس الرئيسي. من أهم أولوياتنا هو تعديل بعض المفاتيح السرية لتوفير مزيد من الحماية لووردبريس. توفر لنا ووردبريس مولِّدًا لإنشاء هذه القيم وليس علينا إنشاؤها بأنفسنا؛ تُستخدم هذه المفاتيح داخليًا فقط، لذا لن تتأثر سهولة الاستخدام إذا وضعنا قيمًا معقدة هنا. نفِّذ الأمر الآتي للحصول على قيم آمنة من مولِّد ووردبريس: curl -s https://api.wordpress.org/secret-key/1.1/salt/ ستحصل على قيمٍ فريدةٍ كما هو ظاهر في المثال الآتي: تحذير: من المهم جدًا طلب قيم فريدة في كل مرة. لا تنسخ القيم الآتية! define('AUTH_KEY', '1jl/vqfs<XhdXoAPz9 DO NOT COPY THESE VALUES c_j{iwqD^<+c9.k<J@4H'); define('SECURE_AUTH_KEY', 'E2N-h2]Dcvp+aS/p7X DO NOT COPY THESE VALUES {Ka(f;rv?Pxf})CgLi-3'); define('LOGGED_IN_KEY', 'W(50,{W^,OPB%PB<JF DO NOT COPY THESE VALUES 2;y&,2m%3]R6DUth[;88'); define('NONCE_KEY', 'll,4UC)7ua+8<!4VM+ DO NOT COPY THESE VALUES #`DXF+[$atzM7 o^-C7g'); define('AUTH_SALT', 'koMrurzOA+|L_lG}kf DO NOT COPY THESE VALUES 07VC*Lj*lD&?3w!BT#-'); define('SECURE_AUTH_SALT', 'p32*p,]z%LZ+pAu:VY DO NOT COPY THESE VALUES C-?y+K0DK_+F|0h{!_xY'); define('LOGGED_IN_SALT', 'i^/G2W7!-1H2OQ+t$3 DO NOT COPY THESE VALUES t6**bRVFSD[Hi])-qS`|'); define('NONCE_SALT', 'Q6]U:K?j4L%Z]}h^q7 DO NOT COPY THESE VALUES 1% ^qUswWgn+6&xqHN&%'); يمكنك نسخ الأسطر الظاهرة عندك مباشرةً إلى ملف الضبط لإعداد المفاتيح الآمنة. انسخ المخرجات التي حصلتَ عليها من الأمر السابق. افتح الآن ملف ضبط ووردبريس: nano /var/www/html/wp-config.php ابحث عن القسم الذي يحتوي على القيم المبدئية لهذه المفاتيح؛ إذ سيبدو كما يلي: . . . define('AUTH_KEY', 'put your unique phrase here'); define('SECURE_AUTH_KEY', 'put your unique phrase here'); define('LOGGED_IN_KEY', 'put your unique phrase here'); define('NONCE_KEY', 'put your unique phrase here'); define('AUTH_SALT', 'put your unique phrase here'); define('SECURE_AUTH_SALT', 'put your unique phrase here'); define('LOGGED_IN_SALT', 'put your unique phrase here'); define('NONCE_SALT', 'put your unique phrase here'); . . . امسح الأسطر أعلاه وألصق الأسطر التي نسختها من الأمر السابق: . . . define('AUTH_KEY', 'VALUES COPIED FROM THE COMMAND LINE'); define('SECURE_AUTH_KEY', 'VALUES COPIED FROM THE COMMAND LINE'); define('LOGGED_IN_KEY', 'VALUES COPIED FROM THE COMMAND LINE'); define('NONCE_KEY', 'VALUES COPIED FROM THE COMMAND LINE'); define('AUTH_SALT', 'VALUES COPIED FROM THE COMMAND LINE'); define('SECURE_AUTH_SALT', 'VALUES COPIED FROM THE COMMAND LINE'); define('LOGGED_IN_SALT', 'VALUES COPIED FROM THE COMMAND LINE'); define('NONCE_SALT', 'VALUES COPIED FROM THE COMMAND LINE'); . . . سنحتاج الآن إلى تعديل معلومات الاتصال بقاعدة البيانات الموجودة في بداية الملف؛ إذ علينا تعديل اسم قاعدة البيانات واسم المستخدم وكلمة المرور الخاصة به والتي ضبطناها في MySQL. إضافةً إلى ذلك، علينا تعديل الطريقة التي على ووردبريس استخدامها للكتابة إلى نظام الملفات، ولمّا كنا قد أعطينا خادوم الويب الإذن للكتابة حيث يحتاج، فيمكننا تحديد طريقة الكتابة إلى نظام الملفات إلى direct. إذا لم نستعمل هذا الخيار في ملف الضبط فسيؤدي ذلك إلى طلب معلومات FTP عندما تحتاج ووردبريس إلى إجراء بعض العمليات على نظام الملفات. يمكن إضافة هذا الخيار تحت خيارات الاتصال بقاعدة البيانات، أو إلى أي مكان آخر في الملف: . . . define('DB_NAME', 'wordpress'); /** MySQL database username */ define('DB_USER', 'wordpressuser'); /** MySQL database password */ define('DB_PASSWORD', 'password'); . . . define('FS_METHOD', 'direct'); احفظ الملف وأغلقه عندما تنتهي من العمل عليه. الخطوة السادسة: إكمال التثبيت عبر واجهة الويب اكتمل الآن ضبط الخادوم، ويمكننا متابعة التثبيت عبر واجهة الويب. افتح متصفح الويب الخاص بك وانتقل إلى اسم نطاق الخادوم أو عنوان IP العام الخاص به: http://server_domain_or_IP اختر اللغة التي تشاء استخدامها: ثم ننتقل إلى صفحة الإعداد الرئيسية. اختر اسمًا لموقع ووردبريس واختر اسم المستخدم (من المستحسن عدم استعمال اسم مثل «admin» لأسباب أمنية). ستولَّد كلمة مرور قوية تلقائيًا، يمكنك حفظ هذه الكلمة أو اختيار أخرى قوية. أدخِل عنوان بريدك الإلكتروني واختر ما إذا كنت تريد فهرسة موقعك من محركات البحث أم لا. بعد أن تنتهي من التثبيت ستؤخذ إلى صفحة تطلب منك تسجيل الدخول: بعد أن تسجِّل دخولك، فستؤخذ إلى لوحة تحكم ووردبريس. ترقية ووردبريس عندما يتوافر إصدار جديد من ووردبريس، فلن تتمكن من تثبيته عبر واجهة الويب وذلك بسبب أذونات الملفات الحالية. الغرض من الأذونات التي اخترناها هو الموازنة بين الأمان وقابلية الاستخدام لحوالي 99% من الحالات، لكنها لن تسمح بتثبيت التحديثات التلقائية. فعند توافر ترقية لووردبريس فعليك تسجيل الدخول مجددًا إلى خادوم الويب كمستخدم sudo، ثم منح عملية خادوم الويب وصولًا إلى جذر الموقع: sudo chown -R www-data /var/www/html افتح لوحة تحكم ووردبريس وحدثها كالمعتاد. بعد أن تنتهي من الترقية، فأعد الأذونات كما كانت سابقًا: sudo chown -R sammy /var/www/html الطريقة السابقة ضرورية عند ترقية برمجية ووردبريس نفسها. الخلاصة يجب أن تكون ووردبريس مثبتةً على خادومك وجاهزةً للاستخدام. هنالك بعض الأمور التي عليك ضبطها مثل بنية الروابط الدائمة لمنشوراتك (في صفحة Settings > Permalinks أو الخيارات > الروابط الدائمة) واختيار قالب جديد (في صفحة Appearance > Themes أو المظهر > قوالب). إذا كانت هذه أوّل مرة تستعمل فيها ووردبريس فخذ وقتًا في تعلم طريقة التعامل معها. ترجمة – بتصرّف – للمقال How To Install WordPress with LAMP on Ubuntu 16.04 لصاحبه Justin Ellingwood.
  2. تمهيد إنَّ MySQL هي أشهر نظام مفتوح المصدر لإدارة قواعد البيانات العلاقية في العالم؛ وحاولت برمجيات إدارة الحزم تقليل الجهد اللازم لتثبيت خادوم MySQL وتشغيله، لكن ما يزال عليك إجراء بعض عمليات الضبط بعد التثبيت. وأنصحك بقضاء بعض الوقت محاولًا زيادة حماية وأمان قواعد بياناتك. تُضبَط MySQL مبدئيا لقبول الاتصالات المحلية فقط، فلو أردتَ السماح بالاتصالات «البعيدة» فمن المهم أن تكون تلك الاتصالات آمنة؛ وسنشرح في هذا الدرس كيفية السماح بالاتصالات البعيدة إلى خادوم MySQL على أوبنتو 16.04 مع تشفير SSL/TLS. المتطلبات المسبقة إذا أردتَ المتابعة مع هذا الدرس، فستحتاج إلى خادومَي أوبونتو 16.04، إذ سنستخدم أحدها لاستضافة خادوم MySQL وسيلعب الآخر دور العميل. عليك أيضًا إنشاء مستخدم ليس جذرًا لكنه يمتلك امتيازات الجذر عبر الأمر sudo، راجع درس الإعداد الابتدائي لخادوم أوبنتو 14.04 للمزيد من المعلومات حول الضبط المبدئي لخادومك. يجب أن تثبّت على الجهاز الأول خادوم MySQL وتضبطه؛ يمكنك العودة إلى درستثبيت وإعداد نظامي إدارة قواعد البياناتMySQL وPostgreSQL على أوبنتو لمزيدٍ من المعلومات حول تثبيت وضبط هذه البرمجية. أما على الجهاز الثاني، فعليك تثبيت حزمة عميل MySQL، إذ تستطيع استخدام الأمر apt لتحديث فهرس الحزم ثم تثبيت البرمجيات الضرورية وذلك بتنفيذ الأمرين الآتيين: sudo apt-get update sudo apt-get install mysql-client يُفتَرَض أن يعمل خادومك وعمليك عملًا سليمًا. التحقق من حالة تشفير SSL/TLS الراهنة على خادوم MySQL علينا قبل البدء أن نتحقق من الحالة الراهنة لتشفير SSL/TLS على خادومنا. سجِّل دخولك إلى خادومك MySQL عبر المستخدم root التابع لقواعد MySQL. سنستخدم في الأمر الآتي الخيار ‎-h لتحديد عنوان IP للجهاز المحلي لكي يتصل العميل باستخدام بروتوكول TCP بدلًا من استخدام ملف socket محلي؛ وهذا سيُمكننا من التحقق من حالة تشفير SSL لاتصالات TCP: mysql -u root -p -h 127.0.0.1 ستُسأل عن كلمة مرور مستخدم root التي اخترتها أثناء عملية تثبيت خادوم MySQL؛ وبعدئذٍ ستنتقل إلى جلسة MySQL تفاعلية. يمكننا إظهار حالة متغيرات SSL/TLS عبر كتابة التعليمة الآتية: SHOW VARIABLES LIKE '%ssl%'; الناتج: +---------------+----------+ | Variable_name | Value | +---------------+----------+ | have_openssl | DISABLED | | have_ssl | DISABLED | | ssl_ca | | | ssl_capath | | | ssl_cert | | | ssl_cipher | | | ssl_crl | | | ssl_crlpath | | | ssl_key | | +---------------+----------+ 9 rows in set (0.01 sec) لاحظ أنَّ قيمة المتغيرين have_openssl و have_ssl تساوي DISABLED، وهذا يعني أنَّ دعم تشفير SSL مبنيٌ داخل الخادوم لكنه ليس مفعلًا بعد. لنتحقق من حالة الاتصال الحالي للتأكد من النتائج السابقة، وذلك بإدخال: \s الناتج: -------------- mysql Ver 14.14 Distrib 5.7.17, for Linux (x86_64) using EditLine wrapper Connection id: 30 Current database: Current user: root@localhost SSL: Not in use Current pager: stdout Using outfile: '' Using delimiter: ; Server version: 5.7.17-0ubuntu0.16.04.1 (Ubuntu) Protocol version: 10 Connection: 127.0.0.1 via TCP/IP Server characterset: latin1 Db characterset: latin1 Client characterset: utf8 Conn. characterset: utf8 TCP port: 3306 Uptime: 3 hours 38 min 44 sec Threads: 1 Questions: 70 Slow queries: 0 Opens: 121 Flush tables: 1 Open tables: 40 Queries per second avg: 0.005 -------------- يمكننا أن نلاحظ من الناتج السابق أنَّ الاتصال غير مشفر عبر SSL، حتى لو كنّا قد اتصلنا عبر بروتوكول TCP. أغلق جلسة MySQL عندما تنتهي: exit يمكننا الآن البدء بضبط MySQL لتستعمل تشفير SSL للاتصالات القادمة إليها. توليد شهادات ومفاتيح SSL/TLS لتفعيل اتصالات مشفرة عبر SSL إلى MySQL، فعلينا أولًا توليد الشهادات والمفاتيح المناسبة. هنالك أداةٌ باسم mysql_ssl_rsa_setup متوافرة مع MySQL 5.7 وما بعدها مهمتها تبسيط هذه العملية؛ وهنالك إصدار MySQL متوافق معها متوافر في أوبنتو 16.04، لذا سنستخدم هذا الأمر لتوليد الملفات اللازمة. ستُنشَأ الملفات في مجلد ملفات MySQL الموجود في المسار ‎/var/lib/mysql؛ وعلينا السماح لعملية MySQL (أي MySQL process) بقراءة الملفات المولّدة، لذا سنُمرِّر القيمة mysql اسمًا للمستخدم الذي سيملك الملفات المولّدة: sudo mysql_ssl_rsa_setup –uid=mysql سيكون ناتج عملية التوليد مشابهًا لما يلي: Generating a 2048 bit RSA private key ...................................+++ .....+++ writing new private key to 'ca-key.pem' ----- Generating a 2048 bit RSA private key ......+++ .................................+++ writing new private key to 'server-key.pem' ----- Generating a 2048 bit RSA private key ......................................................+++ .................................................................................+++ writing new private key to 'client-key.pem' ----- تحقق من إنشاء الملفات الذي جرت عملية توليدها بالأمر find: sudo find /var/lib/mysql -name '*.pem' -ls الناتج: 256740 4 -rw-r--r-- 1 mysql mysql 1078 Mar 17 17:24 /var/lib/mysql/server-cert.pem 256735 4 -rw------- 1 mysql mysql 1675 Mar 17 17:24 /var/lib/mysqlsql/ca-key.pem<^> 256739 4 -rw-r--r-- 1 mysql mysql 451 Mar 17 17:24 /var/lib/mysqlsql/public_key.pem<^> 256741 4 -rw------- 1 mysql mysql 1679 Mar 17 17:24 /var/lib/mysqlsql/client-key.pem<^> 256737 4 -rw-r--r-- 1 mysql mysql 1074 Mar 17 17:24 /var/lib/mysqlsql/ca.pem<^> 256743 4 -rw-r--r-- 1 mysql mysql 1078 Mar 17 17:24 /var/lib/mysqlsql/client-cert.pem<^> 256736 4 -rw------- 1 mysql mysql 1675 Mar 17 17:24 /var/lib/mysqlsql/private_key.pem<^> 256738 4 -rw------- 1 mysql mysql 1675 Mar 17 17:24 /var/lib/mysqlsql/server-key.pem<^> آخر عمود من الناتج السابق يُظهِر أسماء الملفات المولّدة، أما العمود الرابع والخامس فيؤكدان أنَّ المستخدم المالك والمجموعة المالكة هو mysql. تُمثِّل هذه الملفات مفاتيح وشهادات لسلطة الشهادات Certificate authority (للملفات التي تبدأ بالسابقة «ca»)، ولعملية خادوم MySQL (للملفات التي تبدأ بالسابقة «server»)، ولعملاء MySQL (للملفات التي تبدأ بالسابقة «client»). إضافةً إلى ما سبق، هنالك الملفان private_key.pem و public_key.pem اللذان تستعملهما MySQL لنقل كلمة المرور بأمان دون استخدام SSL. تفعيل اتصالات SSL الآمنة على خادوم MySQL ستبحث الإصدارات الحديثة من MySQL عن ملفات الشهادات الملائمة ضمن مجلد بيانات MySQL عندما يبدأ الخادوم. ولهذا السبب، لن نحتاج إلى تعديل ضبط MySQL لتفعيل تشفير SSL. كل ما علينا فعله هو إعادة تشغيل خدمة MySQL: sudo systemctl restart mysql بعد إعادة التشغيل، افتح اتصالًا إلى خادوم MySQL بالأمر الذي ذكرناه في بداية هذا الدرس؛ وسيحاول عميل MySQL الاتصال عبر تشفير SSL إن كان مدعومًا من الخادوم: mysql -u root -p -h 127.0.0.1 لننظر مرةً أخرى إلى المعلومات التي طلبناها أوّل مرة، ولنلاحظ القيم المرتبطة بالمتغيرات المتعلقة بتشفير SSL: SHOW VARIABLES LIKE '%ssl%'; الناتج: +---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | have_openssl | YES | | have_ssl | YES | | ssl_ca | ca.pem | | ssl_capath | | | ssl_cert | server-cert.pem | | ssl_cipher | | | ssl_crl | | | ssl_crlpath | | | ssl_key | server-key.pem | +---------------+-----------------+ 9 rows in set (0.00 sec) نجد أنَّ قيم المتغيرات have_openssl و have_ssl أصبحت YES بدلًا من DISABLED؛ وسنلاحظ وجود قيم مرتبطة بالمتغيرات ssl_ca و ssl_cert و ssl_key وهي أسماء ملفات الشهادات المولَّدة. لنتحقق مجددًا من معلومات الاتصال بتنفيذ: \s الناتج: -------------- . . . SSL: Cipher in use is DHE-RSA-AES256-SHA . . . Connection: 127.0.0.1 via TCP/IP . . . -------------- أصبح الناتج هذه المرة يشير إلى استخدام تشفير SSL لجعل اتصالنا مع الخادوم آمنًا. لنعد إلى سطر الأوامر بالخروج من MySQL: exit أصبح بإمكان خادومنا الآن تشفير الاتصالات، لكن ما يزال علينا إجراء بعض الضبط الإضافي للسماح بالاتصالات البعيدة وإجبار العملاء على استخدام الاتصالات الآمنة. ضبط الاتصالات الآمنة للاتصالات البعيدة بعد أن أصبح تشفير SSL متاحًا على الخادوم، يمكننا البدء بضبط الوصول البعيد، وعلينا فعل ما يلي لتحقيق ذلك: جعل استخدام SSL ضروريًا للاتصال. ربط الخادوم ببطاقة شبكية عامة. إنشاء مستخدم MySQL للاتصالات البعيدة. تعديل قواعد الجدار الناري للسماح بالاتصالات الخارجية. جعل استخدام SSL ضروريًا لإجراء الاتصال أصبح خادوم MySQL مضبوطًا لقبول اتصالات SSL من العملاء؛ لكنه ما يزال يسمح بإجراء اتصالات غير مشفرة إذا طلب العميل ذلك. يمكننا حل هذه المشكلة بتفعيل الخيار require_secure_transport الذي لا يسمح إلا بالاتصالات المشفرة عبر SSL أو عبر مقبس Unix محلي؛ ولعدم إتاحة الاتصال عبر المقبس المحلي إلا ضمن الخادوم نفسه، فالحل الوحيد لاتصال العملاء البعيدين هو استخدام SSL للتشفير. عدِّل ملف ‎/etc/mysql/my.cnf باستعمال محررك النصي المفضل لتفعيل هذا الخيار: sudo nano /etc/mysql/my.cnf ستجد داخل الملف تعليمتَي ‎!includedir التي تستخدم لقراءة ملفات الضبط الإضافية، علينا وضع الضبط الخاص بنا تحت تلك الأسطر لتجاوز أيّة تعليمات ضبط موجودة داخل ملفات الضبط الإضافية. ابدأ بإنشاء قسم [mysqld] لاستهداف عملية خادوم MySQL، واضبط تحت ترويسة هذا القسم الخيار require_secure_transport إلى ON: . . . !includedir /etc/mysql/conf.d/ !includedir /etc/mysql/mysql.conf.d/ [mysqld] # Require clients to connect either using SSL # or through a local socket file require_secure_transport = ON لن نحتاج إلى أكثر من هذا السطر لجعل استخدام الاتصالات المشفرة ضروريًا. إنَّ خادوم MySQL مضبوطٌ مبدئيًّا للاستماع إلى الاتصالات الآتية من الحاسوب المحلي فقط، وللاستماع إلى الاتصالات البعيدة، فيمكننا ضبط قيمة التعليمة bind-address للإشارة إلى بطاقة شبكية مختلفة. للسماح لخادوم MySQL بقبول الاتصالات من جميع البطاقات الشبكية، فيمكننا ضبط قيمة التعليمة bind-address إلى 0.0.0.0: . . . !includedir /etc/mysql/conf.d/ !includedir /etc/mysql/mysql.conf.d/ [mysqld] # Require clients to connect either using SSL # or through a local socket file require_secure_transport = ON bind-address = 0.0.0.0 احفظ الملف وأغلقه بعد أن تنتهي من تعديله. أعد تشغيل خادوم MySQL لتطبيق الضبط الجديد: sudo systemctl restart mysql تأكد أنَّ خادوم MySQL يستمع على 0.0.0.0 بدلًا من 127.0.0.1 بكتابة: sudo netstat -plunt الناتج: Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 4330/mysqld tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1874/sshd tcp6 0 0 :::22 :::* LISTEN 1874/sshd القيمة 0.0.0.0 في الناتج السابق تُشير إلى أنَّ خدمة MySQL تستمع إلى الاتصالات في جميع البطاقات الشبكية المتاحة. علينا بعدئذٍ السماح لاتصالات MySQL عبر الجدار الناري، وذلك بإنشاء استثناء كما يلي: sudo ufw allow mysql الناتج: Rule added Rule added (v6) يجب أن تتمكن الاتصالات البعيدة من الوصول إلى خادوم MySQL. إنشاء مستخدم MySQL للاتصالات البعيدة أصبح خادوم MySQL جاهزًا للاستماع إلى الاتصالات البعيدة، لكننا لا نملك حاليًا أيّ مستخدم مضبوط لكي يتصل من حاسوبٍ آخر. سجِّل دخولك إلى خادوم MySQL عبر المستخدم root: mysql -u root -p يمكننا الآن إنشاء حساب للمستخدم البعيد عبر الأمر CREATE USER، وسنستخدم عنوان IP لجهاز العميل في قسم المضيف من تعليمة تعريف المستخدم الجديد لجعل الاتصال عبر هذا المستخدم محصورًا بذاك الجهاز. ولضمان الاتصال عبر SSL حتى لو عُطِّلَ الخيار require_secure_transport مستقبلًا، فسنُحدِّد في تعليمة تعريف الحساب أنَّه يجب استعمال SSL عند الاتصال باستخدام هذا الحساب بتضمين REQUIRE SSL: CREATE USER 'remote_user'@'mysql_client_IP' IDENTIFIED BY 'password' REQUIRE SSL; علينا بعد ذلك أن نمنح المستخدم صلاحيات على قواعد البيانات أو الجداول التي ينبغي له الوصول إليها؛ وسنُنشِئ قاعدة بيانات لشرح هذه الفكرة باسم example وسنمنح مستخدمنا ملكيتها: CREATE DATABASE example; GRANT ALL ON example.* TO 'remote_user'@'mysql_client_IP'; علينا تحديث جداول الصلاحيات لتطبيق التعديلات مباشرةً: FLUSH PRIVILEGES; أغلِق جلسة MySQL بعد أن تنتهي من تنفيذ الأوامر السابقة: exit اختبار الاتصالات البعيدة لاختبار إمكانية إجراء اتصال من جهاز العميل إلى الخادوم بنجاح، فاستخدم الخيار ‎-u لتحديد اسم المستخدم البعيد، والخيار ‎-h لتحديد عنوان IP لخادوم MySQL: mysql -u remote_user -p -h mysql_server_IP بعد كتابتك لكلمة المرور، فستسجل دخولك إلى الخادوم البعيد. نفذ الأمر الآتي للتأكد من أنَّ اتصالك آمن: \s الناتج: -------------- . . . SSL: Cipher in use is DHE-RSA-AES256-SHA . . . Connection: mysql_server_IP via TCP/IP . . . -------------- اخرج من عميل MySQL وعد إلى سطر الأوامر: exit جرِّب الآن الاتصال بطريقة غير آمنة إلى الخادوم: mysql -u remote_user -p -h mysql_server_IP –ssl-mode=disabled بعد أن يُطلَب منك إدخال كلمة المرور، فستجد أنَّ الاتصال قد رُفِض: ERROR 1045 (28000): Access denied for user 'remote_user'@'mysql_server_IP' (using password: YES) هذا ما بدأنا درسنا محاولين إنجازه: فالاتصالات المشفرة عبر SSL إلى الخادوم مسموحة، والاتصالات غير المشفرة ممنوعة. لهذا الحد، أصبح خادوم MySQL مضبوطًا للسماح بالاتصالات البعيدة الآمنة؛ ويمكنك أن تتوقف عند هذه النقطة إذا كان ذلك يلبي احتياجاتك الأمنية، لكن هنالك المزيد من الأمور التي يمكنك فعلها لزيادة الحماية. ضبط التحقق من الاتصالات الآتية إلى خادوم MySQL إنَّ خادوم MySQL مضبوطٌ حاليًا مع شهادة SSL موقعة من سلطة شهادات محلية (Local certificate authority)؛ والشهادة والمفتاح الخاصَين بالخادوم كافيان لتشفير الاتصالات القادمة إلى الخادوم. لكننا لم نستفد من «الثقة» التي يمكن لسلطة الشهادات توفيرها، فعند توزيع شهادة سلطة الشهادات إلى العملاء، إضافةً إلى شهادة ومفتاح خاصَين بالعميل، فيمكن لكلا الطرفين التأكد أنَّ الشهادات موقعة من سلطة شهادات موثوقة. يمكن أن يساعد ذلك بمنع الاتصالات المزورة إلى أجهزة خبيثة. لتطبيق هذا النوع من الحماية الإضافية، فسنحتاج إلى: نقل ملفات SSL الملائمة إلى جهاز العميل إنشاء ملف ضبط للعميل تعديل حساب المستخدم البعيد لطلب وجود شهادة موثوقة نقل شهادات العميل إلى جهازه علينا بادئ الأمر الحصول على شهادة سلطة الشهادات التابعة لخادوم MySQL مع ملفات شهادات العميل ووضعها في جهاز العميل. سنبدأ بفعل ذلك بإنشاء مجلد باسم client-ssl في جهاز العميل في مجلد المنزل للمستخدم الذي ستستعمله للاتصال: mkdir ~/client-ssl ولأنَّ مفتاح الشهادة هو ملفٌ حساس، فعلينا قفل الوصول إلى المجلد لكي لا يتمكن أي شخصٍ سوى المستخدم المالك له من الوصول إليه: chmod 700 ~/client-ssl يمكننا الآن نسخ معلومات الشهادة إلى مجلدٍ جديد. اعرض محتوى شهادة سلطة الشهادات في خادوم MySQL بكتابة الأمر الآتي: sudo cat /var/lib/mysql/ca.pem الناتج: -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- انسخ جميع المحتوى بما فيه الأسطر التي فيها BEGIN CERTIFICATE و END CERTIFICATE إلى الحافظة. أنشِئ ملفًا جديدًا في جهاز العميل باستعمال الأمر الآتي: nano ~/client-ssl/ca.pem ألصق محتوى الشهادة المنسوخ في ذاك الملف، ثم احفظه وأغلق الملف عندما تنتهي منه. علينا الآن عرض محتوى شهادة العميل في خادوم MySQL: sudo cat /var/lib/mysql/client-cert.pem الناتج: -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- انسخ محتوياته مجددًا، واحرص على تضمين أول وآخر سطر. أنشِئ ملفًا جديدًا في عميل MySQL بالاسم نفسه ضمن مجلد client-ssl: nano ~/client-ssl/client-cert.pem ألصق محتويات الحافظة إلى الملف، ثم احفظه وأغلقه. ثم اعرض محتويات مفتاح العميل على خادوم MySQL: sudo cat /var/lib/mysql/client-key.pem الناتج: -----BEGIN RSA PRIVATE KEY----- . . . -----END RSA PRIVATE KEY----- انسخ محتوياته مجددًا إلى حافظتك، واحرص على تضمين أول وآخر سطر. أنشِئ ملفًا جديدًا في عميل MySQL بالاسم نفسه ضمن مجلد client-ssl: nano ~/client-ssl/client-key.pem ألصق محتويات الحافظة إلى الملف، ثم احفظه وأغلقه. يجب يكون في جهاز العميل جميع الشهادات اللازمة للوصول إلى خادوم MySQL؛ وعلينا الآن تعديل حساب المستخدم. تعديل حساب المستخدم البعيد لطلب وجود شهادة موثوقة أصبح لدى عميل MySQL الملفات اللازمة للتحقق من الشهادة عند الاتصال بخادوم MySQL، لكن الخادوم ليس مضبوطًا بعد لطلب وجود شهادة للعميل من سلطة شهادات موثوقة. لتعديل ذلك، علينا تسجيل الدخول عبر المستخدم root في خادوم MySQL: mysql -u root -p سنحتاج إلى تعديل المتطلبات الخاصة بالمستخدم البعيد، فبدلًا من استعمال عبارة REQUIRE SSL علينا استعمال عبارة REQUIRE X509 والتي ستُطبِّق الإجراءات الأمنية نفسها، لكنها ستتطلب أيضًا أن يملك العميل شهادةً موقعةً من سلطة شهادات يثق بها خادوم MySQL. علينا استعمال الأمر ALTER USER لتعديل المتطلبات الخاصة بالمستخدم: ALTER USER 'remote_user'@'mysql_client_IP' REQUIRE X509; ثم حدِّث جداول الصلاحيات لتأخذ التعديلات مجراها مباشرةً: FLUSH PRIVILEGES; اخرج من سطر أوامر MySQL عندما تنتهي بالأمر: exit سنتأكد في الخطوة التالية أننا ما زلنا نستطيع الاتصال. تجربة التحقق من الشهادة عند الاتصال من المناسب الآن معرفة أنَّ كلا الطرفين سيستطيع التحقق من شهادة الآخر عند الاتصال. لنحاول أولًا الاتصال من عميل MySQL دون توفير شهادات له: mysql -u remote_user -p -h mysql_server_IP الناتج: ERROR 1045 (28000): Access denied for user 'remote_user'@'mysql_client_IP' (using password: YES) سيرفض الخادوم الاتصال إذا لم نوفِّر شهادة للعميل. حاول الآن الاتصال مع توفير الخيارات ‎--ssl-ca و ‎--ssl-cert و ‎--ssl-key للإشارة إلى الملفات الموافقة لها والموجودة في مجلد ‎~/client-ssl: mysql -u remote_user -p -h mysql_server_IP --ssl-ca=~/client-ssl/ca.pem --ssl-cert=~/client-ssl/client-cert.pem –ssl-key=~/client-ssl/client-key.pem يجب أن تستطيع تسجيل الدخول إلى خادوم MySQL بنجاح. يمكنك العودة إلى سطر الأوامر بتنفيذ: exit لقد تأكدنا الآن من القدرة على الوصول إلى الخادوم، لكن يمكننا تحسين قابلية الوصول إليه بإنشاء ملف ضبط. إنشاء ملف ضبط لعميل MySQL لتجنب تحديد ملفات الشهادات في كل مرة نتصل فيها إلى الخادوم، فيمكننا إنشاء ملف ضبط بسيط لعميل MySQL. أنشِئ ملفا مخفيًا في مجلد المنزل في عميل MySQL باسم ‎~/.my.cnf: nano ~/.my.cnf أنشِئ في بداية ذاك الملف قسمًا باسم [client]، ويمكننا بعد ذلك السطر إضافة الخيارات ssl-ca و ssl-cert و ssl-key للإشارة إلى الملفات التي نسخناها من الخادوم؛ يجب أن يبدو شكل الملف كالآتي: [client] ssl-ca = ~/client-ssl/ca.pem ssl-cert = ~/client-ssl/client-cert.pem ssl-key = ~/client-ssl/client-key.pem يُخبِر الخيار ssl-ca العميل أن يتحقق أنَّ الشهادة التي وفرها خادوم MySQL موقعةٌ من سلطة الشهادات التي أشرنا إلى شهادتها. وهذا يجعل العميل متأكدًا أنه يتصل بخادوم MySQL موثوق. أما الخياران ssl-cert و ssl-key فيشيران إلى الملفات اللازمة لإثبات أنَّ العميل يملك شهادة موقعة من سلطة الشهادات نفسها. سنحتاج إلى ذلك إذا أردنا في خادوم MySQL التحقق أنَّ العميل موثوق من سلطة الشهادات أيضًا. احفظ الملف وأغلقه عندما تنتهي من التعديلات عليه. يمكنك الآن الاتصال إلى خادوم MySQL دون إضافة الخيارات ‎--ssl-ca و ‎--ssl-cert و ‎--ssl-key في سطر الأوامر: mysql -u remote_user -p -h mysql_server_ip يجب أن يملك الخادوم والعميل الآن الشهادات اللازمة للاتصال بأمان، وضبطنا كلا الطرفين للتحقق أنَّ الشهادة موقعة من سلطة الشهادات الموثوقة. الخلاصة يجب أن يكون خادومك مضبوطًا لقبول الاتصالات الآمنة من العملاء البعيدين. وإذا اتبعتَ الخطوات اللازمة للتحقق من الشهادات عبر سلطة الشهادات فسيتأكد كلا الطرفين من وثوقية الطرف الآخر. ترجمة –وبتصرّف– للمقال How To Configure SSL/TLS for MySQL on Ubuntu 16.04 لصاحبه Justin Ellingwood
  3. سهّل محرك قواعد بيانات MySQL من استخدام قواعد البيانات العلاقية Relational، وألغى – إلى حدٍ ما – الخطوط التي تفصل بين قواعد بيانات SQL و NoSQL بعد أن أضاف حقلًا لنوع البيانات JSON. في يومٍ من الأيام السالفة، كان هناك حاسوب وحيدٌ، ثم أتى أحدهم وبنى آخر، وأراد أن ينقل بعض الشفرات من الحاسوب الأول، ولهذا السبب احتجنا إلى طريقةٍ لنقل المعلومات دون اعتمادها على العتاد الذي يُشغِّلُها؛ ومن ذلك الوقت وإلى الآن انتشرت الكثير من ترميزات المحارف مثل ASCII و EBCIDC و SGML و XML …إلخ. وسطع نجمها لفترةٍ من الزمن. ولكن في السنوات الأخيرة اشتهرت صيغة JSON (أي JavaScript Object Notation) التي تستعمل لتبادل البيانات بصيغةٍ مهيكلة. كان نظام قواعد البيانات MySQL قبل الإصدار 5.7 يخزّن مستندًا مُصاغًا بصيغة JSON في حقلٍ نصي، لكن من الصعب البحث في السلاسل النصية الطويلة، وكتابة تعبير نمطي Regular expression للعثور على القيم ضمن تلك السلاسل سيكون أمرًا صعبًا، وإذا غيّرت قيمة من القيم المخزنة ضمن سلسلة JSON فعليك إعادة كتابة السلسلة كلها، وهذا لا يصب في صالح الأداء، لكنه ضروريٌ إذا كنت تعمل على إصدار MySQL 5.6 وما قبله. نوع البيانات JSON في قواعد MySQL أُضيفَ نوعٌ جديدٌ من أنواع البيانات في الإصدار MySQl 5.7 ألا وهو JSON، فقد أصبح مثل الأرقام الصحيحة، والسلاسل النصية …إلخ. وبهذا أصبحت هنالك طريقةٌ لتخزين مستند JSON كامل في عمودٍ من أعمدة جدول في قاعدة البيانات، وقد يكون حجم هذا المستند مقدرًا بالغيغابايت! يتحقق الخادوم أنَّ القيمة المُراد تخزينها في هذا الحقل هي مستند JSON صالح، ومن ثم يحفظه بصيغةٍ ثنائية Binary صُمِّمَت خصيصًا لتسهيل البحث فيها؛ يجدر بالذكر أنَّ نوع البيانات الجديد كان السبب في تحديث نسخة MySQL العاملة في مختلف الخواديم أكثر من أيّ ميزة أخرى. يأتي نوع البيانات الجديد مدعّمًا بأكثر من 20 دالة، وهذه الدوال تستطيع أن تستخلص أزواج «المفتاح-القيمة» Key-value من المستند، أو تحدِّث البيانات، أو توفِّر بيانات وصفية Metadata حول البيانات المخزنة، وتخرج الأعمدة التي ليست بصيغة JSON إلى صيغة JSON، والكثير… إصدار MySQL 8 أصبح في مرحلة Milestone release وسيضيف ميزات ودوال جديد. الدالة JSON_PRETTY_PRINT ستؤدي إلى إظهار المخرجات بتنسيق يُسهِّل فهمها؛ وستُضاف تحسينات أخرى مثل إمكانية تعديل البيانات دون إعادة كتابة المستند بأكمله. كان حفظ البيانات بصيغةٍ غير منسقة يعدّ من ميزات NoSQL، لكن الكثير من قواعد البيانات العلاقية مثل Oracle و SOL Server و PostgreSQL وغيرها قد أضافت نوع البيانات JSON، مما يُصعِّب تعريف الفرق بين قواعد بيانات NoSQL و SQL. لماذا علينا الاهتمام بميزة Document Store بعد أن ظهر نوع البيانات JSON بفترةٍ قصيرة، ظهرت ميزة MySQL Document Store، والتي صُمِّمَت للمبرمجين الذين لا يعرفون لغة الاستعلام البنيوية (SQL) لكنهم يريدون استخدام قواعد البيانات. يُنشِئ المُطوِّر بنى البيانات ويُطبِّق دوال إنشاء البيانات والحصول عليها وتحديثها وحذفها (تُختَصر هذه العمليات عادةً بالاختصار CRUD) على البيانات وفقًا لحاجته باستعمال لغة البرمجة التي يشاء (مثل Java و JavaScript و Node.js و Python و ‎C++‎ وستُتاح الميزة للغات الأخرى قريبًا). ليس من الضروري أن يعرف المُطوِّر لغةَ SQL، ولن يلقيَ بالًا إلى أنَّ لغة SQL ستُستعمَل وراء الكواليس؛ حيث ستُخزَّن البيانات في عمود JSON، وبهذه الطريقة يستطيع المطورون استخدام البيانات وحفظها دون الحاجة إلى إنشاء جداول في قاعدة البيانات، ولا إلى تضييع الوقت في التخطيط لقاعدة البيانات، ولا إلى انتظار مدير قواعد البيانات لكي يُنشِئها لهم. يجب ألّا ننسى أنَّ أغلبية المشاريع تبدأ دون معرفة كيف ستبدو البيانات بعد إطلاق المشروع، وستتطور بنية تلك البيانات مع مرور الزمن لملائمة مختلف الظروف والحاجات. إذا احتجنا إلى إنشاء قاعدة بيانات علاقية تقليدية من مستندات JSON، فمن السهل استخراج المفاتيح وتحويلها إلى أعمدة لكي تعمل عليها SQL القياسية، ويلي ذلك فهرسة الأعمدة الناتجة لتسريع عملية البحث فيها. الجوانب السلبية إنَّ صيغة JSON ممتازةٌ لتخزين البيانات بصيغةٍ «غير رسمية»، لكن قواعد البيانات العلاقية ملائمةٌ للبيانات المُقسَّمة إلى أقسام صغيرة يسهل الوصول إليها. أضف إلى ذلك عدم القدرة على فرض معايير دقيقة لتخزين البيانات، فيمكن أن يكون اسم المفتاح الذي يُخزِّن عنوان البريد الإلكتروني email أو eMail أو e-mail أو غير ذلك؛ لكن لا أظن أنَّ هذا الأمر سيُشكِّل عائقًا أمامك إذا كنتَ مدركًا له مسبقًا. إذا كنتَ جاهزًا لتجربة حقل JSON، فنزِّل MySQL، ونزِّل بطريقك برمجية MySQL Workbench حيث تدعم أعمدة JSON، وكلا البرمجيتين متاحتان مجانًا. ترجمة – بتصرّف – للمقال What you need to know about JSON in MySQL لصاحبه Dave Stokes.
  4. إذا كنتَ تحب الكتب، فيمكنك أن تحصل على كميةٍ كبيرةٍ منها بالاشتراك في النشرات الإخبارية، وهذا لأنَّ توفير تنزيل مجاني للكتب هو محفِّز للتسجيل في القوائم البريدية وهو استراتيجية ناجحة لزيادة معدّل الاشتراك في قائمتك البريدية. من المرجَّح أنَّك تستخدم هذه الاستراتيجية أو فكّرتَ باستخدامها وتريد معرفة كيفية فعل ذلك. وعلى أيّة حال، إذا لم تستعمل هذه التقنية من قبل ولا تعلم كيف توفِّر ذلك الكتاب للمشتركين الجدد وأردتَ استكشاف طرائق فعل ذلك، فهذه المقالة مناسبةٌ لك. الذي تحتاج له هو البحث عن طريقة بسيطة لإعطاء الكتاب الذي وعدتَ به دون إمكانية تنزيله من الزوار قبل أن يشتركوا، مع تفادي فهرسة صفحة تحميل الكتاب من محركات البحث. دعني أبشِّرَك أننا نملك بعض الخيارات بهذا الصدد. سننظر في هذا المقال إلى خمس طرائق التي يمكنك فيها إتاحة تنزيل ملف مجانًا. وهذه الطرائق سهلة التطبيق ومناسبة لزوار موقعك وستخفي نيتك ببناء قائمة بريدية من زوار الموقع ومن محركات البحث. الخيار الأول: استخدام التخزين السحابي هذه هي أبسط طريقة وهي الخيار الصائب لأغلبية أنواع مواقع الويب. عليك أن ترفع ملفك إلى خدمة التخزين السحابي المفضَّلة عندك مثل Google Drive أو Dropbox أو Box أو غيرها، ثم ولِّد رابط URL لمشاركة الملف مع الآخرين وضمِّن رابط التنزيل السابق في رسالة البريد الإلكتروني الترحيبية بمشتركي القائمة البريدية الجدد. هذا الحل مناسب للكثيرين، لكن ربما تكون هنالك أسباب تجعلك تنفر من التخزين السحابي، فمثلًا: ربما لا تحب فكرة استخدام التخزين السحابي، وقد يكون من الصعب إنشاء أذونات صحيحة للملفات وقد تُشكِّل عائقًا أمام مستخدميك إذا لم تفعلها بطريقة بصحيحة. أضف إلى ذلك أنَّ تخزين الملف على خدمة سحابية يعني أنَّك تضيّع على نفسك فرصة ثمينة لإبقاء المستخدمين في موقعك. وبغض النظر عن الأسباب، إذا ارتأيتَ أنَّ التخزين السحابي ليس مناسبًا لك، فما تزال لدينا أربعة خيارات أخرى يمكنك التفكير بها. الخيار الثاني: استخدام مزود خدمة القائمة البريدية خيار آخر بسيط هو توفير الملف لتنزيله عبر استخدام مزود خدمة القائمة البريدية، لكن لا تسمح جميع مزودات خدمة القوائم البريدية لك بفعل ذلك، لكن إن كان مزود الخدمة الخاص بك يسمح بذلك، مثل MailChimp أو ConvertKit، فارفع ملفك (احرص ألّا يتجاوز الحد الأقصى لحجم الملف المرفوع) ثم ضمِّنه كرابط في البريد المُرسَل لتأكيد التسجيل في موقعك. إذا لم يكن يوفِّر مزود خدمة القائمة البريدية الخاص بك هذه الميزة، فمن المرجح أنَّهم يوفرون قدرةً على الاندماج مع مزود خدمة من طرف ثالث الذي يسمح لك باستضافة وتضمين الملفات بسهولة، لكن قد تكون هنالك بعض التكاليف الإضافية إذا أردتَ استخدام خدمة من طرف ثالث. وصحيحٌ أنَّ هذا الخيار سهل التطبيق ومناسب للمشتركين، لكنه لن يدفع المشتركين إلى العودة إلى موقعك؛ وإذا كان هذا مهمًا بالنسبة إليك –وأتوقع أنَّه كذلك– فاستمر بالقراءة، فبقية الخيارات ستضع الفكرة السابقة بالحسبان. الخيار الثالث: استخدام صفحة غير قابلة للفهرسة أحد الأسباب الرئيسية لاستخدام فريق التسويق للقوائم البريدية هو محاولة حثّ المستخدمين على العودة إلى الموقع الإلكتروني. لذا لِمَ لا تفعل ذلك من الكتاب المجاني الذي توفِّره؟ - أنشِئ صفحةً لعرض معلومات عن الملف ورابط تنزيله. - ارفع الملف إلى موقعك وضع رابط التنزيل في الصفحة التي أنشأتها سابقًا. - شارك رابط الصفحة في رسالة الترحيب بعد الاشتراك بالقائمة البريدية. هذا يسمح للمشتركين الجدد بتنزيل الملف بسهولة مع تحقيق هدفك بإعادتهم إلى الموقع، وبالتالي ستستفيد من رؤيتهم للإعلانات، ولتوفِّر لهم أفضل ما في موقعك لإبقائهم لأطول فترة ممكنة. هنالك بعض الأمور العملية التي يجب أن تأخذها بالحسبان إذا شئت استخدام هذه الطريقة. يجب أن تحرص على تصعيب العثور على الصفحة التي تستضيف رابط الكتاب أو سيتمكن زوار موقعك من تحميل الكتاب دون التسجيل في قائمتك البريدية. ولفعل ذلك أنصحك باتباع الخطوات الآتية: - لا تضع رابطًا لهذه الصفحة من أي مكان في موقعك عدا رسالة الترحيب بمشتركي القائمة البريدية. - استخدم إضافة مثل Smartcrawl لإضافة خاصية noindex إلى الصفحة ولحذفها من خريطة الموقع لكي تتجاهلها محركات البحث إن وجدَتها. - راقب إحصائيات زيارة الصفحة، فلو كانت الصفحة تحصل على زوار أكثر من عدد المشتركين الجدد بالقائمة البريدية، فمن المحتمل أنَّ رابط الصفحة قد نُشِرَ في مكانٍ آخر. لذا غيّر رابط URL للصفحة واستخدم إضافة لإعادة التوجيه لإعادة توجيه الرابط القديم إلى صفحتك الرئيسية أو إلى صفحة الهبوط المناسبة. من سلبيات هذه الطريقة هي أنَّ رابط الصفحة يمكن أن يُشارك على مواقع التواصل الاجتماعي، فلو كنتَ تراقب تحليل بيانات زيارات موقعك، فستَحُسّ على ذلك بسرعة وستقدر على تغيير رابط URL، لكنني أرى أنَّ الأمر مربك في بعض الأحيان. هذا الحل مناسب لأغلبيتنا، لكن ماذا لو كان الملف الذي تريد تنزيله قيمًا ومهم جدًا ولا تريد تركه دون حماية؟ ربما تريد شيئًا أكثر حمايةً في هذه الحالة. الخيار الرابع: استخدام صفحة محمية بكلمة مرور إذا أردتَ جعل الملف الذي تريد من مشتركي موقعك تحميله أكثر أمانًا. فأحد الحلول السهولة هو حماية الصفحة بكلمة مرور وتوفير رابط الصفحة وكلمة المرور في رسالة الترحيب بالقائمة البريدية. وبهذه الطريقة سيكون الملف أكثر أمانًا، وستستفيد من عودة المشتركين إلى موقعك لتحميل الملف. ولكي يستطيع الزائر مشاركة هذه الصفحة مع البقية، فسيحتاج إلى وضع رابط الصفحة وكلمة مرور، وهذا سيجعل عملية التنزيل أكثر أمانًا، لكن في المقابل ستصبح العملية أصعب على المشتركين الجدد للوصول إلى الملف الموعود (إذ أضفتَ خطوةً جديدةً). يمكن إعادة تأمين الملف بسهولة بعد نشر أحد الزوار للرابط وكلمة المرور: حيث عليك تغيير كلمة المرور فقط، ولا حاجة إلى تغيير رابط URL. الخيار الخامس: استخدام كود التفعيل في MarketPress إذا كنتَ تستخدم MarketPress فيمكنك استخدام الملف الذي تريد من مستخدميك تنزيله للترويج لقائمتك البريدية، إذ عليك توفير كود تخفيض (أو كوبون) كمُحفِّز للاشتراك في قائمتك البريدية. ثم أعطهم كود التخفيض برسالة الترحيب بمشتركي القائمة البريدية وضع رابطًا إلى الملف على موقعك. هذا الخيار مرنٌ ومناسب لأنه يسمح لك بتوفير كود يؤدي إلى تخفيض بنسبة مئوية ثابتة لأي منتج من منتجاتك. وبفعلك لذلك، فأنت تزيد من مشتركي القائمة البريدية وفي نفس الوقت تزيد من مبيعاتك. إذا لم تستخدم أكواد التخفيض من قبل مع MarketPress، فاعلم أنَّ إعدادها سهل، ويمكنك فعل ذلك بثلاثة خطوات: سجل دخولك إلى لوحة تحكم ووردبريس. اذهب إلى صفحة Store Settings > Add Ons واحرص على أنَّ إضافة أكواد التخفيض (coupons) مفعّلة. اذهب إلى Store > Coupons وأنشِئ كود تخفيض جديد. هذا كل ما في الأمر! عليك الآن توفير كود التخفيض الذي أنشأتَه للمشتركين الجدد بالقائمة البريدية، ووضع رابط إلى متجرك. الخلاصة تحفيز زوار موقعك على الاشتراك بالقائمة البريدية عبر توفير ملف للتحميل مجانًا هي استراتيجية مجرِّبة وفعّالة، وكما رأيت، ليس من الصعب تصميم طريقة لتوصيل المحتوى مع الحفاظ على حماية الملف المعني دون تعقيد العملية على زوار الموقع. والمفتاح الرئيسي لنجاح هذه العملية هو تحديد أهدافك منها قبل اعتماد إحدى طرائق توفير الملف. إذا كان هدفك هو تبسيط الأمر ما استطعت، فأنصحك باستعمال التخزين السحابي أو خدمات مزود القائم البريدية لتوفير الملف. أما إذا رَغِبتَ بعودة المشتركين إلى موقعك، فاستخدم صفحة غير مفهرسة من محركات البحث أو صفحة محمية بكلمة مرور لتوفير رابط لتنزيل الملف. في النهاية، إذا كنتَ تستعمل MarketPress، فإنشاء أكواد تخفيض سيسمح لك باستخدام الدعم المُضمَّن في MarketPress لإنشاء «منتجات» قابلة للتحميل، مما يزيد من احتمال عودة المشتركين إلى موقعك. ترجمة –وبتصرّف– للمقال How to Give Away a Free Ebook (and Get More Subscribers) with WordPress لصاحبه Jon Penland حقوق الصورة البارزة محفوظة لـ Freepik
  5. نحن نرسل معلوماتنا الشخصية في كل يوم عبر الإنترنت، ففي الساعة الماضية دفعتُ المستحقات على بطاقتي الائتمانية، واشتريتُ كتابًا، وحفظتُ نسخةً من عناوين منازل أصدقائي، وأرسلتُ بعض رسائل البريد الإلكتروني، واشتريتُ بعض الحاجيات. أصبحتْ مشاركة معلوماتنا شائعة جدًا في هذه الفترة، حتى أننا لم نعد نفكر قبل مشاركتها. هذا هو الغرض من SSL، فشهادات SSL تحمي التفاصيل التي نشاركها على الإنترنت، مانعةً وقوعها في الأيدي الخاطئة. استخدام SSL –وبالتالي HTTPS– لحماية موقع ووردبريس الخاص بك وزواره لن يكون أمرًا صعبًا ومعقدًا، وسنناقش في هذا الدرس ما هو SSL وكيف نستخدمه. ما هو SSL؟ «طبقة المقابس الآمنة» (Secure Socket Layer اختصارًا SSL) بدأت كطريقة لزيادة الحماية بين المواقع الإلكترونية وزوارها في 1994 من شركة Netscape Communications حيث وجدوا أنَّ الحاجة لمثل هذه التقنية في تزايد. تم تحسين SSL لاحقًا وأُصدِرَ لأول مرة بنسخة 3.0 في عام 1996، لكنه كان يحتوي على ثغرات. ومن ثم استحوذت «Internet Engineering Task Force» (اختصارًا IETF) عليه رسميًا في 1999 وطوِّرَ كثيرًا منذ ذاك الحين. وفي الوقت الراهن، أعيدت تسمية SSL إلى TLS ‏(Transport Layer Security أي حماية طبقة النقل)، لكن ما يزال يشار إليه باسم SSL أو TLS/SSL. وما يزال الغرض منه هو ذاته إلى يومنا هذا، وأصبحت هذه التقنية معياريةً لحماية مواقع الويب. متى يجب أن تستعمل SSL؟ أعلنت شركة Google منذ مدة أنَّها ستزيد من تقييم المواقع التي تستعمل SSL في نتائج بحثها، لكن ربما تجد زيادةً مقدارها 1% في هذه الفترة، لإعطاء الجميع فرصةً للانتقال إلى استخدام SSL. وبغض النظر عمّا سبق: إذا كان يطلب موقعك من المستخدمين تسجيل دخولهم أو إعطاء معلوماتهم الشخصية مثل أسمائهم وعناوين بريدهم الإلكتروني وتفاصيل بطاقتهم الائتمانية وهلم جرًا… فيجب أن توفِّر حمايةً عبر SSL. فبدونها قد تُعرِّض معلومات المستخدمين للخطر. كيف يعمل SSL؟ يعمل SSL عبر تشفير المعلومات المُمرَّرة بين الخادوم الذي يُستضاف عليه الموقع إلى المتصفح بدلًا من ترك المعلومات بصيغتها النصية، مما يعني أنَّ النص سيُعاد صياغته بطريقةٍ تجعله يبدو وكأنه سلسلة من الحروف غير المفهومة والأرقام، بدلًا من بقائه بصيغته المفهومة. لإنشاء اتصال SSL آمن على موقع ويب، فيجب أن يحصل صاحب الموقع على شهادة SSL (أي SSL certificate) من إحدى الشركات المتخصصة والتي تُسمى «سلطة الشهادات» (Certificate Authority). وبعد دفع المبلغ، فيجب إرسال معلومات الموقع الإلكتروني إلى سلطة الشهادات مثل الاسم والعنوان ورقم الهاتف. توضح هذه الصورة بعض المعلومات اللازمة لتسجيل شهادة SSL أساسية، هنالك معلوماتٌ أخرى تتطلبها الشهادات الأكثر أمانًا: وبالمقابل، سيحصل مالك الموقع على مفتاح عمومي (public key) ومفتاح خاص (private key). لا يجب مشاركة المفتاح الخاص مع أي شخص (مَثَلُهُ كمَثل كلمة المرور) لكن المفتاح العمومي ليس مهمًا أن يبقى مخفيًا. تلك المفاتيح هي أحرف وأرقام متناسقة مع بعضها رياضيًا، وهي تشبه المفتاح والقفل. وتُنشَأ عبر خوارزمية باسم Secure Hash Algorithm. يُرسَل المفتاح العمومي مع البيانات التي أدخلتها مسبقًا في ملفٍ باسم «Certificate Signing Request» (أي طلب توقيع الشهادة). ستتحقق سلطة الشهادات من المعلومات التي أدخلتَها وتتأكد أنها دقيقة (وأنَّك لستَ مُخترِقًا) وإذا جرى كل شيءٍ على ما يرام، فستوقَّع الشهادة عبر خوارزمية SHA. بعد ذلك ستصدر شهادة SSL، وهذه هي المرحلة التي يمكن للموقع فيها استخدام اتصالات مشفرة عبر SSL. وعندما يزور المستخدم موقعًا محميًا، فسيتأكد الخادوم أنَّ شهادة SSL تتطابق مع المفتاح الخاص، ومن ثم سينُشَأ «نفق مشفَّر» بين الخادوم (أو الموقع) والمتصفح (أو المستخدم). كيف تبدو المواقع المحمية بشهادات SSL؟ السابقة https ستظهر قبل رابط URL بدلًا من البروتوكول الافتراضي http. يمكنك أن تلاحظ قفلًا لونه أخضر معروضٌ في حقل العنوان في متصفحك. إذا اختار القائمون على الموقع شراء شهادة موسعة (تسمى «Extended Validation Certificate») فسيصبح شريط العنوان أخضر اللون بأكمله، أو سيحتوي على اسم الشركة باللون الأخضر قبل عنوان URL. أما في متصفح Safari فلن يصبح شريط العنوان أخضر اللون، ولن يكون لاسم الشركة خلفيةٌ خضراء، وإنما سيُستعمَل اللون الأخضر لكتابة اسم الشركة: عادةً، توفِّر الشهادات الموسعة حمايةً أكثر، وتُصدَر بعد أن تجتاز الشركة تحققات أكثر من سلطة الشهادات. وسيُطلَب من القائمين عليها توفير دليل على العنوان الفيزيائي وغير ذلك من الأمور القانونية، إضافةً إلى المعلومات الأساسية التي ذكرناها آنفًا. متى تتوقف شهادة SSL عن العمل إذا انتهت صلاحية شهادة SSL أو كانت موقعةً ذاتيًا (self-signed) أو كانت غير صالحة، فسيتحول لون كلمة https إلى الأحمر وأحيانًا يوضع خط فوقها، وأغلبية المتصفحات تحذِّرك إذا كان الموقع يستخدم شهادة SSL متوقفة، وتطلب تأكيدك قبل الدخول إلى الموقع غير الموثوق: عندما تنتهي صلاحية الشهادة، فيجب على مالك الموقع أن يُجدِّد شهادة SSL ببساطة، وذلك عبر سلطة الشهادات؛ لكن من الأفضل عدم الانتظار حتى انتهاء الشهادة لتجديدها، لكي يكون موقعك محميًا دومًا. أو سينبِّهك المتصفح إذا كنتَ تستخدم شهادة موقعة ذاتيًا وذلك إذا أصدرتَ شهادة SSL خاصة بك ولم تذهب إلى سلطة شهادات لتحقق من صحة المعلومات التي أدخلتها. أغلبية المتصفحات تثق بشهادات SSL المُصدَرَة من سلطة شهادات موثوقة، وستعرض تحذيرًا لجميع المواقع التي تستخدم شهادات موقعة ذاتيًا. إذا اشتريتَ شهادة SSL من شركة ليس ذات مستوى مرموق، فقد يُعتَبَر أنَّ موقعك يستخدم شهادة موقعة ذاتيًا. قد تصبح شهادة SSL غير صالحة لعدد من الأسباب، أحدها هو استخدام خوارزمية SHA قديمة. عملية التجزئة (hashing) هي عملية تحويل كمية كبيرة من المعلومات النصية إلى كمية أصغر يُشار إليها بالمفتاح (key) ويتم تحويلها عبر تطبيق مجموعة من القواعد الرياضية. وهنالك حاجةٌ إلى عملية تجزئة أقوى مع تقدم التقنية. لم تعد خوارزمية SHA0 مستخدمةً، وخفَّ استخدام خوارزمية SHA1 كثيرًا في الآونة الأخيرة، وأصبح متصفح Chrome يُظهِر تحذيرات بدءًا من عام 2016 للمواقع التي تستخدم خوارزمية SHA1. المعيار الحالي للتشفير هو خوارزمية SHA2 وأتوقع أن تحل محلها خوارزمية SHA3. قد يبدو لك أنَّ شهادة SSL غير صالحة إذا لم يتمكن المتصفح من التحقق منها من سلطة الشهادات. وهذا يحدث إذا كان اسم النطاق المستعمل في الشهادة يختلف عن اسم نطاق الموقع الذي يستخدمها. أفضل طريقة لحل هذه المشاكل هي تحديث شهادة SSL بالتنسيق مع سلطة الشهادات وباتباع تعليماتهم. إذا ظهر قفلٌ أمامه مثلثٌ أصفرٌ صغير، فمن المرجَّح أنَّ السبب هو أنَّ موقعك يحتوي على روابط لصفحات غير آمنة، تأكَّد أنَّ جميع صورك وعناصر القائمة وجميع الروابط في موقعك تستعمل بروتوكول https. من السهل معرفة السبب وراء عدم صلاحية الشهادة عبر استخدام أداة مجانية باسم Why No Padlock حيث تُعلِمُك مباشرةً بالمشكلة بما في ذلك استعمال الصور والسكربتات لبروتوكول http بدلًا من https. استخدام شهادة SSL مع ووردبريس بعد أن تملك شهادة SSL فيمكنك الآن استخدامها مع موقعك الذي يعتمد على ووردبريس. لا تنسَ أن تأخذ نسخةً احتياطيةً من كامل الموقع قبل إجراء أيّة تغييرات لمنع فقدان بياناتك في حال حدث شيءٌ غيرُ متوقعٍ. يمكنك الإكمال بعد أخذ النسخة الاحتياطية. لضبط شهادة SSL على موقع ووردبريس مستقل أو على شبكة متعددة المواقع، فافتح ملف wp-config.php وأضف السطر الآتي من الشيفرة. حيث سيُجبِر صفحات تسجيل الدخول ولوحة التحكم على استعمال تشفير SSL: define('FORCE_SSL_ADMIN', true); احرص على أن تضع السطر السابق قبل التعليق الآتي: /* That's all, stop editing! Happy blogging. */ سنضبط الآن إعادة توجيه (301) لكي يعاد توجيه كل زوار موقعك إلى النسخة الآمنة من الموقع التي تستعمل https بدلًا من http. عدِّل ملف ‎.htaccess أو أنشِئ واحدًا إن لم يكن موجودًا من قبل. إذا كان موجودًا فعليك وضع الشيفرة الآتية في أول الملف قبل أي شيءٍ آخر. لا تنسَ وضع اسم نطاقك بدلًا من mysite.com واحرص على إدخال المنفذ الصحيح إذا لم يستعمل موقعك المنفذ 80. <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{SERVER_PORT} 80 RewriteRule ^(.*)$ https://www.mysite.com/$1 [R,L] </IfModule> افتح موقعك الآن لتجرِّبه. فإذا ظهرت الكلمة https في عنوان URL مع القفل الأخضر فهذا يعني أنَّ موقعك أصبح يستعمل شهادة SSL. الخلاصة الحصول على شهادة SSL لموقعك هي خطوةٌ مهمةٌ في طريق حمايته وحماية زواره، لكنها ليست الخطوة الوحيدة التي عليها فعلها، يمكنك الاطلاع على هذا الدرس. إذا أردتَ معلوماتٍ أكثر عن استخدام SSL مع ووردبريس لاحتياجاتك الخاصة، فانظر إلى صفحة Administration Over SSL في توثيق ووردبريس. هل أنت مهتم بالحصول على شهادة SSL مجانية؟ أعلنت EFF ‏(Electronic Frontier Foundation) في عام 2014 أنها تعمل على مشروع مفتوح المصدر لإنشاء شهادات SSL مجانًا، وأُطلِق هذا المشروع لاحقًا باسم Let’s Encrypt. هنالك إضافاتُ تساعدك في ضبط شهادة SSL في موقعك، لكن انتبه فقد لا تكون تلك الإضافات مُحدَّثةً وقد لا تتوافق مع آخر نسخة من ووردبريس. قد تحسب أنَّه لا بأس باستعمال إضافات قديمة إذا كان يستعملها الكثيرون وقد اختبرتَها في موقعٍ تجريبي دون مشاكل، لكن حماية موقعك ليست شيئًا يُستهان به. ترجمة –وبتصرّف– للمقال How to Use SSL and HTTPS with WordPressلصاحبته Jenni McKinnon
  6. تعرّض موقعك للاختراق هو أحد أكثر التجارب إحباطًا والتي ستواجهك أثناء عملك كمدير أحد المواقع؛ ولسوء الحظ، لن تغني عنك تعزيزاتك الأمنية شيئًا، إذ يُختَرَق يوميًا حوالي 30000 موقع إلكتروني، ومن المرجح أن يقع موقعك فرسيةً عاجلًا أم آجلًا، لذا من المهم أن تعلم ما الذي يتوجب عليك فعله عند حدوث ذلك. لكن لحسن الحظ، من الممكن أن تتعرَّف على الآلية التي أُختِرَق موقعك بواسطتها وذلك عبر القيام ببعض أعمال «التنقيب» في سجلات الموقع؛ وبعد أن تُحدِّد كيف وصل المخترق إلى موقعك تمامًا، فيمكنك ترقيع الثغرة الأمنية لتنجب استغلالها مجددًا مستقبلًا. يتوافر في WPMU DEV فريق دعم فني خبير يستطيع مساعدتك في تشخيص مشاكل موقعك أو يمكنك استخدام إضافة مثل Defender لترقيع الثغرات الأمنية قبل وقوع الهجوم (سنستفيض بالشرح عن هذا الموضوع لاحقًا). لكن إن كنتَ عازمًا على حلِّ المشكلة بنفسك، فسأذكر في هذا الدرس أكثر الطرائق التي يمكن اختراق موقعك عبرها شيوعًا وكيف تتحقق من السجلات بحثًا عن أدلة. كيف تُختَرَق المواقع «الجيدة» يتسلل المخترقون إلى موقعك بإحدى الطريقتين الآتيتين: إما يدويًا وإما عبر استعمال برنامج يُنشئونه لهذا الغرض يسمى «bot» أو «hackbot»؛ ولمّا كانت أغلبية الهجمات تستهلك وقت المخترقين فلذا تستعمل برمجيات hackbot لمهاجمة مئات أو حتى آلاف المواقع ساعيًّا بشكلٍ ممنهج. هذه هي الطرائق الرئيسية التي يستطيع المخترقون الوصول عبرها إلى موقعك: هجمات تخمين اسم المستخدم وكلمة المرور: وهذا النمط من الهجمات يُطلَق عليه اسم «Brute force attacks». فلو كنتَ تستخدم اسم المستخدم الافتراضي (مثل «admin» أو «administrator») مع كلمة مرور ضعيفة، فأنت «تهدي» المخترقين نصف المعلومات التي يحتاجون لها للدخول إلى موقعك. ثغرات في البرمجيات: عندما يجد المخترقون ثغرةً أمنيةً في برمجية ووردبريس أو في إحدى إضافاتها أو قوالبها أو السكربتات المستعملة فيها، فسيحاولون استغلالها للوصول إلى موقعك، بمحاولة «حقن» السكربتات الخاصة بهم على سبيل المثال. الأبواب الخلفية: يوضع ملفٌ خبيثٌ بين ملفات موقعك، والذي يحتوي على سكربت الذي يسمح للمخترق بالوصول المتكرر إلى موقعك دون أن يخلِّف وراءه أثرًا. البرمجيات الخبيثة والفيروسات: إذا كان حاسوبك يعاني من الفيروسات والبرمجيات الخبيثة، فيمكن أن يستعملها المخترقون للوصول إلى موقعك. خادوم غير مؤمن: يجب أن يكون الخادوم الذي تستضيف عليه موقعك آمنًا؛ فلو لم يكن كذلك، فيستطيع المخترقون استغلال الثغرات الأمنية فيه لاختراق موقعك. أذونات غير صحيحة للملفات: عليك ضبط الأذونات (permissions) على جميع ملفاتك وهذا يعني أنَّك تستطيع أن تُحدِّد مَن يستطيع قراءة السكربتات والكتابة إليها وتنفيذها. فإذا سمحتَ بكل ذلك فيمكن للمخترق أن يُعدِّل ملفاتك بسهولة، ويضيف سكربت خبيث مثل الأبواب الخلفية، وبالتالي سيُختَرَق موقعك. استخدام موقع احتيالي: يمكن للمخترقين إنشاء مواقع تتنكر بصفة الشركة الأصلية أو المدونة المعنية، ويُنشِئون نماذج تسجيل مصممة خصيصًا لجمع المعلومات لاستخدامها لاحقًا، وفي حال كان الموقع مبينًا على ووردبريس فيُحتَمل أن يسرقوا بيانات الدخول. ثغرات XML-RPC: يُستخدَم بروتوكول XML-RPC لميزة pingbacks و trackbacks في ووردبريس، حيث يسمح لووردبريس بإنشاء عدد من الاستدعاءات عبر طلبية HTTP والذي يعني أنَّ موقعك يمكن أن يُرسِل pingback لموقعٍ آخر بالكامل ويستلم الرد. والمشكلة هي أنَّ المخترقين يستخدمون هذه الميزة لتنفيذ هجمات brute force عن بعد. هجمات XSS‏ (Cross-site Scripting): ثغرة XSS هي أنَّ الشيفرة مكتوبة بطريقة تجعل من الممكن للمخترق أن يكتب وينفذ شيفرة JavaScript خبيثة والتي تحاول الوصول إلى بيانات المتصفح الخاص بالمستخدم؛ ويتم ذك عادةً عبر وضع رابط لإرسال المستخدمين إلى أحد المواقع لسرقة أيّة بيانات يدخلونها أثناء تصفحهم للموقع الهدف. حيث يستطيعون مثلًا سرقة بيانات دخول مدير الموقع للوصول إلى لوحة التحكم. تزوير الطلب عبر المواقع (Cross-site request forgery اختصارًا CSRF): يحدث هذا النوع من الهجمات عندما يزوِّر المخترق طلبيةً من المستخدم عبر استخدام الشيفرات. وهذا يعني أنَّ المخترق قد يُعدِّل على الطلبية العادية لأغراضه الخبيثة. ولعدم امتلاك المخترقين على وصولٍ كمسؤول (admin) فإنهم يخدعون المستخدم لجعله ينفذ شيئًا لإعطاء ترخيص بتنفيذ الطلبية الخبيثة. وهذا النوع من الهجوم يمكن أن يستخدم لخداع المستخدم لفعل العديد من الأشياء مثل إرسال تفاصيل الدخول للمخترق مما يعطيه وصولًا إلى الموقع. المشكلة أنَّ القائمة السابقة ليست شاملةً وهنالك الكثير من الطرائق المتاحة أمام المخترقين، ومن المستحيل التخمين ما هي الآلية التي ستُتبَع لاختراق موقعك، وماذا عليك أن تفعل لحل المشكلة. إذا بدأت بالتخمين، فمن المرجح أنَّك لن تكتشف الخلل، وستضيع وقتك في حلقة مفرغة؛ فما يزال المخترق متمكنًا من الوصول إلى موقعك، في حين أنَّك حَلَلتَ إحدى المشكلات ظانًا أنَّك أغلقتَ الثغرة التي دخل منها؛ إلى أن يتعرض موقعك للاختراق مجددًا، وهكذا… الطريقة الوحيدة التي يمكنك أن تتأكد فيها من أمان موقعك بعد اختراقه هي أن تَعلَم تمام العلم ما هو الجزء الذي تعرض إلى الهجوم من موقعك، وبعد معرفتك لذلك فيمكنك أن تصلح المشكلة وتنهي الحلقة المفرغة. التعامل مع السجلات يمكن أن توفِّر لك سجلات موقعك دلائل مهمة تبيّن كيف اُخترِقَ موقعك؛ ولا ضير من تفقد سجل أخطاء موقعك. مكان هذه السجلات يختلف من موقعٍ لآخر وذلك اعتمادًا على الاستضافة، لذا أنصحك بالتواصل مع مزود الاستضافة إذا لم تعرف أين تجد السجلات. في لوحة cPanel، يمكنك أن تجد هذه السجلات في قسم Metrics بعد تسجيل الدخول. ويكون سجل الأخطاء قصيرًا عادةً، لذا يمكنك الضغط على زر Errors لتنظر إلى سجل الأخطاء أو يمكنك الضغط على زر Raw Access للوصول إلى سجل الوصول (access log) أولًا. يجب أن يعطيك سجل الوصول فكرةً عن كيفية اُخترِق موقعك. يمكن أن يعطيك سجل الأخطاء فكرةً سريعةً عمّا حدث، لكن لعدم عرض محاولات الوصول الناجحة إلى موقعك، فلن تستفيد منه كثيرًا. لكن مع ذلك أرى أنَّ من المناسب البدء به فلربما يعطيك فكرة عمّا عليك البحث في سجلات الوصول. للحصول على نسخة من سجل الوصول، فاضغط على رز Raw Access، ثم اختر أحد المواقع الموجودة في القائمة لتنزيل نسخة من السجلات. بعد التنزيل، يمكنك استخراج المحتويات وعرضها في برنامجك المفضَّل مثل المحرر Brackets المفتوح المصدر. أبقِ في بالك أنَّ cPanel تحتفظ بسجلات آخر 24 ساعة فقط، لكن يمكنك تشغيل ميزة الأرشفة لحفظ بيانات الوصول لشهرٍ أو أكثر. أما لسجلات الأخطاء، فيُسجَّل عادةً آخر 300 خطأ فيها. لتفعيل الأرشفة، عليك تفعيل الحقل الموجود في القسم العلوي من الصفحة المُعَنوَن «Archive logs in your home directory at the end of each stats run every 24 hours»، يمكنك أيضًا تفعيل الحقل الذي يدنوه لحذف السجلات القديمة من الأشهر الماضية، وهذا يساعد في تقليل المساحة التخزينية والموارد التي يحتاج لها موقعك للاستمرار في أرشفة السجلات. لا تنسَ الضغط على زر Save لحفظ تغييراتك؛ يجدر بالذكر أنَّ هذين الخيارين مفعلان افتراضيًا، لكن قد يعطلها مزود الاستضافة لبعض خطط الاستضافة. من المهم ملاحظة أنَّك لا تملك وقتًا طويلًا لتفحص السجلات القديمة، فعند اختراق موقعك، فيجب عليك فورًا الحصول على السجلات وتحليلها لأن لديك فرصة زمنية قصيرة قبل حذف سجلاتك حذفًا دائمًا. لذا احفظ منها ما استطعت لمنع فقدان البيانات المهمة. فهم بنية السجلات ستبدو سجلات الأخطاء والوصول متشابهة وكأنها كتل كبيرة من النص غير المفهوم، لكن بعد أن تتعلم طريقة عرض المعلومات في السجلات، فلن يصعب عليك فهمها كما قد تتوقع. عندما ننظر إلى سجل الأخطاء، فمن المهم أن نعلم بنيته: الوقت والتاريخ نوع الخطأ عنوان IP للمستخدم وصف الخطأ مسار الصفحة التي تمت محاولة عرضها رابط URL للموقع الذي أتى المستخدم منه إلى الصفحة المعنية تنطبق المعلومات السابقة على عدد كبير من أنواع السجلات، لكن لاحظ أنَّ سجلك قد يكون مختلفًا لذا أنصحك بالرجوع إلى التوثيق الذي تقدمه لك شركة الاستضافة (أو الذي يوفره خادومك). والمِثل ينطبق على سجلات الوصول. هذه هي بنية أغلبية سجلات الوصول: عنوان IP للمستخدم الوقت والتاريخ طريقة الوصول إلى الصفحة عبر برتوكول HTTP (مثلًا: GET أو POST …إلخ.) والإصدار رمز حالة HTTP (مثلًا: 200 أو 404). عدد البايتات المُستقبَلة البرمجيات المستخدمة (المتصفح أو نظام تشغيل الجهاز) من الذي وصل إلى موقعك (ستُعرَض عبارة User Agent للمتصفح عادةً) البيانات التي يجدر بنا البحث عنها عندما تحدِّق بالبيانات الموجودة في السجل فستبدو وكأنها كلام ليس ذا معنى ما لم تكن تعلم ما الذي عليك التركيز عليه. أنصحك بالبدء بإلقاء نظرة سريعة على سجل الأخطاء فقد يعطيك تلميحةً عمّا عليك البحث عنه في سجل الوصول. إذا لاحظتَ أنَّ أحدهم حاول الوصول إلى ملفاتٍ لن يحاول المستخدم العادي زيارتها –لكن المخترقين سيفعلون ذلك– فلاحظ عنوان IP. إذ إنَّ المخترقين يحاولون الوصول إلى ملفات وصفحات مثل ملف ‎.htaccess و install.php و wp-config.php والأجزاء الأخرى المشابهة من موقعك. هذا مثال عن سطرٍ موجودٍ في سجل الأخطاء الذي ولَّدتهُ برمجية cPanel: [Fri May 06 23:14:24.856758 2016] [core:error] [pid 27290] (13)Permission denied: [client 12.345.68.90:33106] AH00132: file permissions deny server access: /path/to/your-site/.htaccess في رسالة الخطأ السابقة: منع الخادوم وصول المستخدم إلى أحد الملفات لعدم امتلاكه صلاحيات لعرض الملف (الذي هو ‎.htaccess)، إذا لم تتعرَّف على عنوان IP ولم يكن تابعًا لك، فيجب أن تأخذ حذرك وتسجِّل ملاحظة عن هذا السطر. إذا لم تكن تعرف عنوان IP الخاص بك، فسيخبرك Google ما هو بالبحث باستخدام العبارة «what is my IP address». عندما ترى أخطاءً مشابهةً، فيمكنك الانتقال الآن إلى سجل الوصول، وحاول أن تبحث عن أسطر مشابهة، لكن عند تمكُّن المستخدم من الوصول إلى الصفحة بنجاح، ويكون ذلك عندما تُعاد القيمة 200 كرمز لحالة HTTP في سجل الوصول. وهذا يشير إلى نجاح تنفيذ طلب المستخدم. ابحث أيضًا عن مستخدمين يحاولون الوصول إلى صفحات يفترض أنَّ الزائر سيزورها مرةً أو مرتين فقط وخصوصًا أنَّ الفاصل بين الزيارات معدود بالثواني. لكن أبقِ في ذهنك أنَّ هنالك الكثير من المكونات التي يجب تحميلها لعرض صفحة واحدة، ومن الطبيعي أن تلاحظ أنَّ أحد عناوين IP يحاول الوصول إلى نفس الصفحة مراتٍ عدّة كما في المثال الآتي: 12.345.678.910 - - [15/May/2016:15:15:03 -0700] "GET /wp-admin/network/ HTTP/1.1" 302 - "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36" 12.345.678.910 - - [15/May/2016:15:15:04 -0700] "GET /wp-login.php?redirect_to=http%3A%2F%2Fyour-site.com%2Fwp-admin%2Fnetwork%2F&reauth=1 HTTP/1.1" 200 2564 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36" 12.345.678.910 - - [15/May/2016:15:15:06 -0700] "GET /wp-admin/load-styles.php?c=1&dir=ltr&load%5B%5D=dashicons,buttons,forms,l10n,login&ver=29ef489256bcba8815b5864843de10bb HTTP/1.1" 200 38435 "http://your-site.com/wp-login.php?redirect_to=http%3A%2F%2Fwpwringer.com%2Fwp-admin%2Fnetwork%2F&reauth=1" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36" 12.345.678.910 - - [15/May/2016:15:15:07 -0700] "GET /wp-admin/images/wordpress-logo.svg?ver=20131107 HTTP/1.1" 200 1521 "http://your-site.com/wp-admin/load-styles.php?c=1&dir=ltr&load%5B%5D=dashicons,buttons,forms,l10n,login&ver=29ef489256bcba8815b5864843de10bb" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36" 12.345.678.910 - - [15/May/2016:15:15:09 -0700] "GET /favicon.ico HTTP/1.1" 200 - "http://your-site.com/wp-login.php?redirect_to=http%3A%2F%2Fwpwringer.com%2Fwp-admin%2Fnetwork%2F&reauth=1" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36" 12.345.678.910 - - [15/May/2016:15:15:16 -0700] "GET /wp-includes/js/thickbox/loadingAnimation.gif HTTP/1.1" 200 15238 "http://your-site.com/wp-admin/network/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36" 12.345.678.910 - - [15/May/2016:15:15:16 -0700] "GET /wp-admin/admin-ajax.php?action=dashboard-widgets&widget=dashboard_primary&pagenow=dashboard-network HTTP/1.1" 200 1744 "http://your-site.com/wp-admin/network/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36" 12.345.678.910 - - [15/May/2016:15:15:14 -0700] "GET /wp-admin/load-styles.php?c=1&dir=ltr&load%5B%5D=dashicons,admin-bar,wp-pointer,common,forms,admin-menu,dashboard,list-tables,edit,revisions,media,themes,about,nav-menus,widgets&load%5B%5D=,site-icon,l10n,buttons,wp-auth-check&ver=29ef489256bcba8815b5864843de10bb HTTP/1.1" 200 90768 "http://your-site.com/wp-admin/network/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36" 12.345.678.910 - - [15/May/2016:15:15:15 -0700] "GET /wp-admin/load-scripts.php?c=1&load%5B%5D=hoverIntent,common,admin-bar,jquery-ui-widget,jquery-ui-position,wp-pointer,wp-ajax-response,jquery-color,wp-lists,quicktags,jqu&load%5B%5D=ery-query,admin-comments,jquery-ui-core,jquery-ui-mouse,jquery-ui-sortable,postbox,dashboard,thickbox,plugin-install,svg-painter&load%5B%5D=,heartbeat,wp-auth-check&ver=29ef489256bcba8815b5864843de10bb HTTP/1.1" 200 51768 "http://your-site.com/wp-admin/network/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36" 12.345.678.910 - - [15/May/2016:15:15:30 -0700] "GET /wp-admin/network/plugin-editor.php HTTP/1.1" 200 38208 "http://your-site.com/wp-admin/network/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36" 12.345.678.910 - - [15/May/2016:15:15:32 -0700] "GET /wp-admin/load-scripts.php?c=1&load%5B%5D=hoverIntent,common,admin-bar,jquery-ui-widget,jquery-ui-position,wp-pointer,svg-painter,heartbeat,wp-auth-check&ver=29ef489256bcba8815b5864843de10bb HTTP/1.1" 200 17875 "http://your-site.com/wp-admin/network/plugin-editor.php" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36" 12.345.678.910 - - [15/May/2016:15:15:33 -0700] "GET /favicon.ico HTTP/1.1" 200 - "http://your-site.com/wp-admin/network/plugin-editor.php" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36" أبقِ في بالك أنَّ هذه النوع من الزيارات يحدث يوميًا، وكل سطر في سجل الوصول يشير عادةً إلى جزءٍ من أجزاء الصفحة التي يجري تحميلها، مثل ملفات JavaScript والصور. والفرق بين المخترق الذي يحاول الوصول إلى نفس الصفحة التي يحاول الزائر الاعتيادي الوصول إليها هو أنَّ المخترق قد يحاول استخدام برمجية للوصول إلى نفس الصفحة مرارًا وتكرارًا، لذا ستجد تكرارًا لنفس الأسطر في السجل. السجل الموجود أعلاه يُظهِر زائرًا حاول إدخال عنوان الموقع في متصفحه ويتبعه ‎/wp-admin/network كما هو ظاهر في أول سطر. ونجحت هذه الطلبية، وثم أعيد توجيه المستخدم تلقائيًا إلى صفحة تسجيل الدخول. بعد عدِّة أسطر (التي تمثِّل تحميل مختلف أنواع الوسائط)، سنرى أنَّ المستخدم قد نجح بتسجيل دخوله وأعيد توجيهه إلى لوحة التحكم في السطرين السادس والسابع. ومن ثم انتقل مباشرةً إلى محرر الإضافات (Plugin Editor) في السطر العاشر. وصحيحٌ أنَّ هذه نسخة مختصرة مما ستشاهده في سجلك، لكنها تعطيك فكرة عن سلسلة الأحداث التي تشتبه بها. فلن يحاول الكثير من المستخدمين الوصول إلى محرر الملفات في لوحة التحكم بعد تسجيل دخولهم مباشرةً، لكن سيفعل المخترقون ذلك. وفي هذه الحالة، يبدو أنَّ المخترق قد حصل على اسم المستخدم وكلمة المرور بطريقةٍ ما. إذا رأيتَ في سجلات الأخطاء أو الوصول أنَّ هنالك محاولاتٌ كثيرةٌ للوصول إلى صفحة التسجيل أو صفحة «نسيت كلمة المرور»، فلقد اكتشفتَ مَن اخترق موقعك وكيف استطاع الدخول إلى موقعك عبر هجوم التخمين (brute force attack). قد تستدل على وجود عدد كبير من المحاولات الفاشلة التي تراها قبل نجاح إحداها عبر النظر إلى رمز حالة HTTP؛ فسيشر إلى أخطاءٍ من طرف العميل إذ كان يبدأ بالرقم 400، وهذه بعض دلالات هذه الأرقام: 400 Bad Request: هذا يعني أنَّ المستخدم ارتكب خطأً في صياغة الطلبية. 401 Unauthorized: يحدث هذا الخطأ عندما يُطلَب من المستخدم الاستيثاق للوصول إلى الصفحة، لكن المعلومات التي أدخلها غير صحيحة. 403 Forbidden: هذا الخطأ شبيهٌ بالخطأ 401، فهذا الرقم يشير إلى أنَّ المستخدم غير مرخَّص له برؤية الصفحة التي حاول الوصول إليها، وهذا يعني أنَّ الطلبية صحيحةٌ تقنيًا، لكن الخادوم قرر ألّا يعطي المستخدم وصولًا إلى الصفحة (وهذا هو الاختلاف بينهما). 429 Too Many Requests: إذا ثبَّتتَ إضافةً تحصي عدد مرات وصول أحد المستخدمين إلى الصفحة وتضع حدًا أقصى لها، فسيرى المستخدم هذا الخطأ عندما يجري طلبيات أكثر من الحد الأقصى خلال فترة زمنية محددة. وهذا لن يشكِّل مشكلةً للمستخدمين العاديين لكن ربما يشير إلى استخدام أحد المخترقين لبرمجية اختراق. صحيحٌ أنَّ القائمة السابقة لا تحتوي جميع رموز الأخطاء التي قد تصادفها، لكن يجب أن تعطيك فكرةً عمّا عليك البحث عنه في سجلات الوصول. أصعب جزء من عملية البحث في سجلاتك هي أنَّك ستصادف الكثير من الصفحات التي يطلبها الزوار العاديون حتى لو كان موقعك صغيرًا. فلو سجَّل مدير الموقع دخوله إلى لوحة التحكم فسينتج ذلك حوالي 10 أسطر في سجل الوصول قبل أن يضغطوا على أيّ شيءٍ بعد تسجيل دخولهم، لذا يمكنك أن تتخيل الحجم الكبير للسجل الذي عليك تفحصه حتى لو كان زوار موقع قلّة. ترشيح واستبعاد الزوار العاديين إذا رشَّحتَ الطلبيات العادية فيمكنك أن تضيّق مجال بحثك كثيرًا، شارك موقع Sucuri Security بعض النصائح حول كيفية تفسير سجلاتك باستخدام الأداة grep بعد الاتصال عبر SSH. افتح عميل SSH المفضَّل لديك مثل Terminal لأجهزة Mac OS X أو PuTTY للأجهزة العاملة بنظام ويندوز وأدخل أحد الأوامر المذكورة أدناه والتي تناسب احتياجاتك. يمكنك أن تقلل كمية السجلات التي عليك تفحصها إلى أكثر من النصف عبر استبعاد جميع الأسطر التي تتضمن طلبيات لصفحات وملفات معينة مثل ملفات CSS و JavaScript والصور، والزيارات إلى الصفحة الرئيسية وصفحة ‎/contact و ‎/singup. $ cat access-log |grep -Ev "\.(js|css|png|jpg|jpeg) HTTP/1" |grep -Ev " GET (/|/contact|/signup) HTTP/1"| more أشرنا إلى الصفحة الرئيسية في المثال السابق بأوّل علامة / في العبارة GET (/|/contact|/signup)، يمكنك أيضًا تغيير contact و singup إلى صفحات أخرى إذا ارتأيتَ أنها تتعلق بموقعك ولن يهتم بها أحد المخترقين. إذا وجدتَ أنَّ الأمر السابق لم يُرشِّح أغلبية سجلك، وما يزال عليك تفحص آلاف الطلبيات، فيمكنك تجربة الأمر الآتي للبحث عن الطلبيات التي تحاول الوصول إلى صفحة تسجيل الدخول وإلى لوحة التحكم: $ cat access-log |grep -E "wp-admin|wp-login|POST /" | more ولمّا كان هذه الصفحات هدفًا للهجمات، فيمن المفيد تفحص هذا النوع من الطلبيات، خصوصًا لو أشار سجل الأخطاء إلى وجود عدد كبير من المحاولات الفاشلة للوصول إلى إحدى الصفحتين السابقتين. إذا كنتَ تعرف عناوين IP لجميع مدراء الموقع، فيمكنك أن تستبعد الطلبيات التي يجرونها عبر الأمر الآتي: $ cat access-log |grep -E "wp-admin|wp-login|POST /" |grep -v "^1.2.3.4|1.2.3.5" | more لاحظ أنَّ عليك أن تضع عناوين IP الحقيقية للمدراء بدلًا من 1.2.3.4 و 1.2.3.5 في الأمر السابق. يجب أن تساعدك الأوامر السابقة بتضييق مجال بحثك في السجلات لتجعل الطلبيات التي يجريها المخترقون واضحةً لك، لكن لا تظن أنَّ هذه الطريقة بالدقة التي كنتَ تتوقعها، فلا تُغفِل أنَّ لديك وقت قليل جدًا لتفحص السجلات في أغلبية الحالات، وقد لا تخرج بأيّة نتائج من هذه العملية، حتى لو استعنتَ بالأداة grep، فمعرفة كيف استطاع المخترق الوصول إلى موقعك كالبحث عن أبرة في كومة قش. هذا هو السبب وراء أتمتة هذه العملية، فهي أكثر دقةً وكفاءة مقارنةً بالبحث اليدوي في السجلات. أتمتة عملية استكشاف الأخطاء هنالك الكثير من الإضافات التي يمكن أن تراقب موقعك وتُخطِرُكَ إن حاول أحد المخترقين اختراق موقعك، حتى لو كنتَ نائمًا :-) . هذه الإضافات مثل Wordfence و Sucuri Security تملك خيارات رائعة لتأمين موقعك، وتوفر قائمةً مذهلةً من الخيارات لحجب المخترقين، الأمر الذي قد يصعب على بعض المستخدمين فعله. يمكنك –بشكلٍ بديل– استخدام Defender الذي يحجب المخترقين، ويسمح لك بتقوية أمن موقعك ببضع نقرات، ويحاول إصلاح الأجزاء التي قد تكون عرضةً للاختراق، ولا تُغفِل سهولةَ استخدامه وتبسيطه للمعلومات الأمنية. إضافة Defender موجودة أيضًا إذا كنتَ مشتركًا في WPMU DEV لكن يمكنك تجربته مجانًا؛ سأذكر في الأقسام الآتية كيف تعرف إذا تعرضَت للاختراق، وكيف «تنظِّف» موقعك بعد تعرضه لهجوم وكيف تؤمنه منعًا لحدوث مثل هكذا هجمات مستقبلًا، وبهذا ستمنع المخترقين مستقبلًا من مهاجمة موقعك. يمكنك فعل كل ذلك بإصلاح المشاكل الأمنية وتقوية حماية موقعك عبر ضبط إضافة Defender. البدء باستخدام إضافة Defender قبل أن تنظِّف موقعك وتحميه، عليك تنزيل إضافة Defender ثم تثبيتها وتفعيلها على موقعك. إضافة Defender متوافقة مع موقع ووردبريس مستقل، أو مع شبكة متعددة المواقع، أو مع شبكة BuddyPress؛ حيث تُفعَّل إضافة Defender على عموم الشبكة لذا ستتمكن من إدارة حماية جميع موقعك من لوحة تحكم إدارة الشبكة. ولمّا كنت تنوي إجراء تغييرات كبيرة في موقعك، فمن المهم أخذ نسخة احتياطية كاملة لموقعك قبل إجراء أيّة تعديلات، راجع درس الحماية وإضافات النسخ الاحتياطي في ووردبريس تَفحُّص موقعك بعد أن تنتهي من تنزيل الإضافة وتفعيلها، فيمكنك تفحص موقعك بحثًا عن ثغرات مباشرةً. إذا كنتَ تريد التحقق من الإعدادات فاذهب إلى Defender > Settings؛ وإلا، فاذهب مباشرةً إلى Defender > Scan ثم اضغط على زر Scan My Website. قد يأخذ التفحص بضع دقائق في المواقع أو الشبكات الكبيرة، لكن يمكنك أن تنتقل إلى صفحةٍ أخرى في موقعك أو حتى تستطيع أن تغلق متصفحك. وبعد انتهاء عملية التفحص يمكنك العودة ورؤية النتائج. إذا عُثِرَ على ثغرات أو ملفات خبيثة، فستُعرَض قائمةٌ بها، فيمكنك أن تضغط على أيقونة «المفك» بجانب العنصر الموجود في القائمة لعرض المزيد من التفاصيل عن المشكلة ويمكنك حينئذٍ أن تحذف الملف أو تتجاهل هذه الثغرة إذا لم تكن مشكلةً أمنيةً، ويمكنك أيضًا مقارنة الملف مع نسخة حديثة من ووردبريس أو يمكنك استعادة الملف المكتشف من نسخة حديثة من ووردبريس. يمكنك أن تضغط على زر التجاهل لتجاهل الملف مباشرةً، يمكنك استعادة هذه النتيجة في أي وقت لحلها لاحقًا. هنالك أيضًا أيقونة لحذف الملف مباشرةً. ستُضمَّن في الإصدارات القادمة ميزة التسجيل الحي تلقائيًا وعلى مدار الساعة، لكن لمّا كانت إضافة Defender تكتشف التغيرات التي أُجريَت على ملفاتك، فيمكنك –نظريًا– أن تعرف كيف وصل المخترق إلى موقعك دون الحاجة إلى النظر في السجلات، فلطالما أبقيتَ على إضافة Defender مثبتةً فلا يصيبنّك القلق حول أمن موقعك، لأن المخترقين سيوقَفون قبل أن تَسنَح لهم فرصة الدخول غير المصرَّح به إلى موقعك. يمكنك أيضًا أن تتأكد أن المخترقين لن يتلفوا على أمن موقعك وذلك بتقوية الحماية، ويمكنك فعل ذلك ببضع نقرات وذلك في صفحة Defender > Hardener. الثغرات التي لم تُحَل بعد ستعلَّم باللون الأصفر، ويمكنك الضغط على زر + لعرض تفاصيل حول الثغرة. ويمكنك –بنقرة واحدة– أن تحل المشكلة أو أن تتجاهلها؛ ويمكنك العودة إلى المشاكل التي تجاهلتها لاحقًا. يمكنك –إضافةً إلى تعطيل محرر الملفات وتحديث المفاتيح الأمنية– أن تُغيّر بادئة جداول قاعدة البيانات، وأن تُغيّر اسم المستخدم للحساب الافتراضي admin، وأن تخفي التبليغ عن الأخطاء في الواجهة الأمامية، وأن تمنع تنفيذ شيفرات PHP غير المصرَّح بها، والكثير غير ذلك. يمكنك أيضًا أتمتة إجراء التفحص بالذهاب إلى صفحة Defender > Automated Scans كي تنام قرير العين عالمًا أنَّ موقعك مؤمن حتى لو كنتَ مشغولًا عنه. يمكنك أن تضبط اليوم والوقت الذي سيُجرى فيه الفحص، ويمكنك أن تحدِّد إذا كنتَ تريد فعل ذلك يوميًا أو أسبوعيًا أو شهريًا. الخلاصة بعد أن تعرف كيف اُخترِق موقعك عبر تفحص سجلات الوصول يدويًا أو عبر إضافة Defender تلقائيًا، فيمكنك أن ترقِّع الثغرة الأمنية التي استعملها المخترق للوصول إلى موقعك، بدلًا من أن تحس أنَّك لا تعرف ما الذي حدث وتحاول تخمين ذلك. لكن بعد أن تنظِّف موقعك وتؤمنه، فلا تحسبنَّ أنَّ المعركة قد انتهت، فمن المهم أن تثبِّت إحدى الإضافات الأمنية مثل Defender لحماية موقعك على مدار الساعة. تثبيت إضافة Defender يعني أنَّ محاولات الاختراق المستقبلية ستُصدَّ قبل أن تصبح مشكلةً تعطِّل موقعك وتأخذ من وقتك. يمكن لإضافة Defender أن تساعدك في تحديث موقع ووردبريس مع قوالبك وإضافاتك لتحصل على آخر التحديثات الأمنية. ترجمة –وبتصرّف– للمقال Help, I’ve Been Hacked! How to Troubleshoot and Fix a WordPress Siteلصاحبته Jenni McKinnon حقوق الصورة البارزة محفوظة لـ Vecteezy
  7. يتطلب تطبيق (وفهم) هذا الدرس درايةً واسعةً بلغة JavaScript وتقنيات CSS3، أي أنه ليس لمن لا خبرة له معهما. أرجو أن تتطلع على الدروس المتاحة هنا في الأكاديمية، وعلى كتاب تعلم JavaScript.
  8. تحدثنا في الجزء السابق عن أهمية إعادة توجيه العناوين القديمة للصفحات إلى عناوينها الجديدة، وسنكمل شرح كيفية فعل ذلك في ووردبريس في هذا الدرس. أولًا: نقل موقع ووردبريس قديم إلى مضيفٍ جديد هذه الحالة غير معقدة أبدًا، فلو كنتَ تنقل موقع ووردبريس إلى مضيفٍ جديد دون تغيير بنية الروابط الدائمة لصفحات الموقع، فلا توجد اختلافات في الروابط الدائمة مما لا يستدعي استخدام إعادة توجيه عبر الرمز 301. وهذا بسبب امتلاك الموقع لنفس الصفحات التي كانت فيه بنفس روابطها الدائمة، وذلك من وجه نظر Google، فلو كان يتوقع وجود إحدى الصفحات في الرابط example.com/great-optimized-page فسيجد نفس الصفحة بنفس المحتوى بعد نقل الموقع إلى المضيف الجديد، وكل ما اختلف هو أنَّ الخادوم الذي كان مسؤولًا عن إيصال الصفحة إلى العميل قد تغيّر. لن تحتاج إلى إجراء الكثير من العمليات للحفاظ على تقييم SEO عندما تنقل موقع ووردبريس من مضيفٍ لآخر مع الإبقاء على بنية الروابط الدائمة نفسها في أغلبية الحالات، لكن ما يزال عليك أن تنتبه إلى أمرين مهمين ألا وهما: سرعة الموقع والانتقال إلى خادومٍ يستعمل تشفير SSL. التأكد من أداء وسرعة الموقع أوّل عنصر يمكن أن يؤثر في تقييم SEO عند نقل موقع ووردبريس من مضيفٍ إلى آخر هو سرعة الموقع، إذ يُكافِئ Google المواقع السريعة، لذا احرص على نقل عملائك إلى بيئةٍ أسرع وليس إلى بيئةٍ أبطأ، وابذل جهدك لكي تتفادى حدوث أيّة مشاكل في الأداء والتي قد تحدث عند الانتقال إلى مضيفٍ آخر. أداة PageSpeed Insights من Google هي أداةٌ رائعةً لاستكشاف المشاكل المتعلقة بالأداء وتحسين سرعة الموقع. أنصحك أيضًا بالتواصل مع فريق الدعم الفني لشركة الاستضافة وتتأكد من ضَبطِ كلَّ شيءٍ ضبطًا صحيحًا، فاطلب منهم مثلًا التحقق أنَّ التخزين المؤقت لصفحات الموقع يعمل عملًا سليمًا. فلو انتقلتَ مثلًا إلى شركة الاستضافة SiteGround، فعليك أن تُفعِّل ميزات التخزين المؤقت يدويًا، وربما ترغب بتعطيل بعض إضافات التخزين المؤقت مثل W3 Total Cache، لكي لا تتكرر النسخ المؤقتة في الخادوم. قد تستصعب فعل ما سبق ذكره، لذا حاول التواصل مع فريق الدعم التقني لشركة الاستضافة ليساعدك في الأمر. الجوانب التقنية الأخرى الخاصة بضبط الخادوم، مثل استخدام آخر إصدار من PHP وتفعيل شيءٍ يسمى php-fpm (والذي لا أتظاهر بفهم الغاية منه)، يمكن أن تؤثر في أداء موقعك تأثيرًا كبيرًا، لذا إذا أشارت أداة PageSpeed Insights إلى أداءٍ ضعيفٍ أو متوسط (خصوصًا إذا كان وقت استجابة الخادوم كبيرًا) فلا تخجل من الحديث عن هذه المشكلة مع فريق الدعم التقني لمحاولة حلّها. الانتقال إلى SSL ثاني أمر عليك أخذه بالحسبان عند نقل موقع ووردبريس بين خادومين هو أمرٌ تقنيٌ بحت، ويتمحور حول شهادات SSL، فلو كنتَ تنتقل من موقع لا يستعمل شهادات SSL (أي أنَّ روابط URL تشبه http://example.com) إلى بيئة تستعمل فيها شهادات SSL (الروابط تشبه https://example.com)، فيجب أن تتنبَّه إلى ذلك؛ لأنَّ محرِّك البحث والزوار سيبحثون عن الصفحات في http://example.com وإذا لم تمنعهم من ذلك، فسينتهي بهم المطاف بالدخول إلى النسخة القديمة غير الآمنة من الموقع، وهذا يعني: 1. سيُدخِل المستخدمون بياناتهم الشخصية مثل معلومات بطاقاتهم الائتمانية في النسخة غير الآمنة من الموقع، وهذا أمرٌ خطيرٌ جدًا من الناحية الأمنية. 2. ستكون معلومات تحليل موقعك غريبةً جدًا، لأنك سترى إحصائيات عن زيارات النسخة الآمنة أو غير الأمنة كلًا على حدة. 3. سيرى محرِّك Google نسختين من الصفحات، إحداها موجودةٌ في http://example.com والأخرى مماثلةٌ تمامًا لها موجودةٌ في https://example.com، مما يجعله يظن أنَّك تملك موقعين منفصلين فيهما نفس المحتوى، مما يعرِّضك للعقوبة بسبب تكرارك للمحتوى نفسه. دون الدخول بتفاصيل تقنية بحتة عن هذه المشكلة، دعني أخبرك ماذا عليك أن تفعل: ثبِّت إضافة WP Force SSL وهي إضافةٌ بسيطةٌ ورائعةٌ والتي تُعيد توجيه جميع الزيارات التي تقصد الصفحات التي تبدأ بالسابقة ‎http://‎ إلى نسخة https://‎ منها. عليك اتباع التعليمات الخاصة بالإضافة والمتعلقة بضبط ووردبريس ثم عليك تفعيلها. هنالك عملٌ كثيرٌ علينا فعله لجعل شهادات SSL المثبتة حديثًا تعمل عملًا سليمًا، لكن بعد تثبيت إضافة WP Force SSL فستتأكد من عدم وجود نسختين من موقعك مما لا يضر بتقييم SEO. أبقِ في ذهنك أنَّ Google سيُكافئ المواقع التي تستعمل تشفير SSL، لذا اعلم أنَّ انتقالك إلى استخدام شهادة SSL سيفيدك على المدى الطويل. ثانيًا: نقل موقع عادي إلى موقع ووردبريس إذا كنتَ تنقل موقعًا من تقنيةٍ مختلفةٍ إلى ووردبريس، فعليك حكمًا أن تفهم كيفية استعمال إعادة التوجيه من النوع 301، وكيف ستساعدك إعادة التوجيه في ووردبريس في الحفاظ على تقييم SEO للصفحات القديمة. السبب وراء استخدام إعادة التوجيه هو اختلاف التقنيات واختلاف بُنى الروابط الدائمة. التقنيات المختلفة تستعمل بُنى مختلفة للروابط الدائمة انظر إلى الصفحات الأربع الآتية: 1. example.com/great-optimized-page.html 2. example.com/great-optimized-page.asp 3. example.com/great-optimized-page.php 4. example.com/index.php?page=great-optimized-page لا يعلم Google أرتباط أيِّ صفحةٍ من الصفحات السابقة بأخرى، فلها أربعة روابط URL مختلفة تمامًا، ولا يملك Google أيّة فكرة أنَّها تُشير جميعها إلى المحتوى نفسه. إذا كتبتَ موقعك من الصفر باستخدام PHP فستبدو الروابط الدائمة لصفحاتك كالآتية: example.com/great-optimized-page.php. أما إذا استعملتَ صفحات HTML الثابتة، فستبدو الروابط الدائمة كما يلي: example.com/great-optimized-page.html. أما إذا كنّا سننتقل من دروبال إلى ووردبريس فقد تكون بنية الروابط الدائمة مختلفةً اختلافًا جذريًا. تتضمن خطتك نقل أحد أنواع المواقع السابقة إلى ووردبريس؛ والتي تُتيح استعمال عدِّة أشكال من الروابط الدائمة، وأشهرها يبدو كالآتي: example.com/great-optimized-page. إذًا عند نقلك لموقعك إلى ووردبريس، فسيصبح لكل صفحة في الموقع رابطٌ دائمٌ جديدٌ بسبب اختلاف التقنيات المستعملة، وهذا ينطبق على الغالبية العظمى من التقنيات التي تحاول نقلها إلى ووردبريس. كيف نخبر Google عن التغييرات في الروابط الدائمة وكما شرحنا أعلاه، لا يعلم Google عن تغيير الروابط الدائمة شيئًا إلا إذا أخبرتَه بذلك، وعليك فعل ذلك للحفاظ على تقييم SEO لعملائك، لذا حان الوقت الآن لتعلّم كيفية إعادة توجيه الصفحات في ووردبريس. الطريقة التقنية لفعل ذلك هي عبر تعديل ملف ‎.htaccess، والذي يستعمله خادوم أباتشي (Apache) ليقرأ التعليمات منه، لكن كتابة قواعد ملف ‎.htaccess هي مهمةٌ صعبة ومن السهل تعطيل الموقع في حال ارتكاب خطأ صغير فيه؛ لكن هنالك حلٌّ آخر يوفِّر بديلًا رائعًا أقل تعقيدًا من التعديل اليدوي هو استخدام إضافة مجانية خاصة بوردبريس باسم Simple 301 Redirects. إضافة Simple 301 Redirects تسمح لنا بتمرير الزيارات إلى مكانٍ آخر ضمن النطاق نفسه، أو إلى نطاقٍ آخر مختلفٍ تمامًا، كما لو أنَّك تكتب قواعد ملف ‎.htaccess يدويًا. يمكنك أيضًا استخدام المحارف البديلة (wildcard) لتحديد جميع الصفحات، فلنقل مثلًا أنك تريد إعادة توجيه جميع الصفحات الموجودة في domain.com/‎ إلى مكانها الجديد في domain.com/app/‎. هذه صورةٌ لصفحة إعدادات إضافة Simple 301 Redirects التي تُظهِر جميع عمليات إعادة التوجيه في أحد المواقع: ‎كما ترى، الروابط الموجودة في العمود على يسار الصورة يُعاد توجيهها إلى مختلف أشكال الروابط. يجدر بالذكر أنَّ هنالك ملحق مجاني باسم Bulk Uploader يتبع لإضافة Simple 301 Redirects، والذي يساعدك في معالجة عشرات ومئات عمليات إعادة التوجيه عبر رفع ملف بتنسيق CSV. كيف تعثر على الصفحات التي عليك إعادة توجيهها أفضل طريقة لمعرفة ما هي روابط URL التي تريد إعادة توجيهها هي إنشاء سجل بها قبل نقلك الموقع إلى ووردبريس، إذ يمكنك إنشاء ورقة عمل Excel أو Calc تحتوي جميع الروابط الدائمة الموجودة في الموقع الأصلي وأين يجب أن تُشير بعد الانتقال إلى موقع ووردبريس. يجب أن تبدو السجلات كالآتية: ‎/great-optimized-page.asp > /great-optimized-page وذلك لكل صفحة موجودة في موقعك. لكن في حال نقلتَ موقعك إلى ووردبريس ولنفترض أنّك فقدتَ سجل الصفحات التي كانت موجودةً في موقعك القديم، فتكون أفضل طريقة حينئذٍ هي رؤية ما الذي يعرفه Google عن موقعك عبر تصفح صفحة النتائج الخاصة بموقعك. إذا كتبتَ عبارة بحث شبيهة بالعبارة الآتية site:exmaple.com في مربع البحث في Google، فستلاحظ وجود عدد كبير من روابط URL التي تُشير إلى صفحاتٍ في موقعك القديم (مثل صفحة ‎/great-optimized-page.asp)، وذلك لأنَّ Google ما يزال يعرض تلك النتائج ويُوجِّه الزوار إليها، حتى لو لم تعد موجودةً. يجدر بك إعادة توجيه جميع النتائج التي تعثر عليها باستخدام إضافة Simple 301 Redirects، أو أيِّ إضافةٍ أخرى تُجري عملية إعادة التوجيه، أو يمكنك كتابة قواعد ملف ‎.htaccess يدويًا. يمكنك اختبار نجاح عملية إعادة التوجيه بضغطك على أحد الروابط، فإن وجدتَ أنها تُشير إلى الصفحة المعنية في ووردبريس فذلك أمرٌ حسن، أو انتهى بك المطاف في صفحة «404» فعليك تحري ما هي المشكلة التي تمنع إعادة التوجيه إلى الصفحات الجديدة. ثالثًا: تغيير بنية الروابط الدائمة في موقع ووردبريس موجود مسبقًا عندما تُغيّر بنية الروابط الدائمة في ووردبريس بالدخول إلى «Settings > Paramalinks» (الإعدادات > الروابط الدائمة)، فأنت تزيد من احتمال ظهور رسائل 404 للزوار. تحاول ووردبريس إعادة توجيه بعض أنواع التعديلات على الروابط الدائمة تلقائيًا، لكن لا تفعل ذلك لجميع أنواع التعديلات، ولسوء الحظ لا تكون القواعد التي تتبعها ووردبريس لإعادة التوجيه متناسقةً ولا يسهل فهمها، لذا لا تعتمد على ووردبريس لفعل ذلك، فعندما تُغيّر بنية الروابط الدائمة (حتى لو فعلتَ ذلك لرابطٍ دائمٍ لصفحةٍ واحدةٍ فقط) فعليك تجربة الموقع يدويًا باحثًا عن صفحات 404، وتجرِّب الروابط الدائمة القديمة، ثم تصلح أيّة صفحات 404 تعثر عليها باستخدام Simple 301 Redirects أو غيرها من الأدوات. الخلاصة ليس بالضرورة أن تكون عمليات إعادة التوجيه معقدةً، لكن من المهم جدًا أن نستعمل إعادة التوجيه عند الحاجة لما لها من أثرٍ كبيرٍ في الحفاظ على تقييم الصفحات في محرِّكات البحث. ترجمة –وبتصرّف– للمقال Understanding 301 Redirects in WordPress لصاحبه Fred Meyer
  9. تمهيد المُحرِّر التدفقي sed هو أداةٌ قويةٌ جدًا لتعديل النصوص، حيث تستطيع باستخدامها معالجة النصوص بجهدٍ بسيط. ناقشنا في الدرس السابق أساسيات استخدام المحرر Sed، وسنكمل مشوارنا في هذا الدرس بشرح بعض المواضيع المتقدمة. إمكانية تطبيق تغييرات عِدّة معا هنالك حالاتٌ ترغب فيها بتمرير أوامر sed عِدّة في الوقت نفسه، وهنالك طرائق عدّة لفعل ذلك. إذا لم تتوافر عندك الملفات التي عملنا عليها في الدرس السابق، فيمكنك تطبيق هذه الأوامر لإنشائها لكي نعدِّلها في سياق هذا الدرس: cd cp /usr/share/common-licenses/BSD . cp /usr/share/common-licenses/GPL-3 . 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 ولقدرة تعامل الأمر sed مع مجرى الإدخال والإخراج القياسي، فيمكننا استدعاء الأمر sed أكثر من مرة عبر استعمال الأنابيب (pipes، وذلك عبر رمز الخط الشاقولي |). تذكر أنَّ عليك تخليص (escape) المحرف & لأنَّه يحمل معنىً خاصًا في sed: sed 's/and/\&/' annoying.txt | sed 's/people/horses/' الناتج: this is the song that never ends yes, it goes on & on, my friend some horses started singing it not knowing what it was & they'll continue singing it forever just because… لقد نجحت محاولتنا، لكنّ ذلك غير عملي لأننا استدعينا الأمر sed مرات عدّة، وهو أطول، ولن نستفيد من ميزات sed المبنية داخله… يمكننا إرسال مختلف التعليمات إلى الأمر sed باستخدام الخيار ‎-e قبل كل تعليمة. سنُعيد كتابة الأمر السابق وسنستخدم فيه الخيار ‎-e: sed -e 's/and/\&/' -e 's/people/horses/' annoying.txt طريقةٌ أخرى لتنفيذ تعليمات sed عدّة معًا هي استعمال الفاصلة المنقوطة ; لفصل التعليمات عن بعضها. وهذا شبيهٌ بالأمر السابق لكن دون الحاجة إلى استعمال الخيار ‎-e: sed 's/and/\&/;s/people/horses/' annoying.txt لاحظ عند استخدامنا للخيار ‎-e أننا احتجنا إلى تضمين التعليمات المختلفة داخل علامتَي اقتباس؛ لكن عندما فصلنا التعليمات عبر الفاصلة المنقوطة فوضعناها جميعًا ضمن سلسلة نصية وحيدة محاطة بعلامتَي اقتباس فقط. وعلى الرغم من أنَّ الطريقتين السابقتين المستعملتين لتنفيذ تعليمات عدّة معًا مفيدتان، لكن هنالك حالات نضطر فيها إلى تمرير ناتج أحد أوامر sed إلى الآخر عبر أنبوب. خذ مثلًا المعامل =، الذي يُضيف رقم السطر في سطر جديد قبله. أمعن النظر في ناتج الأمر الآتي: sed '=' annoying.txt الناتج: 1 this is the song that never ends 2 yes, it goes on and on, my friend 3 some people started singing it 4 not knowing what it was 5 and they'll continue singing it forever 6 just because… إذا أردنا تغيير تنسيق الأرقام عبر تعديل النص، فسنجد أنَّ ذلك لا يعمل كما نتوقع. لشرح هذه الفترة، سنتعلم قليلًا عن التعليمة G التي تُضيف سطرا فارغا بين كل سطرين مبدئيًّا (استخدام هذه التعليمة أكثر تعقيدًا مما سبق، لكننا سنتعلمها لاحقًا): sed 'G' 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… إذا دمجنا بين هذين الأمرين، فسنتوقع إضافة سطر فارغ بين كل سطر عادي وسطر يحتوي رقمًا، لكننا سنحصل على ناتجٍ مختلف: sed '=;G' annoying.txt الناتج: 1 this is the song that never ends 2 yes, it goes on and on, my friend 3 some people started singing it 4 not knowing what it was . . . . . . ما حدث هو أنَّ المعامل = عدّل مجرى الإخراج مباشرة، وهذا يعني أننا لا نستطيع التعديل على الناتج؛ لكن يمكننا الالتفاف على هذه الإشكالية عبر استدعاء الأمر sed مرتين، مُمرِّرين ناتج أوّل أمر إلى ثاني أمر عبر أنبوب: sed '=' annoying.txt | sed 'G' الناتج: 1 this is the song that never ends 2 yes, it goes on and on, my friend 3 some people started singing it . . . . . . سنحصل الآن على الناتج الذي كنّا نتوقعه. أبقِ في ذهنك أنَّ بعض التعليمات تسلك هذا السلوك نفسه، وأتوقع أنَّك ستلاحظ ذلك مباشرةً عندما يختلف الناتج عمّا تتوقعه. طرائق متقدمة لتحديد المجالات إحدى ميزات استخدام المجالات (أو العناوين) في sed هي إمكانية استخدام التعابير النمطية معيارًا لتحديد الأسطر التي ستُطبَّق عليها التعليمات، وهذا يعني أننا لسنا محدودين بإمكانية إجراء التعديلات على الأسطر ذات القيمة المعلومة مسبقًا، كما تعلمنا في الدرس السابق: sed '1,3s/.*/Hello/' annoying.txt الناتج: Hello Hello Hello not knowing what it was and they'll continue singing it forever just because… يمكننا –بدلًا مما سبق– استعمال التعابير النمطية لمطابقة الأسطر التي تحتوي أنماطًا معيّنةً، ويمكننا فعل ذلك بوضع تعبير المُطابَقة بين خطين مائلين / قبل البدء بتعريف تعليمات التعديل، كما في المثال الآتي: sed '/singing/s/it/& loudly/' annoying.txt الناتج: this is the song that never ends yes, it goes on and on, my friend some people started singing it loudly not knowing what it was and they'll continue singing it loudly forever just because… وضعنا في المثال السابق الكلمة «loudly» بعد أوّل كلمة «it» في كل سطر يحتوي على السلسلة النصية «singing»، لاحظ أنَّ السطرين الثاني والرابع لم يُعدَّلا لعدم مطابقتهما للتعبير. قد تمسي التعابير التي تستعمل لتحديد مجالات الأسطر التي ستُنفَّذ عليها التعليمات معقدةً جدًا، لكن ذلك يوفِّر قدرًا كبيرًا من المرونة عند تنفيذ التعليمات. المثال الآتي ليس معقدًا أبدًا، لكنه يوضِّح كيف يمكن استخدام التعابير النمطية لتحديد مجالات لتُستعمَل مع تعليماتٍ أخرى. هذا التعبير يُطابِق أيّة أسطر فارغة (التعبير النمطي يُطابِق بداية السطر متبوعةً مباشرةً بنهاية السطر) ومن ثم تمرير هذه المجال من الأسطر إلى تعليمة الحذف: sed ‘/^$/d’ GPL-3 الناتج: GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for . . . . . . أبقِ في ذهنك أنَّنا نستطيع استعمال التعابير النمطية لتحديد بداية المجال ونهايته؛ فمثلًا، يمكننا حذف جميع الأسطر التي تأتي بعد سطرٍ يحتوي الكلمة «START» حتى يأتي سطرٌ فيه الكلمة «END» بتنفيذ الأمر: sed '/^START$/,/^END$/d' inputfile لكن عليك أن تنتبه أنَّ الأمر السابق سيحذف كل شيء بدءًا من أوّل START انتهاءً بأول END، ثم سيُكرِّر عملية الحذف إذا ظهرت الكلمة START مرةً أخرى. إذا أردتَ عكس المجال (أي تنفيذ التعلميات على الأسطر التي لا تُطابِق النمط)، فيمكنك اتباع النمط بعلامة التعجب !؛ فمثلًا، يمكننا حذف جميع الأسطر غير الفارغة (أعلمُ أنَّ هذا ليس مفيدًا، لكنني أعرضه هنا مثالًا) باستعمال الأمر الآتي: sed '/^$/!d' GPL-3 الناتج: لاحظ أنَّ بإمكانك استعمال علامة التعجب لعكس مجال التحديد على أي نوع من المجالات كالمجالات الرقمية، وليست حكرًا على التعابير النمطية فقط. استخدام ذاكرة التخزين (Hold Buffer) إحدى الميزات التي تزيد من قابلية استخدام sed لإجراء تعديلات على أسطر عدّة معًا تسمى «ذاكرة التخزين» (Hold Buffer)، وهي مكانٌ مؤقتٌ لتخزين النصوص التي يمكن تعديلها من تعليمات عدّة. وجود هذه الذاكرة يعني أننا نستطيع تخزين بعض الأسطر أثناء عملنا على أسطرٍ أخرى، ويمكننا إجراء العمليات على كلٍّ منها عند الحاجة. هذه هي التعليمات التي تستخدم مع ذاكرة التخزين: h: نسخ السطر المُطابَق (والذي نعمل عليه حاليًا) إلى ذاكرة التخزين (مما يؤدي إلى حذف المحتوى الذي كان موجودًا في ذاكرة التخزين). H: إضافة السطر المُطابَق إلى ذاكرة التخزين في نهايتها، وسيُوضَع قبله محرف السطر الجديد ‎\n. g: وضع محتويات ذاكرة التخزين مكان السطر المُطابَق (مما يؤدي إلى حذفه). G: إضافة محتويات ذاكرة التخزين إلى السطر المُطابَق مع فصلهما بمحرف السطر الجديد ‎\n. x: التبديل بين السطر المُطابَق وذاكرة التخزين. لنستكشف هذه الفكرة عبر تفحّص أحد الأمثلة المعقدة قليلًا. هذا المثال يبيّن كيفية دمج الأسطر المتجاورة مع بعضها بعضًا (في الواقع، يملك sed تعليمةً لفعل ذلك بسهولة وهي التعليمة N التي تدمج كل سطر إلى السطر الذي يسبقه، لكننا سنفعل ذلك يدويًا للتدرّب): sed -n '1~2h;2~2{H;g;s/\n/ /;p}' 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… الأمر السابق يبدو مخيفًا! لنقسِّمه إلى أجزاء ليسهل فهمه. أوّل شيء علينا ملاحظته هو أننا استعملنا الخيار ‎-n لإيقاف طباعة الأسطر تلقائيًا، مما يؤدي إلى عدم طباعة أيّة أسطر إلا التي نطلب من sed إخراجها. أوّل جزء من التعليمة هو ‎1~2h وأوّل قسم منها هو مجالٌ من الأسطر والذي يعني أنَّ على sed تنفيذ التعليمات على الأسطر ذات الأرقام الفردية بدءًا من السطر الأول (أي تخطي سطر بين كل سطرين)؛ أما h فهي تعليمة نسخ الأسطر المُطابَقة إلى ذاكرة التخزين. الجزء الثاني من التعليمة أكثر تعقيدًا، حيث يبدأ بمجال يُحدِّد الأسطر ذات الأرقام الزوجية (أي عكس المجال السابق). تُحتوى بقية التعليمة بين قوسين معكوفين، وهذا يعني أنَّ جميع الأوامر الموجودة بين القوسين ستُطبَّق على المجال المُحدَّد؛ وفي حال لم نستعمل الأقواس فستُطبَّق التعليمة H على المجال فقط، وبقية التعليمات ستُنفَّذ على جميع الأسطر. التعليمة H ستؤدي إلى نسخ محرف السطر الجديد متبوعًا بالسطر الذي جرت مُطابَقته إلى نهاية ذاكرة التخزين. ثم نسخنا ذاكرة التخزين (التي تحتوي على سطرٍ ذي رقمٍ فردي متبوع بمحرف السطر الجديد متبوع بسطرٍ ذي رقمٍ زوجي) مكان السطر المُطابَق، وذلك بالتعليمة g. ثم وضعنا فراغًا بدلًا من محرف السطر الجديد، ومن ثم طبعنا الناتج بالتعليمة p. وكما ذكرنا آنفًا، استخدام التعليمة N يؤدي إلى تقليل طول الأمر كثيرًا. الأمر التالي يُنتِج الناتج السابق نفسه: sed -n 'N;s/\n/ /p' 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… استخدام سكربتات sed إذا بدأت باستخدام التعليمات المعقدة، فستجد أنَّ من الأسهل كتابتها في محرِّرٍ نصي؛ وهذا مفيدٌ جدًا إذا كانت عندك تعليماتٌ كثيرة وأردتَ تطبيقها جميعها على النص نفسه. على سبيل المثال، إذا كنتَ تكتب رسائلك الإلكترونية في ملفاتٍ نصيةٍ بسيطة، فقد تُجري عليها بعض عمليات المعالجة لتنسيق النص قبل استخدامه، وفي هذه الحالة ستستفيد من كتابة سكربت sed. وبدلًا من كتابة أوامر sed كاملة في السكربت، كل ما عليك فعله هو وضع التعليمات في ملف وتمرير هذا الملف كمعطى (argument) إلى sed. أي أنَّ سكربت sed هو قائمة بتعليمات sed فقط (أي الأجزاء التي تضعها عادةً بين علامتي اقتباس مفردتين). مثلًا: s/this/that/g s/snow/rain/g 1,5s/pinecone/apricot/g يمكنك بعدئذٍ استخدام السكربت على النحو الآتي: sed -f sedScriptName fileToEdit تسمح لك سكربتات sed بكتابة جميع تعليمات التعديل في ملفٍ واحد، ثم تنفيذه على الملفات النصية التي ترغب بتنسيقها. الخلاصة أضفنا المزيد من المعلومات عن المُحرِّر sed إلى جعبتنا في هذه المقالة. أصدقكَ القول أنَّ تعليمات sed ليست سهلة الفهم من الوهلة الأولى، وعليك تجربتها مرات عدّة لتعتاد استخدامها. ولهذا السبب أنصحك بالتدرّب على إجراء مختلف أنواع التعديلات على النصوص لتحترف استخدام sed. أرجو أن تكون قد فهمتَ القدرات التي يعطيك إياها المُحرِّر sed عند إجراء تعديلات على النصوص، فكلما ارتقيتَ بمستواك في استخدامه قلَّ الوقت اللازم لإنجاز التعديلات على الملفات النصية. ترجمة –وبتصرّف– للمقال Intermediate Sed: Manipulating Streams of Text in a Linux Environment لصاحبه Justin Ellingwood.
  10. يوفّر الكائن Math في جافا سكريبت الكثير من الدوال والقيم منتشرة الاستخدام في الحساب والرياضيات. مثلا، إن أردنا الحصول على قيمة الثابت π (باي) الذي هو النسبة بين محيط دائرة وقطرها فيمكننا ذلك باستخدام القيمة Math.PI. من أكثر دوال الكائن Math شيوعا الدالة Math.max (والدالة المقابلة لها Math.min)؛ فمن الشائع أن تواجه مجموعةً من الأرقام وعليك معرفة أكبر أو أصغر رقم فيها، وصحيحٌ أنَّ بإمكانك استخدام معامل رياضي في JavaScript لمعرفة الإجابة، أو عبر استعمال سلسلة من عبارات if الشرطية، لكن هاتين الدالتين أكثر مرونةً وكفاءة. الدالتان Math.max وMath.min، على النقيض من الدوال التي تكتبها بنفسك في JavaScript، مبنيتان في أساس لغة JavaScript وهما جزءٌ من الكائن الساكن (static object) الخاص بالدوال الرياضية Math، وهو ما يعني أن بالإمكان استعمال دوال هذا الكائن في أيّ مكان من التطبيق أو المشروع دون تهيئة كائن خاص بها. تقبل هاتان الدالتان مجموعةً من الأرقام وسائط لها، إذ تُعيد الدالة Math.max أكبر رقم في المجموعة. جرّب مثلًا التعليمة البرمجية الآتية في console في المتصفح: Math.max(5, 10, 11, 3, 22); > 22 ملاحظة: إذا وضعت أي نوعٍ غير رقمي من القيم فستكون النتيجة هي NaN والذي يعني Not A Number (أي أن المدخلات أو المخرجات ليس رقمية). لاحظ أنَّ بإمكاننا تمرير متغيرات أو خاصيات (properties) تابعة لأحد الكائنات (object) إلى الدالة Math.max: let orbitalPeriod = new Object(); orbitalPeriod.Mercury = 87.97, orbitalPeriod.Venus = 224.70, orbitalPeriod.Mars = 686.98; Math.max(orbitalPeriod.Mercury, orbitalPeriod.Venus, orbitalPeriod.Mars) > 686.98 أحد الأمور الذي لا تستطيع الدالة Math.max فعلها هو العثور على أكبر قيمة في المصفوفة، وهو ما دعا في الماضي إلى استخدام حلقات التكرار ومقارنة كل القيم في ما بينها (عبر إحدى الخوارزميات المعروفة) وتخزين أكبر قيمة؛ إلا أن الأمر تغيّر مع ظهور المتصفحات الحديثة التي تدعم معيار EmacScript 6، والتي تمكّن من استعمال معامل باسم spread، وهو عبارة عن ثلاثة نقاط ... توضع أمام المصفوفة أثناء تمريرها للدالة، مما يختصر العملية كثيرًا: let orbitalPeriods = [87.97, 224.70, 686.98]; longestOrbit = Math.max(... orbitalPeriods); يُعرَف التأثير الذي يُحدثه المعامل Spread على المصفوفة باسم التوزيع Expension. يُكافئ تمرير مصفوفة موّزّعة تمرير جميع عناصرها على هيئة مجموعة أرقام؛ أي أن الكتابتين أدناه متكافئتان: Math.max(5, 10, 11, 3, 22); Math.max(... [5, 10, 11, 3, 22]); تُستعمل الدالة Math.max أيضًا للاختيار بين رقمين (إما/أو)، فلو كنّا نحتاج إلى قياس عرض الشاشة فسنستعمل: let screenWidth = Math.max(screen.width, 0); يجب أن تكون قيمة المتغير screenWidth النهائية رقميةً: فإما أن تساوي عرض الشاشة، أو إذا لم نستطع الحصول على تلك القيمة أو كانت غير رقمية، فالناتج 0. الحصول على أصغر رقم في المجموعة الدالة Math.min تعمل عكس عمل الدالة Math.max، فبتمرير مجموعة من القيم إليها ستُعيد الدالة Math.min أصغرها. Math.min(0.139, 0.15, 1); > 0.139 تُستعمَل الدالة Math.min عادةً لتعريف ما هي الحدود أو الشروط الدنيا، فلو كانت لدينا كرةٌ تتحرك داخل مستطيل، وكان الجانب الأيمن للمستطيل موجودًا على بعد 500 بكسل من إطار العرض، ولا نريد أن تخرج الكرة من المستطيل عند ارتطام جانبها الأيمن (ball.rigth) بحافة المستطيل (500)، فسنكتب: let collide = Math.min(ball.right,500); أحببتُ أن أنوِّه إلى إمكانية «توزيع» القيم الموجودة في مصفوفة داخل الدالة Math.min، فلو كانت لدينا مصفوفةٌ باسم bugSizes وأردنا معرفة أدنى قيمة فيها، فيمكننا كتابة: Math.min(... bigSizes) يمكننا بشكلٍ بديلٍ استخدام apply للحصول على نفس النتيجة، وهذه الطريقة مدعومةٌ دعمًا أفضل في المتصفحات: Math.min.apply(null, bigSizes); يمكن استعمال apply مع الدالة Math.max أيضًا. ترجمة – بتصرّف – للمقال To the Max: Using Math.min and .max لصاحبه Dudley Storey.
  11. سنستعمل في هذا الدرس تحريكات 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
  12. وجدتُ العمل كمطوّر على ووردبريس قليل المتاعب، فإنتاج مواقع وِب جميلة هو خدمةٌ رائعةٌ نقدمها للآخرين وأمرٌ لا يؤدي إلى كارثة إذا لم تتقنه تمامًا بادئ الأمر؛ حيث لا يُشابه العمل طيارا حربيا أو في حرس الحدود! لكن مع ذلك، هنالك استثناءات: ففي بعض الأحيان سبَّب لي عملي مشاكل وكان شعوري بالذنب وتأنيب الضمير سيؤدي بي إلى ترك عملي. ولاحظتُ أنَّ أسباب ذلك هي نفسها، ولقد وضعتها في رسمٍ توضيحي، لذا سأترك المخطط ليتحدث عنها: رغبتُ بترك عملي مرات عدّة عندما أثرّتُ على تقييم موقع العميل في محركات البحث؛ فأخطاء 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.
  13. عندما يتعلق الأمر ببناء نوع معيّنٍ من تطبيقات الويب، فأوّل ما يخطر ببالي هو استخدام ووردبريس! ومن بين المشاريع البرمجية التي عملتُ عليها في السنتين الماضيتين، تطلّب نصفها - على الأقل - إمكانية إدارة حسابات المستخدمين، وهو ما يعني عادةً السماح للمستخدمين بإنشاء حساباتهم، بإدخال بعض البيانات في حقلَي الاسم وعنوان البريد الإلكتروني، ومن ثم ستُرسَل إليهم رسالةٌ بريديةٌ لتفعيل حسابهم بعد إرسال النموذج. توفِّر ووردبريس طريقةً سهلةً لإدارة المستخدمين عبر لوحة التحكم، وإذا كان موقعك تعليميًا أو كنتَ تستعمله مدونةً لك، فلا حاجة إلى إنشاء آليةٍ أخرى لإدارة المستخدمين، لكن إذا كنتَ تبني تطبيقًا متكاملًا، فهنالك طرائق أخرى للتعامل مع حسابات المستخدمين. لنقل مثلًا أنَّ المُصمِّم قد وضع طريقةً معينةً لإدارة الحسابات في الموقع، وإذا أجبرتَ المستخدمين على استخدام لوحة التحكم فستتسبَّب بتخريب تجربة المستخدم، أليس كذلك؟ هنالك طرائقٌ أفضل لتسجيل حسابات المستخدمين وإدارتها بدلًا من استخدام لوحة التحكم التابعة لووردبريس، وسنناقش في هذا الدرس كيفية إنشاء حسابات المستخدمين برمجيًا، وسنلقي نظرةً على النقاط الآتية: الحصول على معلومات المستخدم بصيغةٍ معيّنة، التعرّف على المعلومات اللازمة لإنشاء حساب مستخدم، إنشاء الحساب، التأكد أنَّ شيفرتنا تخضع لمعايير كتابة الشفرات القياسية، وأنها سهلة القراءة والصيانة. لا تقلق، لن يكون هذا الدرس طويلا ومملًّا، أعدك بذلك! إنشاء حسابات مستخدمي ووردبريس يحتاج أحدنا إلى المعلومات الآتية –على الأقل– لإنشاء حساب مستخدم جديد: اسم المستخدم كلمة المرور عنوان البريد الإلكتروني عندما يتعلق الأمر بإنشاء اسم المستخدم، فأنا أفضِّل استخدام البريد الإلكتروني اسمًا للمستخدم لأنني أضمن أنَّه فريد ولن يتكرر مرةً أخرى؛ ولهذا سنستخدمه في هذا الدرس؛ وسنتحدث عن توليد كلمة المرور لاحقًا. أضف إلى ذلك ضرورة افتراض أنَّ البيانات ستأتيك بأيّ صيغة: فربما تكون بصيغة 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
  14. تمهيد المُحرِّر التدفقي 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
  15. بعد تعلّم المعلومات الرئيسية عن الفضاء ثلاثي الأبعاد والإضاءة، تعجّلنا في نهاية الدرس السابق لكي نحصل على صورة ثلاثية الأبعاد، إلا أنني سأعود قليلًا إلى الوراء وأشرح كائنات 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
  16. تمهيد بشكل عام، نستعمل حاويات 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
  17. قالب 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.
  18. شرحنا في أوّل جزء من هذه السلسلة أساسيات استخدام إطار عمل 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.
  19. تمهيد بايثون هي أداةٌ رائعةٌ لمعالجة البيانات. ومن المحتمل أنَّ البرامج التي تكتبها ستتضمن قراءة البيانات أو كتابتها أو تعديلها، ولهذا السبب ستستفيد جدًا من قدرة بايثون على التعامل مع مختلف صيغ الملفات التي تُخزِّن أصنافًا متنوعةً من البيانات. ليكن لديك -على سبيل المثال- برنامج بايثون الذي يتحقق من امتيازات الوصول لقائمة من المستخدمين؛ فيكون من المرجح في هذه الحالة أن تُخزَّن قائمة المستخدمين في ملفٍ نصيٍ بسيط. أو ربما لم تكن تتعامل مع النصوص، وإنما مهمة برنامجك هي التحليل الاقتصادي؛ ومن المرجح أنَّك ستأخذ الأرقام التي ستجري عملياتك عليها من ورقة عمل في أحد البرامج مثل 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
  20. يأتي قالب 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.
  21. لقد أنشأتَ شركتك لتلبي احتياجات مستخدميك، أليس كذلك؟ سواءً كنتَ ستساعدهم شخصيًا أو على الهاتف أو عبر البريد الإلكتروني، وتستطيع مساعدتهم أيضًا –دون تدخلٍ منك– عبر موقعك الإلكتروني. وسواءً أكانوا زبائن محتملين يستكشفون الموقع، أو زوارًا مشتركين في موقعك لكنهم لم يشتروا شيئًا بعد، أو أنَّهم زبائن يدفعون لقاء خدماتك؛ لكن الأمر ليس مهمًا، وإنما المهم أن تتواصل معهم دومًا. وما لم تكن تسأل زوارك عمّا يفعلون، فكيف ستعرف شعورهم تجاه شركتك؟ هذا الأمر مهمٌ جدًا خصوصًا لموقعك الإلكتروني، الذي يُشكِّل دعامةً أساسيةً لشركتك والذي أنفقته عليه مالًا ووقتًا كثيرًا لبنائه. أقتبس المقولة الآتية من 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.
  22. كانت هنالك الكثير من المحاولات الباكرة لابتكار تقنية لإنشاء رسوميات ثلاثية الأبعاد 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.
  23. تأتيني عادةً أسئلةٌ عمّا إذا كانت شبكةٌ متعددة المواقع تعمل على ووردبريس هي خيارٌ مناسبٌ لمواقع الشركات التي لديها أقسامٌ متنوعةٌ ذات طابعٍ مختلف. ويكون جوابي عادةً هو «نعم»، يمكن لشبكةٍ متعددة المواقع فعل ذلك. لكن ليس من الضروري إنشاء موقعٍ منفصلٍ لكل قسمٍ من أقسام شركتك (حتى لو استعملتَ شبكةً متعددة المواقع تعتمد على ووردبريس)؛ فإذا أردتَ مشاركة المحتوى غير الخاص بقسمٍ معيّن، أو كانت «هوية» (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.
  24. تمهيد عمومًا، نستعمل حاويات Docker لمرة وحيدة فقط، وهي تعمل إلى أن ينتهي تنفيذ الأمر الموكل إليها. وافتراضيًا تكون البيانات المُنشَأة داخل الحاوية متاحةً فقط إلى الحاوية وقابلة للوصول عند تشغيل الحاوية فقط. حجوم Docker (أي Docker volumes) يمكن أن تُستخدَم لمشاركة الملفات بين النظام المضيف (host system) وحاوية Docker. لنقل –على سبيل المثال– أننا نريد استخدام صورة Nginx الرسمية ونحتفظ بسجل دائم لخادوم Nginx لكي نحلل معلوماته لاحقًا. وافتراضيًا، ستكتب صورة Niginx الرسمية سجلاتها إلى ‎/var/log/nginx داخل الحاوية، والتي لا نستطيع في الحالة العادية الوصول إلى ذاك الملف من النظام المضيف. سنشرح في هذا الدرس كيفية جعل البيانات الموجودة داخل الحاوية متاحةً للجهاز المضيف. المتطلبات المسبقة خادوم أوبنتو 16.04 (أو 14.04) مضبوطٌ كما في درس «الإعداد الابتدائي لخادوم أوبنتو 14.04»، بما في ذلك إعداد حساب مستخدم عادي لكنه يملك امتيازات الجذر (root) عبر الأداة sudo. برمجية Docker مثبّتة على حاسوبك إذا كنتَ جديدًا على Docker، فسلسلة «docker ecosystem» المنشورة على الأكاديمية تعطيك لمحةً عن طريقة استعمالها. الخطوة الأولى: الوصل الترابطي لحجم التخزين الأمر الآتي سيُنشِئ مجلدًا باسم nginxlogs في مجلد المنزل (Home) للمستخدم الحالي ويصله وصلًا ترابطيًا (bindmount) إلى ‎/var/log/nginx في الحاوية: docker run --name=nginx -d -v ~/nginxlogs:/var/log/nginx -p 5000:80 nginx لنأخذ دقيقةً من الزمن لتفحص الأمر بالتفصيل: ‎--name=nginx: تسمية الحاوية لكي نستطيع الإشارة إليها لاحقًا بسهولة. ‎-d: فصل العملية الحالية من الطرفية وتشغيلها في الخلفية، وإلا فسنرى مِحَث Nginx فارغ ولن نتمكن من استعمال جلسة الطرفية إلا بعد انتهاء تنفيذ Nginx. ‎-v ~/nginxlogs:/var/log/nginx: ضبط حجم الوصل الترابطي الذي يربِط بين مجلد ‎/var/log/nginx داخل حاوية Nginx إلى مجلد ‎‎~/nginxlogs في الجهاز المضيف. تَستعمِل Docker النقطتين الرأسيتين : لفصل مسار المضيف عن مسار الحاوية، ويجب ذكر مسار المضيف أولًا. ‎-p 5000:80: ضبط تمرير المنافذ؛ حيث تستمع خدمة Nginx داخل الحاوية إلى المنفذ 80 افتراضيًا، وهذا الخيار يؤدي إلى ربط المنفذ 80 في الحاوية إلى المنفذ 5000 في النظام المضيف. nginx: تحديد أنَّ هذه الحاوية يجب بناؤها على صورة Nginx، والتي تُنفِّذ الأمر nginx -g "deamon off"‎ لتشغيل Nginx. ‎-v /path:/path/in/container وصل المسار ‎/path في الجهاز المضيف إلى ‎/path/in/container في الحاوية. ‎-v path:/path/in/container إنشاء حجم باسم path دون علاقة بالمضيف. الخطوة الثانية: الوصول إلى البيانات من المضيف لدينا الآن خادوم Nginx يعمل داخل حاوية موجودة في جهازنا المحلي، ولدينا المنفذ 5000 الموجود في جهاز المضيف مرتبطٌ بالمنفذ 80 للحاوية. افتح عنوان خادوم الويب في متصفحك، مستخدمًا عنوان IP لجهازك المحلي ورقم المنفذ 5000، مثلًا: http://203.0.113.0:5000 ويجب أن تشاهد الرسالة الآتية: وإذا نظرنا الآن في مجلد ‎~/nginxlogs الموجود في المضيف، فسنرى الملف access.log المُنشَأ من حاوية nginx والذي تظهر فيه الطلبية التي أجريناها: cat ~/nginxlogs/access.log يجب أن يحتوي شيئًا شبيهًا بما يلي: 203.0.113.0 - - [11/Nov/2016:00:59:11 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36" "-" إذا أجريتَ أيّة تعديلات إلى مجلد ‎~/nginxlogs فيجب أن تراها من داخل الحاوية مباشرةً. الخلاصة شرحنا في هذا الدرس كيفية إنشاء حجم Docker لمشاركة المعلومات بين الحاوين والنظام المضيف، مما يفيدنا كثيرًا في بيئات التطوير، حيث من الضروري الوصول إلى السجلات لتحليلها. ترجمة -وبتصرّف- للمقال How To Share Data between the Docker Container and the Host لصاحبته Melissa Anderson
  25. هل جميع الزيارات التي تأتي موقعك هي زياراتٌ «جيدة»؟ إذا نظرتَ إلى الأمر بسطحية فقد تظن ذلك، لكن من الأفضل أن تُمعِن النظر في الزيارات التي تأتي إلى موقعك، فليست جميع الزيارات لها أغراضٌ حسنة! هنالك الكثير من الأسباب وراء رغبتك بالتحكم بالأماكن الجغرافية المسموح لها بالوصول إلى موقعك: إذا كانت عندك شركةٌ محلية ولم ترغب بإضاعة التّدفّق bandwith الخاص بخادومك على زياراتٍ لن تستفيد منها آتيةٍ من دولٍ أخرى. ففي النهاية لن يهتم بسلعك إلا السكان المحليون، ولن تلقي لها العائلات الموجودة في أقصاع الأرض بالًا. إذا كنت منزعجًا من التحليل الشهري لموقعك الذي يخرِّبه أولئك المزعجون الذين يلمؤن موقعك بالرسائل عديمة المعنى… إذا كانت شركتك تستهدف الزبائن الموجودين في منطقة جغرافية مُحدَّدة لأنَّ خدماتك مطلوبة جدًا في تلك المناطق دون وجود منافسة حقيقية من شركاتٍ أخرى. إذا كنتَ تخاف على موقعك من المخترقين من الدول الأخرى، وتريد أن تجعله أكثر أمانًا. لن يكون للموقع الجغرافي لزوار بعض المواقع أثرًا فيها، لكنه قد يُشكِّل عاملًا مهمًا في بعضها الآخر. فالسبب الوحيد لبنائك موقعًا (باستخدام ووردبريس) هو لكي تصل إلى الجمهور الصحيح، وهذا هو السبب وراء أهمية تحسين أداء موقعك، وأمانه. لكن إن كنت تظن أنَّ استهداف منطقة جغرافية معيّنة سيفيدك في إيصال رسالتك، فأكمل قراءة هذا الدرس. موارد تساعدك في تحديد المنطقة الجغرافية التي تأتي الزيارات منها يمكن أن تخبرك أداة Google Analytics من أين تأتي زيارات موقعك جغرافيًا. فكل مرة يزور أحدهم موقعك، فسيخبر عنوان IP للزائر برمجيةَ Google Analytics (أو أيّة برمجية أخرى تُسجِّل هذه المعلومات) عن المكان الجغرافي الذي يزور منه هذا الشخص موقعك. التصنيف عبر القارات: أو عبر الدول: أو عبر المدن: أظنَّ أنَّ الفكرة قد وصلتك. وصحيحٌ أنَّ أداة Google Analytics مفيدةٌ في معرفة من أين تأتي زيارات موقعك، لكن هذا الأداة لن تساعدك في زيادة الزيارات من مناطق جغرافية معيّنة إلى موقعك (أو بالعكس، تخفيض عدد الزيارات من مناطق أخرى). لكن إذا كنتَ تريد أن تُحدِّد من أين يأتي زوارك، فانظر إلى إحصائيات Google Analytics أولًا. بعد أن تأخذ فكرةً لا بأس بها عن أماكن قدوم زوار موقعك، فحان الوقت لكي تُقيّم إن كان هؤلاء الزوار مناسبين لموقعك أم لا. هنالك الكثير من الأدوات الخارجية التي يمكنك استخدامها لتحسين موقعك من ناحية توجيه المحتوى لمناطق جغرافية معيّنة. CDNs هي أداةٌ رائعة لكي تستعملها لتوجيه محتواك لجميع أنحاء العالم، لكن لا توجد خواديم لموقعك في جميع أنحاء العالم. يمكنك أيضًا استخدام Google Webmaster Tools لتحديد دولة لتكون هدفًا لموقعك. إذا كنتَ تتساءل في نفسك عن ووردبريس، فللأسف لا توجد ميزة مُضمَّنة في ووردبريس لمساعدة المطورين بتحسين المواقع التي يطورنها جغرافيًا. لكن هنالك عددٌ من الإضافات التي تساعدك في تلبية احتياجاتك بهذا الخصوص. الاستخدامات المختلفة لاستهداف الزوار جغرافيًا إذا أردتَ حجب الوصول إلى موقعك من دولة معيّنة، أو تستهدف أخرى، أو تُظهِر منتجات معيّنة اعتمادًا على الدولة التي يزور منها الزبون موقعك، أو حتى توفير محتوى مختلف بناءً على الموقع الجغرافي؛ فستحتاج حينئذٍ إلى إضافةٍ لكي تستطيع فعل ذلك. هذه بعض الإضافات التي قد تساعدك في استهداف زوار معيّين بناءً على عناوين IP الخاصة بهم. إضافات لتوفير محتوى مخصص اعتمادًا على الموقع الجغرافي هذه الإضافات مناسبة لتوفير محتوى اعتمادًا على الدولة. GeoTargeting Lite هذه هي النسخة المجانية من إضافة GeoTargeting Pro الشاملة والتي سنذكرها أدناه. الفرق الوحيد بين هذه الإضافة وإضافة GeoTargeting Pro هو أنَّ هذه الإضافة تسمح لك بجعل المحتوى يستهدف دولةً معيّنةً فقط، وإذا أردتَ ميزاتٍ متقدمة أو قدرةً على حجب الوصول لدول معيّنة، فعليك استخدام إضافة لهذا الغرض، أو شراء إضافة GeoTargeting Pro الكاملة. Geo Targeting add-on for Icegram إذا كنتَ تستخدم إضافة Icegram لوردبريس (الإضافة المدفوعة، وليست المجانية)، فربما تكون مهتمًا في معرفة أنَّ هنالك إمكانية تثبيت إضافة لتوجيه المحتوى جغرافيًا. فيمكن توسعة الإضافة الرائعة لإنشاء حملة تسويقية لكي تستهدف مناطق جغرافية معيّنة. إضافات لعرض إعلانات اعتمادًا على الموقع الجغرافي AdRotate إذا كنتَ تُخطِّط للكسب من وراء موقعك وكنت تريد تقديم إعلانات مختلفة اعتمادًا على الدولة أو الولاية أو المدينة، فانظر إلى إضافة AdRotate. تتوافر نسخة مجانية من هذه الإضافة في موقع ووردبريس، لكن النسخة المدفوعة توفِّر إمكانية توجيه الإعلانات إلى مناطق جغرافية. Advanced Ads هذه إضافةٌ أخرى للإعلانات التي توفر إمكانية توجيه الإعلانات إلى مناطق جغرافية معيّنة. وعلى الرغم من أنَّ هذه الإضافة مجانية، لكن عليك تثبيت إضافات geotargeting و geoblocking لها وتلك الإضافات غير مجانية. أما لو أردتَ تخصيص قواعد استهداف المناطق الجغرافية، فهذه هي الإضافة التي ستحتاج لها. إضافات الحجب حسب المناطق الجغرافية استعمل هذه الإضافات عندما تريد حجب دول أو مناطق كاملة من الوصول إلى موقعك. IP Geo Block جعل المحتوى يستهدف مناطق معيّنة يؤدي إلى توفير محتوى متنوع للزوار من مناطق مختلفة، أما الهدف من الحجب هو منع الوصول تمامًا من مناطق معيّنة، وهذا لا يعني منع الزوار فقط، وإنما حجب الأشخاص الذين يحاولون اختراق موقعك لأغراضٍ خبيثة. هذه الإضافة يمكنها أن تحجب الزوار عبر عناوين IP أو يمكن حجب دول كاملة من الوصول إلى موقعك عبر: صفحة تسجيل الدخول نموذج التعليقات جميع المداخل الموجودة في السند الخلفي (back-end) لموقعك. iQ Block Country إذا كنت مهتمًا بتقليل عدد زوار موقعك، فيمكنك استخدام هذه الإضافة، وهنالك عددٌ من الأسباب يدفعك لذلك: محاولة إصلاح مصدر المشكلة التي تخرِّب إحصائيات موقعك. توفير محتوى خاص للمستخدمين القادمين من دول أخرى. منع المخترقين من العثور على موقعك. إضافة شاملة لتوجيه المحتوى ولحجبه اعتمادًا على الموقع الجغرافي أما إذا أردتَ تحكمًا كاملًا بتوجيه محتواك أو إعادة توجهيه أو حجب الزوار بناءً على موقعهم الجغرافي، فانظر إلى الإضافة الآتية. GeoTargeting Pro هذه هي إضافةٌ مدفوعةٌ التي تساعد المواقع الكبيرة (أو شبكةً متعددة المواقع) التي تحتاج إلى توفير محتواها اعتمادًا على الموقع الجغرافي للزوار من كل الدول حول العالم. ستملك تحكمًا كاملًا بأنواع المحتوى الذي سيُخدِّم إلى مختلف الزوار وما هي الصفحات التي سيُعاد توجيه الزوار إليها. المحتوى القابل للتوجيه حسب الموقع الجغرافي يتضمن: منشورات المدونة، والصفحات الثابتة، وقوائم التنقل، والودجات… الخلاصة قد تجد أنَّ توجيه المحتوى أو حجبه اعتمادًا على الموقع الجغرافي مفيدٌ جدًا عندما تحاول الوصول إلى الجمهور المناسب لموقعك، وتُبعد الزوار غير المفيدين، وتُحسِّن من أداء موقعك، وتزيد من حمايته. إذا كنت تتساءل إن كان موقعك سيستفيد من توجيه المحتوى اعتمادًا على الموقع الجغرافي، وكان في ذهنك جمهورٌ يقطنُ في منطقةٍ معيّنة، فأنصحك بإلقاء نظرةٍ على الإضافات والأدوات السابقة. ترجمة -وبتصرّف- للمقال A Guide to Geotargeting and Geographic-Specific Content for WordPress لصاحبته Brenda Barron.
×
×
  • أضف...