From 15d72b1f333287a04af46cac2df865b207846552 Mon Sep 17 00:00:00 2001 From: Jonathan Hedstrom Date: Wed, 25 Mar 2015 09:44:37 -0700 Subject: [PATCH] Merge pull request #26 from dawehner/drupal-6 Initial version of the Drupal 6 support for extension ~3.0 Signed-off-by: Jonathan Hedstrom --- spec/Drupal/Driver/Cores/Drupal6Spec.php | 26 + src/Drupal/Driver/Cores/CoreInterface.php | 2 + src/Drupal/Driver/Cores/Drupal6.php | 447 ++++++++++++++++++ .../Driver/Fields/Drupal6/TaxonomyHandler.php | 45 ++ 4 files changed, 520 insertions(+) create mode 100644 spec/Drupal/Driver/Cores/Drupal6Spec.php create mode 100644 src/Drupal/Driver/Cores/Drupal6.php create mode 100644 src/Drupal/Driver/Fields/Drupal6/TaxonomyHandler.php diff --git a/spec/Drupal/Driver/Cores/Drupal6Spec.php b/spec/Drupal/Driver/Cores/Drupal6Spec.php new file mode 100644 index 00000000..79eed1a2 --- /dev/null +++ b/spec/Drupal/Driver/Cores/Drupal6Spec.php @@ -0,0 +1,26 @@ +beConstructedWith('path', 'http://www.example.com', $random); + } + + function it_is_initializable() + { + $this->shouldHaveType('Drupal\Driver\Cores\Drupal6'); + } + + function it_should_return_a_random_generator() + { + $this->getRandom()->shouldBeAnInstanceOf('Drupal\Component\Utility\Random'); + } +} diff --git a/src/Drupal/Driver/Cores/CoreInterface.php b/src/Drupal/Driver/Cores/CoreInterface.php index e083adbb..cea7da1c 100644 --- a/src/Drupal/Driver/Cores/CoreInterface.php +++ b/src/Drupal/Driver/Cores/CoreInterface.php @@ -89,6 +89,8 @@ public function userAddRole(\stdClass $user, $role_name); */ public function validateDrupalSite(); + public function processBatch(); + /** * Create a taxonomy term. */ diff --git a/src/Drupal/Driver/Cores/Drupal6.php b/src/Drupal/Driver/Cores/Drupal6.php new file mode 100644 index 00000000..9db83186 --- /dev/null +++ b/src/Drupal/Driver/Cores/Drupal6.php @@ -0,0 +1,447 @@ +drupalRoot); + require_once DRUPAL_ROOT . '/includes/bootstrap.inc'; + $this->validateDrupalSite(); + } + + // Bootstrap Drupal. + $current_path = getcwd(); + chdir(DRUPAL_ROOT); + drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + if (empty($GLOBALS['db_url'])) { + throw new BootstrapException('Missing database setting, verify the database configuration in settings.php.'); + } + drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); + chdir($current_path); + } + + /** + * {@inheritDoc} + */ + public function clearCache() { + // Need to change into the Drupal root directory or the registry explodes. + $current_path = getcwd(); + chdir(DRUPAL_ROOT); + drupal_flush_all_caches(); + chdir($current_path); + } + + /** + * {@inheritDoc} + */ + public function nodeCreate($node) { + $current_path = getcwd(); + chdir(DRUPAL_ROOT); + + // Set original if not set. + if (!isset($node->original)) { + $node->original = clone $node; + } + + // Assign authorship if none exists and `author` is passed. + if (!isset($node->uid) && !empty($node->author) && ($user = user_load(array('name' => $node->author)))) { + $node->uid = $user->uid; + } + + // Convert properties to expected structure. + $this->expandEntityProperties($node); + + // Attempt to decipher any fields that may be specified. + $this->expandEntityFields('node', $node); + + // Set defaults that haven't already been set. + $defaults = clone $node; + module_load_include('inc', 'node', 'node.pages'); + node_object_prepare($defaults); + $node = (object) array_merge((array) $defaults, (array) $node); + + node_save($node); + + chdir($current_path); + return $node; + + } + + /** + * {@inheritDoc} + */ + public function nodeDelete($node) { + node_delete($node->nid); + } + + /** + * Implements CoreInterface::runCron(). + */ + public function runCron() { + return drupal_cron_run(); + } + + /** + * {@inheritDoc} + */ + public function userCreate(\stdClass $user) { + // Default status to TRUE if not explicitly creating a blocked user. + if (!isset($user->status)) { + $user->status = 1; + } + + // Clone user object, otherwise user_save() changes the password to the + // hashed password. + $account = clone $user; + // Convert role array to a keyed array. + if (isset($user->roles)) { + $roles = array(); + foreach ($user->roles as $rid) { + $roles[$rid] = $rid; + } + $user->roles = $roles; + } + $account = user_save((array) $account, (array) $account); + // Store the UID + $user->uid = $account->uid; + return $user; + } + + /** + * {@inheritDoc} + */ + public function userDelete(\stdClass $user) { + $current_path = getcwd(); + chdir(DRUPAL_ROOT); + user_delete((array) $user, $user->uid); + chdir($current_path); + } + + /** + * {@inheritdoc} + */ + public function processBatch() { } + + /** + * {@inheritDoc} + */ + public function userAddRole(\stdClass $user, $role_name) { + $roles = array_flip(user_roles()); + $role = $roles[$role_name]; + if (!$role) { + throw new \RuntimeException(sprintf('No role "%s" exists.', $role_name)); + } + user_multiple_role_edit(array($user->uid), 'add_role', $role); + } + + /** + * Fetches a user role by role name. + * + * @param $role_name + * A string representing the role name. + * + * @return + * A fully-loaded role object if a role with the given name exists, or FALSE + * otherwise. + * + * @see user_role_load() + */ + protected function userRoleLoadByName($role_name) { + $result = db_query('SELECT * FROM {role} WHERE name = "%s"', $role_name); + return db_fetch_object($result); + } + + /** + * Check to make sure that the array of permissions are valid. + * + * @param array $permissions + * Permissions to check. + * @param bool $reset + * Reset cached available permissions. + * @return bool TRUE or FALSE depending on whether the permissions are valid. + */ + protected function checkPermissions(array $permissions, $reset = FALSE) { + + if (!isset($this->availablePermissons) || $reset) { + $this->availablePermissons = array_keys(module_invoke_all('permission')); + } + + $valid = TRUE; + foreach ($permissions as $permission) { + if (!in_array($permission, $this->availablePermissons)) { + $valid = FALSE; + } + } + return $valid; + } + + /** + * {@inheritDoc} + */ + public function roleCreate(array $permissions) { + // Verify permissions exist. + $all_permissions = module_invoke_all('perm'); + foreach ($permissions as $name) { + $search = array_search($name, $all_permissions); + if (!$search) { + throw new \RuntimeException(sprintf("No permission '%s' exists.", $name)); + } + } + // Create new role. + $name = $this->random->name(8); + db_query("INSERT INTO {role} SET name = '%s'", $name); + // Add permissions to role. + $rid = db_last_insert_id('role', 'rid'); + db_query("INSERT INTO {permission} (rid, perm) VALUES (%d, '%s')", $rid, implode(', ', $permissions)); + return $name; + } + + /** + * {@inheritDoc} + */ + public function roleDelete($role_name) { + $roles = array_flip(user_roles()); + $rid = $roles[$role_name]; + db_query('DELETE FROM {role} WHERE rid = %d', $rid); + if (!db_affected_rows()) { + throw new \RuntimeException(sprintf('No role "%s" exists.', $rid)); + } + } + + /** + * {@inheritDoc} + */ + public function validateDrupalSite() { + if ('default' !== $this->uri) { + // Fake the necessary HTTP headers that Drupal needs: + $drupal_base_url = parse_url($this->uri); + // If there's no url scheme set, add http:// and re-parse the url + // so the host and path values are set accurately. + if (!array_key_exists('scheme', $drupal_base_url)) { + $drupal_base_url = parse_url($this->uri); + } + // Fill in defaults. + $drupal_base_url += array( + 'path' => NULL, + 'host' => NULL, + 'port' => NULL, + ); + $_SERVER['HTTP_HOST'] = $drupal_base_url['host']; + + if ($drupal_base_url['port']) { + $_SERVER['HTTP_HOST'] .= ':' . $drupal_base_url['port']; + } + $_SERVER['SERVER_PORT'] = $drupal_base_url['port']; + + if (array_key_exists('path', $drupal_base_url)) { + $_SERVER['PHP_SELF'] = $drupal_base_url['path'] . '/index.php'; + } + else { + $_SERVER['PHP_SELF'] = '/index.php'; + } + } + else { + $_SERVER['HTTP_HOST'] = 'default'; + $_SERVER['PHP_SELF'] = '/index.php'; + } + + $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF']; + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $_SERVER['REQUEST_METHOD'] = NULL; + + $_SERVER['SERVER_SOFTWARE'] = NULL; + $_SERVER['HTTP_USER_AGENT'] = NULL; + + $conf_path = conf_path(TRUE, TRUE); + $conf_file = $this->drupalRoot . "/$conf_path/settings.php"; + if (!file_exists($conf_file)) { + throw new BootstrapException(sprintf('Could not find a Drupal settings.php file at "%s"', $conf_file)); + } + $drushrc_file = $this->drupalRoot . "/$conf_path/drushrc.php"; + if (file_exists($drushrc_file)) { + require_once $drushrc_file; + } + } + + /** + * Given an entity object, expand any property fields to the expected structure. + */ + protected function expandEntityProperties(\stdClass $entity) { + // The created field may come in as a readable date, rather than a timestamp. + if (isset($entity->created) && !is_numeric($entity->created)) { + $entity->created = strtotime($entity->created); + } + + // Map human-readable node types to machine node types. + $types = node_get_types(); + foreach ($types as $type) { + if ($entity->type == $type->name) { + $entity->type = $type->type; + continue; + } + } + } + + /** + * Load vocabularies, optional by VIDs. + * + * @param array $vids + * The vids to load + * + * @return array + * An array of vocabulary objects + */ + protected function taxonomyVocabularyLoadMultiple($vids = array()) { + $vocabularies = taxonomy_get_vocabularies(); + if ($vids) { + return array_intersect_key($vocabularies, array_flip($vids)); + } + return $vocabularies; + } + + /** + * {@inheritDoc} + */ + public function termCreate(\stdClass $term) { + // Map vocabulary names to vid, these take precedence over machine names. + if (!isset($term->vid)) { + $vocabularies = \taxonomy_get_vocabularies(); + foreach ($vocabularies as $vid => $vocabulary) { + if ($vocabulary->name == $term->vocabulary_machine_name) { + $term->vid = $vocabulary->vid; + } + } + } + + if (!isset($term->vid)) { + + // Try to load vocabulary by machine name. + $vocabularies = $this->taxonomyVocabularyLoadMultiple(array($term->vid)); + if (!empty($vocabularies)) { + $vids = array_keys($vocabularies); + $term->vid = reset($vids); + } + } + + // If `parent` is set, look up a term in this vocab with that name. + if (isset($term->parent)) { + $parent = \taxonomy_get_term_by_name($term->parent); + if (!empty($parent)) { + $parent = reset($parent); + $term->parent = $parent->tid; + } + } + + if (empty($term->vid)) { + throw new \Exception(sprintf('No "%s" vocabulary found.')); + } + + // Attempt to decipher any fields that may be specified. + $this->expandEntityFields('taxonomy_term', $term); + + // Protect against a failure from hook_taxonomy_term_insert() in pathauto. + $current_path = getcwd(); + chdir(DRUPAL_ROOT); + $term_array = (array) $term; + \taxonomy_save_term($term_array); + chdir($current_path); + + // Loading a term by name returns an array of term objects, but there should + // only be one matching term in a testing context, so take the first match + // by reset()'ing $matches. + $matches = \taxonomy_get_term_by_name($term->name); + $saved_term = reset($matches); + + return $saved_term; + } + + /** + * {@inheritDoc} + */ + public function termDelete(\stdClass $term) { + $status = 0; + if (isset($term->tid)) { + $status = \taxonomy_del_term($term->tid); + } + // Will be SAVED_DELETED (3) on success. + return $status; + } + + /** + * Helper function to get all permissions. + * + * @return array + * Array keyed by permission name, with the human-readable title as the value. + */ + protected function getAllPermissions() { + $permissions = array(); + foreach (module_invoke_all('permission') as $name => $permission) { + $permissions[$name] = $permission['title']; + } + return $permissions; + } + + /** + * {@inheritDoc} + */ + public function getModuleList() { + return module_list(); + } + + protected function expandEntityFields($entity_type, \stdClass $entity) { + return parent::expandEntityFields($entity_type, $entity); + } + + /** + * {@inheritDoc} + */ + public function getEntityFieldTypes($entity_type) { + $taxonomy_fields = ['taxonomy' => 'taxonomy']; + if (!module_exists('content')) { + return $taxonomy_fields; + } + $return = array(); + $fields = content_fields(); + foreach ($fields as $field_name => $field) { + if ($this->isField($entity_type, $field_name)) { + $return[$field_name] = $field['type']; + } + } + + $return += $taxonomy_fields; + + return $return; + } + + /** + * {@inheritDoc} + */ + public function isField($entity_type, $field_name) { + if ($field_name === 'taxonomy') { + return TRUE; + } + if (!module_exists('content')) { + return FALSE; + } + $map = content_fields(); + return isset($map[$field_name]); + } + +} diff --git a/src/Drupal/Driver/Fields/Drupal6/TaxonomyHandler.php b/src/Drupal/Driver/Fields/Drupal6/TaxonomyHandler.php new file mode 100644 index 00000000..7d5cc5b1 --- /dev/null +++ b/src/Drupal/Driver/Fields/Drupal6/TaxonomyHandler.php @@ -0,0 +1,45 @@ +