Last active
August 29, 2015 14:05
-
-
Save joates/3cf7e514a05393e892f8 to your computer and use it in GitHub Desktop.
for reference: here is the Drupal / PHP version of my Mmmovies project (i think i did this about 2 years ago) it works really well actually, it's quite a lot of code though (at over 1k LoC) in the main file, plus a tiny bit more in the include file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @file | |
* Miscellaneous supporting functions. | |
*/ | |
/** | |
* Returns a structured array defining the fields bundled with this content type. | |
* | |
* This is provided as a function so that it can be used in both hook_install() | |
* and hook_uninstall(). | |
* | |
* @return associative array (field definitions) | |
*/ | |
function _mmmovies_api_installed_fields($type = '') { | |
$t = get_t(); | |
switch ($type) { | |
case 'movie_releases': | |
return array( | |
// Count | |
'count' => array( | |
'field_name' => 'field_mmmovies_api_count', | |
'label' => $t('Count'), | |
'type' => 'number_integer', | |
'cardinality' => 1, | |
), | |
); | |
break; | |
} | |
} | |
/** | |
* Returns a structured array defining the instances for this content type. | |
* | |
* The instance lets Drupal know which widget to use to allow the user to enter | |
* data and how to react in different view modes. | |
* | |
* This is provided as a function so that it can be used in both hook_install() | |
* and hook_uninstall(). | |
* | |
* @return associative array (instance & display mode definitions) | |
*/ | |
function _mmmovies_api_installed_instances($type = '') { | |
$t = get_t(); | |
switch ($type) { | |
case 'movie_releases': | |
return array( | |
// Count | |
'count' => array( | |
'field_name' => 'field_mmmovies_api_count', | |
'type' => 'number_integer', | |
'label' => $t('Count'), | |
'widget' => array( | |
'type' => 'number_integer', | |
'weight' => 0, | |
), | |
'display' => array( | |
'default' => array( | |
'label' => 'inline', | |
'type' => 'number_integer', | |
'weight' => 0, | |
), | |
'teaser' => array( | |
'type' => 'hidden', | |
), | |
), | |
), | |
); | |
break; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @file | |
* Provides a Services API resource. | |
* | |
* This web service can be used to identify movie releases by month (and year). | |
*/ | |
/** | |
* Implements hook_menu_alter(). | |
* | |
* Changes the extra menu-items on the login form. | |
*/ | |
function mmmovies_api_menu_alter(&$items) { | |
$items['user/register']['type'] = MENU_CALLBACK; | |
$items['user/password']['type'] = MENU_CALLBACK; | |
} | |
/** | |
* Implements hook_cron(). | |
* | |
* Periodically check that stored data is updated when | |
* the external resource responds with a changed version. | |
*/ | |
function mmmovies_api_cron() { | |
/* | |
* Note: This limit could be derived from the total count of nodes | |
* divided by the number of cron runs in a given time period. | |
* e.g. max(1, ceil($count_nodes / (30 * 12)) // 30 days, cron runs 2 hourly | |
*/ | |
$limit = 10; | |
$threshold = REQUEST_TIME - 86400; // 1 day ago | |
//$threshold = REQUEST_TIME - 2592000; // 30 days ago | |
// Locate the 'stale' data. | |
$result = db_select('node', 'n') | |
->fields('n', array('nid')) | |
->condition('type', 'movie_releases', '=') | |
->condition('changed', $threshold, '<') | |
->orderBy('changed', 'ASC') | |
->range(0, $limit) | |
->execute(); | |
$nids = $result->fetchCol(); | |
if (count($nids)) { | |
foreach ($nids as $nid) { | |
// Published node. | |
$node = node_load($nid); | |
// Check for a more recent (unpublished) node revision. | |
$vid = array_shift(array_keys(node_revision_list($node))); | |
if ($vid != $node->vid && !empty($vid)) { | |
$node = node_load($nid, $vid); | |
} | |
$item = field_get_items('node', $node, 'field_mmmovies_api_count'); | |
$value = field_view_value('node', $node, 'field_mmmovies_api_count', $item[0]); | |
$count_old = intval($value['#markup']); | |
// Import new data for comparison. | |
$data = mmmovies_api_get_releases($node->title); | |
$count_new = count($data['response']); | |
/** | |
* Note: This could easily be upgraded to diff compare the | |
* serialized data instead of only testing the count of records. | |
*/ | |
if ($count_new && ($count_new != $count_old)) { | |
// Create a new node revision with the updated data. | |
list($month, $year) = explode('-', $node->title); | |
_mmmovies_api_store_movie_releases($data['response'], $month, $year); | |
watchdog('mmmovies_api', 'Added new revision to ' . $node->title); | |
} | |
else { | |
/** | |
* Only update the node->changed field. | |
* | |
* Note: | |
* This intentionally bypasses node_save() so that | |
* revisions are NOT created in the moderation queue. | |
*/ | |
$node->changed = REQUEST_TIME; | |
db_update('node') | |
->fields(array('changed' => $node->changed)) | |
->condition('nid', $node->nid) | |
->execute(); | |
} | |
} | |
} | |
} | |
/** | |
* Implements hook_enable(). | |
*/ | |
function mmmovies_api_enable() { | |
// Grant permissions to administrator | |
$user1 = user_role_load_by_name('administrator'); | |
user_role_grant_permissions($user1->rid, array_keys(node_list_permissions('movie_releases'))); | |
// Discover/create the Movie Manager identity. | |
$name = variable_get('mmmovies_user_name', 'Mmmovies'); | |
// Discover/create the Movie Manager role. | |
$role_name = 'Movie Manager (mmmovies)'; | |
if (!$role = user_role_load_by_name($role_name)) { | |
// Add a new role | |
$role = new stdClass(); | |
$role->name = $role_name; | |
user_role_save($role); | |
// Grant permissions to the new role | |
$role = user_role_load_by_name($role_name); | |
user_role_grant_permissions($role->rid, array_keys(node_list_permissions('movie_releases'))); | |
user_role_grant_permissions($role->rid, array('access content' ,'administer nodes', 'administer movies releases')); | |
} | |
// Discover/create the Movie Manager user. | |
if (!$user = user_load_by_name($name)) { | |
// Add a new user account | |
$account = new stdClass(); | |
$account->is_new = TRUE; | |
$edit = array( | |
'name' => $name, | |
//'status' => 1, | |
'roles' => array($role->rid => $role_name), | |
); | |
user_save($account, $edit); | |
} | |
} | |
/** | |
* Implements hook_permission(). | |
*/ | |
function mmmovies_api_permission() { | |
/** NOTE: This permission is assigned to a role but not actually used. **/ | |
return array( | |
'administer movies releases' => array( | |
'title' => t('Administer movie releases'), | |
), | |
); | |
} | |
/** | |
* Implements hook_theme(). | |
*/ | |
function mmmovies_api_theme() { | |
return array( | |
'plax_movie_card' => array( | |
'render element' => 'element', | |
), | |
'mmmovies_api_releases_request_form' => array( | |
'render element' => 'form', | |
), | |
); | |
} | |
/** | |
* Implements hook_menu(). | |
*/ | |
function mmmovies_api_menu() { | |
$items['projects'] = array( | |
'title' => 'Projects', | |
'page callback' => 'mmmovies_api_projects_page', | |
'access callback' => TRUE, | |
'menu_name' => 'main-menu', | |
'expanded' => TRUE, | |
'type' => MENU_NORMAL_ITEM, | |
); | |
$items['projects/mmmovies'] = array( | |
'title' => 'Mmmovies', | |
'page callback' => 'mmmovies_api_mmmovies_page', | |
'access callback' => TRUE, | |
'menu_name' => 'main-menu', | |
'type' => MENU_NORMAL_ITEM, | |
); | |
$items['projects/mmmovies_api'] = array( | |
'title' => 'Mmmovies API', | |
'page callback' => 'mmmovies_api_mmmovies_api_page', | |
'access callback' => TRUE, | |
'menu_name' => 'main-menu', | |
'type' => MENU_NORMAL_ITEM, | |
); | |
$items['projects/mmmovies/demo'] = array( | |
//'title' => 'Demo', | |
'page callback' => 'mmmovies_api_mmmovies_demo_page', | |
'page arguments' => array(3), | |
'access callback' => TRUE, | |
'type' => MENU_CALLBACK, | |
); | |
$items['mmmovies/api/status'] = array( | |
'title' => 'API Status', | |
'page callback' => 'mmmovies_api_status', | |
'access callback' => TRUE, | |
'type' => MENU_CALLBACK, | |
); | |
return $items; | |
} | |
/** | |
* Custom access callback. | |
*/ | |
function mmmovies_api_demo_previous_next_access() { | |
return (arg(0) == 'prev' || arg(0) == 'next' || (arg(0) == 'mmmovies' && arg(1) == 'demo')) ? TRUE : FALSE; | |
} | |
/** | |
* Custom page callback. | |
*/ | |
function mmmovies_api_projects_page() { | |
/** NOTE: This is just the parent tab for other submenu items. **/ | |
return ''; | |
} | |
/** | |
* Custom page callback. | |
*/ | |
function mmmovies_api_mmmovies_page() { | |
// Hide the page title. | |
drupal_set_title(''); | |
$output = '<p>Mmmovies is a module that will enable easier management of a collection of movie titles.<br />'; | |
$output .= 'This will be my first contribution to the Drupal community.</p>'; | |
$output .= '<br />'; | |
$output .= 'Here is a ' . l('demonstration', 'projects/mmmovies/demo') . '.'; | |
return $output; | |
} | |
/** | |
* Custom page callback. | |
*/ | |
function mmmovies_api_mmmovies_api_page() { | |
// Hide the page title. | |
drupal_set_title(''); | |
$output = '<p>Mmmovies API is a web service hosted on this website.<br />'; | |
$output .= 'It can be used to locate all the movie releases for a specific month and year.</p>'; | |
$output .= '<br />'; | |
$output .= render(drupal_get_form('mmmovies_api_releases_request_form')); | |
return $output; | |
} | |
/** | |
* Build a simple form using Form API. | |
*/ | |
function mmmovies_api_releases_request_form() { | |
$form = array(); | |
for ($key = 1; $key <=12; $key++) { | |
$months[$key] = date('F', strtotime('2000-' . $key)); | |
} | |
$years = drupal_map_assoc(range(1888, date('Y', REQUEST_TIME))); | |
$form['month'] = array( | |
'#type' => 'select', | |
'#title' => t('Month'), | |
'#options' => $months, | |
'#default_value' => date('n', REQUEST_TIME), | |
); | |
$form['year'] = array( | |
'#type' => 'select', | |
'#title' => t('Year'), | |
'#options' => $years, | |
'#default_value' => date('Y', REQUEST_TIME), | |
); | |
$form['submit'] = array( | |
'#type' => 'submit', | |
'#value' => 'View movie releases', | |
); | |
return $form; | |
} | |
/** | |
* Theme function for the form. | |
*/ | |
function theme_mmmovies_api_releases_request_form($variables) { | |
if (user_is_logged_in()) { | |
$variables['form']['submit']['#prefix'] = '<div class="form-submit-button">'; | |
$variables['form']['submit']['#suffix'] = '</div>'; | |
return drupal_render_children($variables['form']); | |
} | |
else { | |
$login = l('login', 'user/login', array('query' => array('destination' => 'projects/mmmovies_api'))); | |
return 'You need to ' . $login . ' to use this!'; | |
} | |
} | |
/** | |
* Form API validate callback. | |
*/ | |
function mmmovies_api_releases_request_form_validate($form, &$form_state) { | |
// NOTE: Nothing to validate here. | |
} | |
/** | |
* Form API submit callback. | |
*/ | |
function mmmovies_api_releases_request_form_submit($form, &$form_state) { | |
$month = $form_state['values']['month']; | |
$month = ($month < 10 ? '0' . $month : $month); | |
$year = $form_state['values']['year']; | |
$date_full = date('F', strtotime('2000-' . $month . '-01')) . " $year"; | |
// Check for a valid record in the database. | |
$query = "SELECT * FROM {node} WHERE title = :title"; | |
$result = db_query($query, array(':title' => $month . '-' . $year))->fetchAll(); | |
if (!empty($result[0])) { | |
drupal_goto('mmmovies/api/releases/' . $month . '-' . $year); | |
} | |
else { | |
// Show progress indicator while new data is collected. | |
$batch = array( | |
'title' => t('Please wait'), | |
'operations' => array( | |
array('_mmmovies_api_get_releases_batch', array($month, $year)), | |
), | |
'progress_message' => t('Locating movie releases for %date', array('%date' => $date_full)), | |
'finished' => '_mmmovies_api_get_releases_batch_finished', | |
); | |
batch_set($batch); | |
} | |
} | |
/** | |
* Display a progress indicator using the Batch API. | |
*/ | |
function _mmmovies_api_get_releases_batch($month, $year, &$context) { | |
$chunk_size = 10; | |
if (!isset($context['sandbox']['releases'])) { | |
// Gather data from external resource. | |
$releases = _mmmovies_api_get_imdb_releases($year, $month); | |
$context['sandbox']['releases'] = $releases; | |
$context['sandbox']['keys'] = array_keys($releases); | |
$context['sandbox']['completed'] = 0; | |
$context['sandbox']['total'] = count($releases); | |
$chunk_size = 2; // Display progress early on 1st iteration! | |
} | |
// Trim some refs from the keys stack. | |
$refs = array(); | |
for ($i = 1; $i <= $chunk_size; $i++) { | |
if (count($context['sandbox']['keys'])) { | |
$refs[] = array_shift($context['sandbox']['keys']); | |
} | |
} | |
foreach ($refs as $imdb_ref) { | |
// Get additional movie data from themoviedb. | |
$tmdb_data = _mmmovies_api_get_tmdb_releases($imdb_ref); | |
$context['sandbox']['releases'][$imdb_ref]['tmdb_data'] = $tmdb_data; | |
$context['sandbox']['completed']++; | |
} | |
// Update Batch API status. | |
if ($context['sandbox']['completed'] < $context['sandbox']['total']) { | |
$context['finished'] = $context['sandbox']['completed'] / $context['sandbox']['total']; | |
//$context['message'] = t('Now processing "%movie"...', array('%movie' => $context['sandbox']['releases'][$imdb_ref]['title'])); | |
} | |
if ($context['finished'] == 1) { | |
$context['results'] = $context['sandbox']['releases']; | |
$context['results']['date'] = $month . '-' . $year; | |
} | |
} | |
/** | |
* Batch API callback used when the batch run completes. | |
*/ | |
function _mmmovies_api_get_releases_batch_finished($success, $results, $operations) { | |
list($month, $year) = explode('-', $results['date']); | |
unset($results['date']); | |
// Store a copy of the data. | |
if (count($results)) { | |
_mmmovies_api_store_movie_releases($results, $month, $year); | |
} | |
/** | |
* Attempt to overwrite browser history so that batch | |
* does not re-run when the back button is used to navigate! | |
*/ | |
//global $base_url; | |
//$url = $base_url . '/mmmovies/api/releases/' . $month . '-' . $year; | |
//$js = 'window.location.replace(' . $url . ');'; | |
//drupal_add_js($js, 'inline'); | |
drupal_goto('mmmovies/api/releases/' . $month . '-' . $year); | |
} | |
/** | |
* Custom page callback. | |
* | |
* Displays a basic analysis of the storage statistics. | |
*/ | |
function mmmovies_api_status() { | |
global $user; | |
$min_nid = $max_nid = ''; | |
$count = $total = $timestamp = $max = 0; | |
$min = 999; | |
$query = "SELECT nid FROM {node} WHERE type = :type"; | |
$result = db_query($query, array(':type' => 'movie_releases'))->fetchCol(); | |
foreach ($result as $nid) { | |
$node = node_load($nid); | |
$item = field_get_items('node', $node, 'field_mmmovies_api_count'); | |
$value = field_view_value('node', $node, 'field_mmmovies_api_count', $item[0]); | |
$value = intval($value['#markup']); | |
$count++; | |
$total += $value; | |
if ($value < $min) { $min = $value; $min_nid = $nid; } | |
if ($value > $max) { $max = $value; $max_nid = $nid; } | |
$timestamp = ($node->changed > $timestamp ? $node->changed : $timestamp); | |
} | |
if (user_access('administer nodes')) { | |
// Display links to administrator. | |
$min = l($min, "node/$min_nid"); | |
$max = l($max, "node/$max_nid"); | |
} | |
$header = array('Count', 'Sum', 'Min', 'Max', 'Avg', 'Last Update'); | |
$rows = array(array($count, $total, $min, $max, round($total / $count), date("j-M-Y, g:i a", $timestamp))); | |
return theme('table', array('header' => $header, 'rows' => $rows));; | |
} | |
/** | |
* Custom page callback. | |
*/ | |
function mmmovies_api_mmmovies_demo_page($date = '') { | |
global $user; | |
$build = array(); | |
if (preg_match('/\d{2}-\d{4}/', $date)) { | |
list($month, $year) = explode('-', $date); | |
$prev_date = date('m-Y', strtotime("$year-$month-01 - 1 month")); | |
$next_date = date('m-Y', strtotime("$year-$month-01 + 1 month")); | |
$query = "SELECT * FROM {node} WHERE title = :title"; | |
$options = array(':title' => $date); | |
if (user_access('administer nodes')) { | |
$build['links'] = array( | |
'prev' => array( | |
'#theme' => 'link', | |
'#text' => 'Previous', | |
'#path' => 'projects/mmmovies/demo/' . $prev_date, | |
'#options' => array('attributes' => array('class' => array('quick-link')), 'html' => FALSE), | |
), | |
'next' => array( | |
'#theme' => 'link', | |
'#text' => 'Next', | |
'#path' => 'projects/mmmovies/demo/' . $next_date, | |
'#options' => array('attributes' => array('class' => array('quick-link')), 'html' => FALSE), | |
), | |
'#prefix' => '<div id="demo-page-quick-links">', | |
'#suffix' => '</div>', | |
); | |
} | |
} | |
else { | |
$query = "SELECT * FROM {node} WHERE type = :type ORDER BY RAND() LIMIT 1"; | |
$options = array(':type' => 'movie_releases'); | |
} | |
$result = db_query($query, $options)->fetchAll(); | |
if (empty($result[0])) { | |
drupal_set_message('No status information is available at the moment.', 'warning'); | |
return ''; | |
} | |
$node = node_load($result[0]->nid); | |
$movies = unserialize($node->body[LANGUAGE_NONE][0]['value']); | |
foreach ($movies as $key => $movie) { | |
if (empty($movie['tmdb_data']) || empty($movie['tmdb_data']['covers']) || empty($movie['tmdb_data']['posters'])) { | |
// Discard any movie that does not include both images. | |
unset($movies[$key]); | |
} | |
} | |
// Trim the total movies to nicely fit into 3-column layout. | |
$remainder = count($movies) > 9 ? count($movies) % 3 : 0; | |
if ($remainder) { | |
$movies = array_slice($movies, 0, 0 - $remainder); | |
} | |
foreach ($movies as $movie) { | |
$build[] = _mmmovies_api_build_movie_card($movie); | |
} | |
$month = date('F', strtotime("2000-" . substr($result[0]->title, 0, 2) . "-01")); | |
drupal_set_title(t("Movies released during %month %year", array('%month' => $month, '%year' => substr($result[0]->title, -4))), PASS_THROUGH); | |
// Add the intro message. | |
/** | |
$intro = array( | |
'#markup' => '<div class="glass-logo"></div>' . | |
'<div class="demo-intro"><span class="demo-title">This is a demo of the Parallax 3D Effect.</span><br />' . | |
'<em>hover your mouse pointer over an example below and then move it around to view the simulated 3D effect.</em> ' . | |
'This is an innovative front-end UI design with progressive enhancement for compatibility ' . | |
'with older browsers and also incorporates a <em>responsive layout</em> which adapts to fit devices with different screen sizes.<br /></div>' , | |
'#prefix' => '<div class="plax-demo">', | |
'#suffix' => '</div>', | |
); | |
array_unshift($build, $intro); | |
*/ | |
return $build; | |
} | |
/** | |
* Private helper function. | |
* Generates the markup to display a plaxified movie card. | |
*/ | |
function _mmmovies_api_build_movie_card($movie) { | |
$title = $movie['title']; | |
if (strlen($title) > 32) { | |
$title = '<h2>' . $movie['title'] . '</h2>'; | |
} | |
else { | |
$title = '<h2 class="right">' . $movie['title'] . '</h2>'; | |
} | |
$num_posters = count($movie['tmdb_data']['posters']); | |
$poster = $num_posters > 1 ? mt_rand(0 , $num_posters - 1) : 0; | |
$num_covers = count($movie['tmdb_data']['covers']); | |
$cover = $num_covers > 1 ? mt_rand(0 , $num_covers - 1) : 0; | |
$desc = decode_entities($movie['tmdb_data']['overview']); | |
//$desc = preg_replace('/&#(\d+);/me',"chr(\\1)",$desc); #decimal notation | |
//$desc = preg_replace('/&#x([a-f0-9]+);/mei',"chr(0x\\1)",$desc); #hex notation | |
if (strlen($desc) > 190) { | |
$trimmed = substr($movie['tmdb_data']['overview'], 0, 190); | |
$trimmed = substr($trimmed, 0, strrpos($trimmed, ' ')); | |
$trimmed = preg_replace('/[^a-zA-Z]$/', '', $trimmed); | |
$desc = '<p>' . $trimmed . '...</p>'; | |
} | |
else { | |
$desc = '<p>' . $movie['tmdb_data']['overview'] . '</p>'; | |
} | |
$certificate = '--'; | |
if (!empty($movie['imdb_data']['certificate'])) { | |
$certificate = preg_replace('/_/', '-', $movie['imdb_data']['certificate']); | |
} | |
if ($certificate == '--' && !empty($movie['tmdb_data']['certification'])) { | |
$certificate = $movie['tmdb_data']['certification']; | |
} | |
$runtime = '--'; | |
if (!empty($movie['imdb_data']['runtime'])) { | |
$runtime = 0 + $movie['imdb_data']['runtime']; | |
} | |
if ($runtime == '--' && !empty($movie['tmdb_data']['runtime'])) { | |
$runtime = $movie['tmdb_data']['runtime']; | |
} | |
$box_office = '--'; | |
if (!empty($movie['imdb_data']['box_office'])) { | |
$box_office = sprintf("%1.1f", $movie['imdb_data']['box_office'] / 1000000); | |
} | |
$box_office = ($box_office > 0) ? '$<span class="cash">' . $box_office . '</span>M' : | |
'< $<span class="cash">100</span>K'; | |
$genre = !empty($movie['imdb_data']['genre']) ? $movie['imdb_data']['genre'] : ''; | |
if (!empty($genre)) { | |
$genre = '<span class="genre-label">Genre: </span><em>' . $genre; | |
} | |
return array( | |
'title' => $title, | |
'poster' => '<img src="' . $movie['tmdb_data']['posters'][$poster] . '" />', | |
'cover' => '<img src="' . $movie['tmdb_data']['covers'][$cover] . '" />', | |
'desc' => $desc, | |
'rated' => $certificate, | |
'runtime' => $runtime, | |
'box_office' => $box_office, | |
'cast' => preg_replace('/, /', '<br />', $movie['imdb_data']['cast']), | |
'genre' => $genre, | |
'#theme' => 'plax_movie_card', | |
); | |
} | |
/** | |
* Custom theme function. | |
*/ | |
function theme_plax_movie_card($variables) { | |
$cert_label = strlen($variables['element']['rated']) < 7 ? 'Rated ' : ''; | |
$output = '<div class="plax-container">' . | |
'<div class="plax-background-layer-1"></div>' . | |
'<div class="plax-background-layer-2">' . $variables['element']['poster'] . '</div>' . | |
'<div class="plax-content-layer-1">' . $variables['element']['title'] . '</div>' . | |
'<div class="plax-content-layer-2">' . $variables['element']['cover'] . '</div>' . | |
'<div class="plax-content-layer-3">' . | |
'<div class="movie-details">' . $variables['element']['desc'] . | |
'<div class="cast">' . $variables['element']['cast'] . '</div>' . | |
'<div class="movie-stats-1">' . | |
'<div class="rating">' . $cert_label . '<span class="cert">' . $variables['element']['rated'] . '</span></div>' . | |
'<div class="runtime">' . $variables['element']['runtime'] . ' mins</div>' . | |
'<div class="box-office">' . $variables['element']['box_office'] . '</div>' . | |
'</div>' . | |
'<div class="genre">' . $variables['element']['genre'] . '</em></div>' . | |
'</div>' . | |
'</div>'; | |
$output .= '</div>'; | |
return $output; | |
} | |
/** | |
* Implements hook_ctools_plugin_api(). | |
* | |
* This just lets services know that we are defining an endpoint in code. | |
*/ | |
function mmmovies_api_ctools_plugin_api($owner, $api) { | |
if ($owner == 'services' && $api == 'services') { | |
return array( | |
'version' => 3, | |
); | |
} | |
} | |
/** | |
* Implements hook_default_services_endpoint(). | |
* | |
* Defines the endpoint. | |
*/ | |
function mmmovies_api_default_services_endpoint() { | |
$export = array(); | |
$endpoint = new stdClass(); | |
$endpoint->disabled = FALSE; /* Edit this to true to make a default endpoint disabled initially */ | |
$endpoint->api_version = 3; | |
$endpoint->name = 'mmmovies_api'; | |
$endpoint->server = 'rest_server'; | |
$endpoint->path = 'mmmovies/api'; | |
$endpoint->authentication = array(); | |
$endpoint->server_settings = array( | |
'formatters' => array( | |
'json' => TRUE, | |
'bencode' => FALSE, | |
'jsonp' => FALSE, | |
'php' => FALSE, | |
'rss' => FALSE, | |
'xml' => FALSE, | |
), | |
'parsers' => array( | |
'application/json' => TRUE, | |
'application/vnd.php.serialized' => TRUE, | |
'application/x-www-form-urlencoded' => TRUE, | |
'multipart/form-data' => TRUE, | |
), | |
); | |
$endpoint->resources = array( | |
'releases' => array( | |
'alias' => 'releases', | |
'operations' => array( | |
'retrieve' => array( | |
'enabled' => 1, | |
), | |
), | |
), | |
); | |
$endpoint->debug = 0; | |
$export['mmmovies_api'] = $endpoint; | |
return $export; | |
} | |
/** | |
* Implements hook_services_resources(). | |
*/ | |
function mmmovies_api_services_resources() { | |
return array( | |
'releases' => array( | |
'retrieve' => array( | |
'help' => 'Get movie releases', | |
'callback' => 'mmmovies_api_get_releases', | |
'access callback' => '_mmmovies_api_resource_access', | |
'args' => array( | |
array( | |
'name' => 'date', | |
'type' => 'string', | |
'description' => 'Date (format: MM-YYYY)', | |
'source' => array('path' => '0'), | |
'optional' => FALSE, | |
'default value' => '0', | |
), | |
), | |
), | |
), | |
); | |
} | |
/** | |
* Private helper function. | |
* Grants access to the web services resource. | |
*/ | |
function _mmmovies_api_resource_access() { | |
// Validate only for logged-in users. | |
return user_is_logged_in(); | |
// Validate for everyone. | |
//return TRUE; | |
} | |
/** | |
* Custom callback function. | |
* Discovers movie releases during a given date (month-year). | |
* | |
* @return | |
* An associative array of movies keyed by reference. | |
*/ | |
function mmmovies_api_get_releases($date) { | |
$releases = array(); | |
// Validate the input parameter. | |
if (!preg_match('/\d{2}-\d{4}/', $date)) { | |
return array( | |
'status' => 'ERROR', | |
'response' => t('Date parameter is invalid - please provide a date with the format MM-YYYY'), | |
); | |
} | |
else { | |
$month = substr($date, 0, 2); | |
$year = substr($date, 3, 4); | |
} | |
if (empty($month) || $month < 1 || $month > 12 || empty($year) || $year < 1888 || $year > date('Y')) { | |
return array( | |
'status' => 'ERROR', | |
'response' => t('Date parameter is invalid - dates before 1888 and dates in the future are not allowed'), | |
); | |
} | |
// Check for a valid record in the database. | |
$query = "SELECT * FROM {node} WHERE title = :title"; | |
$result = db_query($query, array(':title' => $month . '-' . $year))->fetchAll(); | |
if (!empty($result[0])) { | |
$node = node_load($result[0]->nid); | |
$releases = unserialize($node->body[LANGUAGE_NONE][0]['value']); | |
} | |
else { | |
// Gather data from external resources. | |
$releases = _mmmovies_api_get_imdb_releases($year, $month); | |
foreach (array_keys($releases) as $imdb_ref) { | |
// Get additional movie data from themoviedb. | |
$releases[$imdb_ref]['tmdb_data'] = _mmmovies_api_get_tmdb_releases($imdb_ref); | |
} | |
// Store a copy of the data. | |
if (count($releases)) { | |
_mmmovies_api_store_movie_releases($releases, $month, $year); | |
} | |
} | |
// Return the data. | |
if (count($releases)) { | |
return array( | |
'status' => 'OK', | |
'response' => $releases, | |
); | |
} | |
else { | |
$error_month = date('F', strtotime("2000-$month-01")); | |
return array( | |
'status' => 'ERROR', | |
'response' => t("There are no releases for $error_month $year"), | |
); | |
} | |
} | |
/** | |
* Private helper function. | |
*/ | |
function _mmmovies_api_get_imdb_releases($year, $month) { | |
$releases = array(); | |
// Query imdb's advanced search. | |
$imdb_adv_search_params = array( | |
'boxoffice_gross_us=1,', | |
'count=100', | |
"release_date=$year-$month,$year-$month", | |
'sort=boxoffice_gross_us,desc', | |
'title_type=feature', | |
); | |
$url = 'http://www.imdb.com/search/title?' . implode('&', $imdb_adv_search_params); | |
$request = drupal_http_request($url); | |
if (!empty($request->data) && $request->code == 200) { | |
preg_match_all('/<tr class="(?:odd|even) detailed">.*?<\/tr>/s', $request->data, $rows); | |
foreach ($rows[0] as $row) { | |
$fields = array(); | |
$regex_to_scrape_fields = array( | |
'imdb_ref' => '<a href="\/title\/(tt\d+)\/', | |
'image' => '<a href="\/title\/tt.*?<img src="(.*?)"', | |
'title' => '<td class="title">.*?<a href="\/title\/tt.*?>(.*?)<\/a>', | |
'year' => '<span class="year_type">\((\d{4})\)<\/span>', | |
'user_rating' => '<span class="rating-rating"><span class="value">(.*?)<\/span>', | |
'user_votes' => '<div class="rating rating-list".*?\((.*?) votes', | |
//'short_plot' => '<span class="outline">(.*?)<\/span>', | |
'director' => '<span class="credit">\s+Dir: <a href="\/name\/nm.*?">(.*?)<\/a>', | |
'cast' => '<span class="credit">.*?With: (<a href="\/name\/nm.*?">.*?)<\/span>', | |
'genre' => '<span class="genre">(<a href="\/genre\/.*?">.*?)<\/span>', | |
'certificate' => '<span class="certificate">.*?title="(.*?)"', | |
'runtime' => '<span class="runtime">(\d+) mins\.<\/span>', | |
'box_office' => '<td class="sort_col">\$(.*?)(M|K)*<\/td>', | |
); | |
foreach ($regex_to_scrape_fields as $key => $regex) { | |
preg_match_all("/$regex/s", $row, $matches); | |
if (!empty($matches[1][0])) { | |
switch ($key) { | |
case 'title': | |
$fields[$key] = decode_entities($matches[1][0]); | |
break; | |
case 'cast': | |
$fields[$key] = decode_entities($matches[1][0]); | |
$fields[$key] = strip_tags($fields[$key]); | |
$fields[$key] = preg_replace('/\\n/', '', $fields[$key]); | |
break; | |
case 'genre': | |
$fields[$key] = strip_tags($matches[1][0]); | |
$fields[$key] = preg_replace('/ \| /', ', ', $fields[$key]); | |
break; | |
case 'user_votes': | |
$fields[$key] = preg_replace('/,/', '', $matches[1][0]); | |
break; | |
case 'box_office': | |
$multiplier = (!empty($matches[2][0]) ? ($matches[2][0] == 'M' ? 1000000 : 1000) : 1); | |
$fields[$key] = $matches[1][0] * $multiplier; | |
break; | |
default: | |
$fields[$key] = $matches[1][0]; | |
} | |
} | |
unset($matches); | |
} | |
$imdb_ref = $fields['imdb_ref']; | |
unset($fields['imdb_ref']); | |
$releases[$imdb_ref]['title'] = $fields['title']; | |
$releases[$imdb_ref]['imdb_data'] = $fields; | |
} | |
} | |
return $releases; | |
} | |
/** | |
* Private helper function. | |
*/ | |
function _mmmovies_api_get_tmdb_releases($imdb_ref) { | |
$api_key = '###'; /** add api key from your account here (https://www.themoviedb.org/account/signup) **/ | |
$url = "http://api.themoviedb.org/2.1/Movie.imdbLookup/en/json/$api_key/$imdb_ref"; | |
$request = drupal_http_request($url); | |
if (!empty($request->data) && $request->code == 200) { | |
$tmdb_data = json_decode($request->data); | |
$tmdb_data = (array)$tmdb_data[0]; | |
// Select a subset of the poster images. | |
$covers = array(); | |
$last_id = ''; | |
if (isset($tmdb_data['posters'])) { | |
foreach ($tmdb_data['posters'] as $key => $image_obj) { | |
if ($image_obj->image->size == 'thumb' && $image_obj->image->id != $last_id) { | |
$covers[] = $image_obj->image->url; | |
$last_id = $image_obj->image->id; | |
} | |
else { | |
continue; | |
} | |
} | |
// Rebuild data array including the selected cover images. | |
unset($tmdb_data['posters']); | |
$tmdb_data['covers'] = $covers; | |
} | |
// Select a subset of the backdrop images. | |
$posters = array(); | |
$last_id = ''; | |
if (isset($tmdb_data['backdrops'])) { | |
foreach ($tmdb_data['backdrops'] as $key => $image_obj) { | |
if ($image_obj->image->size == 'thumb' && $image_obj->image->id != $last_id) { | |
$posters[] = $image_obj->image->url; | |
$last_id = $image_obj->image->id; | |
} | |
else { | |
continue; | |
} | |
} | |
// Rebuild data array including the selected poster images. | |
unset($tmdb_data['backdrops']); | |
$tmdb_data['posters'] = $posters; | |
} | |
} | |
return !empty($tmdb_data) ? $tmdb_data : array(); | |
} | |
/** | |
* Private helper function. | |
*/ | |
function _mmmovies_api_store_movie_releases($releases, $month, $year) { | |
$user_name = variable_get('mmmovies_user_name', 'Mmmovies'); | |
$user = user_load_by_name($user_name); | |
$count = count($releases); | |
$lang = LANGUAGE_NONE; | |
$query = "SELECT * FROM {node} WHERE title = :title"; | |
$result = db_query($query, array(':title' => $month . '-' . $year))->fetchAll(); | |
if (!empty($result[0])) { | |
$count = count($releases); | |
// Update existing node with new revision. | |
$node = node_load($result[0]->nid); | |
$node->field_mmmovies_api_count[$lang][0]['value'] = $count; | |
$node->body[$lang][0]['value'] = serialize($releases); | |
$node->revision = 1; | |
$node->log = "New data imported for $count movie releases."; | |
//$node->changed = REQUEST_TIME; | |
node_save($node); | |
} | |
else { | |
// Create a new node. | |
$node = new stdClass(); | |
$node->type = "movie_releases"; | |
$node->language = $lang; | |
$node->title = $month . '-' . $year; | |
$node->uid = $user->uid; | |
$node->field_mmmovies_api_count[$lang][0]['value'] = $count; | |
$node->body[$lang][0]['value'] = serialize($releases); | |
node_save($node); | |
} | |
} | |
/** | |
* Implements hook_node_view_alter(). | |
*/ | |
function mmmovies_api_node_view_alter(&$build) { | |
if ($build['#bundle'] == 'movie_releases') { | |
// Hide the serialized data (in the body field). | |
$build['body']['#access'] = FALSE; | |
} | |
} | |
/** | |
* Implements hook_form_alter(). | |
*/ | |
function mmmovies_api_form_alter(&$form, &$form_state, $form_id) { | |
if ($form_id == 'movie_releases_node_form') { | |
// Protect serialized data from user modification. | |
$form['body']['#disabled'] = TRUE; | |
} | |
} | |
/** | |
* Implements hook_preprocess_node(). | |
*/ | |
function mmmovies_api_preprocess_node(&$variables) { | |
if ($variables['type'] == 'movie_releases') { | |
// Disable display of 'Submitted by' information | |
$variables['display_submitted'] = FALSE; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment