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

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

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

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

  • Days Won

    15

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

87 Excellent

10 متابعين

آخر الزُوّار

4,206 زيارة للملف الشّخصي
  1. تمهيد يسمح لك استخدام حلقات for أو while في بايثون بأتمتة وتكرار المهام بطريقة فعّالة. لكن في بعض الأحيان، قد يتدخل عامل خارجي في طريقة تشغيل برنامجك، وعندما يحدث ذلك، فربما تريد من برنامجك الخروج تمامًا من حلقة التكرار، أو تجاوز جزء من الحلقة قبل إكمال تنفيذها، أو تجاهل هذا العامل الخارجي تمامًا. لذا يمكنك فعل ما سبق باستخدام تعابير break و continue و pass. التعبير break يوفِّر لك التعبير break القدرة على الخروج من حلقة التكرار عند حدوث عامل خارجي. حيث عليك وضع التعبير break في الشيفرة التي ستُنفَّذ في كل تكرار للحلقة، ويوضع عادةً ضمن تعبير if. ألقِ نظرةً إلى أحد الأمثلة الذي يستعمل التعبير break داخل حلقة for: number = 0 for number in range(10): number = number + 1 if number == 5: break # break here print('Number is ' + str(number)) print('Out of loop') هذا برنامجٌ صغيرٌ، هيّأنا في بدايته المتغير number بجعله يساوي الصفر، ثم بنينا حلقة تكرار for التي تعمل لطالما كانت قيمة المتغير number أصغر من 10. ثم قمنا بزيادة قيمة المتغير number داخل حلقة for بمقدار 1 في كل تكرار، وذلك في السطر number = number + 1. ثم كانت هنالك عبارة if التي تختبر إن كان المتغير number مساوٍ للرقم 5، وعند حدوث ذلك فسيُنفَّذ التعبير break للخروج من الحلقة. وتوجد داخل حلقة التكرار الدالة print()‎ التي تُنفَّذ في كل تكرار إلى أن نخرج من الحلقة عبر التعبير break، وذلك لأنَّها موجودة بعد التعبير break. لكي نتأكد أننا خرجنا من الحلقة، فوضعنا عبارة print()‎ أخيرة موجودة خارج حلقة for. سنرى الناتج الآتي عند تنفيذ البرنامج: Number is 1 Number is 2 Number is 3 Number is 4 Out of loop الناتج السابق يُظهِر أنَّه بمجرد أن أصبح العدد الصحيح number مساويًا للرقم 5، فسينتهي تنفيذ حلقة التكرار عبر التعبير break. الخلاصة: التعبير break يؤدي إلى الخروج من حلقة التكرار. التعبير continue التعبير continue يسمح لنا بتخطي جزء من حلقة التكرار عند حدوث عامل خارجي، لكن إكمال بقية الحلقة إلى نهايتها. بعبارةٍ أخرى: سينتقل تنفيذ البرنامج إلى أوّل حلقة التكرار عند تنفيذ التعبير continue. يجب وضع التعبير continue في الشيفرة التي ستُنفَّذ في كل تكرار للحلقة، ويوضع عادةً ضمن تعبير if. سنستخدم نفس البرنامج الذي استعملناها لشرح التعبير break أعلاه، لكننا سنستخدم التعبير continue بدلًا من break: number = 0 for number in range(10): number = number + 1 if number == 5: continue # continue here print('Number is ' + str(number)) print('Out of loop') الفرق بين استخدام التعبير continue بدلًا من break هو إكمال تنفيذ الشيفرة بغض النظر عن التوقف الذي حدث عندما كانت قيمة المتغير number مساويةً إلى الرقم 5. لننظر إلى الناتج: Number is 1 Number is 2 Number is 3 Number is 4 Number is 6 Number is 7 Number is 8 Number is 9 Number is 10 Out of loop نلاحظ أنَّ السطر الذي يجب أن يحتوي على Number is 5 ليس موجودًا في المخرجات، لكن سيُكمَل تنفيذ حلقة التكرار بعد هذه المرحلة مما يطبع الأرقام من 6 إلى 10 قبل إنهاء تنفيذ الحلقة. يمكنك استخدام التعبير continue لتفادي استخدام تعابير شرطية معقدة ومتشعّبة، أو لتحسين أداء البرنامج عن طريق تجاهل الحالات التي ستُرفَض نتائجها. الخلاصة: التعبير continue سيؤدي إلى جعل البرنامج يتجاهل تنفيذ حلقة التكرار عند تحقيق شرط معين، لكن بعدئذٍ سيُكمِل تنفيذ الحلقة كالمعتاد. التعبير pass التعبير pass يسمح لنا بالتعامل مع أحد الشروط دون إيقاف عمل حلقة التكرار بأي شكل، أي ستُنفَّذ جميع التعابير البرمجية الموجودة في حلقة التكرار ما لم تستعمل تعابير مثل break أو continue فيها. وكما هو الحال مع التعابير السابقة، يجب وضع التعبير pass في الشيفرة التي ستُنفَّذ في كل تكرار للحلقة، ويوضع عادةً ضمن تعبير if. سنستخدم نفس البرنامج الذي استعملناها لشرح التعبير break أو continue أعلاه، لكننا سنستخدم التعبير pass هذه المرة: number = 0 for number in range(10): number = number + 1 if number == 5: pass # pass here print('Number is ' + str(number)) print('Out of loop') التعبير pass الذي يقع بعد العبارة الشرطية if يخبر البرنامج أنَّ عليه إكمال تنفيذ الحلقة وتجاهل مساواة المتغير number للرقم 5. لنشغِّل البرنامج ولننظر إلى الناتج: Number is 1 Number is 2 Number is 3 Number is 4 Number is 5 Number is 6 Number is 7 Number is 8 Number is 9 Number is 10 Out of loop لاحظنا عند استخدامنا للتعبير pass في هذا البرنامج أنَّ البرنامج يعمل كما لو أننا لم نضع عبارة شرطية داخل حلقة التكرار؛ حيث يخبر التعبير pass البرنامج أن يكمل التنفيذ كما لو أنَّ الشرط لم يتحقق. يمكن أن تستفيد من التعبير pass عندما تكتب برنامجك لأوّل مرة أثناء تفكيرك بحلّ مشكلة ما عبر خوارزمية، لكن قبل أن تضع التفاصيل التقنية له. الخلاصة تسمح لك التعابير break و continue و pass باستعمال حلقات for و while بطريقةٍ أكثر كفاءة. ترجمة –وبتصرّف– للمقال How To Use Break, Continue, and Pass Statements when Working with Loops in Python 3لصاحبته Lisa Tagliaferri
  2. تمهيد نستفيد من البرامج الحاسوبية خيرَ استفادة في أتمتة المهام وإجراء المهام التكرارية لكيلا نحتاج إلى القيام بها يدويًا، وإحدى طرائق تكرار المهام المتشابهة هي استخدام حلقات التكرار، وسنشرح في درسنا هذا حلقة تكرار while. حلفة تكرار while تؤدي إلى تكرار تنفيذ قسم من الشيفرة بناءً على متغير منطقي (boolean)، وسيستمر تنفيذ هذه الشيفرة لطالما كانت نتيجة التعبير المستعمل معها تساوي true. يمكنك أن تتخيل أنَّ حلقة while هي عبارة شريطة تكرارية، فبعد انتهاء تنفيذ العبارة الشرطية if فيُستَكمَل تنفيذ بقية البرنامج، لكن مع حلقة while فسيعود تنفيذ البرنامج إلى بداية الحلقة بعد انتهاء تنفيذها إلى أن يصبح الشرط مساويًا للقيمة false. وعلى النقيض من حلقات for التي تُنفَّذ عدد معيّن من المرات، فسيستمر تنفيذ حلقات while اعتمادًا على شرطٍ معيّن، لذا لن تحتاج إلى عدد مرات تنفيذ الحلقة قبل إنشائها. حلقة while الشكل العام لحلقات while في لغة بايثون كالآتي: while [a condition is True]: [do something] سيستمر تنفيذ التعليمات البرمجية الموجودة داخل الحلقة إلى أن يصبح الشرط false. لنُنشِئ برنامجًا صغيرًا فيه حلقة while، ففي هذه البرنامج سنطلب من المستخدم إدخال كلمة مرور. وهنالك خياران أمام حلقة التكرار: - إما أن تكون كلمة المرور صحيحة، فعندها سينتهي تنفيذ حلقة while. - أو أن تكون كلمة المرور غير صحيحة، فعندها سيستمر تنفيذ حلقة التكرار. لنُنشِئ ملفًا باسم password.py في محررنا النصي المفضَّل، ولنبدأ بتهيئة المتغير paasword بإسناد سلسلة نصية فارغة إليه: password = '' نستخدم المتغير السابق للحصول على مدخلات المستخدم داخل حلقة التكرار while. علينا بعد ذلك إنشاء حلقة while مع تحديد ما هو الشرط الذي يجب تحقيقه: password = '' while password != 'password': أتبَعنا –في المثال السابق– الكلمة المحجوزة while بالمتغير password، ثم سنتحقق إذا كانت قيمة المتغير password تساوي السلسلة النصية 'password' (لا تنسَ أنَّ قيمة المتغير سنحصل عليها من مدخلات المستخدم)، يمكنك أن تختار أي سلسلة نصية تشاء لمقارنة مدخلات المستخدم بها. هذا يعني أنَّه لو أدخل المستخدم السلسلة النصية password فستتوقف حلقة التكرار وسيُكمَل تنفيذ البرنامج وستُنفَّذ أيّة شيفرات خارج الحلقة، لكن إذا أدخل المستخدم أيّة سلسلة نصية لا تساوي password فسيُكمَل تنفيذ الحلقة. علينا بعد ذلك إضافة الشيفرة المسؤولة عمّا يحدث داخل حلقة while: password = '' while password != 'password': print('What is the password?') password = input() نفَّذ البرنامج عبارة print داخل حلقة while والتي تسأل المستخدم عن كلمة مروره، ثم أسندنا قيمة مدخلات المستخدم (التي حصلنا عليها عبر الدالة input()‎) إلى المتغير password. سيتحقق البرنامج إذا كانت قيمة المتغير password تساوي السلسلة النصية 'password'، وإذا تحقق ذلك فسينتهي تنفيذ حلقة while. لنضف سطرًا آخر إلى البرنامج لنعرف ماذا يحدث إن أصبحت قيمة الشرط مساويةً إلى false: password = '' while password != 'password': print('What is the password?') password = input() print('Yes, the password is ' + password + '. You may enter.') لاحظ أنَّ آخر عبارة print()‎ موجودة خارج حلقة while، لذا عندما يُدخِل المستخدم الكلمة password عند سؤاله عن كلمة مروره، فستُطبَع آخر جملة والتي تقع خارج حلقة التكرار. لكن ماذا يحدث لو لم يدخل المستخدم الكلمة password قط؟ حيث لن يستمر تنفيذ البرنامج ولن يروا آخر عبارة print()‎ وسيستمر تنفيذ حلقة التكرار إلى ما لا نهاية! يستمر تنفيذ حلقة التكرار إلى ما لا نهاية إذا بقي تنفيذ البرنامج داخل حلقة تكرار دون الخروج منها. وإذا أردتَ الخروج من حلقة تكرار نهائية، فاضغط Ctrl+C في سطر الأوامر. احفظ البرنامج ثم شغِّله: python password.py سيُطلَب منك إدخال كلمة المرور، ويمكنك تجربة ما تشاء من الكلمات. هذا مثالٌ عن ناتج البرنامج: What is the password? hello What is the password? sammy What is the password? PASSWORD What is the password? password Yes, the password is password. You may enter. أبقِ في ذهنك أنَّ السلاسل النصية حساسة لحالة الأحرف إلا إذا استعملتَ دالةً من دوال النصوص لتحويل السلسلة النصية إلى حالة الأحرف الصغيرة (على سبيل المثال) قبل التحقق منها. مثال عن برنامج يستخدم حلقة while بعد أن تعلمنا المبدأ الأساسي لحلقة تكرار while، فلنُنشِئ لعبة تعمل على سطر الأوامر لتخمين الأرقام والتي تستعمل الحلقة while . نريد من الحاسوب أن يُنشِئ أرقامًا عشوائيةً لكي يحاول المستخدمون تخمينها، لذا علينا استيراد الوحدة random عبر استخدام العبارة import، وإذا لم تكن هذه الحزمة مألوفةً لك فيمكنك قراءة المزيد من المعلومات عن توليد الأرقام العشوائية في توثيق بايثون. لنُنشِئ بدايةً ملفًا باسم guess.py في محررك النصي المفضَّل: import random علينا الآن إسناد عدد صحيح عشوائي إلى المتغير number، ولنجعل مجاله من 1 إلى 25 (بما فيها تلك الأرقام) كيلا نجعل اللعبة صعبة جدًا. import random number = random.randint(1, 25) يمكننا الآن إنشاء حلقة while، وذلك بتهيئة متغير ثم كتابة الحلقة: import random number = random.randint(1, 25) number_of_guesses = 0 while number_of_guesses < 5: print('Guess a number between 1 and 25:') guess = input() guess = int(guess) number_of_guesses = number_of_guesses + 1 if guess == number: break هيئنا متغيرًا اسمه number_of_guesses قيمته 0، وسوف نزيد قيمته عند كل تكرار للحلقة لكي لا تصبح حلقتنا لا نهائية. ثم سنضيف تعبير while الذي يشترط ألّا تزيد قيمة المتغير number_of_guesses عن 5. وبعد المحاولة الخامسة سيُعاد المستخدم إلى سطر الأوامر، وإذا حاول المستخدم إدخال أيّ شيء غير رقمي فسيحصل على رسالة خطأ. أضفنا داخل حلقة while عبارة print()‎ لطلب إدخال رقم من المستخدم، ثم سنأخذ مدخلات المستخدم عبر الدالة input()‎ ونُسنِدَها إلى المتغير guess، ثم سنحوِّل المتغير guess من سلسلة نصية إلى عدد صحيح. وقبل انتهاء حلقة التكرار، فعلينا زيادة قيمة المتغير number_of_guesses بمقدار 1، لكيلا تُنفَّذ حلقة التكرار أكثر من 5 مرات. وفي النهاية، كتبنا عبارة if شرطية لنرى إذا كان المتغير guess الذي أدخله المستخدم مساوٍ للرقم الموجود في المتغير number الذي ولَّده الحاسوب، وإذا تحقق الشرط فسنستخدم عبارة break للخروج من الحلقة. أصبح البرنامج جاهزًا للاستخدام، ويمكننا تشغيله عبر تنفيذ الأمر: python guess.py صحيحٌ أنَّ البرنامج يعمل عملًا سليمًا، لكن المستخدم لن يعلم إذا كان تخمينه صحيحًا ويمكنه أن يخمِّن الرقم خمس مرات دون أن يعلم إذا كانت إحدى محاولاته صحيحة. هذا مثال عن مخرجات البرنامج: Guess a number between 1 and 25: 11 Guess a number between 1 and 25: 19 Guess a number between 1 and 25: 22 Guess a number between 1 and 25: 3 Guess a number between 1 and 25: 8 لنضف بعض العبارات الشرطية خارج حلقة التكرار لكي يحصل المستخدم على معلومات فيما إذا استطاعوا تخمين الرقم أم لا، وسنضيف هذه العبارات في نهاية الملف: import random number = random.randint(1, 25) number_of_guesses = 0 while number_of_guesses < 5: print('Guess a number between 1 and 25:') guess = input() guess = int(guess) number_of_guesses = number_of_guesses + 1 if guess == number: break if guess == number: print('You guessed the number in ' + str(number_of_guesses) + ' tries!') else: print('You did not guess the number. The number was ' + str(number)) في هذه المرحلة سيُخبِر البرنامجُ المستخدمَ إذا استطاعوا تخمين الرقم، لكن ذلك لن يحدث إلا بعد انتهاء حلقة التكرار وبعد انتهاء عدد مرات التخمين المسموحة. ولمساعد المستخدم قليلًا، فلنضف بعض العبارات الشرطية داخل حلقة while وتلك العبارات ستخبر المستخدم إذا كان تخمينه أعلى من الرقم أو أصغر منه، لكي يستطيعوا تخمين الرقم بنجاح، وسنضيف تلك العبارات الشرطية قبل السطر الذي يحتوي على if guess == number: import random number = random.randint(1, 25) number_of_guesses = 0 while number_of_guesses < 5: print('Guess a number between 1 and 25:') guess = input() guess = int(guess) number_of_guesses = number_of_guesses + 1 if guess < number: print('Your guess is too low') if guess > number: print('Your guess is too high') if guess == number: break if guess == number: print('You guessed the number in ' + str(number_of_guesses) + ' tries!') else: print('You did not guess the number. The number was ' + str(number)) وعندما نُشغِّل البرنامج مرةً أخرى بتنفيذ python guess.py، فيمكننا ملاحظة أنَّ المستخدم سيحصل على بعض المساعدة، فلو كان الرقم المولَّد عشوائيًا هو 12 وكان تخمين المستخدم 18، فسيُخبره البرنامج أنَّ الرقم الذي خمنه أكبر من الرقم العشوائي، وذلك لكي يستطيع تعديل تخمنيه وفقًا لذلك. هنالك الكثير من التحسينات التي يمكن إجراؤها على الشيفرة السابقة، مثل تضمين آلية لمعالجة الأخطاء التي تحدث عندما لا يُدخِل المستخدم عددًا صحيحًا، لكن كان غرضنا هو رؤية كيفية استخدام حلقة while في برنامج قصير ومفيد يعمل من سطر الأوامر. الخلاصة شرحنا في هذا الدرس كيف تعمل حلقات while في بايثون وكيفية إنشائها. حيث تستمر حلقات while بتنفيذ مجموعة من الأسطر البرمجية لطالما كان الشرط مساويًا للقيمة true. ترجمة –وبتصرّف– للمقال How To Construct While Loops in Python 3 لصاحبته Lisa Tagliaferri
  3. نحن نرسل معلوماتنا الشخصية في كل يوم عبر الإنترنت، ففي الساعة الماضية دفعتُ المستحقات على بطاقتي الائتمانية، واشتريتُ كتابًا، وحفظتُ نسخةً من عناوين منازل أصدقائي، وأرسلتُ بعض رسائل البريد الإلكتروني، واشتريتُ بعض الحاجيات. أصبحتْ مشاركة معلوماتنا شائعة جدًا في هذه الفترة، حتى أننا لم نعد نفكر قبل مشاركتها. هذا هو الغرض من 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
  4. تمهيد يبدو نوع البيانات tuple في بايثون كما يلي: coral = ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') tuple هي بنية بيانات تُمثِّل سلسلة مرتبة من العناصر غير القابلة للتبديل، وبالتالي لا يمكن تعديل القيم الموجودة فيها. يستعمل نوع البيانات tuple لتجميع البيانات، فكل عنصر أو قيمة داخل tuple تُشكِّل جزءًا منه. توضع القيم داخل نوع البيانات tuple بين قوسين ( ) ويُفصَل بينها بفاصلة ,، وتبدو القيم الفارغة كما يلي coral = ()‎، لكن إذا احتوى نوع البيانات tuple على قيم –حتى لو كانت قيمةً واحدةً فقط– فيجب وضع فاصلة فيه مثل coral = ('blue coral',). إذا استخدمنا الدالة print()‎ على tuple، فسنحصل على الناتج الآتي الذي يُبيّن أنَّ القيمة الناتجة ستوضع بين قوسين: print(coral) ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') عند التفكير بنوع tuple وغيره من بنى البيانات التي تُعبَر من أنوع «المجموعات» (collections)، فمن المفيد أن تضع ببالك مختلف المجموعات الموجودة في حاسوبك: تشكيلة الملفات الموجودة عندك، وقوائم التشغيل للموسيقى، والمفضلة الموجودة في متصفحك، ورسائل بريدك الإلكتروني، ومجموعة مقاطع الفيديو التي تستطيع الوصول إليها من التلفاز، والكثير. نوع tuple شبيه بالقوائم (lists)، لكن القيم الموجودة فيه لا يمكن تعديلها، وبسبب ذلك، فأنت تخبر الآخرين أنَّك لا تريد إجراء أيّة تعديلات على هذه السلسلة من القيم عندما تستعمل tuple في شيفرتك. إضافةً إلى ما سبق، ولعدم القدرة على تعديل القيم، فسيكون أداء برنامجك أفضل، حيث ستُنفَّذ الشيفرة بشكل أسرع إذا استعملتَ tuple بدلًا من القوائم (lists). فهرسة نوع البيانات tuple يمكن الوصول إلى كل عنصر من عناصر tuple بمفرده لأنَّه سلسلة مرتبة من العناصر، وذلك عبر الفهرسة. وكل عنصر يرتبط برقم فهرس، الذي هو عدد صحيح يبدأ من الفهرس 0. لمثال coral السابق، ستبدو الفهارس والقيم المرتبطة بها كالآتي: ‘blue coral’ ‘staghorn coral’ ‘pillar coral’ ‘elkhorn coral’ 0 1 2 3 العنصر الأول الذي يُمثِّل السلسلة النصية 'blue coral' تبدأ بالفهرس 0، وتنتهي القائمة بالفهرس رقم 3 المرتبط بالقيمة 'elkhorn coral'. ولأن كل عنصر من عناصر tuple له رقم فهرس مرتبط به، فسنتمكن من الوصول إلى عناصره فرادى. يمكننا الآن الوصول إلى عنصر معيّن في tuple عبر استخدام رقم الفهرس المرتبط به. print(coral[2]) pillar coral تتراوح قيم الفهارس في المتغير coral من 0 إلى 3 كما هو ظاهر في الجدول السابق، لذا يمكننا استدعاء العناصر الموجودة فيه فرادى كما يلي: coral[0] coral[1] coral[2] coral[3] إذا حاولنا استدعاء المتغير coral مع رقم فهرس أكبر من 3، فستظهر رسالة خطأ تشير إلى أنَّ الفهرس خارج المجال: print(coral[22]) IndexError: tuple index out of range إضافةً إلى أرقام الفهارس الموجبة، يمكننا أيضًا الوصول إلى الفهارس باستخدام رقم فهرس سالب، وذلك بالعد بدءًا من نهاية قائمة العناصر وسيرتبط آخر عنصر بالفهرس ‎-1، وهذا مفيدٌ جدًا إذا كان لديك متغير من النوع tuple وكان يحتوي عناصر كثيرة وأردتَ الوصول إلى أحد عناصره انطلاقًا من النهاية. ففي مثالنا السابق عن coral، إذا أردنا استخدام الفهارس السالبة فالناتج كالآتي: ‘elkhorn coral’ ‘pillar coral’ ‘staghorn coral’ ‘blue coral’ -1 -2 -3 -4 إذا أردنا طباعة العنصر 'blue coral' باستخدام الفهارس السالبة، فستبدو التعليمة كما يلي: print(coral[-4]) blue coral يمكننا إضافة العناصر النصية الموجودة في tuple إلى السلاسل النصية الأخرى باستخدام المعامل +: print('This reef is made up of ' + coral[1]) This reef is made up of staghorn coral استطعنا في المثال السابق إضافة عنصر موجود في الفهرس 1 مع السلسلة النصية 'This reef is made up of '، ويمكننا أيضًا استخدام المعامل + لإضافة بنيتَي tuple معًا. الخلاصة: يمكننا الوصول إلى كل عنصر من عناصر tuple على حدة باستخدام أرقام الفهارس (الموجبة أو السالبة) المرتبطة بها. تقطيع قيم tuple يمكننا استخدام الفهارس للوصول إلى عدِّة عناصر من tuple، أما التقطيع فيسمح لنا بالوصول إلى عدِّة قيم عبر إنشاء مجال من أرقام الفهارس المفصولة بنقطتين رأسيتين [x:y]. لنقل أننا نريد عرض العناصر الموجودة في وسط المتغير coral، يمكننا فعل ذلك بإنشاء قطعة جديدة: print(coral[1:3]) ('staghorn coral', 'pillar coral') عند إنشاء قطعة جديدة –كما في المثال السابق– فيمثِّل أوّل رقم مكان بدأ القطعة (متضمنةً هذا الفهرس)، ورقم الفهرس الثاني هو مكان نهاية القطعة (دون تضمين هذا الفهرس بالقطعة)، وهذا هو السبب وراء عرض المثال السابق للقيم المرتبطة بالعناصر الموجودة في الفهرسين 1 و 2. إذا أردتَ تضمين إحدى نهايتَي القائمة، فيمكنك حذف أحد الأرقام في التعبير tuple[x:y]، فمثلًا، لنقل أننا نريد عرض أوّل ثلاثة عناصر من coral، والتي هي 'blue coral' و 'staghorn coral' و 'pillar coral'، فيمكننا فعل ذلك كالآتي: print(coral[:3]) ('blue coral', 'staghorn coral', 'pillar coral') المثال السابق عرض العناصر من بداية القائمة وتوقف قبل العنصر ذي الفهرس 3. لتضمين كل العناصر الموجودة في نهاية tuple، فيمكننا عكس التعبير السابق: print(coral[1:]) ('staghorn coral', 'pillar coral', 'elkhorn coral') يمكننا استخدام الفهارس السالبة أيضًا عند التقطيع، كما فعلنا مع أرقام الفهارس الموجبة: print(coral[-3:-1]) print(coral[-2:]) ('staghorn coral', 'pillar coral') ('pillar coral', 'elkhorn coral') هنالك معاملٌ إضافيٌ يمكننا استعماله ويسمى «الخطوة»، ويُشير إلى عدد العناصر التي يجب تجاوزها بعد الحصول على أوّل عنصر من القائمة. حذفنا في جميع أمثلتنا السابقة معامل الخطوة، حيث القيمة الافتراضية له في بايثون هي 1، لذا سنحصل على جميع العناصر الموجودة بين الفهرسَين المذكورين. شكل هذا التعبير العام هو tuple[x:y:z]، إذ يُشير المعامل z إلى الخطوة. لنُنشِئ قائمةً أكبر، ثم نقسِّمها، ونعطيها القيمة 2 كخطوة: numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) print(numbers[1:11:2]) (1, 3, 5, 7, 9) التعبير numbers[1:11:2] سيطبع القيم الموجودة بين رقمين الفهرسين 1 (بما في ذلك العنصر المرتبط بالفهرس 1) و 11 (دون تضمين ذلك العنصر)، ومن ثم ستخبر قيمةُ الخطوة 2 البرنامجَ أنَّ يتخطى عنصرًا بين كل عنصرين. يمكننا حذف أوّل معاملين واستخدام معامل الخطوة بمفرده بتعبيرٍ برمجيٍ من الشكل tuple[::z]: print(numbers[::3]) (0, 3, 6, 9, 12) طبعنا في المثال السابق عناصر numbers بعد ضبط قيمة الخطوة إلى 3، وبالتالي سيتم تخطي عنصرين. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 الخلاصة: تقطيع tuples باستخدام أرقام الفهارس الموجبة والسالبة واستعمال معامل الخطوة يسمح لنا بالتحكم بالناتج الذي نريد عرضه. إضافة بنى tuple إلى بعضها يمكن أن نُضيف بنى tuple إلى بعضها أو أن «نضربها» (multiply)، تتم عملية الإضافة باستخدام المعامل +، أما عملية الضرب فباستخدام المعامل *. يمكن أن يُستخدَم المعامل + لإضافة بنيتَي tuple أو أكثر إلى بعضها بعضًا. يمكننا إسناد القيم الموجودة في بنيتَي tuple إلى بنية جديدة: coral = ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') kelp = ('wakame', 'alaria', 'deep-sea tangle', 'macrocystis') coral_kelp = (coral + kelp) print(coral_kelp) الناتج: ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral', 'wakame', 'alaria', 'deep-sea tangle', 'macrocystis') وصحيحٌ أنَّ المعامل + يمكنه إضافة بنى tuple إلى بعضها، لكن يمكن أن يستعمل لإنشاء بنية tuple جديدة ناتجة عن جمع بنى أخرى، لكن لا يمكنه تعديل بنية tuple موجودة مسبقًا. أما المعامل * فيمكن استخدامه لضرب بنى tuple، فربما تريد إنشاء نسخ من الملفات الموجودة في أحد المجلدات إلى الخادوم أو مشاركة قائمة بالمقطوعات الموسيقية التي تحبها مع أصدقائك، ففي هذه الحالات سترغب بمضاعفة مجموعات من البيانات (أو «ضربها»). لنضرب البنية coral بالرقم 2 والبنية kelp بالرقم 3، ثم نسندها إلى بنى tuple جديدة: multiplied_coral = coral * 2 multiplied_kelp = kelp * 3 print(multiplied_coral) print(multiplied_kelp) الناتج: ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral', 'blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') ('wakame', 'alaria', 'deep-sea tangle', 'macrocystis', 'wakame', 'alaria', 'deep-sea tangle', 'macrocystis', 'wakame', 'alaria', 'deep-sea tangle', 'macrocystis') يمكننا باستخدام المعامل * أن نُكرِّر (أو نُضاعِف) بنى tuple بأي عدد من المرات نشاء، مما سينُشِئ بنى tuple جديدة اعتمادًا على محتوى البنى الأصلية. الخلاصة هي أنَّ بنى tuple يمكن إضافتها إلى بعضها أو ضربها لتشكيل بنى tuple جديدة عبر استخدام المعاملَين + و *. دوال التعامل مع tuple هنالك دوال مُضمَّنة في لغة بايثون للتعامل مع بنى tuple، لننظر إلى بعضها. len()‎ وكما في السلاسل النصية والقوائم، يمكننا حساب طول (أو عدد عناصر) بنية tuple باستخدام الدالة len()‎ حيث نُمرِّر إليها بنية tuple كمعامل (parameter)، كما يلي: len(coral) هذه الدالة مفيدة إذا أردنا أن نَضمَن أنَّ لبنية tuple عدد عناصر معيّن، فمثلًا يمكننا الاستفادة من ذلك بمقارنة بنيتين مع بعضهما. إذا أردنا طباعة عدد عناصر kelp و numbers، فسيظهر الناتج الآتي: print(len(kelp)) print(len(numbers)) الناتج: 4 13 الناتج أعلاه يشير إلى أنَّ للبنية kelp أربعة عناصر: kelp = ('wakame', 'alaria', 'deep-sea tangle', 'macrocystis') أما البنية numbers فتملك ثلاثة عشر عنصرًا: numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) وصحيحٌ أنَّ هذه الأمثلة عناصرها قليلة نسبيًا، إلا أنَّ الدالة len()‎ تستطيع أن تخبرنا بعدد عناصر بنى tuple الكبيرة. الدالتان max()‎ و min()‎ عندما نتعامل مع بنى tuple مكوَّنة من عناصر رقمية (بما فيها الأعداد الصحيحة والأرقام ذات الفاصلة العشرية)، فيمكننا استخدام الدالتين max()‎ و min()‎ للعثور على أكبر وأصغر قيمة موجودة في بنية tuple معيّنة. تسمح لنا هاتان الدالتان باستخراج معلومات تخص البيانات القابلة للإحصاء، مثل نتائج الامتحانات أو درجات الحرارة أو أسعار المنتجات …إلخ. لننظر إلى بنية tuple مكونة من أعداد عشرية: more_numbers = (11.13, 34.87, 95.59, 82.49, 42.73, 11.12, 95.57) للحصول على القيمة العظمى من بين القيم الآتية فعلينا تمرير بنية tuple إلى الدالة max()‎ كما في max(more_numbers)، وسنستخدم الدالة print()‎ لعرض الناتج: print(max(more_numbers)) 95.59 أعادت الدالة max()‎ أعلى قيمة في بنية more_numbers. وبشكلٍ شبيهٍ بما سبق نستخدم الدالة min()‎: print(min(more_numbers)) 11.12 أُعيدَ هنا أصغر رقم عشري موجودة في البنية. يمكن الاستفادة من الدالتين max()‎ و min()‎ كثيرًا للتعامل مع بنى tuple التي تحتوي الكثير من القيم. كيف تختلف بنى tuple عن القوائم (list) الفرق الرئيسي بين tuple و list هو عدم القدرة على تعديلها، وهذا يعني أنَّنا لا نستطيع إضافة أو حذف أو استبدال العناصر داخل بنية tuple. لكن يمكننا إضافة بنيتَي tuple أو أكثر إلى بعضها بعضًا لتشكيل بنية جديدة كما رأينا في أحد الأقسام السابقة. لتكن لدينا البنية coral الآتية: coral = ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') لنقل أننا نريد استبدال العنصر 'blue coral' ووضع العنصر 'black coral' بدلًا منه. فلو حاولنا تغيير بنية tuple بنفس الطريقة التي نُعدِّل فيها القوائم بكتابة: coral[0] = 'black coral' فستظهر رسالة خطأ كالآتية: TypeError: 'tuple' object does not support item assignment وذلك بسبب عدم إمكانية تعديل بنى tuple. إذا أنشأنا بنية tuple ثم قررنا أنَّ ما نحتاج له هو بنية list، فيمكننا تحويلها إلى قائمة list، وذلك بالدالة list()‎: list(coral) أصبحت بنية coral قائمةً الآن: coral = ['blue coral', 'staghorn coral', 'pillar coral'] يمكننا أن نلاحظ أنَّ بنية tuple تحوَّلتَ إلى قائمة list لأنَّ الأقواس المحيطة بالقيم أصبح مربعة الشكل. وبشكلٍ شبيهٍ بما سبق، نستطيع تحويل القوائم من النوع list إلى tuple باستخدام الدالة tuple()‎. الخلاصة نوع البيانات tuple هو مجموعةٌ من البيانات المتسلسلة التي لا يمكن تعديلها، ويوفِّر تحسينًا في أداء برامجك لأنه أسرع معالجةً من القوائم في بايثون. وعندما يراجع الآخرون شيفرتك فسيعلمون من استخدامك لبنى tuple أنك لا تريد تعديل هذه القيم. شرحنا في هذا الدرس الميزات الأساسية لبنى tuple بما في ذلك الفهارس وتقطيعها وتجميعها، وعرضنا بعض الدوال المُضمَّنة المتوافرة لهذا النوع من البيانات. ترجمة –وبتصرّف– للمقال Understanding Tuples in Python 3لصاحبته Lisa Tagliaferri
  5. تعرّض موقعك للاختراق هو أحد أكثر التجارب إحباطًا والتي ستواجهك أثناء عملك كمدير أحد المواقع؛ ولسوء الحظ، لن تغني عنك تعزيزاتك الأمنية شيئًا، إذ يُختَرَق يوميًا حوالي 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
  6. يتطلب تطبيق (وفهم) هذا الدرس درايةً واسعةً بلغة JavaScript وتقنيات CSS3، أي أنه ليس لمن لا خبرة له معهما. أرجو أن تتطلع على الدروس المتاحة هنا في الأكاديمية، وعلى كتاب تعلم JavaScript.
  7. تحدثنا في الجزء السابق عن أهمية إعادة توجيه العناوين القديمة للصفحات إلى عناوينها الجديدة، وسنكمل شرح كيفية فعل ذلك في ووردبريس في هذا الدرس. أولًا: نقل موقع ووردبريس قديم إلى مضيفٍ جديد هذه الحالة غير معقدة أبدًا، فلو كنتَ تنقل موقع ووردبريس إلى مضيفٍ جديد دون تغيير بنية الروابط الدائمة لصفحات الموقع، فلا توجد اختلافات في الروابط الدائمة مما لا يستدعي استخدام إعادة توجيه عبر الرمز 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
  8. تمهيد المُحرِّر التدفقي 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.
  9. يوفّر الكائن 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.
  10. سنستعمل في هذا الدرس تحريكات 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
  11. وجدتُ العمل كمطوّر على ووردبريس قليل المتاعب، فإنتاج مواقع وِب جميلة هو خدمةٌ رائعةٌ نقدمها للآخرين وأمرٌ لا يؤدي إلى كارثة إذا لم تتقنه تمامًا بادئ الأمر؛ حيث لا يُشابه العمل طيارا حربيا أو في حرس الحدود! لكن مع ذلك، هنالك استثناءات: ففي بعض الأحيان سبَّب لي عملي مشاكل وكان شعوري بالذنب وتأنيب الضمير سيؤدي بي إلى ترك عملي. ولاحظتُ أنَّ أسباب ذلك هي نفسها، ولقد وضعتها في رسمٍ توضيحي، لذا سأترك المخطط ليتحدث عنها: رغبتُ بترك عملي مرات عدّة عندما أثرّتُ على تقييم موقع العميل في محركات البحث؛ فأخطاء 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.
  12. عندما يتعلق الأمر ببناء نوع معيّنٍ من تطبيقات الويب، فأوّل ما يخطر ببالي هو استخدام ووردبريس! ومن بين المشاريع البرمجية التي عملتُ عليها في السنتين الماضيتين، تطلّب نصفها - على الأقل - إمكانية إدارة حسابات المستخدمين، وهو ما يعني عادةً السماح للمستخدمين بإنشاء حساباتهم، بإدخال بعض البيانات في حقلَي الاسم وعنوان البريد الإلكتروني، ومن ثم ستُرسَل إليهم رسالةٌ بريديةٌ لتفعيل حسابهم بعد إرسال النموذج. توفِّر ووردبريس طريقةً سهلةً لإدارة المستخدمين عبر لوحة التحكم، وإذا كان موقعك تعليميًا أو كنتَ تستعمله مدونةً لك، فلا حاجة إلى إنشاء آليةٍ أخرى لإدارة المستخدمين، لكن إذا كنتَ تبني تطبيقًا متكاملًا، فهنالك طرائق أخرى للتعامل مع حسابات المستخدمين. لنقل مثلًا أنَّ المُصمِّم قد وضع طريقةً معينةً لإدارة الحسابات في الموقع، وإذا أجبرتَ المستخدمين على استخدام لوحة التحكم فستتسبَّب بتخريب تجربة المستخدم، أليس كذلك؟ هنالك طرائقٌ أفضل لتسجيل حسابات المستخدمين وإدارتها بدلًا من استخدام لوحة التحكم التابعة لووردبريس، وسنناقش في هذا الدرس كيفية إنشاء حسابات المستخدمين برمجيًا، وسنلقي نظرةً على النقاط الآتية: الحصول على معلومات المستخدم بصيغةٍ معيّنة، التعرّف على المعلومات اللازمة لإنشاء حساب مستخدم، إنشاء الحساب، التأكد أنَّ شيفرتنا تخضع لمعايير كتابة الشفرات القياسية، وأنها سهلة القراءة والصيانة. لا تقلق، لن يكون هذا الدرس طويلا ومملًّا، أعدك بذلك! إنشاء حسابات مستخدمي ووردبريس يحتاج أحدنا إلى المعلومات الآتية –على الأقل– لإنشاء حساب مستخدم جديد: اسم المستخدم كلمة المرور عنوان البريد الإلكتروني عندما يتعلق الأمر بإنشاء اسم المستخدم، فأنا أفضِّل استخدام البريد الإلكتروني اسمًا للمستخدم لأنني أضمن أنَّه فريد ولن يتكرر مرةً أخرى؛ ولهذا سنستخدمه في هذا الدرس؛ وسنتحدث عن توليد كلمة المرور لاحقًا. أضف إلى ذلك ضرورة افتراض أنَّ البيانات ستأتيك بأيّ صيغة: فربما تكون بصيغة 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
  13. تمهيد الدالة str.format()‎ المتوافرة للسلاسل النصية تسمح لك باستبدال المتغيرات وتنسيق القيم. مما يمنحك القدرة على تجميع العناصر مع بعضها عبر إدخالها في مواضع معينة. سيشرح لك هذا الدرس أشهر الاستخدامات لآلية تنسيق السلاسل النصية في بايثون، والتي ستساعدك في جعل شيفرتك وبرنامجك أسهل قراءةً واستخدامًا. استخدام «المُنسِّقات» تعمل المُنسِّقات (formatters) بوضع حقول قابلة للاستبدال تُعرَّف عبر وضع قوسين معقوفين {} في السلسلة النصية ثم استدعاء الدالة str.format()‎، إذ ستُمرَّر القيمة التي تريد وضعها ضمن السلسلة النصية إلى الدالة format()‎ وستوضع هذه القيمة في نفس مكان الحقل القابل للاستبدال الموجود في السلسلة الأصلية عندما تُشغِّل برنامجك. لنطبع سلسلةً نصيةً تستخدم «مُنسِّقًا» (formatter): print("Sammy has {} balloons.".format(5)) الناتج: Sammy has 5 balloons. أنشأنا في المثال السابق سلسلةً نصيةً تحتوي على قوسين معقوفين: "Sammy has {} balloons." ثم أضفنا الدالة str.format()‎ ومررنا إليها القيمة الرقمية 5 وهذا يعني أنَّ القيمة 5 ستوضع مكان القوسين المعقوفين: Sammy has 5 balloons. يمكننا أيضًا إسناد السلسلة النصية الأصلية التي تحتوي مُنسِّقًا إلى متغير: open_string = "Sammy loves {}." print(open_string.format("open source")) الناتج: Sammy loves open source. أضفنا في المثال السابق السلسلة النصية "open source" إلى سلسلةٍ نصيةٍ أكبر باستبدالها للقوسين المعقوفين الموجودَين في السلسلة الأصلية. تسمح لك المُنسِّقات في بايثون باستخدام الأقواس المعقوفة لحجز أماكن للقيم التي ستمررها مستقبلًا عبر الدالة str.format()‎. استخدام المُنسِّقات لحجز أكثر من مكان يمكنك استخدام أكثر من زوج من الأقواس المعقوفة عند استعمال المُنسِّقات؛ فيمكنك أن تضيف سلسلةً نصيةً أخرى إلى المثال السابق وذلك بإضافة زوج آخر من الأقواس المعقوفة وتمرير قيمة ثانية إلى الدالة كما يلي: new_open_string = "Sammy loves {} {}." # {} مكانين محجوزين عبر print(new_open_string.format("open-source", "software")) # تمرير قيمتين إلى الدالة مفصولٌ بينهما بفاصلة الناتج: Sammy loves open-source software. أضفنا زوجًا آخر من الأقواس المعقوفة إلى السلسلة النصية للسماح بوضع قيمة ثانية، ثم مررنا سلسلتين نصيتين إلى الدالة str.format()‎ مفصولٌ بينهما بفاصلة. سنضيف عمليات استبدال أخرى عبر اتباع نفس الآلية التي شرحناها أعلاه: sammy_string = "Sammy loves {} {}, and has {} {}." print(sammy_string.format("open-source", "software", 5, "balloons")) الناتج: Sammy loves open-source software, and has 5 balloons. إعادة ترتيب المنسقات عبر المعاملات الموضعية عندما نترك الأقواس المعقوفة دون معاملات (parameters) ممررة إليها، فستضع بايثون القيم المُمرَّرة إلى الدالة str.format()‎ بالترتيب. هذا تعبيرٌ فيه زوجين من الأقواس المعقوفة يوضع مكانهما سلسلتان نصيتان شبيهٌ بما رأيناه سابقًا في هذا الدرس: print("Sammy the {} has a pet {}!".format("shark", "pilot fish")) الناتج: Sammy the shark has a pet pilot fish! اُستبدِل أوّل زوجٍ من الأقواس المعقوفة ووضعت مكانه القيمة "shark"، ووضعت القيمة "pilot fish" مكان الزوج الثاني من الأقواس. القيم التي مررناها إلى الدالة str.format()‎ كانت بهذا الترتيب: ("shark", "pilot fish") إن سبق لك دراسة أنواع البيانات المُختلفة على بايثون فقد تلاحظ أنَّ القيمة السابقة هي من النوع tuple، ويمكن الوصول إلى كل قيمة موجودة فيها عبر فهرسٍ رقميٍ تابعٍ لها، والذي يبدأ من الفهرس 0. يمكننا تمرير أرقام الفهارس إلى داخل القوسين المعقوفين: print("Sammy the {0} has a pet {1}!".format("shark", "pilot fish")) سنحصل بعد تنفيذ المثال السابق على نفس الناتج التي ظهر دون تحديد أرقام الفهارس يدويًا، وذلك لأننا استدعينا القيم بالترتيب: Sammy the shark has a pet pilot fish! لكن إن عكسنا أرقام الفهارس في معاملات الأقواس المعقوفة فسنتمكن من عكس ترتيب القيم المُمرَّرة إلى السلسلة النصية الأصلية: print("Sammy the {1} has a pet {0}!".format("shark", "pilot fish")) الناتج: Sammy the pilot fish has a pet shark! لكن إن حاولت استخدام الفهرس ذي الرقم 2 ولم تكن لديك إلا قيمتين موجودتين في الفهرسين 0 و 1، فأنت تستدعي قيمةً خارج المجال المسموح، ولهذا السبب ستظهر رسالة خطأ: print("Sammy the {2} has a pet {1}!".format("shark", "pilot fish")) الناتج: IndexError: tuple index out of range تُشير رسالة الخطأ إلى وجود قيمتين فقط ومكانهما هو 0 و1، لذا كان الفهرس 2 غير مرتبطٍ بقيمةٍ وكان خارج المجال المسموح. لنضف الآن مكانين محجوزين إلى السلسلة النصية ولنمرر بضع قيم إلى الدالة str.format()‎ لكي نفهم آلية إعادة الترتيب فهمًا تامًا. هذه هي السلسلة النصية الجديدة التي فيها أربعة أزواج من الأقواس المعقوفة: print("Sammy is a {}, {}, and {} {}!".format("happy", "smiling", "blue", "shark")) الناتج: Sammy is a happy, smiling and blue shark! ستوضع القيم المُمرَّرة إلى الدالة str.format()‎ بنفس ترتيب ورودها في حال لم نستعمل المعاملات داخل الأقواس المعقوفة. تملك السلاسل النصية المُمرَّرة إلى الدالة str.format()‎ الفهارس الآتية المرتبطة بها: “happy” “smiling” “blue” “shark” 0 1 2 3 لنستخدم الآن أرقام الفهارس لتغيير ترتيب ظهور القيم المرتبطة بها في السلسلة النصية: print("Sammy is a {3}, {2}, and {1} {0}!".format("happy", "smiling", "blue", "shark")) الناتج: Sammy is a shark, blue, and smiling happy! ولمّا كنّا قد بدأنا بالفهرس ذي الرقم 3، فستظهر القيمة "shark" أولًا. أي أنَّ وضع رقم الفهرس بين القوسين كمعامل سيؤدي إلى تغيير ترتيب ظهور القيم في السلسلة النصية الأصلية. نستطيع -بالإضافة إلى المعاملات الموضعية الرقمية- أن نربط بين القيم وبين كلمات محجوزة مخصصة ومن ثم نستدعيها عبر وضع الكلمة المحجوزة بين القوسين المعقوفين كما يلي: print("Sammy the {0} {1} a {pr}.".format("shark", "made", pr = "pull request")) الناتج: Sammy the shark made a pull request. أظهر المثال السابق استخدام كلمة محجوزة كوسيط بالإضافة إلى المعاملات الموضعية، يمكنك استخدام الكلمة المحجوزة pr كوسيط بالإضافة إلى أرقام الفهارس، وتستطيع أيضًا إعادة ترتيب تلك الوسائط كيفما شئت: print("Sammy the {pr} {1} a {0}.".format("shark", "made", pr = "pull request")) الناتج: Sammy the pull request made a shark. استخدام المعاملات الموضعية والكلمات المحجوزة سيمنحنا تحكمًا أكبر بكيفية معالجة السلسلة النصية الأصلية عبر إعادة ترتيب القيم المُمرَّرة إليها. تحديد نوع القيمة يمكنك وضع معاملات أخرى ضمن القوسين المعقوفين، سنستخدم الصيغة الآتية {field_name:conversion} حيث field_name هو الفهرس الرقمي للوسيط الممرَّر إلى الدالة str.format()‎ والذي شرحناه تفصيليًا في القسم السابق، و conversion هو الرمز المستعمل للتحويل إلى نوع البيانات الذي تريده. «رمز التحويل» يعني رمزًا من حرفٍ وحيد الذي تستخدمه بايثون لمعرفة نوع القيمة المُراد «تنسيقها». الرموز التي سنستخدمها في أمثلتنا هي s للسلاسل النصية و d لإظهار الأرقام بنظام العد العشري (ذي الأساس 10) و f لإظهار الأعداد ذات الفاصلة. يمكنك قراءة المزيد من التفاصيل عن رموز التنسيق في بايثون 3 (وغير ذلك من المواضيع المرتبطة بهذا المجال) في التوثيق الرسمي. لننظر إلى مثالٍ نُمرِّر فيه رقمًا صحيحًا عبر الدالة format()‎ لكننا نريد إظهاره كعددٍ ذي فاصلة عبر رمز التحويل f: print("Sammy ate {0:f} percent of a {1}!".format(75, "pizza")) الناتج: Sammy ate 75.000000 percent of a pizza! وضعت القيمة -مكان أوّل ورود للصيغة {field_name:conversion}- كعددٍ ذي فاصلة، أما ثاني ورود للقوسين المعقوفين فكان بالصيغة {field_name}. لاحظ في المثال السابق وجود عدد كبير من الأرقام الظاهرة بعد الفاصلة العشرية، لكنك تستطيع تقليل عددها. فعندما نستخدم الرمز f للقيم ذات الفاصلة نستطيع أيضًا تحديد دقة القيمة الناتجة بتضمين رمز النقطة . متبوعًا بعدد الأرقام بعد الفاصلة التي نود عرضها. حتى لو أكل سامي 75.765367% من قطعة البيتزا فلن نحتاج إلى هذا القدر الكبير من الدقة، إذ يمكننا مثلًا أن نجعل عدد المنازل العشرية ثلاث منازل بعد الفاصلة بوضعنا ‎.3 قبل رمز التحويل f: print("Sammy ate {0:.3f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 75.765 percent of a pizza! أما إذا أردنا عرض منزلة عشرية وحيدة، فيمكننا إعادة كتابة السلسلة السابقة كالآتي: print("Sammy ate {0:.1f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 75.8 percent of a pizza! لاحظ كيف أدى تعديل دقة الأرقام العشرية إلى تقريب الرقم (وفق قواعد التقريب الاعتيادية). وصحيحٌ أننا عرضنا رقمًا دون منازل عشرية كعددٍ ذي فاصلة، إلا أننا إذا حاولنا تحويل عدد عشري إلى عدد صحيح باستخدام رمز التحويل d فسنحصل على خطأ: print("Sammy ate {0:.d} percent of a pizza!".format(75.765367)) الناتج: ValueError: Unknown format code 'd' for object of type 'float' إذا لم ترغب بعرض أيّة منازل عشرية، فيمكنك كتابة تعبير كالآتي: print("Sammy ate {0:.0f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 76 percent of a pizza! لن يؤدي ما سبق إلى تحويل العدد العشري إلى عددٍ صحيح، وإنما سيؤدي إلى تقليل عدد المنازل العشرية الظاهرة بعد الفاصلة. إضافة حواشي لمّا كانت الأماكن المحجوزة عبر القوسين المعقوفين هي حقول قابلة للاستبدال (أي ليست قيمًا فعليةً) فيمكنك إضافة حاشية (padding) أو إضافة فراغ حول العنصر بزيادة حجم الحقل عبر معاملات إضافية، قد تستفيد من هذا الأمر عندما تحتاج إلى تنظيم البيانات بصريًا. يمكننا إضافة حقلٍ بحجمٍ معيّن (مُقاسًا بعدد المحارف) بتحديد ذاك الحجم بعد النقطتين الرأسيتين : كما في المثال الآتي: print("Sammy has {0:4} red {1:16}!".format(5, "balloons")) الناتج: Sammy has 5 red balloons ! أعطينا في المثال السابق حقلًا بحجم 4 محارف للعدد 5، وأعطينا حقلًا بحجم 16 محرفًا للسلسلة النصية balloons (لأنها سلسلة طويلة نسبيًا). وكما رأينا من ناتج المثال السابق، يتم محاذاة السلاسل النصية افتراضيًا إلى اليسار والأعداد إلى اليمين، يمكنك أن تُغيّر من هذا بوضع رمز خاص للمحاذاة بعد النقطتين الرأسيتين مباشرةً. إذ سيؤدي الرمز > إلى محاذاة النص إلى يسار الحقل، أما الرمز ^ فسيوسِّط النص في الحقل، والرمز < سيؤدي إلى محاذاته إلى اليمين. لنجعل محاذاة العدد إلى اليسار ونوسِّط السلسلة النصية: print("Sammy has {0:<4} red {1:^16}!".format(5, "balloons")) الناتج: Sammy has 5 red balloons ! نلاحظ الآن أنَّ محاذاة العدد 5 إلى اليسار، مما يعطي مساحة فارغةً في الحقل قبل الكلمة red، وستظهر السلسلة النصية balloons في منتصف الحقل وتوجد مسافة فارغة على يمينها ويسارها. عندما نُنسِّق الحقل لنجعله أكبر من حجمه الطبيعي فستملأ بايثون الحقل افتراضيًا بالفراغات، إلا أننا نستطيع تغيير محرف الملء إلى محرفٍ آخر بوضعه مباشرةً بعد النقطتين الرأسيتين: print("{:*^20s}".format("Sammy")) الناتج: *******Sammy******** ستضع بايثون السلسلة النصية المُمرَّرة إلى الدالة str.format()‎ ذات الفهرس 0 مكان القوسين المعقوفين لأننا لم نطلب منها عكس ذلك، ومن ثم سنضع النقطتين الرأسيتين ثم سنُحدِّد أننا سنستعمل المحرف * بدلًا من الفراغات لملء الحقل، ثم سنوسِّط السلسلة النصية عبر استعمال الرمز ^ مُحدِّدين أنَّ حجم الحقل هو 20 محرف، ومُشيرين في نفس الوقت إلى أنَّ الحقل هو حقلٌ نصيٌ عبر وضع الرمز s. يمكننا استخدام هذه المعاملات مع المعاملات التي استخدمناها وشرحناها في الأقسام السابقة: print("Sammy ate {0:5.0f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 76 percent of a pizza! حدِّدنا داخل القوسين المعقوفين رقم فهرس القيمة العددية ثم وضعنا النقطتين الرأسيتين ثم اخترنا حجم الحقل ثم وضعنا نقطةً . لنضبط عدد المنازل العشرية الظاهرة، ثم اخترنا نوع الحقل عبر رمز التحويل f. استخدام المتغيرات مرَّرنا منذ بداية هذا الدرس وإلى الآن الأعداد والسلاسل النصية إلى الدالة str.format()‎ مباشرةً، لكننا نستطيع تمرير المتغيرات أيضًا، وذلك بنفس الآلية المعتادة: nBalloons = 8 print("Sammy has {} balloons today!".format(nBalloons)) الناتج: Sammy has 8 balloons today! يمكننا أيضًا استخدام المتغيرات لتخزين السلسلة النصية الأصلية بالإضافة إلى القيم التي ستُمرَّر إلى الدالة: sammy = "Sammy has {} balloons today!" nBalloons = 8 print(sammy.format(nBalloons)) الناتج: Sammy has 8 balloons today! ستُسهِّل المتغيرات من التعامل مع تعبيرات التنسيق وتُبسِّط عملية إسناد مدخلات المستخدم وإظهارها مُنسَّقةً في السلسلة النصية النهائية. استخدام المُنسِّقات لتنظيم البيانات يسطع نجم آلية التنسيق التي نشرحها في هذا الدرس عندما تُستخدَم لتنظيم البيانات بصريًا، فلو أردنا إظهار نتائج قاعدة البيانات إلى المستخدمين، فيمكننا استعمال المُنسِّقات لزيادة حجم الحقل وتعديل المحاذاة لجعل الناتج أسهل قراءةً. لننظر إلى حلقة تكرار تقليدية في بايثون التي تطبع i و i*i و i*i*i لمجالٍ من الأعداد من 3 إلى 13: for i in range(3,13): print(i, i*i, i*i*i) الناتج: 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 11 121 1331 12 144 1728 صحيحٌ أنَّ الناتج مُنظَّمٌ قليلًا، إلا أنَّ الأعداد تتداخل مع بعضها بصريًا مما يُصعِّب قراءة الأسطر الأخيرة من الناتج، وإذا كنتَ تتعامل مع مجموعة أكبر من البيانات التي يتواجد فيها أعداد أكبر (أو أصغر) مما عرضناه في مثالنا، فقد تبدو لك المشكلة جليةً حينها. لنحاول تنسيق الناتج السابق لإعطاء مساحة أكبر لإظهار الأعداد: for i in range(3,13): print("{:3d} {:4d} {:5d}".format(i, i*i, i*i*i)) لم نُحدِّد في المثال السابق ترتيب الحقل وبدأنا مباشرةً بكتابة النقطتين الرأسيتين متبوعةً بحجم الحقل ورمز التحويل d (لأننا نتعامل مع أعداد صحيحة). أعطينا في المثال السابق حجمًا للحقل مساويًا لعدد أرقام العدد الذي نتوقع طباعته في الحقل المعني مضافًا إليه 2، لذا سيبدو الناتج كالآتي: 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 11 121 1331 12 144 1728 يمكننا أيضًا تحديد حجم ثابت للحقل لنحصل على أعمدة متساوية العرض، مما يضمن إظهار الأعداد الكبيرة بصورة صحيحة: for i in range(3,13): print("{:6d} {:6d} {:6d}".format(i, i*i, i*i*i)) الناتج: 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 11 121 1331 12 144 1728 يمكننا أيضًا تعديل محاذاة النص الموجود في الأعمدة باستخدام الرموز > و ^ و <، وتبديل d إلى f لإظهار منازل عشرية، وغير ذلك مما تعلمناه في هذا الدرس لإظهار البيانات الناتجة كما نرغب. الخلاصة قد يكون استخدام المنسقات لوضع القيم في سلسلةٍ نصيةٍ أمرًا مفيدًا لتسهيل تنظيم البيانات والقيم، فالمنسقات هي آليةٌ تساعد في جعل الناتج مقروءًا للمستخدم. ترجمة -وبتصرّف- للمقال How To Use String Formatters in Python 3 لصاحبته Lisa Tagliaferri.
  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