Commit 971d66f2 authored by Aegir user's avatar Aegir user

Add missing eu.tttp.civisualize.

parent 2f572817
<?php
return array (
0=>array (
'name'=>'dataviz_contact',
'entity'=> 'Dashboard',
'params'=> array (
'version'=>'3',
'name'=>'dataviz_contact',
'label'=> 'Dataviz of contacts',
'url' => CRM_Utils_System::url('civicrm/dataviz/contacts', 'snippet=4'),
)
),
1=>array (
'name'=>'dataviz_events',
'entity'=> 'Dashboard',
'params'=> array (
'version'=>'3',
'name'=>'dataviz_event',
'label'=> 'Dataviz of events',
'url' => CRM_Utils_System::url('civicrm/dataviz/events', 'snippet=4'),
)
),
2=>array (
'name'=>'dataviz_contribute',
'entity'=> 'Dashboard',
'params'=> array (
'version'=>'3',
'name'=>'dataviz_contribute',
'label'=> 'Dataviz of contributions',
'url' => CRM_Utils_System::url('civicrm/dataviz/contribute', 'snippet=4'),
)
)
);
<?php
require_once 'CRM/Core/Page.php';
class CRM_Civisualize_Page_Main extends CRM_Core_Page {
function getTemplateFileName () {
$request = CRM_Utils_System::currentPath();
if (false !== strpos($request, '..')) {
die ("SECURITY FATAL: the url can't contain '..'. Please report the issue on the forum at civicrm.org");
}
$request = split ('/',$request);
$tplfile = NULL;
$smarty= CRM_Core_Smarty::singleton( );
$smarty->assign("options",array());
if (CRM_Utils_Array::value(2, $request)) {
$tplfile = _civicrm_api_get_camel_name($request[2]);
$tplfile = explode('?', $tplfile);
$tpl = 'dataviz/'.$tplfile[0].'.tpl';
}
if (CRM_Utils_Array::value(3, $request)) {
$r3 = _civicrm_api_get_camel_name($request[3]);
$smarty->assign("id",$r3);
}
if (!$tplfile) {
$tpl = "CRM/Civizualise/Page/Main.tpl";
}
if( !$smarty->template_exists($tpl) ){
header("Status: 404 Not Found");
die ("Can't find the requested template file templates/$tpl");
}
return $tpl;
}
function run() {
$smarty= CRM_Core_Smarty::singleton( );
$dummy = NULL;
if (array_key_exists('id',$_GET)) {// special treatmenent, because it's often used
$smarty->assign ('id',(int)$_GET['id']);// an id is always positive
}
$pos = strpos (implode (array_keys ($_GET)),'<') ;
if ($pos !== false) {
die ("SECURITY FATAL: one of the param names contains &lt;");
}
$param = array_map( 'htmlentities' , $_GET);
//TODO: sql escape the params too
unset($param['q']);
$smarty->assign_by_ref("request", $param);
CRM_Core_Resources::singleton()
->addScriptFile('eu.tttp.civisualize', 'js/d3.v3.js', 110, 'html-header', FALSE)
->addScriptFile('eu.tttp.civisualize', 'js/dc/dc.js', 110, 'html-header', FALSE)
->addScriptFile('eu.tttp.civisualize', 'js/dc/crossfilter.js', 110, 'html-header', FALSE)
->addStyleFile('eu.tttp.civisualize', 'js/dc/dc.css')
->addStyleFile('eu.tttp.civisualize', 'css/style.css');
require_once 'CRM/Core/Smarty/plugins/function.crmSQL.php';
$smarty->register_function("crmSQL", "smarty_function_crmSQL");
require_once 'CRM/Core/Smarty/plugins/function.crmRetrieve.php';
$smarty->register_function("crmRetrieve", "smarty_function_crmRetrieve");
require_once 'CRM/Core/Smarty/plugins/function.crmTitle.php';
$smarty->register_function("crmTitle", "smarty_function_crmTitle");
return parent::run();
}
}
<?php
function smarty_function_crmRetrieve($params, &$smarty) {
if (!array_key_exists('var', $params) || !array_key_exists('type', $params) || !array_key_exists('name', $params)) {
$smarty->trigger_error("crmRetrieve: missing car, name or type");
return;
}
$value = CRM_Utils_Request::retrieve($params['name'], $params['type']);
if($value===null){
$smarty->trigger_error("crmRetrive: Cannot find a variable with matching name and type");
}
$smarty->assign($params['var'], $value);
}
\ No newline at end of file
<?php
function smarty_function_crmSQL($params, &$smarty) {
$is_error = 0;
$error = "";
$values = "";
$sql="";
if (!array_key_exists('sql', $params) && !array_key_exists('file', $params) && !array_key_exists('json', $params)) {
$smarty->trigger_error("assign: missing 'sql', 'json' OR 'file' parameter");
$error = "crmAPI: missing 'sql', 'json' or 'file' parameter";
$is_error = 1;
}
$parameters = array();
if(array_key_exists('json', $params)){
$json=json_decode(file_get_contents('queries/'.$params["json"].".json", true));//file_get_contents('queries/'.$params["json"].".json", true)
$sql=$json->{"query"};
foreach ($json->{"params"} as $key => $value) {
$var=intval($key);
$name=$value->{"name"};
$type=$value->{"type"};
if(array_key_exists($name, $params)){
$parameters[$var] = array($params[$name],$type);
}
}
}
else if(array_key_exists('sql', $params)){
$sql = $params["sql"];
}
else if(array_key_exists('file', $params)){
$sql = file_get_contents('queries/'.$params["file"].".sql", true);
}
$forbidden=array("delete ", "drop ","update ","grant ");
foreach ($forbidden as $check) {
if(strpos(strtolower($sql), $check)!==false){
$smarty->trigger_error($check."command not allowed");
$error = "crmAPI: you can not ".$check."using crmSQL";
$is_error = 1;
break;
}
}
if (array_key_exists('debug', $params)) {
$smarty->trigger_error("sql:". $params["sql"]);
}
try{
if($is_error==0){
$errorScope = CRM_Core_TemporaryErrorScope::useException();
$dao = CRM_Core_DAO::executeQuery($sql,$parameters);
$values = array();
while ($dao->fetch()) {
$values[] = $dao->toArray();
}
}
}
catch(Exception $e){
$is_error=1;
$error = "crmAPI: ".$e->getMessage();
$values="";
}
if(array_key_exists('set', $params)){
if($values!=""){
//echo "console.log('string')";
$smarty->assign($params['set'], $values);
}
}
return json_encode(array("is_error"=>$is_error, "error"=>$error, "values" => $values), JSON_NUMERIC_CHECK);
}
<?php
function smarty_function_crmTitle($params, &$smarty) {
if (array_key_exists('string', $params)) {
CRM_Utils_System::setTitle($params['string']);
return;
}
if (array_key_exists('array', $params)&&array_key_exists('field', $params)) {
CRM_Utils_System::setTitle($params['array'][0][$params['field']]);
return;
}
}
\ No newline at end of file
CiviCRM Data Visualization Framework
====================================
Civisualize is a civiCRM extension which let you visualize your civiCRM data with the power of [dc.js](http://dc-js.github.io/dc.js/) and [d3.js](http://d3js.org/). dc.js let users create graphs which enable filtering through the data with just a click on the graphs giving you a lot of powers. It's in beta, not going to hurt to hurt your data, do let us know if something works or doesn't.
Civisualize will help you out with ready to use visualizations that we have created and will provide you the right resources if you plan to create your own custom visualization.
- [Installation](#installation)
- [Ready to Use Visualizations](#ready-to-use-visualizations)
- [Create your own visualizations](#create-your-own-visualizations)
- [Suggestions](#we-love-you)
***
Installation
------------
You can install this extension directly from your civicrm site, like any other etension from Administer->System settings->Manage extensions
If you don't have a bootstrap based theme, you should install https://github.com/TechToThePeople/bootstrapvisualize
You can also live at the cutting edge and directly clone the master branch to your civiCRM extension directory using
`git clone https://github.com/TechToThePeople/civisualize.git`
Ready to Use Visualizations
---------------------------
The list of available visualizations is available at [yoursite.org/civicrm/dataviz](/civicrm/dataviz). Some of the currently available visualizations include
- ####Contributions (/dataviz/contribute)
![alt tag](https://raw.githubusercontent.com/TechToThePeople/civisualize/master/examples/contribution.jpg)
This provides an overview of contributions, type of contributors, the day of week the contribution was made, payment instrument, etc.
- ####Contacts (/dataviz/contacts)
![alt tag](https://raw.githubusercontent.com/TechToThePeople/civisualize/master/examples/contacts.jpg)
Contacts overview is a compilation of graphs showing types, gender, age, source of contact, contacts over the time etc.
- ####Events (/dataviz/events)
![alt tag](https://raw.githubusercontent.com/TechToThePeople/civisualize/master/examples/events.jpg)
Events shows a lot of information about all the events of an organization and their participants. It shows events over time, participants over time, money generated from events, type of events, etc. It also displays a table listing all the events.
This table further links to specific event overview (/dataviz/event/<id>) showing participants, their status, their fee etc.
![alt tag](https://raw.githubusercontent.com/TechToThePeople/civisualize/master/examples/event.jpg)
- ####Donor Trends (/dataviz/donortrends)
![alt tag](https://raw.githubusercontent.com/TechToThePeople/civisualize/master/examples/donortrends.jpg)
Based on the idea of [CiviCRM Donor Trends Extension](https://github.com/leez/org.eff.donortrends/), the donor trends overview showcases a Bar Chart with new, lapsed, upgraded, downgraded and maintained donors over the years. This further showcases the gender and age of the donors and a list of all the donors.
Need not be mentioned, you can add more graphs and details to the above mentioned visualizations.
Create your own visualizations
------------------------------
If you are a developer you can customize or create your extensions with a little knowledge of mysql/crmAPI, d3.js/dc.js and crossfilter. Civisualize basically has two parts
- Extract data from civiCRM
- Display data using dc.js/d3
###For the first part, we are using the following methods
- ####{crmAPI}
``{crmAPI entity="OptionValue" option_group_id="14"}``
You can add getstat as a new action on some entities in the api.
- ####{crmSQL}
We have added a new crmSQL to run a mySQL query. For obvious reasons crmSQL only let you run SELECT queries. You can get your data using any of the following three methods.
- #####SQL String
``{crmSQL sql="SELECT count(*) ... group by ...."}``
- #####SQL File
``{crmSQL query="somethingcool"}``
This will fetch the sql query from `/queries/somethingcool.sql`
- #####JSON Object
``{crmSQL json="somethingcooler" cid=4 bla="hello"}``
This will fetch a json object from `/queries/somethingcooler.json`
The format of the json is
```javascript
{ "query":"SELECT * from ABC where id=%1 and bla=%2",
"params":{ "1":{ "name":"cid",
"type":"Integer"},
"2":{ "name":"bla",
"type":"String"}
}
}
```
You can further use {crmRetrieve var="a" name="b" type="Integer"}
This will assign the POST or GET variable named b into a, which can then be given to {crmSQL}
#####Optional Arguments
{crmSQL set="varname"} will assign the result of the sql query to a smarty variable named varname
#####Return Value
{crmSQL} returns a json of the following format
```javascript
{"is_error":0,"error":"error_str","values":"Array of objects"}
```
so we primarily use {crmSQL}.values for our visualizations.
- ####{crmReport}
A 3rd option is to be able to fetch data from a report instance using a {crmReport...}. Eileen has done (most of?) the work already. I think it's on 4.5
###Display data
The principle is to get the data in a template as a json, and apply dc on it until it looks awesome. You simply have to create a template into `templates/dataviz/Something.tpl`. Once you have the data from the above methods you can apply dc on it, and you can access it from `http://yoursite.org/civicrm/dataviz/something`
To get you started, you can visit http://yoursite.org/civicrm/dataviz/contribute or any of the above mentioned visualizations
This is using the wondefully magic dc, that is a layer of love on the top of d3 and crossfilter. Click on the graphs to filter down. magic, I told you. No matter if you use {crmAPI} or {crmSQL}, you end up with a json and a d3 and dc loaded and ready to rock
In the template, put
```javascript
<div id="theplacetograph"></div>
<script>
var mydata={crmAPI or crmSQL};
{literal}
d3("#theplacetograph").selectAll(...).data(mydata.values).domagic(...);
```
We have also used {crmTitle} function which let you set the title of the page, and a print_filter function that will help you in playing around with crossfilter.
Checkour resources at [dc.js](http://dc-js.github.io/dc.js/) and create your first visualization.
We love you
-------
xavier made this, sid helped him. You can find us on civicrm forum, [@eucampaign](http://twitter.com/eucampaign) and [@sid0_o](http://twitter.com/sid0_o) on twitter, or in the dc mailing group. Be warned, d3 is awesome, but the learning curve is steep. Worthwhile, the view at the top is beautiful.
<?php
function civicrm_api3_activity_getstat ($params) {
$sql="SELECT COUNT(id),DATE(register_date) as register from civicrm_participant group by DATE(register_date)";
return _civicrm_api3_basic_getsql ($params,$sql);
}
<?php
function civicrm_api3_contact_getstat ($params) {
// mostly copy pasted from contact_get and the functions called by it
$options = array();
_civicrm_api3_contact_get_supportanomalies($params, $options);
$contacts = _civicrm_api3_get_using_query_object('contact', $params, $options);
$options = _civicrm_api3_get_options_from_params($params, TRUE);
$inputParams = CRM_Utils_Array::value('input_params', $options, array());
$returnProperties = CRM_Utils_Array::value('return', $options, array());
if(!empty($params['check_permissions'])){
// we will filter query object against getfields
$fields = civicrm_api("contact", 'getfields', array('version' => 3, 'action' => 'get'));
// we need to add this in as earlier in this function 'id' was unset in favour of $entity_id
$fields['values'][$entity . '_id'] = array();
$varsToFilter = array('returnProperties', 'inputParams');
foreach ($varsToFilter as $varToFilter){
if(!is_array($$varToFilter)){
continue;
}
$$varToFilter = array_intersect_key($$varToFilter, $fields['values']);
}
}
// $options = array_merge($options,$additional_options);
$sort = CRM_Utils_Array::value('sort', $options, NULL);
$returnSQL = CRM_Utils_Array::value('sql', $options, CRM_Utils_Array::value('options_sql', $options['input_params']));
$smartGroupCache = CRM_Utils_Array::value('smartGroupCache', $params);
$newParams = CRM_Contact_BAO_Query::convertFormValues($inputParams);
$skipPermissions = CRM_Utils_Array::value('check_permissions', $params)? 0 :1;
$query = new CRM_Contact_BAO_Query(
$params, $returnProperties,
NULL, TRUE, FALSE, 1,
$skipPermissions,
TRUE, $smartGroupCache
);
//this should add a check for view deleted if permissions are enabled
if ($skipPermissions){
$query->_skipDeleteClause = TRUE;
}
$query->generatePermissionClause(FALSE, $count);
list($select, $from, $where, $having) = $query->query($count);
$options = $query->_options;
if(!empty($query->_permissionWhereClause)){
if (empty($where)) {
$where = "WHERE $query->_permissionWhereClause";
} else {
$where = "$where AND $query->_permissionWhereClause";
}
}
$sql = "$select $from $where $having";
if (!empty($returnProperties)) {
$extra = array();
$sql = "SELECT count(*) AS total,". substr ($sql, 34,10000); //replace select contact_id, by select count(*)
$sql .= " GROUP BY ". implode (",",array_keys($returnProperties)) ;
} else {
$sql = "SELECT count(*) AS total $from $where $having";
$extra = array ("tip"=>"if you need to group by a field, use the return param, eg return=contact_type,gender",
"warning"=> "use getcount, getstat without param might be blocked in the future");
if (!empty($sort)) {
$sql .= " ORDER BY $sort ";
} else {
$sql .= " ORDER BY total DESC ";
}
}
if ($returnSQL) {
return array("is_error"=>1,"sql"=>$sql,"from"=>$from,"where"=>$where,"having"=>$having);
}
$dao = CRM_Core_DAO::executeQuery($sql);
$values = array();
while ($dao->fetch()) {
$values[] = $dao->toArray();
}
return civicrm_api3_create_success($values, $params, "contact", "getstat", $dao,$extra);
}
<?php
function civicrm_api3_group_contact_getstat ($params) {
$sql = "SELECT count(*) as total,civicrm_group.id, civicrm_group.name
FROM civicrm_group_contact, civicrm_group
WHERE civicrm_group_contact.group_id = civicrm_group.id
GROUP BY civicrm_group.id";
return _civicrm_api3_basic_getsql ($params,$sql);
}
<?php
function civicrm_api3_participant_getstat ($params) {
$sql="SELECT COUNT(id),DATE(register_date) as register from civicrm_participant group by DATE(register_date)";
return _civicrm_api3_basic_getsql ($params,$sql);
}
<?php
function civicrm_api3_tag_getstat ($params) {
$sql = "SELECT count(*) as total, civicrm_tag.id, civicrm_tag.name, civicrm_tag.parent_id, description
FROM civicrm_tag, civicrm_entity_tag
WHERE civicrm_tag.id = civicrm_entity_tag.tag_id AND entity_table = 'civicrm_contact'
GROUP BY civicrm_tag.id";
return _civicrm_api3_basic_getsql ($params,$sql);
}
<?php
// AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file
/**
* (Delegated) Implementation of hook_civicrm_config
*/
function _civisualize_civix_civicrm_config(&$config = NULL) {
static $configured = FALSE;
if ($configured) return;
$configured = TRUE;
$template =& CRM_Core_Smarty::singleton();
$extRoot = dirname( __FILE__ ) . DIRECTORY_SEPARATOR;
$extDir = $extRoot . 'templates';
if ( is_array( $template->template_dir ) ) {
array_unshift( $template->template_dir, $extDir );
} else {
$template->template_dir = array( $extDir, $template->template_dir );
}
$include_path = $extRoot . PATH_SEPARATOR . get_include_path( );
set_include_path( $include_path );
}
/**
* (Delegated) Implementation of hook_civicrm_xmlMenu
*
* @param $files array(string)
*/
function _civisualize_civix_civicrm_xmlMenu(&$files) {
foreach (_civisualize_civix_glob(__DIR__ . '/xml/Menu/*.xml') as $file) {
$files[] = $file;
}
}
/**
* Implementation of hook_civicrm_install
*/
function _civisualize_civix_civicrm_install() {
_civisualize_civix_civicrm_config();
if ($upgrader = _civisualize_civix_upgrader()) {
return $upgrader->onInstall();
}
}
/**
* Implementation of hook_civicrm_uninstall
*/
function _civisualize_civix_civicrm_uninstall() {
_civisualize_civix_civicrm_config();
if ($upgrader = _civisualize_civix_upgrader()) {
return $upgrader->onUninstall();
}
}
/**
* (Delegated) Implementation of hook_civicrm_enable
*/
function _civisualize_civix_civicrm_enable() {
_civisualize_civix_civicrm_config();
if ($upgrader = _civisualize_civix_upgrader()) {
if (is_callable(array($upgrader, 'onEnable'))) {
return $upgrader->onEnable();
}
}
}
/**
* (Delegated) Implementation of hook_civicrm_disable
*/
function _civisualize_civix_civicrm_disable() {
_civisualize_civix_civicrm_config();
if ($upgrader = _civisualize_civix_upgrader()) {
if (is_callable(array($upgrader, 'onDisable'))) {
return $upgrader->onDisable();
}
}
}
/**
* (Delegated) Implementation of hook_civicrm_upgrade
*
* @param $op string, the type of operation being performed; 'check' or 'enqueue'
* @param $queue CRM_Queue_Queue, (for 'enqueue') the modifiable list of pending up upgrade tasks
*
* @return mixed based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending)
* for 'enqueue', returns void
*/
function _civisualize_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
if ($upgrader = _civisualize_civix_upgrader()) {
return $upgrader->onUpgrade($op, $queue);
}
}
function _civisualize_civix_upgrader() {
if (!file_exists(__DIR__.'/CRM/Civisualize/Upgrader.php')) {
return NULL;
} else {
return CRM_Civisualize_Upgrader_Base::instance();
}
}
/**
* Search directory tree for files which match a glob pattern
*
* Note: Dot-directories (like "..", ".git", or ".svn") will be ignored.
* Note: In Civi 4.3+, delegate to CRM_Utils_File::findFiles()
*
* @param $dir string, base dir
* @param $pattern string, glob pattern, eg "*.txt"
* @return array(string)
*/
function _civisualize_civix_find_files($dir, $pattern) {
if (is_callable(array('CRM_Utils_File', 'findFiles'))) {
return CRM_Utils_File::findFiles($dir, $pattern);
}
$todos = array($dir);
$result = array();
while (!empty($todos)) {
$subdir = array_shift($todos);
foreach (_civisualize_civix_glob("$subdir/$pattern") as $match) {
if (!is_dir($match)) {
$result[] = $match;
}
}
if ($dh = opendir($subdir)) {
while (FALSE !== ($entry = readdir($dh))) {
$path = $subdir . DIRECTORY_SEPARATOR . $entry;
if ($entry{0} == '.') {
} elseif (is_dir($path)) {
$todos[] = $path;
}
}
closedir($dh);
}
}
return $result;
}
/**
* (Delegated) Implementation of hook_civicrm_managed
*
* Find any *.mgd.php files, merge their content, and return.
*/
function _civisualize_civix_civicrm_managed(&$entities) {
$mgdFiles = _civisualize_civix_find_files(__DIR__, '*.mgd.php');
foreach ($mgdFiles as $file) {
$es = include $file;
foreach ($es as $e) {
if (empty($e['module'])) {
$e['module'] = 'eu.tttp.civisualize';
}
$entities[] = $e;
}
}
}
/**
* Glob wrapper which is guaranteed to return an array.
*
* The documentation for glob() says, "On some systems it is impossible to
* distinguish between empty match and an error." Anecdotally, the return
* result for an empty match is sometimes array() and sometimes FALSE.
* This wrapper provides consistency.
*
* @see http://php.net/glob
* @param string $pattern
* @return array, possibly empty
*/
function _civisualize_civix_glob($pattern) {
$result = glob($pattern);
return is_array($result) ? $result : array();
}
/**
* Inserts a navigation menu item at a given place in the hierarchy
*
* $menu - menu hierarchy
* $path - path where insertion should happen (ie. Administer/System Settings)
* $item - menu you need to insert (parent/child attributes will be filled for you)
* $parentId - used internally to recurse in the menu structure
*/
function _civisualize_civix_insert_navigation_menu(&$menu, $path, $item, $parentId = NULL) {
static $navId;
// If we are done going down the path, insert menu
if (empty($path)) {
if (!$navId) $navId = CRM_Core_DAO::singleValueQuery(</