An Extension Module Example

Requirements

User should be able to add new RSS feed and read the existing feed.

Design

Server Side

  • List view of existing RSS feed.

  • Save action for new RSS feed.

Client Side

  • Enable option to click on the RSS feed and load the items.

  • Provide option to add new RSS feed - accept the URL and title.

Implementation

Getting Started

Create the bootstrap vtlib script in root folder of Vtiger to activate the module entry.

<?php

include_once 'vendor/autoload.php';
include_once 'vtlib/Vtiger/Module.php';
$Vtiger_Utils_Log = true;

$MODULENAME = 'MyRss';

$moduleInstance = Vtiger_Module::getInstance($MODULENAME);
if ($moduleInstance || file_exists('modules/'.$MODULENAME)) {
   echo "Module already present - choose a different name.";
} else {
   $moduleInstance = new Vtiger_Module();
   $moduleInstance->name = $MODULENAME;
   $moduleInstance->parent= 'Tools';
   $moduleInstance->save();

   mkdir('modules/'.$MODULENAME);
   echo "OK\n";
}

Schema

Vtiger_Utils::CreateTable('vtiger_myrss',
'(id integer not null auto_increment, url varchar(255), title varchar(50), primary key (id))');

Directory Structure

Code

schema.xml

<?xml version="1.0"?>
<schema>
    <tables>
        <table>vtiger_myrss</table>
    </tables>
</schema>

MyRss.php

<?php

class MyRss {
        function vtlib_handler($moduleName, $eventType) {
                if ($eventType == 'module.postinstall') {
                        $data = array('url' => 'https://www.vtiger.com/blogs/?feed=rss2',
                                    'title' => 'Vtiger Blogs');
                                MyRss_Record_Model::create($data);
                }
        }
}

views/List.php

<?php
class MyRss_List_View extends Vtiger_Index_View {
        // We are overriding the default SideBar UI to list our feeds.
        public function preProcess(Vtiger_Request $request, $display = true) {
                $feeds = MyRss_Record_Model::findAll();
                $viewer = $this->getViewer($request);
                $viewer->assign('FEEDS', $feeds);
                return parent::preProcess($request, $display);
        }

        // Injecting custom javascript resources
        public function getHeaderScripts(Vtiger_Request $request) {
                $headerScriptInstances = parent::getHeaderScripts($request);
                $moduleName = $request->getModule();
                $jsFileNames = array(
                        "modules.$moduleName.resources.jquery_rss_min", // . = delimiter
                );
                $jsScriptInstances = $this->checkAndConvertJsScripts($jsFileNames);
                $headerScriptInstances = array_merge($headerScriptInstances, $jsScriptInstances);
                return $headerScriptInstances;
        }
}

models/Record.php

<?php
class MyRss_Record_Model extends Vtiger_Base_Model {
        public function save() {
                $db = PearDatabase::getInstance();
                $db->pquery('INSERT INTO vtiger_myrss (url, title) VALUES(?,?)', array(
                        $this->get('url'), $this->get('title')
                ));
                return $db->getLastInsertID();
        }
        static function create($data) {
                $instance = self::findWithUrl($data['url']);
                if ($instance) {
                        throw new Exception('Duplicate Feed');
                }
                $instance = new self($data);
                return $instance->save();
        }
        static function findWithUrl($url) {
                $db = PearDatabase::getInstance();
                $rs = $db->pquery('SELECT * FROM vtiger_myrss WHERE url=?', array($url));
                return $db->num_rows($rs)? new self($db->fetch_array($rs)) : NULL;
        }
        static function findAll() {
                $db = PearDatabase::getInstance();
                $instances = array();
                $rs = $db->pquery('SELECT * FROM vtiger_myrss ORDER BY id DESC', array());
                if ($db->num_rows($rs)) {
                        while ($data = $db->fetch_array($rs)) {
                                $instances[] = new self($data);
                        }
                }
                return $instances;
        }
}

actions/Save.php

<?php
class MyRss_Save_Action extends Vtiger_Action_Controller {
        public function checkPermission() {
                return true;
        }
        public function process(Vtiger_Request $request) {
                $feedurl = $request->get('url');
                if (empty($feedurl)) {
                        throw new Exception('URL cannot be empty');
                }

                $data = array('url' => $feedurl, 'title' => $feedurl);
                MyRss_Record_Model::create($data);

                $response = new Vtiger_Response();
                $response->setResult(array('feed' => $data));
                return $response;
        }
}

Index.tpl

<div class="listViewPageDiv">
        <div class="listViewTopMenuDiv">
                <div class="listViewActionsDiv">
                        <div id="RssFeedTitle">
                             Learn more about RSS
                             <a href="http://en.wikipedia.org/wiki/RSS" target="_blank">here</a>.
                        </div>
                </div>
        </div>
        <div id="RssFeedContainer" class="listViewEntriesDiv">
                &leftarrow; Selected feed content will be displayed here.
        </div>
</div>
<div id="RssFeedAddFormTpl" style="display: none;">
        <div class="modelContainer">
                <form method="GET" action="javascript:;" class="RssFeedAddForm">
                        <div class="modal-header">
                                <a class="close" data-dismiss="modal">x</a>
                                <h3>New RSS Feed</h3>
                        </div>
                        <div class="modal-body">
                                RSS Feed: <input type="text" name="url" class="validate[required]">
                        </div>
                        <div class="modal-footer">
                                <a class="cancelLink cancelLinkContainer pull-right" data-dismiss="modal">
                                 {vtranslate('LBL_CANCEL')}</a>
                                <button class="btn btn-success" type="submit">Add</button>
                        </div>
                </form>
        </div>
</div>

resources/MyRss.js

jQuery.Class('MyRss_List_Js', {}, {

        registerEvents: function() {
                this.registerFeedClick();
                this.registerFeedAdd();
        },

        registerFeedClick: function() {
                jQuery('[data-feedurl]').click(function(e){
                        e.preventDefault();
                        var feedAnchor = jQuery(this);
                        var feedurl = feedAnchor.data('feedurl');
                        jQuery('#RssFeedTitle').text(feedAnchor.html() + ' - ' + feedurl);
                        var options = {
                                limit: 50
                        }
                        jQuery('#RssFeedContainer').empty().rss(feedurl, options);
                });
        },

        registerFeedAdd: function() {
                jQuery('#RssFeedAdd').click(function(e){
                        var formTplClone = jQuery('#RssFeedAddFormTpl').clone().css({display: 'block'});
                        app.showModalWindow(formTplClone, function(){
                                var targetForm = jQuery('.RssFeedAddForm', formTplClone);
                                targetForm.validationEngine(app.validationEngineOptions);
                                targetForm.submit(function(){
                                        if (!targetForm.validationEngine('validate')) {
                                                return;
                                        }
                                        var params = 'module=MyRss&action=Save&';
                                        params += jQuery(targetForm).serialize();
                                        AppConnector.request(params).then(function(response){
                                                window.location.reload();
                                                //app.hideModalWindow();
                                        });
                                });
                        });
                });
        }

});

Distribution

You can achieve through Package Export API

Deployment

Go to Module Manager Import the module.

You can achieve through Package Import API