Slide 1

Slide 1 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Writing OOP Modules for Drupal 7 John Bafford! http://bafford.com — @jbafford — john@bafford.com ! ! The Brick Factory! http://thebrickfactory.com — @thebrickfactory — jbafford@thebrickfactory.com 1

Slide 2

Slide 2 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Who Am I? • John Bafford
 Vice President, Programming Services
 The Brick Factory • PHP developer since 1999 • Programmer since 1990 2

Slide 3

Slide 3 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 The Brick Factory • Full Service Web Development Shop • Farragut North, DC • We do lots of Drupal sites 3

Slide 4

Slide 4 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Drupal 8 is Coming! • Second beta release October 15 • Huge API changes • Lots of work 4

Slide 5

Slide 5 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 So What Do You Do? 5

Slide 6

Slide 6 text

Well, as much as possible, anyway. Be Lazy.

Slide 7

Slide 7 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 How? • I’m glad you asked. • Write Drupal 8-style OOP modules for Drupal 6/7 • Case study of one implementation 7

Slide 8

Slide 8 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 (website demo) 8

Slide 9

Slide 9 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Basic Concept • Custom Node Type • Map page and AJAX response • Custom Templates • JavaScript 9

Slide 10

Slide 10 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 What We Don’t Want 10

Slide 11

Slide 11 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 (movie of bad code omitted) 11

Slide 12

Slide 12 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Separation of Concerns • One of the most important OOP design principles • Each unit of code has one, and only one, responsibility 12

Slide 13

Slide 13 text

function custom_ecard_user_validate($form, &$form_state) { if (!empty($form_state['values']['tbf_field'])) { form_set_error('', t('An error occured with processing the form.')); } $siteUrl = $form_state['values']['field_siteurl']['und'][0]['value']; if(!ctype_alnum($siteUrl) && !empty($siteUrl)) { form_set_error('', t('The site url can only contain letters and numbers.')); } else { // check to make sure no other site is using this site url if(isset($form_state['user']->uid)) { $result = db_query("SELECT entity_id FROM field_data_field_siteurl WHERE entity_type=:fieldtype AND field_siteurl_value=:siteurl AND entity_id!=:user_id", array( ':fieldtype' => 'user', ':siteurl' => $siteUrl, ':user_id' => (int)$form_state['user']->uid, )); } else { $result = db_query("SELECT entity_id FROM field_data_field_siteurl WHERE entity_type=:fieldtype AND field_siteurl_value=:siteurl", array( ':fieldtype' => 'user', ':siteurl' => $siteUrl, )); } if($result->rowCount() > 0) { form_set_error('', t('The site url "' . $siteUrl . '" is already in use.')); } else { $path = array( 'source' => 'ecards/' . $siteUrl, 'alias' => $siteUrl, ); path_save($path); } } }

Slide 14

Slide 14 text

function custom_ecard_site_url_in_use($siteUrl, $user) { $params = array( ':fieldtype' => 'user', ':siteurl' => $siteUrl, ); $crit = ''; if($user) { $crit = 'AND entity_id != :user_id'; $params[':user_id'] = (int)$user->userId; } $result = db_query("SELECT entity_id FROM field_data_field_siteurl WHERE entity_type=:fieldtype AND field_siteurl_value=:siteurl $crit", $params); return ($result->rowCount() > 0); } ! ! function custom_ecard_user_validate($form, &$form_state) { if (!empty($form_state['values']['tbf_field'])) { form_set_error('', t('An error occured with processing the form.')); } $siteUrl = $form_state['values']['field_siteurl']['und'][0]['value']; if(!ctype_alnum($siteUrl) && !empty($siteUrl)) { form_set_error('', t('The site url can only contain letters and numbers.')); } else if(custom_ecard_site_url_in_use($siteUrl, $form_state['user'])) { form_set_error('', t('The site url "' . $siteUrl . '" is already in use.’)); } else { $path = array( 'source' => 'ecards/' . $siteUrl, 'alias' => $siteUrl, ); path_save($path); } }

Slide 15

Slide 15 text

require_once('src/Entity/IranVictim.php'); require_once('src/Form/IranVictimForm.php'); require_once('src/Controller/VictimController.php'); function uani_victims_menu() { return array( 'veritas' => array( 'title' => "Iran VERITAS Project: Documenting Iran's Violence, Extremism, Repression and Terror", 'page callback' => 'VictimController::victimsAction', 'access callback' => true, 'type' => MENU_NORMAL_ITEM, ), 'veritas/_ajax/victim' => array( 'title' => 'Victim Ajax Callback', 'page callback' => 'VictimController::ajaxVictimAction', 'page arguments' => array(3), 'access callback' => true, 'type' => MENU_CALLBACK, ), ); } ! function uani_victims_node_info() { return array( IranVictim::NODE_TYPE => IranVictim::nodeInfo(), ); } function uani_victims_perm() { return IranVictim::perms(); } function uani_victims_form($node, $form_state) { return IranVictimForm::form($node, $form_state); } ! ! ! ! ! ! ! function uani_victims_view($node, $teaser = false, $page = false) { if($page && $node && $node->type == 'uani_victims') { drupal_goto(VictimController::victimUrl($node)); exit; } return $node; } function uani_victims_theme($existing, $type, $theme, $path) { $helper = 'templates/uani_victims_helper.tpl.php'; return array( 'uani_victims' => array( 'template' => 'templates/uani_victims', 'file' => $helper, 'arguments' => array( 'parameters' => null, ), ), 'uani_victim_list_individual' => array( 'template' => 'templates/ uani_victim_list_individual', 'file' => $helper, 'arguments' => array( 'victim' => null, ), ), 'uani_victim_overlay' => array( 'template' => 'templates/uani_victim_overlay', 'file' => $helper, 'arguments' => array( 'parameters' => null, ), ), ); }

Slide 16

Slide 16 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 16

Slide 17

Slide 17 text

! === 6/uani_victims.install ===! function uani_victims_schema() { return array( 'uani_victims' => array( 'fields' => array( 'vid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), 'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), ), 'primary key' => array('vid', 'nid'), ), ); } ! ! ! ! === 6/src/Form/IranVictimForm.php ===! ! class IranVictimForm { public static function form($node, $form_state) { $type = node_get_types('type', $node); $form = array(); $form['title'] = array( '#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5 ); $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count); return $form; } } ! ! ! === 6/src/Entity/IranVictim.php ===! ! class IranVictim { const NODE_TYPE = 'uani_victims'; const FIELD_VIDEO = 'field_uani_iran_video'; const IMAGE_LIST = 'field_uani_iran_list_image'; const IMAGE_CONTENT = 'field_uani_iran_image'; public static function victimTypes() { return array( 'foreign' => 'Global Victims', 'usa' => 'U.S. Victims', 'iran' => 'Iranian Victims', ); } public static function placemarkTypes() { return array( '' => 'None', 'usa' => 'USA', 'foreign' => 'Foreign Country', 'iran_domestic' => 'Iran Domestic', 'us_right' => 'US Flag Right', 'us_middle' => 'US Flag Middle', 'us_left' => 'US Flag Left', ); } public static function nodeInfo() { return array( 'name' => 'Iran Victims', 'module' => VictimController::MODULE_NAME, 'description' => 'Iran Victims', 'has_title' => true, 'title_label' => 'Title', 'has_body' => true, 'body_label' => 'Content', 'min_word_count' => 0, ); } }

Slide 18

Slide 18 text

! === 6/uani_victims.install ===! function uani_victims_schema() { return array( 'uani_victims' => array( 'fields' => array( 'vid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), 'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), ), 'primary key' => array('vid', 'nid'), ), ); } ! ! ! ! === 6/src/Form/IranVictimForm.php ===! ! class IranVictimForm { public static function form($node, $form_state) { $type = node_get_types('type', $node); $form = array(); $form['title'] = array( '#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5 ); $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count); return $form; } } ! ! ! === 6/src/Entity/IranVictim.php ===! ! class IranVictim { const NODE_TYPE = 'uani_victims'; const FIELD_VIDEO = 'field_uani_iran_video'; const IMAGE_LIST = 'field_uani_iran_list_image'; const IMAGE_CONTENT = 'field_uani_iran_image'; public static function victimTypes() { return array( 'foreign' => 'Global Victims', 'usa' => 'U.S. Victims', 'iran' => 'Iranian Victims', ); } public static function placemarkTypes() { return array( '' => 'None', 'usa' => 'USA', 'foreign' => 'Foreign Country', 'iran_domestic' => 'Iran Domestic', 'us_right' => 'US Flag Right', 'us_middle' => 'US Flag Middle', 'us_left' => 'US Flag Left', ); } public static function nodeInfo() { return array( 'name' => 'Iran Victims', 'module' => VictimController::MODULE_NAME, 'description' => 'Iran Victims', 'has_title' => true, 'title_label' => 'Title', 'has_body' => true, 'body_label' => 'Content', 'min_word_count' => 0, ); } }

Slide 19

Slide 19 text

! === 6/uani_victims.install ===! function uani_victims_schema() { return array( 'uani_victims' => array( 'fields' => array( 'vid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), 'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0), ), 'primary key' => array('vid', 'nid'), ), ); } ! ! ! ! === 6/src/Form/IranVictimForm.php ===! ! class IranVictimForm { public static function form($node, $form_state) { $type = node_get_types('type', $node); $form = array(); $form['title'] = array( '#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5 ); $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count); return $form; } } ! ! ! === 6/src/Entity/IranVictim.php ===! ! class IranVictim { const NODE_TYPE = 'uani_victims'; const FIELD_VIDEO = 'field_uani_iran_video'; const IMAGE_LIST = 'field_uani_iran_list_image'; const IMAGE_CONTENT = 'field_uani_iran_image'; public static function victimTypes() { return array( 'foreign' => 'Global Victims', 'usa' => 'U.S. Victims', 'iran' => 'Iranian Victims', ); } public static function placemarkTypes() { return array( '' => 'None', 'usa' => 'USA', 'foreign' => 'Foreign Country', 'iran_domestic' => 'Iran Domestic', 'us_right' => 'US Flag Right', 'us_middle' => 'US Flag Middle', 'us_left' => 'US Flag Left', ); } public static function nodeInfo() { return array( 'name' => 'Iran Victims', 'module' => VictimController::MODULE_NAME, 'description' => 'Iran Victims', 'has_title' => true, 'title_label' => 'Title', 'has_body' => true, 'body_label' => 'Content', 'min_word_count' => 0, ); } }

Slide 20

Slide 20 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 20

Slide 21

Slide 21 text

=== 6/src/Controller/VictimController.php ===! ! class VictimController { const MODULE_NAME = 'uani_victims'; public static function legendOrder() { return array('all', 'usa', 'iran', 'foreign'); } public static function getVictims() { $arr = array(); $res = db_query("SELECT nid from {node} where type='%s' ORDER BY title", IranVictim::NODE_TYPE); while($o = mysql_fetch_object($res)) { $node = node_load($o->nid); $arr[] = $node; } return $arr; } protected static function getVictimIdFromPath($path) { $res = db_query("SELECT nid from {content_type_uani_victims} uv WHERE uv.field_uani_iran_path_value='%s'", $path); $o = mysql_fetch_object($res); if($o) return $o->nid; else return null; } ! ! public static function getVictimFromId($id) { if(!is_numeric($id) || (int)$id != $id) $id = self::getVictimIdFromPath($id); return node_load($id); } protected static function parsePoly($polyStr) { $arr = array(); foreach(explode("\n", $polyStr) as $coord) { if(preg_match('/(\d+\.?\d*)\s*,\s*(\d+\.? \d*)/', $coord, $out)) $arr[] = array((float)$out[1], (float) $out[2]); } if($arr) return $arr; else return null; } ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! public static function getPlacemarks() { $arr = array(); $res = db_query("SELECT n.title, uv.* FROM {node} n INNER JOIN {content_type_uani_victims} uv ON n.vid=uv.vid WHERE n.type='%s' AND uv.field_uani_iran_placemark_type_value <> '' ", IranVictim::NODE_TYPE); while($o = mysql_fetch_object($res)) { if($o->field_uani_iran_placemark_type_value == 'iran_domestic') $poly = self::parsePoly($o- >field_uani_iran_placemark_poly_value); else $poly = $o- >field_uani_iran_placemark_poly_value; $arr[] = array( 'victimType' => $o- >field_uani_iran_victim_type_value, 'type' => $o- >field_uani_iran_placemark_type_value, 'name' => $o->title, 'cc' => $o- >field_uani_iran_foreign_country_value, 'lat' => $o- >field_uani_iran_placemark_lat_value, 'lon' => $o- >field_uani_iran_placemark_lon_value, 'poly' => $poly, 'color' => $o- >field_uani_iran_placemark_poly_c_value, 'nid' => $o->nid, ); } return $arr; } ! public static function victimUrl($victim) { $url = '/veritas/'; if(!empty($victim->field_uani_iran_path[0] ['value'])) $url .= $victim->field_uani_iran_path[0] ['value']; else $url .= $victim->nid; return $url; } ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! protected static function getVictimsData($victims, $showVictimID) { $placemarks = array_merge(self::getPlacemarks(), array( array( 'type' => 'iran_zoom', 'name' => '', 'lat' => 32.4, 'lon' => 54, ), )); $victimTypes = array_keys(IranVictim::victimTypes()); $victimTypes[] = 'all'; $nav = array_fill_keys($victimTypes, array()); foreach($victims as $victim) { $victimType = $victim- >field_uani_iran_victim_type[0]['value']; $ns = array( 'nid' => $victim->nid, 'type' => $victimType, 'name' => $victim->title, 'date' => $victim- >field_uani_iran_sort_date[0]['value'], 'url' => self::victimUrl($victim), ); $nav[$victimType][] = $ns; $nav['all'][] = $ns; } $victimSortOrder = array( 'usa' => 0, 'foreign' => 1, 'iran' => 2, ); foreach($nav as $navType => &$navs) { usort($navs, function($a, $b) use($victimSortOrder) { $aord = $victimSortOrder[$a['type']]; $bord = $victimSortOrder[$b['type']]; if($aord != $bord) return $aord - $bord; if($a['type'] == 'usa') return -strcmp($a['date'], $b['date']); else return strcmp($a['name'], $b['name']); }); } return array('UANIVictims' => array( 'basePath' => '/' . drupal_get_path('module', 'uani_victims'), 'placemarks' => $placemarks, 'nav' => $nav, 'initialVictim' => $showVictimID, )); } ! ! public static function ajaxVictimAction($id) { $node = self::getVictimFromId($id); if(!$node || $node->type != IranVictim::NODE_TYPE) return drupal_not_found(); header('Content-type: application/json'); echo json_encode(array( 'id' => $node->nid, 'view' => theme('uani_victim_overlay', array( 'victim' => $node, 'victimURL' => self::victimUrl($node), )), 'url' => $url, 'title' => "Iran VERITAS Project: Documenting Iran's Violence, Extremism, Repression and Terror | UANI | " . $node->title, 'type' => $node- >field_uani_iran_victim_type[0]['value'], )); exit; } ! public static function victimsAction() { $showVictimID = arg(1); $victims = self::getVictims(); $victimsSorted = array( 'all' => array(), 'usa' => array(), 'foreign' => array(), 'iran' => array(), ); foreach($victims as $v) $victimsSorted[$v- >field_uani_iran_victim_type[0]['value']][] = $v; foreach($victimsSorted as $type => &$victimList) { usort($victimList, function($a, $b) use($type) { if($type == 'usa') return -strcmp($a- >field_uani_iran_sort_date[0]['value'], $b- >field_uani_iran_sort_date[0]['value']); else return strcmp($a->title, $b->title); }); } $modulePath = drupal_get_path('module', 'uani_victims'); drupal_add_js($modulePath . '/js/ jquery.history.js', 'module'); drupal_add_js($modulePath . '/js/uani_victims.js', 'module'); drupal_add_css($modulePath . '/templates/ uani_victims.css', 'module'); drupal_add_js(self::getVictimsData($victims, $showVictimID), 'setting'); $types = IranVictim::victimTypes(); $types['all'] = 'Home'; $victimTypes = array(); foreach(self::legendOrder() as $type) $victimTypes[$type] = $types[$type]; return theme('uani_victims', array( 'victims' => $victims, 'victimsSorted' => $victimsSorted, 'victimTypes' => $victimTypes, )); } } ! ! ! ! ! ! !

Slide 22

Slide 22 text

! public static function getVictims() { $arr = array(); $res = db_query("SELECT nid from {node} where type='%s' ORDER BY title", IranVictim::NODE_TYPE); while($o = mysql_fetch_object($res)) { $node = node_load($o->nid); $arr[] = $node; } return $arr; } ! protected static function getVictimIdFromPath($path) { $res = db_query("SELECT nid from {content_type_uani_victims} uv WHERE uv.field_uani_iran_path_value='%s'", $path); $o = mysql_fetch_object($res); if($o) return $o->nid; else return null; } ! public static function getVictimFromId($id) { if(!is_numeric($id) || (int)$id != $id) $id = self::getVictimIdFromPath($id); return node_load($id); } ! ! ! ! ! ! ! public static function getPlacemarks() { $arr = array(); $res = db_query("SELECT n.title, uv.* FROM {node} n INNER JOIN {content_type_uani_victims} uv ON n.vid=uv.vid WHERE n.type='%s' AND uv.field_uani_iran_placemark_type_value <> '' ", IranVictim::NODE_TYPE); while($o = mysql_fetch_object($res)) { if($o->field_uani_iran_placemark_type_value == 'iran_domestic') $poly = self::parsePoly($o- >field_uani_iran_placemark_poly_value); else $poly = $o->field_uani_iran_placemark_poly_value; $arr[] = array( 'victimType' => $o- >field_uani_iran_victim_type_value, 'type' => $o->field_uani_iran_placemark_type_value, 'name' => $o->title, 'cc' => $o->field_uani_iran_foreign_country_value, 'lat' => $o->field_uani_iran_placemark_lat_value, 'lon' => $o->field_uani_iran_placemark_lon_value, 'poly' => $poly, 'color' => $o- >field_uani_iran_placemark_poly_c_value, 'nid' => $o->nid, ); } return $arr; } ! ! ! ! !

Slide 23

Slide 23 text

! public static function legendOrder() { return array('all', 'usa', 'iran', 'foreign'); } ! ! ! ! ! ! ! ! ! ! ! ! protected static function parsePoly($polyStr) { $arr = array(); foreach(explode("\n", $polyStr) as $coord) { if(preg_match('/(\d+\.?\d*)\s*,\s*(\d+\.? \d*)/', $coord, $out)) $arr[] = array((float)$out[1], (float) $out[2]); } if($arr) return $arr; else return null; } ! ! ! ! ! ! ! public static function victimUrl($victim) { $url = '/veritas/'; if(!empty($victim->field_uani_iran_path[0] ['value'])) $url .= $victim->field_uani_iran_path[0] ['value']; else $url .= $victim->nid; return $url; } ! ! ! ! ! ! ! ! ! ! ! ! ! ! !

Slide 24

Slide 24 text

=== 6/src/Controller/VictimController.php ===! ! public static function ajaxVictimAction($id) { $node = self::getVictimFromId($id); if(!$node || $node->type != IranVictim::NODE_TYPE) return drupal_not_found(); header('Content-type: application/json'); echo json_encode(array( 'id' => $node->nid, 'view' => theme('uani_victim_overlay', array( 'victim' => $node, 'victimURL' => self::victimUrl($node), )), 'url' => $url, 'title' => "Iran VERITAS Project: Documenting Iran's Violence, Extremism, Repression and Terror | UANI | " . $node->title, 'type' => $node->field_uani_iran_victim_type[0]['value'], )); exit; } !

Slide 25

Slide 25 text

=== 6/src/Controller/VictimController.php ===! ! protected static function getVictimsData($victims, $showVictimID) { $placemarks = array_merge(self::getPlacemarks(), array( array( 'type' => 'iran_zoom', 'name' => '', 'lat' => 32.4, 'lon' => 54, ), )); $victimTypes = array_keys(IranVictim::victimTypes()); $victimTypes[] = 'all'; $nav = array_fill_keys($victimTypes, array()); foreach($victims as $victim) { $victimType = $victim->field_uani_iran_victim_type[0]['value']; $ns = array( 'nid' => $victim->nid, 'type' => $victimType, 'name' => $victim->title, 'date' => $victim->field_uani_iran_sort_date[0]['value'], 'url' => self::victimUrl($victim), ); $nav[$victimType][] = $ns; $nav['all'][] = $ns; } $victimSortOrder = array( 'usa' => 0, 'foreign' => 1, 'iran' => 2, ); foreach($nav as $navType => &$navs) { usort($navs, function($a, $b) use($victimSortOrder) { $aord = $victimSortOrder[$a['type']]; $bord = $victimSortOrder[$b['type']]; if($aord != $bord) return $aord - $bord; if($a['type'] == 'usa') return -strcmp($a['date'], $b['date']); else return strcmp($a['name'], $b['name']); }); } return array('UANIVictims' => array( 'basePath' => '/' . drupal_get_path('module', 'uani_victims'), 'placemarks' => $placemarks, 'nav' => $nav, 'initialVictim' => $showVictimID, )); } ! ! ! public static function victimsAction() { $showVictimID = arg(1); $victims = self::getVictims(); $victimsSorted = array( 'all' => array(), 'usa' => array(), 'foreign' => array(), 'iran' => array(), ); foreach($victims as $v) $victimsSorted[$v->field_uani_iran_victim_type[0]['value']][] = $v; foreach($victimsSorted as $type => &$victimList) { usort($victimList, function($a, $b) use($type) { if($type == 'usa') return -strcmp($a->field_uani_iran_sort_date[0]['value'], $b- >field_uani_iran_sort_date[0]['value']); else return strcmp($a->title, $b->title); }); } $modulePath = drupal_get_path('module', 'uani_victims'); drupal_add_js($modulePath . '/js/jquery.history.js', 'module'); drupal_add_js($modulePath . '/js/uani_victims.js', 'module'); drupal_add_css($modulePath . '/templates/uani_victims.css', 'module'); drupal_add_js(self::getVictimsData($victims, $showVictimID), 'setting'); $types = IranVictim::victimTypes(); $types['all'] = 'Home'; $victimTypes = array(); foreach(self::legendOrder() as $type) $victimTypes[$type] = $types[$type]; return theme('uani_victims', array( 'victims' => $victims, 'victimsSorted' => $victimsSorted, 'victimTypes' => $victimTypes, )); } } ! ! ! ! ! ! ! ! ! ! ! ! ! !

Slide 26

Slide 26 text

=== 6/src/Controller/VictimController.php ===! ! public static function ajaxVictimAction($id) { $node = self::getVictimFromId($id); if(!$node || $node->type != IranVictim::NODE_TYPE) return drupal_not_found(); header('Content-type: application/json'); echo json_encode(array( 'id' => $node->nid, 'view' => theme('uani_victim_overlay', array( 'victim' => $node, 'victimURL' => self::victimUrl($node), )), 'url' => $url, 'title' => "Iran VERITAS Project: Documenting Iran's Violence, Extremism, Repression and Terror | UANI | " . $node->title, 'type' => $node->field_uani_iran_victim_type[0]['value'], )); exit; } ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! public static function victimsAction() { $showVictimID = arg(1); $victims = self::getVictims(); $victimsSorted = array( 'all' => array(), 'usa' => array(), 'foreign' => array(), 'iran' => array(), ); foreach($victims as $v) $victimsSorted[$v->field_uani_iran_victim_type[0]['value']][] = $v; foreach($victimsSorted as $type => &$victimList) { usort($victimList, function($a, $b) use($type) { if($type == 'usa') return -strcmp($a->field_uani_iran_sort_date[0]['value'], $b->field_uani_iran_sort_date[0]['value']); else return strcmp($a->title, $b->title); }); } $modulePath = drupal_get_path('module', 'uani_victims'); drupal_add_js($modulePath . '/js/jquery.history.js', 'module'); drupal_add_js($modulePath . '/js/uani_victims.js', 'module'); drupal_add_css($modulePath . '/templates/uani_victims.css', 'module'); drupal_add_js(self::getVictimsData($victims, $showVictimID), 'setting'); $types = IranVictim::victimTypes(); $types['all'] = 'Home'; $victimTypes = array(); foreach(self::legendOrder() as $type) $victimTypes[$type] = $types[$type]; return theme('uani_victims', array( 'victims' => $victims, 'victimsSorted' => $victimsSorted, 'victimTypes' => $victimTypes, )); }

Slide 27

Slide 27 text

(yay!) We have a Drupal 6 Module now

Slide 28

Slide 28 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Let’s Upgrade to Drupal 7! 28

Slide 29

Slide 29 text

=== 7/uani_victims.install ===! ! function uani_victims_schema() { return array( 'iran_victims' => array( 'fields' => [ 'ivid' => ['type' => 'serial', 'unsigned' => true], ‘bundle_type' => ['type' => 'varchar', 'length' => 255, 'not null' => true], 'name' => ['type' => 'varchar', 'length' => 255, 'not null' => true], 'description' => ['type' => 'text', 'not null' => true], 'path' => ['type' => 'varchar', 'length' => 255, 'not null' => true, 'default' => ''], 'type' => ['type' => 'varchar', 'length' => 15, 'not null' => true], 'placemark_type' => ['type' => 'varchar', 'length' => 15, 'not null' => true], 'country_code' => ['type' => 'varchar', 'length' => 2, 'not null' => true, 'default' => ''], 'placemark_lat' => ['type' => 'float', 'not null' => true, 'default' => 0], 'placemark_lon' => ['type' => 'float', 'not null' => true, 'default' => 0], 'poly_region' => ['type' => 'text', 'not null' => true], 'poly_color' => ['type' => 'varchar', 'length' => 31, 'not null', 'default' => ''], 'sort_date' => ['type' => 'int', 'not null' => true, 'default' => 0], 'start_date' => ['type' => 'varchar', 'length' => 255, 'not null' => true, 'default' => ''], 'end_date' => ['type' => 'varchar', 'length' => 255, 'not null' => true, 'default' => ''], ], 'primary key' => ['ivid'], ), ); } ! !

Slide 30

Slide 30 text

=== 6/src/Entity/IranVictim.php ===! ! public static function nodeInfo() { return array( 'name' => 'Iran Victims', 'module' => VictimController::MODULE_NAME, 'description' => 'Iran Victims', 'has_title' => true, 'title_label' => 'Title', 'has_body' => true, 'body_label' => 'Content', 'min_word_count' => 0, ); }

Slide 31

Slide 31 text

=== 6/src/Entity/IranVictim.php ===! ! public static function nodeInfo() { return array( 'name' => 'Iran Victims', 'module' => VictimController::MODULE_NAME, 'description' => 'Iran Victims', 'has_title' => true, 'title_label' => 'Title', 'has_body' => true, 'body_label' => 'Content', 'min_word_count' => 0, ); } === 7/src/Entity/IranVictim.php ===! ! public static function nodeInfo() { return array( 'label' => t('Iran Victim'), 'description' => t('An entity type used for Iran victims'), 'entity class' => 'IranVictim', 'controller class' => 'EntityAPIController', 'base table' => 'iran_victims', 'fieldable' => true, 'entity keys' => [ 'id' => 'ivid', 'label' => 'name', 'bundle' => 'bundle_type', ], 'bundles' => [ 'iran_victims' => [ 'label' => 'Iran Victims', 'admin' => [ 'path' => 'admin/structure/ iran_victims/manage', ] ], ], 'module' => VictimController::MODULE_NAME, 'access callback' => 'uani_victims_access_callback', 'admin ui' => [ 'path' => 'admin/content/iran_victims', 'controller class' => 'VictimUIController', ], ); }

Slide 32

Slide 32 text

=== 7/src/Form/IranVictimForm.php ===! class IranVictimForm { public static function form($form, $form_state, $value = null) { $fields = [ 'name' => [ '#title' => 'Name', '#type' => 'textfield', '#required' => true, ], 'description' => [ '#title' => 'Description', '#type' => 'textarea', '#required' => true, ], 'path' => [ '#title' => 'Path', '#type' => 'textfield', '#required' => true, ], 'type' => [ '#title' => 'Type', '#type' => 'select', '#options' => IranVictim::victimTypes(), '#required' => true, ], 'placemark_type' => [ '#title' => 'Placemark Type', '#type' => 'select', '#options' => IranVictim::placemarkTypes(), '#required' => true, ], 'country_code' => [ '#title' => 'Country Code', '#type' => 'textfield', '#required' => false, ], 'placemark_lat' => [ '#title' => 'Placemark Latitude', '#type' => 'textfield', '#required' => false, ], 'placemark_lon' => [ '#title' => 'Placemark Longitude', '#type' => 'textfield', '#required' => false, ], 'poly_region' => [ '#title' => 'Poly Region', '#type' => 'textarea', '#required' => false, ], 'poly_color' => [ '#title' => 'Poly Color', '#type' => 'textfield', '#required' => false, ], 'sort_date' => [ '#title' => 'Sort Date', '#type' => 'textfield', '#required' => false, ], 'start_date' => [ '#title' => 'Start Date', '#type' => 'textfield', '#required' => false, ], 'end_date' => [ '#title' => 'End Date', '#type' => 'textfield', '#required' => false, ], ]; foreach(array_keys($fields) as $k) $fields[$k]['#default_value'] = (isset($value->$k) ? $value-> $k : ''); $fields['submit'] = [ '#type' => 'submit', '#value' => 'Save', ]; return array_merge($form, $fields); } } ! ! !

Slide 33

Slide 33 text

=== 6/uani_victims.module ===! ! function uani_victims_node_info() { return array( IranVictim::NODE_TYPE => IranVictim::nodeInfo(), ); } function uani_victims_form($node, $form_state) { return IranVictimForm::form($node, $form_state); } function uani_victims_view($node, $teaser = false, $page = false) { if($page && $node && $node->type == 'uani_victims') { drupal_goto(VictimController::victimUrl($node )); exit; } return $node; }

Slide 34

Slide 34 text

=== 6/uani_victims.module ===! ! function uani_victims_node_info() { return array( IranVictim::NODE_TYPE => IranVictim::nodeInfo(), ); } function uani_victims_form($node, $form_state) { return IranVictimForm::form($node, $form_state); } function uani_victims_view($node, $teaser = false, $page = false) { if($page && $node && $node->type == 'uani_victims') { drupal_goto(VictimController::victimUrl($node )); exit; } return $node; } === 7/uani_victims.module ===! function uani_victims_entity_info() { return array( IranVictim::NODE_TYPE => IranVictim::nodeInfo(), ); } function iran_victims_form($form, $form_state, $value = null) { return IranVictimForm::form($form, $form_state, $value); } function iran_victims_form_submit(&$form, & $form_state) { $victim = entity_ui_form_submit_build_entity($form, $form_state); $victim->save(); $form_state['redirect'] = 'admin/content/ iran_victims'; }

Slide 35

Slide 35 text

=== 6/src/Controller/VictimController.php === ! public static function getVictims() { $arr = array(); $res = db_query("SELECT nid from {node} where type='%s' ORDER BY title", IranVictim::NODE_TYPE); while($o = mysql_fetch_object($res)) { $node = node_load($o->nid); $arr[] = $node; } return $arr; } protected static function getVictimIdFromPath($path) { $res = db_query("SELECT nid from {content_type_uani_victims} uv WHERE uv.field_uani_iran_path_value='%s'", $path); $o = mysql_fetch_object($res); if($o) return $o->nid; else return null; } public static function getVictimFromId($id) { if(!is_numeric($id) || (int)$id != $id) $id = self::getVictimIdFromPath($id); return node_load($id); }

Slide 36

Slide 36 text

=== 6/src/Controller/VictimController.php === ! public static function getVictims() { $arr = array(); $res = db_query("SELECT nid from {node} where type='%s' ORDER BY title", IranVictim::NODE_TYPE); while($o = mysql_fetch_object($res)) { $node = node_load($o->nid); $arr[] = $node; } return $arr; } protected static function getVictimIdFromPath($path) { $res = db_query("SELECT nid from {content_type_uani_victims} uv WHERE uv.field_uani_iran_path_value='%s'", $path); $o = mysql_fetch_object($res); if($o) return $o->nid; else return null; } public static function getVictimFromId($id) { if(!is_numeric($id) || (int)$id != $id) $id = self::getVictimIdFromPath($id); return node_load($id); } === 7/src/Controller/VictimController.php === public static function getVictims() { $query = new EntityFieldQuery; $result = $query ->entityCondition('entity_type', IranVictim::NODE_TYPE) ->propertyOrderBy('name') ->execute(); if(isset($result[IranVictim::NODE_TYPE])) { $ids = array_keys($result[IranVictim::NODE_TYPE]); return self::getMultipleVictims($ids); } return []; } protected static function getVictimIdFromPath($path) { $query = new EntityFieldQuery; $result = $query ->entityCondition('entity_type', IranVictim::NODE_TYPE) ->propertyCondition('path', $path) ->execute(); if(!empty($result[IranVictim::NODE_TYPE])) return array_keys($result[IranVictim::NODE_TYPE])[0]; else return null; } public static function getVictimFromId($id) { if(!is_numeric($id) || (int)$id != $id) $id = self::getVictimIdFromPath($id); return self::getOneVictim($id); }

Slide 37

Slide 37 text

=== 6/src/Controller/VictimController.php === public static function getPlacemarks() { $arr = array(); $res = db_query("SELECT n.title, uv.* FROM {node} n INNER JOIN {content_type_uani_victims} uv ON n.vid=uv.vid WHERE n.type='%s' AND uv.field_uani_iran_placemark_type_value <> '' ", IranVictim::NODE_TYPE); while($o = mysql_fetch_object($res)) {…} return $arr; } ! ! ! ! ! ! ! ! ! ! !

Slide 38

Slide 38 text

=== 6/src/Controller/VictimController.php === public static function getPlacemarks() { $arr = array(); $res = db_query("SELECT n.title, uv.* FROM {node} n INNER JOIN {content_type_uani_victims} uv ON n.vid=uv.vid WHERE n.type='%s' AND uv.field_uani_iran_placemark_type_value <> '' ", IranVictim::NODE_TYPE); while($o = mysql_fetch_object($res)) {…} return $arr; } ! ! ! ! ! ! ! ! ! ! ! === 7/src/Controller/VictimController.php === public static function getPlacemarks() { $query = new EntityFieldQuery; $result = $query ->entityCondition('entity_type', IranVictim::NODE_TYPE) ->execute(); $arr = []; if(isset($result[IranVictim::NODE_TYPE])) { $ids = array_keys($result[IranVictim::NODE_TYPE]); foreach(self::getMultipleVictims($ids) as $o) {…} } return $arr; } ! ! ! ! ! ! ! ! ! ! ! ! !

Slide 39

Slide 39 text

=== 6/src/Controller/VictimController.php === ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! === 7/src/Controller/VictimController.php === public static function getOneVictim($id) { $victim = entity_load(IranVictim::NODE_TYPE, [$id]); if(isset($victim[$id])) return $victim[$id]; return null; } public static function getMultipleVictims($ids) { if(!is_array($ids)) $ids = [$ids]; return entity_load(IranVictim::NODE_TYPE, $ids); } ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !

Slide 40

Slide 40 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 What about the CCK fields? 40

Slide 41

Slide 41 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Magic 41

Slide 42

Slide 42 text

“__get() is utilized for reading data from inaccessible properties.” public mixed __get ( string $name )

Slide 43

Slide 43 text

=== 6/src/Controller/VictimController.php ===! ! foreach($victims as $victim) { $victimType = $victim- >field_uani_iran_victim_type[0]['value']; $ns = array( 'nid' => $victim->nid, 'type' => $victimType, 'name' => $victim->title, 'date' => $victim- >field_uani_iran_sort_date[0]['value'], 'url' => self::victimUrl($victim), ); $nav[$victimType][] = $ns; $nav['all'][] = $ns; }

Slide 44

Slide 44 text

=== 6/src/Controller/VictimController.php ===! ! foreach($victims as $victim) { $victimType = $victim- >field_uani_iran_victim_type[0]['value']; $ns = array( 'nid' => $victim->nid, 'type' => $victimType, 'name' => $victim->title, 'date' => $victim- >field_uani_iran_sort_date[0]['value'], 'url' => self::victimUrl($victim), ); $nav[$victimType][] = $ns; $nav['all'][] = $ns; } === 7/src/Controller/VictimController.php ===! ! foreach($victims as $victim) { $victimType = $victim- >field_victim_type_value; $ns = array( 'nid' => $victim->nid, 'type' => $victimType, 'name' => $victim->title, 'date' => $victim->field_sort_date_value, 'url' => self::victimUrl($victim), ); $nav[$victimType][] = $ns; $nav['all'][] = $ns; }

Slide 45

Slide 45 text

=== 7/src/Entity/IranVictim.php ===! ! public function __get($name) { static $map = [ 'field_victim_type_value' => 'type', 'nid' => 'ivid', 'body' => 'description', 'title' => 'name', ]; if(isset($map[$name])) return $this->{$map[$name]}; else if(preg_match('/^field_(.*?)_value$/', $name, $match)) { $f = $match[1]; return (isset($this->$f) ? $this->$f : null); } return $this->$name; }

Slide 46

Slide 46 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 (website demo) 46

Slide 47

Slide 47 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 47 Drupal 6: 100%

Slide 48

Slide 48 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 48 Drupal 6: 72% Drupal 7: 27%

Slide 49

Slide 49 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Let’s Upgrade to Drupal 8! 49

Slide 50

Slide 50 text

=== 7/module.info === ! ! name = UANI Victims! description = UANI Custom Code! core = 7.x! ! project = UANI! version = 0.1! ! dependencies[] = date! dependencies[] = entity! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !

Slide 51

Slide 51 text

=== 7/module.info === ! ! name = UANI Victims! description = UANI Custom Code! core = 7.x! ! project = UANI! version = 0.1! ! dependencies[] = date! dependencies[] = entity! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! === 8/module.info.yml === ! ! name: Iran Victims description: UANI Custom Code package: UANI type: module version: 0.1 core: 8.x dependencies: ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !

Slide 52

Slide 52 text

=== 7/uani_victims.module === ! ! function uani_victims_menu() { return array( 'veritas' => array( 'title' => "Iran VERITAS Project: Documenting Iran's Violence, Extremism, Repression and Terror", 'page callback' => 'VictimController::victimsAction', 'access callback' => true, 'type' => MENU_NORMAL_ITEM, ), 'veritas/_ajax/victim' => array( 'title' => 'Victim Ajax Callback', 'page callback' => 'VictimController::ajaxVictimAction', 'page arguments' => array(3), 'access callback' => true, 'type' => MENU_CALLBACK, ), ); } ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !

Slide 53

Slide 53 text

=== 7/uani_victims.module === ! ! function uani_victims_menu() { return array( 'veritas' => array( 'title' => "Iran VERITAS Project: Documenting Iran's Violence, Extremism, Repression and Terror", 'page callback' => 'VictimController::victimsAction', 'access callback' => true, 'type' => MENU_NORMAL_ITEM, ), 'veritas/_ajax/victim' => array( 'title' => 'Victim Ajax Callback', 'page callback' => 'VictimController::ajaxVictimAction', 'page arguments' => array(3), 'access callback' => true, 'type' => MENU_CALLBACK, ), ); } ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! === 8/module.routing.yml === ! veritas: path: /veritas/{id} defaults: _content: \Drupal\uani_victims\Controller \VictimController::victimsAction _title: Iran VERITAS Project: Documenting Iran's Violence, Extremism, Repression and Terror id: null requirements: _access: 'TRUE' veritas.ajax: path: /veritas/_ajax/victim/{id} defaults: _controller: \Drupal\uani_victims\Controller \VictimController::ajaxVictimAction requirements: _access: 'TRUE' uani_victims.victim_list: path: '/admin/content/iran_victim/list' defaults: _entity_list: 'uani_victims_iran_victim' _title: 'Iran Victims List' requirements: _permission: 'view iran victims' uani_victims.victim_add: path: '/admin/content/iran_victim/add' defaults: _entity_form: uani_victims_iran_victim.add _title: 'Add Victim' requirements: _entity_create_access: 'uani_victims_iran_victim' uani_victims.victim_edit: path: '/admin/content/iran_victim/{uani_victims_iran_victim}/edit' defaults: _entity_form: uani_victims_iran_victim.edit _title: 'Edit Victim' requirements: _entity_access: 'uani_victims_iran_victim.edit' uani_victims.victim_delete: path: '/admin/content/iran_victim/{uani_victims_iran_victim}/delete' defaults: _entity_form: uani_victims_iran_victim.delete _title: 'Delete Victim' requirements: _entity_access: 'uani_victims_iran_victim.delete'

Slide 54

Slide 54 text

=== 7/uani_victims.install === ! ! function uani_victims_schema() { return array( 'iran_victims' => array( 'fields' => [ 'ivid' => ['type' => 'serial', 'unsigned' => true], 'name' => ['type' => 'varchar', 'length' => 255, 'not null' => true], 'description' => ['type' => 'text', 'not null' => true], 'path' => ['type' => 'varchar', 'length' => 255, 'not null' => true, 'default' => ''], 'type' => ['type' => 'varchar', 'length' => 15, 'not null' => true], 'placemark_type' => ['type' => 'varchar', 'length' => 15, 'not null' => true], 'country_code' => ['type' => 'varchar', 'length' => 2, 'not null' => true, 'default' => ''], 'placemark_lat' => ['type' => 'float', 'not null' => true, 'default' => 0], 'placemark_lon' => ['type' => 'float', 'not null' => true, 'default' => 0], 'poly_region' => ['type' => 'text', 'not null' => true], 'poly_color' => ['type' => 'varchar', 'length' => 31, 'not null', 'default' => ''], 'sort_date' => ['type' => 'int', 'not null' => true, 'default' => 0], 'start_date' => ['type' => 'varchar', 'length' => 255, 'not null' => true, 'default' => ''], 'end_date' => ['type' => 'varchar', 'length' => 255, 'not null' => true, 'default' => ''], ], 'primary key' => ['ivid'], ), ); } ! ! ! ! !

Slide 55

Slide 55 text

=== 7/uani_victims.install === ! ! function uani_victims_schema() { return array( 'iran_victims' => array( 'fields' => [ 'ivid' => ['type' => 'serial', 'unsigned' => true], ‘bundle_type' => ['type' => 'varchar', 'length' => 255, 'not null' => true], ! 'name' => ['type' => 'varchar', 'length' => 255, 'not null' => true], 'description' => ['type' => 'text', 'not null' => true], 'path' => ['type' => 'varchar', 'length' => 255, 'not null' => true, 'default' => ''], 'type' => ['type' => 'varchar', 'length' => 15, 'not null' => true], 'placemark_type' => ['type' => 'varchar', 'length' => 15, 'not null' => true], 'country_code' => ['type' => 'varchar', 'length' => 2, 'not null' => true, 'default' => ''], 'placemark_lat' => ['type' => 'float', 'not null' => true, 'default' => 0], 'placemark_lon' => ['type' => 'float', 'not null' => true, 'default' => 0], 'poly_region' => ['type' => 'text', 'not null' => true], 'poly_color' => ['type' => 'varchar', 'length' => 31, 'not null', 'default' => ''], 'sort_date' => ['type' => 'int', 'not null' => true, 'default' => 0], 'start_date' => ['type' => 'varchar', 'length' => 255, 'not null' => true, 'default' => ''], 'end_date' => ['type' => 'varchar', 'length' => 255, 'not null' => true, 'default' => ''], ], 'primary key' => ['ivid'], ), ); } === 8/uani_victims.install === ! function uani_victims_install() { entity_create('uani_victims_iran_victim', [ 'label' => 'Iran Victims', 'type' => 'generic', 'description' => 'Iran Victims', 'settings' => ['published' => 0], ])->save(); } !

Slide 56

Slide 56 text

=== 8/src/Entity/IranVictim.php ===! ! namespace Drupal\uani_victims\Entity; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Entity\ContentEntityBase; use Drupal\Core\Entity\EntityTypeInterface; /** * @ContentEntityType( * id = "uani_victims_iran_victim", * label = @Translation("Iran Victim"), * handlers = { * "list_builder" = "Drupal\uani_victims\Entity\Controller\IranVictimListBuilder", * "form" = { * "add" = "Drupal\uani_victims\Form\IranVictimForm", * "edit" = "Drupal\uani_victims\Form\IranVictimForm", * "delete" = "Drupal\uani_victims\Form\IranVictimDeleteForm", * }, * "access" = "Drupal\uani_victims\VictimAccessControlHandler", * }, * * base_table = "iran_victims", * admin_permission = "administer iran victims", * fieldable = false, * entity_keys = { * "id" = "id", * "label" = "name" * }, * links = { * "edit-form" = "uani_victims.victim_edit", * "delete-form" = "uani_victims.victim_delete", * }, * ) */ class IranVictim extends ContentEntityBase {

Slide 57

Slide 57 text

=== 8/src/Entity/IranVictim.php ===! ! public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { return [ 'id' => BaseFieldDefinition::create('integer') ->setLabel(t('ID')) ->setReadOnly(true), 'name' => BaseFieldDefinition::create('string') ->setLabel(t('Name')) ->setSettings(['default_value' => '', 'max_length' => 255, 'text_processing' => 0]) ->setDisplayOptions('form', ['type' => 'string']), 'description' => BaseFieldDefinition::create('string_long') ->setLabel(t('Description')) ->setSettings(['default_value' => '', 'text_processing' => 0]) ->setDisplayOptions('form', ['type' => 'string_textarea']), 'path' => BaseFieldDefinition::create('string') ->setLabel(t('Path')) ->setSettings(['default_value' => '', 'max_length' => 255, 'text_processing' => 0]) ->setDisplayOptions('form', ['type' => 'string']), 'type' => BaseFieldDefinition::create('list_string') ->setLabel(t('Type')) ->setSettings(['allowed_values' => self::victimTypes(), 'max_length' => 15]) ->setDisplayOptions('form', ['type' => 'options_select']), 'placemark_type' => BaseFieldDefinition::create('list_string') ->setLabel(t('Placemark Type')) ->setSettings(['allowed_values' => self::placemarkTypes(), 'max_length' => 15]) ->setDisplayOptions('form', ['type' => 'options_select']), 'country_code' => BaseFieldDefinition::create('string') ->setLabel(t('Country Code')) ->setSettings(['default_value' => '', 'max_length' => 2, 'text_processing' => 0]) ->setDisplayOptions('form', ['type' => 'string']), 'placemark_lat' => BaseFieldDefinition::create('float') ->setLabel(t('Placemark Latitude')) ->setSettings(['default_value' => '', 'max_length' => 255, 'text_processing' => 0]) ->setDisplayOptions('form', ['type' => 'string']), 'placemark_lon' => BaseFieldDefinition::create('float') ->setLabel(t('Placemark Longitude')) ->setSettings(['default_value' => '', 'max_length' => 255, 'text_processing' => 0]) ->setDisplayOptions('form', ['type' => 'string']), 'poly_region' => BaseFieldDefinition::create('string_long') ->setLabel(t('Poly Region')) ->setSettings(['default_value' => '', 'text_processing' => 0]) ->setDisplayOptions('form', ['type' => 'string_textarea']), 'poly_color' => BaseFieldDefinition::create('string') ->setLabel(t('Poly Color')) ->setSettings(['default_value' => '', 'max_length' => 31, 'text_processing' => 0]) ->setDisplayOptions('form', ['type' => 'string']), 'sort_date' => BaseFieldDefinition::create('integer') ->setLabel(t('Sort Date')) ->setDisplayOptions('form', ['type' => 'string']), 'start_date' => BaseFieldDefinition::create('string') ->setLabel(t('Start Date')) ->setSettings(['default_value' => '', 'max_length' => 255, 'text_processing' => 0]) ->setDisplayOptions('form', ['type' => 'string']), 'end_date' => BaseFieldDefinition::create('string') ->setLabel(t('End Date')) ->setSettings(['default_value' => '', 'max_length' => 255, 'text_processing' => 0]) ->setDisplayOptions('form', ['type' => 'string']), ]; } !

Slide 58

Slide 58 text

=== 7/src/Form/IranVictimForm.php ===! class IranVictimForm { public static function form($form, $form_state, $value = null) { $fields = [ 'name' => [ '#title' => 'Name', '#type' => 'textfield', '#required' => true, ], 'description' => [ '#title' => 'Description', '#type' => 'textarea', '#required' => true, ], 'path' => [ '#title' => 'Path', '#type' => 'textfield', '#required' => true, ], 'type' => [ '#title' => 'Type', '#type' => 'select', '#options' => IranVictim::victimTypes(), '#required' => true, ], 'placemark_type' => [ '#title' => 'Placemark Type', '#type' => 'select', '#options' => IranVictim::placemarkTypes(), '#required' => true, ], 'country_code' => [ '#title' => 'Country Code', '#type' => 'textfield', '#required' => false, ], 'placemark_lat' => [ '#title' => 'Placemark Latitude', '#type' => 'textfield', '#required' => false, ], 'placemark_lon' => [ '#title' => 'Placemark Longitude', '#type' => 'textfield', '#required' => false, ], 'poly_region' => [ '#title' => 'Poly Region', '#type' => 'textarea', '#required' => false, ], 'poly_color' => [ '#title' => 'Poly Color', '#type' => 'textfield', '#required' => false, ], 'sort_date' => [ '#title' => 'Sort Date', '#type' => 'textfield', '#required' => false, ], 'start_date' => [ '#title' => 'Start Date', '#type' => 'textfield', '#required' => false, ], 'end_date' => [ '#title' => 'End Date', '#type' => 'textfield', '#required' => false, ], ]; foreach(array_keys($fields) as $k) $fields[$k]['#default_value'] = (isset($value->$k) ? $value->$k : ''); $fields['submit'] = [ '#type' => 'submit', '#value' => 'Save', ]; return array_merge($form, $fields); } } ! ! !

Slide 59

Slide 59 text

=== 7/src/Form/IranVictimForm.php ===! class IranVictimForm { public static function form($form, $form_state, $value = null) { $fields = [ 'name' => [ '#title' => 'Name', '#type' => 'textfield', '#required' => true, ], 'description' => [ '#title' => 'Description', '#type' => 'textarea', '#required' => true, ], 'path' => [ '#title' => 'Path', '#type' => 'textfield', '#required' => true, ], 'type' => [ '#title' => 'Type', '#type' => 'select', '#options' => IranVictim::victimTypes(), '#required' => true, ], 'placemark_type' => [ '#title' => 'Placemark Type', '#type' => 'select', '#options' => IranVictim::placemarkTypes(), '#required' => true, ], 'country_code' => [ '#title' => 'Country Code', '#type' => 'textfield', '#required' => false, ], 'placemark_lat' => [ '#title' => 'Placemark Latitude', '#type' => 'textfield', '#required' => false, ], 'placemark_lon' => [ '#title' => 'Placemark Longitude', '#type' => 'textfield', '#required' => false, ], 'poly_region' => [ '#title' => 'Poly Region', '#type' => 'textarea', '#required' => false, ], 'poly_color' => [ '#title' => 'Poly Color', '#type' => 'textfield', '#required' => false, ], 'sort_date' => [ '#title' => 'Sort Date', '#type' => 'textfield', '#required' => false, ], 'start_date' => [ '#title' => 'Start Date', '#type' => 'textfield', '#required' => false, ], 'end_date' => [ '#title' => 'End Date', '#type' => 'textfield', '#required' => false, ], ]; foreach(array_keys($fields) as $k) $fields[$k]['#default_value'] = (isset($value->$k) ? $value->$k : ''); $fields['submit'] = [ '#type' => 'submit', '#value' => 'Save', ]; return array_merge($form, $fields); } } ! ! ! === 8/src/Form/IranVictimForm.php ===! ! namespace Drupal\uani_victims\Form; use Drupal\Core\Entity\ContentEntityForm; use Drupal\Core\Form\FormStateInterface; class IranVictimForm extends ContentEntityForm { public function save(array $form, FormStateInterface $form_state) { $form_state- >setRedirect('uani_victims.victim_list'); ! parent::save($form, $form_state); } }

Slide 60

Slide 60 text

=== 7/src/Entity/IranVictim.php ===! ! public function __get($name) { static $map = [ 'field_victim_type_value' => 'type', 'nid' => 'ivid', 'body' => 'description', 'title' => 'name', ]; if(isset($map[$name])) return $this->{$map[$name]}; else if(preg_match('/^field_(.*?)_value$/', $name, $match)) { $f = $match[1]; return (isset($this->$f) ? $this->$f : null); } return $this->$name; } ! ! !

Slide 61

Slide 61 text

=== 8/src/Entity/IranVictim.php ===! ! public function &__get($name) { static $map = [ 'field_victim_type_value' => 'type', 'nid' => 'id', 'ivid' => 'id', 'body' => 'description', 'title' => 'name', ]; if(isset($map[$name])) { $n = $map[$name]; $val = $this->get($map[$name])->value; return $val; } if(preg_match('/^field_(.*?)_value$/', $name, $match)) { $n = $match[1]; if($this->hasField($n)) { $val = $this->get($n)->value; return $val; } } return parent::__get($name); } === 7/src/Entity/IranVictim.php ===! ! public function __get($name) { static $map = [ 'field_victim_type_value' => 'type', 'nid' => 'ivid', 'body' => 'description', 'title' => 'name', ]; if(isset($map[$name])) return $this->{$map[$name]}; else if(preg_match('/^field_(.*?)_value$/', $name, $match)) { $f = $match[1]; return (isset($this->$f) ? $this->$f : null); } return $this->$name; }

Slide 62

Slide 62 text

=== 7/src/Controller/VictimController.php === public static function getOneVictim($id) { $victim = entity_load(IranVictim::NODE_TYPE, [$id]); if(isset($victim[$id])) return $victim[$id]; return null; } public static function getMultipleVictims($ids) { if(!is_array($ids)) $ids = [$ids]; return entity_load(IranVictim::NODE_TYPE, $ids); } ! public static function getVictims() { $query = new EntityFieldQuery; $result = $query ->entityCondition('entity_type', IranVictim::NODE_TYPE) ->propertyOrderBy('name') ->execute(); if(isset($result[IranVictim::NODE_TYPE])) { $ids = array_keys($result[IranVictim::NODE_TYPE]); return self::getMultipleVictims($ids); } return []; } protected static function getVictimIdFromPath($path) { $query = new EntityFieldQuery; $result = $query ->entityCondition('entity_type', IranVictim::NODE_TYPE) ->propertyCondition('path', $path) ->execute(); if(!empty($result[IranVictim::NODE_TYPE])) return array_keys($result[IranVictim::NODE_TYPE])[0]; else return null; } public static function getVictimFromId($id) { if(!is_numeric($id) || (int)$id != $id) $id = self::getVictimIdFromPath($id); return self::getOneVictim($id); }

Slide 63

Slide 63 text

=== 7/src/Controller/VictimController.php === public static function getOneVictim($id) { $victim = entity_load(IranVictim::NODE_TYPE, [$id]); if(isset($victim[$id])) return $victim[$id]; return null; } public static function getMultipleVictims($ids) { if(!is_array($ids)) $ids = [$ids]; return entity_load(IranVictim::NODE_TYPE, $ids); } ! public static function getVictims() { $query = new EntityFieldQuery; $result = $query ->entityCondition('entity_type', IranVictim::NODE_TYPE) ->propertyOrderBy('name') ->execute(); if(isset($result[IranVictim::NODE_TYPE])) { $ids = array_keys($result[IranVictim::NODE_TYPE]); return self::getMultipleVictims($ids); } return []; } protected static function getVictimIdFromPath($path) { $query = new EntityFieldQuery; $result = $query ->entityCondition('entity_type', IranVictim::NODE_TYPE) ->propertyCondition('path', $path) ->execute(); if(!empty($result[IranVictim::NODE_TYPE])) return array_keys($result[IranVictim::NODE_TYPE])[0]; else return null; } public static function getVictimFromId($id) { if(!is_numeric($id) || (int)$id != $id) $id = self::getVictimIdFromPath($id); return self::getOneVictim($id); } === 8/src/Controller/VictimController ===! ! public function getOneVictim($id) { return entity_load(IranVictim::NODE_TYPE, $id); } public function getMultipleVictims($ids) { if(!is_array($ids)) $ids = [$ids]; return entity_load(IranVictim::NODE_TYPE, $ids); } protected function getVictims() { $result = \Drupal::entityQuery(IranVictim::NODE_TYPE) ->sort('name') ->execute(); return entity_load_multiple(IranVictim::NODE_TYPE, $result); } protected function getVictimIdFromPath($path) { $result = \Drupal::entityQuery(IranVictim::NODE_TYPE) ->condition('path', '=', $path) ->sort('name') ->execute(); return entity_load(IranVictim::NODE_TYPE, $result); } public function getVictimFromId($id) { if(!is_numeric($id) || (int)$id != $id) $id = $this->getVictimIdFromPath($id); return $this->getOneVictim($id); }

Slide 64

Slide 64 text

=== 7/src/Controller/VictimController.php === public static function victimUrl($victim) { $url = '/veritas/'; if(!empty($victim->path)) $url .= $victim->path; else $url .= $victim->nid; return $url; }

Slide 65

Slide 65 text

=== 7/src/Controller/VictimController.php === public static function victimUrl($victim) { $url = '/veritas/'; if(!empty($victim->path)) $url .= $victim->path; else $url .= $victim->nid; return $url; } === 8/src/Controller/VictimController ===! ! protected function victimUrl($victim) { return \Drupal::urlGenerator() ->generateFromRoute('veritas', [ 'id' => ($victim->path ? $victim->path : $victim->id), ]); }

Slide 66

Slide 66 text

=== 7/src/Controller/VictimController ===! ! public static function ajaxVictimAction($id) { $victim = self::getVictimFromId($id); if(!$victim) return drupal_not_found(); header('Content-type: application/json'); echo json_encode(array( 'id' => $victim->ivid, 'view' => theme('uani_victim_overlay', array( 'victim' => $victim, 'victimURL' => self::victimUrl($victim), )), 'url' => $url, 'title' => "Iran VERITAS Project: Documenting Iran's Violence, Extremism, Repression and Terror | UANI | " . $victim- >title, 'type' => $victim- >field_victim_type_value, )); exit; }

Slide 67

Slide 67 text

=== 7/src/Controller/VictimController ===! ! public static function ajaxVictimAction($id) { $victim = self::getVictimFromId($id); if(!$victim) return drupal_not_found(); header('Content-type: application/json'); echo json_encode(array( 'id' => $victim->ivid, 'view' => theme('uani_victim_overlay', array( 'victim' => $victim, 'victimURL' => self::victimUrl($victim), )), 'url' => $url, 'title' => "Iran VERITAS Project: Documenting Iran's Violence, Extremism, Repression and Terror | UANI | " . $victim- >title, 'type' => $victim- >field_victim_type_value, )); exit; } === 8/src/Controller/VictimController ===! ! public function ajaxVictimAction($id) { $victim = $this->getVictimFromId($id); if(!$victim) return drupal_not_found(); return new AjaxResponse([ 'id' => $victim->ivid, 'view' => \_theme('uani_victim_overlay', array( 'victim' => $victim, )), 'url' => $this->victimUrl($victim), 'title' => "Iran VERITAS Project: Documenting Iran's Violence, Extremism, Repression and Terror | UANI | " . $victim- >title, 'type' => $victim- >field_victim_type_value, ]); }

Slide 68

Slide 68 text

=== 7/src/Controller/VictimController ===! ! public static function victimsAction() { $showVictimID = arg(1); $victims = self::getVictims(); $victimsSorted = array( 'all' => array(), 'usa' => array(), 'foreign' => array(), 'iran' => array(), ); foreach($victims as $v) $victimsSorted[$v->field_victim_type_value][] = $v; foreach($victimsSorted as $type => &$victimList) { usort($victimList, function($a, $b) use($type) { if($type == 'usa') return -strcmp($a->field_sort_date_value, $b- >field_sort_date_value); else return strcmp($a->title, $b->title); }); } $modulePath = drupal_get_path('module', 'uani_victims'); drupal_add_js($modulePath . '/js/jquery.history.js'); drupal_add_js($modulePath . '/js/uani_victims.js'); drupal_add_css($modulePath . '/templates/uani_victims.css'); drupal_add_js(self::getVictimsData($victims, $showVictimID), 'setting'); $types = IranVictim::victimTypes(); $types['all'] = 'Home'; $victimTypes = array(); foreach(self::legendOrder() as $type) $victimTypes[$type] = $types[$type]; return theme('uani_victims', array( 'victims' => $victims, 'victimsSorted' => $victimsSorted, 'victimTypes' => $victimTypes, )); }

Slide 69

Slide 69 text

=== 7/src/Controller/VictimController ===! ! public static function victimsAction() { $showVictimID = arg(1); $victims = self::getVictims(); $victimsSorted = array( 'all' => array(), 'usa' => array(), 'foreign' => array(), 'iran' => array(), ); foreach($victims as $v) $victimsSorted[$v->field_victim_type_value][] = $v; foreach($victimsSorted as $type => &$victimList) { usort($victimList, function($a, $b) use($type) { if($type == 'usa') return -strcmp($a->field_sort_date_value, $b- >field_sort_date_value); else return strcmp($a->title, $b->title); }); } $modulePath = drupal_get_path('module', 'uani_victims'); drupal_add_js($modulePath . '/js/jquery.history.js'); drupal_add_js($modulePath . '/js/uani_victims.js'); drupal_add_css($modulePath . '/templates/uani_victims.css'); drupal_add_js(self::getVictimsData($victims, $showVictimID), 'setting'); $types = IranVictim::victimTypes(); $types['all'] = 'Home'; $victimTypes = array(); foreach(self::legendOrder() as $type) $victimTypes[$type] = $types[$type]; return theme('uani_victims', array( 'victims' => $victims, 'victimsSorted' => $victimsSorted, 'victimTypes' => $victimTypes, )); } === 8/src/Controller/VictimController ===! ! public function victimsAction($showVictimID = null) { $victims = $this->getVictims(); $victimsSorted = array( 'all' => array(), 'usa' => array(), 'foreign' => array(), 'iran' => array(), ); foreach($victims as $v) $victimsSorted[$v->field_victim_type_value][] = $v; foreach($victimsSorted as $type => &$victimList) { usort($victimList, function($a, $b) use($type) { if($type == 'usa') return -strcmp($a->field_sort_date_value, $b- >field_sort_date_value); else return strcmp($a->title, $b->title); }); } $modulePath = drupal_get_path('module', 'uani_victims'); _drupal_add_js($this->getVictimsData($victims, $showVictimID), 'setting'); $types = IranVictim::victimTypes(); $types['all'] = 'Home'; $victimTypes = array(); foreach($this->legendOrder() as $type) $victimTypes[$type] = $types[$type]; return [ '#attached' => [ 'css' => [$modulePath . '/templates/uani_victims.css'], 'js' => [ $modulePath . '/js/jquery.history.js', $modulePath . '/js/uani_victims.js', ], 'library' => [ 'core/jquery.ui.dialog', ], ], '#theme' => 'uani_victims', '#victims' => $victims, '#victimsSorted' => $victimsSorted, '#victimTypes' => $victimTypes, ]; }

Slide 70

Slide 70 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 And we’re done! 70

Slide 71

Slide 71 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Well, Almost • Templates still need to be written (can’t avoid that) • We’ve ignored the theme, so our quick implementation doesn’t look awesome • But it works. 71

Slide 72

Slide 72 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Wins • Very easy upgrade from Drupal 6 -> Drupal 7 -> Drupal 8 • Did not have to substantially modify the interesting parts of our module • Used some magic to give us an implementation time reduction (at the expense of performance) 72

Slide 73

Slide 73 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 73 Drupal 6: 100%

Slide 74

Slide 74 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 74 Drupal 6: 72% Drupal 7: 27%

Slide 75

Slide 75 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 75 Drupal 6: 60% Drupal 7: 13% Drupal 8: 16%

Slide 76

Slide 76 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Your Results May Vary 76

Slide 77

Slide 77 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Questions? https://joind.in/talk/view/11888 ! John Bafford! http://bafford.com — @jbafford — john@bafford.com ! The Brick Factory! http://thebrickfactory.com — @thebrickfactory — jbafford@thebrickfactory.com 77

Slide 78

Slide 78 text

© 2014 John Bafford php[world] 2014 — 11/13/2014 Thank You! https://joind.in/talk/view/11888 ! John Bafford! http://bafford.com — @jbafford — john@bafford.com ! The Brick Factory! http://thebrickfactory.com — @thebrickfactory — jbafford@thebrickfactory.com 78