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.
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
vtigercrm/
modules/
MyRss/
schema.xml
MyRss.php - class MyRss
views/
views/List.php - class MyRss_List_View
models/
models/Record.php - class MyRss_Record_Model
actions/
actions/Save.php - class MyRss_Save_Action
layouts/
vlayout/
modules/
MyRss/
SideBar.tpl
Index.tpl
resources/
resources/MyRss.js - jQuery.Class MyRss_List_Js
jquery_rss_min.js
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;
}
}
SideBar.tpl
<div class="quickWidgetContainer">
<div class="quickWidget">
<div class="quickWidgetHeader">
<h5 class="pull-left">Your RSS Feeds</h5>
<button class="btn pull-right" id="RssFeedAdd">+</button>
<div class="clearfix"></div>
</div>
<div class="widgetContainer collapse in">
<div class="row-fluid">
<div class="span10">
<ul class="nav nav-list">
{foreach item=FEED from=$FEEDS}
<li>
<a href="{$FEED->get('url')}"
data-feedurl="{$FEED->get('url')}">
{$FEED->get('title')}</a>
</li>
{/foreach}
</ul>
</div>
</div>
</div>
</div>
</div>
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">
← 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