You are here

drupalgap.module in DrupalGap 7

A module to provide a bridge between Drupal websites and PhoneGap mobile applications.

File

drupalgap.module
View source
<?php

/**
 * @file
 * A module to provide a bridge between Drupal websites and PhoneGap mobile
 * applications.
 */

/**
 * Implements hook_help().
 */
function drupalgap_help($path, $arg) {
  switch ($path) {
    case 'admin/help#drupalgap':

      // Show documentation URL.
      $doc_msg = t('The online <a href="@doc_url">DrupalGap Documentation</a> contains
         more information on help topics.', array(
        '@doc_url' => 'http://drupal.org/node/1603690',
      ));
      return "<p>{$doc_msg}</p>";
      break;
  }
}

/**
 * Implements hook_permission().
 */
function drupalgap_permission() {
  return array(
    'administer drupalgap' => array(
      'title' => t('Administer DrupalGap'),
      'description' => t('Perform administration tasks for DrupalGap.'),
    ),
    'drupalgap get vocabularies' => array(
      'title' => t('Get Vocabularies'),
      'description' => t('Retrieve basic data about vocabularies.'),
    ),
    'drupalgap get terms' => array(
      'title' => t('Get Terms'),
      'description' => t('Retrieve basic data about vocabularies terms.'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function drupalgap_menu() {
  $items = array();
  $items['admin/config/services/drupalgap'] = array(
    'title' => 'DrupalGap',
    'type' => MENU_NORMAL_ITEM,
    'description' => 'The status of DrupalGap.',
    'page callback' => 'drupalgap_status',
    'access arguments' => array(
      'administer drupalgap',
    ),
    'file' => 'drupalgap.pages.inc',
  );
  $items['admin/config/services/drupalgap/download-app'] = array(
    'title' => 'Download app',
    'type' => MENU_CALLBACK,
    'page callback' => 'drupalgap_download_app',
    'access arguments' => array(
      'administer drupalgap',
    ),
    'file' => 'drupalgap.pages.inc',
  );
  return $items;
}

/**
 * Implements hook_views_api().
 */
function drupalgap_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'drupalgap'),
  );
}

/**
 * Implements hook_ctools_plugin_api().
 */
function drupalgap_ctools_plugin_api($owner, $api) {
  if ($owner == 'services' && $api == 'services') {
    return array(
      'version' => 3,
      'file' => 'drupalgap.services.inc',
    );
  }
}

/**
* Implements hook_entity_info_alter().
*/
function drupalgap_entity_info_alter(&$entity_info) {

  // Add a DrupalGap view mode to nodes and users.
  $entity_info['node']['view modes']['drupalgap'] = array(
    'label' => t('DrupalGap'),
    'custom settings' => TRUE,
  );
  $entity_info['user']['view modes']['drupalgap'] = array(
    'label' => t('DrupalGap'),
    'custom settings' => TRUE,
  );
}

/**
 * Implements hook_services_request_postprocess_alter().
 */
function drupalgap_services_request_postprocess_alter($controller, $args, &$result) {
  switch ($controller['callback']) {
    case '_system_resource_connect':

      // If the caller wants to skip extras, let them.
      if (isset($_GET['dgSkipExtras']) && $_GET['dgSkipExtras']) {
        return;
      }

      // Grab the extras DG provides to system connect and append them to result.
      module_load_include('inc', 'drupalgap', 'drupalgap.resource');
      $extras = drupalgap_system_connect_extras(false);
      foreach ($extras as $key => $value) {
        $result->{$key} = $value;
      }
      break;
    case '_comment_resource_retrieve':
    case '_comment_resource_index':

      // Iterate over all of the results and pull out node ids and user ids from
      // the comments.
      $nids = null;
      $uids = null;
      if ($controller['callback'] == '_comment_resource_index') {
        $nids = array();
        $uids = array();
        foreach ($result as $i => $comment) {
          if (!in_array($comment->nid, $nids)) {
            $nids[] = $comment->nid;
          }
          if (!in_array($comment->uid, $uids)) {
            $uids[] = $comment->uid;
          }
        }
      }
      else {

        // We're loading a comment, so just extract the single ids.
        $nids = $result->nid;
        $uids = $result->uid;
      }

      // On the comment retrieve resource, add the bundle to the result, it is
      // stored in the node_tye property.
      if ($controller['callback'] == '_comment_resource_retrieve') {
        $result->bundle = $result->node_type;
      }

      // If user profile pictures are enabled, add the picture uri to the
      // comment result(s).
      if (variable_get('user_pictures', 0)) {
        if ($controller['callback'] == '_comment_resource_retrieve' && $uids != 0) {
          watchdog('drupalgap', 'uid');
          $sql = "SELECT fm.uri, u.uid FROM {file_managed} fm " . "INNER JOIN {users} u ON u.picture = fm.fid WHERE u.uid = :uids";
          $picture = db_query($sql, array(
            ':uids' => $uids,
          ))
            ->fetch();
          $result->picture_uri = isset($picture->uri) ? $picture->uri : NULL;
        }
        else {
          if (!empty($uids)) {
            watchdog('drupalgap', 'uids');
            watchdog('drupalgap', '<pre>' . print_r($uids, true) . '</pre>');
            $sql = "SELECT fm.uri, u.uid FROM {file_managed} fm " . "INNER JOIN {users} u ON u.picture = fm.fid WHERE u.uid IN (:uids)";
            $pictures = db_query($sql, array(
              ':uids' => $uids,
            ))
              ->fetchAll();
            watchdog('drupalgap', '<pre>' . print_r($pictures, true) . '</pre>');
            foreach ($result as $i => $comment) {
              $uri = null;
              foreach ($pictures as $picture) {
                if ($picture->uid == $comment->uid) {
                  $uri = $picture->uri;
                  break;
                }
              }
              if ($uri) {
                $result[$i]->picture_uri = $uri;
              }
            }
          }
        }
      }

      // Let's add any custom fields to the index results.
      if ($controller['callback'] == '_comment_resource_index' && !empty($nids)) {

        // Grab a list of all the nids and their types, then build an array of
        // types keyed by their nid.
        $sql = "SELECT n.nid, n.type FROM {node} n WHERE n.nid IN (:nids)";
        $nids_and_types = db_query($sql, array(
          ':nids' => $nids,
        ))
          ->fetchAll();
        $types = array();
        foreach ($nids_and_types as $item) {
          $types[$item->nid] = $item->type;
        }

        // Build an array of entities, keyed by the entity id, and attach the
        // bundle to each, so we can load and attach the fields to each. We need
        // to attach the node_type property to comment entities, it is equal to
        // the bundle value, or we won't be able to always load the fields
        // properly.
        $entities = array();
        foreach ($result as $i => $comment) {
          $result[$i]->bundle = 'comment_node_' . $types[$comment->nid];
          $result[$i]->node_type = $result[$i]->bundle;
          $entities[$comment->cid] = $comment;
        }

        // @todo - what is this line used for?
        if (!empty($entities)) {
          field_attach_load('comment', $entities);
        }
        _drupalgap_add_term_data_to_entities($controller, $args, $result);
      }
      break;
    case '_node_resource_retrieve':
      if (isset($result->rdf_mapping)) {
        unset($result->rdf_mapping);
      }
      _drupalgap_add_term_data_to_entities($controller, $args, $result);
      break;
    case '_user_resource_login':

      // Remove rdf_mapping from user object.
      if ($result && isset($result->user) && isset($result->user->rdf_mapping)) {
        unset($result->user->rdf_mapping);
      }
      break;
  }
}

/**
 *
 */
function drupalgap_system_connect_extras($system_connect_call = true) {
  global $user;
  $results = array();
  if ($system_connect_call) {

    // Make a call to the system connect resource.
    module_load_include('inc', 'services', 'resources/system_resource');
    $results['system_connect'] = _system_resource_connect();
  }

  // Grab the ip address.
  $results['remote_addr'] = $_SERVER['REMOTE_ADDR'];

  // Grab the date formats and types.
  $results['date_formats'] = system_get_date_formats();
  $results['date_types'] = system_get_date_types();

  // For each date type, look in the variable table to find its format. Not all
  // types will have a format.
  foreach ($results['date_types'] as $name => $type) {
    $value = variable_get('date_format_' . $name, false);
    if ($value) {
      $results['date_types'][$name]['format'] = $value;
    }
  }

  // Make calls to various DrupalGap resources.
  $results['site_settings'] = _drupalgap_resource_system_site_settings();
  $results['user_permissions'] = _drupalgap_resource_user_permissions(array(
    'uid' => $user->uid,
  ));
  $results['content_types_list'] = _drupalgap_resource_content_types_list();
  $results['content_types_user_permissions'] = _drupalgap_resource_content_types_user_permissions();
  $results['entity_info'] = entity_get_info();
  $results['field_info_instances']['node'] = field_info_instances('node');
  $results['field_info_instances']['comment'] = field_info_instances('comment');
  $results['field_info_instances']['taxonomy_vocabulary'] = field_info_instances('taxonomy_vocabulary');
  $results['field_info_instances']['taxonomy_term'] = field_info_instances('taxonomy_term');
  $results['field_info_instances']['user'] = field_info_instances('user');
  $results['field_info_fields'] = field_info_fields();
  $results['field_info_extra_fields'] = array();
  foreach ($results['content_types_list'] as $content_type) {
    $results['field_info_extra_fields'][$content_type->type] = field_info_extra_fields('node', $content_type->type, 'form');
  }
  $results['taxonomy_vocabularies'] = _drupalgap_taxonomy_get_vocabularies();

  // For any image fields with a default image, include the file uri.
  $image_fields = field_read_fields(array(
    'type' => 'image',
  ));
  foreach ($image_fields as $field_name => $field) {
    if (!$results['field_info_fields'][$field_name]['settings']['default_image']) {
      continue;
    }
    $fid = $results['field_info_fields'][$field_name]['settings']['default_image'];
    if (!($file = file_load($fid))) {
      continue;
    }
    $results['field_info_fields'][$field_name]['settings']['default_image_uri'] = $file->uri;
  }

  // Finally return the results.
  return $results;
}

/**
 * Implements hook_preprocess_views_views_json_style_simple().
 */
function drupalgap_preprocess_views_views_json_style_simple(&$vars) {

  // Place various view object properties and pager data onto the results.
  global $pager_total, $pager_page_array, $pager_total_items, $pager_limits;
  $view = array(
    'name' => $vars['view']->name,
    'display' => $vars['view']->current_display,
    'path' => implode('/', arg()),
    'root' => $vars['options']['root_object'],
    'child' => $vars['options']['top_child_object'],
    'pages' => $pager_total[0],
    'page' => $pager_page_array[0],
    'count' => intval($pager_total_items[0]),
    'limit' => intval($pager_limits[0]),
  );

  // If there were no pages, adjust the property values accordingly.
  if ($view['pages'] == null) {
    $view['count'] = sizeof($vars['rows'][$view['root']]);
    $view['limit'] = null;
  }

  // If there are any exposed filters, attach them to the view object.
  if (isset($vars['view']->exposed_data)) {
    $view['exposed_data'] = $vars['view']->exposed_data;
    $view['exposed_raw_input'] = $vars['view']->exposed_raw_input;
    $view['filter'] = array();
    foreach ($vars['view']->filter as $field => $filter) {
      if ($filter->options['exposed']) {

        // The site builder didn't select any available options for this
        // exposed filter, but that's OK, they probably want all options
        // available.
        $value_options = isset($filter->value_options) ? $filter->value_options : NULL;

        // The entire field is huge in size and complexity, so let's trim it
        // down to the bare necessities.
        // @TODO - as the Views Exposed Filters feature evolves, revisit this
        // from time to time to see if we need to drop anything else. Anything
        // to help slim the amount of JSON is a good thing.
        $view['filter'][$field] = array(
          //'accept_null' => $filter->accept_null,

          //'always_multiple' => $filter->always_multiple,

          //'always_required' => $filter->always_required,
          'definition' => $filter->definition,
          //'field' => $filter->field,

          //'group_info' => $filter->group_info,

          //'localization_keys' => $filter->localization_keys,

          //'no_operator' => $filter->no_operator,

          //'operator' => $filter->operator,
          'options' => $filter->options,
          //'position' => $filter->position,

          //'real_field' => $filter->real_field,

          //'relationship' => $filter->relationship,
          'value' => $filter->value,
          'value_options' => $value_options,
        );
      }
    }
  }

  // Attach the view data to the rows result.
  $vars['rows']['view'] = $view;
}

/**
 *
 */
function drupalgap_sdk_form($form, &$form_state) {
  global $base_url;

  // Check installation status.
  $installed = variable_get('drupalgap_sdk_installed', 0);
  $dir = variable_get('drupalgap_sdk_dir', 'mobile-application');
  if (!$installed) {

    // Many existing sites will already have the mobile-application directory in
    // use, let's auto detect that.
    if (file_exists($dir)) {
      $installed = 1;
      variable_set('drupalgap_sdk_installed', $installed);
      variable_set('drupalgap_sdk_dir', $dir);
    }
    else {
      $form['#prefix'] = t('With one click, you can install the DrupalGap SDK alongside your Drupal
          site. This allows you to begin building your app immediately.');
    }
  }
  else {

    // If it's installed, but the directory is gone, let them start over.
    if (!file_exists($dir)) {
      $installed = 0;
      variable_set('drupalgap_sdk_installed', $installed);
    }
  }

  // Provide text field for SDK folder path.
  $form['dir'] = array(
    '#type' => 'textfield',
    '#title' => t('Directory'),
    '#default_value' => $dir,
    '#disabled' => $installed,
    '#field_prefix' => $base_url . '/',
    '#description' => $installed ? t('The SDK is already installed.') : t('Specify a folder name for the SDK to live in.'),
  );

  // Provide them with a link to view the app.
  if ($installed) {
    $demo_link = base_path() . $dir . '/index.html';
    $form['dir']['#suffix'] = '<div class="messages status">' . '<a href="' . $demo_link . '" target="_blank" title="' . t('Test your app in a browser') . '">' . t('Launch app') . '</a>' . ' (' . l(t('download'), 'admin/config/services/drupalgap/download-app', array(
      'attributes' => array(
        'title' => t('Download a zip file of your app'),
      ),
    )) . ' | ' . l(t('publish'), 'http://docs.drupalgap.org/7/Compiling_a_Mobile_Application', array(
      'attributes' => array(
        'target' => '_blank',
        'title' => t('Learn how to publish your app for users to download'),
      ),
    )) . ')' . '</div>';
  }
  if (!$installed) {
    $form['submit_button'] = array(
      '#type' => 'submit',
      '#value' => t('Install the SDK'),
    );
  }

  // Show helpful links.
  $form['#suffix'] = theme('item_list', array(
    'items' => array(
      l(t('Getting started guide'), 'http://drupalgap.org/get-started'),
      l(t('API'), 'http://api.drupalgap.org'),
      l(t('Support'), 'http://drupalgap.org/support'),
    ),
  ));
  return $form;
}

/**
 *
 */
function drupalgap_sdk_form_validate($form, &$form_state) {
  $dir = $form_state['values']['dir'];
  if (file_exists($dir)) {
    form_set_error('dir', t('That directory already exists!'));
  }
  else {
    if ($dir == 'drupalgap') {
      form_set_error('dir', t('The name "drupalgap" is reserved, choose a different name.'));
    }
  }
}

/**
 *
 */
function drupalgap_sdk_form_submit($form, &$form_state) {
  global $base_url;

  // Set up vars.
  $dir = check_plain($form_state['values']['dir']);
  $file = "drupalgap-sdk.zip";

  // Dev release.

  //$source = "https://github.com/signalpoint/DrupalGap/archive/7.x-1.x.zip";

  //$name = "DrupalGap-7.x-1.x";

  // Recommended release.
  $source = "https://github.com/signalpoint/DrupalGap/archive/7.0.2.zip";
  $name = "DrupalGap-7.0.2";

  // Download a zip file of the SDK from github.
  if (file_put_contents($file, file_get_contents($source)) === false) {
    return;
  }

  // Unzip the SDK.
  $path = pathinfo(realpath($file), PATHINFO_DIRNAME);
  $zip = new ZipArchive();
  $res = $zip
    ->open($file);
  if ($res === TRUE) {
    $zip
      ->extractTo($path);
    $zip
      ->close();
  }
  else {
    drupal_set_message("Failed to unzip the {$file} file!", 'error');
  }

  // Rename the folder.
  if (!rename($name, $dir)) {
    drupal_set_message("Failed to move unzipped files to the {$dir} directory!", 'error');
    return;
  }

  // Make a copy of default.settings.js, set the site_path and save it as settings.js.
  $settings = file_get_contents("{$dir}/app/default.settings.js");
  $settings = str_replace("Drupal.settings.site_path = '';", "Drupal.settings.site_path = '{$base_url}';", $settings);
  if (file_put_contents("{$dir}/app/settings.js", $settings) === false) {
    drupal_set_message(t('Failed to create the settings.js file!'), 'error');
  }

  // Delete the zip file.
  if (!unlink($file)) {
    drupal_set_message(t("Tried deleting the %file file, but failed", array(
      '%file' => $file,
    )), 'warning');
  }

  // Tell Drupal that everything is installed.
  variable_set('drupalgap_sdk_installed', 1);
  variable_set('drupalgap_sdk_dir', $dir);
  drupal_set_message(t('The DrupalGap SDK was installed successfully! You may now test it below...'));
}

/**
 * Renders the widget to control the module's for the app from the settings page.
 */
function drupalgap_modules_widget() {
  if (!variable_get('drupalgap_sdk_installed', 0)) {
    return '';
  }
  $items = array(
    l(t('Browse contrib modules'), 'http://drupalgap.org/project/modules', array(
      'attributes' => array(
        'target' => '_blank',
      ),
    )),
    l(t('Module help'), 'http://drupalgap.org/node/74', array(
      'attributes' => array(
        'target' => '_blank',
      ),
    )),
  );
  $module_install_form = drupal_get_form('drupalgap_module_install_form');
  $html = '<h2>' . t('Modules') . '</h2>' . theme('item_list', array(
    'items' => $items,
  )) . drupal_render($module_install_form);
  $modules = drupalgap_load_modules('contrib');
  if ($modules) {

    //dpm($modules);
    $rows = array();
    foreach ($modules[0] as $delta => $line) {
      $module = $modules[1][$delta];
      $rows[] = array(
        l($module, 'http://drupalgap.org/project/' . check_plain($module), array(
          'attributes' => array(
            'target' => '_blank',
          ),
        )),
      );
    }
    $html .= '<h3>Contrib</h3>' . theme('table', array(
      'rows' => $rows,
    ));
  }
  return $html;
}

/**
 *
 */
function drupalgap_module_install_form($form, &$form_state) {
  $form['module'] = array(
    '#type' => 'textfield',
    '#title' => t('Install module'),
    '#description' => t('Enter the <em>machine name</em> of the module.'),
    '#field_prefix' => 'http://drupalgap.org/project/',
    '#size' => 16,
    '#required' => TRUE,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Install'),
  );
  return $form;
}

/**
 *
 */
function drupalgap_module_install_form_validate($form, &$form_state) {
}

/**
 *
 */
function drupalgap_module_install_form_submit($form, &$form_state) {
  $module_details = drupalgap_load_module_details($form_state['values']['module']);
  drupalgap_download_and_extract_module_from_github($module_details);
  drupalgap_add_module_to_settings_file($module_details['github']['name']);
}

/**
 *
 */
function drupalgap_path_to_settings_file() {
  return variable_get('drupalgap_sdk_dir', 'mobile-application') . '/app/settings.js';
}

/**
 * Loads the settings.js file as string.
 */
function drupalgap_load_settings_as_string() {
  if (!drupalgap_sdk_installed()) {
    return false;
  }
  return file_get_contents(drupalgap_path_to_settings_file());
}

/**
 * Returns TRUE if the SDK is installed, FALSE otherwise.
 */
function drupalgap_sdk_installed() {
  return variable_get('drupalgap_sdk_installed', 0);
}

/**
 * Given a module type (contrib, custom), this loads the modules listed in
 * settings.js and returns them as regex matches.
 */
function drupalgap_load_modules($type) {
  $settings = drupalgap_load_settings_as_string();

  // @see http://www.phpliveregex.com/
  // original regex: [^\/]Drupal\.modules\.contrib\['(.+)'] = {.*};
  $pattern = "/[^\\/]Drupal\\.modules\\.{$type}\\['(.+)'] = {.*};/";
  $modules = array();
  $result = preg_match_all($pattern, $settings, $modules);
  if (!$result) {
    return FALSE;
  }
  return $modules;
}

/**
 * Given a module name, this will ask drupalgap.org for its module details.
 */
function drupalgap_load_module_details($module) {
  $curl = curl_init();
  curl_setopt_array($curl, array(
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_URL => 'http://www.drupalgap.org/?q=drupalgap/dgm_resources/project_download',
    CURLOPT_POST => 1,
    CURLOPT_POSTFIELDS => 'project_name=' . check_plain($module),
  ));
  $result = drupal_json_decode(curl_exec($curl));
  curl_close($curl);
  return $result;
}

/**
 * Given a module's details, this will download its zip file from github,
 * extract it, delete the zip file, and move its contents to the app/modules
 * folder.
 */
function drupalgap_download_and_extract_module_from_github($module_details) {
  $dir = variable_get('drupalgap_sdk_dir', 'mobile-application');
  $file = $module_details['github']['name'] . '.zip';
  $source = $module_details['github']['html_url'] . '/archive/' . $module_details['github']['default_branch'] . '.zip';

  // Download a zip file of the module from github.
  if (file_put_contents($file, file_get_contents($source)) === false) {
    drupal_set_message(t('Failed to download module!'), 'error');
    return;
  }

  // Unzip the module.
  $path = pathinfo(realpath($file), PATHINFO_DIRNAME) . '/' . $dir . '/app/modules';
  if (!file_exists($path)) {
    mkdir($path);
  }
  $zip = new ZipArchive();
  $res = $zip
    ->open($file);
  if ($res === TRUE) {
    $zip
      ->extractTo($path);
    $zip
      ->close();
  }
  else {
    drupal_set_message("Failed to unzip the {$file} file!", 'error');
  }

  // Delete the zip file.
  if (!unlink($file)) {
    drupal_set_message(t("Tried deleting the %file file, but failed", array(
      '%file' => $file,
    )), 'warning');
  }

  // Rename the folder.
  if (!rename($path . '/' . $module_details['github']['name'] . '-' . $module_details['github']['default_branch'], $path . '/' . $module_details['github']['name'])) {
    drupal_set_message("Failed to move unzipped files to the {$path} directory!", 'error');
    return;
  }
  drupal_set_message("Installed module.");
}

/**
 * Given a module name, this will add it to the settings.js file, right after
 * the last module found in the file.
 */
function drupalgap_add_module_to_settings_file($module) {
  $modules = drupalgap_load_modules('contrib');
  if ($modules) {

    // A module(s) is installed, append this module after the last module.
    $look_for = $modules[0][sizeof($modules[0]) - 1];
  }
  else {

    // No modules are installed yet, add this module right after the module
    // comments. This is a terrible assumption, but we'll use it until we don't
    // have the settings scattered throughout a JS file like a dumb dumb.
    $look_for = '/** Contributed Modules - www/app/modules **/';
  }
  file_put_contents(drupalgap_path_to_settings_file(), str_replace($look_for, $look_for . "\nDrupal.modules.contrib['{$module}'] = {};", drupalgap_load_settings_as_string()));
}

/**
 * @see http://stackoverflow.com/a/1334949/763010
 */
function DrupalGapZip($source, $destination) {

  // credits: http://stackoverflow.com/users/89771/alix-axel
  if (!extension_loaded('zip') || !file_exists($source)) {
    return false;
  }
  $zip = new ZipArchive();
  if (!$zip
    ->open($destination, ZIPARCHIVE::CREATE)) {
    return false;
  }
  $source = str_replace('\\', '/', realpath($source));
  if (is_dir($source) === true) {
    $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
    foreach ($files as $file) {
      $file = str_replace('\\', '/', $file);

      // Ignore "." and ".." folders
      if (in_array(substr($file, strrpos($file, '/') + 1), array(
        '.',
        '..',
      ))) {
        continue;
      }
      $file = realpath($file);
      if (is_dir($file) === true) {
        $zip
          ->addEmptyDir(str_replace($source . '/', '', $file . '/'));
      }
      else {
        if (is_file($file) === true) {
          $zip
            ->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
        }
      }
    }
  }
  else {
    if (is_file($source) === true) {
      $zip
        ->addFromString(basename($source), file_get_contents($source));
    }
  }
  return $zip
    ->close();
}

/**
 *
 */
function _drupalgap_add_term_data_to_entities($controller, $args, &$result) {

  // Determine the entity type, or return if we don't need (or understand) how
  // to process it.
  $entity_type = null;
  $bundle = null;
  switch ($controller['callback']) {
    case '_comment_resource_index':
      $entity_type = 'comment';
      break;
    case '_node_resource_retrieve':
      $entity_type = 'node';
      $bundle = $result->type;
      break;
  }
  if (!$entity_type) {
    return;
  }
  if (is_array($result)) {

    // We have multiple results...
    foreach ($result as $i => $entity) {
      switch ($controller['callback']) {
        case '_comment_resource_index':
          $bundle = $entity->bundle;
          break;
        case '_node_resource_retrieve':
          $bundle = $entity->type;
          break;
      }
      $result[$i] = _drupalgap_add_term_data_to_entity($entity, $entity_type, $bundle);
    }
  }
  else {

    // We have a single result...
    $result = _drupalgap_add_term_data_to_entity($result, $entity_type, $bundle);
  }
}

/**
 *
 */
function _drupalgap_add_term_data_to_entity($entity, $entity_type, $bundle) {

  // Grab the field info instances for this entity type.
  $instances = field_info_instances($entity_type, $bundle);

  // Let's add taxonomy term names along with their ids onto taxonomy term
  // reference fields.
  foreach ($instances as $field => $instance) {
    if ($instance['widget']['module'] == 'taxonomy' || $instance['widget']['module'] == 'options' && isset($instance['display']['default']['module']) && (isset($instance['display']['default']['module']) && ($instance['display']['default']['module'] == 'taxonomy' || $instance['display']['default']['module'] == 'i18n_taxonomy'))) {

      // Determine the field's language from the node, and fall back to
      // 'und' if the field's language isn't set.
      $language = $entity->language;
      if (!isset($entity->{$field}[$language])) {
        $language = 'und';
      }

      // Extract the term ids.
      $tids = array();
      if (isset($entity->{$field}[$language])) {
        foreach ($entity->{$field}[$language] as $delta => $term) {
          $tids[] = $term['tid'];
        }
      }
      if (empty($tids)) {
        continue;
      }

      // Grab the term names.
      $sql = "SELECT t.tid, t.name FROM {taxonomy_term_data} t WHERE t.tid IN (:tids)";
      $terms = db_query($sql, array(
        ':tids' => $tids,
      ))
        ->fetchAll();
      if (empty($terms)) {
        continue;
      }
      foreach ($entity->{$field}[$language] as $delta => $term) {
        foreach ($terms as $_term) {
          if ($term['tid'] == $_term->tid) {
            $entity->{$field}[$language][$delta]['name'] = $_term->name;
            break;
          }
        }
      }
    }
  }
  return $entity;
}

/**
 * Implements hook_default_services_endpoint().
 */
function drupalgap_default_services_endpoint() {
  $endpoints = array();
  $endpoint = new stdClass();
  $endpoint->disabled = FALSE;

  /* Edit this to true to make a default endpoint disabled initially */
  $endpoint->api_version = 3;
  $endpoint->name = 'drupalgap';
  $endpoint->server = 'rest_server';
  $endpoint->path = 'drupalgap';
  $endpoint->authentication = array(
    'services' => 'services',
  );
  $endpoint->server_settings = array(
    'formatters' => array(
      'json' => TRUE,
      'bencode' => FALSE,
      'jsonp' => FALSE,
      'php' => FALSE,
      'rss' => FALSE,
      'xml' => FALSE,
    ),
    'parsers' => array(
      'application/json' => TRUE,
      'application/x-www-form-urlencoded' => TRUE,
      'application/xml' => TRUE,
      'multipart/form-data' => TRUE,
      'application/vnd.php.serialized' => FALSE,
      'text/xml' => FALSE,
    ),
  );
  $endpoint->resources = array(
    'drupalgap_content' => array(
      'actions' => array(
        'content_types_list' => array(
          'enabled' => '1',
        ),
        'content_types_user_permissions' => array(
          'enabled' => '1',
        ),
      ),
    ),
    'drupalgap_system' => array(
      'actions' => array(
        'site_settings' => array(
          'enabled' => '1',
        ),
        'connect' => array(
          'enabled' => '1',
        ),
      ),
    ),
    'drupalgap_taxonomy' => array(
      'actions' => array(
        'get_vocabularies' => array(
          'enabled' => '1',
        ),
        'get_terms' => array(
          'enabled' => '1',
        ),
      ),
    ),
    'drupalgap_user' => array(
      'actions' => array(
        'access' => array(
          'enabled' => '1',
        ),
        'login' => array(
          'enabled' => '1',
        ),
        'logout' => array(
          'enabled' => '1',
        ),
        'register' => array(
          'enabled' => '1',
        ),
        'roles_and_permissions' => array(
          'enabled' => '1',
        ),
        'permissions' => array(
          'enabled' => '1',
        ),
      ),
    ),
    'comment' => array(
      'operations' => array(
        'create' => array(
          'enabled' => '1',
        ),
        'retrieve' => array(
          'enabled' => '1',
        ),
        'update' => array(
          'enabled' => '1',
        ),
        'delete' => array(
          'enabled' => '1',
        ),
        'index' => array(
          'enabled' => '1',
        ),
      ),
      'actions' => array(
        'countAll' => array(
          'enabled' => '1',
        ),
      ),
    ),
    'file' => array(
      'operations' => array(
        'create' => array(
          'enabled' => '1',
        ),
        'retrieve' => array(
          'enabled' => '1',
        ),
        'delete' => array(
          'enabled' => '1',
        ),
        'index' => array(
          'enabled' => '1',
        ),
      ),
    ),
    'node' => array(
      'operations' => array(
        'retrieve' => array(
          'enabled' => '1',
        ),
        'create' => array(
          'enabled' => '1',
        ),
        'update' => array(
          'enabled' => '1',
        ),
        'delete' => array(
          'enabled' => '1',
        ),
        'index' => array(
          'enabled' => '1',
        ),
      ),
    ),
    'system' => array(
      'actions' => array(
        'connect' => array(
          'enabled' => '1',
        ),
        'get_variable' => array(
          'enabled' => '1',
        ),
        'set_variable' => array(
          'enabled' => '1',
        ),
        'del_variable' => array(
          'enabled' => '1',
        ),
      ),
    ),
    'taxonomy_term' => array(
      'operations' => array(
        'retrieve' => array(
          'enabled' => '1',
        ),
        'create' => array(
          'enabled' => '1',
        ),
        'update' => array(
          'enabled' => '1',
        ),
        'delete' => array(
          'enabled' => '1',
        ),
        'index' => array(
          'enabled' => '1',
        ),
      ),
      'actions' => array(
        'selectNodes' => array(
          'enabled' => '1',
        ),
      ),
    ),
    'taxonomy_vocabulary' => array(
      'operations' => array(
        'retrieve' => array(
          'enabled' => '1',
        ),
        'create' => array(
          'enabled' => '1',
        ),
        'update' => array(
          'enabled' => '1',
        ),
        'delete' => array(
          'enabled' => '1',
        ),
        'index' => array(
          'enabled' => '1',
        ),
      ),
      'actions' => array(
        'getTree' => array(
          'enabled' => '1',
        ),
      ),
    ),
    'user' => array(
      'operations' => array(
        'retrieve' => array(
          'enabled' => '1',
        ),
        'create' => array(
          'enabled' => '1',
        ),
        'update' => array(
          'enabled' => '1',
        ),
        'delete' => array(
          'enabled' => '1',
        ),
        'index' => array(
          'enabled' => '1',
        ),
      ),
      'actions' => array(
        'login' => array(
          'enabled' => '1',
          'settings' => array(
            'services' => array(
              'resource_api_version' => '1.0',
            ),
          ),
        ),
        'logout' => array(
          'enabled' => '1',
          'settings' => array(
            'services' => array(
              'resource_api_version' => '1.0',
            ),
          ),
        ),
        'register' => array(
          'enabled' => '1',
        ),
      ),
    ),
  );
  $endpoint->debug = 0;
  $endpoints[] = $endpoint;
  return $endpoints;
}

Functions

Namesort descending Description
DrupalGapZip
drupalgap_add_module_to_settings_file Given a module name, this will add it to the settings.js file, right after the last module found in the file.
drupalgap_ctools_plugin_api Implements hook_ctools_plugin_api().
drupalgap_default_services_endpoint Implements hook_default_services_endpoint().
drupalgap_download_and_extract_module_from_github Given a module's details, this will download its zip file from github, extract it, delete the zip file, and move its contents to the app/modules folder.
drupalgap_entity_info_alter Implements hook_entity_info_alter().
drupalgap_help Implements hook_help().
drupalgap_load_modules Given a module type (contrib, custom), this loads the modules listed in settings.js and returns them as regex matches.
drupalgap_load_module_details Given a module name, this will ask drupalgap.org for its module details.
drupalgap_load_settings_as_string Loads the settings.js file as string.
drupalgap_menu Implements hook_menu().
drupalgap_modules_widget Renders the widget to control the module's for the app from the settings page.
drupalgap_module_install_form
drupalgap_module_install_form_submit
drupalgap_module_install_form_validate
drupalgap_path_to_settings_file
drupalgap_permission Implements hook_permission().
drupalgap_preprocess_views_views_json_style_simple Implements hook_preprocess_views_views_json_style_simple().
drupalgap_sdk_form
drupalgap_sdk_form_submit
drupalgap_sdk_form_validate
drupalgap_sdk_installed Returns TRUE if the SDK is installed, FALSE otherwise.
drupalgap_services_request_postprocess_alter Implements hook_services_request_postprocess_alter().
drupalgap_system_connect_extras
drupalgap_views_api Implements hook_views_api().
_drupalgap_add_term_data_to_entities
_drupalgap_add_term_data_to_entity