diff --git a/README.txt b/README.txt index 3ed2ae992cd21986a008cdf50e536782cf6d5869..723f2d0e8fd7bf38d09238c4ab0709216065336c 100644 --- a/README.txt +++ b/README.txt @@ -47,32 +47,6 @@ to provides localized version of a same item. Support of Tooltip for item rollovers. Use wowhead provider by default. You can also provide your own representation of an item. -Important note about Items: Due to a bug in D7 (http://drupal.org/node/1284332) -if you install the wow_item module, you will not be able to uninstall it. -A field is used to handle serveral locales for the item entity, and the module -declaring the field use it in the same time. This will be fixed in a future -patch and already fixed in D8 (http://drupal.org/node/943772). - Realms: Provides realm status information as Drupal Entity. Provides an ajax auto-completion callback for Realm selection textfield. - -Work in progress -Support of the PVP Resources: this will be a new module included in the default -package. The base component and service communication will be fully implemented, -however additional features will be covered in more specific modules. - -Support of the Auction Resources: same as above. -Realm: Provides a new form widget, realm selector with ajax auto-completion. -Provides a batch processing queue system instead of the drupal cron. Each time -you need to update a guild, character, it gets queued and executed on demand, or -automatically. - -Possible design improvement -In a future version it will completely separate framework and product. For -instance the guild module allows to enable 'guild' entity with its own database -table. This entity is nice for website working with guild only such as -wowprogress.com. However, the guild module also set a default guild used to -define if a user belong to a guild or not. Separate thoses functionalities will -allows community developers to use the default guild functionality and guild -progress website not to use it for instance. diff --git a/UPGRADE.txt b/UPGRADE.txt index c03fca3dcc6f44fd1dac6ff2e65fb5c2a00c6acd..c20e5521af9e6b20532070837fd918c06c718ef0 100755 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -1,19 +1,33 @@ +

2.x upgrade instructions to 3.x:

+ +The 3.x release focus on more unit-testable API and a better quality class +design for developers. + +- First ensure you are running the last available version of the 2.x branch +and updates has been applied. + +- Dependencies: No new dependencies has been added for this release. + +- Put your site in maintenance mode and visit the update.php script. Apply the + updates and check nothing has failed. + +

1.x upgrade instructions to 2.x:

Upgrading is strongly advised, among many bug fixes there is a new architecture granting both scalability and stability. More than 500 unit, functional and -integration tests has been written for this release. +integration tests has been written for the 2.x release. - First ensure you are running the last available version of the 1.x branch and updates has been applied. - Dependencies: Download and install the entity module. -- Important: Disable the WoW Guild Rank module before any atempt to upgrade to -2.x. Do not uninstall it, just disable it. +- Important: Disable the WoW Guild Rank module before any attempt to upgrade to +2.x. Do not un-install it, just disable it. - Put your site in maintenance mode and visit the update.php script. Apply the updates and check nothing has failed. - (Optional) If you previously disabled the WoW Guild Rank module, you can -re-enable it. \ No newline at end of file +re-enable it. diff --git a/lib/WoW/Core/Callback.php b/lib/WoW/Core/Callback.php new file mode 100755 index 0000000000000000000000000000000000000000..1d38b9fb821657fe4027387692c13bb9c872d48a --- /dev/null +++ b/lib/WoW/Core/Callback.php @@ -0,0 +1,19 @@ +entityType = $entity_type; - $this->entityInfo = $entity_info; - $this->idKey = $entity_info['entity keys']['id']; - $this->storage = entity_get_controller($entity_type); + public function __construct($entityType, array $entityInfo, EntityAPIControllerInterface $storage) { + $this->entityType = $entityType; + $this->entityInfo = $entityInfo; + $this->idKey = $entityInfo['entity keys']['id']; + $this->remoteKey = $entityInfo['entity keys']['remote']; + $this->moduleKey = reset(explode('/', $entityInfo['remote path'])); + $this->storage = $storage; } /** - * Fetch remote data resources from the service. + * Fetches remote data resources from the service. + * + * * * @param string $region * The region to fetch the data from. * @param string $language - * The Drupal language to retrieve the data with. + * The language to retrieve the data with. + * @param bool $force + * Whether to force the refresh or not. * - * @return WoWResponse - * An response object in case of HTTP status 200. + * @return array + * An array of remote entities. * * @throws WoWException - * An HTTP Exception in case of HTTP status 500 or 404. + * An HTTP Exception in case of HTTP status 500. */ - public function fetchAll($region, $language, $force = FALSE) { + public function fetch($region, $language, $force = FALSE) { $service = wow_data_service($region); - $db_entities = $entities = $this->storage->load(FALSE); + // Loads all entities for create/save/delete operations. + $entities = $this->storage->load(FALSE); - // Do not call the service if the expiration date has not be reached or - // force is not TRUE. - if ($service->expires($language, 'wow_character', 'class') > REQUEST_TIME || $force) { - $response = $service->newRequest('data/character/classes') + // Do not call the service if the expiration date has not be reached. + if ($service->expires($language, $this->moduleKey, $this->remoteKey) > REQUEST_TIME || $force) { + // Performs a request to the service API by setting a language code. Each + // data resources are saved with the language key in mind. + return $service->newRequest($this->entityInfo['remote path']) ->setLocale($language) + ->onResponse(200, array($this, 'onResult'), array($entities)) + // @TODO: + ->onResponseDefault() ->execute(); - - // Builds the array of entities to return. - $entities = array(); - foreach ($response->getData('classes') as $values) { - $entities[$values[$this->idKey]] = $this->storage->create($values + array( - 'language' => $language, - // TODO: put this in the storage controller. - 'is_new' => empty($db_entities[$values[$this->idKey]]) - )); - } - - // Deletes entities that are not existing anymore. - foreach (array_diff_key($entities, $db_entities) as $entity) { - $entity->delete(); - } - - // Updates the service with the new cache expiration value. - $cache_control = explode('=', $response->getHeader('cache-control')); - $expires = $response->getDate()->format('U') + $cache_control[1]; - $service->setCacheControl($language, 'wow_character', 'class', $expires); } return $entities; } + + /** + * When the service response is 200 OK, compares the response to the local + * database, performs updates, creates, and deletes operations on entities. + * + * @param WoWDataService $service + * @param WoWResponse $response + * @param array $entities + */ + protected function onResult(WoWDataService $service, WoWResponse $response, array $entities) { + $new_entities = array(); + // Builds the array of entities to return. + foreach ($response->getData($this->remoteKey) as $values) { + $id = $values[$this->idKey]; + // Creates entities from their remote equivalent. The idKey respects a + // 1-1 relationship with the database. + $new_entities[$id] = $this->storage->create($values + array( + 'language' => $language, + 'is_new' => empty($entities[$id]) + )); + + // Permanently saves the entity. + $new_entities[$id]->save(); + } + + // Deletes entities that are not existing anymore in the service. + foreach (array_diff_key($entities, $new_entities) as $entity) { + $entity->delete(); + } + + // Updates the service with the new cache expiration value. + $cache_control = explode('=', $response->getHeader('cache-control')); + $expires = $response->getDate()->format('U') + $cache_control[1]; + $service->setCacheControl($language, $this->moduleKey, $this->remoteKey, $expires); + + return $new_entities; + } + } diff --git a/lib/WoW/Core/Entity/Entity.php b/lib/WoW/Core/Entity/Entity.php index e7164af3a8f61dcce08403be4b3e262e5a646702..6a6c30a7ed85301faa13050b2b453c3481e8bbc9 100755 --- a/lib/WoW/Core/Entity/Entity.php +++ b/lib/WoW/Core/Entity/Entity.php @@ -39,22 +39,7 @@ abstract class WoWEntity extends Entity { public $language = LANGUAGE_NONE; /** - * Merges an entity with a response object. - * - * @param array $data - * The entity array as returned by the service. - */ - public function merge(array $values) { - foreach ($values as $key => $value) { - $this->{$key} = $value; - } - } - - /** - * Fetch a remote entity from the service. - * - * @param array $fields - * (Optional) An array of fields to request. + * Refresh this remote entity from the service. * * @return WoWResponse * An response object in case of HTTP status 200 or 304. @@ -62,7 +47,7 @@ abstract class WoWEntity extends Entity { * @throws WoWException * An HTTP Exception in case of HTTP status 500 or 404. */ - public function fetch(array $fields = array()) { - return wow_service_controller($this->entityType)->fetch($this, $fields); + public function refresh(array $fields = array()) { + return wow_service_controller($this->entityType)->refresh($this); } } diff --git a/lib/WoW/Core/Entity/EntityServiceController.php b/lib/WoW/Core/Entity/EntityServiceController.php index 55f71a3adf5b59de90a3859ce899ac160b2b9feb..72e8c31723e2b01db21139f346dfa7d1c555b850 100755 --- a/lib/WoW/Core/Entity/EntityServiceController.php +++ b/lib/WoW/Core/Entity/EntityServiceController.php @@ -46,16 +46,18 @@ abstract class WoWEntityServiceController { /** * Constructs an EntityServiceController abstract class. * - * @param string $entity_type + * @param string $entityType * The entity type. - * @param array $entity_info + * @param array $entityInfo * The entity info array of an entity type. + * @param EntityAPIControllerInterface $storage + * The entity storage controller. */ - public function __construct($entity_type, $entity_info) { - $this->entityType = $entity_type; - $this->entityInfo = $entity_info; - $this->idKey = $entity_info['entity keys']['id']; - $this->storage = entity_get_controller($entity_type); + public function __construct($entityType, array $entityInfo, EntityAPIControllerInterface $storage) { + $this->entityType = $entityType; + $this->entityInfo = $entityInfo; + $this->idKey = $entityInfo['entity keys']['id']; + $this->storage = $storage; } /** @@ -69,6 +71,8 @@ abstract class WoWEntityServiceController { $entity->lastFetched = $response->getDate()->format('U'); if ($response->getCode() == 304 || $response->getCode() == 404) { + // @TODO: remove '404' from IF statement in Drupal 8. This wont be needed + // anymore. // Updates the lastFetched time stamp. This will avoid the trigger of a // refresh when deleting an entity for instance if the refresh method is // set at load time. @@ -95,8 +99,6 @@ abstract class WoWEntityServiceController { $this->merge($entity, $response); break; } - - return $response; } /** @@ -108,6 +110,8 @@ abstract class WoWEntityServiceController { * The response object as returned by the service. */ public function merge(WoWEntity $entity, WoWResponse $response) { - $entity->merge($response->getData()); + foreach ($response->getData() as $key => $value) { + $entity->{$key} = $value; + } } } diff --git a/lib/WoW/Core/Entity/EntityStorageController.php b/lib/WoW/Core/Entity/EntityStorageController.php index 310a5cc08dc47bc2fdd10e3ed44f47154da19ee6..35336562b17a82357b77ba1dfd1ae5c60c78d616 100755 --- a/lib/WoW/Core/Entity/EntityStorageController.php +++ b/lib/WoW/Core/Entity/EntityStorageController.php @@ -61,8 +61,11 @@ abstract class WoWEntityStorageController extends EntityAPIController { foreach ($queried_entities as $entity) { // Check the lastFetched value before proceeding to an update. if ($entity->lastFetched + $this->refreshThreshold < REQUEST_TIME) { - // Refresh the entity by calling the service. - module_invoke($this->entityInfo['load hook'], 'refresh', $entity); + // For efficiency manually save the original character before applying + // any changes. + $entity->original = clone $entity; + $entity->refresh(); + $entity->save(); } } } diff --git a/lib/WoW/Core/Handler.php b/lib/WoW/Core/Handler.php new file mode 100755 index 0000000000000000000000000000000000000000..42307cfebd8cf2ab2f2ca3d55bef779825ded5cb --- /dev/null +++ b/lib/WoW/Core/Handler.php @@ -0,0 +1,92 @@ +request = $request; + $this->service = $service; + $this->handlers = array(); + } + + /** + * Executes the request. + * + * @return WoWResponse|mixed + * The response returned by the service or by the handler function if defined. + */ + public function execute() { + $response = $this->request->execute(); + + // Checks if a handler has been registered for this response code. + if (isset($this->handlers[$response->getCode()])) { + $handler = $this->handlers[$response->getCode()]; + + if (is_null($handler['parameters'])) { + return $handler['callback']; + } + else { + // Adds this response object as first parameter. + array_unshift($handler['parameters'], $this->service, $response); + return call_user_func_array($handler['callback'], $handler['parameters']); + } + } + else { + // No handlers has been defined for this response: throw an exception. + throw new WoWException($response); + } + } + + /** + * Adds a response handler to the request. + * + * @param int $code + * The response code. + * @param callback $callback + * The callback to be called. + * @param array $parameters + * The parameters to be passed to the callback, as an indexed array. + */ + public function onResponse($code, $callback, array $parameters) { + $this->handlers[$code] = array( + 'callback' => $callback, + 'parameters' => $parameters, + ); + + return $this; + } +} diff --git a/lib/WoW/Core/Request.php b/lib/WoW/Core/Request.php index 563b8dbce94702ac77d16d4ced66028e12da1810..f8c2b4f3084548bafa5650085d0016874d405066 100755 --- a/lib/WoW/Core/Request.php +++ b/lib/WoW/Core/Request.php @@ -1,5 +1,4 @@ path = $path; $this->query = array(); $this->headers = array(); + $this->handlers = array(); } /** @@ -80,7 +80,7 @@ class WoWRequest { * The Request. */ public function setLocale($language) { - // Prepares the query by adding a locale string if supported. + // Prepares the query by adding a locale parameter if supported. if ($locale = $this->service->getLocale($language)) { $this->query['locale'] = $locale; } @@ -88,7 +88,7 @@ class WoWRequest { } /** - * Adds a query parameter to the request. + * Adds a non-empty query parameter to the request. * * @param string $key * The query key. @@ -99,15 +99,32 @@ class WoWRequest { * The Request. */ public function setQuery($key, $value) { - $this->query[$key] = is_array($value) ? implode(",", $value) : $value; + if (!empty($value)) { + $this->query[$key] = is_array($value) ? implode(",", $value) : $value; + } return $this; } + /** + * Adds a response handler to the request. + * + * @param int $code + * The response code. + * @param callback $callback + * The callback to be called. + * @param array $parameters + * The parameters to be passed to the callback, as an indexed array. + */ + public function onResponse($code, $callback, array $parameters) { + $handler = new WoWHandler($this, $this->service); + return $handler->onResponse($code, $callback, $parameters); + } + /** * Executes the request. * * @return WoWResponse - * The response returned by the service. + * The response returned by the service */ public function execute() { // The date is used to sign the request, in the following format: diff --git a/modules/wow_character/lib/WoW/Character/CharacterClassServiceController.php b/modules/wow_character/lib/WoW/Character/CharacterClassServiceController.php index 28f9aa7ef4f03ddf655de7a43b11b5d9d9b01d2b..a8a2ff9636cc452014c90100283cc1fdcb3931af 100755 --- a/modules/wow_character/lib/WoW/Character/CharacterClassServiceController.php +++ b/modules/wow_character/lib/WoW/Character/CharacterClassServiceController.php @@ -70,9 +70,11 @@ class WoWCharacterClassServiceController implements WoWDataServiceControllerInte $response = wow_service($region) ->newRequest('data/character/classes') ->setLocale($language) - ->execute(); + ->onResponse(200, array($this, 'onResult'), $parameters) + ->execute(); - $entities = $this->storage->load(FALSE); + $entities = array(); + $db_entities = $this->storage->load(FALSE); // if ($response->getCode() == 200) { @@ -82,17 +84,16 @@ class WoWCharacterClassServiceController implements WoWDataServiceControllerInte foreach ($response->getData('classes') as $values) { $id = $values[$this->idKey]; - // Saves the entity. - $this->storage->create($values + array( + // Creates the entity from service values. + $entities[$id] = $this->storage->create($values + array( 'language' => $language, - 'is_new' => empty($entities[$id]), - ))->save(); - - // Flag as not delete. - unset($entities[$id]); + 'is_new' => empty($db_entities[$id]), + )); + // Permanently saves the entity. + $entities[$id]->save(); } - foreach ($entities as $entity) { + foreach (array_diff_key($db_entities, $entities) as $entity) { // Deletes extra entities that are not in the service anymore. $entity->delete(); } diff --git a/modules/wow_character/lib/WoW/Character/CharacterServiceController.php b/modules/wow_character/lib/WoW/Character/CharacterServiceController.php index 5b52ffd5c4d778a575476b8c8edd19b434c59d76..18f47ca6f80478ade4caef0f15f045f0b440fabd 100755 --- a/modules/wow_character/lib/WoW/Character/CharacterServiceController.php +++ b/modules/wow_character/lib/WoW/Character/CharacterServiceController.php @@ -13,6 +13,66 @@ */ class WoWCharacterServiceController extends WoWEntityServiceController { + /** + * The list of fields supported by the character resource end point. + * + * @var array + */ + public static $fields = array( + 'achievements', + 'appearance', + 'feed', + 'guild', + 'hunterPets', + 'items', + 'mounts', + 'pets', + 'petSlots', + 'professions', + 'progression', + 'pvp', + 'quest', + 'reputation', + 'stats', + 'talents', + 'titles' + ); + + /** + * Refreshes a Character entity. + * + * @param WoWCharacter $character + * The wow_character entity to refresh. + * + * @return WoWResponse + * The Response object returned by the service. + * + * @throws WoWException + * An exception in case of HTTP status 404, 500 or 503. + */ + public function refresh(WoWCharacter $character) { + $fields = array(); + // Fields are user defined: builds the 'fields' parameter with only what is + // needed by the entity. + foreach (self::$fields as $key) { + if (isset($character->{$key})) { + $fields[] = $key; + } + } + + $response = wow_service($character->region) + ->newRequest("character/$character->realm/$character->name") + ->setQuery('fields', $fields) + ->setLocale($character->language) + ->setIfModifiedSince($character->lastModified) + ->execute(); + + // Handles the response from the service. + $this->handleResponse($character, $response); + + return $response; + } + /** * The Character API is the primary way to access character information. * @@ -24,8 +84,12 @@ class WoWCharacterServiceController extends WoWEntityServiceController { * or more additional fields can be retrieved. To access this API, craft a * resource URL pointing to the character whos information is to be retrieved. * - * @param WoWCharacter $character - * The wow_character entity. + * @param string $region + * The guild region. + * @param string $realm + * The character realm. + * @param string $name + * The character name. * @param array $fields * An array of fields to fetch: * - achievements: A map of achievement data including completion timestamps @@ -59,32 +123,29 @@ class WoWCharacterServiceController extends WoWEntityServiceController { * - titles: A list of the titles obtained by the character including the * currently selected title. * - * @return WoWResponse - * The Response object returned by the service. + * @return WoWCharacter + * The Character object returned by the service. * * @throws WoWException * An exception in case of HTTP status 404, 500 or 503. */ - public function refresh(WoWCharacter $character) { + public function fetch($region, $realm, $name, array $fields = array()) { + $values = array('region' => $region, 'realm' => $realm, 'name' => $name); + $entities = $this->storage->load(FALSE, $values); + // Creates the character entity if not found from the storage. + $character = empty($entities) ? reset($entities) : $this->storage->create($values); + $response = wow_service($character->region) ->newRequest("character/$character->realm/$character->name") -// ->setQuery('fields', $fields) + ->setQuery('fields', $fields) ->setLocale($character->language) ->setIfModifiedSince($character->lastModified) ->execute(); - return $this->handleResponse($character, $response); - } + // Handles the response from the service. + $this->handleResponse($character, $response); - public function fetch($realm, $name, array $fields = array()) { - $character = db_select('wow_characters', 'c') - ->fields('c', array('cid')) - ->condition('region', $region) - ->condition('realm', $realm) - ->condition('name', $name) - ->execute() - ->fetch(); - wow_character_load_by_name($region, $realm, $name); + return $character; } /** @@ -92,7 +153,7 @@ class WoWCharacterServiceController extends WoWEntityServiceController { * @see WoWEntityServiceController::merge() */ public function merge($entity, WoWResponse $response) { - $entity->merge($response->getData()); + parent::merge($entity, $response); $entity->lastModified = $response->getData('lastModified') / 1000; } } diff --git a/modules/wow_character/wow_character.character.inc b/modules/wow_character/wow_character.character.inc index 84addecc0bf096c4a36183ac79353775353cfb52..9ee50f66812716ffe864dbc027f4a5c9720a3990 100755 --- a/modules/wow_character/wow_character.character.inc +++ b/modules/wow_character/wow_character.character.inc @@ -2,7 +2,7 @@ /** * @file - * Contains funtions to interact with character module hooks. + * Contains functions to interact with character module hooks. */ /** diff --git a/modules/wow_character/wow_character.module b/modules/wow_character/wow_character.module index dbe9af1241468f69f6748943ca44095f87c7c0cc..5722f7c20b3f40d7325266473f42f4b3e4c11ed7 100644 --- a/modules/wow_character/wow_character.module +++ b/modules/wow_character/wow_character.module @@ -234,31 +234,6 @@ function wow_character_uri_load($region, $realm, $name) { return $character ? wow_character_load($character->cid) : FALSE; } -/** - * Load a character object from the database. - * - * @param string $region - * The character region. - * @param string $realm - * The character realm. - * @param string $name - * The character name. - * - * @return WoWCharacter - * A fully-populated character object. - */ -function wow_character_load_by_name($region, $realm, $name) { - $character = db_select('wow_characters', 'c') - ->fields('c', array('cid')) - ->condition('region', $region) - ->condition('realm', wow_realm_to_slug($realm)) - ->condition('name', drupal_ucfirst($name)) - ->execute() - ->fetch(); - - return $character ? wow_character_load($character->cid) : FALSE; -} - /** * Implements hook_user_delete(). */ @@ -339,6 +314,7 @@ function wow_character_entity_info() { 'entity class' => 'WoWCharacterRace', 'controller class' => 'WoWCharacterRaceController', 'service controller class' => 'WoWDataServiceController', + 'remote path' => 'character/classes', 'base table' => 'wow_character_races', 'load hook' => 'wow_character_race', 'fieldable' => TRUE, @@ -347,6 +323,7 @@ function wow_character_entity_info() { ), 'entity keys' => array( 'id' => 'id', + 'remote' => 'classes', ), ), 'wow_character_class' => array( @@ -504,48 +481,17 @@ function _wow_character_class_callback(&$values, $class) { * - titles: A list of the titles obtained by the character including the * currently selected title. * - * @param array $options - * (Optional) For a complete list of options, see wow_http_request method. - * @param Boolean $catch - * Whether to catch exceptions or not. - * * @return WoWCharacter * The character object returned by the API. The core dataset returned includes * the character's name, level, class, gender and achievement points. * - * @see wow_http_request() - */ -function wow_character_fetch($region, $realm, $name, array $fields = array(), array $options = array(), $catch = TRUE) { - $character = wow_character_load_by_name($region, $realm, $name); - - if (!$character) { - $character = entity_create('wow_character', array( - 'region' => $region, - 'realm' => $realm, - 'name' => $name, - )); - } - - $character->fetch($fields, $options, $catch); - return $character; -} - -/** - * Fetch an already know character. - * - * @param WoWCharacter $character - * A character entity. - * @param array $fields - * (Optional) For the complete list of fields supported, see wow_character_fetch method. + * @throws WoWException + * An exception in case of HTTP status 404, 500 or 503. * - * @see wow_character_fetch() - * @see wow_http_request() + * @see WoWCharacterServiceController::fetch() */ -function wow_character_refresh(WoWCharacter $character, array $fields = array()) { - // For efficiency manually save the original character before applying any changes. - $character->original = clone $character; - $character->fetch($fields); - $character->save(); +function wow_character_fetch($region, $realm, $name, array $fields = array()) { + return wow_service_controller('wow_character')->fetch($region, $realm, $name, $fields); } /** @@ -598,7 +544,8 @@ function wow_character_cron() { ->fetchCol(); // Long-running tasks and tasks that could time out, such as retrieving - // remote data makes use of the queue API instead of executing tak directly. + // remote data makes use of the queue API instead of executing task + // directly. $queue = DrupalQueue::get('wow_character'); foreach (wow_character_load_multiple($cids) as $character) { // Add characters that need to be refreshed to the wow_character queue. diff --git a/modules/wow_character/wow_character.pages.inc b/modules/wow_character/wow_character.pages.inc index 4d9b11314cd36b222491f0cc2348624f3e482357..0300ba366e7d93345b866380004bab2b6a2ebc58 100644 --- a/modules/wow_character/wow_character.pages.inc +++ b/modules/wow_character/wow_character.pages.inc @@ -139,20 +139,15 @@ function wow_character_user_add_validate($form, &$form_state) { $realm = $form_state['values']['realm']; $character_name = $form_state['values']['name']; $fields = module_exists('wow_guild') ? array('guild') : array(); - $character = wow_character_load_by_name($realm->region, $realm->slug, $character_name); - $character = wow_character_fetch($realm->region, $realm->slug, $character_name, $fields, array(), FALSE); - $debug = true; + $character = wow_character_fetch($realm->region, $realm->slug, $character_name, $fields); } catch (WoWException $exception) { switch ($exception->getCode()) { - case 304: - // Already loaded previously. - $character = wow_character_load_by_name($realm->region, $realm->slug, $character_name); - break; case 404: $args = array('%name' => $character_name, '!realm' => drupal_strtoupper($realm->region) . '-' . $realm->name); form_set_error('name', t('%name not found on !realm. Please try again with a correct name.', $args)); return; + // @TODO: 503 and a default exception handler. default: watchdog_exception('wow_character', $exception, t('An exception has been thrown during the %name character validation.', array('%name' => $character_name))); form_set_error('name', t('The website encountered an unexpected error. Please try again later.')); @@ -194,6 +189,7 @@ function wow_character_user_add_validate($form, &$form_state) { */ function wow_character_user_add_submit($form, &$form_state) { $character = $form_state['character']; + // @TODO: pass only the uid to saves bandwidth. Or reuse arg1 ? $account. $character->uid = $form_state['user']->uid; $character->save(); diff --git a/modules/wow_realm/wow_realm.info b/modules/wow_realm/wow_realm.info index 78666d09884cc36ad1e636c0429ba627f023c605..9b05249603f91903bad27d3e4e648d21a6659c7e 100644 --- a/modules/wow_realm/wow_realm.info +++ b/modules/wow_realm/wow_realm.info @@ -1,10 +1,7 @@ name = "Realm" -description = "Realm APIs currently provide realm status information." +description = "Logs API request statistics." package = World of Warcraft dependencies[] = wow core = 7.x -configure = admin/config/wow/realm - -files[] = lib/WoW/Realm/Realm.php -files[] = lib/WoW/Realm/RealmServiceController.php +files[] = lib/WoW/Statistics/Realm.php diff --git a/modules/wow_statistics/wow_statistics.info b/modules/wow_statistics/wow_statistics.info new file mode 100755 index 0000000000000000000000000000000000000000..51c122970bc714e033750a987a55b6bf8f0fa369 --- /dev/null +++ b/modules/wow_statistics/wow_statistics.info @@ -0,0 +1,10 @@ +name = "Statistics" +description = "Realm APIs currently provide realm status information." +package = World of Warcraft +dependencies[] = wow +core = 7.x + +configure = admin/config/wow/realm + +files[] = lib/WoW/Realm/Realm.php +files[] = lib/WoW/Realm/RealmServiceController.php diff --git a/wow.module b/wow.module index 5a32723e4c5e1dbad4edc49fa145a419ab249c74..13b6fb0bda31aa911ed409ac46fb783583d839bd 100644 --- a/wow.module +++ b/wow.module @@ -612,11 +612,14 @@ function wow_service_list($field = 'language') { */ function wow_service_controller($entity_type) { $controllers = &drupal_static(__FUNCTION__, array()); + if (!isset($controllers[$entity_type])) { $entity_info = entity_get_info($entity_type); $class = $entity_info['service controller class']; - $controllers[$entity_type] = new $class($entity_type, $entity_info); + $storage = entity_get_controller($entity_type); + $controllers[$entity_type] = new $class($entity_type, $entity_info, $storage); } + return $controllers[$entity_type]; } @@ -641,18 +644,12 @@ function wow_service($region) { // creation. For now the database is called each time a new region is created. if (!isset($services[$region])) { // Returns the enabled services and their configuration. - $result = db_select('wow_services', 's') + $locales = db_select('wow_services', 's') ->fields('s', array('language', 'locale')) ->condition('enabled', TRUE) ->condition('region', $region) - ->execute(); - - // Creates the array of supported locales for this service, keyed by - // language. - $locales = array(); - foreach($result->fetchAllAssoc('language') as $language => $row) { - $locales[$language] = $row->locale; - } + ->execute() + ->fetchAllKeyed(); // Configures the service to return. We use here static IoC based on the // public and private key configuration. @@ -660,12 +657,16 @@ function wow_service($region) { $public_key = variable_get('wow_public_key'); if (empty($key) || empty($public_key) || !extension_loaded('openssl')) { // Sets an HTTP implementation of the service. - $services[$region] = new WoWServiceHttp(wow_api_info($region)->host, $locales); + $service = new WoWServiceHttp(wow_api_info($region)->host, $locales); } else { // Sets an HTTPS implementation of the service. - $services[$region] = new WoWServiceHttps(wow_api_info($region)->host, $locales); + $service = new WoWServiceHttps(wow_api_info($region)->host, $locales); } + + module_invoke_all('service', $service); + + $services[$region] = $service; } return $services[$region];