ابزار سایت

راهنمای_توسعه_افزونه

فصل 4- راهنمای توسعه افزونه

در این بخش به شرح قدم‌به‌قدم توسعه یک افزونه تحت عنوان «تماس با ما» پرداخته شده است. قابلیت‌های این افزونه به شرح زیر است:

  • صفحه‌ای مجزا در سامانه تحت عنوان تماس‌ با ما به وجود خواهد آمد که پیوند این صفحه در منوی صفحه اصلی قرار می‌گیرد.
  • درون صفحه مذکور فرم ارسال پیام به مسئولین وب‏گاه وجود دارد. این فرم شامل فیلد‌های عنوان،‌ پیام، مخاطبی که قصد ارسال پیام به آن وجود دارد و در انتها رایانشانی ارسال‌کننده نظر است.
  • با پر کردن فرم و ارسال آن، اطلاعات به صورت یک رایانامه به مخاطب مربوطه ارسال می‌شود.

پس از مطالعه این بخش و پیاده‌سازی این افزونه، تمامی مهارت‌های ضروری و اولیه برای توسعه یک افزونه کسب خواهد شد.

4-1- فایل‌های افزونه

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

پوشه اصلی افزونه باید شامل یک فایل تحت عنوان init.php باشد. این فایل با هر درخواست کاربر یک‌بار اجرا می‌شود. چرا که موتوشاب در هر بار درخواست تمامی افزونه‌ها را راه‌اندازی می‌کند و با این راه‌اندازی init.php اجرا خواهد شد.

اکنون باید فایل plugin.xml ایجاد شود که شامل اطلاعات افزونه است.

<?xml version="1.0" encoding="UTF-8"?>
<plugin>
    <name>Your plugin name here</name>
    <key>your_plugin_key_here</key>
    <description>"Contact us" page with the ability to choose departments (email addresses).</description>
    <author>shub</author>
    <authorEmail>plugins@shub.ir</authorEmail>
    <authorUrl>http://www.shub.ir</authorUrl>
    <developerKey>your_developer_key_here</developerKey>
    <build>1</build>
    <copyright>(C) 2009 Skalfa LLC. All rights reserved.</copyright>
    <license>The License</license>
    <licenseUrl>http://www.shub.ir/licenses/bsd-license.php</licenseUrl>
</plugin>

از بین مقادیر بالا شناسه افزونه (plugin key)، شناسه توسعه‌دهنده (developer key) و نسخه (version) باید درست تنظیم شده باشند، در غیر این صورت ممکن است در زمان ارائه افزونه یا به‌روزرسانی خودکار در بخش مدیر مشکلاتی ایجاد شود. شناسه افزونه در این مثال همان contactus است که پیش‌تر ذکر شد.

در این مرحله می‌بایست فایل‌های زیر در پوشه اصلی افزونه‌ قرار گیرند:

  • install.php: کدهای لازم هنگام نصب افزونه مانند ایجاد جداول مناسب پایگاه داده در این فایل قرار می‌گیرند.
  • uninstall.php: کدهای لازم هنگام حذف افزونه در این فایل قرار می‌گیرند؛ مانند حذف جداول افزونه.
  • activate.php: اقدامات لازم در زمان فعال شدن افزونه در این فایل قرار می‌گیرند.
  • deactivate.php: اقدامات لازم در زمان غیرفعال شدن افزونه در این فایل قرار می‌گیرند.

برای استفاده از زیرساخت زبان‌ در موتوشاب کد زیر باید در فایل install.php قرار گیرد.

BOL_LanguageService::getInstance()->addPrefix('contactus', 'Contact Us');

کد بالا کمی جلوتر توضیح داده خواهد شد. دیگر فایل‌ها در حال حاضر خالی باقی می‌مانند. اکنون افزونه برای نصب آماده است.

4-2- نصب افزونه

پیش از آغاز به توسعه، می‌بایست افزونه نصب شود. به بخش «میزفرمان مدیریت» رفته و در بخش «افزونه‌ها» و زیربخش «افزونه‌های در دسترس» افزونه‌ای که قصد توسعه آن را دارید (در این مثال «تماس با ما») ظاهر شده است، با انتخاب گزینه «نصب» آن را نصب کنید. در نهایت، افزونه با موفقیت نصب و فعال‌سازی خواهد شد.

4-3- ایجاد یک صفحه

موتوشاب از الگوی معماری MVC استفاده می‌کند. در این راستا می‌بایست دو پوشه «controllers» و «views» داخل پوشه اصلی ایجاد شود. همچنین باید یک پوشه «controllers» نیز در پوشه «views» نیز ایجاد شود. در این مرحله اولین کنترل‌کننده را در پوشه اصلی controllers ایجاد کنید. در این راستا می‌بایست یک فایل تحت عنوان «contact.php» با محتوای زیر داخل این پوشه ایجاد شود:

class CONTACTUS_CTRL_Contact extends OW_ActionController
{
}

توجه: اسم کلاس کنترل‌کننده باید با یک پیشوند شامل شناسه افزونه (به صورت حروف بزرگ) و کلمه ‘CTRL’ که با «_» جدا شده است، آغاز شود. توجه: همه کنترل‌کننده‌ها باید از کلاس OW_ActionController یا کلاس‌های فرزندش ارث‌بری کنند. توجه: فایلی که شامل تعریف کلاس کنترل‌کننده است باید بر اساس این قانون نام‌گذاری شود:

1. برداشتن پیشوند از نام کلاس 2. تعویض حروف از حالت بزرگ به کوچک 3. جداسازی کلمات با استفاده از «_»

برای مثال، کلاس MYPLUGIN_CTRL_SuperPuper داخل یک فایل با نام super_puper.php قرار دارد.

می‌توان در هر کلاس کنترل‌کننده یک یا چند عملیات با استفاده از تعریف تابع، ایجاد کرد. سپس هر کدام از این عملیات‌ را به یک نشانی وب‌گاه متناظر کرده و در نهایت یک صفحه وب‌گاه ایجاد کرد. در حقیقت هر عملیات یک تابع کلاس با دسترسی عمومی است. در کد زیر یک عملیات تحت عنوان «index» ایجاد شده است که در آن عنوان صفحه و سرآیند آن مقداردهی شده است.

public function index()
{
    $this->setPageTitle("Contact Us");
    $this->setPageHeading("Contact Us");
} 

قابل توجه است که این کد درون کلاس CONTACTUS_CTRL_Contact موجود در فایل contact.php ذخیره شود.

4-4- بومی‌سازی زبان

در مثال بالا عنوان صفحه به طور مستقیم و بدون استفاده از سازوکار بومی‌سازی تنظیم شد. در واقع در چنین شرایطی عنوان صفحه همواره «Contact Us» خواهد بود. چه زبان موتوشاب فارسی و چه انگلیسی باشد. برای آنکه بتوان وابسته به زبان سامانه عبارت‌ها تغییر کنند می‌بایست از زیرساخت بومی‌سازی فراهم‌شده در موتوشاب استفاده کرد.

ساختار زبان در موتوشاب به صورت تعداد زیادی جفت کلید-مقدار است. توسعه‌دهندگان هسته موتوشاب و همچنین افزونه‌های آن همواره در خلال کد‌ها و صفحات از کلید‌ها استفاده می‌کنند و با استفاده از چارچوب زبان تعبیه‌شده به ازای هر زبان مقادیر مناسبی برای کلیدها در نظر می‌گیرند؛ مثلا برای مؤلفه «آمار کاربران» از کلید «admin+widget_user_statistics» استفاده شده است. در زبان انگلیسی مقدار این کلید «User statistics» بوده در زبان فارسی «آمار کاربران» است.

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

به جهت ذخیره‌سازی اطلاعات زبان برای هر بخش و افزونه موجود در موتوشاب یک فایل xml با نام همان بخش یا افزونه به صورت خودکار ساخته می‌شود. به عنوان مثال برای افزونه contactus یک فایل XML تحت عنوان contactus.xml ساخته می‌شود که محتوای آن در ادامه قابل‌مشاهده است.

<?xml version="1.0" encoding="UTF-8"?>
<prefix name=" contactus" label=" Contact Us" language_tag="fa" language_label="فارسی">
 
    <key name=" admin_dept_heading">
	<value> ارتباط با ما:بخش </value>
    </key>
    <key name=" bottom_menu_item">
	<value> تماس با ما</value>
    </key>
.
.
.
    <key name=" form_label_from">
	<value> ایمیل شما</value>
    </key>
 
</prefix>

لازم است به ازای هر زبان که افزونه از آن پشتیبانی می‌کند، یک فایل XML شامل کلیدها و ترجمه‌های آن وجود داشته‌باشد. سپس این فایل به همراه یک فایل XML دیگر با نام language.xml می‌بایست در پوشه‌ای با نام language_fa-IR قرار داده شوند. محتویات فایل language.xml برای زبان فارسی به شرح زیر است:

<?xml version="1.0" encoding="utf-8"?>
<language tag="fa-IR" label="فارسی" rtl="1"/>

برای پشتیبانی از از یک زبان دیگر مانند انگلیسی، فایل XML شامل ترجمه‌های انگلیسی نیز با نام contactus.xml و فایل language.xml مربوط به آن ایجاد شده و در پوشه‌ای با نام language_en قرار داده شود. پس از ایجاد پوشه‌ها، ضروری است تمام آن‌ها(به عنوان مثال پوشه‌های language_fa-IR و language-en) را در یک پوشه با نام langs قرار داد. علاوه بر این، لازم است که دستور زیر برای معرفی این فایل به هسته در فایل install.php مربوط به افزونه اضافه شود.

موتور موتوشاب زبان‌های هر افزونه را از فایل زبان مربوط به آن فراخوانی می‌کند؛ بنابراین تمام افزونه‌ها باید دارای این کلید-مقدار‌ها برای پشتیبانی از زبان‌های مورد نظر باشند. همچنین درصورتی که این فایل‌ها در پوشه افزونه موجود نباشد، در قسمت‌های مختلف افزونه به عنوان چندزبانه شناخته نشده و امکانات مربوط به مدیریت کلید-مقدارها برای آن افزونه قابل استفاده نخواهد بود.

شایان‌ ذکر است که فایل XML ترجمه‌ها از طریق قسمت مدیریت سامانه قابل دریافت است؛ بنابراین، مدیران موتوشاب قادر به پشتیبان‌گیری از فایل‌های ترجمه خواهند بود. همچنین می‌بایست خاطرنشان شود که موتوشاب تنظیماتی را برای راست‌چین و یا چپ‌چین کردن زبان دارا است. در صورتی که در تنظیمات، یک زبان راست‌چین معرفی شود، تمامی نوشته‌های آن در سامانه به صورت راست‌چین نمایش داده خواهد شد؛ اما این به معنای راست‌چین شدن همه مؤلفه‌های داخل صفحه نیست بلکه فقط متون ترجمه‌شده راست‌چین خواهند شد.

در حین توسعه برای فرم تماس به منظور اضافه کردن کلید-مقدار برای زبان‌های مختلف به پنل مدیریت از طریق آدرس زیر مراجعه شود:

<domain>/admin/settings/dev-tools/languages

در صورتی که مدیر با لینک فوق به صفحه تنظیمات مربوط به زبان وارد شود دکمه‌ای تحت عنوان «افزودن متن جدید» به این صفحه افزوده می‌شود که از طریق آن می‌توان جفت کلید-مقدار جدید به فایل حاوی کلیدهای زبان افزود.

توجه شود که این صفحه تنها برای توسعه‌دهندگان در دسترس است و از واسط کاربری استاندارد ویرایش زبان واقع در میز فرمان مدیریت منعطف‌تر است؛ به عبارت دیگر در صورتی که مدیر به صورت عادی این صفحه وارد شود این دکمه نمایان نیست.

با کلیک بر روی دکمه افزودن متن جدید ، کاربر به فرمی برای افزودن یک شناسه جدید منتقل می‌شود. در این صفحه می‌بایست بخشی که نام آن با نام افزونه «Contact Us» تطبیق دارد انتخاب شود (این بخش در طول نصب افزونه توسط کدی که در فایل install.php قرار گرفته، افزوده شده است). اکنون به عنوان کلید مقدار «index_page_title» و به عنوان مقدار عبارت مناسبی وارد شود (مثلا «تماس با ما»). برای کلید «index_page_heading» نیز اقدامی مشابه صورت گیرد. می‌توان برای گرفتن متن شناسه در زبان کنونی متد OW::getLanguage()→text را فراخوانی نمود که در آن شناسه افزونه و شناسه زبان می‌بایست به عنوان پارامتر به این تابع داده شود. به همین منظور، خطوط زیر:

$this->setPageTitle("Contact Us");
$this->setPageHeading("Contact Us");

می‌بایست به صورت زیر تغییر داده شود:

$this->setPageTitle(OW::getLanguage()->text('contactus', 'index_page_title'));
$this->setPageHeading(OW::getLanguage()->text('contactus', 'index_page_heading'));

اکنون ترجمه عنوان و سربار صفحه را می‌توان به زبان‌های مختلف در سامانه ایجاد کرد.

4-5- مسیریابی صفحات

برای اینکه صفحه‌ای به درستی نمایش داده شود، باید یک view برای عملیات آن در کنترل‌کننده تخصیص داده شود. برای این منظور باید یک فایل خالی با نام contact_index.html در پوشه views/controllers ساخته شود. همان‌طور که مشاهده می‌شود، نام view شامل نام فایل کنترل‌کننده (contact.php) و نام عملیات (index) است که با «_» از هم جدا شده‌اند.

تذکر: با ساختن این صفحه خالی، بعد از اولین اجرا، در حافظه نهان (Smarty) بارگذاری می‌شود. لذا بعد از قراردادن کدهایی که در بخش بعدی بیان می‌شوند، دوباره صفحه خالی بارگذاری شده و کدهای HTML نوشته‌شده اجرا نمی‌شوند. لذا لازم است بعد از قرار دادن کدها، پوشه temporary در smarty خالی شود یا حداقل فایل متناظر پاک شود.

سرانجام، صفحه ساخته‌شده با آدرسی به صورت زیر به وجود خواهد آمد:

<domain>/contactus/contact/index

که در آن contactus شناسه افزونه، contact کنترل‌کننده (در واقع فایل حاوی کنترل‌کننده) و index عملیات داخل کنترل‌کننده (تابع index) است. آدرس صفحه کمی طولانی به نظر می‌رسد. موتوشاب برای حل این چالش سازوکار مناسبی را فراهم کرده است. برای اینکه صفحه ساخته‌شده از یک آدرس کوتاه‌تری قابل دسترسی باشد (به طور مثال <domain>/contact)، باید خط زیر به فایل init.php که قبلا ساخته شده است اضافه شود:

OW::getRouter()->addRoute(new OW_Route('contactus.index', 'contact', "CONTACTUS_CTRL_Contact", 'index'));

این خط یک مسیریابی جدید به موتوشاب اضافه می‌کند. در حقیقت این خط بیان می‌کند که در صورتی که آدرس<domain>/contact باشد، درخواست به عملیات index داخل کنترل‌کننده CONTACTUS_CTRL_Contact هدایت شود. عبارت 'contactus.index' نیز عنوان این مسیریابی است. هر مسیریابی می‌بایست یک عنوان متمایز از سایر مسیریابی‌های سامانه داشته باشد. استاندارد مناسب نام‌گذاری مسیریابی «شناسه افزونه» + «عنوان عملیات» است. آنچه که با باز کردن این آدرس در یک مرورگر مشاهده خواهد شد به صورت شکل 1 است.

شکل 1: نمای افزونه بعد از پیاده‌سازی مراحل طراحی

در حال حاضر پیوند به صفحه توسط کاربران قابل مشاهده نیست. برای ایجاد دسترسی به صفحه، باید یک پیوند به منوی پایین صفحه موتوشاب اضافه کرد. بدین منظور یک شناسه bottom_menu_item در زبان‌ها با پیشوند افزونه و ‘Contact us’ به عنوان مقدار آن ایجاد کرده و سپس کد زیر را داخل فایل activate.php اضافه کنید:

OW::getNavigation()->addMenuItem(OW_Navigation::BOTTOM, 'contactus.index', 'contactus', 'bottom_menu_item', OW_Navigation::VISIBLE_FOR_ALL);

همچنین کد زیر به deactivate.php اضافه شود:

OW::getNavigation()->deleteMenuItem('contactus', 'bottom_menu_item');

اکنون به صفحه <domain>/admin/plugins مراجعه کرده و افزونه را غیرفعال و دوباره فعال کنید. اکنون با بازگشت به صفحه اصلی (index)، عنصر “Contact us” در فهرست پایین قابل مشاهده است.

4-6- استفاده از فرم‌ها

در این مرحله یک فرم تماس به صفحه‌ اضافه خواهد شد. موتوشاب سازوکار مناسبی را برای کار با فرم‌ها فراهم کرده است. ابتدا یک آرایه از رایانشانی‌های تماس ایجاد کرده تا در فرم تماس نمایش داده شود. کد زیر را داخل عملیات index درون فایل contact.php قرار دهید:

$contactEmails = array(
    'admin@site.com' => 'Site administration',
    'support@site.com' => 'Technical support',
    'billing@site.com' => 'Billing department'
);

همچنین یک میان‌بر برای فراخوانی OW::getLanguage→text() ایجاد کرده تا ساده‌تر بتوان به این تابع دسترسی داشت.

private function text( $prefix, $key, array $vars = null )
{
    return OW::getLanguage()->text($prefix, $key, $vars);
}

اکنون به جای استفاده از OW::getLanguage()→text از تابع $this→text() می‌توان برای دسترسی به زیرساخت زبان استفاده کرد.

بعد از آن می‌بایست یک فرم برای ارسال اطلاعات ایجاد کرد:

$form = new Form('contact_form');

همچنین باید فیلدهای زیر به فرم اضافه شوند:

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

کد زیر فیلد‌های فوق را به فرم می‌افزاید:

$fieldTo =new Selectbox('to');
foreach ( $contactEmails as $email => $label )
{
    $fieldTo->addOption($email, $label);
}
$fieldTo->setRequired();
$fieldTo->setHasInvitation(false);
$fieldTo->setLabel($this->text('contactus', 'form_label_to'));
$form->addElement($fieldTo);
 
$fieldFrom = new TextField('from');
$fieldFrom->setLabel($this->text('contactus', 'form_label_from'));
$fieldFrom->setRequired();
$fieldFrom->addValidator(new EmailValidator());
$form->addElement($fieldFrom);
 
$fieldSubject = new TextField('subject');
$fieldSubject->setLabel($this->text('contactus', 'form_label_subject'));
$fieldSubject->setRequired();
$form->addElement($fieldSubject);
 
$fieldMessage = new Textarea('message');
$fieldMessage->setLabel($this->text('contactus', 'form_label_message'));
$fieldMessage->setRequired();
$form->addElement($fieldMessage);
 
// Using captcha here to prevent bot spam
$fieldCaptcha = new CaptchaField('captcha');
$fieldCaptcha->setLabel($this->text('contactus', 'form_label_captcha'));
$form->addElement($fieldCaptcha);
 
$submit = new Submit('send');
$submit->setValue($this->text('contactus', 'form_label_submit'));
$form->addElement($submit);

در خصوص کد فوق نکات زیر قابل ذکر است:

  • ابتدا یک شیء $fieldFrom از نوع TextField تحت عنوان «to» ایجاد می‌شود.
  • برچسب فیلد با استفاده از setLabel() قابل تنظیم است.
  • توجه شود که شناسه زبان و ترجمه‌ برای همه فیلدهای فرم در وبگاه‌ اضافه شوند.‌
  • فیلد با استفاده از تابع setRequired() به حالت الزامی در آورده می‌شود.
  • کاربر فقط قادر به ورود رایانشانی در فیلد «to» خواهد بود. در نتیجه سامانه نباید اجازه دهد که او عبارتی که به رایانشانی شباهت ندارد را وارد کند؛ بنابراین یک اعتبارسنج رایانشانی از طریق فراخوانی addValidator() به این فیلد اضافه می‌شود. همچنین می‌توان اعتبارسنج‌های سفارشی تعریف کرده و به عناصر فرم اضافه کرد.
  • باید عناصر با استفاده از تابع adaddElement()، به فرم اضافه شود.
  • همان‌طور که از کد فوق قابل مشاهده است، به سادگی فیلد عبارت امنیتی با استفاده از CaptchaField به فرم اضافه شد.

اکنون باید فرم‌ را با استفاده از کد زیر به کنترل کننده اضافه کرد:

$this->addForm($form);

برای نمایش دادن فرم بر روی صفحه، باید کد HTML متناظر با آن در view متناظر با کنترل‌کننده قرار گیرد؛ بنابراین، کد زیر به فایل views/controllers/contact_index.html افزوده می‌شود:

{form name='contact_form'}
<table class="ow_table_1 ow_form ow_automargin ow_superwide">
<tr class="ow_alt1">
<td class="ow_label">{label name='to'}</td>
<td class="ow_value">{input name='to'}{error name='to'}</td>
</tr>
<tr class="ow_alt2">
<td class="ow_label">{label name='from'}</td>
<td class="ow_value">{input name='from'}{error name='from'}</td>
</tr>
<tr class="ow_alt1">
<td class="ow_label">{label name='subject'}</td>
<td class="ow_value">{input name='subject'}{error name='subject'}</td>
</tr>
<tr class="ow_alt2">
<td class="ow_label">{label name='message'}</td>
<td class="ow_value">{input name='message'}{error name='message'}</td>
</tr>
<tr class="ow_alt1">
<td class="ow_label">{label name='captcha'}</td>
<td class="ow_value ow_center">{input name='captcha'}{error name='captcha'}</td>
</tr>
<tr>
<td class="ow_center" colspan="2">{submit name='send' class='ow_button ow_ic_mail'}</td>
</tr>
</table>
{/form}

کد بالا، یک کد معمول HTML با برچسب‌های Smarty است. این فرم با تگ باز {form name=''} به همراه صفت اجباری name و تگ بسته {/form} تعریف شده است. عناصر فرم با برچسب‌های زیر تعریف شده‌اند:

  • {label name=''}
  • {input name=''}
  • {error name=''}

شایان ذکر است که صفت name برای این برچسب‌ها اجباری است و فایل‌های حاوی جفت کلید-مقدار زبان همان‌گونه که پیش‌تر نیز گفته شد بایستی تعبیه شده باشد. در نهایت باید صفحه مشابه با شکل 2 مشاهده شود.

شکل 2: نمای تکمیل شده افزونه

با اقداماتی که در ادامه آمده است، فرم‌ تعاملی و کاراتر خواهد شد. کد زیر می‌بایست به انتهای تابع index اضافه شود:

if (OW:getRequest()->isPost())
{
    if ( $form->isValid($_POST) )
    {
        $data = $form->getValues();
 
        $mail = OW::getMailer()->createMail();
        $mail->addRecipientEmail($data['to']);
        $mail->setSender($data['from']);
        $mail->setSubject($data['subject']);
        $mail->setTextContent($data['message']);
        OW::getMailer()->addToQueue($mail);
 
        OW::getSession()->set('contactus.dept', $contactEmails[$data['to']]);
        $this->redirectToAction('sent');
    }
}

در کد فوق:

  • فراخوانی OW::getRequest()→isPost() اجازه می‌دهد تا مشخص شود آیا داده‌هایی که توسط فرم فرستاده شده‌اند با متد پست (POST) دریافت شده‌اند یا خیر.
  • $form→isValid($_POST) درستی آرایه داده‌ای $_POST را بررسی می‌کند و داده را به شی‌ء فرم می‌افزاید.
  • با فراخوانی $data = $form→getValues()، آرایه‌ای شامل فیلدهای فرم دریافت خواهد شد. سپس، یک بسته ارسالی با شیء Mailer ساخته شده و به صف ارسال رایانامه اضافه می‌شود.
  • با فراخوانی OW::getSession()، به شی نشست کاربر دسترسی داده خواهد شد. با استفاده از متد set() یک مقدار (رایانشانی انتخابی کاربر) با کلید 'contactus.dept' در نشست کاربر تنظیم می‌شود. به کلید تولید‌شده برای ذخیره در نشست دقت شود. این کلید شامل پیشوند (که شناسه افزونه است) و اسم متغیر است که با یک نقطه جدا شده‌اند. این کار بدین منظور انجام می‌شود که کلید تعریف شده به صورت تصادفی با کلید‌های افزونه‌های دیگر تلاقی نداشته باشد.
  • خط آخر $this→redirectToAction('sent')، کاربر را به صفحه «sent» هدایت می‌کند. هنوز این صفحه تعریف نشده است. در ادامه این اقدام صورت پذیرفته است:

public function sent()
{
    $dept = null;
    if ( OW::getSession()->isKeySet('contactus.dept') )
    {
        $dept = OW::getSession()->get('contactus.dept');
        OW::getSession()->delete('contactus.dept');
    }
    else
    {
        $this->redirectToAction('index');
    }
 
    $feedback = $this->text('contactus', 'message_sent', ( $dept === null ) ? null : array('dept' => $dept));
    $this->assign('feedback', $feedback);
}

  • در ابتدای تابع sent که در حقیقت یک عملیات کنترل‌کننده است، بررسی می‌شود که آیا متغیر در نشست کاربر وجود دارد یا خیر. اگر آن‌جا وجود داشته باشد، دریافت شده (با استفاده از تابع get) و سپس پاک خواهد شد (با استفاده از تابع delete).
  • در این مرحله یک متغیر از نوع متن تحت عنوان $feedback حاوی پیام مناسب به کاربر ایجاد شده و به view متناظر با کنترل‌کننده ارسال خواهد شد. این کار با استفاده از تابع assign صورت می‌پذیرد.
  • همچنین، می‌بایست یک شناسه message_sent در بخش زبان به همراه متن زیر ساخته شود:

“پیام شما با موفقیت ارسال شد.{$dept} به زودی پاسخ شما را خواهد داد. با تشکر.” همانطور که از عبارت فوق قابل مشاهده است، در عبارات زبان می‌توان از متغیر ({$dept}) استفاده کرد. این متغیر با مقداری که به تابع text فرستاده می‌شود جایگزین خواهد شد. توجه شود که سومین پارامتر هنگام فراخوانی متد text به صورت یک آرایه از نوع key ⇒ value است. در این مورد، کلید یا شناسه dept است و مقدار، متغیری است که از نشست دریافت می‌شود. در انتها می‌بایست یک قالب برای عملیاتsent ساخته شود. در این راستا فایل views/controllers/contact_sent.html با محتوای زیر ایجاد می‌گردد.

<div class="ow_center">{$feedback}</div>

همانطور که قابل مشاهده است متغیر $feedback که توسط تابع assign به قالب فرستاده شده بود داخل فایل HTML متناظر با کنترل‌کننده قابل دسترسی است.

با انجام اقدامات فوق، اکنون می‌توان به وبگاه مدیریت از راه فرم تماس بازخورد فرستاد.

4-7- استفاده از پایگاه داده

مشکل اصلی فرم ساخته‌شده در بخش قبل این بود که رایانشانی‌های تماس (دپارتمان‌ها) در تابع index به صورت کد غیرقابل تغییر نوشته شده‌اند. در این مرحله افزونه طوری تغییر داده می‌شود که مقادیر رایانشانی‌ها داخل پایگاه داده قرار گیرد و از طریق میزفرمان مدیریت سامانه قابل دسترسی باشد.

اکنون به شرح ساخت بخش مدیریتی نرم‌افزار برای ذخیره رایانشانی‌ها در پایگاه داده پرداخت می‌شود. بدین منظور ابتدا پوشه‌ای تحت عنوان bol در پوشه اصلی افزونه ساخته می‌شود. در پوشه ساخته‌شده، می‌بایست سه فایل ایجاد شود:

  • department.php: برای تعریف موجودیت «department»
  • department_dao.php: برای تعریف شیء دسترسی به داده
  • service.php: برای تعریف توابع سطح بالای لازم برای استفاده از اشیاء مرتبط با پایگاه داده

همچنین باید کد زیر به department.php اضافه شود:

class CONTACTUS_BOL_Department extends OW_Entity
{
    /**
     * @var string
     */
    public $email;
}

کلاسی که در کد فوق تعریف شده است در واقع یک DTO است. DTO نوعی انعکاس از جداول پایگاه داده است. قانون نام‌گذاری برای کلاس‌ها و فایل‌های DTO با کنترل‌کننده‌ها یکسان است با این تفاوت که پسوند CTRL باید با BOL جایگزین شود. DTO باید از کلاس OW_Entity ارث‌بری داشته باشد. همچنین باید کد DAO به department_dao.php افزوده شود.

class CONTACTUS_BOL_DepartmentDao extends OW_BaseDao
{
 
    /**
     * Constructor.
     *
     */
    protected function __construct()
    {
        parent::__construct();
    }
    /**
     * Singleton instance.
     *
     * @var CONTACTUS_BOL_DepartmentDao
     */
    private static $classInstance;
 
    /**
     * Returns an instance of class (singleton pattern implementation).
     *
     * @return CONTACTUS_BOL_DepartmentDao
     */
    public static function getInstance()
    {
        if ( self::$classInstance === null )
        {
            self::$classInstance = new self();
        }
 
        return self::$classInstance;
    }
 
    /**
     * @see OW_BaseDao::getDtoClassName()
     *
     */
    public function getDtoClassName()
    {
        return 'CONTACTUS_BOL_Department';
    }
 
    /**
     * @see OW_BaseDao::getTableName()
     *
     */
    public function getTableName()
    {
        return OW_DB_PREFIX . 'contactus_department';
    }
}

کلاس DAO برای تعامل با پایگاه داده لازم است. به عبارتی، DAO داده‌ها را از جدول به DTO نگاشت می‌کند و برمی‌گرداند. قاعده نام‌گذاری کلاس و فایل DAO با کنترل‌کننده‌ها یکسان است؛ با این تفاوت که پسوند CTRL باید به BOL تغییر یابد. کلاس DAO از کلاس انتزاعی OW_BaseDao ارث‌بری کرده است و در واقع یک تک‌شیء است. OW_BaseDao دارای تعدادی تابع سودمند برای کار با پایگاه داده است.

سپس می‌بایست کد service.php برای کار با دپارتمان‌ها به service.php افزوده شود.

class CONTACTUS_BOL_Service
{
    /**
     * Singleton instance.
     *
     * @var CONTACTUS_BOL_Service
     */
    private static $classInstance;
 
    /**
     * Returns an instance of class (singleton pattern implementation).
     *
     * @return CONTACTUS_BOL_Service
     */
    public static function getInstance()
    {
        if ( self::$classInstance === null )
        {
            self::$classInstance = new self();
        }
 
        return self::$classInstance;
    }
 
    private function __construct()
    {
 
    }
 
    public function getDepartmentLabel( $id )
    {
        return OW::getLanguage()->text('contactus', $this->getDepartmentKey($id));
    }
 
    public function addDepartment( $email, $label )
    {
        $contact = new CONTACTUS_BOL_Department();
        $contact->email = $email;
        CONTACTUS_BOL_DepartmentDao::getInstance()->save($contact);
 
        BOL_LanguageService::getInstance()->addValue(
            OW::getLanguage()->getCurrentId(),
            'contactus',
            $this->getDepartmentKey($contact->id),
            trim($label));
    }
 
    public function deleteDepartment( $id )
    {
        $id = (int) $id;
        if ( $id > 0 )
        {
            $key = BOL_LanguageService::getInstance()->findKey('contactus', $this->getDepartmentKey($id));
            BOL_LanguageService::getInstance()->deleteKey($key->id, true);
            CONTACTUS_BOL_DepartmentDao::getInstance()->deleteById($id);
        }
    }
 
    private function getDepartmentKey( $name )
    {
        return 'dept_' . trim($name);
    }
 
    public function getDepartmentList()
    {
        return CONTACTUS_BOL_DepartmentDao::getInstance()->findAll();
    }

این کلاس نیز مانند DAO تک‌شیء است. تفاوت بین service و DAO به این صورت است که service تابع‌های DAO را فراخوانی می‌کند. در حقیقت service اطلاعی از چگونگی ذخیره شدن داده نداشته و فقط منطق تجاری برنامه را اجرا می‌کند. در DAO هیچ منطق کسب‌و‌کاری وجود ندارد و تنها پرس‌وجوهای پایگاه داده وجود دارند.

به تابع getDepartmentList در کلاس CONTACTUS_BOL_Service توجه شود. یک نمونه از کلاس CONTACTUS_BOL_DepartmentDao در این متد گرفته ‌شده و با فراخوانی تابع findAll آن، یک آرایه از اشیا CONTACTUS_BOL_Department بازگردانده می‌شود.

در خصوص تابع addDepartment نیز ابتدا یک شی کلاس CONTACTUS_BOL_Department ایجاد می‌شود. سپس یک رایانشانی در آن مقداردهی شده و شی‌ء با استفاده از متد save در کلاس CONTACTUS_BOL_DepartmentDao ذخیره شود. اکنون یک برچسب برای دپارتمان ایجادشده جدید در زیرساخت زبان‌های موتوشاب اضافه می‌کنیم. این کار با استفاده از متد addValue از کلاس BOL_LanguageService انجام می‌شود.

به منظور ایجاد یک جدول دپارتمان هنگام نصب افزونه، کد زیر باید به آخر فایل install.php افزوده شود:

$sql = "CREATE TABLE `" . OW_DB_PREFIX . "contactus_department` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `email` VARCHAR(200) NOT NULL,
    PRIMARY KEY (`id`)
)
ENGINE=MyISAM
ROW_FORMAT=DEFAULT";
 
OW::getDbo()->query($sql);

نکته مهم: ثابت OW_DB_PREFIX یک پیشوند جدول است که در هنگام نصب سامانه تعیین می‌شود. این ثابت باید همیشه هنگام ساخت یا پرس‌وجو با پایگاه داده مورد استفاده قرار گیرد.

نکته مهم: نام جداول باید بر اساس قالب زیر ساخته شوند: <pluginkey>_<tablename>. این قالب از تداخل جداول یک افزونه با افزونه‌های دیگر جلوگیری می‌کند.

با فراخوانی OW::getDbo، شی‌ء OW_Database برای کار با پایگاه داده در دسترس قرار می‌گیرد که سطح پایین‌تر از OW_BaseDao است. کلاس OW_Database یک پوشش برای PDO است. به منظور انجام یک پرس‌و‌جو از یک پایگاه داده، از تابع query واقع در کلاس OW_Database استفاده می‌شود که یک پرس‌و‌جو sql را به عنوان ورودی می‌گیرد.

در بخش میز فرمان مدیریت، افزونه خود را حذف و دوباره نصب نمایید. پس از آن، جدول contactus_department ایجاد خواهد شد.

4-8- ایجاد صفحه تنظیمات در بخش مدیریت

در این بخش یک پنل برای مدیریت دپارتمان‌ها ساخته می‌شود. در این راستا می‌بایست یک فایل admin.php در پوشه controllers افزونه ایجاد شده و سپس کد کنترل‌کننده زیر به این فایل افزوده شود:

class CONTACTUS_CTRL_Admin extends ADMIN_CTRL_Abstract
{
 
    public function dept()
    {
        $this->setPageTitle(OW::getLanguage()->text('contactus', 'admin_dept_title'));
        $this->setPageHeading(OW::getLanguage()->text('contactus', 'admin_dept_heading'));
        $contactEmails = array();
        $deleteUrls = array();
        $contacts = CONTACTUS_BOL_Service::getInstance()->getDepartmentList();
        foreach ( $contacts as $contact )
        {
            /* @var $contact CONTACTUS_BOL_Department */
            $contactEmails[$contact->id]['name'] = $contact->id;
            $contactEmails[$contact->id]['email'] = $contact->email;
            $contactEmails[$contact->id]['label'] = CONTACTUS_BOL_Service::getInstance()->getDepartmentLabel($contact->id);
            $deleteUrls[$contact->id] = OW::getRouter()->urlFor(__CLASS__, 'delete', array('id' => $contact->id));
        }
        $this->assign('contacts', $contactEmails);
        $this->assign('deleteUrls', $deleteUrls);
 
        $form = new Form('add_dept');
        $this->addForm($form);
 
        $fieldEmail = new TextField('email');
        $fieldEmail->setRequired();
        $fieldEmail->addValidator(new EmailValidator());
        $fieldEmail->setInvitation(OW::getLanguage()->text('contactus', 'label_invitation_email'));
        $fieldEmail->setHasInvitation(true);
        $form->addElement($fieldEmail);
 
        $fieldLabel = new TextField('label');
        $fieldLabel->setRequired();
        $fieldLabel->setInvitation(OW::getLanguage()->text('contactus', 'label_invitation_label'));
        $fieldLabel->setHasInvitation(true);
        $form->addElement($fieldLabel);
 
        $submit = new Submit('add');
        $submit->setValue(OW::getLanguage()->text('contactus', 'form_add_dept_submit'));
        $form->addElement($submit);
 
        if ( OW::getRequest()->isPost() )
        {
            if ( $form->isValid($_POST) )
            {
                $data = $form->getValues();
                CONTACTUS_BOL_Service::getInstance()->addDepartment($data['email'], $data['label']);
                $this->redirect();
            }
        }
    }
 
    public function delete( $params )
    {
        if ( isset($params['id']) )
        {
            CONTACTUS_BOL_Service::getInstance()->deleteDepartment((int) $params['id']);
        }
        $this->redirect(OW::getRouter()->urlForRoute('contactus.admin'));
    }
}

به خط زیر در متد dept توجه شود:

$contacts = CONTACTUS_BOL_Service::getInstance()->getDepartmentList();

این خط فهرستی از همه دپارتمان‌های موجود را ارائه می‌کند. پس از بازیابی تمامی رایانشانی‌ها، باید آرایه‌ رایانشانی‌ها به گونه‌ای برای نمایش به صورت جدول به نمایش درآید:

OW::getRouter()->urlFor(__CLASS__, 'delete', array('id' => $contact->id));

با استفاده از متد urlFor از کلاس OW_Router می‌توان آدرس عملیات delete از کنترل‌کننده فعلی به همراه پارامتر id از نوع GET ایجاد کرد.

در این مرحله، باید یک قالب برای عملیات dept ایجاد شود. به همین منظور بایستی فایل views/controllers/admin_dept.html با محتویات زیر ایجاد شود:

<table class="ow_table_1 ow_automargin" style="width: 400px;">
    {foreach from=$contacts item=contact}
    <tr class="{cycle values='ow_alt1,ow_alt2'}">
        <td width="1"><a href="{$deleteUrls[$contact.name]}" onclick="return confirm('{text key="base+are_you_sure"}');" style="width:16px; height:16px; display:block; margin:0 auto;background-repeat:no-repeat;background-position: 50% 50%;" class="ow_ic_delete"></a></td>
        <td>{$contact.email}</td>
        <td>{$contact.label}</td>
    </tr>
    {/foreach}
</table>
 
{form name='add_dept'}
<table class="ow_table_1 ow_form ow_automargin" style="width: 400px;">
    <tr class="ow_alt1">
        <td class="ow_value">{input name='email'}</td>
        <td class="ow_value">{input name='label'}</td>
    </tr>
    <tr>
        <td class="ow_center" colspan="2">{submit name='add' class='ow_button ow_ic_save'}</td>
    </tr>
</table>
{/form}

عبارت {text key=“base+are_you_sure”} یک شیوه هوشمندانه برای نمایش عبارت‌های زبان است (در واقع معادل OW::getLanguage→text برای استفاده در HTML). باید پیشوند افزودن و شناسه زبان را که با نشانه «+» از یکدیگر جدا شده‌اند را به پارامتر key اضافه کرد.

با توجه به توضیحات فوق، صفحه پیکربندی دپارتمان‌ها در آدرس زیر در دسترس است:‌

<domain>/contactus/admin/dept

با افزودن چند دپارتمان، صفحه به صورت زیر قابل مشاهده خواهد بود:

شکل 3: بخش تنظیمات افزونه

داده‌ها در پایگاه داده ذخیره شده است اما در فرم تماس نمایش داده نمی‌شود. برای نمایش آن‌ها در فرم تماس به کنترل‌کننده index رفته و کد زیر را:

$contactEmails = array(
    'admin@site.com' => 'Site administration',
    'support@site.com' => 'Technical support',
    'billing@site.com' => 'Billing department'
);

با کد زیر جایگزین کنید:

$contactEmails = array();
$contacts = CONTACTUS_BOL_Service::getInstance()->getDepartmentList();
foreach ( $contacts as $contact )
{
    /* @var $contact CONTACTUS_BOL_Department */
    $contactEmails[$contact->email] = CONTACTUS_BOL_Service::getInstance()->getDepartmentLabel($contact->id);
}

افزونه در این مرحله تقریبا آماده شده است؛ اما باید پیوند صفحه پیکربندی دپارتمان‌ها به تنظیمات افزونه در پنل مدیریت اضافه شود. به همین منظور باید خط زیر به پایان فایل init.php افزوده شود:

OW::getRouter()->addRoute(new OW_Route('contactus.admin', 'admin/plugins/contactus', "CONTACTUS_CTRL_Admin", 'dept'));

خط بالا صفحه را در آدرس زیر در دسترس قرار می‌دهد:

<domain>/admin/plugins/contactus

همچنین خط زیر می‌بایست به انتهای فایل activate.php شود.

OW::getPluginManager()->addPluginSettingsRouteName('contactus', 'contactus.admin');

در این لحظه می‌بایست افزونه یک‌بار حذف و دوباره نصب گردد. در حال حاضر اگر به صفحه <domain>/admin/plugins مراجعه شود دکمه «تنظیمات» بعد از نام افزونه قرار گرفته است.

شکل 4: نمای افزونه بعد از فعال‌سازی تنظیمات

توجه: اگر جداول و زبان‌ها بر اساس قوانینی که در این فایل ذکر شدند افزوده شوند، در هنگام پاک کردن افزونه، آن‌ها نیز حذف خواهند شد.

راهنمای_توسعه_افزونه.txt · آخرین ویرایش: 2019/01/15 12:45