Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Writing OOP Modules for Drupal 7

Writing OOP Modules for Drupal 7

Drupal 8 is “coming soon”, and features an all-new OOP module interface that’s radically different from Drupal 7’s module interface. Using object-oriented design patterns, separation of concerns, and a thin interface layer between Drupal 7 and your module, this talk will present a way of writing object-oriented modules for Drupal 7 that will make it easier later when you port your modules to Drupal 8.

Presented on November 13, 2014 at php[world].

John Bafford

November 13, 2014
Tweet

More Decks by John Bafford

Other Decks in Technology

Transcript

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

    View Slide

  2. © 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

    View Slide

  3. © 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

    View Slide

  4. © 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

    View Slide

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

    View Slide

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

    View Slide

  7. © 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  12. © 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

    View Slide

  13. 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);
    }
    }
    }

    View Slide

  14. 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);
    }
    }

    View Slide

  15. 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,
    ),
    ),
    );
    }

    View Slide

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

    View Slide

  17. !
    === 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,
    );
    }
    }

    View Slide

  18. !
    === 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,
    );
    }
    }

    View Slide

  19. !
    === 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,
    );
    }
    }

    View Slide

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

    View Slide

  21. === 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,
    ));
    }
    }
    !
    !
    !
    !
    !
    !
    !

    View Slide

  22. !
    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;
    }
    !
    !
    !
    !
    !

    View Slide

  23. !
    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;
    }
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !

    View Slide

  24. === 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;
    }
    !

    View Slide

  25. === 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,
    ));
    }
    }
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !

    View Slide

  26. === 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,
    ));
    }

    View Slide

  27. (yay!)
    We have a Drupal 6 Module now

    View Slide

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

    View Slide

  29. === 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'],
    ),
    );
    }
    !
    !

    View Slide

  30. === 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,
    );
    }

    View Slide

  31. === 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',
    ],
    );
    }

    View Slide

  32. === 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);
    }
    }
    !
    !
    !

    View Slide

  33. === 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;
    }

    View Slide

  34. === 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';
    }

    View Slide

  35. === 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);
    }

    View Slide

  36. === 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);
    }

    View Slide

  37. === 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;
    }
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !

    View Slide

  38. === 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;
    }
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !

    View Slide

  39. === 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);
    }
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  43. === 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;
    }

    View Slide

  44. === 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;
    }

    View Slide

  45. === 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;
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  51. === 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:
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !

    View Slide

  52. === 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,
    ),
    );
    }
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !

    View Slide

  53. === 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'

    View Slide

  54. === 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'],
    ),
    );
    }
    !
    !
    !
    !
    !

    View Slide

  55. === 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();
    }
    !

    View Slide

  56. === 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
    {

    View Slide

  57. === 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']),
    ];
    }
    !

    View Slide

  58. === 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);
    }
    }
    !
    !
    !

    View Slide

  59. === 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);
    }
    }

    View Slide

  60. === 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;
    }
    !
    !
    !

    View Slide

  61. === 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;
    }

    View Slide

  62. === 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);
    }

    View Slide

  63. === 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);
    }

    View Slide

  64. === 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;
    }

    View Slide

  65. === 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),
    ]);
    }

    View Slide

  66. === 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;
    }

    View Slide

  67. === 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,
    ]);
    }

    View Slide

  68. === 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,
    ));
    }

    View Slide

  69. === 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,
    ];
    }

    View Slide

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

    View Slide

  71. © 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

    View Slide

  72. © 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide