Skip to content

Instantly share code, notes, and snippets.

@joates
Last active August 29, 2015 14:05
Show Gist options
  • Save joates/3cf7e514a05393e892f8 to your computer and use it in GitHub Desktop.
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.
<?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;
}
}
<?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