Customizing:admin:locations

From Snapp CMS Developer Documentation

Jump to: navigation, search

How to add custom sub module tabs to Locations module pages This page describes the steps to add an extension to the admin Locations module so that when a user opens a location, an extra tab is presented which allows extended information about the location to be entered and edited.

1. Insert module details to cms_modules db table as follows:

  • id: (auto-increment)
  • title: Your module title
  • description: Description
  • module_id: Your module's name, using underscores, prefixed with "locations_" by convention e.g. locations_mymodule
  • isactive: 1
  • list_order: 99
  • parent_id: The ID of the locationsmanager module (this is usually around 66 in the default install of Snapp
  • custom: 1
  • access_levels: You can set your own, but usually "view:View,add:Add,edit:Edit,delete:Delete" for the standard ACL options

2. Log out of and into the CMS and switch to the Administration tab and select CMS Modules. Your new module should show up under Locations Manager. Make sure it's ticked, and tick it if it isn't.

3. Select CMS Users, double-click on each user and select User Access. Tick all the ACL boxes for your new module.

4. Log out of and back into the CMS to let the new ACL take

5. Create a javascript class

  • Class naming convention: cms.admin.modules.locationsmanager.modules.locations_mymodule

The class should be created in your site's custom folder, in the file: /custom/cms.admin.modules.locationsmanager.modules.locations_mymodule.js

In order to create the tab panel, the class requires locationTab() method, which returns an instance of an Ext.Panel defining how you wish the contents of the tab to display.

Place the JS file under your site's /custom folder.

Here's some example code for how we generated the location categories tab, which displays categories with checkboxes and allows the user to assign categories to a location:

cms.admin.modules.locationsmanager.modules.locations_mymodule = {
	locationTab : function (id) {
		var tabPanel = new Ext.tree.TreePanel({
			title:'My Module',
			rootVisible:false,
			lines:true,
			autoScroll:true,
			id:'locationCategories-' + id,
			loader : new Ext.tree.TreeLoader({
				url: 'load.php',
				baseParams : {	module:'adminLocationsMymodule', // This is the PHP class that handles server-side operations for our new module
						action:'loadLocationCategories', // This is the PHP method that loads initial data for the module
						id:id}                           // Here is the parameter that we're supplying to the method
			}),

			root: new Ext.tree.AsyncTreeNode({
				text:'Categories'
			}),

			// Define the toolbar for the tab
			tbar: [
				{
					iconCls:'iconRefresh',
					handler: function () {
						tabPanel.root.reload();
					}
				}
			],
			listeners : {
				// Here we demonstrate some listeners for checkboxes
				checkchange : function (node,checked) {
					var selectedNode = node;
					if (id > 0) {
						if (cms.admin.user.checkAccess(	'locationsmanager,location_mymodule',
										'edit')) { // These are the rights as set in the cms_module table and granted to the user
							node.ui.iconNode.src = 'images/activity_indicators/indicator_wheel1.gif';
							Ext.Ajax.request({
								url:'load.php',
								params: {	module:'adminLocationsMymodule', // This is the PHP class that handles module server-side operations
										action:'locationCategory',       // This is the method that is called on a checkbox click
										checked:checked,id:id,category:node.id},  // Parameters to send to method
								disableCaching: true,
								success : function (o){
									var response = Ext.decode(o.responseText);
									if (response.success == true) {
										selectedNode.ui.iconNode.src = 'images/blank_1x1.gif';
										selectedNode.ui.highlight('00ff00',{attr: 'background-color',duration:2,easing:'easeOut'});
									}
								},
								failure : cms.admin.errors.process
							});
						}
					}
				}
			}
		});
		return tabPanel;
	}
}


6. Create a PHP class for your new module, based on your module's name.

The class naming convention: admin{ucfirst(master_module_name)}{ucfirst(new_module_name)} - e.g. adminLocationsMymodule

The class should be stored in <classname>.php, e.g. adminLocationsMymodule.php

Place the PHP file under /custom folder


Here's the example we made for the "locations_mymodule" module:

<?
class adminLocationsMymodule {
	public function loadLocationCategories ($data='') {
		global $cms;
		$id = 0;
		$output = array();
		if (is_array($data) || is_object($data)) {
			foreach ($data AS $name=>$value) {
				switch ($name) {
					case 'id': $id = (int)$value; break;
				}
			}
		
		}
		if (!empty($id)) {
			if ($cms->user->checkAccess(array('locationsmanager','locations_mymodule'),'view')) {
				$sql =  "SELECT A.id,A.title,A.parent_id, IFNULL(B.category_id,0) AS selected ".
						"FROM location_categories AS A ".
						"LEFT JOIN ( ".
						"	SELECT DISTINCT category_id ".
						"	FROM location_category_links ".
						"	WHERE location_id=".$id." ".
						") AS B ON A.id=B.category_id ".
						"ORDER BY A.parent_id ASC, A.title ASC";
				$allCategories = $cms->db->getArrays($sql);				
				$this->parent_id = 0;
				foreach (array_filter($allCategories,array($this,'filterCategories')) AS $category) {
					$output[] = $this->buildCategoryNode($category,$allCategories);
				}
			
			} else {
				$output['success'] = false;
				$output['msg'] = 'You DO NOT have sufficient privileges to complete this action';
			}
		} else {
			if ($cms->user->checkAccess(array('locationsmanager','locations_mymodule'),'view')) {
				$sql =  "SELECT A.id,A.title,A.parent_id, CONCAT(0) AS selected ".
						"FROM location_categories AS A ".
						"ORDER BY A.parent_id ASC, A.title ASC";
				$allCategories = $cms->db->getArrays($sql);				
				$this->parent_id = 0;
				foreach (array_filter($allCategories,array($this,'filterCategories')) AS $category) {
					$output[] = $this->buildCategoryNode($category,$allCategories);
				}
			
			} else {
				$output['success'] = false;
				$output['msg'] = 'You DO NOT have sufficient privileges to complete this action';
			}
		} 
		$cms->jsonOutput($output);			
	} 

	public function locationCategory($data='') {
		global $cms;
		$id = 0;
		$category = 0;
		$output = array();
		$action = 'delete';
		if (is_array($data) || is_object($data)) {
			foreach ($data AS $name=>$value) {
				switch ($name) {
					case 'id': $id=(int)$value; break;
					case 'category': $category=(int)$value; break;
					case 'checked': $action = ($value=='true'?'add':'delete'); break;
				}
			}
		}
		if (!empty($id) && !empty($category)) {
			if ($cms->user->checkAccess(array('locationsmanager','locations_mymodule'),'edit')) {
				if ($action == 'add') {
					$sql = "INSERT INTO location_category_links (location_id,category_id) VALUES (".$id.",".$category.");";
					if ($cms->db->query($sql)) {
						$output['success'] = true;
					} else {
						$output['succes'] = false;
						$output['msg'] = 'Error Occured Adding Category to this Location';
					}
				} else {
					$sql = "DELETE FROM location_category_links WHERE location_id=".$id." AND category_id=".$category.";";
					if ($cms->db->query($sql)) {
						$output['success'] = true;
					} else {
						$output['succes'] = false;
						$output['msg'] = 'Error Occurred Removing Category from this Location';
					}
				}
			} else {
				$output['success'] = false;
				$output['msg'] = 'You DO NOT have sufficient privileges to complete this action';
			}
		} else {
			$output['success'] = false;
			$output['msg'] = 'Missing or Invalid ID';
		}
		
		$cms->jsonOutput($output);
	}


	private function filterCategories($row) {
		return $row['parent_id'] == $this->parent_id;
	}



	//    Function to Build Single Category Node

	private function buildCategoryNode($category,$allCategories) {
		$output = array(

			'id' => $category['id'],
			'text' => $category['title'],
			'iconCls' => 'iconCatalogCategory',
			'type' => 'category',
			'data' =>$category

		);

		if (isset($category['selected'])) {

			$output['checked'] = $category['selected'] > 0 ? true:false;
		} else {
			$output['href'] = 'javascript:cms.admin.modules.locationsmanager.viewLocations({category:' .$category['id'].'});';

		}
		$this->parent_id = $category['id'];
		$subCategories = array_filter($allCategories,array($this,'filterCategories'));

		if (!empty($subCategories)) {
			$output['children'] = array();
			$output['leaf'] = false;
			if (isset($category['selected'])) {
				$output['expanded'] = true;
			}
			foreach ($subCategories AS $subCategory) {
				$output['children'][] = $this->buildCategoryNode($subCategory,$allCategories);
			}
		} else {
			$output['leaf'] = true;
		}
		return $output;
	}
}

7. Importantly, note that if you modify Javascript files you will need to clear your browser's cache and perform a full page reload to see those changes. For testing, we recommend using Firefox with the Web Developer Toolbar extension installed and configured to disable all browser cache so that Javascript updates are applied on every refresh and page load. Also use the Firebug extension for debugging Javascript issues. Chrome's development extensions still seem a little flakey at the moment for this sort of thing. Also note that if you make any changes to code handling access controls, you will likely want to log the user out of the admin panel then log in again to ensure that privileges are applied correctly.

Personal tools
Core Components
Standalone Installs