البحث في الموقع
المحتوى عن 'تطبيق ويب'.
-
ناقش ديميان Demian التحديات التي تواجهها المواقع متعددة الأصول multiple origins عند محاولة إنشاء تطبيق ويب تقدمي واحد يشملها جميعًا، وذلك في مقال تطبيق الويب التقدمي على أصول متعددة. يعد موقع التجارة الإلكترونية الافتراضي www.example.com مثالًا على موقع متعدد الأصول، إذا كان: الصفحة الرئيسية مُستضافة على العنوان https://www.example.com. صفحات الفئات مستضافة على عنوان مختلف مثلًا: https://category.example.com. صفحات تفاصيل المنتج مستضافة على عنوان ثالث مختلف، مثلًا: https://product.example.com. تُقيِّد سياسة الأصل المشترك same-origin مشاركة عمّال الخدمة service workers وذاكرة التخزين المؤقت cache، والأذونات permessions، عبر الأصول المختلفة، لذا نوصي بشدة بتجنب هذا النوع من إعداد المواقع (سياسة الأصل المشترك)، ونوصي من لديهم مواقع مبنية بهذه الطريقة بالفعل، بالنظر في الانتقال إلى معمارية الموقع ذات الأصل الواحد single origin إذا أمكن. تجنب استخدام أصول مختلفة لأقسام نفس الموقع عند بنائك تطبيق PWA واحد يشملها جميعًا. سنعتني في هذا المقال بالحالة المعاكسة، بدلًا من توفير تطبيق ويب تقدمي واحد مجزأ على أصول مختلفة، فإننا سنهتم بحالة الشركات التي ترغب بتوفير تطبيقات ويب تقدمية PWA متعددة، تعمل على نفس اسم النطاق، وذلك لإعلام مستخدميها أن تطبيقات الويب التقدمية هذه تنتمي لنفس الشركة أو الخدمة. لكن قبل البدء سنراجع أهم المفاهيم التقنية التي ستلزمنا، وذلك بالاعتماد على رابط موقع التجارة الالكترونية الافتراضي https://www.example.com: مصطلحات تقنية النطاق Domain: هو أي تسلسل من العناوين الموجودة في نظام أسماء النطاقات DNS، مثلًا يعد com وexample.com نطاقان، لكن الفرق هنا أن com هو النطاق الرئيسي بينما تُعد example.com أو مثلًا test.com وblog.com نطاقات فرعية sub domains من النطاق الرئيسي com، أيضًا يعد كلًا من web.example.com وapp.example.com مثلًا وغيرها، نطاقات فرعية sub domains من نطاقها الرئيسي www.example.com الذي يُعد كما قلنا نطاق فرعي بالنسبة للنطاق الرئيسي العام com وهلم جرًا. اسم المضيف Host name: هو اسم مخصص لأي جهاز متصل بشبكة، يُحلّل إلى (ويقابل) عنوان IP الجهاز في نظام أسماء النطاقات DNS، مثلًا www.example.com هو اسم مضيف. الأصل Origin: هو تجميعة من كلًا من البروتوكول، واسم المضيف، والمنفذ (اختياريًا)، مثلًا يعد العنوان https://www.example.com:443 أصل. إنشاء علاقة بين تطبيقات الويب التقدمية PWA قد ترغب في بعض الحالات بإنشاء تطبيقات مستقلة، رغم أنك لا تزال مقررًا أنها متعلقة ببعضها وتنتمي لنفس المؤسسة أو العلامة التجارية brand. يُعد إعادة استخدام نفس اسم النطاق لجميع هذه المواقع طريقة جيدة لتأسيس علاقة قوية بينها، مثلًا: يريد موقع التجارة الإلكترونية https://www.example.com إنشاء تجربة موقع مستقلة تسمح للبائعين بإدارة مخزونهم، مع التأكيد لهم أن هذا الموقع بتجربته المستقلة ينتمي إلى موقع التجارة الرئيسي الذي يشتري المستخدمون منه المنتجات. تريد شركة إخبارية إنشاء تطبيق محدد لحدث رياضي عظيم، وذلك للسماح للمستخدمين بتلقي إحصائيات حول مسابقاتهم المفضلة عبر الإشعارات، وتمكينهم من تثبيت تطبيق PWA هذا على أجهزتهم بالتالي مشاهدة المحتوى الرياضي بسهولة، هذا مع التأكيد للمستخدمين أن التطبيق الرياضي يتبع لتلك الشركة الإخبارية. تريد إحدى الشركات إنشاء تطبيقات منفصلة لكلًا من الدردشة chat ورسائل البريد mail والتقويم calender وتريد أن تعمل هذه التطبيقات بشكل منفصل لكن تكون مرتبطة باسم الشركة. تريد الشركة المالكة لموقع example.com توفير ثلاثة تطبيقات PWA مستقلة، باستخدام نفس اسم المجال لتأسيس علاقة قوية بينها. استخدام أصول منفصلة الطريقة الموصى بها في مثل هذه الحالات، هي بناء كل تطبيق ذي هدف خاص على أصل origin خاص. إذا كنت تريد استخدام نفس اسم النطاق لجميع تطبيقات PWA المرتبطة ببعضها، فيمكنك القيام بذلك باستخدام النطاقات الفرعية sub domains. مثلًا يمكن لشركة تقدم خدمات إنترنت متعددة استضافة تطبيق بريد على الأصل https://mail.example.com وتطبيق تقويم على الأصل https://calendar.example.com، وتقديم الخدمة الرئيسية لأعمالها على الأصل https://www.example.com. وهناك مثال آخر لاستخدام النطاقات الفرعية للحصول على أصول مختلفة، وهو موقع رياضي يريد إنشاء تطبيق مستقل مخصص تمامًا لحدث رياضي مهم، مثل بطولة كرة القدم، وذلك على https://footballcup.example.com، يمكن للمستخدمين تثبيت موقع البطولة هذا على أجهزتهم واستخدامه بشكل مستقل عن موقع الرياضة الرئيسي المستضاف على https://www.example.com. قد تكون هذه الطريقة مفيدة أيضًا للأنظمة التي تتيح للعملاء إنشاء تطبيقاتهم المستقلة بهم تحت علامة الشركة التجارية، كأن يتيح أحد تطبيقات الويب للتجار إنشاء تطبيقاتهم الخاصة على نطاقات فرعية بأسمائهم مثل https://ahmad.example.com وhttps://ali.example.com وغيرها. يضمن استخدام أصول مختلفة العزل بين التطبيقات، أي يمكن لكل تطبيق من تطبيقات PWA المتعلقة ببعضها، إدارة ميزات المتصفح المختلفة بشكل مستقل، بما في ذلك: قابلية التثبيت Installability: حيث يوجد لكل تطبيق منها ملف بيان موارد Manifest، يوفر تجربة التطبيق الخاصة القابلة للتثبيت. التخزين Storage: يوجد لكل تطبيق منها ذاكرة تخزين مؤقتة cache، وميزات تخزين محلي local storage، وجميع أشكال التخزين المحلي للجهاز device-local بشكل أساسي. عمال الخدمة Service Workers: يوجد لكل تطبيق منها عامل خدمة مسؤول عن نطاقات التطبيق المسجلة فيه. الأذونات Permissions: يعطَى لكل تطبيق من تطبيقات PWA المرتبطة ببعضها عبر النطاقات الفرعية والمختلفة في الأصل، أذونات بشكل منفصل عن غيره، وهذا يفيد المستخدمين في معرفة الخدمة التي يمنحونها أذوناتهم بالضبط، كما تُنسب الإشعارات بدقة لكل تطبيق. يُوصى باتباع هذه الدرجة من العزلة (الأصول المختلفة) عند بناء عدة تطبيقات ويب تقدمية PWA مستقلة Independent. إذا أرادت تطبيقات النطاقات الفرعية مشاركة بياناتها المحلية مع بعضها البعض، فبإمكانها القيام بذلك عبر ملفات تعريف الارتباط cookies، أو بطريقة متقدمة أكثر، يمكنها مزامنة التخزين بواسطة الخادم. يعد بناء تطبيقات ويب تقدمية PWA مختلفة على نطاقات منفردة باستخدام النطاقات الفرعية ممارسة جيدة. استخدام نفس الأصل الطريقة الثانية هي بناء تطبيقات PWA المتعلقة ببعضها، على نفس الأصل same origin، ويتضمن ذلك السيناريوهات التالية: مسارات غير متراكبة أي تُستضاف تطبيقات PWA المتعلقة ببعضها على نفس الأصل، لكن مع مسارات paths غير متراكبة non-overlapping، مثلًا: https://example.com/app1 https://example.com/app2 المسارات المتراكبة أو المتداخلة أي تستضاف تطبيقات الويب التقديمة PWA المتعددة على نفس الأصل، لكن أحد نطاقاتها متداخل nested داخل الآخر، مثلًا: https://example.com (التطبيق الخارجي) https://example.com/app (التطبيق الداخلي) يسمح لك كلًا من الواجهة البرمجية Service Worker وتنسيق ملف بيان الموارد manifest بتطبيق الطريقتين السابقتين، وذلك بتحديد النطاقات فرعية أو رئيسية بناء على مستوى المسار path-level، لكن في كلتا الحالتين، يؤدي استخدام نفس الأصل إلى العديد من التحديات والمحدوديّات، النابعة من حقيقة أن المتصفح لن يعتبرها تطبيقات متمايزة، بالتالي لا يُنصح بهذا الأسلوب. سنحلِّل تلك التحديات بالتفصيل في الفقرة القادمة، وسنذكر ما الذي يمكن فعله إذا لم يكن من الملائم استخدام أصول منفصلة. لا يُنصح باستخدام المسارات المتداخلة وغير المتداخلة لاستضافة تطبيقين PWA مستقلين على نفس الأصل. التحديات التي تواجه تطبيقات PWA على نفس الأصل فيما يلي بعضًا من المشكلات العملية التي تواجهنا عند استخدام المسارات المتراكبة وغير المتراكبة لنفس الأصل: التخزين Storage: تُشارَك ملفات تعريف الارتباط cookies وبيانات التخزين المحلي local storage وجميع أشكال التخزين المحلي للجهاز device-local بين التطبيقات ذات نفس الأصل origin. لهذا، فإذا قرر المستخدم مسح البيانات المحلية لأحد تطبيقات PWA المتعلقة ببعضها، فستُمسح بيانات جميع التطبيقات المشتركة بنفس الأصل مع ذلك التطبيق، فلا توجد طريقة لمسح بيانات تطبيق واحد فقط منها بعينه. تحث بعض المتصفحات مثل Chrome وغيره المستخدمين دومًا على مسح البيانات المحلية عند إلغاء تثبيت أحد التطبيقات، وهذا سيؤثر على بيانات التطبيقات الأخرى المشتركة معه في نفس الأصل، والتحدي الآخر الممكن أن تواجهه بالنسبة للتخزين في التطبيقات المشتركة بنفس الأصل، هو أن هذه التطبيقات بطبعها تشارك بعضها حصة التخزين الخاصة بها، بالتالي إذا كان أيًا منها يشغَل مساحة تخزينية كبيرة جدًا، فسيتأثر الآخر سلبًا. الأذونات Permissions: إذا منح المستخدم إذنًا لأحد تطبيقات PWA المتعلقة ببعضها، فسيُمنح نفس الإذن لجميع التطبيقات المشتركة في نفس الأصل مع ذلك التطبيق، فالأذونات مرتبطة بالأصل أيضًا كما التخزين. قد يبدو هذا ميزة، بحيث لا تضطر جميع تلك التطبيقات إلى طلب نفس الإذن، أي طلب نفس الإذن عدة مرات، لكن التحدي هنا يكمن في حظر المستخدم إذنًا لأحد التطبيقات، فوقتها تُمنع التطبيقات الأخرى المشتركة في الأصل من طلب هذا الإذن، بالتالي لا تستطيع جميعها استخدام ميزة التطبيق التي تتطلب ذلك الإذن. إعدادات المستخدم User settings: تعيّن إعدادات المستخدم لتطبيقات الويب أيضًا بالإعتماد على الأصل، مثلًا، إذا كان لتطبيقان مشتركان في الأصل أحجام خطوط مختلفة، ويريد المستخدم ضبط تكبير حجم خط أحدهما فقط، فسيَكبُر حجم خط التطبيقات الأخرى المشتركة معه في نفس الأصل. تجعلنا التحديات السابقة نتجنب هذا النهج (الأصل المشترك). لكن، إذا كنت لا تستطيع استخدام أصل منفصل (مثل نطاق فرعي) لكل تطبيق من تطبيقات PWA المتعلقة ببعضها، فيمكنك ترشيح خيار المسارات غير المتداخلة بدلًا من خيار المسارات المتراكبة أو المتداخلة ضمن خيارَي استخدام نفس الأصل في الأعلى. تحديات إضافية للمسارات المتراكبة أو المتداخلة المشكلة الإضافية المتعلقة بنهج المسارات المتراكبة أو المتداخلة (حيث https://example.com هو التطبيق الخارجي والمسار https://example.com/app هو للتطبيق الداخلي)، هو أن جميع عناوين URL في التطبيق الداخلي ستُعتبر جزءًا من كلا التطبيقين الداخلي والخارجي. وهذا يسبب عمليًا المشكلات التالية: إمكانية التثبيت Installation: إذا زار المستخدم التطبيق الداخلي (مثلًا في متصفح ويب)، وكان التطبيق الخارجي مثبتًا بالفعل في جهاز المستخدم، فلن يعرض المتصفح لافتات لتثبيت التطبيق الداخلي. وذلك لأن المتصفح عندما يتحقق من إنتماء الصفحة الحالية إلى تطبيق مثبت بالفعل أم لا، فإنه سيعتبر التطبيق الداخلي مثبت على الجهاز كون التطبيق الخارجي مثبت بالفعل، ويمكن أن تُحل هذه المشكلة بتثبيت التطبيق الداخلي يدويًا (عبر خيار "إنشاء اختصار" في المتصفح)، أو تثبيت التطبيق الداخلي أولاً، قبل التطبيق الخارجي. الواجهتان البرمجيتان Notification و Padging: إذا تم تثبيت التطبيق الخارجي دون الداخلي، فستُنسب الإشعارات والشارات القادمة من التطبيق الداخلي عن طريق الخطأ إلى التطبيق الخارجي، إذ تنسب الإشعارات والشارات بدقة لكل تطبيق في حال ثبِّت كليها على جهاز المستخدم. التقاط الرابط Link Capturing: إذا تم تثبيت التطبيق الخارجي دون الداخلي، فلن يتم التقاط روابط التطبيق الداخلي المرتبطة بالتطبيق الخارجي، أي بدلًا من أن يُفتح الرابط الداخلي في نفس التبويبة ضمن نطاق تصفح التطبيق الخارجي، فإنه سيُفتح في تبويبة جديدة لأن المتصفح سعتبره تطبيقًا غريبًا (تطبيق مختلف غير مرتبط به). لكن إذا أضيفت تطبيقات PWA هذه إلى المتجر على نظامَي Chrome OS وAndroid، فسيلتقط التطبيق الخارجي تلك الروابط، وأيضًا إذا تم تثبيت التطبيق الداخلي، فسيظل نظام التشغيل يوفر للمستخدم خيار فتح تلك الروابط المتعلقة بالتطبيق الداخلي في التطبيق الخارجي. الخاتمة تطرقنا في هذا المقال إلى الطرق المختلفة الممكن للمطورين من خلالها إنشاء العديد من تطبيقات الويب التقدمية المتعلقة ببعضها على نفس النطاق (وهذا مختلف عن استخدام نطاقات وأصول مختلفة لأقسام موقع واحد كبير). باختصار، نوصي بشدة باستخدام أصل مختلف (مثلًا باستخدام النطاقات الفرعية) لاستضافة تطبيقات PWA المستقلة، إذ يؤدي استضافتها على نفس الأصل إلى عدة تحديات، كون المتصفح لن يعتبرها تطبيقات متمايزة، إذن الخلاصة أن هناك ثلاث حالات لاستضافة تطبيقات PWA المتعلقة ببعضها على نفس النطاق: على أصول منفصلة: موصى بها. على نفس الأصل بمسارات غير متداخلة: ليست مستحسنة. على نفس الأصل بمسارات متراكبة overlapping أو متداخلة nested: لا ينصح بها بشدة. إذا لم يكن بإمكانك استخدام أصول مختلفة، فاستخدم مسارات غير متداخلة، مثلًا https://example.com/app1 وhttps://example.com/app2، بدلًا من المسارات المتراكبة أو المتداخلة مثل https: //example.com (للتطبيق الخارجي) وعنوان https://example.com/app (للتطبيق الداخلي). ترجمة -وبتصرف- للمقال Building multiple Progressive Web Apps on the same domain من موقع web.dev. اقرأ أيضًا ما هي تطبيقات الويب التقدمية PWA استقبال تطبيقات الويب التقدمية البيانات المشاركة عبر الواجهة البرمجية Web Share Target جعل تطبيق الويب التقدمي PWA يبدو كتطبيق أساسي في نظام التشغيل إضافة اختصارات للمهام الشائعة في تطبيقات الويب التقدمية PWA
-
يقدم هذا المقال إرشادات تصميم حول كيفية إنشاء تجربة مستخدم رائعة لتطبيقات الويب التقدمية PWAs على كلًا من الشبكات البطيئة وغير المتصلة. يمكن أن تتأثر جودة اتصال الشبكة بشكل عام بعدة عوامل مثل: تغطية ضعيفة من المزود. ظروف طقس شديدة. انقطاع التيار الكهربائي. الدخول في مناطق ميتة الاتصال دائمًا dead zones مثل البنايات ذات الجدران المانعة لاتصالات الشبكة. الدخول في مناطق ميتة الاتصال مؤقتًا مثل السفر في قطار وعبور نفق. اتصالات الإنترنت المقيدة زمنيًا time-boxed مثل الاتصالات الموجودة في المطارات أو الفنادق. الممارسات الثقافية التي تتطلب وصولًا محدودًا أو معدومًا للإنترنت في أوقات أو أيام محددة. هدفك هو تقديم تجربة مستخدم جيدة في وضع عدم الاتصال تقلل من تأثير التغيُّر في حالة التطبيق عندما يستعيد المستخدم اتصاله بالشبكة. حدد ما تريد إظهاره للمستخدمين في حالة ضعف الاتصال بالشبكة اطرح على نفسك أولًا السؤال التالي: كيف سيبدو نجاح وفشل اتصال الشبكة في تطبيقك؟ الاتصال الناجح هو التجربة العادية لتطبيقك بوجود اتصال بالشبكة، بينما يُمثِّل فشل الاتصال كيفية تصرف تطبيقك في حالة عدم الاتصال بالشبكة أو الاتصال البطيئ بها. عند التفكير في نجاح أو فشل اتصال الشبكة، عليك أن تسأل نفسك أسئلة حول تجربة المستخدم UX المهمة هذه: ما هي المدة التي تنتظرها لتحديد نجاح أو فشل الاتصال؟ ماذا يمكنك أن تفعل أثناء تحديد نجاح أو فشل الاتصال؟ ماذا تفعل في حال فشل الاتصال؟ كيف تبلِّغ المستخدم بما ورد أعلاه (أنه في مرحلة انتظار تحديد حالة التطبيق، أو أن اتصاله فشل أو أن اتصالة استُعيد)؟ بلغ المستخدمين عن حالتهم الحالية وعن تغيُّر حالة التطبيق أخبر المستخدم بالإجراءات التي لا يزال بإمكانه القيام بها عندما يكون في حالة فشل الاتصال، وأيضًا أخبره بحالة التطبيق الحالية، مثلًا ممكن أن يقول الإشعار: بلّغ المستخدم بوضوح عند حدوث تغيُّر في حالة التطبيق في أسرع وقت ممكن. استخدام تطبيق Google I/O مربع إشعار لإخبار المستخدم عندما يكون في حالة عدم اتصال بالشبكة، إذ يقول الإشعار "أنت بلا اتصال، تغييراتك في My Schedule ستُحفظ لوقت لاحق". بلِّغ المستخدمين عندما يتحسن اتصال الشبكة لديهم أو يعود يعتمد كيفية إبلاغك المستخدم بتحسُّن اتصال الشبكة على تطبيقك، فالتطبيقات مثل تطبيق حالة الطقس الذي يعطي الأولوية للمعلومات الآنية، يجب أن يُحدَّث تلقائيًا عند استعادة الاتصال، وإشعار المستخدم في مثل هكذا تطبيقات يجب أن يتم في أقرب وقت ممكن. يوصَى بإِعلام المستخدم أنه قد تم تحديث تطبيق الويب في الخلفية، وذلك باستخدام تلميح مرئي عبر تنبيه أو أي وسيلة إشعارات تؤدي الغرض. أحد الأمثلة على استخدام هذه التجربة هو متصفح Chrome Status التي تُظهر ملاحظة للمستخدم عند تحديث التطبيق في الخلفية. تحتاج بعض التطبيقات مثل تطبيق الطقس إلى تحديث تلقائي، لأن البيانات القديمة ليست مفيدة مع تقلبات الطقس المستمرة. يتيح تطبيق ana (أنا) للمستخدم معرفة وقت تحديث المحتوى عبر مربع إشعار في أعلى الشاشة. يمكنك تخصيص مساحة ثابتة تُظهر من خلالها آخر مرة تم فيها تحديث التطبيق، سيكون هذا مفيدًا لتطبيق تحويل العملات مثلًا. تطبيق Material mony يُظهر آخر تحديث للتطبيق. وينبّه المستخدم عند تحديث التطبيق. يمكن أن تعرض تطبيقات مثل تطبيقات الأخبار إشعارًا بسيطًا بالنقر للتحديث، لإعلام المستخدم بالمحتوى الجديد، فقد يتسبب التحديث التلقائي في فقدان المستخدمين لمكان تصفحهم. صحيفة الانترنت Tailpiece تُنزّل آخر الأخبار تلقائيًا. وتسمح للمستخدمين بالتحديث يدويًا حتى لا يفقدوا مكان تصفحهم في المقال. تحديث واجهة المستخدم UI لتعكس الحالة السياقية الحالية لكل بِت bit من واجهة المستخدم سياقُه ووظائفه الخاصة التي تتغير بناءً على كونها تتطلب اتصالًا ناجحًا بالشبكة أو لا، أحد الأمثلة على ذلك هو مواقع التجارة الإلكترونية التي يمكن تصفحها في وضع عدم الاتصال، فوقتها يُعطَّل زر الشراء إلى حين استعادة الاتصال. يمكن أن تتضمن الأشكال الأخرى للحالات السياقية، البيانات، مثلًا يتيح التطبيق المالي Robinhood للمستخدمين شراء الأسهم، ويَستخدِم الألوان والرسومات لإعلام المستخدم عندما يكون السوق مفتوحًا، وعند إغلاق السوق تتحول الواجهة بالكامل إلى اللون الأبيض ثم تتحول إلى اللون الرمادي، أيضًا عندما تزيد قيمة السهم أو تنقص، يتحول كل عنصر واجهة مستخدم للسهم إلى اللون الأخضر أو الأحمر حسب حالته. توضيح للمستخدم نموذج العرض الخاص بوضع عدم الاتصال يعد وضع عدم الاتصال بالشبكة نموذج عرض جديد لجميع المستخدمين، كون نموذج العرض في الوضع المتصل بالشبكة هو الأساسي والمعروض دائمًا لديهم، لذا تحتاج إلى تعليم المستخدمين التغيّرات التي ستحدث عندما لا يكون لديهم اتصال بالشبكة، أبلغهم بالمكان الذي يتم فيه حفظ البيانات الكبيرة وامنحهم الإعدادات لتغيير سلوك التخزين الافتراضي، وتأكد من استخدام عدة مكونات لتصميم واجهة المستخدم مثل اللغة الغنية بالمعلومات والرموز والإشعارات واللون والصور لتوصيل نموذج العرض بدلاً من الاعتماد على خيار تصميم واحد لذلك. تقديم تجربة افتراضية لوضع عدم الاتصال إذا كان تطبيقك لا يتطلب بيانات كثيرة في عمله، فخزِّن البيانات التي يعتمد تطبيقك عليها مؤقتًا افتراضيًا، إذ إن المستخدمين يمكن أن يشعروا بالإحباط أكثر فأكثر إذا لم يكونوا قادرين على الوصول إلى بياناتهم إلا في وضع الاتصال بالشبكة. حاول أن تجعل التجربة مستقرة قدر الإمكان، فالاتصال غير المستقر سيجعل تطبيقك يبدو أنه غير جدير بالثقة، على عكس التطبيقات التي تقلل من تأثير فشل الاتصال بالشبكة التي تبدو خلَّابة بالنسبة للمستخدم. يمكن لمواقع الأخبار الاستفادة من التنزيل التلقائي والحفظ التلقائي لآخر الأخبار بحيث يُتاح للمستخدم قراءة أخبار اليوم دون الاتصال بالشبكة، ولو على الأقل تنزيل النص بدون صور الخبر، أيضًا يمكن لتلك المواقع التكيُّف مع سلوك المستخدم، مثلًا إذا كان قسم الرياضة هو ما يشاهده المستخدم عادةً، تجعل تلك المواقع تنزيل أخبار هذا القسم هو الأولوية. إذا كان الجهاز غير متصل بالشبكة، فسيُشعر Tailpiece المستخدم بذلك. ويُعرِّف المستخدم أنه لا يزال بإمكانه استخدام التطبيق جزئيًا على الأقل. عندما يتعلق الأمر بالإبلاغ عن حالة أحد التطبيقات، فإن قول "الشبكة معطلة" يرسل رسالة مفادها أن شبكة التطبيق تواجه مشكلات، بينما توضح عبارة "أنت غير متصل" للمستخدم أن المشكلة من طرفه. إبلاغ المستخدم عندما يكون التطبيق جاهزًا للاستخدام دون اتصال بالشبكة يجب أن توضِّح للمستخدمين عند تحميلهم تطبيقك لأول مرة، ما إذا كان بالإمكان استخدام التطبيق في وضع عدم الاتصال أم لا، يمكنك القيام بذلك باستخدام وِدجِت widget يوفر ملاحظات موجزة حول عملية ما، من خلال رسالة أسفل الشاشة مثلًا عند مزامنة قسم أو تنزيل ملف. فكر مرة أخرى في اللغة التي تستخدمها وتأكد من أنها مناسبة لجمهورك، إذ يُسيئ مثلًا الجمهور غير التقني عمومًا فهم مصطلح "غير متصل"، لذا استخدم لغة قائمة على الإجراء action-based تساعد جمهورك على فهمها والاندماج معها. يظهر تطبيق Google I/O إشعارًا مفاده: اكتمل التخزين المؤقت، وستعمل الزيارات المستقبلية في وضع عدم الاتصال. يتضمن موقع Chrome Platform Status معلومات حول المساحة المشغولة بالتخزين المؤقت. اجعل "التخزين في وضع عدم الاتصال" جزءًا واضحًا من واجهة التطبيقات كثيفة البيانات إذا كان أحد تطبيقاتك يستخدم بيانات كثيرة، فتأكد من وجود مفتاح تبديل switch لاضافة عنصر ما للاستخدام دون اتصال save for offline بدلاً من التنزيل التلقائي له، وتأكد أن ذلك المفتاح غير محجوب من عناصر واجهة المستخدم الأخرى وأنه واضح للمستخدم. أحد الأمثلة على ذلك هو مشغل الموسيقى الذي يحتاج ملفات ذات بيانات كبيرة، قد يرغب مستخدم هذا التطبيق في استخدام المشغل دون اتصال بالشبكة، فهنا تنزيل الموسيقى لاستخدامها لاحقًا يتطلب تخطيط مسبق من المستخدم كون الملفات ستحجز مساحة تخزين كبيرة، ووقتها يكون اطلاع المستخدم حول هذا الأمر لازمًا أثناء تهيئته للتطبيق. توضيح ما هو متاح حاليا كن واضحًا فيما يتعلق بالخيار الذي تقدمه، قد تحتاج إلى إظهار نافذة معينة أو إعداد ما يعرض مثلًا "المكتبة غير المتصلة بالشبكة" offline library أو فهرس المحتوى content index، الذي يمكن للمستخدم من خلاله رؤية ما خزّنه في هاتفه بسهولة. أيضًا تأكد أن الإعدادات موجزة وكن واضحًا بشأن مكان تخزين البيانات ومن يمكنه الوصول إليها. إظهار التكلفة الفعلية للإجراء يساوي العديد من المستخدمين قدرة التطبيق في وضع عدم الاتصال بقدرته على "التنزيل"، وغالبًا ما يستغل المستخدمون في البلدان ذات التغطية الضعيفة وقت الاتصال بالشبكة لحفظ محتوى التطبيق للاستخدام دون اتصال. قد يتجنب مستخدمو الباقات المحدودة ذات الكلفة المرتفعة للاتصال بالإنترنت تنزيل ملفات كبيرة خوفًا من زيادة التكلفة، لذا يلزمك أيضًا عرض تكلفة تحميل الملفات أو تكلفة أداء المهام بشكل عام في تطبيقك، مثلًا عندما يكتشف تطبيق الموسيقى المذكور كمثال في فقرة سابقة أن المستخدم في خطة البيانات المدفوعة data plan، يعرِض حجم الملف المُشغَّل، ويتيح للمستخدم رؤية تكلفة تحميله. ساعد في منع التجارب الساخرة غالبًا ما ينجز المستخدمون تجربة ما ساخرة في التطبيق دون أن يدركوا ذلك، مثلًا قبل انتشار تطبيقات مشاركة الملفات المعتمدة على السحابة cloud-based، كان من الشائع أن يحفظ المستخدمون الملفات الكبيرة ويرفقوها برسائل البريد الإلكتروني ليواصلوا تحريرها من جهازٍ مختلف. من المهم عدم الإنجرار إلى تجربتهم الساخرة تلك، بل النظر إلى ما يحاولون تحقيقه منها، بمعنى، بدلاً من التفكير في كيفية جعل إرفاق ملف كبير أسهل، حُل مشكلة مشاركة الملفات الكبيرة عبر أجهزة متعددة باستخدام التخزين السحابي. اجعل العمليات قابلة للنقل من جهاز لآخر عند إنشاء تطبيقات لشبكات غير مستقرة الاتصال، حاول إجراء المزامنة بمجرد أن يتحسن الاتصال حتى تكون تجربة المستخدم قابلة للنقل من جهاز لآخر، مثلًا تخيل أن تطبيق سفر ما يفقد اتصال الشبكة في منتصف مهمة الحجز، وعند استعادة الاتصال، تتيح عملية تزامُن بيانات التطبيق مع حسابات المستخدمين لهم مواصلة الحجز على حواسيبهم المكتبية. ينزعج المستخدمين للغاية من التطبيقات غير القادرة على نقل تجاربهم بين الأجهزة. أيضًا بلّغ المستخدمين بالحالة الحالية لبياناتهم، مثلًا يمكنك إظهار ما إذا تم مزامنة التطبيق أم لا، أعلمهم بكل المستجدات في حالة التطبيق لكن حاول ألا تثقل عليهم بالرسائل والإشعارات. إنشاء تجارب تصميم متكاملة تجارب التصميم المتكاملة inclusive تشمل تصميمًا ذا مغزى، ولغة بسيطة، وأيقونات معيارية، وتمثيلات هادفة، ستساعد المستخدم لإكمال مهمته في التطبيق بسلاسة وبساطة دون أي إعاقة. استخدم لغة بسيطة وموجزة تجربة المستخدم الجيدة لا تتعلق فقط بواجهة مصممة جيدًا، بل تتضمن المسار الذي يسلكه المستخدم بالإضافة إلى الألفاظ المستخدمة في التطبيق. تجنب المصطلحات التقنية عند شرح حالة التطبيق أو مكونات واجهة المستخدم، وضع في اعتبارك أن عبارة "التطبيق غير متصل" قد لا تنقل للمستخدم الحالة الحالية للتطبيق. تجنب المصطلحات غير المفهومة للمستخدمين غير التقنيين مثل: "عامل الخدمة". استخدم اللغة الواضحة التي تصف الإجراء مثل: "تنزيل". استخدم طرق تصميم متعددة لتسهيل تجربة المستخدم استخدم اللغة واللون والمكونات المرئية لإظهار تغيُّر حالة التطبيق أو وضعه الحالي، فقد لا يلاحظ المستخدم حالة التطبيق إذا عُرضت باستخدام اللون فقط. استخدام واجهة مستخدم رمادية لتمثيل وضع عدم الاتصال هو بديهة المصممين، لكن في الويب، يمكن أن يُفهم من هذه الواجهة أن العنصر محمّل، كما أن عناصر واجهة المستخدم الرمادية مثل عناصر الإدخال في النماذج تعني أيضًا أن العنصر معطل، بالتالي استخدام اللون فقط لتصوير الحالة يمكن أن يسبب لبسًا في فهمها. لمنع هذا اللبس، عبِّر عن حالات المستخدم المختلفة بعدة طرق، مثلًا باستخدام كلًا من الألوان والتسميات والأيقونات ومكونات واجهة المستخدم الواضحة وغيرها. لا تستخدم اللون كوسيلة وحيدة لوصف الحالة، كما في الصورة التالية مستخدَم اللون الأحمر فقط للدلالة على تجاوز النص المكتوب للعدد المسموح به من الحروف. بل استخدم مزيجًا من عناصر التصميم لتوصيل المعنى، مثل اللغة الواضحة بالإضافة للون، لاحظ عند استخدم التعبير اللفظي الموضح للخطأ "تجاوزْت الحد الأقصى لعدد الأحرف" مع اللون المعبّر لإزالة اللبس بشأن سبب الخطأ. استخدم أيقونات بليغة تأكد من إيصال المعلومات للمستخدم بشكل صحيح وذلك باستخدام تسميات نصية ذات معنى مع الأيقونات، إذ يمكن أن يسبب استخدام الأيقونات لوحدها لبسًا. قد يسيء المستخدمون فهم بعض الأيقونة المستخدمة في التطبيقات، مثلًا يعد استخدام أيقونة القرص المرن لحفظ البيانات أمرًا منطقيًا بالنسبة للجيل الأكبر سنًا، لكن قد تسبب هذه الأيقونة لبسًا للمستخدمين الأصغر سنًا الذين لم يروا قرصًا مرنًا في حياتهم، وبالمثل فإن أيقونة القائمة التي تشبه البرغر hamburger menu قد تشوِّش المستخدمين عند عرضها دون تسمية. أيقونة الحفظ دون اتصال التي على شكل التنزيل النموذجي -أو ربما إذا كان الإجراء يتضمن مزامنة- قد تكون على شكل أيقونة مزامنة، لذا عند تقديم أحد الأيقونات المتعلقة بوضع عدم الاتصال، حاول أن توفر معها تسمية نصية ووصف. قد تفسَّر بعض الإجراءات على أنها حفظ دون اتصال بدلاً من إظهار حالة الشبكة، لذا فكر في الإجراء الذي تحاول الإبلاغ عنه بدلاً من عرض أيقونات مجردة (ذات طابع عام) للمستخدم، كأن يكون شكل أيقونة حفظ البيانات أو تنزيلها قائمًا على الإجراء action-based أي يدل على طبيعة استخدامها بالضبط. يمكن أن يعني وضع عدم الاتصال عددًا من الأشياء اعتمادًا على السياق، مثل التنزيل، والتصدير، والتثبيت، وما إلى ذلك. لمزيد من المعرفة، راجع مجموعة أيقونات التصميم Material Design. استخدم عناصر نائبة في التخطيط ريثما يجهز المحتوى مخطط الهيكل skeleton layout للتطبيق هو عناصر نائبة توضع في هيكل التطبيق الرئيسي ريثما يُحمّل المحتوى الفعلي، يمكنك استخدامه لتوضّح للمستخدم أن محتوى الصفحة على وشك التحميل. ضع في اعتبارك أيضًا استخدام واجهة التحميل المسبق preloader UI، مع رسالة نصية تخبر المستخدم أنه يجري تحميل التطبيق الآن، يمكنك أيضًا تحريك محتوى ما أو وضع شريط حالة أو حلقات دوارة لإشعار المستخدم أن التطبيق حي وجاري تحميل محتواه، بالتالي إقناع المستخدم بعدم إعادة طلب عنوان التطبيق أو تحديثه. وضع عناصر نائبة في هيكل التطبيق الرئيسي مكان المحتوى ريثما تُحمّل مع عرض إشعار يخبر المستخدم بمعدل التنزيل. ثم وضع المحتوى مكان تلك العناصر النائبة متى ما انتهت عملية التحميل وأصبح المحتوى جاهزًا للعرض. لا تعيق المحتوى عندما يشغِّل المستخدم إجراء مثل إضافة عنصر جديد، فإن التطبيق يتصل بالخادم لمزامنة العنصر الجديد، وللدلالة على ذلك فإنه يعرض عنصر تدخُّلي للتحميل يغطي الشاشة بأكملها (غالبًا أيقونة التحميل الدائرية تتدخل وسط الصفحة وتحجب تنفيذ اي عملية ريثما ينتهي التحميل لتختفي بعدها). قد يعمل هذا بشكل جيد إذا كان لدى المستخدم اتصال شبكة ثابت، لكن إذا كان اتصال الشبكة غير مستقر، فلا يمكن للمستخدمين التخلص من عنصر التحميل الذي يغطي الشاشة أثناء تحميل بياناتهم، فكأن الواجهة تمنعهم من القيام بأي شيء في التطبيق وتعيق ظهور محتواه. هذه ليست تجربة جيدة، بل يجب أن يتجنب تطبيقك التعامل مع طلبات الشبكة التي تعيق ظهور في وضع الاتصال غير المستقر، والسماح بدلًا من ذلك للمستخدمين بالاستمرار في تصفح تطبيقك ومهام قائمة الانتظار التي ستُنفَّذ وتُزامَن بمجرد تحسُّن الاتصال. أظهر حالة الإجراءات من خلال تزويد المستخدمين برد فعل، مثلًا إذا كان أحد مستخدمي تطبيق مستنداتك يُحرِّر مستند عند انقطاع الاتصال، ففكر في تغيير تصميم رد الفعل بحيث يكون مختلفًا بشكل مُلاحَظ عما كان عليه قبل انقطاع الاتصال، لكن حافظ على إظهار أنه قد تم حفظ ملفه وستتم مزامنته عندما يُستعاد الاتصال بالشبكة، فذلك سيؤدي إلى إعلام المستخدم بمختلف الحالات المتاحة وطمأنته بأن قد تم تخزين مهمته أو إجراءه، كما لهذا فائدة إضافية تتمثل في زيادة ثقة المستخدم في استخدام تطبيقك. صمم لجميع طبقات المستخدمين تعد الأجهزة منخفضة الجودة في بعض المناطق أمرًا شائعًا، وكذلك اتصال الشبكة غير المستقر، الذي بالنسبة للعديد من المستخدمين، لا يمكنه تحمّل نقل البيانات، لذا تحتاج في مثل هكذا حالات إلى كسب ثقة المستخدم من خلال الشفافية والاقتصاد في التعامل مع البيانات، وذلك بأن تفكِّر في طرق لمساعدة مستخدمي الاتصالات الضعيفة وطرق لتبسيط الواجهة بهدف تسريع إنجاز المهام. حاول دائمًا سؤال المستخدمين قبل تنزيل محتوى ضخم من البيانات. اعرض خيارات النطاق الترددي المنخفض low bandwidth للمستخدمين ذوي اتصال الشبكة الضعيف، أي قدِّم أصولًا قليلةً للتطبيق إذا كان اتصال الشبكة بطيئ. أيضًا وفِّر ميزة للاختيار ما بين الأصول عالية الجودة و منخفضة الجودة. الخلاصة يعد إعلام المستخدم بحالة التطبيق وبياناته مفتاحًا لتجربة المستخدم غير المتصلة بالشبكة. حاول إنشاء ارتباطات بأشياء مألوفة، مثلًا اجعل تجربة مشاهدة البيانات دون اتصال نفس التجربة المألوفة التنزيل للاستخدام لاحقًا. تذكر هذه الإرشادات عند تصميم تجربة المستخدم للشبكات غير المستقرة: فكر في كيفية التصميم في حالة نجاح الاتصال بالشبكة وحالة فشل الاتصال وأيضًا عدم استقراره. إذا كانت تكلفة نقل أو تخزين البيانات مرتفعة في تطبيقك، يجب مراعاة إعلام المستخدم بذلك. بالنسبة لمعظم المستخدمين على مستوى العالم، فإن البيئة الفنية لاتصال الشبكة (مثلًا مكان المستخدم بالنسبة للموجِّه أو الرواتر) تكاد تكون متنقلة بحتًا. الأجهزة القديمة ذات الأداء المنخفض، بالإضافة لسعة تخزين وذاكرة وقدرة معالجة محدودة، وشاشات صغيرة بجودة قليلة، فتأكد أن يكون مراعاة الأداء على مثل هذه الأجهزة جزء من تصميمك. اسمح للمستخدمين بتصفح تطبيقك عندما يكونوا في وضع عدم اتصال. أعلِم المستخدمين بوضعهم الحالي وتَغيُّر حالتهم. حاول توفير وضع عدم الاتصال افتراضيًا إذا كان تطبيقك لا يحتاج بيانات كثيرة. إذا كان تطبيقك يعالج كمية كبيرة من البيانات، فأعلِم المستخدمين حول كيفية تنزيل البيانات للاستخدام دون اتصال. اجعل المستخدم قادر على التنقل بين الأجهزة مع المحافظة على نفس التجارب بينها لنفس التطبيق. استخدم كلًا من اللغة والأيقونات والتمثيل والكتابة واللون للتعبير عن الأفكار للمستخدم. قدم تشجيع وتطمين وردود أفعال لمساعدة المستخدم في فهم حالات التطبيق المختلفة. ترجمة -وبتصرف- للمقال Offline UX design guidelines من موقع web.dev. اقرأ أيضًا ما هي تطبيقات الويب التقدمية PWA مفهوم Service Worker وتأثيره في أداء وبنية مواقع وتطبيقات الويب مشاركة تطبيقات الويب التقدمية البيانات عبر الواجهة Web Share إضافة اختصارات للمهام الشائعة في تطبيقات الويب التقدمية PWA
-
- pwa
- تطبيق ويب تقدمي
-
(و 1 أكثر)
موسوم في:
-
يمكن لتطبيقات الويب استخدام نفس إمكانيات المشاركة التي توفرها التطبيقات المثبتة على نظام التشغيل platform-specific، وذلك باستخدام الواجهة البرمجية Web Share التي تعمل في الخلفية Web Share API، إذ تتيح هذه الواجهة لتطبيقات الويب إمكانية مشاركة الروابط والنصوص والملفات مع التطبيقات الأخرى المثبتة على الجهاز كما التطبيقات المخصوصة بالنظام (تطبيقات النظام الأصلية أو التطبيقات المثبتة في النظام). مشاركة البيانات في تطبيقات الويب ليس كل شيء، إذ يمكن أن تشاركها التطبيقات الأخرى المحتوى، بمعنى أن بإمكان تطبيقات الويب تلقي البيانات والروابط والنصوص والملفات من التطبيقات المخصوصة بالنظام أو تطبيقات الويب الأخرى. المفاهيم والاستخدامات تتيح الواجهة البرمجية Web Share مشاركة البيانات في تطبيقات الويب ضمن قدرات استخدامها ومحدودياتها، موضّح فيما يلي هذه القدرات كما مذكور أيضًا طريقة استخدام هذه الواجهة في مشاركة محتوى تطبيقات الويب. القدرات والمحدوديّات تمتلك الواجهة البرمجية Web Share القدرات والمحدوديّات التالية: يمكن استخدامها فقط في المواقع الآمنة، التي يُوصل إليها عبر بروتوكول HTTPS. يجب أن تُستدعى استجابةً لإجراء مستخدم مثل النقر click، إذ لا يمكن استدعائها من خلال معالج التحميل onload. يمكن بواسطتها مشاركة عناوين URL أو نصوص أو ملفات. أصبحت متاحة على متصفحات Safari و Android و Chrome OS و Chrome على Windows، اعتبارًا من يناير كانون الأول 2021، أما إتاحتها في Chrome على MacOS لا تزال قيد التطوير، اطلع على MDN للحصول على التفاصيل. مُنتقي وُجْهة المشاركة على مستوى النظام مع تطبيق ويب تقدمي PWA مثبت كخيار للمشاركة إليه. مشاركة الروابط والنصوص إن أردت مشاركة الروابط والنصوص عبر الواجهة البرمجية Web Share، فاستخدم الدالة ()share، وهي دالة تعتمد على الوعد promise مع كائن خصائص properties مطلوب، ولمنع المتصفح من رمي خطأ TypeError، إذ يجب أن يحتوي ذلك الكائن على واحدة على الأقل من الخاصيات التالية: title أوtext أوURL أوfiles، مثلًا يمكنك مشاركة نص بدون عنوان URL أو العكس، وإتاحة إمكانية مشاركة الخصائص الثلاثة يؤدي إلى زيادة مرونة حالات الاستخدام. تخيل أنه بعد تشغيل الشيفرة التالية، اختار المستخدم تطبيق بريد إلكتروني كوُجْهة مشاركة، فحينها يصبح المعامل title هو موضوع رسالة subject والمعامل text هو نص الرسالة والمعامل files يصبح هو مرفقات الرسالة. if (navigator.share) { navigator.share({ title: 'web.dev', text: 'Check out web.dev.', url: 'https://web.dev/', }) .then(() => console.log('Successful share')) .catch((error) => console.log('Error sharing', error)); } إذا حوى موقعك على عناوين URL متعددة لنفس المحتوى، فشارِك عنوان URL الأساسي للصفحة بدلاً من عنوان URL الحالي، أي بدلاً من مشاركة document.location.href؛ يمكنك إيجاد عنوان URL الأساسي في وسم <meta> ضمن <head> الصفحة ومشاركته، فهذا سيوفر تجربة أفضل للمستخدم، ليس فقط على صعيد تجنب عمليات إعادة التوجيه، ولكنه يضمن أيضًا أن عنوان URL المشارك يخدم تجربة المستخدم الصحيحة لعميل معين، مثلًا إذا شاركك صديقك عنوان URL من هاتفه المحمول وفتحته عبر حاسوبك المكتبي، فيجب أن ترى إصدار سطح المكتب من الموقع او التطبيق المُشارك: let url = document.location.href; const canonicalElement = document.querySelector('link[rel=canonical]'); if (canonicalElement !== null) { url = canonicalElement.href; } navigator.share({url}); مشاركة الملفات إذا أردت إتاحة ميزة مشاركة الملفات في تطبيقك، اختبر أولًا إمكانية المشاركة عبر استدعاء التابع ()navigator.canShare، ثم ضمِّن مجموعة من الملفات في استدعاء التابع ()navigator.share: if (navigator.canShare && navigator.canShare({ files: filesArray })) { navigator.share({ files: filesArray, title: 'Vacation Pictures', text: 'Photos from September 27 to October 14.', }) .then(() => console.log('Share was successful.')) .catch((error) => console.log('Sharing failed', error)); } else { console.log(`Your system doesn't support sharing files.`); } لاحظ أن المثال يعالج الكشف عن ميزة إمكانية المشاركة عبر التابع ()navigator.canShare بدلاً من التابع ()navigator.share. كائن البيانات الممرر للدالة ()canShare في هذا المثال يدعم خاصية files فقط، لكن يمكن مشاركة ملفات الصور والفيديو والصوت والنص. (راجع امتدادات الملفات المسموح بمشاركتها حاليًا في Chromium). دراسة حالة لبرنامج Santa Tracker يعد Santa Tracker مشروعًا مفتوح المصدر، وهو تطبيق ترفيهي سنوي تحت عنوان عيد الميلاد أطلقته Google، يتيح للمستخدمين اللعب ومشاهدة وتعلم الأنشطة الصغيرة التي تُضاف يوميًا من بداية ديسمبر كل عام، كما يتيح تعقب السانتا (بابا نويل) أثناء عشية عيد الميلاد. في عام 2016، استخدم فريق Santa Tracker الواجهة البرمجية Web Share على منصة Android، وكانت هذه الواجهة مناسبة تمامًا لتطبيقات الهاتف المحمول، على عكس الأعوام السابقة التي أزال الفريق فيها أزرار المشاركة من نسخة الهاتف المحمول لأن مساحة تلك الأزرار كانت كبيرة في الشاشة، ولم تكن قادرة على إثبات وجود العديد من التطبيقات لمشاركتها المحتوى. لكن باستخدام الواجهة البرمجية Web Share تمكن فريق Santa Tracker من تقديم زر مشاركة واحد ويؤدي الغرض، مما يوفر مساحة ثمينة في الشاشة. توجه إلى Santa Tracker لمشاهدة فعاليِّة مشاركة الويب. زر المشاركة في تطبيق Santa Tracker. دعم المتصفحات هناك فروقات دقيقة في دعم المتصفحات للواجهة البرمجية Web Share، ويوصَى باستخدام استراتيجية اكتشاف الميزات (كما هو موضح في نماذج الشيفرات السابقة) بدلاً من افتراض دعم دالة معينة. دعم متصفحَي Safari و Chrome استخدام الواجهة Web Share لمشاركة العناوين والنصوص وعناوين URL بداية عام 2021 بالإصدارات التالية: Safari الإصدار 12 أو أحدث على macOS و iOS. Chrome الإصدار 75 أو أحدث على Android، والإصدار 89 أو أحدث على Chrome OS و Windows. كما دعم المتصفحان استخدام الواجهة Web Share لمشاركة الملفات أيضًا، وذلك بالإصدارات التالية: Safari الإصدار أو أحدث على macOS وiOS. Chrome الإصدار 75 أو أحدث على Android، والإصدار 89 أو أحدث على Chrome OS و Windows. تتمتع معظم المتصفحات المبنية على متصفح Chromium، مثل Edge، بنفس الدعم لميزة المشاركة مثل الإصدار المقابل من Chrome. ترجمة -وبتصرف- للمقال Integrate with the OS sharing UI with the Web Share API من موقع web.dev اقرأ أيضًا استقبال تطبيقات الويب التقدمية البيانات المشاركة عبر الواجهة البرمجية Web Share Target ما هي تطبيقات الويب التقدمية PWA جعل تطبيق الويب التقدمي PWA يبدو كتطبيق أساسي في نظام التشغيل إضافة اختصارات للمهام الشائعة في تطبيقات الويب التقدمية PWA
-
- pwa
- تطبيق ويب تقدمي
-
(و 1 أكثر)
موسوم في:
-
يُشارك المحتوى مباشرةً في الأجهزة المحمولة وأجهزة سطح المكتب، بالنقر على زر "مشاركة" واختيار أحد التطبيقات المراد مشاركة البيانات معها. مثلًا قد ترغب بمشاركة مقال مثير للاهتمام، إما عبر إرساله كرسالة بريد إلكتروني إلى أصدقاءك أو نشره في مجتمعك على تويتر أو مشاركته مع أي تطبيق آخر متاح لك المشاركة إليه في جهازك. كان فقط بإمكان التطبيقات المثبتة على نظام التشغيل platform-specific التسجيل في نظام التشغيل لاستقبال البيانات المشاركة من التطبيقات المثبتة الأخرى، لكن باستخدام الواجهة البرمجية Web Share Target (وُجهة مشاركة الويب) التي تعمل في الخلفية Web share Target API، أصبح بإمكان تطبيقات الويب المثبتة على الجهاز أيضًا التسجيل مع نظام التشغيل كهدف مشاركة لاستقبال بيانات من التطبيقات الأخرى. إمكانية استقبال تطبيقات الويب البيانات المُشاركة يُعد مكمّلًا لإمكانية تطبيقات أخرى على مشاركة البيانات، يمكنك مراجعة مقال مشاركة البيانات عبر الواجهة Web Share في تطبيقات الويب التقدمية. تجريب الواجهة البرمجية Web Share Target إن أردت تجريب الواجهة البرمجة Web Share Target، اتبع الخطوات التالية: افتح العرض التوضيحي لهذه الواجهة Web Share Target Demo وذلك على متصفح Chrome، بالإصدار 76 فما فوق على نظام أندرويد، أو 89 فما فوق على سطح المكتب. انقر زر المشاركة في العرض التوضيحي. اختر أحد التطبيقات الظاهرة في مُنتقي جهات المشاركة كوجْهة للمشاركة. بعد إتمامك عملية المُشاركة، ستُشاهد النص المُشارَك في التطبيق الذي اخترته. منتقي وجهة المشاركة على مستوى النظام. تسجيل التطبيق كهدف مشاركة يجب أن يستوفي تطبيقك معايير Chrome الخاصة بقابلية تثبيت التطبيقات لتسجيله كوجهة مشاركة، أيضًا حتى يتمكن المستخدم من مشاركة محتوى ما إلى تطبيقك، فيجب أن يكون تطبيقك مثبتًا في شاشته الرئيسية، فهذا يمنع المواقع من إضافة نفسها عشوائيًا إلى منتقي وُجهات المشاركة Share Target Picker المتاح للمستخدم مشاركة المحتوى إليها. حدِّث ملف بيان موارد تطبيق الويب لتسجيل تطبيقك كهدف مشاركة، أضف الخاصية share_target إلى ملف بيانه manifest، فهي تخبر نظام التشغيل بتضمين تطبيقك كخيار في منتقي وُجْهات المشاركة، وبشكل عام ما تضيفه إلى الخاصية share_target يضبط البيانات التي سيقبل تطبيقك أن تشارُكها. هناك ثلاثة طرق شائعة لضبط الخاصية share_target: قبول محتوى المشاركة الأساسي. قبول بيانات مشاركة تُحْدِث تغييرات في التطبيق. قبول تشارُك الملفات. قبول محتوى المشاركة الأساسي إذا أردت لتطبيقك المحدد كوُجْهة مشاركة أن يقبل فقط محتوى المشاركة الأساسي مثل البيانات والروابط والنصوص، فأضف ما يلي إلى ملف بيانه manifest.json: "share_target": { "action": "/share-target/", "method": "GET", "params": { "title": "title", "text": "text", "url": "url" } } إذا كان تطبيقك يتيح المشاركة بواسطة بروتوكول عنوان URL Scheme، وكان يستخدم القيمة body للحقل text بدلاً من القيمة text، يمكنك استبدال text": "text"" في الشيفرة السابقة بالقيمة "text": "body". النوع الافتراضي لنوع طلبية http والمعبر عنه بالحقل method هو "GET". يشير حقل enctype غير الظاهر في الشيفرة السابقة إلى نوع تشفير البيانات، يعيَّن هذا الحقل بالقيمة "application / x-www-form-urlencoded" افتراضيًا عندما تكون الطريقة هي "GET". قبول بيانات مشاركة تُحْدِث تغييرات في التطبيق إذا كانت البيانات المشاركة تُحدِثْ تغييرات في التطبيق الوجْهة بطريقة ما، مثلًا تحفظ إشارات مرجعية فيه، وقتها عيّن الحقل method بالنوع "POST" واضبط الحقل enctype. يُنشئ المثال التالي إشارة مرجعية في التطبيق الوُجْهة، لذا فإنه يستخدم "POST" للحقل method و القيمة "multart/form-data" للحقل enctype: { "name": "Bookmark", "share_target": { "action": "/bookmark", "method": "POST", "enctype": "multipart/form-data", "params": { "url": "link" } } } قبول مشاركة الملفات كما هو الحال مع البيانات المُشاركة التي تُحْدِث تغييرات في التطبيق، يتطلب قبول مشاركة الملفات أن تكون طريقة المشاركة method هي "POST" وأن يكون نوع enctype هو "multipart/form-data"، ويجب إضافة الحقل files، التي تمثل مصفوفة أنواع الملفات التي يقبلها تطبيقك، عناصر المصفوفة عبارة عن كائنات مكونة من حقلين: name وaccept. يمثل حقل accept نوع MIME للملف أو إمتداده أو مصفوفة تحتوي كليهما. يفضّل توفير مصفوفة تتضمن كلاً من نوع MIME وامتداد الملف لاختلاف أنظمة التشغيل في ما تفضِّل منهما. { "name": "Aggregator", "share_target": { "action": "/cgi-bin/aggregate", "method": "POST", "enctype": "multipart/form-data", "params": { "title": "name", "text": "description", "url": "link", "files": [ { "name": "records", "accept": ["text/csv", ".csv"] }, { "name": "graphs", "accept": "image/svg+xml" } ] } } } معالجة المحتوى المشارك طريقة التعامل مع البيانات المُشارَكة أمر متروك لك ويعتمد على تطبيقك، مثلًا: يمكن لتطبيق البريد الإلكتروني، صياغة بريد إلكتروني جديد من المحتوى المشارَك، باستخدام العنوان title كموضوع للبريد الإلكتروني، والنص المُشارك text والرابط url متسلسلَين معًا كجسم لرسالة البريد. يمكن لتطبيق تواصل إجتماعي صياغة منشور جديد من المحتوى المشارَك، بتجاهل العنوان title، والاستفادة من text كنص للمنشور، وإضافة url كرابط للمنشور أو حتى كنص أساسي وذلك إذا كان text مفقود، وإذا كان url مفقودًا، فقد يفحص التطبيق النص text بحثًا عن أي عنوان URL ويضيفه كرابط. يمكن لتطبيق مشاركة الصور إنشاء عرض شرائح جديد من المحتوى المشارَك، باستخدام العنوان title كعنوان لعرض الشرائح و النص text كوصف للعرض والملفات files (ملفات الصور) كشرائح تتحرك في العرض. يمكن لتطبيق الرسائل النصية صياغة رسالة جديدة من المحتوى المشارَك وذلك باستخدام النص text وurl متسلسلَين معًا كنص للرسالة وتجاهل العنوان title. معالجة مشاركات GET إذا اختار المستخدم تطبيقك كوُجْهة مشاركة، وكانت طريقة المشاركة method في تطبيقك هي "GET" (الافتراضية)، يفتح المتصفح نافذة جديدة على عنوان URL بقيمة الحقل action ضمن الخاصية share_target، ويولِّد المتصفح بعدها استعلامًا نصيًا بسيطًا، مثلًا إذا كان التطبيق المشارِك يوفر عنوان title ونص text بقيم معينة، فإن سلسلة الاستعلام التي يولّدها المتصفح ستكون ?title=hello&text=world. ولمعالجة هذا الاستعلام، استخدم مستمع الحدث DOMContentLoaded (يحصل هذا الحدث عند الانتهاء من تحميل المستند وبناء DOM) في الواجهة الأمامية foreground وحلِّل نص الاستعلام: window.addEventListener('DOMContentLoaded', () => { const parsedUrl = new URL(window.location); // searchParams.get() will properly handle decoding the values. console.log('Title shared: ' + parsedUrl.searchParams.get('title')); console.log('Text shared: ' + parsedUrl.searchParams.get('text')); console.log('URL shared: ' + parsedUrl.searchParams.get('url')); }); تأكد من استخدام عامل الخدمة لتخزين صفحة الإجراء action مؤقتًا بحيث تُحمَّل بسرعة وتعمل بموثوقية، حتى إن لم يكن المستخدم متصلًا بالشبكة. يمكن أن تساعدك الأداة Workbox في تنفيذ تخزين مؤقت مسبق preaching بالاستفادة من عامل خدمة تطبيقك. معالجة مشاركات POST إذا كانت طريقة المشاركة method في تطبيقك هي "POST"، أي كان تطبيقك يقبل بيانات مُشاركة تُحْدِث تغييرات فيه أو يقبل تشارُك ملفات، فسيحتوي جسم طلب POST الوارد على البيانات التي مرَّرها التطبيق المُشارِك، والمشفرة باستخدام قيمة enctype المعيّنة في ملف البيان. لا يمكن للواجهة الأمامية معالجة هذه البيانات مباشرة، بل تعاملها كطلب تمرره إلى عامل خدمة تطبيقك، الذي بواسطته يمكنك اعتراضها ومعالجتها باستخدام مستمع الحدث fetch. self.addEventListener('fetch', event => { const url = new URL(event.request.url); // If this is an incoming POST request for the // registered "action" URL, respond to it. if (event.request.method === 'POST' && url.pathname === '/bookmark') { event.respondWith((async () => { const formData = await event.request.formData(); const link = formData.get('link') || ''; const responseUrl = await saveBookmark(link); return Response.redirect(responseUrl, 303); })()); } }); التحقق من المحتوى المُشارَك تحقق من معالجة البيانات المُشاركة، فللأسف ليس هناك ما يضمن أن التطبيقات الوجهة ستشارك المحتوى المخصص في المكان المناسب بناء على معاملاته (معاملات المحتوى المشارك). مثلًا يبقى حقل الرابط url فارغًا في نظام Android كما ترى في الصورة التالية، كونه غير مدعوم في نظام مشاركة Android، فغالبًا ما تظهر عناوين URL في حقل النص، أو أحيانًا في حقل العنوان. معالجة المحتوى المشارَك في التطبيق الوجهة. دعم المتصفحات دعَم متصفحَي Chrome وEdge الواجهة البرمجية Web Share Target اعتبارًا من أوائل عام 2021 على النحو التالي: الإصدار 76 فما فوق من كلا المتصفحين (Chrome وEdge) على نظام Android الإصدار 89 فما فوق من Chrome على نظام Chrome OS. تذكر بالنسبة لجميع أنظمة التشغيل الأساسية، أنه يجب تثبيت تطبيق الويب حتى يظهر كهدف محتمل لاستقبال البيانات المُشاركة. ترجمة -وبتصرف- للمقال Receiving shared data with the Web Share Target API من موقع web.dev اقرأ أيضًا ما هي تطبيقات الويب التقدمية PWA ميزات تطبيق الويب التقدمي مشاركة تطبيقات الويب التقدمية البيانات عبر الواجهة Web Share إضافة اختصارات للمهام الشائعة في تطبيقات الويب التقدمية PWA مفهوم Service Worker وتأثيره في أداء وبنية مواقع وتطبيقات الويب
-
- pwa
- تطبيق ويب تقدمي
-
(و 1 أكثر)
موسوم في:
-
يدعم نظام الويب الآن ميزة اختصارات التطبيقات، وذلك لتسهيل إنجاز مهام التطبيق الرئيسية، فهذه الاختصارات تسمح لمطوري الويب بتوفير وصول سريع إلى أهم الإجراءات الشائعة التي يحتاجها مستخدمو التطبيق كثيرًا. ستتعلم من هذا المقال كيفية تعيين اختصارات للتطبيقات، مع بعضٍ من أفضل الممارسات الممكن اتباعها لرفع مستوى هذه الاختصارات. نبذة عن اختصارات التطبيق تساعد اختصارات تطبيقك المستخدمين على إجراء المهام الشائعة بسرعة في تطبيقاتك، ويؤدي الوصول السهل إلى هذه المهام من أي مكان توجد فيه أيقونة التطبيق إلى زيادة تفاعل المستخدمين مع تطبيقك. تُستدعى قائمة اختصارات التطبيق عن طريق النقر بزر الفأرة الأيمن على أيقونة التطبيق في شريط مهام نظام Windows أو شريط Dock في نظام macOS لبيئة سطح المكتب، أو الضغط لفترة طويلة على أيقونة مشغل التطبيق في نظام Android. قائمة اختصارات التطبيق في Android. قائمة اختصارات التطبيق في Windows. تظهر قائمة اختصارات التطبيقات فقط لتطبيقات الويب التقدمية PWAs المثبتة على سطح مكتب أو جهاز المستخدم المحمول. يعبر كل اختصار من اختصارات تطبيقك عن إجراء ينوي المستخدم فعله، وكل منها مرتبط بعنوان URL ضمن مجال التطبيق، يُفتح هذا العنوان عندما ينقر المستخدمون على الاختصار المعنِي. وفيما يلي أمثلة على الإجراءات المحتملة لاختصارات التطبيق: صفحات التطبيق الرئيسية (الوصول إلى صفحة التطبيق الرئيسية أو صفحة آخر الطلبات مثلًا) مهام البحث في التطبيق مهام إدخال البيانات (مثلًا كتابة رسالة بريد أو منشور) الأنشطة (مثلًا بدء محادثة مع جهات الاتصال الشائعة) تحديد اختصارات التطبيق في بيان تطبيق الويب manifest تُحدَّد اختصارات التطبيقات اختياريًا في ملف بيان موارد تطبيق الويب manifest، والذي هو عبارة عن ملف JSON يخبر المتصفح عن تطبيق الويب التقدمي PWA وكيف يجب أن يتصرف عند تثبيته على حاسوب المستخدم المكتبي أو جهازه المحمول، وتُعرَّف اختصارات التطبيقات في مصفوفة shortcuts بالتحديد ضمن هذا الملف كما في المثال التالي: { "name": "Player FM", "start_url": "https://player.fm?utm_source=homescreen", … "shortcuts": [ { "name": "Open Play Later", "short_name": "Play Later", "description": "View the list of podcasts you saved for later", "url": "/play-later?utm_source=homescreen", "icons": [{ "src": "/icons/play-later.png", "sizes": "192x192" }] }, { "name": "View Subscriptions", "short_name": "Subscriptions", "description": "View the list of podcasts you listen to", "url": "/subscriptions?utm_source=homescreen", "icons": [{ "src": "/icons/subscriptions.png", "sizes": "192x192" }] } ] } كل عضو في مصفوفة الاختصارات shortcuts هو كائن اختصار يتكون على الأقل من حقل الاسم وعنوان url، أما الحقول الأخرى فهي اختيارية. حقل الاسم name: التسمية الممكن للمستخدمين قراءتها لاختصار التطبيق. حقل الاسم المختصر short_name (اختياري): التسمية الممكن للمستخدمين قراءتها لاختصار التطبيق عندما تكون المساحة محدودة، ويوصى بتعيين هذا الحقل حتى لو كان اختياريًا. حقل الوصف description (اختياري): الغرض من اختصار التطبيق. حقل عنوان URL: عنوان URL الذي يُفتح عندما ينقر المستخدم على اختصار التطبيق، ويجب أن يكون عنوان URL هذا موجودًا في نطاق بيان تطبيق الويب. حقل الأيقونات icons (اختياري): مصفوفة من كائنات الصور، يتكون كل كائن منها خاصية src وsize بشكل أساسي، بالإضافة للخاصية الإختيارية type. إذا كنت تريد تعيين أيقونات مثالية الحجم، فاجعلها بالحجم الموصى به 48dp، والذي يتوافق مع كثافات القياسات التالية بوحدة البكسل بناء على معادلة التحويل التالية: px = dp * (dpi / 160) في الشاشات منخفضة الكثافة ldpi والتي تكافئ 120 dpi، الحجم 48dp يساوي 48*0.75=36px في الشاشات متوسطة الكثافة mdpi والتي تكافئ 160 dpi، الحجم 48dp يساوي 48px (نفسه)، أي يكون حجم الأيقونة 48x48 بكسل في الشاشات عالية الكثافة hdpi والتي تكافئ 240 dpi، الحجم 48dp يساوي 48*1.5=72px، أي يكون حجم الأيقونة 72x72 بكسل في الشاشات عالية الكثافة جدًا xhdpi والتي تكافئ 320 dpi، الحجم 48dp يساوي 48*2=96px، أي يكون حجم الأيقونة 96x96 بكسل في الشاشات عالية الكثافة جدًا جدًا xxhdpi والتي تكافئ 480 dpi، الحجم 48dp يساوي 48*3=144px، أي يكون حجم الأيقونة 144x144 بكسل في الشاشات عالية الكثافة جدًا جدًا جدًا xxxhdpi والتي تكافئ 640 dpi، الحجم 48dp يساوي 48*4=192px، أي يكون حجم الأيقونة 192x192 بكسل اختبر اختصارات تطبيقك للتحقق من تعيين اختصارات التطبيق بشكل صحيح، استخدم قائمة Manifest في لوحة Application في أدوات المطور DevTools. تظهر الصورة اختصارات التطبيقات في DevTools توفر هذه القائمة إمكانية معاينة عدة خصائص محفوظة في ملف البيان، بما في ذلك اختصارات التطبيقات، ما يسهل التحقق من تحميل أيقونات الاختصارات إذا تم تعيينها (كونها اختيارية)، بالشكل الصحيح. أفضل الممارسات فيما يلي نصائح بمارسات يوصى اتباعها لزيادة كفاءة اختصارات تطبيقك. طلب اختصارات التطبيق حسب الأولوية يفضَّل تعيين اختصارات التطبيقات حسب الأولوية، وذلك بوضع اختصارات التطبيقات الأكثر أهمية في بداية مصفوفة الاختصارات shortcuts ضمن ملف mainfest، نظرًا لأن الحد الأقصى لعدد الاختصارات التي تظهر في قائمة اختصارات التطبيق يختلف باختلاف نظام التشغيل. مثلًا يتيح Chrome وEdge حتى 10 اختصارات للتطبيق في نظام Windows، بينما يأخذ Chrome في الاعتبار أول 4 اختصارات للتطبيق فقط في نظام Android، ويسمح متصفح Chrome 92 الآن بثلاث اختصارات للتطبيقات فقط على نظام Android 7 استخدم أسماء اختصارات فريدة للتطبيق يجب استخدام اسم مميز لكل اختصار تطبيق. لا تعتمد على الأيقونات للتمييز بين اختصارات التطبيقات لأنها قد لا تكون ظاهرة على جميع الأجهزة، فنظام macOS مثلًا لا يدعم إظهار أيقونات اختصارات التطبيقات المثبّته في شريط Dock. قياس استخدام اختصارات التطبيق يمكنك تعقب عناوين (الخاصية url) اختصارات تطبيقك لأغراض إحصائية، مثلًا لقياس مدى استخدام مستخدمي تطبيقك تلك الاختصارات. دعم المتصفحات يدعم متصفحَي Chrome وEdge اختصارات تطبيقات الويب التقدمية على النحو التالي: الإصدار 84 فما فوق من Chrome على نظام Android. الإصدار 85 فما فوق من كلا المتصفحين (Chrome وEdge) على نظام Windows. الإصدار 92 فما فوق من Chrome على نظام Chrome OS. الإصدار 96 فما فوق من كلا المتصفحين (Chrome وEdge) على نظامَي MacOS ولينكس Linux. دعم فاعليِّة الويب الموثوق فاعليِّة الويب الموثوق Trusted Web Activity هو طريقة لتغليف تطبيق الويب التقدمي بحاوية container تجعله يتصرف كتطبيقات الهواتف المعيارية. تقرأ الأداة Bubblewrap المستخدمة لإنشاء تطبيقات Android باستخدام فاعليِّة الويب الموثوق، اختصارات تطبيق الويب من ملف بيانه manifest، وتولِّد تلقائيًا الإعدادات المقابلة لها لتطبيق Android. لاحظ عند استخدام Bubblewrap أنه يجب تحديد أيقونات لاختصارات التطبيقات وبحجم لا يقل عن 96×96 بكسل. أداة باني تطبيق الويب التقدمي PWABuilder تحوِّل تطبيق الويب التقدمي بسهولة إلى فاعليِّة ويب موثوق، وتدعم اختصارات التطبيقات. نموذج راجع نموذج اختصارات التطبيق ومصدره. ثبت تطبيق PWA هذا وجرب اختصاراته. ترجمة -وبتصرف- للمقال Get things done quickly with app shortcuts من موقع web.dev اقرأ أيضًا ما هي تطبيقات الويب التقدمية PWA ميزات تطبيق الويب التقدمي جعل تطبيق الويب التقدمي PWA يبدو كتطبيق أساسي في نظام التشغيل التحسين التدريجي لتطبيقات الويب التقدمية PWA شرح ملف البيان manifest لتطبيق الويب التقدمي PWA
-
- pwa
- تطبيق ويب تقدمي
-
(و 1 أكثر)
موسوم في:
-
Jenkins هو خادم أتمتة مفتوح المصدر يعمل على أتمتة المهام الفنية المتكررة التي ينطوي عليها التكامل المستمر وتقديم البرامج، Jenkins مكتوبة بلغة Java ويمكن تثبيتها من حزم أوبنتو أو عن طريق تنزيل وتشغيل ملف WAR (وهو عبارة عن مجموعة من الملفات التي تشكل تطبيق ويب كاملًا لتشغيله على خادم). في هذا المقال ستقوم بتثبيت Jenkins بإضافة مستودع حزمة ديبيان، واستخدام هذا المستودع لتثبيت الحزمة من خلال الأداة apt-get. المتطلبات الأساسية من أجل متابعة هذا المقال، ستحتاج إلى: خادم اوبنتو 18.04 فيه مستخدم يحمل صلاحيات sudo وغير جذري (non-root) ومثبت عليه جدار حماية وذلك باتباع دليل الاعداد الاولي لخادم اوبنتو18.04 نوصي بالبدء باستخدام 1 غيغابايت على الأقل من الذاكرة RAM، راجع هذه التوصيات للحصول على متطلبات التثبيت اللازمة. بتثبيت Java 8، باتباع هذا المقال. الخطوة الأولى: تثبيت Jenkins الإصدار المتوفر في متجر أوبنتو الحزم على الأغلب هو اصدار أقدم من الإصدار المتوفر في المشروع نفسه، للحصول على اخر التحديثات والميزات سوف نستخدم الحزم المصانة من قبل مشروع Jenkins نفسه. علينا أولًا إضافة مفتاح المستودع repository للنظام من خلال الأمر التالي: $ wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add - بعد إضافة المفتاح بنجاح سيظهر النظام لك كلمة OK؛ سنضيف بعد ذلك عنوان مستودع لحزمة ديبيان إلى الملف sources.list الموجود في الخادم: $ sudo sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list' بعد تنفيذ الأمرين السابقين بنجاح سوف نقوم بتحديث الحزم في المستودع repository باستخدام الأمر التالي: $ sudo apt update اخيرا سنقوم بتثبيت Jenkins وكل ما تعتمد عليه: $ sudo apt install jenkins الآن Jenkins واعتماديته كلها جاهزة لذا سنقوم بتشغيل الخادم. الخطوة الثانية: بدء تشغيل خادم Jenkins سنستخدم systemctl لتشغيل خادم Jenkins: $ sudo systemctl start jenkins و لعدم إظهار الأمر لأي مخرجات سنستخدم الخيار Status لعرض حالة الخدمة للتأكد أنها قد بدأت دون مشاكل: $ sudo systemctl status jenkins إذا كان كل شيئ على ما يرام ستبدأ المخرجات بالظهور لإظهار أن الخدمة تعمل حاليا و ستبدأ تلقائيا عند الاقلاع: ● jenkins.service - LSB: Start Jenkins at boot time Loaded: loaded (/etc/init.d/jenkins; generated) Active: active (exited) since Mon 2018-07-09 17:22:08 UTC; 6min ago Docs: man:systemd-sysv-generator(8) Tasks: 0 (limit: 1153) CGroup: /system.slice/jenkins.service الآن الخدمة شغالة سنبدأ بإعداد قواعد جدار الحماية حتى نصل لها من خلال متصفح الويب لإكمال ضبطه. الخطوة الثالثة: اضبط إعدادات جدار الحماية بشكل تلقائي Jenkins يعمل على منفذ رقم 8080 لذلك سنعمل على فتح هذا المنفذ داخل جدار الحماية ufw: $ sudo ufw allow 8080 من خلال التحقق من حالة الجدار الناري يمكن رؤية القاعدة الجديدة ب ufw: $ sudo ufw status يمكنك الآن رؤية التراسل الشبكي عبر المنفذ 8080 المتاح من كل الشبكات: Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere 8080 ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6) 8080 (v6) ALLOW Anywhere (v6) ملاحظة: اذا كان جدار الحماية غير فعال يجب تنفيذ الأوامر التالية للسماح ب OpenSSH وتفعيل جدار الحماية: $ sudo ufw allow OpenSSH $ sudo ufw enable بعد تثبيت Jenkin و ضبط إعدادات جدار الحماية الخاص بنا سنكمل الإعدادات المبدئية له. الخطوة الرابعة: ضبط خدمة Jenkins من أجل ضبط خدمة Jenkins علينا الدخول لصفحة الإعدادات من خلال المنفذ الافتراضي 8080 باستخدام اسم النطاق للخادم أو عنوان http://your_server_ip_or_domain:8080. ستظهر لك شاشة مقفلة من Jenkins تعرض موقع الذي تخزن فيه كلمة المرور المبدئية وتطلب إدخالها. من خلال واجهة سطر الأوامر سوف نستخدم الأمر Cat من أجل اظهار كلمة المرور: $ sudo cat /var/lib/jenkins/secrets/initialAdminPassword تتألف كلمة المرور من 32 حرف سوف نقوم بنسخها من واجهة سطر الأوامر و لصقها داخل الحقل Administrator password ثم الضغط على زر الاستمرار Continue. بعد ذلك ستظهر لك صفحة تعرض لك خيارين تثبيت الإضافات المقترحة أو تحديد إضافات معينة. اختر الخيار Install suggested plugins الذي سيبدأ عملية التنزيل مباشرة. عند الانتهاء من التثبيت، سيُطلب منك أولا إعداد المستخدم الإداري (administrative) ومن الممكن تخطي هذه الخطوة والمتابعة كمسؤول باستخدام كلمة المرور الأولية التي استخدمناها في الأعلى لكننا سنقوم بإنشاء مستخدم جديد. ملاحظة: خادم Jenkins الافتراضي غير مشفر، وبالتالي فإن البيانات المقدمة مع هذا النموذج ليست محمية. عندما تكون جاهزًا لاستخدام هذا التثبيت، اتبع هذا المقال التالي؛ سيؤدي ذلك إلى حماية بيانات المستخدم والمعلومات التي يتم إرسالها عبر واجهة الويب. ادخل اسم و كلمة المرور للمستخدم الخاص بك: ستظهر لك صفحة الضبط Instance Configuration وستطلب منك تأكيد عنوان URL المفضل لنسخة Jenkins. قم بتأكيد اسم النطاق لخادمك أو عنوان IP: بعد تأكيد المعلومات المناسبة، انقر فوق حفظ وإنهاء. سترى صفحة Jenkins is Ready!: انقر فوق Start using Jenkins للانتقال للوحة التحكم الرئيسية لـخادم Jenkins: تهانينا ، لقد تم تثبيت Jenkins بنجاح. الخلاصة في هذا المقال قد شرحنا آلية تثبيت Jenkins باستخدام الحزم المقدمة من المشروع، وبدء تشغيل الخادم، وفتح جدار الحماية، وإنشاء مستخدم مسؤول. في هذه المرحلة، يمكنك البدء في استكشاف Jenkins. عند الانتهاء من الاستكشاف، إذا قررت الاستمرار في استخدام Jenkins فاتبع الدليل التالي لحماية كلمات المرور الخاصة بك وأيضًا معلومات النظام أو المنتج الحساسة التي ستكون إرسالها بين جهازك والخادم بنص عادي. ترجمة –وبتصرف– للمقال How To Install Jenkins on Ubuntu 18.04 لصاحبته Melissa Anderson and Kathleen Juell
-
يعتبر دوكر Docker من أشهر التطبيقات مفتوحة المصدر لإنشاء وإدارة ونشر استنساخ التطبيقات باستخدام الحاويات، فالحاوية هي حزمة تحتوي على متطلبات التطبيق للعمل على مستوى نظام التشغيل أي أن التطبيق المنشور باستخدام Docker يعمل في بيئته الخاصة ويُتعامل مع متطلباته بشكل منفصل. أما Flask فهو إطار ويب مصغّر (micro-framework) مبني باستخدام لغة بايثون، وتعود تسميته بالإطار المصغّر لأنه لا يحتاج إلى أدوات محدّدة أو مكونات إضافيّة لكي يعمل، بالإضافة إلى أنه يتميّز بالخفّة والمرونة وبدرجة عالية من التنظيم مما يجعل المبرمجين يفضلونه على بقية الإطارات. سيسمح لك نشر تطبيق Flask باستخدام Docker باستنساخ التطبيق على مختلف الخوادم بأقل إعدادات ممكنة، لذا سنرى كيفية إنشاء تطبيق Flask و نشره باستخدام Docker بالإضافة إلى كيفية تحديث التطبيق بعد نشره. المتطلبات الأساسية لمتابعة هذا الدرس، ستحتاج إلى ما يلي: مستخدم عادي - ليس جذر - مع صلاحيات sudo معّد باستخدام هذا الدليل خادم أوبنتو 18.04 مع Docker، يمكنك إعداده عن طريق اتباع الخطوات في هذا الدرس أو عن طريق استخدام ميّزة التثبيت الخاصة بـ DigitalOcean في هذا الدرس تثبيت Nginx بإتباع الخطوة الأولى من هذا الدرس الخطوة الأولى - إعداد تطبيق Flask ستحتاج إلى إنشاء هيكل مجلدات تطبيقك، لذلك أنشئ مجلدًا في /var/www وسمّهِ على سبيل المثال TestApp: sudo mkdir /var/www/TestApp ثم انتقل إلى هذا المجلد باستخدام الأمر: cd /var/www/TestApp وبعد ذلك، أنشئ المجلد الرئيسي لتطبيق Flask: sudo mkdir -p app/static app/templates تشير راية -p إلى أن mkdir سينشئ المجلد مع جميع المجلدات الرئيسية غير الموجودة، وفي هذه الحالة، سينشئ mkdir مجلد الأب app حتى يتمكن من إنشاء مجلدات static و templates داخله. سيحتوي المجلد app على جميع الملفات المتعلّقة بتطبيق Flask مثل الواجهات (views) والمخططات الأوليّة (blueprints). فالواجهة هي الشيفرة البرمجيّة التي تكتبها للاستجابة إلى طلبات تطبيقك، أما المخططات الأوليّة فتنشئ مكونات التطبيق وتدعم الأنماط الشائعة داخل التطبيق أو عبر تطبيقات مختلفة. ستضع جميع الأصول مثل الصور وملفات CSS وملفات جافاسكربت في مجلد static ، وأما بالنسبة لقوالب HTML فستضعهم في مجلد templates. والآن، بعد أن أنشأنا هيكل المجلدات الأساسية، فسننشئ الملفات التي نحتاجها لتشغيل تطبيق Flask. أنشئ أولًا ملف __init__.py داخل مجلد app، سيجعل هذا الملف مفسر بايثون يتعامل مع مجلد app كحزمة. أنشئ ملف __init__.py باستخدام الأمر التالي: sudo nano app/__init__.py تسمح لك الحزم في بايثون بجمع الوحدات إلى مساحات أسماء منطقيّة أو تسلّسلات هرميّة لكي تتمكن من تقسيم الشيفرة البرمجيّة إلى كتل منفصلة قابلة للإدارة ذات وظائف معيّنة. بعد ذلك، ستضيف شيفرة برمجيّة إلى __init__.py لإنشاء نسخة من Flask واستدعاء المنطق الموجود في ملف views.py، والذي ستنشئه بعد إنهائك لهذا الملف. أضف الشيفرة البرمجيّة التاليّة إلى الملف الجديد: from flask import Flask app = Flask(__name__) from app import views ستنشئ الآن ملف views.py في مجلد app، سيحتوي هذا الملف على أغلب منطق التطبيق. sudo nano app/views.py بعد ذلك أضف هذه الشيفرة البرمجيّة إلى ملف views.py، ستُعيد هذه الشيفرة البرمجيّة السلسلة النصيّة hello world! إلى زوار صفحة الويب. from app import app @app.route('/') def home(): return "hello world!" يسمى السطر @app.route فوق الدالة بالمزخرِف ويعمل على تعديل الدالة التي بعده، وفي هذه الحالة، سيخبر المزخرِف Flask أي رابط URL ستنفّذ دالة home()، وأما بالنسبة إلى النص hello world المُعاد من الدالة home فسيظهر للمستخدم في المتصفّح. الآن، بعد الانتهاء من ملف views.py، فنحن مستعدين لإنشاء ملف uwsgi.ini الذي سيحتوي على إعدادات uWSGI لتطبيقنا وهذا الأخير عبارة عن خيار نشر لـ Nginx والذي هو خادم لكل من البروتوكول والتطبيق، أي يخدم بروتوكولات uWSGI و FastCGI و HTTP. استخدم الأمر التالي لإنشاء هذا الملف: sudo nano uwsgi.ini بعد ذلك، أضف هذه الإعدادات إلى الملف لإعداد خادم uWSGI: [uwsgi] module = main callable = app master = true تعرّف هذه الشيفرة البرمجية الوحدة التي سيعمل منها تطبيق Flask، والتي هي في هذه الحالة ملف main.py، ولقد أشرنا إليه بـ main. وبالنسبة إلى خيار callable فهو يخبر uWSGI باستخدام نسخة من app المصدّر من التطبيق الرئيسي. ويسمح الخيار master للتطبيق أن يعمل دائمًا ولن يتوقف إلا لبعض الوقت عند إعادة تحميل التطبيق كاملًا. بعد ذلك، أنشئ ملف main.py ليكون مدخل تطبيقك، فالمدّخل يخبر uWSGI عن كيفية التعامل مع تطبيقك. sudo nano main.py والآن، أضف السطر التالي إلى الملف حيث أن هذا الأخير سيستدعي نسخة من Flask يسمى app من حزمة التطبيق التي أنشأناها سابقًا. from app import app وفي النهاية، أنشئ الملف requirements.txt لتحديد الاعتماديات التي سيثبتها مدير الحزم pip إلى نشر Docker الخاص بك: sudo nano requirements.txt أضف السطر التالي في الملف /var/www/TestApp/app/requirements.txt لإضافة Flask كتبعيّة: Flask==1.0.2 يحدد هذا السطر نسخة Flask التي يجب تثبيتها، والتي هي في وقت كتابة هذا المقال، النسخة 1.0.2، ويمكنك التأكد من التحديثات على الموقع الرسمي لـ Flask. والآن لقد أنتهيت من إعداد تطبيق Flask ومستعد لإعداد Docker. الخطوة الثانية - إعداد Docker سننشئ في هذه الخطوة ملفين، Dockerfile و start.sh لإنشاء نشر Docker الخاص بك، فملف Dockerfile هو مستند نصي يحتوي على الأوامر التي تُستخدم لتجميع الصورة (image) وأما بالنسبة لملف start.sh فهو سكربت shell الذي سيبني الصورة وسينشئ الحاوية من ملف Dockerfile. أنشئ أولًا ملف Dockerfile: sudo nano Dockerfile بعد ذلك، أضف إعداداتك التي ترغب بها إلى Dockerfile، ستحدّد هذه الأوامر كيف ترغب ببناء الصورة وكيف ستضاف المتطلبات الإضافيّة. الملف /var/www/TestApp/Dockerfile: FROM tiangolo/uwsgi-nginx-flask:python3.6-alpine3.7 RUN apk --update add bash nano ENV STATIC_URL /static ENV STATIC_PATH /var/www/app/static COPY ./requirements.txt /var/www/requirements.txt RUN pip install -r /var/www/requirements.txt في هذا المثال، سنبني صورة Docker (أي image) من صورة موجودة بالفعل وهي tiangolo/uwsgi-nginx-flask ويمكنك إيجادها على DockerHub وتعتبر هذه الصورة من أفضل الصور الموجودة لأنها تدعم العديد من نسخ بايثون بالإضافة إلى أنظمة التشغيل المختلفة. أول سطرين سيحددان الصورة الرئيسيّة التي ستستخدمها لتشغيل التطبيق وتثبيت معالج أوامر Bash والمحرّر النصي nano، وستثبت أيضًا عميل git للسحب والدفع إلى خدمات استضافة التحكّم بالإصدارات مثل GitHub و GitLab و Bitbucket. أما بالنسبة إلى ENV STATIC_URL /static فهو متغيّر البيئة الخاص بصورة Docker الحالية ويحدد الملف الثابت (static) الذي سيحتوي على جميع الأصول مثل الصور وملفات CSS و ملفات جافا سكربت. بالنسبة لآخر سطرين فستنسخ الملف requirements.txt إلى الحاوية ومن ثم ستثبّت التبعيات. والآن، بعد أن أنهينا ملف Dockerfile، أصبحنا مستعدين لإنشاء ملف start.sh الذي سيبني حاوية Docker، وقبل كتابة سكربت start.sh، تأكد من وجود منفذ (port) مفتوح لاستخدامه في الإعدادات، ويمكنك التأكد من ذلك عن طريق تشغيل الأمر التالي: sudo nc localhost 56733 < /dev/null; echo $? إذا كانت مخرجات الأمر السابق هي 1 فهذا المنفذ مفتوح ويمكن استخدامه، وخلافا ذلك، ستحتاج إلى اختيار منفذ آخر لاستخدامه في ملف إعدادات start.sh. بمجرّد أن تجد منفذ مفتوح للاستخدام، أنشئ سكربت start.sh: sudo nano start.sh سيبني سكربت start.sh ملف Dockerfile وينشئ الحاوية من صورة Docker، ولهذا سنضيف إلى الملف /var/www/TestApp/start.sh الإعدادات التالية: #!/bin/bash app="docker.test" docker build -t ${app} . docker run -d -p 56733:80 \ --name=${app} \ -v $PWD:/app ${app} يسمى السطر الأول بـ shebang، وهو يحدد أن هذا الملف هو ملف bash وستُنفّذ أوامره، وأما السطر الثاني فيحدّد اسم الصورة والحاوية وستُحفظ على شكل متغيّر يسمى app. أما السطر التالي فيخبر Docker ببناء الصور من ملف Dockerfile الموجود في المجلّد الحالي، وفي هذا المثال، سينشئ صورة تسمى docker.test. الأسطر الثلاثة الأخيرة تنشئ حاوية تسمى docker.test والتي تعمل على المنفذ 56733، وفي النهاية، تربط المجلد الحالي بمجلد /var/www في الحاوية. يمكنك استخدام الراية -d لتشغيل الحاوية في وضع العفريت (daemon mode) أو كعملية خلفيّة (background process)، ويمكنك تضمين الراية -p لربط منفذ الخادم إلى منفذ معيّن في حاوية Docker، وفي هذه الحالة ستربط منفذ 59733 بمنّفذ 80 في حاوية Docker. بالنسبة إلى الراية -v (volume) فيحدد مكان Docker لوصله في الحاوية. شغّل سكربت start.sh لإنشاء صورة Docker وبناء حاوية من الصورة: sudo bash start.sh بمجرّد الانتهاء من الأمر السابق، استخدم الأمر التالي لعرض جميع الحاويات التي تعمل: sudo docker ps سيظهر لك شيء مشابه لهذا: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 58b05508f4dd docker.test "/entrypoint.sh /sta…" 12 seconds ago Up 3 seconds 443/tcp, 0.0.0.0:56733->80/tcp docker.test سترى أن حاوية docker.test تعمل، والآن لنزور عنوان IP في المنفذ الذي اخترناه في متصفحك: http://ip-address:56733 ستشاهد صفحة مشابهة لهذه الصفحة: في هذه الخطوة نجحنا في نشر تطبيق Flask على Docker، سنستخدم في الخطوة التالية القوالب لعرض المحتوى إلى المستخدمين. الخطوة الثالثة - خدمة ملفات القوالب القوالب هي ملفات لعرض محتوى ثابت وحيوي لزوار تطبيقك، في هذه الخطوة، سننشئ قالب HTML لإنشاء الصفحة الرئيسية لتطبيقك. إبدأ بإنشاء ملف home.html في مجلد app/templates: sudo nano app/templates/home.html أضف الشيفرة البرمجية إلى قالبك لإنشاء صفحة HTML5 تحتوي على عنوان وبعض النصوص. الملف /var/www/TestApp/app/templates/home.html: <!doctype html> <html lang="en-us"> <head> <meta charset="utf-8"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <title>Welcome home</title> </head> <body> <h1>Home Page</h1> <p>This is the home page of our application.</p> </body> </html> بعد ذلك، عدّل على ملف app/views.py لخدمة الملف المنشئ حديثًا: sudo nano app/views.py أضف أولا السطر التالي إلى بداية ملفك لإستدعاء أسلوب render_template من Flask والذي يرمّز صفحة HTML لتصيير (render) صفحة ويب للمستخدم. from flask import render_template ... أضف مسار (route) آخر في نهاية الملف لتصيير ملف القالب حيث ستحدّد هذه الشيفرة البرمجية أن المستخدمين سيخدمون بمحتوى ملف home.html عند زيارة مسار /template في تطبيقك. ... @app.route('/template') def template(): return render_template('home.html') سيبدو ملف app/views.py بعد تحديثه كالتالي: from flask import render_template from app import app @app.route('/') def home(): return "Hello world!" @app.route('/template') def template(): return render_template('home.html') من أجل تفعيل هذه التغييرات، ستحتاج إلى إيقاف وإعادة تشغيل حاويات Docker. شغّل الأمر التالي لإعادة بناء الحاوية: sudo docker stop docker.test && sudo docker start docker.test زر تطبيقك الآن على http://your-ip-address:56733/template لمشاهدة خدمة القالب الجديد. في هذه الخطوة أنشأت ملف قالب Docker لخدمة زوار تطبيقك وفي الخطوة القادمة سنرى كيف يمكنك تفعيل التغييرات دون الحاجة إلى إعادة تشغيل حاوية Docker. الخطوة الرابعة - تحديث التطبيق ستحتاج في بعض الأحيان إلى القيام ببعض التغييرات في تطبيقك مثل تثبيت متطلبات جديدة، تحديث حاوية Docker أو تغيير شيفرة HTML أو المنطق، لذلك، سنتحدّث الآن عن إعداد touch-reload للقيام بهذه التغييرات دون الحاجة إلى إعادة تشغيل حاوية Docker. يراقب بايثون autoreloading نظام الملفات للتغييرات وتحديث التطبيق عند إيجاد تغيير، وهذه العملية غير منصوح بها عند مرحلة الإنتاج (production) لأنها ستستهلك الكثير من الموارد. سنستخدم touch-reload لمراقبة التغييرات لملف معيّن وإعادة التحميل عند تحديث أو استبدال هذا الملف. لفعل ذلك، نبدأ بفتح ملف uwsgi.ini: sudo nano uwsgi.ini ونضيف هذا السطر الأخير إلى نهاية ملف uwsgi.ini كما هو موضح في الشيفرة التالية: module = main callable = app master = true touch-reload = /app/uwsgi.ini يحدد هذا السطر الملف الذي يجب التعديل عليه لإعادة تحميل كامل التطبيق. لمشاهدة هذا، غيّر قليلا في تطبيقك، فعلى سبيل المثال، افتح ملف app/views.py: sudo nano app/views.py استبدل السلسلة النصيّة التي تعود من الدالة home: from flask import render_template from app import app @app.route('/') def home(): return "<b>There has been a change</b>" @app.route('/template') def template(): return render_template('home.html') والآن، إذا فتحت الصفحة الرئيسية لتطبيقك على http://ip-address:56733 ستلاحظة عدم ظهور التغييرات، وهذا بسبب أن شرط إعادة التحميل هو تغيير ملف uwsgi.ini، لذا استخدم touch لتفعيل هذا الشرط: sudo touch uwsgi.ini أعد تحميل صفحة الرئيسية في تطبيقك على متصفحك مرّة أخرى، ستظهر لك التغييرات: في هذه الخطوة، أعددت شرط touch-reload لتحديث تطبيقك بعد القيام بتغييرات. الخلاصة في هذا الدرس، أنشأت ونشرت تطبيق Flask على حاوية Docker، ولقد أعددّت touch-reload لإعادة تحميل التطبيق دون الحاجة إلى إعادة تشغيل الحاوية. يمكنك الآن التوسع أكثر واكتشاف Docker بطريقة معمّقة، ويمكنك البدء بالتوثيق الرسمي ترجمة -وبتصرف- للمقال How To Build and Deploy a Flask Application Using Docker on Ubuntu 18.04 لصاحبه Michael Okoh
-
مع انتقال أدوات المطوّرين إلى «السحابة» (cloud)، يتزايد إنشاء واعتماد منصّات بيئة التطوير المتكاملة (IDE) السحابية. تتيح منصّات Cloud IDE التعاون في الوقت الحقيقي بين فرق المطوّرين من أجل العمل في بيئة تطوير موحّدة ما من شأنه التقليل من حالات عدم التوافق وتحسين الإنتاجية. حيث يمكن الوصول إليها من خلال متصفحات الويب، وتتوفر Cloud IDEs من كل أنواع الأجهزة الحديثة. إن code-server هو نسخة Microsoft Visual Studio Code تعمل على خادم بعيد ويمكن الوصول إليها مباشرة من متصفحك. المحرر Visual Studio Code هو محرّر تعليمات برمجيّة حديث مع دعم متكامل لـ Git ومنقّح شيفرة وإكمال تلقائي ذكي وميزات أخرى قابلة للتخصيص والتوسيع. يعني هذا أنه سيتوفر لديك دائمًا بيئة تطوير متناسقة حتى مع استخدامك لأجهزة متنوعة تعمل بأنظمة تشغيل مختلفة. ستقوم في هذه الدليل بإعداد منصّة code-server cloud IDE على جهاز يعمل بنظام تشغيل أبونتو 18.04 وإتاحتها على «اسم النطاق» (domain) خاصتك، وسيكون محميّ بشهادات TLS Let's Encrypt المجانيّة. وسيكون لديك في النهاية نسخة Microsoft Visual Studio Code تعمل على خادم Ubuntu 18.04 الخاص بك ومتاحةً على اسم نطاقك ومحميّة بكلمة مرور. المتطلبات المُسبقة خادم يعمل بنظام تشغيل Ubuntu 18.04 على الأقل مع ذاكرة وصول عشوائي (RAM) سعة 2 جيجا بايت، وامتيازات «وصول المستخدم الجذر» (root access)، وحساب sudo «عادي» (non-root). يمكنك إعداد ذلك باتباع هذا الدليل للإعداد الأولي للخادم. نسخة Nginx مُثبتة على خادمك. للحصول على دليل حول كيفيّة القيام بذلك، أكمل الخطوات من 1 إلى 4 حول كيفيّة تثبيت Nginx على Ubuntu 18.04. كلًّا من سجلي DNS التاليين المُعدّين لخادمك. يمكنك متابعة هذه المقدمة إلى DigitalOcean DNS للحصول على تفاصيل حول كيفية إضافتها. سجل لـyour-domain يشير إلى عنوان IP العام للخادم الخاص بك. سجل لـwww.your-domain يشير إلى عنوان IP العام لخادمك. اسم نطاق domain مسجّل بالكامل لاستضافة code-server يُشير إلى خادمك الخاص. سيَستخدم هذا الدليل code-server.your-domain في كامل المقال. يمكنك شراء اسم نطاق من Namecheap أو الحصول على واحد مجانًا عبر Freenom أو استخدام أي موقع تسجيل نطاقات تختاره. الخطوة 1: تثبيت code-server ستقوم في هذا القسم بإعداد code-server على خادمك الخاص. سيستلزم ذلك تنزيل أحدث إصدار وإنشاء خدمة systemd والتي من شأنها أن تُبقي code-server يعمل دائمًا في الخلفية. ستحدد أيضًا سياسة إعادة التشغيل للخدمة، بحيث يبقى code-server متاحًا بعد الأعطال أو إعادات التشغيل المحتملة. سنُخزن جميع البيانات المتعلقة بـ code-server في مجلد باسم ~/code-server أنشئه عن طريق تنفيذ الأمر التالي: mkdir ~/code-server انتقل إليه: cd ~/code-server ستحتاج إلى الانتقال إلى صفحة إصدارات Github لـ code-server واختيار أحدث إصدار من Linux (سيحتوي اسم الملف على الكلمة "linux"). كان الإصدار الأخير عند كتابة هذا التقرير هو 2.1692. نزّله باستخدامwgetعن طريق تشغيل الأمر التالي: wget https://github.com/cdr/code-server/releases/download/2.1692-vsc1.39.2/code-server2.1692-vsc1.39.2-linux-x86_64.tar.gz فكّ بعد ذلك ضغط الأرشيف من خلال تشغيل الأمر التالي: tar -xzvf code-server2.1692-vsc1.39.2-linux-x86_64.tar.gz ستحصل على مجلد باسم الملف الأصلي الذي قمت بتنزيله والذي يحتوي على ملف الـ code-server القابل للتنفيذ. انتقل إليه: cd code-server2.1692-vsc1.39.2-linux-x86_64 انسخ ملف code-server التنفيذي إلى المسار /usr/local/bin حتى تتمكن من الوصول إليه عبر كامل نطاق النظام عن طريق تشغيل الأمر التالي: sudo cp code-server /usr/local/bin أنشئ بعد ذلك مجلد لـ code-server حيث ستُخزين بيانات المستخدم: sudo mkdir /var/lib/code-server الآن وبعد تنزيلك لـ code-server وجعله متاحًا على مستوى النظام، ستُنشئ خدمة systemd لإبقاء code-server يعمل في الخلفيّة بشكل دائم. ستخزّن ضبط الخدمة في ملف باسم code-server.service، في المسار/lib/systemd/system حيث يخزن systemd خدماته. أنشئه باستخدام محرّر نصوصك الخاص: sudo nano /lib/systemd/system/code-server.service أضف الأسطر التالية في الملف /lib/systemd/system/code-server.service: [Unit] Description=code-server After=nginx.service [Service] Type=simple Environment=PASSWORD=your_password ExecStart=/usr/local/bin/code-server --host 127.0.0.1 --user-data-dir /var/lib/code-server --auth password Restart=always [Install] WantedBy=multi-user.target يتوجب عليك هنا أولًا تحديد وصفٍ للخدمة. تذكر بعد ذلك أنّ خدمة nginx يجب أن تُشغّل قبل هذه الخدمة. يمكنك بعد القسم [Unit] تحديد نوع الخدمة (تعني simple أنه يجب تشغيل العملية ببساطة) وتجهيز الأمر الذي سيُنفّذ. يمكنك أيضًا تحديد أنّه يجب تشغيل code-server العموميّ القابل للتنفيذ مع إضافة بعض «الوسائط» (arguments) لـ code-server. الوسيط --host 127.0.0.1 يربطه مع localhost، وبذلك يمكن الوصول إليه مباشرة فقط من داخل خادمك الخاص. يعين الوسيط --user-data-dir /var/lib/code-server مسار بيانات المستخدم الخاص به، - يُحدّد --auth password أنّه يجب مصادقة الزائرين باستخدام كلمة مرور، كما هو محدّد في متغير البيئة PASSWORD المصرّح عنه في السطر الذي يسبقه. لا تنسَ استبدال your_password بكلمة مرورك الخاصّة ثم احفظ وأغلق الملف. يطلب السطر التالي من systemd إعادة تشغيل code-server عند حصول أي خلل (على سبيل المثال عند تعطله أو إنهاء العملية قسريًّا). يطلب القسم [Install] من systemd بدء هذه الخدمة عندما يصبح بالإمكان تسجيل الدخول إلى الخادم الخاص بك. ابدأ خدمة code-server عن طريق تشغيل الأمر التالي: sudo systemctl start code-server تحقّق من أنها بدأت بشكل صحيح من خلال مراقبة حالاتها: sudo systemctl status code-server سترى خرجًا مشابهًا لما يلي: code-server.service - code-server Loaded: loaded (/lib/systemd/system/code-server.service; disabled; vendor preset: enabled) Active: active (running) since Mon 2019-12-09 20:07:28 UTC; 4s ago Main PID: 5216 (code-server) Tasks: 23 (limit: 2362) CGroup: /system.slice/code-server.service ├─5216 /usr/local/bin/code-server --host 127.0.0.1 --user-data-dir /var/lib/code-server --auth password └─5240 /usr/local/bin/code-server --host 127.0.0.1 --user-data-dir /var/lib/code-server --auth password Dec 09 20:07:28 code-server-ubuntu-1804 systemd[1]: Started code-server. Dec 09 20:07:29 code-server-ubuntu-1804 code-server[5216]: info Server listening on http://127.0.0.1:8080 Dec 09 20:07:29 code-server-ubuntu-1804 code-server[5216]: info - Using custom password for authentication Dec 09 20:07:29 code-server-ubuntu-1804 code-server[5216]: info - Not serving HTTPS مكّن خدمة code-server لجعله يعمل تلقائيًا بعد إعادة تشغيل الخادم من خلال تشغيل الأمر التالي: sudo systemctl enable code-server قمتَ في هذه الخطوة بتنزيل code-server وجعله متاحًا على نحوٍ عمومي. وقمت بعدها بإنشاء خدمة systemd له وتمكينها، لذلك سيبدأ code-server بالعمل عند كل إقلاع للخادم. بعد ذلك، ستعرضه في نطاقك الخاص من خلال ضبط Nginx ليكون بمثابة وكيل عكسيّ بين الزائر و code-server. الخطوة 2: عرض code-server على نطاقك الخاص ستقوم في هذا القسم بضبط Nginx كوكيل عكسي لـ code-server. كما تعلمت في خطوة المتطلبات المُسبقة لـ Nginx، تُخزّن ملفات ضبط الموقع الخاصة به ضمن /etc/nginx/sites-available، ويجب ربطها لاحقًا بـ /etc/nginx/sites-enabled لتصبح نشطة. سنُخزين الضبط لعرض خادم الشفرة في نطاقك في ملف باسم code-server.conf تحت المسار /etc/nginx/sites-available. ابدأ بإنشائه باستخدام المحرّر الخاص بك: sudo nano /etc/nginx/sites-available/code-server.conf أضف الأسطر التالية في الملف /etc/nginx/sites-available/code-server.conf: server { listen 80; listen [::]:80; server_name code-server.your-domain; location / { proxy_pass http://localhost:8080/; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Accept-Encoding gzip; } } استبدل code-server.your-domain باسم لنطاق المطلوب ثم احفظ وأغلق الملف. تحدّد في هذا الملف أنه على Nginx أن يتنصت إلى منفذ HTTP رقم 80. ثم يمكنك تحديد server_name والذي يُخبر Nginx عن النطاق الذي يقبل الطلبات ويطبّق هذا الضبط المحدّد. في الكتلة التالية بالنسبة، لموقع الجذر (/) يمكنك تحديد أنّ الطلبات يجب تمريرها ذهابًا وإيابًا إلى code-server يعمل على localhost:8080. تطلب الأسطر الثلاثة التالية (بدءًا من proxy_set_header) من Nginx نقل بعض ترويسات طلبات HTTP اللازمة من أجل التشغيل الصحيح لـ WebSockets، والذي يستخدمه code-server على نطاق واسع. ستحتاج إلى إنشاء رابط له في المجلد /etc/nginx/sites-enableمن أجل تنشيط ضبط هذا الموقع وذلك من خلال تشغيل الأمر الآتي: sudo ln -s /etc/nginx/sites-available/code-server.conf /etc/nginx/sites-enabled/code-server.conf شغّل الأمر التالي لاختبار صحّة الضبط: sudo nginx -t سترى الخرج التالي: Output nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ستحتاج إلى إعادة تشغيل Nginx لكي يُطّبق الضبط: sudo systemctl restart nginx لديك الآن تثبيت code-server قابل للوصول على نطاقك. ستقوم في الخطوة التالية بتأمينه من خلال تطبيق شهادة Let's Encrypt TLS المجانيّة. الخطوة 3: تأمين النطاق ستقوم في هذا القسم بتأمين نطاقك الخاص باستخدام شهادة Let’s Encrypt TLS، والتي ستوفرها باستخدام Certbot. ستحتاج من أجل تثبيت أحدث إصدار من Certbot إلى إضافة مستودع الحزمة الخاص به إلى خادمك عن طريق تشغيل الأمر التالي: sudo add-apt-repository ppa:certbot/certbot ستحتاج إلى الضغط علىENTER للموافقة. ثبّت بعد ذلك Certbot وإضافة Nginx الخاص به: sudo apt install python-certbot-nginx كنت قد مكّنت ufw (جدار حماية غير معقّد) كجزء من المتطلبات المُسبقة وقمت بضبطه للسماح بحركة مرور HTTP غير المشفّرة. وستحتاج إلى ضبطه لقبول حركة المرور المشفّرة حتى تتمكن من الوصول إلى الموقع الآمن عن طريق تشغيل الأمر التالي: sudo ufw allow https سيكون الخرج: Output Rule added Rule added (v6) ستحتاج كما في Nginx إلى إعادة تحميله حتى يصبح الضبط ساري المفعول: sudo ufw reload سيُظهر الخرج ما يلي: Firewall reloaded انتقل بعد ذلك في متصفحك إلى النطاق الذي استخدمته لـ code-server. حيث سيظهر محّث تسجيل الدخول لـ code-server. سيطلب code-server كلمة المرور الخاصّة بك. أدخل الكلمة التي اخترتها في الخطوة السابقة واضغط على** Enter IDE**. ستدخل الآن إلى code-server وسترى محرّر واجهة المستخدم الرسومية GUI مباشرةً. الآن وبعد تأكّدك من أن خادم الشيفرة قد عُرض بشكل صحيح في نطاقك، سنُثبّت شهادات Let’s Encrypt TLS لتأمينه، باستخدام Certbot. شغّل الأمر التالي لطلب شهادات لنطاقك: sudo certbot --nginx -d code-server.your-domain تقوم في هذا الأمر بتشغيل certbot لطلب شهادات لنطاقك — يمكنك تمرير اسم النطاق باستخدام الراية -d تخبر الراية --nginx بتغيير ضبط موقع Nginx تلقائيًا لدعم HTTPS. تذكر استبدال code-server.your-domain باسم نطاقك الخاص. إذا كانت هذه هي المرة الأولى التي تُشغّل فيها Certbot فسيُطلب منك تقديم عنوان بريد إلكتروني للإشعارات العاجلة وقبول شروط خدمة EFF. سيطلب Certbot بعد ذلك شهادات لنطاقك من Let’s Encrypt. كما سيسألك بعدها فيما إذا كنت ترغب في إعادة توجيه جميع زيارات HTTP إلى HTTPS: Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Select the appropriate number [1-2] then [enter] (press 'c' to cancel): يُوصى بتحديد الخيار الثاني من أجل زيادة الأمان. اضغطENTERبعد إدخال اختيارك. سيكون الخرج مشابهًا لهذا: IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/code-server.your-domain/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/code-server.your-domain/privkey.pem Your cert will expire on ... To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le هذا يعني أن Certbot أنشئ شهادات TLS بنجاح وطبّقها على ضبط Nginx لنطاقك. يمكنك الآن القيام بإعادة تحميل اسم نطاق code-server الخاص بك في متصفحك ورؤية رمز القفل على يسار عنوان الموقع مما يعني أن اتصالك أصبح آمنًا. الآن وبعد أن أصبح لديك code-server يمكن الوصول إليه في نطاقك من خلال وكيل Nginx عكسي آمن، أصبحت على استعداد لاستعراض واجهة المستخدم لـ code-server. الخطوة 4: استخدام واجهة code-server ستستخدم في هذا القسم بعضًا من ميزات واجهة code-server. ولأنّ code-server هو عبارة عن Visual Studio Code يعمل على السحابة، فإنّه يحتوي على نفس واجهة إصدار سطح المكتب المستقلة. يوجد على الجانب الأيسر من الـ IDE صف عمودي مكوّن من ستة أزرار يُتيح استعمال أكثر الميزات استخدامًا في لوحة جانبية تُعرف باسم Activity Bar. هذا الشريط قابل للتخصيص بحيث يمكنك نقل هذه الأزرار إلى ترتيب مختلف أو إزالتها من الشريط. افتراضيًا يفتح الزر الأول القائمة العامة في قائمة منسدلة، بينما يفتح الزر الثاني لوحة Explorer التي توفر التنقل الهرمي الشبيه ببنية المشروع. يمكنك إدارة المجلدات والملفات هنا – من إنشاء وحذف ونقل وإعادة تسمية حسب الضرورة. يُوفّر الزر التالي التالية إمكانية الوصول إلى وظيفة البحث والاستبدال. بعد ذلك، وبالترتيب الافتراضي (أي الزر الرابع) يُتيح معاينة لأنظمة التحكم في المصدر مثل Git. يدعم Visual Studio code أيضًا موفري التحكم بالمصدر الآخرين ويمكنك العثور على مزيد من الإرشادات لتدفقات عمل التحكم بالمصدر مع المحرّر في هذه الوثائق. يوفر خيار المنقّح debugger في Activity Bar جميع الأدوات الشائعة للتنقيح في اللوحة. يأتي Visual Studio Code مع دعم مضمّن لمنقّح وقت التشغيل Node.js وأي لغة تنتمي إلى Javascript. وبالنسبة للغات أخرى يمكنك تثبيت إضافاتextensions للمنقح المطلوب. كما يمكنك حفظ ضبط التنقيح في ملفlaunch.json. يوفر الزر الأخير في Activity Bar قائمة للوصول إلى الملحقات المُتاحة في Marketplace. إنّ الجزء الرئيسي من واجهة المستخدم الرسوميّة هو محرّرك، والذي يمكنك فصله بواسطة علامات تبويب لتحرير شيفرتك. ويمكنك تغيير طريقة العرض الخاصة بك إلى نظام شبكة أو إلى طريقة عرض الملفات جنبًا إلى جنب. بعد إنشاء ملف جديد من خلال قائمة File، سيُفتح ملف فارغ في علامة تبويب جديدة، وبعد حفظه، سيكون اسم الملف معروضًا في اللوحة الجانبيّة لـ Explorer. يمكن إنشاء المجلدات عن طريق النقر بزر الفأرة الأيمن على الشريط الجانبي لـ Explorer والنقر فوق New Folder. يمكنك توسيع أحد المجلدات بالنقر فوق اسمه وكذلك سحب الملفات والمجلدات وإسقاطها في الأجزاء العليا من التسلسل الهرمي لنقلها إلى موقع جديد. بالإمكان الوصول إلى محطة طرفية عن طريق إدخال CTRL + SHIFT +، أو عن طريق النقر على Terminal في القائمة المنسدلة العليا واختيار New Terminal. سيُفتح الجهاز في لوحة أصغر وسيُوضع مجلد العمل الخاص به في مساحة عمل المشروع، والذي يحتوي على الملفات والمجلدات المعروضة في لوحة Explorer الجانبيّة. لقد أخذت نظرةً عامة عالية المستوى على واجهة code-server وراجعت بعضًا من الميزات الأكثر استخدامًا. الخلاصة لديك الآن بيئة التطوير المتكاملة code-server السحابية متعددة الاستخدامات مُثبّتة على خادم Ubuntu 18.04 ومعروضة في نطاقك المؤّمن باستخدام شهادات Let’s Encrypt. يمكنك الآن العمل على المشاريع بشكل فردي، وكذلك في إطار التعاون الجماعي. يحرّر تشغيل Cloud IDE الموارد على جهازك المحلي ويسمح لك بتوسيع نطاق الموارد عند الحاجة. راجع لمزيد من المعلومات توثيق Visual Studio Code للحصول على ميزات إضافيّة وإرشادات مفصّلة حول المكونات الأخرى لـ code-server. ترجمة وبتصرف للمقال How To Set Up the code-server Cloud IDE Platform on Ubuntu 18.04 لصاحبه Savic
-
- بيئة تطوير متكاملة
- تطبيق سحابي
-
(و 3 أكثر)
موسوم في:
-
أصبحت لغة Go -التي هي لغة برمجية عاميّة الغرض- واحدةً من لغات البرمجة الأكثر شعبيةً لتطوير الواجهة الخلفية. بالتركيز على البساطة، فإنَّ مصممي لغة Go ابتكروا لغةً سهلة التعلّم وأسرع من العديد من اللغات الأخرى لتطبيقات الويب، مستفيدين من الميزات الفعّالة مثل قدرتها على التعامل مع عدة طلبات في وقت واحد بسبب تزامنها. لهذا السبب، سيكون نشر تطبيق ويب بلغة Go مفيدًا للعديد من مطوري الواجهة الخلفية. Nginx هو أحد أشهر خوادم الويب في العالم نظرًا لاستخدامه الخفيف للموارد وموثوقيته في حال وجود حمل زائد. تعتمد العديد من المواقع الضخمة والأكثر زيارةً على Nginx لتقدّم محتواها. في النشر، غالبًا ما يُستخدم Nginx موازن حمل(load balancer) أو وكيل عكسي (reverse proxy) لزيادة الأمان وجعل التطبيق أكثر قوة. بالتزامن مع لغة Go لتطوير الواجهة الخلفية في الويب، يمكن لخادم Nginx تقديم تطبيق ويب سريع وقوي. ستتعلم في هذا الدرس كيفية بناء تطبيق ويب Hello World بلغة Go وننشره على خادم أوبنتو 18.04 مستخدمين Nginx كخادم عكسي. المتطلبات الأساسية لمتابعة هذا الدرس، ستحتاج إلى ما يلي: خادم أوبنتو 18.04 تم ضبطه باتباع التهيئة الأولية لخادم أوبنتو 18.04، متضمنًا مستخدمًا عاديًا بصلاحيات sudo وجدار حماية. يجب أن تكون لغة Go مثبّتة على حاسوبك. خادم Nginx مثبّت باتّباع الدرس كيف تثبّت خادم Nginx على أوبنتو 18.04. لا تتبع الخطوة 5 - إعداد كتل الخادم؛ ستنشئ كتلة خادم Nginx لاحقًا في هذا الدرس. اسم نطاق يشير إلى خادمك. سنستخدم your_domain في هذا الدرس. هذا ضروري للحصول على شهادة SSL لموقعك، حتى يمكنك أن تقدّم تطبيقك بأمان مع تشفير TLS. بالإضافة إلى ذلك، لتحقيق نشر بدرجة الإنتاج لتطبيق الويب Go، من المهم أن تحافظ على خادمك آمنًا بتثبيت شهادة TLS/SSL. أرشح لك هذه الخطوة بشدة. لتؤمّن تطبيق الويب Go، اتّبع كيف تؤمّن خادم Nginx بشهادة Let's Encrypt على أوبنتو بعد الخطوة 3 من هذا الدرس لتحصل على شهادة TLS/SSL مجانية. الخطوة 1: بناء تطبيق ويب بلغة البرمجة Go في هذه الخطوة، ستبني نموذج تطبيق ويب بلغة Go يعرض عبارة Hello World على نطاقك your_domain ويقوم بتحيّة المستخدم على الرابط your_domain/greet/. إذا كنت تريد تعلّم المزيد من أساسيات البرمجة في Go، تفحّص مقالتنا كيف تكتب برنامجك الأول بلغة Go. بدايةً، أنشئ مجلد جديد في مجلد GOPATH ليتضمن الملف المصدري. يمكنك تسمية المجلد بأي اسم تريده، ولكن في هذا الدرس سنستخدم اسم go-web: $ mkdir $GOPATH/go-web باتّباع بنية الملف المقترحة في درس فهم، تنصيب وتهيئة بيئة عمل لغة البرمجة Go، سيعطي هذا مجلدك المسار ~/go/go-web: بعد ذلك، نفّذ التعليمة التالية لتغيّر المجلد إلى مجلدك الذي أنشأته حديثًا في GOPATH: $ cd $GOPATH/go-web استخدم محرر النصوص nano أو المحرر المفضل لك لإنشاء ملف باسم main.go، والذي سيتضمن الشيفرة المصدرية لتطبيقك الويب: $ nano main.go لإنشاء وظيفة التطبيق Hello World، أضف شيفرة Go التالية إلى ملف main.go المنشأ حديثًا: package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World") }) http.HandleFunc("/greet/", func(w http.ResponseWriter, r *http.Request) { name := r.URL.Path[len("/greet/"):] fmt.Fprintf(w, "Hello %s\n", name) }) http.ListenAndServe(":9990", nil) } لنتعرّف الآن على ما سيفعله جزء الشيفرة السابق، بدءًا من السطر الأول. كتبت أولًا نقطة الدخول إلى تطبيقك: package main … تخبر تعليمة package main مُصرِّف Go أن يصرّف هذا الملف على أنّه برنامجٌ تنفيذي بدلًا من أن يكون مكتبة مشتركة (shared library). ثمّ لديك تعليمات import: … import ( "fmt" "net/http" ) … يستورد هذا الجزء الوحدات الضرورية لعمل هذه الشيفرة، والتي تشمل حزمة fmt القياسية وحزمة net/http لخادم الويب الخاص بك. يُنشئ الجزء التالي مسارك الأول في الدالة main والتي هي نقطة الدخول لأي تطبيق Go: … func main () { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World") }) … } … أنشئ المسار الأب / ضمن الدالة func main، التي ستعيد النص Hello World عندما تُطلب. كما هو موضح في الجزء التالي فإنّ المسار الثاني يقبل معاملًا في الرابط (URL parameter)، في هذه الحالة اسمًا، لعرضه مصحوبًا بالتحية. … func main () { … http.HandleFunc("/greet/", func(w http.ResponseWriter, r *http.Request) { name := r.URL.Path[len("/greet/"):] fmt.Fprintf(w, "Hello %s\n", name) }) … } … يستخدم هذا URL.Path الخاص بلغة Go لتخزين القيمة مباشرةً بعد /greet/ وتمريرها كاسم من معامل الرابط. أخيرًا، هيّئ الخادم: … func main () { … http.ListenAndServe(":9990", nil) } يشغّل الجزء السابق الخادم ويعرض تطبيقك عبر المنفذ 9990 باستخدام خادم http المدمج بلغة Go. احفظ الملف وأغلق المحرر النصي بمجرد الانتهاء من فحص الشيفرة في main.go. بعد ذلك، ابنِ الملف الثنائي القابل للتنفيذ لتطبيقك باستخدام الأمر: $ go build main.go التعليمة السابقة ستصرِّف الملف main.go لإنتاج ملف قابل للتنفيذ باسم main. لقد أنشأت نموذج تطبيق ويب بلغة Go. بعد ذلك، ستنشئ ملف وحدة systemd ليبقى تطبيقك شغّالًا في الخلفية حتى عندما لا تصل إلى خادمك. الخطوة 2: إنشاء ملف وحدة Systemd في هذه الخطوة، ستنشئ ملف وحدة systemd ليبقى تطبيقك شغّالًا في الخلفية حتى عندما يسجّل المستخدم خروجه من الخادم. سيجعل هذا تطبيقك ثابتًا، مما يجعلك أقرب بخطوة للنشر على مستوى الإنتاج. بدايةً، أنشئ ملفًا جديدًا في مجلد /lib/systemd/system باسم goweb.service باستخدام محرر النصوص nano أو محرر النصوص المفضّل لك: $ sudo nano /lib/systemd/system/goweb.service لضبط معاملات الخدمة، أضف الجزء التالي في الملف. [Unit] Description=goweb [Service] Type=simple Restart=always RestartSec=5s ExecStart=/home/user/go/go-web/main [Install] WantedBy=multi-user.target يحدّد المتغير ExecStart=/home/user/go/go-web/main أنَّ نقطة الدخول لهذه الخدمة عبر الملف main القابل للتنفيذ الموجود في المجلد /home/user/go/go-web، إذ user هو اسم المستخدم لحساب المستخدم العادي الذي يملك صلاحيات sudo على الخادم. Restart=always تضمن أنَّ systemd سيحاول دائمًا أن يعيد تشغيل البرنامج إذا توقف. في السطر التالي، RestartSec=5s يضبط وقت انتظار لخمس ثوانٍ بين محاولات إعادة التشغيل. يحدِّد WantedBy=multi-user.target في أي حالة سيفعّل الخادم الخدمة. احفظ وأغلق الملف. بعد أن كتبت ملف وحدة الخدمة، شغّل خدمة الويب Go باستخدام الأمر: $ sudo service goweb start للتأكّد فيما إذا كانت الخدمة شغّالة، استخدم الأمر التالي: $ sudo service goweb status ستستقبل الخرج التالي: Output ● goweb.service - goweb Loaded: loaded (/lib/systemd/system/goweb.service; disabled; vendor preset: enabled) Active: active (running) since Wed 2019-07-17 23:28:57 UTC; 6s ago Main PID: 1891 (main) Tasks: 4 (limit: 1152) CGroup: /system.slice/goweb.service └─1891 /home/user/go/go-web/main لتتعلم المزيد عن العمل مع ملف وحدة systemd، ألقِ نظرة على فهم وحدات وملفات وحدة Systemd. الآن بعد أن أصبح لديك تطبيقًا شغّالًا، يمكنك ضبط الوكيل العكسي Nginx. الخطوة 3: ضبط وكيل عكسي مع Nginx ستنشئ في هذه الخطوة كتلة خادم Nginx وستضبط الوكيل العكسي Nginx لتعرض تطبيقك على الإنترنت. أولًا، غيّر مجلد العمل الخاص بك إلى مجلد sites-available على الخادم Nginx: $ cd /etc/nginx/sites-available أنشئ ملفًا جديدًا باسم النطاق الذي تريد عرض تطبيقك عليه. سيستخدم هذا الدرس الملف your_domain: $ sudo nano your_domain أضف الأسطر التالية إلى الملف لوضع الإعدادات لـ your_domain: server { server_name your_domain www.your_domain; location / { proxy_pass http://localhost:9990; } } كتلة الخادم Nginx تستخدم proxy_pass لتقديم تطبيق الويب Go على عنوان IP لخادمك المشار إليه على أنّه مضيف محلي localhost لتشغيله على المنفذ 9990 . يشير server_name إلى اسم النطاق المعيّن لعنوان IP الخاص بك، في هذه الحالة your_domain وwww.your_domain. بعد ذلك، أنشئ رابطًا رمزيًا لإعدادات Nginx هذه في مجلد sites-enabled بتشغيل الأمر التالي: $ sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/your_domain الرابط الرمزي هو اختصار لملف في موقع آخر. يشير الاختصار المُنشأ حديثًا إلى الملف الأصلي للتكيّف مع التحديثات عند إجراء التعديلات عليه. يتطلب Nginx نسخة من الإعدادات في كلا المجلدين. بعدها، أعد تحميل إعدادات Nginx بتشغيل تعليمة إعادة التحميل: $ sudo nginx -s reload لتتأكد أن نشرك يعمل، قم بزيارة http://your_domain في متصفحك، إذ ستتم تحيتك بسلسلة نصية Hello World. ملاحظة: كما ذُكر في قسم المتطلبات الأساسية، يُنصح في هذه النقطة أن يتم تفعيل SSL/TLS على خادمك. سيضمن ذلك تشفير جميع الاتصالات بين التطبيق وزوّاره، وهو أمر مهم بشكلٍ خاص إذا طلب التطبيق معلومات حساسة مثل تسجيل الدخول أو كلمة المرور. اتّبع الآن درس كيف تؤمّن خادم Nginx بشهادة Let's Encrypt على أوبنتو لتحصل على شهادة SSL مجانية لخادم Nginx على أوبنتو 18.04. بعد أن تحصل على شهادات SSL/TLS خاصة بك، عُد وأكمل هذا الدرس. ضبطت الآن الوكيل العكسي Nginx لتعرض تطبيقك على اسم النطاق الخاص بك، وقمت بتأمين تطبيق الويب Go مع SSL/TLS. في الخطوة التالية، ستختبر تطبيقك عبر اتصالٍ آمن. الخطوة 4: اختبار التطبيق في هذه الخطوة، ستختبر تطبيقك عبر اتصالٍ آمن لتتأكد من أنَّ كل شيء يعمل. افتح متصفح الويب المفضل لك، وقم بزيارة https://your_domain: ستستقبل رسالة Hello World بسيطة. استقبال هذه الرسالة عند استخدام https:// في الرابط يشير إلى أنَّ التطبيق يُعرض عبر اتصالٍ آمن. بعد ذلك، حاول زيارة المسار الثاني https://your_domain/greet/your-name مستبدلًا بالعبارة your-name أيّ اسم آخر تريد أن يلقي عليه تطبيقك التحيّة: سيعيد التطبيق تحيّة بسيطة مع your-name، الذي يعتمد على المعامل الممرّر للرابط. حالما تستقبل هذه النتائج، تكون قد نشرت تطبيق ويب Go بنجاح. خاتمة في هذا الدرس، أنشأت تطبيق ويب بسيط بلغة Go مستخدمًا مكاتبها القياسية، وقمت بضبط وكيل عكسي مستخدمًا Nginx، واستخدمت شهادة SSL على نطاقك لتأمين تطبيقك. لتتعلّم المزيد حول Go، تفحّص توثيقها الرسمي. بإمكانك أيضًا أن تلقي نظرة على قسم لغة GO لتتعلم المزيد حول البرمجة بهذه اللغة الفعالة. ترجمة -وبتصرف- للمقال How To Deploy a Go Web Application Using Nginx on Ubuntu 18.04 لصاحبه Michael Okoh
-
بعد أن تعرّفنا على أساسيات لغة بايثون حان الوقت للانتقال إلى مرحلة جديدة. في هذه السّلسلة من الدّروس سنتعرّف على أساسيات تطوير تطبيقات الويب بلغة بايثون، وذلك بالاستعانة بإطار العمل Flask، يعتبر Flask إطارا مُصغّرا Micro-Framework أي أنّه يُقدّم للمُبرمج أدوات مُساعدة بسيطة، وبعكس إطار Django فهو مُناسب للمُبتدئين الذين تعرّفوا على لغة بايثون حديثا. متطلبات هذه السلسلة لمتابعة هذه الدّروس وفهمها، ستحتاج إلى معرفة بسيطة بلغة بايثون. ستحتاج كذلك إلى معرفة بسيطة بلغة HTML الهيكلية، وكذلك القليل من لغة CSS لتنسيق الصّفحات إذ لن أشرح ما يتعلق بلغة HTML وCSS لأنّ ذلك ليس من اختصاص السّلسلة. يمكنك مراجعة الدروس التالية على أكاديمية حسوب لتعلم أساسيات هذه اللغات: سلسلة دروس تعلم لغة بايثون تعلّم لغة HTML ولغة CSS ما هو تطبيق الويب؟ تطبيق الويب، هو كل تطبيق يُمكن الوصول إليه عن طريق مُتصفّح للويب (Firefox ،Chrome ،Safari) ويقوم بتقديم صفحات مرئية حسب طلب الزّائر. يُمكن اعتبار موقع الأكاديمية هذا تطبيق ويب، إذ يتفاعل مع الزائر بتقديم المقالات بشكل متناسق، ويوفّر إمكانية المُشاركة للمُستخدمين عبر صندوق التّعليقات وغير ذلك من الخصائص. الصفحة التي تقرأ منها هذا المقال حاليا أصلها شيفرات لغة HTML وهي لغة أساسية في الويب. وتُستعمل لغات البرمجة مثل لغة Python لتقديم شيفرة HTML من الخادوم إلى المُتصفّح الذي يعرضها بدوره للمُستخدم. ما يعني أنّ الهدف النهائي من برمجة التّطبيق هو تقديم ملفات HTML من الخادوم إلى العميل (المُستخدم). خلاصة القول أنّك عندما تدخل إلى موقع الأكاديمية عن طريق رابط academy.hsoub.com، يرسل المُتصفّح طلبا للخادوم الخاص بالأكاديمية، عندما يستقبل الخادوم الطلب يقوم مُباشرة بتنفيذ الشيفرة المكتوبة بلغة برمجية، الشيفرة البرمجيّة تُجيب بملفات HTML ويعرضها لك المُتصفّح فور استقبالها. ما سنتعلّمه في هذه السّلسلة هو كيفيّة التعامل مع طلبات المُستخدم وكيفيّة تقديم ملفات HTML للمُتصفّح باستخدام لغة بايثون. ما هو إطار العمل؟ إطار العمل هو مجموعة من المكتبات والوحدات التي تحتوي على دوال مُساعدة تُمكّن المُبرمج من كتابة تطبيقات دون الاضطرار إلى التعامل مع التفاصيل الدقيقة التي تتطلب وقتا وجهدا كبيرين. يُمكن أن يكون إطار العمل خاصا بتطوير تطبيقات الويب مثل Flask أو Django، ويُمكن كذلك أن يكون مُخصّصا لمجالات أخرى كبناء تطبيقات سطح المكتب مثلا. تتوفّر لغة بايثون على العديد من أطر العمل الخاصّة بتطوير الويب، والتالي قائمة ببعض الأطر مع وصف مختصر لكلّ إطار. Django: إطار عمل ضخم، يتوفّر على عدد هائل من الدوال المُساعدة، كما يعتبر أنسب خيار لمن يرغب بتطوير تطبيقات كبيرة ومُعقّدة متعدّدة الوظائف، يتميّز بشهرته الواسعة وهو سهل التّعلم، يعتبر مناسبا كذلك لمن يرغب بإنشاء تطبيق بسرعة وهو شائع بين الشّركات النّاشئة. Flask: إطار عمل مُصغّر/صغير، يتوفّر على عدد لا بأس به من الدوال المُساعدة، شهرته تقريبا بنفس شهرة Django، مُناسب لتطوير تطبيقات صغيرة ومُتوسّطة (مُدونة، منتدى، موقع شخصي… ). Tornado: إطار عمل مُخصّص للتطبيقات التي تتطلب سرعة في مُعالجة الطّلبات وسرعة في التجاوب كتطبيقات الدّردشة مثلا. Bottle: إطار عمل صغير جدا، يوفّر أدنى المُتطلبات لتطوير تطبيق بسرعة، ويعتبر أصغر من إطار Flask. سبق وأن نشرنا درسا عنه. TurboGears: خصائصه تقترب من خصائص إطار Django، الاختلاف الرئيسي يكمن في الأدوات والمكتبات التي يعتمد عليها كالاتصال بقواعد البيانات وما إلى ذلك ويُعتبر خيارا آخر لمن يرغب بتطوير تطبيقات كبيرة. صحيح أن هناك أطر عمل أخرى لكنّ ما تقدّم ذكره يعتبر أبرزها. لماذا Flask؟ وقع الاختيار على إطار العمل Flask لسهولة تعلّمه بالنّسبة للمبتدئ، إذ سيبدو مألوفا لمن تعرّف حديثا على لغة بايثون، وبما أنّه إطار عمل مُصغّر فسيسهل عليك فهم خطوات إنشاء تطبيق كامل، خاصّة أنّك تستطيع أن تبني تطبيقا في ملفّ بايثون واحد. يتميّز إطار Flask كذلك بإتاحة إمكانيّة ربط تطبيقك بمُختلف مكتبات لغة بايثون، والتي يُمكنك تنصيبها بسهولة بأداة pip، وهي أداة لإدارة الحزم (مثل Gem بالنّسبة للغة روبي و Composer بالنّسبة للغة PHP). يُمكن كذلك الاعتماد على إضافات لجعل الإطار أقرب إلى الأطر الكبيرة مثل Django إذ يمتلك إطار العمل Flask العديد من الإضافات التي يُمكنك تنصيبها واستعمالها في مشروعك، ما يُمكن أن يُساعدك على إنشاء مشاريع كبيرة. Flask أم Django؟ يعتبر الاختيار بين إطار Flask وإطار Django من القرارات الصّعبة على المُبتدئ، لكنّ عليك فهم الفرق بين الإطارين لتختار ما يُناسبك، فكما قلنا سابقا فإطار Django يُوفّر عددا هائلا من الدوال والأدوات المُساعدة، أما إطار Flask فيُوفّر أدوات بسيطة وعددا أقلّ من الدوال المُساعدة. يُمكنك اختيار تعلّم إطار Django إذا كانت لديك خبرة مُسبقة بأحد أطر العمل في اللغات الأخرى مثل Laravel أو Ruby On Rails، كما يُنصح به إذا كان المشروع الذي ستعمل عليه كبيرا كتطبيق تواصل اجتماعي أو تطبيق خدمي. أما إذا لم تكن تملك أية خبرة مُسبقة فأنصح بتعلّم إطار Flask أولا، وبعد التمكن من التعامل معه وإتقان ذلك يُمكنك الانتقال إلى استعمال Django متى ما دعت الحاجة إلى ذلك، وستجد حينها بأنّ الوقت الذي استثمرته في تعلّم Flask قد أتى أكله، وسيسهل عليك تعلّم إطار Django وفهم كيفيّة عمله. كيف تستفيد من هذه السلسلة من الدروس؟ سلسلة الدروس هذه ستكون موزعة حسب المُخطّط التالي: إعداد بيئة التّطوير وإنشاء تطبيقك الأول تقديم ملفات HTML وملفات CSS والصور استخدام قاعدة بيانات مع تطبيق Flask كل درس سيكون شبه مُستقل عن الدّرس الذي يسبقه، وذلك لتكون الدروس مرجعا لك في حالة نسيان أي جزئية. في نهاية السّلسلة ستكون قادرا على استعمال لغة بايثون لتطوير تطبيق يعمل على المُتصفّح ويتصل بقاعدة بيانات. ختاما في الدّرس المُقبل سنقوم بإعداد بيئة التّطوير بتنصيب الأدوات المطلوبة، كما سننشئ تطبيقا بسيطا لعرض صفحة ويب على المُتصفّح.
-
هذا الدرس جزء من سلسلة تعلم Laravel والتي تنتهج مبدأ "أفضل وسيلة للتعلم هي الممارسة"، حيث ستكون ممارستنا عبارة عن إنشاء تطبيق ويب للتسوق مع ميزة سلة المشتريات. يتكون فهرس السلسلة من التالي: مدخل إلى Laravel 5.تثبيت Laravel وإعداده على كلّ من Windows وUbuntu.أساسيات بناء تطبيق باستخدام Laravel.إنشاء روابط محسنة لمحركات البحث (SEO) في إطار عمل Laravel.نظام Blade للقوالب.تهجير قواعد البيانات في Laravel. استخدام Eloquent ORM لإدخال البيانات في قاعدة البيانات، تحديثها أو حذفها. إنشاء سلة مشتريات في Laravel. (هذا الدرس)الاستيثاق في Laravel.إنشاء واجهة لبرمجة التطبيقات API في Laravel.إنشاء مدوّنة باستخدام Laravel.استخدام AngularJS واجهةً أمامية Front end لتطبيق Laravel.الدوّال المساعدة المخصّصة في Laravel.استخدام مكتبة Faker في تطبيق Laravel لتوليد بيانات وهمية قصدَ الاختبار. نكمل بناء متجرنا الإلكتروني بإضافة خاصية سلة المشتريات. سنستخدم حزمة Laravel ShoppingCart لإضافة هذه الخاصية إلى المشروع. يغطي الدرس المواضيع التالية: تثبيت حزمة Laravel Shopping Cart بـComposer. إعداد Laravel 5 لاستخدام الحزمة.إضافة عناصر إلى سلة المشتريات.العثور على العناصر الموجودة في سلة المشتريات.تحديث عنصر في سلة المشتريات.حذف عنصر من سلة المشتريات.استخدام Composer لتثبيت Laravel Shopping Cartنفتح ملف composer.json لإضافة اعتمادية جديدة للمشروع. نبحث عن فقرة require: "require": { "php": ">=5.5.9", "laravel/framework": "5.2.*", "doctrine/dbal": "v2.4.2" },نضيف حزمة ShoppingCart على النحو التالي: "require": { "php": ">=5.5.9", "laravel/framework": "5.2.*", "doctrine/dbal": "v2.4.2", "gloudemans/shoppingcart": "~1.3" },ملحوظة: انتبه للفواصل في نهاية الأسطر. نعلم Composer أننا نريد تثبيت الإصدار 1.3 من الحزمة gloudemans/shoppingcart؛ ثم نحدّث المشروع بتنفيذ الأمر التالي: composer updateإعداد Laravel لاستخدام حزمة ShoppingCartنحتاج بعد تثبيت حزمة ShoppingCart لإعداد Laravel للعمل معها. نفتح ملف الإعداد config/app.php ونضيف السطر التالي إلى مصفوفة providers: Gloudemans\Shoppingcart\ShoppingcartServiceProvider::class,يسجل الإعداد أعلاه مزوّد خدمة Provider لسلة المشتريات. ملحوظة: يتيح مزود الخدمة Service Provider في Laravel تحميل صنف (في الذاكرة) مع بدء عمل التطبيق، كما أنه يمكِّن من إضافة عناصر جديدة إلى حاويات الخدمة Service container وهي أداة تدير الاعتمادات بين مختلف الأصناف في إطار العمل. سنضيف الآن كنية Alias لصنف سلة المشتريات. افتح ملف الإعداد config/app.php وأضف العنصر التالي إلى مصفوفة aliases: 'Cart' => Gloudemans\Shoppingcart\Facades\Cart::class,ملحوظة: الكنى Aliases هي أسماء مختصرة للأصناف تُكتب بدلا من الاسم الكامل للصنف. يسجّل التطبيقُ عند بدئه هذه الأصنافَ لكنه لا يحملها إلا عند الحاجة. إضافة عناصر لسلة المشترياتسنضيف في هذه الفقرة إمكانية إضافة عنصر إلى سلة المشتريات في صفحة المنتج. تُجرى العملية على ثلاث خطوات: إنشاء مسار لإجراء HTTP POST الذي يضيف العناصر إلى سلة المشتريات.التعديل على القالب products.blade.php من أجل تضمين استمارة لإرسال البيانات مع معرّف المنتَج.التعديل على المتحكم Front.php لكي يتبقى بيانات الاستمارة ويضيفها إلى السلة.مسار إجراء HTTP POSTافتح ملف المسارات routes.php وأضف المسار التالي: Route::post('/cart', 'Front@cart');يعرف السطر السابق مسارات لتلقي إجراءات POST على رابط سلة المشتريات. سيُستخدَم هذا المسار لإضافة عناصر إلى سلة المشتريات. استمارة صفحة المنتجالخطوة التالية هي التعديل على قالب صفحة المنتج وإضافة استمارة form لإرسال معرّف المنتج إلى سلة المشتريات من أجل إضافته إليها. افتح ملف العرض products.blade.php وعدّله ليصبح محتواه التالي: @extends('layouts.layout') @section('content') <section id="advertisement"> <div class="container"> <img src="{{asset('images/shop/advertisement.jpg')}}" alt="" /> </div> </section> <section> <div class="container"> <div class="row"> <div class="col-sm-3"> <div class="left-sidebar"> @include('shared.sidebar') </div> </div> <div class="col-sm-9 padding-right"> <div class="features_items"><!--features_items--> <h2 class="title text-center">Features Items</h2> @foreach ($products as $product) <div class="col-sm-4"> <div class="product-image-wrapper"> <div class="single-products"> <div class="productinfo text-center"> <img src="{{asset('images/shop/product9.jpg')}}" alt="" /> <h2>${{$product->price}}</h2> <p>{{$product->name}}</p> <a href="{{url('cart')}}" class="btn btn-default add-to-cart"><i class="fa fa-shopping-cart"></i>Add to cart</a> <a href='{{url("products/details/$product->id")}}' class="btn btn-default add-to-cart"><i class="fa fa-info"></i>View Details</a> </div> <div class="product-overlay"> <div class="overlay-content"> <h2>${{$product->price}}</h2> <p>${{$product->name}}</p> <form method="POST" action="{{url('cart')}}"> <input type="hidden" name="product_id" value="{{$product->id}}"> <input type="hidden" name="_token" value="{{ csrf_token() }}"> <button type="submit" class="btn btn-fefault add-to-cart"> <i class="fa fa-shopping-cart"></i> Add to cart </button> </form> <a href='{{url("products/details/$product->id")}}' class="btn btn-default add-to-cart"><i class="fa fa-info"></i>View Details</a> </div> </div> </div> <div class="choose"> <ul class="nav nav-pills nav-justified"> <li><a href=""><i class="fa fa-plus-square"></i>Add to wishlist</a></li> <li><a href=""><i class="fa fa-plus-square"></i>Add to compare</a></li> </ul> </div> </div> </div> @endforeach <ul class="pagination"> <li class="active"><a href="">1</a></li> <li><a href="">2</a></li> <li><a href="">3</a></li> <li><a href="">»</a></li> </ul> </div><!--features_items--> </div> </div> </div> </section> @endsection التغيير الأساسي هنا هو إضافة استمارة form مع إجراء post: <form method="POST" action="{{url('cart')}}">...</form>تُرسل الاستمارة معرف المنتج إلى سلة المشتريات الذي يعرفه حقل product_id: <input type="hidden" name="product_id" value="{{$product->id}}">تحتوي الاستمارة أيضا على عنصر أمني هو: <input type="hidden" name="token" value="{{ csrftoken() }}">الذي يعرف رمز أمان Security token للحماية ضد هجمات تزوير الطلب عبر الموقع Cross-site request forgery (تُكتب CSRF أو XCRF اختصارا). احفظ التعديلات. ملحوظة: تقوم هجمات تزوير الطلب عبر الموقع على البحث عن روابط تمكن إعادة استخدامها لتنفيذ إجراءات معينة تحتاج لصلاحيات خاصة، تتوفر عادة عند المستخدمين المسجلين، ثم الاحتيال عليهم لكي يفتحوا هذه الروابط - غالبا دون علمهم - وتنفيذ الإجراءات الطلوبة. تحمي رموز الأمان المستخدمين من هذه الهجمات. التعديل على المتحكم Frontنحتاج، ليمكن لنا استخدام سلة المشتريات، التعديل على دالة cart لاستيرد فضاء الأسماء Cart. افتح ملف المتحكم Front.php وأضف الأسطر التالية لاستيراد فضاءات الأسماء Redirect، Request وCart على التوالي: يُستخدَم Request للوصول إلى بيانات إجراءات HTTP. يُستخدَم Redirect لتوجيه المستخدم بعد تنفيذ عملية لا تحتاج لإظهار بيانات في الصفحة.للوصول إلى عناصر سلة المشتريات نستخدم Cart.اعثُر على الدالة cart في المتحكم Front.php وحدّثها كالتالي: public function cart() { if (Request::isMethod('post')) { $product_id = Request::get('product_id'); $product = Product::find($product_id); Cart::add(array('id' => $product_id, 'name' => $product->name, 'qty' => 1, 'price' => $product->price)); } $cart = Cart::content(); return view('cart', array('cart' => $cart, 'title' => 'Welcome', 'description' => '', 'page' => 'home')); }نفحص أولا نوع إجراء HTTP الذي أتى منه الطلب إلى الدالة: if if (Request::isMethod(‘فإن كان الإجراء من نوع POST ننفذ الشفرة الموالية. نعثر علي معرف المنتج المُرسَل في الطلب: $productid = Request::get('productid');دالة Request::get لا تتعلق بنوعية إجراء HTTP ويتساوى عندها POST وGET. تُستخدَم هذه الدالة للعثور على متغيرات مرسلة في الطلب. نستخدم نموذج المنتج Product للعثور على المنتج في قاعدة البيانات: $product = Product::find($product_id);نستخدم كائن المنتج في التعليمة السابقة للحصول على بيانات المنتج وإضافتها إلى سلة المشتريات بإرسال نداء إلى الدالة Cart::add: Cart::add(array('id' => $product_id, 'name' => $product->name, 'qty' => 1, 'price' => $product->price));ننادي الدالة Cart::content للحصول على محتوى سلة المشتريات ونضيفه إلى المتغيرات الممررة إلى العرض cart.blade.php: $cart = Cart::content(); return view('cart', array('cart' => $cart, 'title' => 'Welcome', 'description' => '', 'page' => 'home'));العثور على العناصر الموجودة في سلة المشترياتبقيت لنا خطوة واحدة قبل أن نستطيع تجربة سلة المشتريات. نحتاج لتعديل العرض cart لكي يُظهر محتويات السلة. نفتح ملف العرض cart.blade.php لتحريره. نعدّل الملف على النحو التالي: @extends('layouts.layout') @section('content') <section id="cart_items"> <div class="container"> <div class="breadcrumbs"> <ol class="breadcrumb"> <li><a href="#">Home</a></li> <li class="active">Shopping Cart</li> </ol> </div> <div class="table-responsive cart_info"> @if(count($cart)) <table class="table table-condensed"> <thead> <tr class="cart_menu"> <td class="image">Item</td> <td class="description"></td> <td class="price">Price</td> <td class="quantity">Quantity</td> <td class="total">Total</td> <td></td> </tr> </thead> <tbody> @foreach($cart as $item) <tr> <td class="cart_product"> <a href=""><img src="images/cart/one.png" alt=""></a> </td> <td class="cart_description"> <h4><a href="">{{$item->name}}</a></h4> <p>Web ID: {{$item->id}}</p> </td> <td class="cart_price"> <p>${{$item->price}}</p> </td> <td class="cart_quantity"> <div class="cart_quantity_button"> <a class="cart_quantity_up" href=""> + </a> <input class="cart_quantity_input" type="text" name="quantity" value="{{$item->qty}}" autocomplete="off" size="2"> <a class="cart_quantity_down" href=""> - </a> </div> </td> <td class="cart_total"> <p class="cart_total_price">${{$item->subtotal}}</p> </td> <td class="cart_delete"> <a class="cart_quantity_delete" href=""><i class="fa fa-times"></i></a> </td> </tr> @endforeach @else <p>You have no items in the shopping cart</p> @endif </tbody> </table> </div> </div> </section> <!--/#cart_items--> <section id="do_action"> <div class="container"> <div class="heading"> <h3>What would you like to do next?</h3> <p>Choose if you have a discount code or reward points you want to use or would like to estimate your delivery cost.</p> </div> <div class="row"> <div class="col-sm-6"> <div class="chose_area"> <ul class="user_option"> <li> <input type="checkbox"> <label>Use Coupon Code</label> </li> <li> <input type="checkbox"> <label>Use Gift Voucher</label> </li> <li> <input type="checkbox"> <label>Estimate Shipping & Taxes</label> </li> </ul> <ul class="user_info"> <li class="single_field"> <label>Country:</label> <select> <option>United States</option> <option>Bangladesh</option> <option>UK</option> <option>India</option> <option>Pakistan</option> <option>Ucrane</option> <option>Canada</option> <option>Dubai</option> </select> </li> <li class="single_field"> <label>Region / State:</label> <select> <option>Select</option> <option>Dhaka</option> <option>London</option> <option>Dillih</option> <option>Lahore</option> <option>Alaska</option> <option>Canada</option> <option>Dubai</option> </select> </li> <li class="single_field zip-field"> <label>Zip Code:</label> <input type="text"> </li> </ul> <a class="btn btn-default update" href="">Get Quotes</a> <a class="btn btn-default check_out" href="">Continue</a> </div> </div> <div class="col-sm-6"> <div class="total_area"> <ul> <li>Cart Sub Total <span>$59</span></li> <li>Eco Tax <span>$2</span></li> <li>Shipping Cost <span>Free</span></li> <li>Total <span>${{Cart::total()}}</span></li> </ul> <a class="btn btn-default update" href="{{url('clear-cart')}}">Clear Cart</a> <a class="btn btn-default check_out" href="{{url('checkout')}}">Check Out</a> </div> </div> </div> </div> </section><!--/#do_action--> @endsection نستخدم الدالة count لتحديد ما إذا كان المتغير cart يحوي أي عناصر، فإن كان أنشأنا جدولا لعرض عناصر سلة المشتريات ديناميكيا: @if(count($cart)) <table class="table table-condensed">نستخدم الحلقة التكرارية foreach لعدّ عناصر سلة المشتريات: @foreach(item) لكل منتج في سلة المشتريات نعرض اسم المنتج، معرّفه، ثمنه ومجموعا فرعيا Subtotal لتكلفة المنتج على التوالي: {{$item->name}} {{$item->id}} {{$item->price}} {{$item->subtotal}}المجموع الفرعي هو حاصل ضرب ثمن المنتج بكميّته. في الأسفل نعرض الثمن الكلي للمنتجات الموجودة في سلة المشتريات: {{Cart::total()}}تحديث العناصر الموجودة في سلة المشترياتنضيف في هذه الفقرة إمكانية تحديث كمية المنتج في سلة المشتريات بالنقر على زر الزيادة + أو النقصان - بجانب الكمية. سنستخدم لهذا الغرض الاستعلام عن طريق رابط URL. افتح ملف العرض cart.blade.php واعثر على السطرين: <a class="cart_quantity_up" href=""> + </a> <a class="cart_quantity_down" href=""> - </a>حدّث الشفرة المصدرية لكل منهما حتى تصبح على النحو التالي: <a class="cart_quantity_up" href='{{url("cart?product_id=$item->id&increment=1")}}'> + </a> <a class="cart_quantity_down" href='{{url("cart?product_id=$item->id&decrease=1")}}'> - </a>تولد الشفرة: {{url("cart?productid=$item->id&increment=1")}}رابطا بالهيئة http://larashop.dev/cart?productid=1&increment=1. يعيّن الرابط معرّف المنتج الذي نريد تحديث كميته واتجاه التحديث. إذا كان المتسوق يريد زيادة الكمية (نقر على +) نعطي القيمة 1 للمعطى increment؛ أما إذا كان المتسوق يريد نقص الكمية (نقر على -) فنعطي القيمة 1 للمتغير decrease. يجب الآن تعديل المتحكم للتعاطي مع رغبة المتسوق في تحديث الكمية. نعدل الدالة cart لتصبح على النحو التالي: public function cart() { // إضافة منتج جديد إلى سلة المشتريات if (Request::isMethod('post')) { $product_id = Request::get('product_id'); $product = Product::find($product_id); Cart::add(array('id' => $product_id, 'name' => $product->name, 'qty' => 1, 'price' => $product->price)); } // زيادة كمية منتج في سلة المشتريات if (Request::get('product_id') && (Request::get('increment')) == 1) { $rowId = Cart::search(array('id' => Request::get('product_id'))); $item = Cart::get($rowId[0]); Cart::update($rowId[0], $item->qty + 1); } // نقص كمية منتج في سلة المشتريات if (Request::get('product_id') && (Request::get('decrease')) == 1) { $rowId = Cart::search(array('id' => Request::get('product_id'))); $item = Cart::get($rowId[0]); Cart::update($rowId[0], $item->qty - 1); } $cart = Cart::content(); return view('cart', array('cart' => $cart, 'title' => 'Welcome', 'description' => '', 'page' => 'home')); } في الشيفرة أعلاه نتحقق من تعيين المتغيّرين product_id وincrement. if (Request::get('product_id') && (Request::get('increment')) == 1)إذا كان المتغيران معيَّنيْن وقيمة المتغيّر increment تساوي 1 فهذا يعني أن المتسوق أراد زيادة الكمية. يستخدم السطر الموالي معرّف المنتج product_id للبحث بين عناصر سلة المشتريات ويرجع مصفوفة بمعرّفات أسطُر rowId موافقة للبحث: $rowId = Cart::search(array('id' => Request::get('product_id')));معرّف السطر هو معرّف وحيد لعنصُر في السلة يُولّد تلقائيا، ويُستخدَم لتحديث العناصر في السلة. نستخدم معرف العنصر للعثور على كائن يمثل المنتج في سلة المشتريات: $item = Cart::get($rowId[0]);نحتفظ بالعنصر الأول من المصفوفة التي ترجعها الدالة Cart::get، في حالتنا لا يوجد سوى عنصر واحد. نبحث عن كائن المنتج لمعرفة الكمية الموجودة في السلة وبالتالي يمكن لنا زيادتها أو نقصها. نستدعي الدالة Cart::update ونمرر لها معرف المنتج ونزيد الكمية بـ1: Cart::update($rowId[0], $item->qty + 1); السطر الموالي يتحقق من تعيين المتغيرين product_id وdecrease: if (Request::get('productid') && (Request::get('decrease')) == 1)وفي حال كانت الإجابة نعم يفحص قيمة المتغير decrease وإذا كانت 1 ينقص كمية المنتج بـ1 في سلة المشتريات: Cart::update($rowId[0], $item->qty 1); افتح رابط صفحة المنتجات http://larashop.dev/products، اختر أحدها بتمرير المؤشر فوقه وأضفه إلى سلة المشتريات بالنقر على زر Add to cart. ستحصل على النتيجة التالية (تتغير النتيجة حسب المنتج الذي اخترته في الخطوة السابقة). اضغط على الزر + لزيادة كمية منتج في سلة المشتريات. لاحظ تغير المجموع الفرعي مع تغير الكمية. حذف عناصر من سلة المشترياتحذفُ عنصُر من سلة المشتريات مشابه لتحديثه: $rowId = Cart::search(array('id' => Request::get('product_id'))); Cart::remove($rowId[0]); تحذف الدالة Cart::remove عنصرا من سلة المشتريات اعتمادا على معرف السطر الممرَّر إليها. لحذف جميع عناصر سلة المشتريات دفعة واحدة نستخدم الدالة Cart::destroy. خاتمةكان الهدف من هذا الدرس شرح كيفية تثبيت حزمة Laravel ShoppingCart واستخدامها في مشروع Laravel الخاص بك للحصول على ميزة سلة مشتريات. استعملنا خلال هذا الدرس الحزمة في صفحة المنتج، يمكنك التوسع في استخدام الحزمة في صفحات الموقع الأخرى إن أردت. ترجمة -وبتصرّف- لمقال Laravel 5 Shopping Cart لصاحبه Rodrick Kazembe.
-
كثُر الحديث في السنوات الأخيرة عن إطار العمل Laravel وانتشر بين مطوّري PHP حتى إنه أصبح إطار العمل الأكثر استخداما بينهم، سواء للمشاريع الشخصية أو المهنية، حسب استبيان أجراه موقع SitePoint الشهير. كما أنه من أكثر المشاريع التي يُساهَم فيها على GitHub. سنتعرّف في سلسلة الدّروس هذه، التي يمثّل هذا المقال مقدّمة لها، على إطار العمل Laravel وأهم المبادئ التي يعمل وفقا لها. لماذا Laravel؟توجد الكثير من الأسباب التي تدعو لاختيار Laravel منها ماهو نفعيّ (انتشار أكبر يعني فرصًا أكثر للحصول على فرص توظيف) ومنها ماهو تقني بحت. قبل الإجابة على السؤال "لماذا Laravel؟" قد يكون من المفيد محاولة الإجابة عن "لماذا إطار عمل؟" بمعنى آخر ألا يمكنك كمطوّر PHP البدء من الصفر وبناء تطبيقك حسب الحاجة؟ يمكننا القول -باختصار- أن أطر العمل تجعلك تتخلص من ضرورة الاعتناء بتفاصيل كثيرة، ترفع كثيرا من إنتاجيتك وتقيك من أخطاء التعامل المباشر مع بيئة لغة البرمجة من قبيل أخطاء التعامل مع استعلامات قواعد البيانات التي قد تنتج عنها هجمات الحقن بتعليمات SQL المعروفة بــSQL injection. تفرِض أطر العمل الجيدة على مستخدميها الفصل بين أجزاء التطبيق وتنفيذ بنية Architecture مجرَّبة تؤدي في النهاية إلى الرفع من تصميم التطبيق وجعل الشفرة المصدرية أيسر في القراءة وأسهل في الصيانة والاختبار. نجمل في ما يلي أهم الأسباب التي تجعل من اختيار Laravel مناسبًا: سهولة الاستخدام.الفصل بين عناصر التطبيق مما يسهل عمل فريق من المطوّرين وتقاسم المهامّ بينهم.دعم التطوير السريع للتطبيقات Rapid Application Developing, RAD: توفر أداة Artisan وسيلة سريعة لإنشاء شفرة مصدرية نمطية للتعديل المباشر عليها. كما أنها تُستخدَم لمهامّ أخرى مثل تشغيل الاختبارات الأحادية Unit tests، تهجير قواعد البيانات، وغيرها.التضمين الافتراضي لوظائف شائعة الاستخدام في تطبيقات الويب، مثل الاستيثاق Authentication، التوجيه Routing، إدارة قواعد البيانات، إرسال البريد الإلكتروني.متحكمات RESTful: يعني هذا أنه يمكن الاستفادة من أفعال HTTP القياسية مثل PUT، POST، GET وDELETE.إدارة الاعتماديات Dependencies باستخدام Composer وهو ما يعني إمكانية استخدام الحزم والمكتبات الموجودة على الموقع www.packagist.com بيُسر ضمن مشاريعك.استخدام إطار العمل Eloquent وهو إطار للربط بين الكائنات في قاعدة البيانات والتصنيفات في شفرة التطبيق Object Relational Mapper.بنية MVCتتبع المشاريع في إطار العمل Laravel بنية Model-View-Controller (تُختصر بـMVC) التي تقسّم التطبيق إلى ثلاثة أجزاء متصلة في ما بينها، وتُفرّق بين التمثيل الداخلي للمعلومة والطرق التي تُقدَّم بها المعلومة إلى المستخدم. يقع جزء النموذج Model في قلب بنية التطبيق؛ تُعالَج في هذا القسم البيانات وتُنفَّذ عليها القواعد (في تطبيق محاسبة تنتمي الفواتير والعمليات عليها إلى هذا الجزء). يتولى الجزء الثاني في البنية وهو العرض View مهمة تقديم البيانات إلى المستخدم. يمكن أن تأخذ نفس البيانات أشكالا عدّة للعرض (مخطّط بياني، جدول، … إلخ). أما الجزء الثالث (المتحكِّم Controller) فيأخذ مُدخلات ويحوّلها إلى أوامر يرسلها للنموذج والعرض. يمكن شرح الأمر على النحو التالي: يرسل المتحكّم أمرا إلى النموذج لتعديل حالته (تحرير فاتورة). كما يمكنه إرسال أمر إلى العرض بتغيير طريقة تقديم البيانات (الانتقال بين أسطر الفاتورة).يخزّن النموذج البيانات المعثور عليها وفقا لأوامر المتحكّم وتلك المعروضة في العرض.يولّد العرض مخرجات للمستخدم حسب البيانات الخزّنة في النموذج.يجمل المخطط البياني التالي آلية العمل. سلسلة دروس Laravel: إنشاء تطبيق ويب للتسوقننطلق في سلسلة الدروس هذه من مبدأ أن أفضل وسيلة للتعلم هي الممارسة، لذا سنبدأ خطوة خطوة بإنشاء تطبيق ويب للتسوق مع ميزة سلة المشتريات. تعطي الصورة التالية فكرة عن شكل الموقع الذي نريد إنشاءه. تشمل السلسلة المواضيع التالية: تثبيت Laravel وإعداده على كلّ من Windows وUbuntu.أساسيات بناء تطبيق باستخدام Laravel.إنشاء روابط محسنة لمحركات البحث (SEO) في إطار عمل Laravel.نظام Blade للقوالب.تهجير قواعد البيانات في Laravel.استخدام Eloquent ORM لإدخال البيانات في قاعدة البيانات، تحديثها أو حذفها.إنشاء سلة مشتريات في Laravel.الاستيثاق في Laravel.إنشاء واجهة لبرمجة التطبيقات API في Laravel.إنشاء مدوّنة باستخدام Laravel.استخدام AngularJS واجهةً أمامية Front end لتطبيق Laravel.الدوّال المساعدة المخصّصة في Laravel.استخدام مكتبة Faker في تطبيق Laravel لتوليد بيانات وهمية قصدَ الاختبار.ترجمة -بتصرّف- لمقال Laravel 5 Tutorial لصاحبه Rodrick Kazembe.
-
نحن نصبح اليوم أكثر وأكثر انشغالًا باللحاق بوتيرة عالمنا المتسارعة، ونتلقى على الدوام كمًا كبيرًا من المعلومات المختلفة بأنواعها ومصادرها مضافة إليها الأفكار المتنوعة المتعلّقة بحياتنا العملية والشخصية والتي تراودنا يوميًا. ومع هذا الكم من المعلومات والأفكار، يصعب على عقولنا استيعابها وتذكرها، مما يجعلنا بحاجة إلى وسيلة لخزنها وتنظيمها. لحسن الحظ، هناك العديد من الأدوات والتطبيقات لحفظ الملاحظات مثل OneNote ،Google Keep ،Simplenote، إلخ. يتميّز من بين هذه الأدوات تطبيق Evernote الذي يستخدم لتسجيل الملاحظات، تخزينها وتنظيمها. يمكن أن تكون الملاحظات أي شيء من النصوص إلى الصور إلى الملفات الصوتية، كما يمكن أن تكون صفحات ويب أو رسائل بريد إلكتروني. ويمكن إنشاء هذه الملاحظات والوصول إليها من مختلف الأجهزة كأجهزة سطح المكتب، الهواتف، أو الأجهزة اللوحية. حيث يدعم التطبيق أنظمة Windows، Mac ،iOS Android، وكذلك الويب. وبالإضافة إلى حفظ الملاحظات وتنظيمها، يتيح التطبيق أيضًا إمكانية التعاون بين الزملاء والأصدقاء من خلال خاصية المشاركة والنشر. سنساعدك من خلال هذا الدرس، والدروس القادمة، على التعرّف على Evernote والخصائص التي يوفّرها لتدوين كل ملاحظاتك وأفكارك والاحتفاظ بها على منصة سهلة البحث والوصول. ملاحظة: توجد ثلاثة إصدارات من Evernote: Basic وهو مجاني Plus بسعر 25$/السنة Premium بسعر 50$/السنة ويمكنك الاطلاع على الخصائص المُتاحة مع كل إصدار بزيارة الصفحة الخاصة بالتطبيق Evernote. تنصيب Evernote كما ذكرنا أعلاه، يعمل Evernote على مختلف الأجهزة، ولتنصيبه على حاسوبك اذهب إلى صفحة تنزيل Evernote: اختر نوع الجهاز من القائمة، وسيتم تنزيل البرنامج على جهازك. قم بتنصيبه ثم التسجيل بإدخال عنوان بريدك الإلكتروني والكلمة السرية: بإمكانك أيضًا تنصيب التطبيق على أجهزتك المحمولة من متجر App Store أو متجر Google Play. كما بإمكانك استخدام التطبيق دون تنصيبه من خلال إصدار الويب. واجهة Evernote واضحة وسهلة، وهي تحتوي على شريط قوائم، شريط أدوات، بالإضافة إلى الجزء الأيسر Left Pane، قائمة الملاحظات Note List، ولوحة معاينة الملاحظات Note Panel التي تُعرض بشكل افتراضي عند فتح البرنامج لأول مرّة: بإمكانك التحكّم في عرض وإخفاء هذه الأجزاء من قائمة View: يضم الجزء الأيسر مجموعات دفاتر الملاحظات Notebooks والوسوم Tags التي تستخدم لتصنيف وتنظيم الملاحظات، بالإضافة إلى دفتر الملاحظات المحذوفة Trash. كيفية إنشاء الملاحظات الملاحظة هي عبارة عن أي شيء تريد حفظه من أجل الرجوع إليه لاحقًا عند الحاجة. بإمكانك حفظ ملاحظات نصية أو صوتية. كما بإمكانك حفظ صور، ملفات PDF، جداول بيانات، مستندات وورد، وغيرها من أنواع الملفات. وبذلك يصبح الوصول إليها سهلا من على أي جهاز. لإنشاء ملاحظة انقر على زر New Note من شريط الأدوات (أو اضغط على مفتاحي Ctrl+N): ستُفتح نافذة مستقلة تحتوي على مجموعة خيارات وأدوات خاصة بإنشاء وتنسيق ملاحظتك: ابدأ بإدخال عنوان للملاحظة في حقل Title، واختر دفتر الملاحظات الذي تريد حفظ الملاحظة فيه ثم قم بإضافة وسم لتصنيف الملاحظة بالنقر على Add Tag. إذا لم تقم بتحديد دفتر ملاحظات معيّن، سيتم حفظ الملاحظة تلقائيًا في دفتر الملاحظات الافتراضي الذي يُنشأ عند تنصيب البرنامج. ملاحظة: تستخدم الوسوم ودفاتر الملاحظات لتنظيم الملاحظات، وسنخصص لها درسًا إن شاء الله. أدوات تنسيق النصوص مثل نوع الخط، حجمه، سمكه، إلخ. يمكنك استخدام أداة التمييز اللوني Highlight إذا كنت تريد التركيز على عنصر معيّن في الملاحظة، أو استخدام Code Block لاحتواء الشيفرات إذا كانت ملاحظتك تحتوي على نصوص برمجية. أدوات تخطيط ومحاذاة نصوص الملاحظة. يمكنك استخدام التعداد النقطي أو التعداد الرقمي. وإذا كانت لديك مجموعة من العناصر تريد التركيز على إنهائها، قم بإنشاء قائمة تدقيق/مراجعة checklist ثم قم بتأشير العنصر الذي انتهيت منه. هناك أيضًا أدوات التحكم في محاذاة النص والمسافات البادئة. أدوات إدراج جدول بعدد صفوف وأعمدة محدد، أو إدراج خط فاصل. أدوات إرفاق ملفات متنوّعة أو تسجيل ملف صوتي. بإمكانك إرفاق الملفات بالنقر على زر مشبك الورق واختيار الملف، أو سحبه مباشرة من مجلد الحفظ وإفلاته في حقل تحرير الملاحظة. ولتسجيل ملاحظة صوتية انقر على أيقونة الميكروفون ثم على زر Record. أداة تكرار الأمر الأخير؛ أي تنفيذ آخر أمر تم استخدامه. إذا كانت ملاحظتك تتطلب الرجوع إليها في وقت محدّد، بإمكانك إضافة تذكير بوقت وتاريخ معيّنين بالنقر على زر السهم أمام عنوان الملاحظة ثم اختيار Reminder > Add Date لتحديد الوقت والتاريخ المرغوبين: لا يوجد هناك أمر لحفظ الملاحظة، لأنّها تُحفظ تلقائيًا كلّما قمت بتعديل محتوياتها. عند الانتهاء من إنشاء الملاحظة أغلق النافذة. تتم مزامنة الملاحظات مع الأجهزة الأخرى التي تستخدمها بشكل تلقائي أيضًا وكل فترة زمنية معيّنة. مع ذلك بإمكانك عمل المزامنة يدويًا بالنقر على أيقونة المزامنة Sync: إذا كنت ترغب في تغيير فترة المزامنة التلقائية للملاحظات بصورة عامة اذهب إلى: Tools > Options من تبويب Sync تأكّد من تأشير الخيار Synchronize automatically ثم اختر الفترة الزمنية المرغوبة من القائمة: بإمكانك تغيير طريقة عرض الملاحظات في قائمة الملاحظات، أو تغيير طريقة ترتيبها (حسب تاريخ الإنشاء أو التحديث أو حسب العنوان) وفرزها (حسب العنوان، دفتر الملاحظات، الوسم، إلخ) بالنقر على أيقونة المربعات واختيار الخيار المرغوب: تحرير الملاحظات بإمكانك تحرير الملاحظات التي قمت بإنشائها بنفسك أو التي قام أحد زملائك بمشاركتها معك (بشرط أن يكون نوع الترخيص عند المشاركة "Can Edit" أو "Can Edit and Invite"). انقر على الملاحظة بشكل مزدوج لفتحها. قم بإجراء التغييرات المرغوبة على النصوص، وبإمكانك استخدام الأوامر في قائمة Edit كالنسخ Copy، اللصق Paste، التراجع والإلغاء Undo & Redo أو الحذف Delete. تأكد من تحديد النص أو العنصر قبل أن تقوم بتطبيق الأمر: وكذلك لديك خيار البحث والاستبدال Find and Replace والتدقيق اللغوي Check Spelling. تتوفّر أيضًا مجموعة أدوات لتحرير الصور والتأشير عليها بإضافة أسهم، خطوط، تعليقات، أشكال، إلخ. مرر مؤشر الفأرة فوق الصورة التي تريد تحريرها ثم انقر على أيقونة تحرير الصورة (حرف a داخل دائرة): قم بإضافة ما يلزمك من تأشيرات على الصورة ثم أغلق النافذة. وإذا رغبت في إزالة جميع التأشيرات التي قمت بإضافتها افتح محرر الصورة ثم اذهب إلى: Edit > Clear Annotations بنفس الطريقة يمكنك التأشير على ملفات PDF. حذف الملاحظات لحذف ملاحظة من قائمة الملاحظات حدد الملاحظة ثم اذهب إلى: Edit > Delete (أو انقر على مفتاح Delete): لن يتم حذف الملاحظة نهائيًا، بل سيتم نقلها إلى دفتر الملاحظات المحذوفة Trash. يتم مزامنة مجلد Trash عبر جميع الأجهزة التي تستخدم Evernote عليها، لكن إذا قمت بحذف ملاحظة من دفتر ملاحظات محلي* لن يتم مزامنة تلك الملاحظة المحذوفة. *دفتر الملاحظات المحلي Local Notebook: هو دفتر الملاحظات الذي يحفظ على الجهاز الذي أنشأته عليه فقط ولا تتم مزامنته مع Evernote Service. ويُنشأ عبر المسار File> New Local Notebook. إذا رغبت في استرجاع الملاحظة المحذوفة، انقر عليها بزر الفأرة الأيمن واختر Restore Note: أمّا إذا رغبت في حذف الملاحظة من سلة المحذوفات بشكل نهائي، انقر عليها بزر الفأرة الأيمن واختر Expunge Note. كن حذرًا عن استخدام هذا الخيار لأنّك لن تتمكن من استرجاع الملاحظات المحذوفة بهذه الطريقة: بإمكانك أيضًا حذف جميع الملاحظات في مجلد Trash بشكل شامل ونهائي بالنقر عليه بزر الفأرة الأيمن واختيار Empty Trash: عرض الملاحظات باستخدام وضع العرض يعتبر وضع العرض Presentation Mode من الأدوات الرائعة التي تفيد في توفير الوقت إذا كنت ترغب في مشاركة المعلومات التي قمت بحفظها في ملاحظاتك مع زملائك أو أصدقائك. بإمكانك عرض الملاحظة على جهازك أو على شاشة عرض. هذه الخاصية تتوفر لمستخدمي الإصدار المدفوع Evernote Premium أو إصدار الشركات Evernote Business من التطبيق، لكنها تُتاح للمستخدمين الجدد كتجربة مجانية لمدة 30 يوم. لعرض ملاحظة معينة باستخدام وضع العرض انقر على زر السهم أمام عنوان الملاحظة واختر Present: سيقوم التطبيق بتعديل الخط ليناسب حجم الشاشة كما سيقسّم الملاحظة (إن اقتضت الحاجة) إلى عدة صفحات يمكنك تصفّحها بالضغط على مفتاح Space: بإمكانك اختيار نوع المؤشر وتشغيل الوضع الليلي من الأزرار في الزاوية العليا اليمنى. ولإغلاق وضع العرض انقر على أيقونة ×.
-
يهدف هذا الدرس إلى بناء تطبيق CRUD (إدراج بيانات في قاعدة بيانات، القراءة منها، تحديثها وحذفها؛ اختصار لـUpdate، Read، Create وDelete). التطبيق عبارة عن واجهة لإدارة لائحة من الموظفين: إضافة موظّف، تحرير بياناته أو حذفها. سنستخدم Laravel 5 للنهاية الخلفية Backend و AngularJS للنهاية الأمامية Frontend. سنستخدم أيضا Bootstrap لإضافة لمسة جمالية إلى التطبيق. هذا الدرس جزء من سلسلة تعلم Laravel والتي تنتهج مبدأ "أفضل وسيلة للتعلم هي الممارسة"، حيث ستكون ممارستنا عبارة عن إنشاء تطبيق ويب للتسوق مع ميزة سلة المشتريات. يتكون فهرس السلسلة من التالي: مدخل إلى Laravel 5.تثبيت Laravel وإعداده على كلّ من Windows وUbuntu.أساسيات بناء تطبيق باستخدام Laravel.إنشاء روابط محسنة لمحركات البحث (SEO) في إطار عمل Laravel.نظام Blade للقوالب.تهجير قواعد البيانات في Laravel.استخدام Eloquent ORM لإدخال البيانات في قاعدة البيانات، تحديثها أو حذفها.إنشاء سلة مشتريات في Laravel.الاستيثاق في Laravel.إنشاء واجهة لبرمجة التطبيقات API في Laravel.إنشاء مدوّنة باستخدام Laravel.استخدام AngularJS واجهةً أمامية Front end لتطبيق Laravel. (هذا الدرس)الدوّال المساعدة المخصّصة في Laravel.استخدام مكتبة Faker في تطبيق Laravel لتوليد بيانات وهمية قصدَ الاختبار. AngularJS هو إطار عمل جافاسكريبت قويّ وفعّال، يتبنى مبدأ النموذج-العرض-المتحكم MVC ويعمل لدى العميل (المتصفّح). للاطلاع على المزيد عن AngularJS تابع سلسلة دروس مدخل إلى تعلم AngularJS . يفترض هذا الدرس معرفة أساسيات Laravel 5، تثبيت كلّ من PHP، MySQL، ِApache وComposer. يغطي الدرس المواضيع التالية: إنشاء نهاية خلفية في Laravel 5، ستكون عبارة عن واجهة برمجية API.بنية تطبيق AngularJS.واجهة برمجية باستخدام Laravel 5ننشئ في هذه الفقرة النهاية الخلفية للتطبيق الذي نعمل عليه. سيتولى Laravel هذه المهمة. نبدأ بإنشاء تطبيق Laravel ثم نعدّه لتوفير الواجهة البرمجية. الخطوة الأولى: إنشاء تطبيق Laravelننفذ الأمر التالي في أصل المستند Document root لإنشاء مشروع Laravel باسم angulara: composer create-project laravel/laravel angularaراجع درس تثبيت Laravel 5 وإعداده على Windows وUbuntu للمزيد حول تثبيت Laravel وإعداده. سنفترض في بقية هذا الدرس تثبيت Laravel على المنصة المفضلة لديك وإنشاء مضيف افتراضي باسم angulara.dev. الخطوة التالية هي إعداد تهجير قاعدة البيانات. الخطوة الثانية: إعداد قاعدة البياناتستحتاج لقاعدة بيانات للعمل مع التطبيق. استخدم برنامج إدارة قاعدة البيانات المفضل لديك أو نفذ الأمر التالي في سطر أوامر MySQL لإنشاء قاعدة بيانات للمشروع: CREATE DATABASE `angulara`;ننتقل الآن لإعداد Laravel لكي يعمل مع قاعدة البيانات. افتح ملف env. في مجلد تطبيق Laravel وعدل القيم التالية بما يناسب: DB_HOST=localhost // اسم قاعدة البيانات DB_DATABASE=angulara // اسم مستخدم في MySQL لديه صلاحيات الكتابة والقراءة في القاعدة أعلاه DB_USERNAME=root // كلمة سر المستخدم DB_PASSWORD=melodyاحفظ التعديلات. نستغل أداة Artisan لإنشاء ملف تهجير خاص بجدول يحوي بيانات الموظفين employees. نفذ الأمر التالي لإنشاء ملف التهجير: php artisan make:migration create_employees_tableستحصُل على الرسالة التالية التي تعرض اسم الملف المنشأ: Created Migration: 2016_01_05_203637_create_employees_tableينشئ الأمر ملفا في المجلد database/migrations، نفتحه للتعديل عليه. عدّل المحتوى كالتالي: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateEmployeesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('employees', function (Blueprint $table) { $table->increments('id'); $table->string('name')->unique(); $table->string('email')->unique(); $table->string('contact_number'); $table->string('position'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('employees'); } }احفظ التعديلات ثم نفذ أمر ِArtisan التالي: php artisan migrateينفذ الأمر التهجيرات وينشئ جدول بيانات الموظفين. تظهر الرسائل التالية بعد تنفيذ الأمر: Migration table created successfully. Migrated: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_100000_create_password_resets_table Migrated: 2016_01_05_203637_create_employees_tableتحقق من قاعدة بيانات MySQL، ستجد أن جدولا جديدا أُدرِج في القاعدة. الخطوة الثالثة: إنشاء واجهة برمجيةننشئ متحكما Controller للتطبيق، سنسميه Employees: php artisan make:controller Employeesثم يأتي دور النموذج Model: php artisan make:model Employeeسنضيف مصفوفة في النموذج لتفعيل الإسناد الشامل؛ افتح ملف النموذج وعدّله على النحو التالي: <?php namespace App; use Illuminate\Database\Eloquent\Model; class Employee extends Model { protected $fillable = array('id', 'name', 'email','contact_number','position'); }احفظ التعديلات. ننتقل للتعديل على المتحكم. افتح ملف المتحكم Employees وعدله ليصبح كالتالي <?php namespace App\Http\Controllers; use App\Employee; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller; class Employees extends Controller { /** * Display a listing of the resource. * * @return Response */ public function index($id = null) { if ($id == null) { return Employee::orderBy('id', 'asc')->get(); } else { return $this->show($id); } } /** * Store a newly created resource in storage. * * @param Request $request * @return Response */ public function store(Request $request) { $employee = new Employee; $employee->name = $request->input('name'); $employee->email = $request->input('email'); $employee->contact_number = $request->input('contact_number'); $employee->position = $request->input('position'); $employee->save(); return 'Employee record successfully created with id ' . $employee->id; } /** * Display the specified resource. * * @param int $id * @return Response */ public function show($id) { return Employee::find($id); } /** * Update the specified resource in storage. * * @param Request $request * @param int $id * @return Response */ public function update(Request $request, $id) { $employee = Employee::find($id); $employee->name = $request->input('name'); $employee->email = $request->input('email'); $employee->contact_number = $request->input('contact_number'); $employee->position = $request->input('position'); $employee->save(); return "Sucess updating user #" . $employee->id; } /** * Remove the specified resource from storage. * * @param int $id * @return Response */ public function destroy($id = null) { if ($id == null) { } else { $employee = Employee::find($id); $employee->delete(); return "Employee record successfully deleted #" . $id; } } }يعرِّف المتحكم الدوالّ index لسرد قائمة بالموظفين، store لإضافة موظف جديد، show لعرض موظف بناء على معرّفه، update لتحديث بيانات موظّف وdestroy لحذف موظّف. تبقى لنا إعداد المسارات Routes في Laravel. افتح ملف المسارات routes.php وعدله كالتالي: <?php /* |-------------------------------------------------------------------------- | Application Routes |-------------------------------------------------------------------------- | | Here is where you can register all of the routes for an application. | It's a breeze. Simply tell Laravel the URIs it should respond to | and give it the controller to call when that URI is requested. | */ Route::get('/', function () { return view('index'); }); Route::get('/api/v1/employees/{id?}', 'Employees@index'); Route::post('/api/v1/employees', 'Employees@store'); Route::post('/api/v1/employees/{id}', 'Employees@update'); Route::delete('/api/v1/employees/{id}', 'Employees@destroy');لاحظ استخدام إجراءات HTTP وفقا للمبدأ المشروح في درس إنشاء واجهة برمجية API في Laravel 5. يختلف التعامل مع المسار api/v1/employees حسب إجراء HTTP المستخدم: get يُستخدم للحصول على معلومات موظف أو قائمة بالموظفين، post لإضافة موظف جديد، put لتحديث بيانات موظّف وdelete لحذف موظّف. أكملنا الآن جانب النهاية الخلفية بإنشاء واجهة برمجية. ننتقل للواجهة الأمامية التي ستستخدم الواجهة البرمجية للحصول على بيانات الموظفين وتنسيقها قبل عرضها في المتصفح. بنية تطبيق AngularJSسيأخذ مجلد public (الواجهة الأمامية لتطبيق Laravel) الشكل التالي: يحوي المجلّد الفرعي app ملفات جافاسكريبت التابعة لـAngularJS.توجد متحكمات AngularJS في المجلّد app/controllers.توجد ملفات نواة AngularJS في المجلّد app/lib. توجد أيضا إمكانية استخدام شبكة توصيل محتوى Content delivery network, CDN.توجد ملفات CSS في المجلد css.ملفات جافاسكريبت غير التابعة لـAngularJS توجد في المجلد js.توجد ملفات الواجهة الأمامية في الملف المرفق، سنشرح ما يحتاج لشرح منها. الملف app.jsسنستخدم الملف public/app/app.js لتعريف التطبيق الخاص بنا: var app = angular.module('employeeRecords', []) .constant('API_URL', 'http://angulara.dev/api/v1/');ننشئ وحدة Module في AngularJS ونسند الكائن إلى المتغير app. سيُستخدم هذا المتغير في جميع ملفات AngularJS؛ ثم نعرّف ثابتا Constant لرابط الواجهة البرمجية API_URL. ملحوظة: إن كنت اخترت اسما مختلفا للمضيف فضعه مكان angulara.dev. المتحكم Employees.jsسيكون هذا الملف مسؤولا عن التفاعل مع عروض تطبيق AngularJS: app.controller('employeesController', function($scope, $http, API_URL) { // الحصول على لائحة الموظفين $http.get(API_URL + "employees") .success(function(response) { $scope.employees = response; }); // عرض نافذة منبثقة $scope.toggle = function(modalstate, id) { $scope.modalstate = modalstate; switch (modalstate) { case 'add': $scope.form_title = "Add New Employee"; break; case 'edit': $scope.form_title = "Employee Detail"; $scope.id = id; $http.get(API_URL + 'employees/' + id) .success(function(response) { console.log(response); $scope.employee = response; }); break; default: break; } console.log(id); $('#myModal').modal('show'); } //save new record / update existing record $scope.save = function(modalstate, id) { var url = API_URL + "employees"; var request_method = 'POST'; //append employee id to the URL if the form is in edit mode if (modalstate === 'edit'){ url += "/" + id; request_method = 'PUT'; } $http({ method: request_method, url: url, data: $.param($scope.employee), headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(response) { console.log(response); location.reload(); }).error(function(response) { console.log(response); alert('This is embarassing. An error has occured. Please check the log for details'); }); } //delete record $scope.confirmDelete = function(id) { var isConfirmDelete = confirm('Are you sure you want this record?'); if (isConfirmDelete) { $http({ method: 'DELETE', url: API_URL + 'employees/' + id }). success(function(data) { console.log(data); location.reload(); }). error(function(data) { console.log(data); alert('Unable to delete'); }); } else { return false; } } });نعرِّف متحكما باسم employeesController في المتغيّر app الذي أنشأناه في الملف app.js. حدّدنا الاعتماديات بـ $http، scope$ وAPI_URL. نستدعي خدمة http$ في AngularJS لإرسال طلب إلى الواجهة البرمجية، مع تمرير المعطى API_URL + "employees": $http.get(API_URL + "employees").success(function(response) {$scope.employees = response;});عند نجاح الطلب نُسنِد الإجابة إلى المتغير scope.employees$ الذي سيصبح متاحا في العرض View لإظهار محتواه. تعرض الدالة: $scope.toggle = function(modalstate, id) {…}نافذة منبثقة تحوي استمارة لإضافة موظّف جديد أو تعديل بيانات موظّف موجود حسب الحالة. نستخدم الدالة: $scope.save = function(modalstate, id){…}في حالتيْ الإضافة والتعديل. بالنسبة لإضافة موظّف نستخدم إجراء POST؛ وفي حالة التعديل نستخدم الإجراء PUT. تحذف الدالة: $scope.confirmDelete = function(id){…}بيانات موظّف من جدول الموظفين. عرض البيانات المتحصل عليها من الواجهة البرمجية بـAngularJSسننشئ عرضا لإظهار البيانات التي نتحصل عليها من الواجهة البرمجية؛ يستخدم كل من angularJS وBlade الحاضنات المزدوجة {{}} لعرض البيانات، لذا لن نحفظ عرض AngularJS بلاحقة blade.php حتى لا يعدّ نظام Blade العرض موجها له. محتوى العرض هو التالي، نحفظ الملف index.php في ملف العروض resources/views حتى يُحمّل عند طلبه من المسار / (الصفحة الرئيسية للموقع): <!DOCTYPE html> <html lang="en-US" ng-app="employeeRecords"> <head> <title>Laravel 5 AngularJS CRUD Example</title> <!-- Load Bootstrap CSS --> <link href="<?= asset('css/bootstrap.min.css') ?>" rel="stylesheet"> </head> <body> <h2>Employees Database</h2> <div ng-controller="employeesController"> <!-- Table-to-load-the-data Part --> <table class="table"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Email</th> <th>Contact No</th> <th>Position</th> <th><button id="btn-add" class="btn btn-primary btn-xs" ng-click="toggle('add', 0)">Add New Employee</button></th> </tr> </thead> <tbody> <tr ng-repeat="employee in employees"> <td>{{ employee.id }}</td> <td>{{ employee.name }}</td> <td>{{ employee.email }}</td> <td>{{ employee.contact_number }}</td> <td>{{ employee.position }}</td> <td> <button class="btn btn-default btn-xs btn-detail" ng-click="toggle('edit', employee.id)">Edit</button> <button class="btn btn-danger btn-xs btn-delete" ng-click="confirmDelete(employee.id)">Delete</button> </td> </tr> </tbody> </table> <!-- End of Table-to-load-the-data Part --> <!-- Modal (Pop up when detail button clicked) --> <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title" id="myModalLabel">{{form_title}}</h4> </div> <div class="modal-body"> <form name="frmEmployees" class="form-horizontal" novalidate=""> <div class="form-group error"> <label for="inputEmail3" class="col-sm-3 control-label">Name</label> <div class="col-sm-9"> <input type="text" class="form-control has-error" id="name" name="name" placeholder="Fullname" value="{{name}}" ng-model="employee.name" ng-required="true"> <span class="help-inline" ng-show="frmEmployees.name.$invalid && frmEmployees.name.$touched">Name field is required</span> </div> </div> <div class="form-group"> <label for="inputEmail3" class="col-sm-3 control-label">Email</label> <div class="col-sm-9"> <input type="email" class="form-control" id="email" name="email" placeholder="Email Address" value="{{email}}" ng-model="employee.email" ng-required="true"> <span class="help-inline" ng-show="frmEmployees.email.$invalid && frmEmployees.email.$touched">Valid Email field is required</span> </div> </div> <div class="form-group"> <label for="inputEmail3" class="col-sm-3 control-label">Contact Number</label> <div class="col-sm-9"> <input type="text" class="form-control" id="contact_number" name="contact_number" placeholder="Contact Number" value="{{contact_number}}" ng-model="employee.contact_number" ng-required="true"> <span class="help-inline" ng-show="frmEmployees.contact_number.$invalid && frmEmployees.contact_number.$touched">Contact number field is required</span> </div> </div> <div class="form-group"> <label for="inputEmail3" class="col-sm-3 control-label">Position</label> <div class="col-sm-9"> <input type="text" class="form-control" id="position" name="position" placeholder="Position" value="{{position}}" ng-model="employee.position" ng-required="true"> <span class="help-inline" ng-show="frmEmployees.position.$invalid && frmEmployees.position.$touched">Position field is required</span> </div> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-primary" id="btn-save" ng-click="save(modalstate, id)" ng-disabled="frmEmployees.$invalid">Save changes</button> </div> </div> </div> </div> </div> <!-- Load Javascript Libraries (AngularJS, JQuery, Bootstrap) --> <script src="<?= asset('app/lib/angular/angular.min.js') ?>"></script> <script src="<?= asset('js/jquery.min.js') ?>"></script> <script src="<?= asset('js/bootstrap.min.js') ?>"></script> <!-- AngularJS Application Scripts --> <script src="<?= asset('app/app.js') ?>"></script> <script src="<?= asset('app/controllers/employees.js') ?>"></script> </body> </html>نربط تطبيق AngularJS بوسم html حتى يكون له كامل التحكّم في العناصر الموجودة في الصفحة: <html lang="en-US" ng-app="employeeRecords"> نفس الشيء بالنسبة للمتحكم employeesController الذي نربطه بعنصر div حتى نجعل جميع دوال المتحكم متاحة في هذا العنصر: <div ng-controller="employeesController">نستخدم تعليمة ng-repeat للمرو على جميع عناصر المجموعة employees: <tr ng-repeat="employee in employees">تعمل ng-repeat بطريقة مشابهة لعمل الحلقة التكرارية foreach. التحقق من بيانات استمارات AngularJSاستخدمنا في استمارات الموظفين طرقا للتحقق من المُدخلات. اعثر على الشفرات التالية في ملف العرض index.php ولاحظ استخدامها: <form name="frmEmployees" class="form-horizontal" novalidate="">تعرّف هذه الشفرة استمارة باسم frmEmployees وتضيف إليها خاصيّة novalidate للطلب من HTML 5 الامتناع عن تدقيق الاستمارة، سنتولى الأمر. <input type… ng-model="employee.name" ng-required="true">تستخدم تعليمة ng-model لربط حقول الاستمارة ببيانات النموذج Employee. ربطنا الحقل أعلاه بخاصية name في النموذج. بهذه الطريقة يكون محتوى الحقل متاحا للنموذج، والعكس أيضا. تدل التعليمة ng-required أن الحقل مطلوب. إن لم تذكر قيمة لهذا الحقل فلن يمكن إرسال الاستمارة. <span class="help-inline" ng-show="frmEmployees.name.$invalid && frmEmployees.name.$touched">لا يظهر هذا العنصُر إلا إذا أخفق التحقق من حقل name قبله. تظهر رسالة تفيد أن قيمة الحقل مطلوبة. ng-disabled="frmEmployees.$invalid" تعطّل هذه التعليمة إمكانية إرسال الاستمارة عند إخفاق التحقق من أحد الحقول المطلوبة. حان الآن وقت تجربة ما عملناه في الخطوات السابقة، افتح الموقع (بالنسبة لي اخترتُ إنشاء مضيف افتراضي angulara.dev): http://angulara.dev/ستحصُل، إن اتبعت الخطوات السابقة، على الواجهة التالية: اضغط على زر Add New Employee لإضافة موظّف. ستظهر نافذة منبثقة لإضافة بيانات الموظّف. تصبح الواجهة بعد إضافة موظفين على النحو التالي: تمكّن الأزرار edit و delete الموجودة في كل سطر تحديثَ أو حذف بيانات موظّف. الملف المرفق: مجلد public. ترجمة -وبتصرّف- لمقال Laravel 5 AngularJS Tutorial لصاحبه Rodrick Kazembe.
-
أكملنا في الجزء السابق من هذه السّلسلة إعداد التطبيق المثال؛ يجب علينا الآن تصميم خطة استرداد Recovery plan. خطة الاسترداد (أو الاستعادة) عبارة عن مجموعة موثقة من العمليات التي يجب تنفيذها لتصحيح إعدادات بيئة العمل بعد حدوث مشاكل مزمنة أو عند حصول أخطاء إدارية. يساعد إنشاء خطة استرداد أيضا على تحديد العناصر والبيانات الأساسية ضمن إعدادات خادوم التطبيق. يمكن أن تتمثل خطة استرداد قاعدية من إخفاق بيئة العمل في لائحة بالخطوات الواجب اتباعها لإنجاز النشر الابتدائي للخادوم؛ إلى جانب عمليات إضافية لاستعادة بيانات التطبيق من النسخ الاحتياطية. للحصول على خطة استرداد أفضل يجب، إضافة للتوثيق الجيد، استغلال سكربتات النشر وأدوات إدارة الإعدادات مثل Chef، Ansible وPuppet؛ للمساعدة في أتممة وتسريع الاسترداد. سنوضح في هذا الجزء من السّلسلة كيفية إنشاء خطة استرداد قاعدية لتطبيق ووردبريس الذي أعددناه. سيساعدك الشرح في البدء بتصميم خطة استرداد خاصة بك حتى ولو كانت احتياجاتك مختلفة. متطلبات خطة الاستردادمطلبنا الأساسي هو إمكانية استرداد بيئة العمل بعد فقدان واحد من الخواديم المكوّنة لها، واستعادة وظيفة التطبيق وبياناته؛ إلى حد زمني مقبول. سننشئ جردًا لإعدادات كل خادوم نحدد من خلاله البيانات التي نحتاج لنسخ احتياطية منها؛ ثم نكتب خطة استرداد اعتمادا على ما يوجد في الخادوم. يجب أن نتحقق بعد تنفيذ أي واحدة من خطط الاسترداد أن التطبيق استُرِدّ بطريقة صحيحة. سنخرج بخطة استرداد لكل نوع من الخواديم التي يتكون منها التطبيق: خادوم قاعدة البيانات.خواديم التطبيق.خادوم توزيع الحمل.نبدأ بخادوم قاعدة البيانات. خادوم قاعدة البياناتبإعادة تتبع خطواتنا (وإعادة النظر في الدرس السابق) نجد أن خادوم قاعدة البيانات أنشئ وفقا للخطوات التالية: تثبيت MySQL.إعداد MySQL.إعادة تشغيل MySQL.إنشاء قاعدة البيانات والمستخدمين.خطة استرداد خادوم قاعدة البياناتنعرف، بالنظر لطريقة إنشاء خادوم قاعدة البيانات، أنه تمكن إعادة إنشائه من الصفر ما عدا محتوى قاعدة البيانات. تُخزَّن أغلب بيانات تطبيق ووردبريس (التدوينات مثلا) في قاعدة البيانات. يعني هذا أنه يجب علينا الاحتفاظ بنسخة احتياطية من قاعدة البيانات إن أردنا توفير القدرة على استرداد خادوم قاعدة البيانات. سنحتفظ أيضا بنسخ احتياطية من ملف إعدادات MySQL الذي غيرنا فيه قليلا. في ما يلي ملخَّص لخطة الاسترداد الخاصة بخادوم قاعدة البيانات: يجب الآن التدرب على تنفيذ خطة الاستعادة هذه، والتأكد من الاحتفاظ بالنسخ الاحتياطية الضرورية. خواديم التطبيقنعرف بالنظر في طريقة إنشاء خواديم التطبيق (ومراجعة الدرس السابق)؛ أنّ الخطوات المتَّبعة كانت على النحو التالي: تثبيت كل من PHP وApache وإعدادهما.تنزيل التطبيق (ووردبريس) وإعداده.نسخ ملفات التطبيق إلى جذر المستند Document root.تكرار ملفات التطبيق على جميع خواديم التطبيق.خطة الاسترداد لخادوم التطبيقتمكن إعادة إنشاء خادوم التطبيق من الصفر، ما عدا ملفات التطبيق. تتضمن ملفات التطبيق في المثال ملفات إعداد ووردبريس (التي تحتوي على معلومات الاتصال بقاعدة البيانات)، إضافات ووردبريس المثّبَّتة، والملفات المحمَّلة. أي أنه يجب علينا الاحتفاظ بنسخة احتياطية من ملفات التطبيق إن أردنا إمكانية استرداد خادوم التطبيق. أعددنا الخواديم بحيث تُكرَّر الملفات على خواديم التطبيق؛ يعني هذا أننا لا نحتاج لاستعادة البيانات من النسخ الاحتياطية إلا إذا أخفقت خواديم التطبيق جميعها أو تلَفتْ البيانات بطريقة أو بأخرى. إن بقي خادوم تطبيق واحد على الأقل يعمل جيدا، مع ملفات التطبيق الصحيحة؛ فإن إعادة ضبط تكرار الملفات مرة أخرى سيعيد الملفات الصحيحة إلى خادوم التطبيق الجديد. في ما يلي ملخَّص لخطة الاسترداد الخاصة بخواديم التطبيقات، اعتمادا على الجرد أعلاه: اكتمل إعداد خطة الاسترداد لخواديم التطبيق. يجب الآن التدرب على تنفيذ خطة الاسترداد، والتأكد من الاحتفاظ بالنسخ الاحتياطية الضرورية. خادوم توزيع الحملالمبدأ هو نفسه، ننظر إلى خطوات إنشاء موزع الحمل في الدرس السابق؛ فنخرج بالتالي: الحصول على شهادة SSL والملفات المتعلقة بها.تثبيت HAProxy.إعداد HAProxy.إعادة تشغيل HAProxy.خطة الاسترداد لخادوم توزيع الحملنعرف من الخطوات أعلاه، أنه تمكن إعادة إنشاء موزع الحمل انطلاقا من صفر، ما عدا الملفات المتعلقة بشهادة SSL. يعني هذا أنه يتوجب علينا الاحتفاظ بنسخ احتياطية من الملفات المتعلقة بشهادة SSL إن أردنا توفير القابلية لاسترداد خادوم توزيع الحمل. سنضيف أيضا ملف إعداد HAProxy ضمن النسخ الاحتياطية. نضع - مثل ما فعلنا مع العناصر السابقة، ملخصا لخطة الاسترداد الخاصة بخادوم توزيع الحمل: اكتمل إعداد خطة الاسترداد لخادوم توزيع الحمل. يجب الآن التدرب على تنفيذ خطة الاسترداد، والتأكد من الاحتفاظ بالنسخ الاحتياطية الضرورية. اعتبارات أخرىإن تطلب استرداد أحد العناصر إعادة ضبط عنصر آخر (تغير عنوان IP الخاص بخادوم قاعدة البيانات مثلا)؛ فعليك التأكد من إدراج الخطوات الضرورية في خطط الاسترداد. ستحتاج أيضا لكتابة خطط استرداد للعناصر الأخرى المكوِّنة للتطبيق مثل خادوم النطاقات؛ وكذلك جميع العناصر التي يمكن أن تضيفها في المستقبل مثل خواديم النسخ الاحتياطي، المراقبة والسجلات. يجب، مع تطور إعداداتك، إعادة النظر في خطط الاستعادة الموجودة وإجراء التغييرات اللّازمة. لم نتطرق لحد الساعة إلى كيفية إنشاء ثم استعادة النسخ الاحتياطية؛ لذا سنحتاج للنظر في هذه التفاصيل لاحقا. سيكون النسخ الاحتياطي موضوع الجزء الموالي من هذا الدليل. خاتمةيجب الاحتفاظ بخطط الاستعادة الخاصة بالخواديم المختلفة في مكان آمن يمكن لمن أراد تنفيذ الخطوات الموجودة فيها الوصولُ إليه. احتفظ بها في مكان منفصل تماما عن بيئة العمل. ترجمة -وبتصرّف- لمقال Building for Production: Web Applications — Recovery Planning لصاحبه Mitchell Anicas. حقوق الصورة البارزة: Designed by Freepik.
-
- استعادة
- نسخ احتياطي
-
(و 1 أكثر)
موسوم في: