رفتن به مطلب

معماری فعلی پرستاشاپ - پرستاشاپ در سال 2019 و بعد از آن


پست های پیشنهاد شده

نوشته شده توسط- پابلو بوروویچ   (مدیر توسعه هسته پرستاشاپ)

مترجم: غلامعلی هلاکو

این اولین مقاله از مجموعه مقالاتی است که ما  معرفی کردیم و هدف آن توصیف مکان فعلی ما ، مقصد ما و برخی ایده ها در مورد چگونگی رسیدن به آنجا است.

همانطور که در مقاله اول اشاره شد ، اولین قدم برای اینکه بتوانید مسیری را به آینده ترسیم کنید این است که بفهمید در حال حاضر در کجا ایستاده اید. در این مقاله ، ما سعی خواهیم کرد و معماری فعلی پرستاشاپ را توصیف خواهیم کرد.

معماری فعلی پرستاشاپ

(یا "نقطه A - جایی که ما هستیم")

در اینجا مروری داریم بر پرستاشاپ 1.7 و تغییراتی که از اوایل سال 2019داشته است.

image.png.e31632e5aa6dc85f03808669808b65ff.png

(شکل 1: بررسی اجمالی معماری پرستاشاپ 1.7 ، اوایل سال 2019)

معماری PrestaShop را می توان در دو بخش اصلی منطقی از هم جدا کرد:

  •      Front Office (یا FO) رخ عمومی سایت فروشگاه ،
  •   Back Office   (یا BO) - جایی که بازرگانان فروشگاه خود را مدیریت می کنند.

در شکل 1 ، این توسط دو ستون بزرگ آبی نشان داده شده است.

هر یک از این بخشها را می توان در دو قسمت جدا کرد که در همه برنامه های وب مشترک است:

  •     front-end - بخشی که اساساً در مرورگر اجرا می شود ،
  •     Back-end - که در سرور اجرا می شود.

در شکل 1 ، این تفکیک با یک تقسیم افقی با یک خط نقطه نقطه نشان داده شده است ، جایی که "مرورگر" نمای جلویی و "سرور" نمای پشتی است.

بررسی اجمالی

اگر چگونگی ساختار back-end را تحلیل کنیم ، می توان عناصر مشترک BO و FO را پیدا کرد:

  • ·         پایگاه داده
  • ·         هسته اصلی (کسب و کار)
  • ·         ماژول ها

مانند اکثر برنامه های وب از این نوع ، PrestaShop به شدت مبتنی بر بانک اطلاعاتی است. آنجاست که منبع واحد حقیقی یافت می شود. این بدان معناست که همه داده ها در آنجا ذخیره می شوند ، صرف نظر از اینکه در BO یا FO استفاده می شوند.

     در شکل 1 ، پایگاه داده کمی خارج از نمودار قرار گرفته است تا به این واقعیت اشاره کند که این یک سیستم برای خود است که می تواند در یک سرور جداگانه ، در یک کلاستر و غیره باشد.

ابر بنفش به تصویر کشیده شده در بالای پایگاه داده همان چیزی است که ما آن را Core Business می نامیم. این مجموعه بزرگ کدی است که آنچه پرستاشاپ را پرستاشاپ می کند مدیریت می کند ، همچنین به عنوان "منطق تجارت" نیز شناخته می شود. این شامل کنترل کننده ها ، کلاس های کمکی و موارد دیگر است. این جنبه را با جزئیات بیشتر در ادامه این مقاله پوشش خواهیم داد.

سپس ، ماژول ها وجود دارد. ماژول ها از بسیاری جهات امکان شخصی سازی پرستاشاپ را دارند. آنها یا با اتصال به نقاط داخلی (که در کل کد قرار می گیرند) یا با جایگزینی اجزای اصلی با قسمت های اصلی خود (با استفاده از تعاریف قدیمی سیستم یا تعاریف سرویس Symfony) با هسته ارتباط برقرار می کنند.

در حالی که در این نمودار ماژول ها را در قسمت Back-end کارها قرار داده ایم ، آنها می توانند در Front-end نیز تأثیرگذار باشند. ما هنگام بحث در مورد ماژول به آن خواهیم پرداخت.

پرستاشاپ عمدتا  دو روش برای ارائه اطلاعات به مرورگر دارد: صفحات HTML و داده های ساخت یافته (XML یا JSON).

کنترل کننده ها به طور عمده صفحات HTML را تولید می کنند. ساختار این صفحات توسط موضوعی تعریف می شود که داده های ارائه شده توسط کنترل کننده را به HTML تبدیل می کند. این هم برای FO و BO صادق است.

 

     در شکل 1 ، مضامین توسط ستونهای صورتی  که هم در قسمت جلویی

    و هم در انتهای پشت همپوشانی دارند به تصویر کشیده شده است.

PrestaShop 1.7 از قالب های شخص ثالث در FO پشتیبانی می کند ، اما در BO پشتیبانی نمی کند. به نظر می رسد گیج کننده باشد ، دو موضوع BO وجود دارد ، "تم پیش فرض" و "قالب جدید" نامیده می شود. نگران نباشید ، این در بخش موضوعات توضیح داده خواهد شد.

PrestaShop دارای دو رابط API است:

·         BO API - برای ارائه اطلاعات به صفحات مستقر در VueJS (در حال حاضر ترجمه و مدیریت کالا) استفاده می شود.

·         سرویس های وب - برای ادغام سرویس های شخص ثالث استفاده می شود.

در حالی که سرویس های وب می توانند داده های XML یا JSON را تولید کنند ، BO API فقط JSON است.

در front-side ، بسته به موضوع همه چیز بسیار متفاوت است. وقتی به موضوعات پرداختیم این را پوشش خواهیم داد.

اکنون که یک ایده کلی درباره چگونگی کار داریم ، بگذارید کمی در آن ابر بنفش فرو برویم.

پشته هسته اصلی( کسب و کار)

در حالی که کنترل کننده ها در BO و FO متفاوت خواهند بود ، تقریباً همه کد PHP PrestaShop بین این دو محیط به اشتراک گذاشته شده است. این کد در چهار زیر سیستم منطقی تقسیم شده است:

  • کد قدیمی یا موروثی (Legacy) - واقع در /classes و  /controllers
  • کد آداپتور (Adaptor)- واقع در /src/Adapter
  • کد هسته (Core)- واقع در /src/Core
  • کد Symfony (یا "بسته پرستاشاپ") - واقع در /src/PrestaShopBundle

     

برای توضیح این ساختار ، بیایید کمی به گذشته نگاه کنیم.

تا [نسخه]1.6.0 [پرستاشاپ]، همه کدهای مشترک در فهرست Classes زندگی می کردند. یکی از مشکلات اصلی معماری قدیمی این است که کلاسها هم ساکن هستند و هم خیلی بزرگ. از آنجا که اصلاح مجدد تمام کلاسهای قدیمی بسیار طولانی می شود ، در طی توسعه 1.6.1 تصمیم گرفته شد کد قدیمی(موروثی) از کد جدید و به هم پیوستگی آزاد جدا شود.

این تصمیم با هدف تولید تدریجی یک کد کد تمیزتر و بدون ایجاد تغییراتی ایجاد شده است ، بنابراین به توسعه دهندگان ماژول فرصت می دهد تا ماژول های خود را تنظیم کنند.

خلاصه: چرا داشتن کد جدا نشده مهم است؟

جفت شدن کد درجه وابستگی متقابل دو ماژول است. با افزایش اتصال ، سیستم شروع به نمایش برخی از مشکلات رایج می کند:

·         تغییر چیزی در A شما را مجبور می کند که چیزی را در B تغییر دهید ، سپس C ، سپس D

·         تغییر چیزی در D ممکن است اثرات ناخواسته و پیش بینی نشده ای در A ایجاد کند که منجر به اشکال شود.

·         سازگاری یا استفاده مجدد از کد دشوارتر است ، زیرا مسیرها از قبل ایجاد شده اند (به آنها "کدگذاری سخت" گفته می شود).

·         آزمایش ماژول ها به طور مستقل از یکدیگر دشوارتر است.

جدا کردن کد از طریق طراحی دقیق با استفاده از اصول SOLID امکان حل این مشکلات را فراهم می کند.

به دنبال آن ، سازه به سه قطعه تقسیم شد:

     کد قدیمی - که همانطور که بود باقی مانده است

     کلاسهای هسته- کد جدید و تمیز

     کلاسهای آداپتور - پلی برای کلاسهای قدیمی ، برای جلوگیری از وابستگی های پنهان میراث در کد هسته

در طی توسعه اولیه 1.7 ، با معرفی چارچوب Symfony ، زیر سیستم چهارم مبتنی بر Symfony اضافه شد: PrestaShopBundle. در حالی که بقیه کد تقریباً چارچوب شناختی است ، PrestaShopBundle یک بسته Symfony است و بنابراین دارای ویژگی های خاص Symfony است.

در اینجا نحوه آماده سازی آن آمده است:

image.png.8426c0b86d32df0f898f2ae13c500838.png

(شکل 2: پشته هسته کسب و کار )

در شکل 2 ، ما می توانیم چهار زیر سیستم را که در بالا توضیح داده شد ، درک کنیم.

اگر فکر می کنید که این جدایی بیش از حد پیچیده است ، حق با شماست! اما این مرحله گذار برای اینکه ما بتوانیم به صورت تدریجی به جلو حرکت کنیم ضروری است. اینجا کجاست؟

به منطقه زرد نقطه دار که دارای کد موقت است توجه می کنید؟ شما حدس می زنید کد داخل آن منطقه زرد موقتی است. این بدان معناست که آن کد دیر یا زود به پشته Core یا PrestaShop Bundle منتقل می شود و پس از خالی شدن آن منطقه ، حذف می شود. البته ، چنین تغییری در نسخه جزئی انجام نخواهد شد ، بنابراین می توانید انتظار داشته باشید که این چهار پشته برای کل عمر 1.7 وجود داشته باشد.

اگر به روابط بین هر پشته دقت کنید ، خواهید دید که کد خارج از منطقه کد موقتی با کلاسهای قدیمی تعامل مستقیم ندارد. همانطور که قبلا توضیح داده شد ، لایه Adapter بین کد Legacy و کد "جدید" قرار دارد تا انتقال کد از پشته Legacy به پشته Core آسان شود.

آن چطور کار میکند؟

هر زمان که یک کلاس Core (یا PrestaShopBundle) به چیزی که توسط یک کلاس Legacy ارائه شده نیاز دارد ، به جای استفاده مستقیم از کلاس Legacy ، این کار را به یک Adapter که خودش از کلاس Legacy استفاده می کند (به الگوی Adapter رجوع کنید) تفویض می کند.

اینجا جالب است. به طور کلی ، این آداپتورها رابطی را که در Core اعلام شده است پیاده سازی می کنند (حتی اگر همیشه چنین نبوده است ، کلاسهای جدید این کار را می کنند). وابسته ساختن مصرف کنندگان آن آداپتور به جای خود کلاس Adapter به رابط کاربری (نگاه کنید به اصل وارونگی وابستگی) ، امکان اجرای مجدد کلاسهای Adapter در Core را بدون نیاز به تغییر کد موجود که به آنها بستگی دارد ، امکان پذیر می کند.

چرا از کلاس Legacy به طور مستقیم استفاده نمی کنید؟

برای مبتدیان ، اکثر کلاسهای قدیمی ثابت هستند و از آنجا که طبق تعریف آنها نمی توانند تزریق شوند ، این امر منجر به کد همراه و غیرقابل آزمایش می شود. علاوه بر این ، مواردی که استاتیک نیستند به طور کلی هنوز مسئولیت های زیادی دارند (نگاه کنید به اصل مسئولیت منفرد) و / یا بیش از حد بسیاری از روش ها یا خصوصیات عمومی (نگاه کنید به اصل باز / بسته) ، بنابراین نمی توان آنها را برای پیاده سازی یک رابط مناسب ایجاد کرد.

به عنوان یک نکته جانبی ، ممکن است متوجه شده باشید که همه پشته ها حاوی Hooks به عنوان نقاط پسوند هستند ، اما فقط پشته Legacy دارای جایگزینی است. ما در قسمت بعدی این مجموعه به آن خواهیم پرداخت (خراب نمی شود!).

کنترل کننده ها

PrestaShop بر اساس الگوی MVC ساخته شده است ، جایی که کنترل کننده ها مسئولیت رسیدگی به درخواست ها و بازگشت پاسخ ها را دارند ، در حالی که ایده آل ترین کار برای خدمات اختصاص داده شده را انجام می دهند.

کنترل کننده ها در دو خانواده بزرگ تقسیم می شوند: خانواده هایی که درخواست های FO را کنترل می کنند و دیگر کنترل کننده های درخواست BO.

image.png.7b916e24b85e72aae60473948b19afc8.png

(شکل 3: کنترل کننده های هسته)

همانطور که شکل 3 توضیح می دهد ، در حال حاضر چندین نوع کنترل کننده وجود دارد:

  📘کننده های FO - که درخواست های FO را کنترل می کنند

   o       کنترل کننده های قدیمی (Legacy)

                 📰 کنترل کننده های بومی (FrontControllers)

                📰کنترل کننده های ماژول (ModuleFrontControllers ، مبتنی بر FrontControllers)

📘   کنترل کننده های BO - که درخواست های BO را کنترل می کنند

o       کنترل کننده های قدیمی(Legacy)

          📰کنترل کننده های بومی (AdminControllers)

         📰 کنترل کننده های ماژول (ModuleAdminControllers ، براساس AdminControllers)

o       کنترل کننده های Symfony

        📰کنترل کننده های چارچوب (FrameworkBundleAdminControllers)

        📰   کنترل کننده های API (ApiControllers)

هیچ کنترلی برای سرویس های وب وجود ندارد ، زیرا این سیستم عمدتا مبتنی بر پیکربندی است و بسیار محکم با ObjectModel مرتبط است.

همه کنترلرهای قدیمی در پشته قدیمی قرار دارند - شما حدس می زدید - و از Smarty برای الگو برداری استفاده می کنند. از طرف دیگر ، کنترلرهای Symfony در PrestaShopBundle قرار دارند و از Twig استفاده می کنند.

همانطور که مهاجرت به Symfony پیشرفت می کند ، کنترلرهای میراث BO از پشته قدیمی به پشته بسته نرم افزاری PrestaShop منتقل می شوند. پس از اتمام انتقال BO ، دیگر هیچ کنترل کننده قدیمی در BO وجود نخواهد داشت.

قالب

دو قالب در پرستاشاپ وجود دارد:

  • ·         قالبFO - که می توانید هر تعداد که بخواهید داشته باشید و از بازار Addons قابل بارگیری است
  • ·         قالب های BO - که قابل تعویض نیستند

از آنجا که قالب FO در بالای کنترلرهای قدیمی کار می کنند ، براساس Smarty ساخته شده اند. همه آنها یک کتابخانه جاوا اسکریپت مشترک را با نام "core.js" ادغام می کنند که دارای jQuery 2.x می باشد.

در 1.7 ، PrestaShop ویژگی قالب والدین و کودک را ارائه داد که به موضوعات اجازه می دهد تا قالب های دیگر را گسترش دهند. این ویژگی ساخت قالب های سفارشی را بسیار آسان می کند.

پرستاشاپ همراه با یک قالب پیش فرض FO ، به نام "کلاسیک" ارائه می شود. از آنجا که قالب کلاسیک با پرستاشاپ تکامل می یابد ، بسیاری از قالب های 1.7 جامعه "کودکان" Classic هستند ، که به آنها امکان می دهد تمام پیشرفتهایی را که در هر نسخه به کلاسیک اضافه می شود ، به ارث برسانند ، بدون اینکه حداقل کاری برای نویسندگان آنها لازم باشد. این جزئیات را بخاطر داشته باشید ، ما در مقاله بعدی در مورد یک عارضه جانبی عمده آن بحث خواهیم کرد.

در مورد قالب BO ، چند نکته ظریف وجود دارد که باید از آنها آگاه باشید.

اول ، همانطور که قبلا توضیح داده شد ، قالب های BO قابل تعویض نیستند ، اما با این وجود دو مورد وجود دارد: قالب پیش فرض و جدید.

پس چرا دو تا وجود دارد؟ خوب ، کنترل کننده های قدیمی مبتنی بر Smarty هستند ، در حالی که کنترل کننده های Symfony مبتنی بر Twig هستند. در نتیجه ، برای هر کدام یک قالب وجود دارد: کنترل کننده های قدیمی از طرح زمینه پیش فرض استفاده می کنند ، در حالی که موارد Symfony از قالب جدید استفاده می کنند. از آنجا که کنترل کننده ها به تدریج به Symfony منتقل می شوند ، الگوها از Smarty به Twig تبدیل می شوند و از حالت پیش فرض به قالب جدید منتقل می شوند.

برای جالبتر ساختن قالب های ، پیش فرض مبتنی بر Bootstrap 3 است و jQuery 1.x را ادغام می کند ، در حالی که قالب جدید مبتنی بر کیت UI PrestaShop (موجود در GitHub) است که خود براساس Bootstrap 4 و jQuery 3x است.

Twig زبان الگویی Symfony است. در نسخه 1.7 ، برای همه صفحاتی که بازنویسی شده اند برای استفاده از Symfony (صفحه محصول و صفحه ماژول ها) استفاده می شود ، اما نه برای رابط عمومی (منو ، هدر ، و غیره) و نه صفحات غیر بازنویسی شده ، که همچنان استفاده خواهند شد Smarty دو موتور الگو در مرحله انتقال در کنار هم در دسترس خواهند بود.

اما موارد بیشتری وجود دارد

آنچه در هنگام اعلام معماری 1.7 گفته شد:

این بدان معنی است که رابط کاربری سراسری با قالب پیش فرض ، حتی در صفحات Symfony ، همچنان اداره می شود ، که بخشی از آن توضیح می دهد که چرا صفحات Symfony ممکن است گاهی اوقات کندتر از صفحات قدیمی باشند (زیرا از هر دو Twig و Smarty استفاده می کنند). خبر خوب این است که این یک مسئله موقتی است ، وقتی همه چیز به Twig و Symfony منتقل شود بهتر خواهد شد.

سرانجام ، صفحات Vue وجود دارد. صفحات Vue ترکیبی هستند: صفحات BO مبتنی بر نیمه Symfony ، نیمه API. در آن صفحات ، اسکلت صفحه ابتدا توسط یک کنترل کننده Symfony ارائه می شود (بنابراین بر اساس موضوع جدید) و سپس یک برنامه VueJS مرورگر را در اختیار شما قرار می دهد و محتوای آن را بر اساس داده های ارسال شده توسط BO API ترسیم می کند.

همانطور که قبلاً گفته شد ، در حال حاضر فقط صفحات مدیریت انبار و ترجمه بر اساس این فناوری ساخته شده اند. حتی اگر فکر می کنیم که این راه آینده است (که بعداً در این مجموعه به جزئیات بیشتری خواهیم پرداخت) ، در می یابیم که رفتن در این مسیر در نسخه های جزئی نسخه های قابل انعطاف عمده و ناسازگاری به عقب را ایجاد می کند. بنابراین ، احتمالاً هیچ صفحه جدید Vue / BO API در 1.7 وجود ندارد.

ماژول ها

سیستم Modules یک رویکرد افزونه را برای قابلیتهای اضافه شده ارائه می دهد. همانطور که قبلاً توضیح داده شد ، این بخش عمدتاً به نقاط تمدید مشخصی موسوم به "قلاب" یا "Hooks" متکی است ، اما تأثیر آنها و روابط ریشه دار آنها بسیار فراتر از آن است.

اگر به بلوک Modules در مرکز شکل 1 نگاه کنید ، متوجه خواهید شد که تعداد زیادی فلش از آن می آیند و می روند. بیایید این روابط را بررسی کنیم.

همانطور که گفتیم ، مسیر اصلی برای ادغام ماژول Hooks است ، که در سراسر سیستم قرار گرفته است. ماژول ها می توانند به منظور ایجاد یا تغییر ویژگی ها به آنها متصل شوند.

هوک ها دو نوع هستند:

·         هوک های نمایشگر - به طور عمده (اما نه به طور انحصاری) در الگوها ، به ماژول ها اجازه می دهند محتوایی را که در جایی از صفحه نمایش داده می شود ، تزریق کنند.

·         هوک های عملیاتی - به ماژول ها اجازه دهید از اتفاقات رخ داده در سیستم مطلع شوند و به صورت اختیاری با تغییر داده های ارائه شده ، رفتار سیستم را تغییر دهند.

سیستم ماژول چندین ویژگی دیگر را نیز فراهم می کند:

·         کنترل کننده های ماژول - ماژول ها می توانند مسیرهای جدید و صفحات سفارشی را در FO یا BO اضافه کنند.

·         گزینه های پرداخت - ماژول های پرداخت می توانند گزینه های پرداخت را در روند پرداخت اضافه کنند.

·         اعلام و اشتراک سرویس ها - از 1.7.4 ، ماژول ها می توانند از خدمات Symfony استفاده و اعلام کنند.

همچنین می توان از ماژول ها برای شخصی سازی PrestaShop استفاده کرد:

·      Class override system - این سیستم به ماژول اجازه می دهد هر کلاس را در پشته Legacy جایگزین کند.

·          الگوی BO نادیده گرفته می شود - اجازه می دهد الگوها را از موضوع جدید در BO جایگزین کنید.

·           Service overrides یا لغو خدمات - از 1.7.4 ، ماژول ها می توانند خدمات اصلی را جایگزین سرویس های اصلی خود کنند.

·          تزریق CSS و JS - ماژول ها همچنین می توانند سبک و کد جاوا اسکریپت را به یک صفحه تزریق کنند.

علاوه بر این ، ماژول ها را می توان با قالب ها سفارشی کرد. قالب هایی که از یک ماژول مشخص پشتیبانی می کنند می توانند الگوهای FO خود ماژول را نادیده بگیرند تا ادغام آنها بهبود یابد.

همانطور که مشاهده می کنید ، سیستم ماژول از ویژگی های بسیاری برخوردار است و ماژول ها را بسیار قدرتمند می کند. ماژول ها به سیستم Core دسترسی کامل دارند و حتی اگر ماژول های ارسالی به Addons Marketplace فرآیند بررسی کیفیت و امنیت را طی کنند ، ادغام می تواند بسیار عمیق هسته باشد. این قدرت با هزینه همراه است: هرچه یکپارچه سازی و سفارشی سازی عمیق تر باشد ، خطر بروزرسانی و قابلیت همکاری بیشتر است.

و بیشتر

اگر به اینجا می رسیدید ، اکنون می دانید PrestaShop بسیار پیچیده تر از آن است که به نظر می رسد. نمای کلی بالای مقاله را به خاطر می آورید؟ اکنون به این نسخه دقیق تر از آن نگاهی بیندازید:

در واقع زیر سیستم های بسیار دیگری نسبت به مواردی که ما توضیح دادیم وجود دارد ، تعداد زیادی نیز برای پوشش جزئی همه آنها در اینجا. فقط برای ذکر چند مورد دیگر:

·         ترجمه و بومی سازی - PrestaShop می تواند به هر زبان و کشوری ترجمه و بومی سازی شود: ارزها ، مالیات ها ، مکان های حمل و نقل و موارد دیگر.

·         بروزرسانی - PrestaShop از بارگیری و به روزرسانی ماژول ها پشتیبانی می کند و همچنین می تواند با استفاده از ماژول Autoupgrade خود را به روز کند.

·         صادرات / واردات - بازرگانان می توانند داده های خود را از / به PrestaShop منتقل کنند.

·         ایمیل ها - همه ایمیل های ارسال شده توسط PrestaShop قابل تنظیم و طرح زمینه هستند.

برای امروز کافی است! در بخش بعدی ، ما بررسی خواهیم کرد که نقاط اصلی درد این معماری چیست.

منبع:

https://build.prestashop.com/news/prestashop-in-2019-and-beyond-part-1-current-architecture/

 

لینک به دیدگاه
به اشتراک گذاری در سایت های دیگر

به گفتگو بپیوندید

هم اکنون می توانید مطلب خود را ارسال نمایید و بعداً ثبت نام کنید. اگر حساب کاربری دارید، برای ارسال با حساب کاربری خود اکنون وارد شوید .

مهمان
ارسال پاسخ به این موضوع ...

×   شما در حال چسباندن محتوایی با قالب بندی هستید.   حذف قالب بندی

  تنها استفاده از 75 اموجی مجاز می باشد.

×   لینک شما به صورت اتوماتیک جای گذاری شد.   نمایش به صورت لینک

×   محتوای قبلی شما بازگردانی شد.   پاک کردن محتوای ویرایشگر

×   شما مستقیما نمی توانید تصویر خود را قرار دهید. یا آن را اینجا بارگذاری کنید یا از یک URL قرار دهید.

در حال بارگذاری


  • کاربران آنلاین در این صفحه

    هیچ کاربر عضوی،در حال مشاهده این صفحه نیست.

×
×
  • اضافه کردن...