Oxwall  is the most popular SNS framework that using PHP was developed as a project of Skalfa LLC. This is a supplementary explanation of the “Plugin Development Crash Course”. Check it out.

* This tutorial doesn’t include explanation of background and detail of codes. Please refer original tutorials.

1. Setup CentoOS-7 on the VirtualBox see this.

* You should be changed to the name of user account like “oxwall”.

2. Install packages to running the Oxwall

2.1 PHP/HTTP install

[oxwall@localhost ~]$ sudo yum install -y epel-release
[oxwall@localhost ~]$ sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
[oxwall@localhost ~]$ sudo yum -y install --enablerepo=remi,remi-php70 php php-devel php-mbstring php-pdo php-gd php-mysql php-xml
[oxwall@localhost ~]$ sudo yum -y --enablerepo=remi,remi-php70 install php-pecl-zip
[oxwall@localhost ~]$ sudo yum -y --enablerepo=remi,remi-php70 install php-pecl-mysql

2.2 MySQL install

[oxwall@localhost ~]$ sudo yum -y localinstall http://dev.mysql.com/get/mysql57-community-release-el7-7.noarch.rpm
[oxwall@localhost ~]$ sudo yum -y install mysql mysql-devel mysql-server mysql-utilities

[oxwall@localhost ~]$ sudo systemctl start mysqld
[oxwall@localhost ~]$ sudo systemctl enable mysqld

[oxwall@localhost ~]$ sudo cat /var/log/mysqld.log | grep password
2018-01-29T02:35:32.202031Z 1 [Note] A temporary password is generated for root@localhost: o6K;lud#>kGa

[oxwall@localhost ~]$ sudo mysql_secure_installation

* Please Note: Password “o6K;lud#>kGa” is needed to setup MySQL. Then New password setup, Remove Anonymous users, Disallow root login remotely, Remove test database and access to it, Reload privilege tables now.

2.3 HTTP setup

[oxwall@localhost ~]$ sudo vi /etc/httpd/conf/httpd.conf

151 AllowOverride All

[oxwall@localhost ~]$ sudo vi /etc/sysconfig/selinux

7 SELINUX=disabled

[oxwall@localhost ~]$ sudo systemctl start httpd
[oxwall@localhost ~]$ sudo systemctl enable httpd
[oxwall@localhost ~]$ sudo firewall-cmd --permanent --zone=public --add-service=http
[oxwall@localhost ~]$ sudo firewall-cmd --reload

2.4 Reboot

[oxwall@localhost ~]$ sudo reboot

2.5 Now,  You can access to the http server from Safari.

3. Oxwall install/setup

3.1 Download and unzip

[root@localhost ~]$ cd /var/www/html
[root@localhost ~]$ sudo su

[root@localhost html]# wget https://developers.oxwall.com/dl/oxwall-1.8.4.1.zip
[root@localhost html]# unzip oxwall-1.8.4.1.zip

[root@localhost html]# wget https://www.phpmyadmin.net/downloads/phpMyAdmin-latest-all-languages.tar.gz
[root@localhost html]# tar -xvzf phpMyAdmin-latest-all-languages.tar.gz
[root@localhost html]# mv phpMyAdmin-4.7.7-all-languages phpMyAdmin

[root@localhost html]# sudo groupadd www
[root@localhost html]# sudo usermod -a -G www oxwall
[root@localhost html]# sudo chown -R root:www /var/www
[root@localhost html]# sudo chmod 2775 /var/www
[root@localhost html]# vi .htaccess

14 RewriteCond %{REQUEST_URI} !(^/phpMyAdmin/)

3.1 MySQL database setup, access to http://192.168.56.101/phpMyAdmin, login as root

3.2 Create MySQL DB, [New ] -> Name:”oxwall_db” Collation: utf8_general_ci -> [Create]

3.3 Restart HTTP

[root@localhost html]# systemctl restart httpd

3.4 Access to the 192.168.56.101

3.5 Fill out Site info appropriately

3.6 Fill out database info

3.6 Copy and paste code replacing the existing one into ow_includes/config.php file.

3.7 Cron settings, then [CONTINUE]

[root@localhost html]# crontab -e

*/1 * * * * /bin/php /var/www/html/ow_cron/run.php

3.7 Remove install folder, then page refresh.

[root@localhost html]# rm -rf ow_install

4. Next step is the Oxwall tutorial’s tutorial

4.1 Get Developer key, Go to Oxwall Store

– Sign up *Free

– Get Developer Key, [STORE] -> [DEVELOPER TOOL], Copy “Your Developer Key”.

4.3 Create Plugin files , and files initialize.

[root@localhost html]# cd /var/www/html/ow_plugins
[root@localhost ow_plugins]# mkdir crashcourse
[root@localhost ow_plugins]# cd crashcourse
[root@localhost crashcourse]# touch init.php
[root@localhost crashcourse]# vi plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<name>Crash Course</name>
<key>crashcourse</key>
<description>"Tutorial" page with the ability to choose departments (email addresses).</description>
  <author>UBUNIFU INC.</author>
  <authorEmail>xxx@ubunifu.co</authorEmail>
  <authorUrl>https://ubunifu.co</authorUrl>
  <developerKey>your_developer_key_here</developerKey>
  <build>1</build>
  <copyright>(C) 2018 UBUNIFU INC. All rights reserved.</copyright>
  <license>The BSD License</license>
  <licenseUrl>http://www.opensource.org/licenses/bsd-license.php</licenseUrl>
</plugin>
[root@localhost crashcourse]# touch install.php
[root@localhost crashcourse]# touch uninstall.php
[root@localhost crashcourse]# touch activate.php
[root@localhost crashcourse]# touch deactivate.php
[root@localhost crashcourse]# echo "<?php" >> install.php
[root@localhost crashcourse]# echo "BOL_LanguageService::getInstance()->addPrefix('crashcourse', 'Crash Course');" >> install.php

4.4 Plugin installation

– Go to Admin area → Plugins → Available Plugins. Click Install on your new plugin.

4.5 Creating a page

[root@localhost crashcourse]# mkdir controllers
[root@localhost crashcourse]# mkdir views
[root@localhost crashcourse]# mkdir views/controllers
[root@localhost crashcourse]# touch controllers/crash.php
[root@localhost crashcourse]# touch views/controllers/crash_index.html
[root@localhost crashcourse]# vi controllers/crash.php
<?php

class CRASHCOURSE_CTRL_Crash extends OW_ActionController
{
  public function index()
  {
    $this->setPageTitle(OW::getLanguage()->text('crashcourse', 'index_page_title'));
    $this->setPageHeading(OW::getLanguage()->text('crashcourse', 'index_page_heading'));
  }
}

– Access to http://192.168.56.101/crashcourse/crash/index

– Input appropriate. -> [CONTINUE]

– Access to http://192.168.56.101/crashcourse/crash/index

* If you wish to make localization, go to http://192.168.56.101/admin/settings/dev-tools/languages

4.6 Page routing

– Set the routing

[root@localhost crashcourse]# vi init.php
<?php

OW::getRouter()->addRoute(new OW_Route('crashcourse.index', 'crash', "CRASHCOURSE_CTRL_Crash", 'index'));

– Access to http://192.168.56.101/crash

– Adds a link into the bottom menu.

[root@localhost crashcourse]# vi activate.php
<?php

OW::getNavigation()->addMenuItem(OW_Navigation::BOTTOM, 'crashcourse.index', 'crashcourse', 'bottom_menu_item', OW_Navigation::VISIBLE_FOR_ALL);
[root@localhost crashcourse]# vi deactivate.php
<?php

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

– Go to [Installed plugins] page, then deactivate Crash Course and Re-activate Crash Course. Now, you can see a link in bottom menu.

4.7 Using forms

 – Adds code into the crash.php

[root@localhost crashcourse]# vi controllers/crash.php
<?php
class CRASHCOURSE_CTRL_Crash extends OW_ActionController 
{ 
  public function index() 
  {
    $this->setPageTitle(OW::getLanguage()->text('crashcourse', 'index_page_title')); 
    $this->setPageHeading(OW::getLanguage()->text('crashcourse', 'index_page_heading')); 

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

    $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('crashcourse', 'form_label_to'));
    $form->addElement($fieldTo);
 
    $fieldFrom = new TextField('from');
    $fieldFrom->setLabel($this->text('crashcourse', 'form_label_from'));
    $fieldFrom->setRequired();
    $fieldFrom->addValidator(new EmailValidator());
    $form->addElement($fieldFrom);
 
    $fieldSubject = new TextField('subject');
    $fieldSubject->setLabel($this->text('crashcourse', 'form_label_subject'));
    $fieldSubject->setRequired();
    $form->addElement($fieldSubject);
 
    $fieldMessage = new Textarea('message');
    $fieldMessage->setLabel($this->text('crashcourse', 'form_label_message'));
    $fieldMessage->setRequired();
    $form->addElement($fieldMessage);
 
    // Using captcha here to prevent bot spam
    $fieldCaptcha = new CaptchaField('captcha');
    $fieldCaptcha->setLabel($this->text('crashcourse', 'form_label_captcha'));
    $form->addElement($fieldCaptcha);
 
    $submit = new Submit('send');
    $submit->setValue($this->text('crashcourse', 'form_label_submit'));
    $form->addElement($submit);

    $this->addForm($form);

    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('crashcourse.dept', $contactEmails[$data['to']]);
        $this->redirectToAction('sent');
      }
    }
  }

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

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

 – Create a view

[root@localhost crashcourse]# vi views/controllers/crash_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}

– Create view for the sent

[root@localhost crashcourse]# vi views/controllers/crash_sent.html
<div class="ow_center">{$feedback}</div>

4.8 Cache clear * If you don’t clear the cache, modified pages are not reflected.

– Go to https://developers.oxwall.com/store/item/579

– Download plugin

– FTP enable/setup for to upload plugin.

[root@localhost html]# yum install -y vsftpd ftp
[root@localhost html]# vi /etc/vsftpd/user_list

#root

[root@localhost html]# vi /etc/vsftpd/ftpusers

#root

[root@localhost html]# echo "setsebool -P ftp_home_dir on" >> /etc/sysconfig/selinux
[root@localhost html]# firewall-cmd --permanent --add-port=21/tcp
[root@localhost html]# firewall-cmd --reload
[root@localhost html]# systemctl enable vsftpd
[root@localhost html]# systemctl start vsftpd

– Plugin upload, [Add new] -> [Choose file] -> “Casheextreme.zip” -> [UPLOAD]

– FTP info input, host: 192.168.56.101, Login: root, Password: ****, -> [ENTER]

– Install plugin

– Cache clear, [Installed Plugin] -> Cache Extreme -> [SETTINGS] -> [CLEAN IT UP]

 

– Access to http://192.168.56.101/crash

4.9 Using database

– Adds files for database

[root@localhost crashcourse]# mkdir bol
[root@localhost crashcourse]# touch bol/department.php
[root@localhost crashcourse]# touch bol/department_dao.php
[root@localhost crashcourse]# touch bol/service.php
[root@localhost crashcourse]# vi bol/department.php
<?php

class CRASHCOURSE_BOL_Department extends OW_Entity
{
  /**
  * @var string
  */
  public $email;
}
[root@localhost crashcourse]# vi bol/department_dao.php
<?php

class CRASHCOURSE_BOL_DepartmentDao extends OW_BaseDao
{
  /**
  * Constructor.
  *
  */
  protected function __construct()
  {
    parent::__construct();
  }
  /**
  * Singleton instance.
  *
  * @var CRASHCOURSE_BOL_DepartmentDao
  */
  private static $classInstance;
  /**
  * Returns an instance of class (singleton pattern implementation).
  *
  * @return CRASHCOURSE_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 'CRASHCOURSE_BOL_Department';
    }
    /**
    * @see OW_BaseDao::getTableName()
    *
    */
    public function getTableName()
    {
      return OW_DB_PREFIX . 'crashcourse_department';
    }
}
[root@localhost crashcourse]# vi bol/service.php
<?php
class CRASHCOURSE_BOL_Service
{
    /**
     * Singleton instance.
     *
     * @var CRASHCOURSE_BOL_Service
     */
    private static $classInstance;
 
    /**
     * Returns an instance of class (singleton pattern implementation).
     *
     * @return CRASHCOURSE_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('crashcourse', $this->getDepartmentKey($id));
    }
 
    public function addDepartment( $email, $label )
    {
        $contact = new CRASHCOURSE_BOL_Department();
        $contact->email = $email;
        CRASHCOURSE_BOL_DepartmentDao::getInstance()->save($contact);
 
        BOL_LanguageService::getInstance()->addValue(
            OW::getLanguage()->getCurrentId(),
            'crashcourse',
            $this->getDepartmentKey($contact->id),
            trim($label));
    }
 
    public function deleteDepartment( $id )
    {
        $id = (int) $id;
        if ( $id > 0 )
        {
            $key = BOL_LanguageService::getInstance()->findKey('crashcourse', $this->getDepartmentKey($id));
            BOL_LanguageService::getInstance()->deleteKey($key->id, true);
            CRASHCOURSE_BOL_DepartmentDao::getInstance()->deleteById($id);
        }
    }
 
    private function getDepartmentKey( $name )
    {
        return 'dept_' . trim($name);
    }
 
    public function getDepartmentList()
    {
        return CRASHCOURSE_BOL_DepartmentDao::getInstance()->findAll();
    }
}
[root@localhost crashcourse]# vi install.php
<?php

BOL_LanguageService::getInstance()->addPrefix('crashcourse', 'Crash Course');

$sql = "CREATE TABLE `" . OW_DB_PREFIX . "crashcourse_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);

4.10 Creating Settings page in Admin area

 – Create the admin.php

[root@localhost crashcourse]# vi controllers/admin.php
<?php

class CRASHCOURSE_CTRL_Admin extends ADMIN_CTRL_Abstract
{
 
    public function dept()
    {
        $this->setPageTitle(OW::getLanguage()->text('crashcourse', 'admin_dept_title'));
        $this->setPageHeading(OW::getLanguage()->text('crashcourse', 'admin_dept_heading'));
        $contactEmails = array();
        $deleteUrls = array();
        $contacts = CRASHCOURSE_BOL_Service::getInstance()->getDepartmentList();
        foreach ( $contacts as $contact )
        {
            /* @var $contact CRASHCOURSE_BOL_Department */
            $contactEmails[$contact->id]['name'] = $contact->id;
            $contactEmails[$contact->id]['email'] = $contact->email;
            $contactEmails[$contact->id]['label'] = CRASHCOURSE_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('crashcourse', 'label_invitation_email'));
        $fieldEmail->setHasInvitation(true);
        $form->addElement($fieldEmail);
 
        $fieldLabel = new TextField('label');
        $fieldLabel->setRequired();
        $fieldLabel->setInvitation(OW::getLanguage()->text('crashcourse', 'label_invitation_label'));
        $fieldLabel->setHasInvitation(true);
        $form->addElement($fieldLabel);
 
        $submit = new Submit('add');
        $submit->setValue(OW::getLanguage()->text('crashcourse', 'form_add_dept_submit'));
        $form->addElement($submit);
 
        if ( OW::getRequest()->isPost() )
        {
            if ( $form->isValid($_POST) )
            {
                $data = $form->getValues();
                CRASHCOURSE_BOL_Service::getInstance()->addDepartment($data['email'], $data['label']);
                $this->redirect();
            }
        }
    }
 
    public function delete( $params )
    {
        if ( isset($params['id']) )
        {
            CRASHCOURSE_BOL_Service::getInstance()->deleteDepartment((int) $params['id']);
        }
        $this->redirect(OW::getRouter()->urlForRoute('crashcourse.admin'));
    }
}

– Create the admin_dept.html

[root@localhost crashcourse]# vi 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}

– Cache clear

– [Installed plugin] -> Crash Course -> [UNINSTALL]

– [Available plugin] -> Crash Course -> [INSTALL]

– Access to http://192.168.56.101/crashcourse/admin/dept

– Modify controllers/crash.php

[root@localhost crashcourse]# vi controllers/crash.php
<?php
class CRASHCOURSE_CTRL_Crash extends OW_ActionController 
{ 
  public function index() 
  {
    $this->setPageTitle(OW::getLanguage()->text('crashcourse', 'index_page_title')); 
    $this->setPageHeading(OW::getLanguage()->text('crashcourse', 'index_page_heading')); 

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

    $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('crashcourse', 'form_label_to'));
    $form->addElement($fieldTo);
 
    $fieldFrom = new TextField('from');
    $fieldFrom->setLabel($this->text('crashcourse', 'form_label_from'));
    $fieldFrom->setRequired();
    $fieldFrom->addValidator(new EmailValidator());
    $form->addElement($fieldFrom);
 
    $fieldSubject = new TextField('subject');
    $fieldSubject->setLabel($this->text('crashcourse', 'form_label_subject'));
    $fieldSubject->setRequired();
    $form->addElement($fieldSubject);
 
    $fieldMessage = new Textarea('message');
    $fieldMessage->setLabel($this->text('crashcourse', 'form_label_message'));
    $fieldMessage->setRequired();
    $form->addElement($fieldMessage);
 
    // Using captcha here to prevent bot spam
    $fieldCaptcha = new CaptchaField('captcha');
    $fieldCaptcha->setLabel($this->text('crashcourse', 'form_label_captcha'));
    $form->addElement($fieldCaptcha);
 
    $submit = new Submit('send');
    $submit->setValue($this->text('crashcourse', 'form_label_submit'));
    $form->addElement($submit);

    $this->addForm($form);

    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('crashcourse.dept', $contactEmails[$data['to']]);
        $this->redirectToAction('sent');
      }
    }
  }

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

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

– Access to http://192.168.56.101/crash

– Adds SETTING button

[root@localhost crashcourse]# vi init.php
<?php

OW::getRouter()->addRoute(new OW_Route('crashcourse.index', 'crash', "CRASHCOURSE_CTRL_Crash", 'index'));
OW::getRouter()->addRoute(new OW_Route('crashcourse.admin', 'admin/plugins/crashcourse', "CRASHCOURSE_CTRL_Admin", 'dept'));
[root@localhost crashcourse]# vi install.php
<?php

BOL_LanguageService::getInstance()->addPrefix('crashcourse', 'Crash Course');

$sql = "CREATE TABLE `" . OW_DB_PREFIX . "crashcourse_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::getPluginManager()->addPluginSettingsRouteName('crashcourse', 'crashcourse.admin');

– Cache clear

– [Installed plugin] -> Crash Course -> [UNINSTALL]

– [Available plugin] -> Crash Course -> [INSTALL]

– [Installed plugin] -> Crash Course -> [SETTINGS]

 

Done 😃