array(
'variables' => array('jsmap' => NULL, 'options' => NULL, 'regions' => NULL),
),
'jsmap_region' => array(
'variables' => array('jsmap_name' => NULL, 'region_title' => NULL, 'records' => NULL, 'region_data' => NULL),
),
);
}
/**
* Implements hook_module_preprocess_hook().
*
* Add map data as Drupal JS settings
*/
function template_preprocess_jsmap(&$vars) {
global $language;
static $counter = 0;
$vars['jsmap_id'] = $settings['jsmap_id'] = "jsmap_$counter";
if ($path = libraries_get_path('raphael')) {
drupal_add_js($path . '/raphael-min.js');
}
drupal_add_css(drupal_get_path('module', 'jsmap') .'/css/jsmap.css');
drupal_add_js(drupal_get_path('module', 'jsmap') .'/js/jsmap.js');
//set defaults
$settings['defaults'] = $vars['options'] ? $vars['options'] : array(
'general' => array(
'fontSize' => "10",
'scale' => '1',
'showNames' => MAP_VALUE_DEFAULT,
'data_position' => 'static',
),
'pathDefaults' => array(
'fill' => '#fff',
'fill-hover' => '#c80000',
'stroke' => '#3899E6',
'stroke-width' => '1',
'interactive' => MAP_VALUE_DEFAULT,
)
);
$settings['regions'] = $vars['regions'];
drupal_add_js(array('jsmaps' => array($counter => $settings)), 'setting');
$counter++;
}
/**
* Theme a map!
*/
function theme_jsmap($vars) {
return '
';
}
/**
* Implements hook_views_api().
*/
function jsmap_views_api() {
return array(
'api' => '3.0-alpha1',
);
}
/*field api ==========================================*/
/**
* Implements hook_field_info()
* Enter description here ...
*/
function jsmap_field_info(){
return array(
'jsmap_region_reference' => array(
'label' => t('Map region'),
'description' => t('Reference to a map region'),
'default_formatter' => 'jsmap_region_reference_format',
'default_widget' => 'options_select',
'settings' => array('map' => ''),
)
);
}
/**
* Implements hook_field_widget_info_alter().
* Add our field type to the list of types that are allowed to use options_select
*/
function jsmap_field_widget_info_alter(&$info) {
$info['options_select']['field types'][] = 'jsmap_region_reference';
}
/**
* Implements hook_field_settings_form
* Enter description here ...
* @param unknown_type $field
* @param unknown_type $instance
*/
function jsmap_field_settings_form($field, $instance, $has_data){
$settings = $field['settings'];
$options = array();
if($maps = entity_load('jsmap_map')){
foreach($maps as $map){
$options[$map->map] = $map->label;
}
}
$form['map'] = array(
'#type' => 'select',
'#title' => t('Map'),
'#options'=> $options,
'#default_value' => $settings['map'],
'#required' => TRUE,
'#disabled' => $has_data,
);
return $form;
}
/**
* Implements hook_options_list().
*/
function jsmap_options_list($field) {
$function = !empty($field['settings']['options_list_callback']) ? $field['settings']['options_list_callback'] : 'jsmap_allowed_values';
return $function($field);
}
function jsmap_allowed_values($field){
$options = array();
if($regions = entity_load('jsmap_region', FALSE, array('map' => $field['settings']['map']))){
foreach($regions as $region){
$options[$region->id] = $region->name;
}
}
return $options;
}
/**
* Implements hook_field_is_empty().
*/
function jsmap_field_is_empty($item, $field) {
switch ($field['type']) {
case 'jsmap_region_reference':
return empty($item['id']);
break;
}
}
/**
* Implements hook_field_formatter_info
* Enter description here ...
*/
function jsmap_field_formatter_info(){
return array(
'jsmap_region_reference_format' => array(
'label' => 'Rendered region',
'description' => 'Draws the region based on the provided shape data',
'field types' => array('jsmap_region_reference'),
'settings' => array(
'scale' => '1',
'max_width' => '',
'max_height' => '',
'display_labels' => MAP_VALUE_DEFAULT,
'label_size' => '10',
'region_color' => '#FFFFFF',
'interactive' => MAP_VALUE_DEFAULT,
'region_hover_color' => '#1669AD',
'stroke_color' => '#3899E6',
'stroke_width' => '1'
),
)
);
}
/**
* Implements hook_field_formatter_settings_form
* Enter description here ...
* @param unknown_type $field
* @param unknown_type $instance
* @param unknown_type $view_mode
* @param unknown_type $form
* @param unknown_type $form_state
*/
function jsmap_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state){
$yesno_options = array(
MAP_VALUE_DEFAULT => 'Determined by the region',
MAP_VALUE_YES => 'Always',
MAP_VALUE_NO => 'Never',
);
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$form = array();
$form['scale'] = array(
'#title' => t('Scale'),
'#type' => 'textfield',
'#size' => 3,
'#default_value' => $settings['scale'],
);
$form['max_width'] = array(
'#title' => t('Maximum Width (in px)'),
'#type' => 'textfield',
'#size' => 5,
'#default_value' => $settings['max_width'],
);
$form['max_height'] = array(
'#title' => t('Maximum Height (in px)'),
'#type' => 'textfield',
'#size' => 5,
'#default_value' => $settings['max_height'],
);
$form['display_labels'] = array(
'#title' => t('Display Labels'),
'#type' => 'select',
'#options' => $yesno_options,
'#default_value' => $settings['display_labels'],
);
$form['label_size'] = array(
'#type' => 'textfield',
'#title' => t('Region label font size'),
'#default_value' => $settings['label_size'],
);
$form['region_color'] = array(
'#type' => 'textfield',
'#title' => t('Region background color'),
'#default_value' => $settings['region_color'],
);
$form['interactive'] = array(
'#type' => 'select',
'#options' => $yesno_options,
'#title' => t('Make region interactive'),
'#default_value' => $settings['interactive'],
);
$form['region_hover_color'] = array(
'#type' => 'textfield',
'#title' => t('Region background color on hover'),
'#default_value' => $settings['region_hover_color'],
);
$form['stroke_color'] = array(
'#type' => 'textfield',
'#title' => t('Stroke (line) color'),
'#default_value' => $settings['stroke_color'],
);
$form['stroke_width'] = array(
'#type' => 'textfield',
'#title' => t('Stroke (line) width'),
'#default_value' => $settings['stroke_width'],
);
return $form;
}
/**
* Implements hook_field_formatter_settings_summary
* Enter description here ...
* @param unknown_type $field
* @param unknown_type $instance
* @param unknown_type $view_mode
*/
function jsmap_field_formatter_settings_summary($field, $instance, $view_mode){
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
if($settings['scale'] > 0) return t('Map region displayed at a scale of %scale', array('%scale' => $settings['scale']));
return t('Map region scaled to fit region %widthpx wide and %heightpx high', array('%width' => $settings['max_width'], '%height' => $settings['max_height']));
}
/**
* Implements hook_field_formatter_view
* Enter description here ...
* @param unknown_type $entity_type
* @param unknown_type $entity
* @param unknown_type $field
* @param unknown_type $instance
* @param unknown_type $langcode
* @param unknown_type $items
* @param unknown_type $display
*/
function jsmap_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display){
$settings = $display['settings'];
$output = array();
switch($display['type']){
case 'jsmap_region_reference_format':
if(is_array($items)){
foreach($items as $delta => $item){
$regions = entity_load('jsmap_region', array($item['id']));
$region = reset($regions);
$region->path = $region->svg_path;
$uri = entity_uri($entity_type,$entity);
$region->url = $uri['path'];
$region->body = $region->name;
//@todo put this in a proper render array using #theme
$output[$delta] = array('#markup' => theme('jsmap', array(
'jsmap' => NULL,
'options' => array(
'general' => array(
'fontSize' => $settings['label_size'],
'scale' => $settings['scale'],
'max_width' => $settings['max_width'],
'max_height' => $settings['max_height'],
'showNames' => $settings['display_labels'],
),
'pathDefaults' => array(
'interactive' => $settings['interactive'],
'fill' => $settings['region_color'],
'fill-hover' => $settings['region_hover_color'],
'stroke' => $settings['stroke_color'],
'stroke-width' => $settings['stroke_width']
)
),
'regions' => array($region),
)
));
}
}
break;
}
return $output;
}
/* Entity stuff ======================================================================*/
/**
* Implement hook_entity_info().
*
* We define two entities here - the actual entity that will hold our domain
* specific information and an entity that holds information about the different
* types of entities. See here: http://drupal.org/node/977380 for a discussion on this
* choice.
*/
function jsmap_entity_info() {
$return['jsmap_region'] = array(
'label' => t('Region'),
'entity class' => 'JsmapRegion',
'controller class' => 'JsmapRegionController',
'base table' => 'jsmap_region',
'fieldable' => TRUE,
'entity keys' => array(
'id' => 'id',
'bundle' => 'map',
),
// Bundles are defined in hook_entity_info_alter
'bundles' => array(),
// Bundle keys tell the FieldAPI how to extract information from the bundle objects
'bundle keys' => array(
'bundle' => 'map',
),
'label callback' => 'entity_class_label',
'uri callback' => 'entity_class_uri',
'creation callback' => 'jsmap_region_create',
'access callback' => 'jsmap_region_access',
'module' => 'jsmap',
// Enable the entity API's admin UI.
'admin ui' => array(
'path' => 'admin/structure/jsmap_maps/regions',
'file' => 'jsmap_region.admin.inc',
'controller class' => 'JsmapRegionUIController',
'menu wildcard' => '%jsmap_region',
),
);
$return['jsmap_map'] = array(
'label' => t('Map'),
'entity class' => 'JsmapMap',
'controller class' => 'JsmapMapController',
'base table' => 'jsmap_map',
'fieldable' => FALSE,
'bundle of' => 'jsmap_region',
'exportable' => TRUE,
'entity keys' => array(
'id' => 'id',
'name' => 'map',
'label' => 'label',
),
'access callback' => 'jsmap_map_access',
'module' => 'jsmap',
// Enable the entity API's admin UI.
'admin ui' => array(
'path' => 'admin/structure/jsmap_maps',
'file' => 'jsmap_map.admin.inc',
'controller class' => 'JsmapMapUIController',
),
);
return $return;
}
/**
* Implements hook_entity_info_alter().
*
* We are adding the info about the maps via a hook to avoid a recursion
* issue as loading the maps requires the entity info as well.
*
* @todo This needs to be improved
*/
function jsmap_entity_info_alter(&$entity_info) {
foreach (jsmap_get_maps() as $map => $info) {
$entity_info['jsmap_region']['bundles'][$map] = array(
'label' => $info->label,
'admin' => array(
'path' => 'admin/structure/jsmap_maps/manage/%jsmap_map',
'real path' => 'admin/structure/jsmap_maps/manage/' . $map,
'bundle argument' => 4,
'access arguments' => array('administer maps'),
),
);
}
}
class JsmapMap extends Entity {
public $label;
public function __construct($values = array()) {
parent::__construct($values, 'jsmap_map');
}
}
/**
* The Controller for maps
*/
class JsmapMapController extends EntityAPIControllerExportable {
public function __construct($entityType) {
parent::__construct($entityType);
}
/**
* Create a map - we first set up the values that are specific
* to our map schema but then also go through the EntityAPIController
* function.
*
* @param $values
* Initial values for our Map
*
* @return
* A map object with all default fields initialized.
*/
public function create(array $values = array()) {
// Add values that are specific to our map
$values += array(
'id' => '',
);
$map = parent::create($values);
//add all regions that are passed into this function
if(!empty($values['regions']) && is_array($values['regions'])){
foreach($values['regions'] as $region){
$region = entity_create('jsmap_region', $region);
entity_save('jsmap_region', $region);
}
}
return $map;
}
/**
* Implements EntityAPIControllerInterface.
*
* @return
* A serialized string in JSON format suitable for the import() method.
*/
public function export($entity, $prefix = '') {
$vars = $this->get_exportable_data($entity);
return entity_var_json_export($vars, $prefix);
}
public function svgExport($entity){
$vars = $this->get_exportable_data($entity);
$dom = new DOMDocument('1.0','utf-8');
$svg = $dom->createElement('svg');
$dom->appendChild($svg);
$svg->setAttribute('version', '1.1');
$svg->setAttribute('id', 'svg2');
$svg->setAttribute('xmlns:svg', 'http://www.w3.org/2000/svg');
$svg->setAttribute('xmlns', 'http://www.w3.org/2000/svg');
$svg->setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
$svg->setAttribute('xml:space', 'preserve');
if(is_array($vars['regions'])){
foreach($vars['regions'] as $region){
$path = $dom->createElement('path');
$path->setAttribute('id',$region['external_id_value']);
$path->setAttribute('d',$region['svg_path']);
$svg->appendChild($path);
}
}
$dom->formatOutput = true;
return $dom->saveXML();
}
private function get_exportable_data($entity){
$vars = get_object_vars($entity);
unset($vars['id']);//unset the auto-increment field
//add all the regions to the entity
$vars['regions'] = array();
$jsmap_region_controller = entity_get_controller('jsmap_region');
if($jsmap_regions = $jsmap_region_controller->load(FALSE, array('map' => $entity->map))){
foreach($jsmap_regions as $jsmap_region){
$region_vars = get_object_vars($jsmap_region);
unset($region_vars['id']);
$vars['regions'][] = $region_vars;
}
}
return $vars;
}
}
class JsmapRegion extends Entity {
public function __construct($values = array()) {
parent::__construct($values, 'jsmap_region');
}
protected function defaultLabel() {
return $this->name;
}
protected function defaultUri() {
return array('path' => 'jsmap_region/'. $this->id);
}
}
/**
* The Controller for region entities
*/
class JsmapRegionController extends EntityAPIController {
public function __construct($entityType) {
parent::__construct($entityType);
}
/**
* Create a region - we first set up the values that are specific
* to our region schema but then also go through the EntityAPIController
* function.
*
* @param $values
* initial values for our region
*
* @return
* A region object with all default fields initialized.
*/
public function create(array $values = array()) {
// Add values that are specific to our region
$values += array(
'id' => '',
'name' => '',
'created' => '',
'changed' => '',
);
$region = parent::create($values);
return $region;
}
}
/**
* Implements hook_permission().
*/
function jsmap_permission() {
// We set up permisssions to manage entity types, manage all entities and the
// permissions for each individual entity
$permissions = array(
'administer maps' => array(
'title' => t('Administer maps'),
'description' => t('Create and delete fields for regions'),
),
'administer regions' => array(
'title' => t('Administer regions'),
'description' => t('Edit and delete all regions'),
),
);
//Generate permissions per map
foreach (jsmap_get_maps() as $map) {
$map_name = check_plain($map->map);
$permissions += array(
"edit any $map_name region" => array(
'title' => t('%map_name: Edit any region', array('%map_name' => $map->label)),
),
"view any $map_name region" => array(
'title' => t('%map_name: View any region', array('%map_name' => $map->label)),
),
);
}
return $permissions;
}
/**
* Determines whether the given user has access to a region.
*
* @param $op
* The operation being performed. One of 'view', 'update', 'create', 'delete'
* or just 'edit' (being the same as 'create' or 'update').
* @param $region
* Optionally a region or a map to check access for. If nothing is
* given, access for all regions is determined.
* @param $account
* The user to check for. Leave it to NULL to check for the global user.
* @return boolean
* Whether access is allowed or not.
*/
function jsmap_region_access($op, $region = NULL, $account = NULL) {
if (user_access('administer regions', $account)) {
return TRUE;
}
if (isset($region) && $map_name = $region->map) {
$op = ($op == 'view') ? 'view' : 'edit';
if (user_access("$op any $map_name region", $account)) {
return TRUE;
}
}
return FALSE;
}
/**
* Access callback for the entity API.
*/
function jsmap_map_access($op, $map = NULL, $account = NULL) {
return user_access('administer maps', $account);
}
/**
* Gets an array of all maps, keyed by the map name.
*
* @param $map_name
* If set, the map with the given name is returned.
* @return Map[]
* Depending whether $map_name isset, an array of maps or a single one.
*/
function jsmap_get_maps($map_name = NULL) {
$maps = entity_load_multiple_by_name('jsmap_map', isset($map_name) ? array($map_name) : FALSE);
return isset($map_name) ? reset($maps) : $maps;
}
/**
* Menu argument loader; Load a map by string.
*
* @param $map
* The machine-readable name of a map to load.
* @return
* A map array or FALSE if $map does not exist.
*/
function jsmap_map_load($map) {
return jsmap_get_maps($map);
}
/**
* Fetch a jsmap_region object.
*
* @param $jsmap_region_id
* Integer specifying the region id.
* @param $reset
* A boolean indicating that the internal cache should be reset.
* @return
* A fully-loaded $jsmap_region object or FALSE if it cannot be loaded.
*
*/
function jsmap_region_load($jsmap_region_id, $reset = FALSE) {
$regions = jsmap_region_load_multiple(array($jsmap_region_id), array(), $reset);
return reset($regions);
}
/**
* Load multiple jsmap_regions based on certain conditions.
*
* @param $jsmap_region_ids
* An array of jsmap_region IDs.
* @param $conditions
* An array of conditions to match against the {jsmap_region} table.
* @param $reset
* A boolean indicating that the internal cache should be reset.
* @return
* An array of jsmap_region objects, indexed by jsmap_region_id.
*
* @see entity_load()
* @see jsmap_region_load()
*/
function jsmap_region_load_multiple($jsmap_region_ids = array(), $conditions = array(), $reset = FALSE) {
return entity_load('jsmap_region', $jsmap_region_ids, $conditions, $reset);
}
/**
* Deletes a jsmap_region.
*/
function jsmap_region_delete(JsmapRegion $jsmap_region) {
$jsmap_region->delete();
}
/**
* Delete multiple jsmap_regions.
*
* @param $jsmap_region_ids
* An array of jsmap_region IDs.
*/
function jsmap_region_delete_multiple(array $jsmap_region_ids) {
entity_get_controller('jsmap_region')->delete($jsmap_region_ids);
}
/**
* Create a jsmap_region object.
*/
function jsmap_region_create($values = array()) {
return entity_get_controller('jsmap_region')->create($values);
}
/**
* Saves a map to the db.
*/
function jsmap_map_save(JsmapMap $map) {
$map->save();
}
/**
* Deletes a map from the db.
*/
function jsmap_map_delete(JsmapMap $map) {
$map->delete();
}
/**
* Responds to deletion of a jsmap_map entity
*/
function jsmap_jsmap_map_delete(JsmapMap $map) {
if($regions = entity_load('jsmap_region', FALSE, array('map' => $map->map))){
foreach($regions as $region){
$region->delete();
}
}
}
/**
* URI callback for jsmap_regions
*/
function jsmap_region_uri(JsmapRegion $jsmap_region){
return array(
'path' => 'jsmap_region/' . $jsmap_region->id,
);
}
/**
* Menu title callback for showing individual entities
*/
function jsmap_region_page_title(JsmapRegion $jsmap_region){
return $jsmap_region->name;
}