1. WARNING - This part of forum is for English language writed posts ONLY! Any other language isn't alowed! Please respect forum rules, otherwise, your posts will be deleted, and you will be warned, one time only!
  2. Nobody can help you if you did not specify the version of your SE and/or plufgin.
  3. Мы продолжаем публиковать интересные статьи на тему SocialEngine 4. Одна из статей посвящена правильному выбору сервера для вашей социальной сети, а другая содержит советы по увеличению производительности платформы. Также мы добавили большую статью о пользовательских уровнях. Ознакомиться со статьями вы можете в разделе Вопросы и Ответы SocialEngine 4.
  4. Вам кажется, что ваша версия SocialEngine 4, работает медленно?

    Голосуйте. Пишите свою версию системы, железо на чем работает и количество пользователей. Будем увеличивать производительность :-) Подробнее

  5. В связи с участившимися случаями попыток продажи пользователями форума различных "сборок" коммерческих социальных платформ, обычно основанных на SocialEngine 3, вводится новое правило для форума. Запрещается создание тем или размещение в уже созданных предложений о продаже или размещение ссылок на сайты, где происходит продажа "сборок". Пользователи, которые продолжат свою коммерческую деятельность в данном направлении, будут заблокированы. Подробнее.

User Manuals User Manuals - little about SE basics, from parts.

Discussion in 'Tutorials & Little School for SE' started by nadri, Jul 31, 2013.

Thread Status:
Not open for further replies.
  1. nadri Thread starter Administrator


    Offline
    • Admin
    • Знаменитый
    Message Count:
    1,257
    Likes Received:
    392
    My version of SE:
    4.6.0
    Widgets


    Widget creation is one of the most common tasks in SE4 module development. Widgets are called "blocks" in the admin panel which in my opinion is a better term and for the rest of this post I will use blocks and widgets interchangeably. Widgets can contain forms, lists, menus and other widgets.

    Widget codes are contained in a modules "widgets" path so a modules widgets path relative to the SE4 install directory is: application/modules/<Modulename>/widgets.

    Widget names are prefixed with the modules name (following the rules discussed in earlier posts) and a period (ex. core.html-block and core.comment).
    Widgets can also reside outside of a module which is the case if you decide to use SE4 SDK to create
    a widget package. These widgets reside in application/widgets and their names are not prefixed with a module name.

    To create a widget we need to create a sub-directory in the widgets path. In our case we want to create a widget called "profile-cars". This widget is meant to beused in the users' profile page in the main page tabs similar
    to profile-videos and profile-albums in the Video module and Album module respectively. The name of the
    widget is "car.profile-cars" and the path is ../application/modules/Car/widgets/profile-cars.
    Within this directory we must create Controller.php and index.tpl

    We define a Controller class in Controller.php following this pattern:
    PHP:
    class <Modulename>_Widget_<WidgetName>Controller extends Engine_Content_Widget_Abstract
    {
      public function 
    indexAction()
      {
      }
    }
    With these in place we now have a complete widget. One way to test it is with the content view helper. You can use the following code to display a widget on any view script:
    PHP:

    <?php echo $this->content()->renderWidget('modulename.widget-name')
    As it is our widget will render but we wont see anything in the browser since our index.tpl file is empty. We'll get into that soon. First let us make sure that our widget is available for use in pages in the admin panel.
    To do that we need to add our widgets specs in our modules content.php file. This file is located in ../application/modules/Modulename/settings/content.php. This file contains the specifications of the widgets provided by the module.

    Below is the list of the possible spec parameters, their possible values and what they mean:

    type - optional and defaults to "widget".

    title - The title of the widget. This appears on the admin end as the label of the block in the available blocks list.

    description - The description of the widget. This appears on the admin end as the tool tip when hovering on the block.

    name - The name of the widget. This is the full name of the widget as discussed earlier. This is the name used in the name parameter when using the renderWidget of the content view helper. This is also name field of the engine4_core_content table then the changes in the page is saved.

    category - The available blocks list is categorized. The category is independent of the name of the module that contains the widget. You may create your own category or put your widget on an existing category. Note that the category name is case sensitive and that not setting this parameter will cause the widget to be unavailable in the layout editor.

    special - A non-empty value means the block is the first block on the category it is in. This also makes the background orange instead of green.

    autoEdit - A non-empty value means the edit form pop-up displays when the block is dropped on the page. The edit form pop-up for non-autoedit blocks only displays then the edit link is clicked on the block.

    adminForm - defines the structure of the block's edit form in the layout editor. The elements of this form defines the block's parameters. These parameters are stored as JSON in the engine4_core_content table's params field. On the widgets controller all parameters can be retrieved with _getAllParams and individual parameters can be retrieved with _getParam. adminForm follows the following structure:
    PHP:

        
    'adminForm' =>
          
    'elements' => array(
            array(
              
    'elementtype'// text, textarea, radio, checkbox, etc
              
    'name',        // the name of the parameters/field
              
    array(
                
    // Options. These are passed in the element class' constructor as the options parameter
                
    'label' => ...
                
    'required' => ...
                
    'validators' => ...
    Setting this parameter means the edit form will only have the title field for editing. Errors will occur if this parameter is set incorrectly.
    When this parameter is set SE4 will automatically prepend the title field (if not manually defined) and append the "hide on mobile site" field.

    requirements - The page requirements of the widget that determines whether or not the widget will render. Pages (discussed below) have a provides field.
    This can be one of the following: no-subject, subject, subject=<item name>, viewer, no-viewer. A block that requires no-viewer will only render if the page is being viewed while the user is not logged in. A block that requires subject=car will only render if the page's subject is a car. There are special cases of requires.
    These are header-footer and page-content. Widgets that require header-footer will only render when placed on the header/footer page fragments. Page-content is only used in the widget core.content which contains the rendered content of a view script if a controller invokes:
    PHP:

            $this
    ->_helper->content
                
    ->setEnabled()
                ;
    More on this when we discuss pages.
    isPaginated - a non-empty field means the page will be paginated and the parameter form will include the count field which determines the number of pages to show per page. Additional code must be written for paginated widgets discussed below.

    defaultParams - The default value of the parameters.

    canHaveChildren / childAreaDescription - only used in core.container-tabs.

    Here is the specification of our car.profile-cars widget:
    PHP:

      
    array(
        
    'title' => 'Profile Cars',
        
    'description' => 'Displays a member\'s cars on their profile.',
        
    'category' => 'Cars',
        
    'type' => 'widget',
        
    'name' => 'car.profile-cars',
        
    'isPaginated' => true,
        
    'requirements' => array(
          
    'subject' => 'user',
        ),
      ),
    Here is the controller code:
    PHP:

    class Car_Widget_ProfileCarsController extends Engine_Content_Widget_Abstract
    {
      
    // required since our widget "isPaginated"
      
    protected $_childCount;
     
      public function 
    indexAction()
      {
        
    // Don't render this if not authorized
        
    $viewer Engine_Api::_()->user()->getViewer();
        if( !
    Engine_Api::_()->core()->hasSubject() ) {
          return 
    $this->setNoRender();
        }
     
        
    // Get subject and check auth
        
    $subject Engine_Api::_()->core()->getSubject();
        if( !
    $subject->authorization()->isAllowed($viewer'view') ) {
          return 
    $this->setNoRender();
        }
     
        
    // Get paginator
        
    $profile_owner_id $subject->getIdentity();
        
    $this->view->paginator $paginator Engine_Api::_()->car()->getCarsPaginator(array(
          
    'user_id' => $profile_owner_id,
          
    'search' => 1
        
    ));
     
        
    // Set item count per page and current page number
     
        // note that $this->_getParam accepts an optional parameter which will serve as
     
        // the value of the parameter should the parameter be empty.
     
        
    $paginator->setItemCountPerPage($this->_getParam('itemCountPerPage'8));
        
    $paginator->setCurrentPageNumber($this->_getParam('page'1));
     
        
    // Do not render if nothing to show
        
    if( $paginator->getTotalItemCount() <= ) {
          return 
    $this->setNoRender();
        } else {
          
    $this->_childCount $paginator->getTotalItemCount();
        }
      }
      
    // required since our widget "isPaginated"
      
    public function getChildCount()
      {
        return 
    $this->_childCount;
      }
    }
    Widgets have decorators and in the widget's controller we can manipulate these decorators. We can also retrieve the widgets' parameters and tell the widget to render or not render.

    Any uncaught exceptions raised in the widget's controller or view script will cause it not to render and messages will be written in the error log. In addition, if the site is in development mode messages will be shown
    via fire php. You may use $this->setNoRender(true) to cause the element not to render. You usually want the rest of the controller code to be skipped if you don't want the widget to render so return
    $this->setNoRender(true) would be better.

    By default widgets have two decorators: title and container. The generally have the following HTML structure:
    PHP:

    <div class="generic_layout_container layout_<modulename>_<widget_name>">
    <
    h3><title></h3>
     
    <
    main content>
     
    </
    div>
    We may use the outer most div's class to be able to style the widget via css. The outer most div of the widget is the container decorator of the widget.

    This can be manipulated via $this->getElement()->getDecorator('container') in the widget's controller. The title decorator on displays if the title parameter is set in the layout editor or a default value is set in the widget specification. In the widgets controller these can be overwritten my manipulating the title
    decorator. $this->getElement()->getDecorator('title')->setTitle('My Widget') will set the widgets title to "My Widget" ignoring the values set in the layout editor or the widget specification.
    --- добавлено: Jul 31, 2013 3:48 PM ---
    Pages

    Some of the views in social engine modules have views that can be edited via the layout editor in the admin panel. Examples are video browse page, album browse page and user/member profile page. In our car module we want the browse and view pages to be editable in the layout editor in the admin panel.
    Pages are instances of Engine_Model_Page and are stored in engine4_core_pages and the description this table's fields follows:

    page_id - the table's primary key. This is the page query parameter of the layout editor.

    name - the name of the action that will render this page of the format <module-name>_<controller-name>_<action-name>.

    displayname - the name of the page as it appears in the layout editor.

    url - only applicable to custom pages.

    title - the title of the page. On the title tag this appears as <site title> - <page title>. The title tag can be manipulated via the headTitle view helper.

    description/keywords - appears on the meta description and the meta-tag of the page

    custom - a flag indicating if the page is created via the layout editor.

    layout - the layout that the page will use. Value must be one of the base names of the layout scripts in ../application/modules/Core/layout/scripts. Leaving this empty will use the aptly named layout script default.tpl.

    levels - only applicable to custom pages. Determines the user levels that can view this page.

    provides - what the page provides. Determines whether or not the widgets on this page will render or not. See above.

    view_count - the number of times the page has been viewed.

    For non-custom pages the page structure are created when the module is installed via the callback class. In our car modules manifest.php we have:
    PHP:

        
    'callback' => array(
          
    'path' => 'application/modules/Car/settings/install.php',
          
    'class' => 'Car_Installer',
        ),
    (for a details about the manifest file see this page)

    We define the Car_Installer class in ../application/modules/Car/settings/install.php:
    PHP:

    class Car_Installer extends Engine_Package_Installer_Module
    {
      public function 
    onInstall()
      {
        
    $this->_addUserProfileContent();
        
    $this->_addCarViewPage();
        
    $this->_addCarBrowsePage();
     
        
    parent::onInstall();
      }
     
      protected function 
    _addCarBrowsePage()
      {
        
    $db $this->getDb();
     
        
    // profile page
        
    $page_id $db->select()
          ->
    from('engine4_core_pages''page_id')
          ->
    where('name = ?''car_index_browse')
          ->
    limit(1)
          ->
    query()
          ->
    fetchColumn();
     
        
    // insert if it doesn't exist yet
        
    if( !$page_id ) {
          
    // Insert page
          
    $db->insert('engine4_core_pages', array(
            
    'name' => 'car_index_browse',
            
    'displayname' => 'Car Browse Page',
            
    'title' => 'Car Browse',
            
    'description' => 'This page lists cars.',
            
    'custom' => 0,
          ));
          
    $page_id $db->lastInsertId();
     
          
    // Insert top
          
    $db->insert('engine4_core_content', array(
            
    'type' => 'container',
            
    'name' => 'top',
            
    'page_id' => $page_id,
            
    'order' => 1,
          ));
          
    $top_id $db->lastInsertId();
     
          
    // Insert main
          
    $db->insert('engine4_core_content', array(
            
    'type' => 'container',
            
    'name' => 'main',
            
    'page_id' => $page_id,
            
    'order' => 2,
          ));
          
    $main_id $db->lastInsertId();
     
          
    // Insert top-middle
          
    $db->insert('engine4_core_content', array(
            
    'type' => 'container',
            
    'name' => 'middle',
            
    'page_id' => $page_id,
            
    'parent_content_id' => $top_id,
          ));
          
    $top_middle_id $db->lastInsertId();
     
          
    // Insert main-middle
          
    $db->insert('engine4_core_content', array(
            
    'type' => 'container',
            
    'name' => 'middle',
            
    'page_id' => $page_id,
            
    'parent_content_id' => $main_id,
            
    'order' => 2,
          ));
          
    $main_middle_id $db->lastInsertId();
     
          
    // Insert main-right
          
    $db->insert('engine4_core_content', array(
            
    'type' => 'container',
            
    'name' => 'right',
            
    'page_id' => $page_id,
            
    'parent_content_id' => $main_id,
            
    'order' => 1,
          ));
          
    $main_right_id $db->lastInsertId();
     
          
    // Insert menu
          
    $db->insert('engine4_core_content', array(
            
    'type' => 'widget',
            
    'name' => 'car.browse-menu',
            
    'page_id' => $page_id,
            
    'parent_content_id' => $top_middle_id,
            
    'order' => 1,
          ));
     
          
    // Insert content
          
    $db->insert('engine4_core_content', array(
            
    'type' => 'widget',
            
    'name' => 'core.content',
            
    'page_id' => $page_id,
            
    'parent_content_id' => $main_middle_id,
            
    'order' => 1,
          ));
     
          
    // Insert search
          
    $db->insert('engine4_core_content', array(
            
    'type' => 'widget',
            
    'name' => 'car.browse-search',
            
    'page_id' => $page_id,
            
    'parent_content_id' => $main_right_id,
            
    'order' => 1,
          ));
     
          
    // Insert gutter menu
          
    $db->insert('engine4_core_content', array(
            
    'type' => 'widget',
            
    'name' => 'car.browse-menu-quick',
            
    'page_id' => $page_id,
            
    'parent_content_id' => $main_right_id,
            
    'order' => 2,
          ));
        }
      }
     
     
      protected function 
    _addUserProfileContent()
      {
        
    $db    $this->getDb();
        
    $select = new Zend_Db_Select($db);
     
     
        
    // profile page
        
    $select
          
    ->from('engine4_core_pages')
          ->
    where('name = ?''user_profile_index')
          ->
    limit(1);
        
    $page_id $select->query()->fetchObject()->page_id;
     
     
        
    // car.profile-cars
     
        // Check if it's already been placed
        
    $select = new Zend_Db_Select($db);
        
    $select
          
    ->from('engine4_core_content')
          ->
    where('page_id = ?'$page_id)
          ->
    where('type = ?''widget')
          ->
    where('name = ?''car.profile-cars')
          ;
        
    $info $select->query()->fetch();
     
        if( empty(
    $info) ) {
     
          
    // container_id (will always be there)
          
    $select = new Zend_Db_Select($db);
          
    $select
            
    ->from('engine4_core_content')
            ->
    where('page_id = ?'$page_id)
            ->
    where('type = ?''container')
            ->
    limit(1);
          
    $container_id $select->query()->fetchObject()->content_id;
     
          
    // middle_id (will always be there)
          
    $select = new Zend_Db_Select($db);
          
    $select
            
    ->from('engine4_core_content')
            ->
    where('parent_content_id = ?'$container_id)
            ->
    where('type = ?''container')
            ->
    where('name = ?''middle')
            ->
    limit(1);
          
    $middle_id $select->query()->fetchObject()->content_id;
     
          
    // tab_id (tab container) may not always be there
          
    $select
            
    ->reset('where')
            ->
    where('type = ?''widget')
            ->
    where('name = ?''core.container-tabs')
            ->
    where('page_id = ?'$page_id)
            ->
    limit(1);
          
    $tab_id $select->query()->fetchObject();
          if( 
    $tab_id && @$tab_id->content_id ) {
              
    $tab_id $tab_id->content_id;
          } else {
            
    $tab_id null;
          }
     
          
    // tab on profile
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type'    => 'widget',
            
    'name'    => 'car.profile-cars',
            
    'parent_content_id' => ($tab_id $tab_id $middle_id),
            
    'order'  => 12,
            
    'params'  => '{"title":"Cars","titleCount":true}',
          ));
     
        }
      }
     
      protected function 
    _addCarViewPage()
      {
        
    $db    $this->getDb();
        
    $select = new Zend_Db_Select($db);
     
        
    // Check if it's already been placed
        
    $select = new Zend_Db_Select($db);
        
    $select
          
    ->from('engine4_core_pages')
          ->
    where('name = ?''car_index_view')
          ->
    limit(1);
          ;
        
    $info $select->query()->fetch();
     
        if( empty(
    $info) ) {
          
    $db->insert('engine4_core_pages', array(
            
    'name' => 'car_index_view',
            
    'displayname' => 'Car View Page',
            
    'title' => 'View Car',
            
    'description' => 'This is the view page for a car.',
            
    'custom' => 0,
            
    'provides' => 'subject=car',
          ));
          
    $page_id $db->lastInsertId('engine4_core_pages');
     
          
    // containers
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'container',
            
    'name' => 'main',
            
    'parent_content_id' => null,
            
    'order' => 1,
            
    'params' => '',
          ));
          
    $container_id $db->lastInsertId('engine4_core_content');
     
     
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'container',
            
    'name' => 'left',
            
    'parent_content_id' => $container_id,
            
    'order' => 1,
            
    'params' => '',
          ));
          
    $left_id $db->lastInsertId('engine4_core_content');
     
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'container',
            
    'name' => 'right',
            
    'parent_content_id' => $container_id,
            
    'order' => 1,
            
    'params' => '',
          ));
          
    $right_id $db->lastInsertId('engine4_core_content');
     
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'container',
            
    'name' => 'middle',
            
    'parent_content_id' => $container_id,
            
    'order' => 3,
            
    'params' => '',
          ));
          
    $middle_id $db->lastInsertId('engine4_core_content');
     
          
    // middle column content
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'widget',
            
    'name' => 'core.container-tabs',
            
    'parent_content_id' => $middle_id,
            
    'order' => 1,
            
    'params' => '{"max":6}'
          
    ));
          
    $container_tabs_id $db->lastInsertId('engine4_core_content');
     
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'widget',
            
    'name' => 'activity.feed',
            
    'parent_content_id' => $container_tabs_id,
            
    'order' => 1,
            
    'params' => '{"title":"Activity"}',
          ));
     
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'widget',
            
    'name' => 'car.profile-fields',
            
    'parent_content_id' => $container_tabs_id,
            
    'order' => 2,
            
    'params' => '{"title":"Info"}',
          ));
     
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'widget',
            
    'name' => 'car.profile-albums',
            
    'parent_content_id' => $container_tabs_id,
            
    'order' => 3,
            
    'params' => '{"title":"Albums","titleCount":true}',
          ));
     
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'widget',
            
    'name' => 'car.profile-videos',
            
    'parent_content_id' => $container_tabs_id,
            
    'order' => 4,
            
    'params' => '{"title":"Videos","titleCount":true}',
          ));
     
          
    // right column
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'widget',
            
    'name' => 'car.show-same-tags',
            
    'parent_content_id' => $right_id,
            
    'order' => 1,
            
    'params' => '',
          ));
     
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'widget',
            
    'name' => 'car.show-also-liked',
            
    'parent_content_id' => $right_id,
            
    'order' => 2,
            
    'params' => '',
          ));
     
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'widget',
            
    'name' => 'car.show-same-poster',
            
    'parent_content_id' => $right_id,
            
    'order' => 3,
            
    'params' => '',
          ));
     
          
    // left column
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'widget',
            
    'name' => 'car.profile-photo',
            
    'parent_content_id' => $left_id,
            
    'order' => 1,
            
    'params' => '',
          ));
     
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'widget',
            
    'name' => 'car.profile-ratings',
            
    'parent_content_id' => $left_id,
            
    'order' => 2,
            
    'params' => '',
          ));
     
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'widget',
            
    'name' => 'car.profile-options',
            
    'parent_content_id' => $left_id,
            
    'order' => 3,
            
    'params' => '',
          ));
     
          
    $db->insert('engine4_core_content', array(
            
    'page_id' => $page_id,
            
    'type' => 'widget',
            
    'name' => 'car.profile-info',
            
    'parent_content_id' => $left_id,
            
    'order' => 4,
            
    'params' => '',
          ));
        }
      }
    }
    This class' onInstall method runs when the module is install and construct the pages that we need to be editable in the layout editor.
    Page construction involves checking the page already exists and creating it if it doesn't. The first item that goes in the page is the main container.
    The main containers contain the sub-containers: top, right, bottom, left and middle. Finally sub-containers contain blocks/widgets.

    The location and position of widgets/containers are saved in engine4_core_content. A brief description of the fields on this table follows:

    content_id - the tables primary key. In a widget's controller this value can be retrieved by $this->getElement()->getIdentity(). This value is usually used on ajax paginated widgets like profile.cars
    page_id - the page_id of the page containing the widget/container
    type - widget or container
    name - the full name of the widget (<modulename>.<widget-name>) if type is widget or 'main', 'top', 'right', 'bottom', 'left' and 'middle' if type is container.
    params - not applicable for containers. For widgets this is the widget parameters as set in the layout editor
    parent_content_id - the content_id of the widget/container that contains this widget/container
    order - the other in which the widget/container as appears on its parent widget/container
    attribs - unused

    This completes our discussion on widgets and pages. Stuff that the end user can see.
    --- добавлено: Jul 31, 2013 4:44 PM ---
    Menus


    To help our users find their way around we need menus. Most of the group of links you find in SE4 pages are menus wrapped within a widget.
    To be able to create our own menus or add menu items to existing menus we need to take a look at two tables engine4_core_menus and engine4_core_menuitems

    The contents of engine4_core_menus are the editable menus. That is, menus listed under this table can me manipulated by the admin user in ../admin/menus
    The name field is the prefix used in the name of the menuitems in engine4_core_menuitems. This is how you will refer to menu when you retrieve it for use in your code.
    The type field determines if this menu is a custom one created by an admin using the "Add Menu" function in ../admin/menus. The title field is the title of the menu
    as displayed in the menu editor. This is not visible to end users. The order field determines the order the menu is displayed in the drop down in the menu editor.
    Note inserting the details of your menu in engine4_core_menus only allows admin users to manipulate your menu.

    The more interesting table is engine4_core_menuitems. All the menu items in SE4 standard or otherwise goes here.

    The name field is the name of the menu item. This is how you will refer to the menu that contains the menu item. For example in our car module we want to have
    a main navigation menu for cars which will appear beneath the SE4s default header menu. This menu will have 3 items: "Browse", "My Cars", "Create New Car".
    To do this we need to create 3 menu items car_main_browse, car_main_manage and car_main_create. If we want the admins to be able to manipulate this menu all we
    need to do is to insert a row to engine4_core_menus name car_main.

    The module field determines which module the menu item is enclosed in. Disabling/enabling the enclosing module disable/enable the display of the menu item.

    The label field determines the text in the generated link. HTML tags are allowed here.

    The params field is in JSON format and determines the attributes of the generated link. This set's the parameters for the router that will generate the value
    of the href tag of the link. Routing is discussed in Part 3 of this tutorial. Field/value pairs that is not part of the routing parameters will become attributes
    of the generated link. Adding "target:_blank" for example will make the link open in a new tab/window. Adding the "uri" parameter and leaving out other routing
    parameters (route, controller, etc.). Will make the link point to the specified uri. You may use the "javascript:" scheme to make the link execute arbitrary javascript code when clicked.

    The menu field determines which menu the menu item belongs to while the submenu field determines which submenu it belongs within the menu. Note that css/javascript must
    be provided for the submenu to look and behave properly.

    The enabled field determines if the menu is displayed or not. The custom field determines if the menu item is created via the menu editor in the admin panel.
    This also determines if the item can be deleted from the menu. The order field determines the order of the item as it appears on the menu.

    With what we discussed so far we need the following sql statements in the my.sql file of our Car module for our main menu:
    Code:
    INSERT IGNORE INTO engine4_core_menus (`name`, `type`, `title`, `order`) VALUES ('car_main', 'standard', 'Car Main Navigation Menu', 999);
    INSERT IGNORE INTO engine4_core_menuitems (`name`, `module`, `label`, `plugin`, `params`, `menu`, `submenu`, `enabled`, `custom`, `order`) VALUES
    ('car_main_browse', 'car', 'Browse Cars', 'Car_Plugin_Menus::canViewCars', '{"route":"car_general","action":"browse"}', 'car_main', '', 1, 0, 1),
    ('car_main_manage', 'car', 'My Cars', 'Car_Plugin_Menus::canCreateCars', '{"route":"car_general","action":"manage"}', 'car_main', '', 1, 0, 2),
    ('car_main_create', 'car', 'Create Car', 'Car_Plugin_Menus::canCreateCars', '{"route":"car_general","action":"create"}', 'car_main', '', 1, 0, 3);
    
    Now we can discuss the plugin field. The field comes in two formats: <ClassName> and <ClassName>::<Method>. Using the latter we can control whether the menu item is
    displayed or not my making the method return true or false depending on the situation. For example:
    PHP:

    class Car_Plugin_Menus
    {
      public function 
    canViewCars()
      {
        
    // Check auth
        
    return (bool) Engine_Api::_()->authorization()->isAllowed('car'$viewer'view');
      }
     
    }
    Will make the browse link to display/hide depending on whether the currenly log in user has authorization to view cars (discussed in part 4 or this tutorial).

    Specifying only the the class name of the plugin allows for more flexibility. This will allow you not only to hide/show the menu item but also set the href,
    icon, style and other attributes of the generated link. A great example of this is the Messages_Plugin_Menus which has a method to add the number of unread messages on the menu item label.

    Now that we have our menu we need to display it. This is the easy part to retrieve the menu from a controller:
    PHP:

    // Get navigation
    <?php
    $this
    ->view->navigation Engine_Api::_()->getApi('menus''core')
            ->
    getNavigation('[menu_name]', [array of options], '[active_menu_item_name]');
    And in the view script:
    PHP:

    <?php
    $this
    ->navigation() ->menu() ->setContainer($this->navigation) ->render(); ?>
    As discussed earlier most of the menus that appear in the front end of SE4 are wrapped in widgets. In our case we might want to put our menu in a widget
    which the user can move around in the layout editor. To enable that we would need to create widgets and pages.
    Mexis likes this.
  2. SPQR Super-Moderator


    Offline
    • Super Moderator
    Message Count:
    282
    Likes Received:
    215
    Permissions and privacy:


    The permissions/privacy mechanism of SE4 is provided by the core module Authorization. The stuff that makes it all go are two tables, engine4_authorization_permissions
    and engine4_authorization_allow. The permissions table has information on which/who can do so and so in a general context. The allow table has information on which
    can do so and so on a particular resource/SE4 item. The admin permission settings that you set on admin the admin page generally goes to the permissions table and the
    privacy settings that end users set on various items they create or own on the site goes to the allow table.

    Permissions are set typically when the module is installed. They are in the permissions section of .. /application/modules/<Modulename>/settings/my.sql. I wont be discussing
    how the whole mechanism works since it will take a whole other post. Lets tackle it with an example. On the Video_IndexController is this snippet:
    PHP:

    if( !$this->_helper->requireAuth()->setAuthParams('video'null'view')->isValid()) return;
    This just checks if the current viewer (2nd parameter, null means the current viewer) can view (3rd parameter) videos (video items, 1st parameter). If not the request is
    forwarded to the "not permitted" page. Whether the isValid method returns a true or false is based on the content of the engine4_authorization_permissions db table.
    You may refer the my.sql file of each module to see how it works.

    The defaults on my.sql are straight forward. Most of the time you can just copy/paste and search/replace the parameters for it to fit your needs.

    The permissions section of my.sql sets the default permissions. To enable a administrator to edit these settings. An admin page for it must be created. In the case of
    the video module this is handled by the level action of the Video_AdminSettingsController.

    Allows are set during item creation and can be changed when the item settings are edited. These are the "privacy settings" on the creation form. In the video module
    these are in the Video_IndexController, in createAction and marked "AUTH STUFF".

    Allows determines who can do something on an item. These must be set on the created item otherwise the only one who will be able to do something (view, edit, comment, share, delete, etc.)
    on the item will be the owner of the item or super admins.

    Much the same way as permissions. You may copy/paste and search/replace the "AUTH STUFF" snippets of most modules so you can use them on the modules you are creating.

    There is a quick shortcut that can be used when dealing privacy/allows. Let say for example in our Car module we want the car's privacy setting to be the same as the owner's
    privacy setting. The owner of cars are users. Users' privacy settings are under members/settings/privacy. For that to be the case. We can define the getAuthorizationItem method in our car model. Like so:
    PHP:

    public function getAuthorizationItem()
    {
      return 
    $this->getOwner();
    }
    With this in place we can remove the privacy fields in our creation form. Authorization can be utilized to determine when/how and to whom menus and menu items are displayed.
    Mexis likes this.
  3. nadri Thread starter Administrator


    Offline
    • Admin
    • Знаменитый
    Message Count:
    1,257
    Likes Received:
    392
    My version of SE:
    4.6.0
    Routes

    If our cars are going to make rounds in our community pages must be created where users can browse, view, create and edit cars. For this we must define routes, controllers and views.

    Social Engine's routing framework uses Zend Framework's routing component. It's advisable to review the doc if you are not familiar with it. Routes are defined in the manifest under the key "routes".

    SE4 defines two default routes a route named "default" and a route named "admin_default". The default route is the same as the default route of ZF.
    The default route is /:module/:controller/:action/*. The * means any succeeding values are key/value parameters pairs. This means for example
    that /core/index/index/foo/bar/ will dispatch the index action of the index controller in the core module with the parameter "foo" set to "bar".

    The admin_default route behaves the same way as the default route but url begins with "/admin". In addition, the dispatched controller is prefixed with "Admin".
    For example /admin/foo/bar/baz/ will dispatch the bar action of the AdminBar controller of the foo module. So to figure out which files are rendering a particular
    admin page all you need to do is look at the url. For example, to figure out the files that is generating the page /admin/user/manage we just need to observe the url itself.
    From the url it involves the user module, the manage controller of the user module and the index action of that controller. So the controller class is User_AdminManageController
    in the file ../application/modules/User/AdminManageController.php and the view script is ../application/modules/User/views/scripts/admin-manage/index.tpl

    Note that any admin controller can also be dispatched using the default route. The index action, of the admin manage controller of the use module can be dispatched with /user/admin-manage/index

    Routes have a name and that name is used with in SE4 in a number of ways. There is a url view helper and a url action helper that constructs urls. For both the url view helper
    and the url action helper you may set the name of a named route to indicate the you intend to use the named route instead of the default route. The assemble method of the router
    also uses the name of the route the same way. You may use the route name to indicate the route your are constructing. SE4 also defines the htmlLink view helper. The htmlLink view
    helper is the reason why you will rarely use the url view and action helper. The first parameter is either an array or a string. If it's a string it will be the value of the href
    attribute of the anchor element to be constructed. If it's an array it is used as routing parameters. To indicate the route you want to use simply add a route key.
    For example $this->htmlLink(array('route' => 'group_profile', 'id' => 1), 'a group') will generate <a href="/group/1">a group</a>.

    Let's create some routes.
    PHP:

    <?php return array (
    'package' =>
    array (
    'type' => 'module',
    'name' => 'car',
    'version' => '4.0.0',
    'path' => 'application/modules/Car',
    'title' => 'Car',
    'description' => 'Car',
    'author' => 'http://social-engine-tutorials.blogspot.com/',
    'callback' =>
    array (
    'class' => 'Engine_Package_Installer_Module',
    ),
    'actions' =>
    array (
    => 'install',
    => 'upgrade',
    => 'refresh',
    => 'enable',
    => 'disable',
    ),
    'directories' =>
    array (
    => 'application/modules/Car',
    ),
    'files' =>
    array (
    => 'application/languages/en/car.csv',
    ),
    ),
    // Items ---------------------------------------------------------------------
    'items' => array(
    'car_car'
    ),
    // Routes --------------------------------------------------------------------
    'routes' => array(
    'car_extended' => array(
    'route' => 'car/:controller/:action/*',
    'defaults' => array(
    'module' => 'car',
    'controller' => 'index',
    'action' => 'index',
    ),
    'reqs' => array(
    'controller' => '\D+',
    'action' => '\D+',
    )
    ),
    'car_general' => array(
    'route' => 'cars/:action/*',
    'defaults' => array(
    'module' => 'car',
    'controller' => 'index',
    'action' => 'browse',
    ),
    'reqs' => array(
    'action' => '(browse|create|list|manage)',
    )
    ),
    'car_specific' => array(
    'route' => 'cars/:action/:car_id/*',
    'defaults' => array(
    'module' => 'car',
    'controller' => 'car',
    'action' => 'index',
    ),
    'reqs' => array(
    'action' => '(edit|delete)',
    'car_id' => '\d+',
    )
    ),
    'car_profile' => array(
    'route' => 'car/:id/*',
    'defaults' => array(
    'module' => 'car',
    'controller' => 'profile',
    'action' => 'index',
    ),
    'reqs' => array(
    'id' => '\d+',
    )
    ),
    )
    ); 
    ?>
    Each key in the routes array serves as the route's name. We created routes that cater to specific purposes. The route car_general caters to all cars in general.
    This route is used when users browse available cars, when a user list and manage the cars he own and to create new cars. The route car_specific is used for actions to be done
    on individual cars. The car_profile route is used to serve a car's profile page. This is to be used mainly in a car's getHref method (discussed later). The car_extended route
    is used for action such as adding photo's or video's of cars.

    The route key of the manifest array is used as a parameter to the addConfig method of the router. Again, it is best to consult the zend framework documentation if you are not familiar with it.

    From the routes we defined it is necessary for us to create the controllers index, car, and profile and the view scripts for their actions.

    PHP:

    <?php
    // car controller
     
    class Car_CarController extends Core_Controller_Action_Standard
    {
    public function 
    init()
    {
    }
     
    public function 
    editAction()
    {
    }
     
    public function 
    deleteAction()
    {
    }
     
    }
    PHP:

    <?php
    // index controller
     
    class Car_IndexController extends Core_Controller_Action_Standard
    {
    public function 
    init()
    {
    }
     
    public function 
    indexAction()
    {
     
    }
     
    public function 
    createAction()
    {
    }
     
    public function 
    browseAction()
    {
    }
     
    public function 
    manageAction()
    {
    }
     
    }
    PHP:

    <?php
    // profile controller
     
    class Car_ProfileController extends Core_Controller_Action_Standard
    {
    public function 
    init()
    {
    }
     
    public function 
    indexAction()
    {
    }
     
    }
    Public facing controllers (like the ones we created) extends Core_Controller_Action_Standard. Public facing controllers that requires a logged in user extends
    Core_Controller_Action_User. Admin controllers extends Core_Controller_Action_Admin.

    For each controllers we need to create directory under ../application/modules/Car/views/scripts named after the name of the controller. So we need views/scripts/profile and views/scripts/car.
    The one for index is automatically generated by the SDK. For each action in each controller create the appropriate view script file. You may leave them empty for now.

    With these in place. You should now be able to navigate to the routes we defined in the manifest. Try navigating to /cars/browse. You should see a page with SE4's header and footer.

    Common problems include not found responses. If this happens. Check the name of the controller that is supposed to be dispatched. It should follow the pattern <ModuleName>_<ControllerName>Controller.
    Also check the file name of the controller it should be ../application/modules/<ModuleName>/controllers/<ControllerName>Controller.php. Missing view scripts causes exceptions and should be easy to fix.[/PHP]
    Mexis likes this.
  4. SPQR Super-Moderator


    Offline
    • Super Moderator
    Message Count:
    282
    Likes Received:
    215
    Model, Item, Db Table

    The stuff that makes Social Engine 4 go are items. Items are the things that go around the community. Photos, albums, groups, videos even users are items in the context of SE4.
    Items normally have title, description, body or content, slug, owners, parents etc. Items can be shared, commented on and liked.

    In our module we have cars. Cars are owned by users. They have a profile page, a profile photo, can be liked, shared, commented on, can have a number of photos and can have videos.

    Please note the following regarding items in SE4:

    - Items have type which is the item's name prefixed with the enclosing module's name and an underscore. In our case it's car_car. The prefixed can be dropped buy adjustments must be made elsewhere.
    - Item name/type is used as database table names. It follows than you can only use characters that are allowed in mysql table names. I don't have an exhaustive list but to be safe use
    only alphanumeric characters and underscores.
    - Items have a globally unique identifier which formed by the item's type an underscore and the primary key of the item. For example car_1
    - Items' class extends Core_Model_Item_Abstract
    - Items class declaration resides in ../application/modules/<ModuleName>/Model/<ItemName>.php

    The class name is prefixed with <ModuleName>_Model_
    Item's can be retrieved anywhere using Engine_Api::_()->getItem('<item_type>', '<item_id>') or Engine_Api::_()->getItemByGuid('<item_guid>'). In view scripts items can be retrieved
    using the item view helper $this->item('<item_type>', '<item_id>'). To be able to use these methods the item must be declared in the module's manifest. This is done by adding an 'items' key in the manifest. In our case:
    PHP:

        <?php return array (
        
    'package' =>
        array (
        
    'type' => 'module',
        
    'name' => 'car',
        
    'version' => '4.0.0',
        
    'path' => 'application/modules/Car',
        
    'title' => 'Car',
        
    'description' => 'Car',
        
    'author' => 'http://social-engine-tutorials.blogspot.com/',
        
    'callback' =>
        array (
        
    'class' => 'Engine_Package_Installer_Module',
        ),
        
    'actions' =>
        array (
        
    => 'install',
        
    => 'upgrade',
        
    => 'refresh',
        
    => 'enable',
        
    => 'disable',
        ),
        
    'directories' =>
        array (
        
    => 'application/modules/Car',
        ),
        
    'files' =>
        array (
        
    => 'application/languages/en/car.csv',
        ),
        ),
        
    'items' => array(
        
    'car_car'
        
    ),
        ); 
    ?>

    In addition the module's core api must be defined. Create the directory ../application/modules/Car/Api and create the file Api/Core.php

    PHP:

        <?php
       
        
    class Car_Api_Core extends Core_Api_Abstract
        
    {
        }
      
    - Another way to retrieve an item is via the items table discussed below.


    Create the file ../application/modules/Car/Model/Car.php

    PHP:

    <?php
     
    class Car_Model_Car extends Core_Model_Item_Abstract
    {
    }
     
    and the file ../application/modules/Car/Model/DbTable/Cars.php

    PHP:

    <?php
     
    // Cars/Model/DbTable/Cars.php
     
    class Car_Model_DbTable_Cars extends Engine_Db_Table
    {
    protected 
    $_rowClass 'Car_Model_Car';
    }
     
    And in ../application/modules/Car/settings/my.sql



    -- Cars/settings/my.sql
    PHP:

    DROP TABLE 
    IF EXISTS engine4_car_cars;
    CREATE TABLE engine4_car_cars (
    car_id INT(11UNSIGNED NOT NULL AUTO_INCREMENT,
    title VARCHAR(128NOT NULL,
    description MEDIUMTEXT NOT NULL,
    owner_id INT(11UNSIGNED NOT NULL,
    owner_type INT(11UNSIGNED NOT NULL,
    photo_id INT(11UNSIGNED NOT NULL,
    creation_date DATETIME NOT NULL,
    modified_date DATETIME NOT NULL,
    view_count INT(11UNSIGNED NOT NULL,
    comment_count INT(11UNSIGNED NOT NULL,
    search TINYINT(1NOT NULL,
    PRIMARY KEY (car_id),
    INDEX (photo_id),
    INDEX (owner_id)
    );
    Run the query in mysql as well.

    Please note the following:

    - The table class name is the item name prefixed with Engine_Model_DbTable_ and suffixed with an "s".
    - The corresponding mysql table name is the item type prefixed with engine4_ and suffixed with an "s".
    - As mentioned above the item type prefix can be dropped. In our case if we choose not to use the default item type "car_car" an use "car" instead, then the item type we declare in the manifest is "car".
    - In the item's table we set $_name to "cars" and our the mysql table name will be engine4_cars
    - To get an instance of the db table we use Engine_Api::_()->getDbTable('<item_name_s_suffixed>', '<module_name>'). Another way is via Engine_Api::_()->getItemTable('<item_type>'). This only works
    if the item is declared in the manifest and the modules core api is declared. To get an item via it's id we use Engine_Api::_()->getDbTable(...)->find(<id>)->current
    Engine_Db_Table extends Zend_Db_Table you may refer to the Zend Frame documentation to see what you have at your disposal.

    Lets go back to the my.sql file. Queries in this file are run when the module is installed. Since this file doesn't exist when we install our module at the start development we had to run this manually.
    Any errors encountered the execution of the statements in this file will cause the whole installation process to fail.

    Since as mentioned earlier, cars have owners, we have owner_id, owner_type in the table definition. With this we can query for the owner of a car using the getOwner method. By default the owner type
    is user so getOwner will return an instance of User_Model_User. In the future we may decide that other items can own cars. So for example we may decide that groups can own cars. A car owned by a group
    will have an owner_type of 'group_group' and calling getOwner in that instance of car will return an instance of Group_Model_Group

    Items can also have parents. The table definition must have a parent_type and parent_id which work just like owner_type and owner_id. getParent() and getChildren will be at your disposal if you plan
    on using parents/children on items. In addition you may set the property protected property $_parent_is_owner to make the getParent() return the owner of the item.

    Cars have a primary photo. That is why on the table definition we have photo_id. When to photo_id is set. We can use the getPhotoUrl on the item to get the url of the photo file. It accepts an optional
    parameter that controls the size of the image whose url will be returned. In view scripts the itemPhoto view helper can be used to generate an image tag for an item's photo.

    Items can be liked, commented on and tagged. To enable any or all to this you just need to add the interface in the item's model class:

    PHP:

    public function comments()
    {
    return new 
    Engine_ProxyObject($thisEngine_Api::_()->getDbtable('comments''core'));
    }
     
    public function 
    likes()
    {
    return new 
    Engine_ProxyObject($thisEngine_Api::_()->getDbtable('likes''core'));
    }
     
    public function 
    tags()
    {
    return new 
    Engine_ProxyObject($thisEngine_Api::_()->getDbtable('tags''core'));
    }
     
    That covers everything about items, models, and db tables.
    Mexis likes this.
  5. nadri Thread starter Administrator


    Offline
    • Admin
    • Знаменитый
    Message Count:
    1,257
    Likes Received:
    392
    My version of SE:
    4.6.0
    Setting Up Tasks or Cron Jobs in Social Engine


    To set up a periodically running task in social engine one we must create tasks. Periodically running tasks are usually accomplished by setting up a cron job.
    But in social engine we piggy back in the task scheduler which can be set up to trigger in a number of ways. Cron being one of them.

    The first step is to add a row for our task in the table engine4_core_tasks. The only fields you need to are:

    title - this is the text that appears under "Name" in the task scheduler page
    module - which module the task belongs. Disabling the module also disables the task
    plugin - the class that defines the action to run. Named <ModuleName>_Plugin_Task_<TaskName>
    timeout - the number of seconds between task execution. Setting this to 60 means your task will execute every minute.

    You may add this query in the settings/my.sql or settings my.upgrade-x.x.x-x.x.x.sql in case of upgrades.

    Next we need to define the plugin class. This is located in ../application/modules/<ModuleName>/Plugin/Task/<TaskName>. We just need to extends Core_Plugin_Task_Abstract and
    define the public method execute. Within this method we write the statements the we need to run when the task is executed.

    There are a lot of useful information in the tasks log. I suggest you monitor this log as during testing and debugging. In addition you may add debugging information
    in this log with $this->getLog()->debug('<debug info>').
    Mexis likes this.
  6. SPQR Super-Moderator


    Offline
    • Super Moderator
    Message Count:
    282
    Likes Received:
    215
    Social Engine Package Manifest File Explained




    Introduction
    When creating a social engine package via the SDK, one of the files created is the manifest file which contains a number of meta data and not much else.
    It is up to the developer to figure out which element goes which. This post is an attempt to help others figure out what the parts of the manifest file, how they work and when they should be used.

    Initial Content
    The package manifest file initially contains an array definition. This is an associative array containing a single element and this element has "package" as key.
    The keys "type", "name", "version", "path", "title", "description", and "author" are self-descriptive and most of them are the values you entered when creating the package via the SDK.

    Note that version is used to determine if the package upgrades or downgrades a package when it is installed in case the package is already installed before hand.
    In case of upgrades SE also calculates whether it needs to run upgrade sql scripts. Lets consider a module named Foo version 4.0.0. During installation the queries in the files my.sql and my-install.sql will be executed. If the version of the package is incremented to 4.0.1, you may add the file my-upgrade-4.0.0-4.0.1.sql. When the package is rebuilt and installed so that it upgrades a previous 4.0.0 install the sql statements in the upgrade sql script will be executed.

    For modules, the name of the package is the name space for the module. That is if the name of the package is "Foo" then the module will reside in applications/Foo and every
    resources therein will be prefixed with "Foo_". Naming a module with hypens like foo-bar will result in a name space such as "FooBar" and a prefix of "FooBar_".

    The "directories" key are the paths/directories defined by the package. Initially it contains only the package's path. Additional paths can be added and defined here (I have not tested all possibilities and limitations of this function).

    The "files" key are the files contained in the package that is outside the packages path. A good example is the language file that is initially created for modules. You may add additional files outside the package path and add a key on this this files array and that file will be included in the package.

    Additional Package Meta
    There are additional keys in the package array that are not defined initially. They are "revision", "changeLog", "callback", "dependencies" and "tests".

    The key "callback" defines, no surprise here, the callback class. This call back is used to run custom queries to build pages or altering tables as needed. The class is expected to be in settings/install.php. This class must extend Engine_Package_Installer_Module for modules and Engine_Package_Installer_Themes for themes.

    During installation the statements defined in the methods onPreinstall and onInstall of the callback class will be run during the appropriate step of the install. Note that these method are only called during installation and not during upgrades. This methods are useful for queries that you need to run but are otherwise impossible to do so without the use of stored procedures. Like building the pages for your module.

    Composers
    Composers are the scripts that allows end users to attach items on posts. For example, the core module defines a link composer. That is where the "Add Link" link on the activity feed widget is defined. The subject merits a whole other blog post.

    Hooks
    The hooks array contains two element arrays that defines engine hooks. The "event" key is the event you want to hook on to and the "resource" key is the class that contains the handler. Here are a list of events that can be hooked on to out of the box and here is an example of a hook. You may create additional events via the Engine_Hooks_Dispatcher.

    Items
    The items array contains the array contains the items defined in the module. Items are at the core of socialengine both literally and theoretically. It is something than can be viewed, owned, posted on, commented on, attached, deleted moderated etc. It is yet another subject of a blog post. For now it's enough for us to know that defining an item in the manifest also requires us to define a model and a db table for the item to be of use.

    Items extends Core_Model_Item_Abstract. By default an item's name is the concatenation of the containing module's name an underscore and the name of the item itself. You may change this by changing the value of the $_name property of the model.

    Routes
    Routes should be familiar to experienced Zend Framework users or users of other MVC frameworks. It's a subject of another blog post. For now let me refer you to how routing works in ZF which is the framework SE is build upon.
    Mexis likes this.
  7. nadri Thread starter Administrator


    Offline
    • Admin
    • Знаменитый
    Message Count:
    1,257
    Likes Received:
    392
    My version of SE:
    4.6.0
    Complete List of Social Engine 4 Hooks


    Here is a complete list of Social Engine 4 hooks and where they are called / triggered. Most of the names are self explanatory but you may need to examine
    when / how the event is called / triggered and what payload it contains. Here is an example of a socialengine event hook.

    onAuthorizationLevelDeleteBefore
    Never called in a default socialengine install. The name itself is self-descriptive. There is a hook defined in the Payment module's manifest and a handler in Payment/Plugin/Core.php.

    onFieldsValuesSave
    application/modules/Fields/Form/Standard.php

    onItemDeleteBefore / onItemDeleteAfter
    application/modules/Core/Model/Item/Abstract.php

    onRenderLayoutAdmin
    application/modules/Core/layouts/scripts/admin.tpl
    application/modules/Core/layouts/scripts/admin-simple.tpl

    onRenderLayoutDefault
    application/modules/Core/layouts/scripts/default-simple.tpl
    application/modules/Core/layouts/scripts/default.tpl

    onRenderLayoutAdminDefaultSimple
    never called

    onRenderLayoutDefault
    application/modules/Mobi/layouts/scripts/default-simple.tpl
    application/modules/Core/layouts/scripts/default-simple.tpl
    application/modules/Core/layouts/scripts/default.tpl

    onRenderLayoutDefaultSimple
    application/modules/Core/layouts/scripts/default-simple.tpl
    application/modules/Mobi/layouts/scripts/default-simple.tpl

    onRenderLayoutMobileDefault
    application/modules/Mobi/layouts/scripts/default.tpl

    onRenderLayoutMobileDefaultSimple
    Never called in a default socialengine install. A hook is defined in the Core module's manifest.

    onStatistics
    application/modules/Core/widgets/statistics/Controller.php

    onUserCreateBefore
    application/modules/Invite/Plugin/Signup.php
    application/modules/Network/Plugin/User.php
    application/modules/Payment/Plugin/Signup/Subscription.php
    application/modules/User/Plugin/Signup/Fields.php
    application/modules/User/Plugin/Signup/Invite.php
    application/modules/User/Plugin/Signup/Photo.php

    onFieldMetaCreate
    application/modules/Fields/Model/DbTable/Meta.php

    onFieldMetaCreate_*
    application/modules/Fields/Model/DbTable/Meta.php

    onPaymentIpn_*
    application/modules/Payment/Plugin/Gateway/Paypal.php
    application/modules/Payment/Plugin/Gateway/2Checkout.php

    onUserEnable
    application/modules/User/controllers/SignupController.php
    application/modules/User/controllers/AdminManageController.php

    onUserSignupAfter
    application/modules/User/controllers/SignupController.php

    onUserLoginBefore
    application/modules/User/controllers/AuthController.php

    onUserLoginAfter
    application/modules/User/controllers/AuthController.php

    onUserLogoutBefore
    application/modules/User/controllers/AuthController.php

    onUserLogoutAfter
    application/modules/User/controllers/AuthController.php

    onUserProfilePhotoUpload
    application/modules/User/controller/Edit/Controller.php

    onItemCreateBefore / onItemCreateAfter / onItemUpdateBefore / onItemUpdateAfter / onItemDeleteBefore / onItemDelete/After
    application/Modules/Core/Model/Item/Abstract.php

    on<item name>CreateBefore / on<item name>CreateAfter / on<item name>UpdateBefore / on<item name>UpdateAfter / on<item name>DeleteBefore / on<item name>DeleteAfter
    application/Modules/Core/Model/Item/Abstract.php

    onAdminStatistics
    application/modules/Core/widgets/admin-statistics/Controller.php

    getAdminNotifications
    application/modules/Core/widgets/admin-dashboard/Controller.php

    onGenerateCrossDomain
    application/modules/Core/controllers/UtilityController.php

    addActivity
    application/modules/Activity/Model/DbTable/Actions.php

    getActivity
    application/modules/Activity/Model/DbTable/Actions.php
    Mexis likes this.
  8. SPQR Super-Moderator


    Offline
    • Super Moderator
    Message Count:
    282
    Likes Received:
    215
    Social Engine 4 Hook Example


    Introduction

    Following up on my previous post about SE4 hooks. On this post we create a simple hook. For example we want to create a reward system.
    We reward a user a point per log-in per day i.e. a user gets a point if he logs in in a particular day but the point is only rewarded once per day.
    We encapsulate this feature in a theoretical plugin called "point-module."

    Letting SE4 Know

    We need to let SE4 know that we plan on hooking to an event. In particular the event when the user successfully logged in. SE4 has an aptly named event "onUserLoginBefore" which
    is triggered when the user successfully logs in but before the lastlogin_date field is updated. On our module's manifest--the file ../application/modules/<module_name>/settings/manifest.php we put
    PHP:

    <?php
    array (
        
    'type' => 'module',
        
    'name' => 'point',
        
    'version' => '4.0.0',
        
    'path' => 'application/modules/Point',
        
    'title' => 'Points',
        
    'description' => 'Points',
        
    'author' => 'Marco Enrico',
        
    'callback' =>
        array (
          
    'class' => 'Engine_Package_Installer_Module',
        ),
        
    'actions' =>
        array (
          
    => 'install',
          
    => 'upgrade',
          
    => 'refresh',
          
    => 'enable',
          
    => 'disable',
        ),
        
    'directories' =>
        array (
          
    => 'application/modules/Point',
        ),
        
    'files' =>
        array (
          
    => 'application/languages/en/point.csv',
        ),
      ),
      
    // Hooks ---------------------------------------------------------------------
      
    'hooks' => array(
        array(
          
    'event' => 'onUserLoginBefore',
          
    'resource' => 'Point_Plugin_Core'
        
    )
      )
    );
    This lets SE4 know that we plan on handling the "onUserLoginBefore" event with an instance of the class Point_Plugin_Core.

    The Handler

    Next we create the handler for the event. In ../application/modules/Point/Plugin/Core.php:

    PHP:

    <?php
     
     
    class Point_Plugin_Core extends Core_Plugin_Abstract
    {
      public function 
    onUserLoginBefore($event)
      {
        
    $user $event->getPayload();
     
        if (
    date('d'strtotime($user->lastlogin_date)) != date('d')) {
          
    // user's first time to login today
          // add points...
          // ...
          
    $this->addPoints(1);
        }
      }
     
      public function 
    addPoints($points$user null)
      {
        
    //...
       
      
    }
     
    }
    Summary

    This example illustrates that it's pretty simple to implement a social engine hook. All you really have to know is which hook best serves the task you're trying
    to accomplish. "What if none of the hooks apply to a customization I'm trying to accomplish?", you ask.
    Mexis likes this.
  9. nadri Thread starter Administrator


    Offline
    • Admin
    • Знаменитый
    Message Count:
    1,257
    Likes Received:
    392
    My version of SE:
    4.6.0
    SE4 Forms


    I initially intended to make a post about tasks and jobs as part 7 of this series. After working on a number of projects in the months that passed I realized that I forgot to discuss the most common task in SE4 (it's called SE PHP now but I'll still refer to it as SE4 if you don't mind) customization which is customizing forms and creating forms.

    In SE4 there are "standard forms" and "custom fields form". Take for example when in the sign up process, assuming the default order that comes with SE4 out of the box, you are first greeted with a form entitled "Create Account". This form is an instance of User_Form_Signup_Account which is an instance of Engine_Form. This is the most most common form you'll encounter in SE4 and the subject of this post. The next form in the sign up process asks for your profile details. This is an instance of User_Form_Signup_Fields. Among other things, the elements of this type of form can be customized via the admin panel. These types of form extends Fields_Form_Standard. Most 3rd party modules have this type of form to provide a way for site admins to customize the information to be asked from users pertaining to a particular item. This is a complex and interesting subject matter which is best discused in another post.

    Creating Forms

    In a module, scripts providing class definitions for forms are located in ../application/modules/<Modulename>/Form. In our Car module example we need a creation form. So we create the file ../application/modules/Car/Form/Create.php and it it we put:
    PHP:

        <?php  
        
    class Car_Form_Create extends Engine_Form
        
    {
          public function 
    init()
          {
          }
        }
    The Engine_Form class supports setDescription and setTitle to set the description and title of the form respectively. You can consult the documentation for Zend_Form for a complete list of supported methods.

    In the body of init() is where we define the elements of the form. The easiest way to add an element is via this snippet:
    PHP:

        $this
    ->addElement('<elementtype>''<name>', array(/* options */));
    Out of the box SE4 (4.5.0) supports the following types:

    To specify an element type you may use lower case letters but be careful with multi word names. For example, "multiselect" works as well as "Multiselect" but not "multiSelect". In addition, "TinyMce" and "tinyMce" works but not "tinymce".

    The name of the element is required. The name will be the id and name attribute of the rendred form element on the page. These can be overridden by using the element's setAttrib method.

    By extending Engine_Form form classes gets the default decorators and thus the same stying as default SE4 forms.

    The options parameter sets the attributes of the element or it's options. HTML attributes may be specified as key/value pairs in the options parameter array as in the following example:
    PHP:

        <?php
       
        
    class Car_Form_Create extends Engine_Form
        
    {
          public function 
    init()
          {
            
    $this->setTitle('Post a Car');
            
    $this->setDescription('Please set the details of your car post below.');
            
    $this->addElement('text''title', array(
              
    // The label of the element rendered as a label tag wrapped
              // in div with id <elementname>-label
              
    'label' => 'Title',
              
    // Descriptions are rendered above the form element by default.
              
    'description' => '<em>Ex. "My First Car"</em>',
              
    'required' => true,
              
    'filters' => array(
                new 
    Engine_Filter_Censor(),
                
    'StripTags'
              
    )
            ));
            
    $this->title->getDecorator('description')->setEscape(false);
       
            
    $this->addElement('textarea''description', array(
              
    'label' => 'Description',
              
    'filters' => array(
                new 
    Engine_Filter_Censor(),
                
    'StripTags'
              
    )
            ));
       
            
    $this->addElement('text''tags', array(
              
    'label' => 'Tags',
              
    'autocomplete' => 'off',
              
    'filters' => array(
                new 
    Engine_Filter_Censor(),
              )
            ));
       
            
    $this->addElement(new Car_Form_Element_Color('color', array(
              
    'label' => 'Color',
              
    'cellWidth' => 12,
              
    'cellHeight' => 16,
              
    'validators' => array(
                array(
    'Regex'true, array('pattern' => '/^#[a-f0-9]{6}$/i',
                
    'messages' => 'Invalid color.'))
              )
            )));
       
            
    // Another way of doing the code above by adding a prefix path.
            // This is convinient should you be using a lot of custom form elements
            //$this->addPrefixPath('Car_Form_Element', APPLICATION_PATH .
            //'/application/modules/Car/Form/Element', 'element');
            //$this->addElement('color', 'color', array(
            //  'label' => 'Color',
            //  'validators' => array(
            //    array('Regex', true, array('pattern' => '/^#[a-f0-9]{6}$/i',
            //    'messages' => 'Invalid color.'))
            //  )
            //));
       
            // Using a regular
            
    $this->addElement('button''submit', array(
              
    'type' => 'reset',
              
    'decorators' => array('ViewHelper'),
              
    'label' => 'Create',
              
    'ignore' => true
            
    ));
       
            
    $this->addElement('cancel''cancel', array(
              
    'link' => true,
              
    'decorators' => array('ViewHelper'),
              
    'label' => 'Cancel',
              
    'prependText' => ' or '
            
    ));
       
            
    $this->addDisplayGroup(array('submit''cancel'), 'buttons');
          }
        }
    The most commonly used options are label, description, and required. Label is the label of the element. Any html code in the value of label are escaped. Unlike label, description can render html code. This is done by setting escaping to false as shown in the example above.

    For select, Multiselect, MultiCheckbox, and radio the option multiOptions is used to set the options of the element. This can be an numerically indexed array or an associative array. The keys/indexes will be used as the value of the option elements and the values will be used as the label of the option elements. These elements includes the InArray validator by default. If you dynamically load the options via javascript this can be problematic. To disable it use the option registerInArrayValidator to false.

    For button, cancel, reset, cancel and submit the label is used as the label of the button and the label decorator is disabled. For buttons to be grouped and styled properly a display group is used in addition to overriding the default decorators. Please note that although reset and submit are available they are instances of Zend_Form_Element_Submit and Zend_Form_Element_Reset respectively and thus are not styled properly. You may use button for this and set type to "submit" or "reset" to emulate the function. The cancel element is a custom SE4 form element is used very often. By default it is a button which has an onclick attribute which sends the browser back to the previous page. Setting "link" to true will render an anchor tag. As an anchor you may use the "href" option to send the browser to the specified location. The "prependText" is the text prepended to the element (often " or ").

    The "decorator", "validators" and "filters" options are discussed in the zend manual.

    Creating Custom Elements

    In the example above we added the custom element color. The example also shows two ways to specify elements to be added to the form. Here is the class declaration of our color element located in ../application/modules/Car/Form/Element/Color.php:
    PHP:
      <?php
       
        
    class Car_Form_Element_Color extends Engine_Form_Element_Text
        
    {
          public 
    $helper 'formColor';
        }
    The view helper FormColor does the most of the magic here. Here is the code located in ../application/modules/Car/View/Helper/FormColor.php :
    PHP:
      <?php  
        
    class Car_View_Helper_FormColor extends Zend_View_Helper_FormElement
        
    {
       
          public function 
    formColor($name$value null$attribs null,
              
    $options null$listsep " \n")
          {
            
    $info $this->_getInfo($name$value$attribs$options$listsep);
            
    extract($info); // name, value, attribs, options, listsep, disable
            
    $cellWidth = empty($attribs['cellWidth']) ? $attribs['cellWidth'];
            unset(
    $attribs['cellWidth']);
            
    $cellHeight = empty($attribs['cellHeight']) ? 12 $attribs['cellHeight'];
            unset(
    $attribs['cellHeight']);
            
    $this->view->headScript()->appendFile($this->view->layout()->staticBaseUrl
              
    'application/modules/Car/externals/scripts/color-picker.js');
            return 
    $this->view->formText($name$value$attribs$options)
              . 
    $this->view->inlineScript()->appendScript(
                
    sprintf("new ColorPicker($('%s'), {cellWidth: %d, cellHeight: %d})",
                  
    $name$cellWidth$cellHeight));
          }
        }
    Most of the time creating a custom form elements involves just changing how it is rendered via a view helper which is specified by the $helper property of the element. In this example render a text element in addition to some inline javascript code to create the color picker. Additional styles would be needed for the color picker to display properly.
    View helpers can be a subject of a another post. But for now it should be noted that for our FormColor view helper to be available to the framework, the following code must be added to the bootstrap file ../application/modules/Car/Bootstrap.php:

    PHP:
     <?php  
        
    class Car_Bootstrap extends Engine_Application_Bootstrap_Abstract
        
    {
          public function 
    __construct($application)
          {
            
    parent::__construct($application);
            
    $this->initViewHelperPath();
          }
       
        }
    Using Forms
    In a controller action the form should be instantiated and assigned to to view. To populate a form with values use the populate method. Submitted form values are validated via the isValid. This method has the effect of populating the form with filtered values and setting error messages for the form or form elements if any. Use the getValues method to get the filtered values from the form after a successful validation.

    Rendering Forms
    The following is the content of the view script for the create action for the index controller of the Car module:
    PHP:

        <?php echo $this->partial('_tagsCommonJS.tpl''car'?>
        <?php echo $this->form->render($this);
    The partial contains javascript code to control the tags field's auto suggest feature. It will be used on both the create and edit routines of the car module and will probably be used in other view scripts for albums, videos etc so it's logical to put it on a separate script for easy reuse. The following shows it's content:
    PHP:

        <?php
          $this
    ->headScript()
            ->
    appendFile($this->layout()->staticBaseUrl 'externals/autocompleter/Observer.js')
            ->
    appendFile($this->layout()->staticBaseUrl 'externals/autocompleter/Autocompleter.js')
            ->
    appendFile($this->layout()->staticBaseUrl 'externals/autocompleter/Autocompleter.Local.js')
            ->
    appendFile($this->layout()->staticBaseUrl 'externals/autocompleter/Autocompleter.Request.js');
        
    ?>
       
        <script type="text/javascript">
          en4.core.runonce.add(function()
          {
            new Autocompleter.Request.JSON('tags', '<?php echo $this->url(array('controller' => 'tag''action' => 'suggest'), 'default'true?>', {
              'postVar' : 'text',
              'customChoices' : true,
              'minLength': 1,
              'selectMode': 'pick',
              'autocompleteType': 'tag',
              'className': 'tag-autosuggest',
              'filterSubset' : true,
              'multiple' : true,
              'injectChoice': function(token){
                var choice = new Element('li', {'class': 'autocompleter-choices', 'value':token.label, 'id':token.id});
                new Element('div', {'html': this.markQueryValue(token.label),'class': 'autocompleter-choice'}).inject(choice);
                choice.inputValue = token;
                this.addChoiceEvents(choice).inject(this.choices);
                choice.store('autocompleteChoice', token);
              }
            });
          });
        </script>
    Advanced Stuff
    You probably read this post wanting to learn how to do stuff like customizing the layout of form, adding sub-forms or creating multi page forms. Unfortunately these are lengthy topics and are discussed else where on the web. Ask Mr. Google and build upon the stuff you just read and you'll be on your way.
    macfionn, Mexis and cybermaniac like this.
Thread Status:
Not open for further replies.

Share This Page

All rights reserved SocEngine.ru ©