diff --git a/queue_example/lib/Drupal/queue_example/Forms/QueueExampleForm.php b/queue_example/lib/Drupal/queue_example/Forms/QueueExampleForm.php new file mode 100644 index 0000000..114f2dd --- /dev/null +++ b/queue_example/lib/Drupal/queue_example/Forms/QueueExampleForm.php @@ -0,0 +1,270 @@ +retrieveQueue($queue_name); + + // Add CSS to make the form a bit denser. + // $form['#attached']['css'] = array(drupal_get_path('module', 'queue_example') . '/queue_example.css'); + + $form['help'] = array( + '#type' => 'markup', + '#markup' => '
' . t('This page is an interface on the Drupal queue API. You can add new items to the queue, "claim" one (retrieve the next item and keep a lock on it), and delete one (remove it from the queue). Note that claims are not expired until cron runs, so there is a special button to run cron to perform any necessary expirations.') . '
', + ); + + $form['queue_name'] = array( + '#type' => 'select', + '#title' => t('Choose queue to examine'), + '#options' => MapArray::copyValuesToKeys(array('queue_example_first_queue', 'queue_example_second_queue')), + '#default_value' => $queue_name, + ); + $form['queue_show'] = array( + '#type' => 'submit', + '#value' => t('Show queue'), + '#submit' => array(array($this, 'submitShowQueue')), + ); + $form['status_fieldset'] = array( + '#type' => 'fieldset', + '#title' => t('Queue status for @name', array('@name' => $queue_name)), + '#collapsible' => TRUE, + ); + $form['status_fieldset']['status'] = array( + '#type' => 'markup', + '#markup' => theme('queue_items', array('items' => $items)), + ); + $form['insert_fieldset'] = array( + '#type' => 'fieldset', + '#title' => t('Insert into @name', array('@name' => $queue_name)), + ); + $form['insert_fieldset']['string_to_add'] = array( + '#type' => 'textfield', + '#size' => 10, + '#default_value' => t('item @counter', array('@counter' => $form_state['storage']['insert_counter'])), + ); + $form['insert_fieldset']['add_item'] = array( + '#type' => 'submit', + '#value' => t('Insert into queue'), + '#submit' => array(array($this, 'submitAddQueueItem')), + ); + $form['claim_fieldset'] = array( + '#type' => 'fieldset', + '#title' => t('Claim from queue'), + '#collapsible' => TRUE, + ); + + $form['claim_fieldset']['claim_time'] = array( + '#type' => 'radios', + '#title' => t('Claim time, in seconds'), + '#options' => array(0 => t('none'), 5 => t('5 seconds'), 60 => t('60 seconds')), + '#description' => t('This time is only valid if cron runs during this time period. You can run cron manually below.'), + '#default_value' => !empty($form_state['values']['claim_time']) ? $form_state['values']['claim_time'] : 5, + ); + $form['claim_fieldset']['claim_item'] = array( + '#type' => 'submit', + '#value' => t('Claim the next item from the queue'), + '#submit' => array(array($this, 'submitClaimItem')), + ); + $form['claim_fieldset']['claim_and_delete_item'] = array( + '#type' => 'submit', + '#value' => t('Claim the next item and delete it'), + '#submit' => array(array($this, 'submitClaimDeleteItem')), + ); + $form['claim_fieldset']['run_cron'] = array( + '#type' => 'submit', + '#value' => t('Run cron manually to expire claims'), + '#submit' => array(array($this, 'submitRunCron')), + ); + $form['delete_queue'] = array( + '#type' => 'submit', + '#value' => t('Delete the queue and items in it'), + '#submit' => array(array($this, 'submitDeleteQueue')), + ); + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, array &$form_state) { + + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, array &$form_state) { + + } + + /** + * Retrieves the queue from the database for display purposes only. + * + * It is not recommended to access the database directly, and this is only here + * so that the user interface can give a good idea of what's going on in the + * queue. + * + * @param $queue_name + * The name of the queue from which to fetch items. + * + * @return array + */ + public function retrieveQueue($queue_name) { + $items = array(); + $result = db_query("SELECT item_id, data, expire, created FROM {queue} WHERE name = :name ORDER BY item_id", + array(':name' => $queue_name), + array('fetch' => \PDO::FETCH_ASSOC)); + foreach ($result as $item) { + $items[] = $item; + } + return $items; + } + + /** + * Submit function for the show-queue button. + * + * @param array $form + * @param array $form_state + */ + public function submitShowQueue(array &$form, array &$form_state) { + $queue = \Drupal::queue($form_state['values']['queue_name']); + $queue->createQueue(); // There is no harm in trying to recreate existing. + + // Get the number of items. + $count = $queue->numberOfItems(); + + // Update the form item counter. + $form_state['storage']['insert_counter'] = $count +1; + + // Unset the string_to_add textbox. + unset($form_state['input']['string_to_add']); + + $form_state['rebuild'] = TRUE; + } + + /** + * Submit function for the insert-into-queue button. + * + * @param array $form + * @param array $form_state + */ + public function submitAddQueueItem(array &$form, array &$form_state) { + // Get a queue (of the default type) called 'queue_example_queue'. + // If the default queue class is SystemQueue this creates a queue that stores + // its items in the database. + $queue = \Drupal::queue($form_state['values']['queue_name']); + $queue->createQueue(); // There is no harm in trying to recreate existing. + + // Queue the string. + $queue->createItem($form_state['values']['string_to_add']); + $count = $queue->numberOfItems(); + drupal_set_message(t('Queued your string (@string_to_add). There are now @count items in the queue.', array('@count' => $count, '@string_to_add' => $form_state['values']['string_to_add']))); + $form_state['rebuild'] = TRUE; // Allows us to keep information in $form_state. + + // Unsetting the string_to_add allows us to set the incremented default value + // for the user so they don't have to type anything. + unset($form_state['input']['string_to_add']); + $form_state['storage']['insert_counter']++; + } + + /** + * Submit function for the "claim" button. Claims (retrieves) an item from + * the queue and reports the results. + * + * @param array $form + * @param array $form_state + */ + public function submitClaimItem(array &$form, array &$form_state) { + $queue = \Drupal::queue($form_state['values']['queue_name']); + $queue->createQueue(); // There is no harm in trying to recreate existing. + $item = $queue->claimItem($form_state['values']['claim_time']); + $count = $queue->numberOfItems(); + if (!empty($item)) { + drupal_set_message(t('Claimed item id=@item_id string=@string for @seconds seconds. There are @count items in the queue.', array('@count' => $count, '@item_id' => $item->item_id, '@string' => $item->data, '@seconds' => $form_state['values']['claim_time']))); + } + else { + drupal_set_message(t('There were no items in the queue available to claim. There are @count items in the queue.', array('@count' => $count))); + } + $form_state['rebuild'] = TRUE; + } + + /** + * Submit function for "Claim and delete" button. + * + * @param array $form + * @param array $form_state + */ + public function submitClaimDeleteItem(array &$form, array &$form_state) { + $queue = \Drupal::queue($form_state['values']['queue_name']); + $queue->createQueue(); // There is no harm in trying to recreate existing. + $count = $queue->numberOfItems(); + $item = $queue->claimItem(60); + if (!empty($item)) { + drupal_set_message(t('Claimed and deleted item id=@item_id string=@string for @seconds seconds. There are @count items in the queue.', array('@count' => $count, '@item_id' => $item->item_id, '@string' => $item->data, '@seconds' => $form_state['values']['claim_time']))); + $queue->deleteItem($item); + $count = $queue->numberOfItems(); + drupal_set_message(t('There are now @count items in the queue.', array('@count' => $count))); + } + else { + $count = $queue->numberOfItems(); + drupal_set_message(t('There were no items in the queue available to claim/delete. There are currently @count items in the queue.', array('@count' => $count))); + } + $form_state['rebuild'] = TRUE; + } + + /** + * Submit function for "run cron" button. + * + * Runs cron (to release expired claims) and reports the results. + * + * @param array $form + * @param array $form_state + */ + public function submitRunCron(array &$form, array &$form_state) { + drupal_cron_run(); + $queue = \Drupal::queue($form_state['values']['queue_name']); + $queue->createQueue(); // There is no harm in trying to recreate existing. + $count = $queue->numberOfItems(); + drupal_set_message(t('Ran cron. If claimed items expired, they should be expired now. There are now @count items in the queue', array('@count' => $count))); + $form_state['rebuild'] = TRUE; + } + + /** + * Submit handler for clearing/deleting the queue. + * + * @param array $form + * @param array $form_state + */ + public function submitDeleteQueue(array &$form, array &$form_state) { + $queue = \Drupal::queue($form_state['values']['queue_name']); + $queue->deleteQueue(); + drupal_set_message(t('Deleted the @queue_name queue and all items in it', array('@queue_name' => $form_state['values']['queue_name']))); + } + +} diff --git a/queue_example/lib/Drupal/queue_example/Tests/QueueExampleTestCase.php b/queue_example/lib/Drupal/queue_example/Tests/QueueExampleTestCase.php new file mode 100644 index 0000000..801b12c --- /dev/null +++ b/queue_example/lib/Drupal/queue_example/Tests/QueueExampleTestCase.php @@ -0,0 +1,82 @@ + 'Queue Example functionality', + 'description' => 'Test Queue Example functionality', + 'group' => 'Examples', + ); + } + + /** + * Enable modules and create user with specific permissions. + */ + function setUp() { + parent::setUp(); + } + + /** + * Login user, create an example node, and test blog functionality through the admin and user interfaces. + */ + function testQueueExampleBasic() { + + // Load the queue with 5 items. + for ($i = 1; $i <= 5; $i++) { + $edit = array('queue_name' => 'queue_example_first_queue', 'string_to_add' => "boogie$i"); + $this->drupalPostForm('queue_example/insert_remove', $edit, t('Insert into queue')); + $this->assertText(t('There are now @number items in the queue', array('@number' => $i))); + } + // Claim each of the 5 items with a claim time of 0 seconds. + for ($i = 1; $i <= 5; $i++) { + $edit = array('queue_name' => 'queue_example_first_queue', 'claim_time' => 0); + $this->drupalPostForm(NULL, $edit, t('Claim the next item from the queue')); + $this->assertPattern(t('%Claimed item id=.*string=@string for 0 seconds.%', array('@string' => "boogie$i"))); + } + $edit = array('queue_name' => 'queue_example_first_queue', 'claim_time' => 0); + $this->drupalPostForm(NULL, $edit, t('Claim the next item from the queue')); + $this->assertText(t('There were no items in the queue available to claim')); + + // Sleep a second so we can make sure that the timeouts actually time out. + // Local systems work fine with this but apparently the PIFR server is so + /// fast that it needs a sleep before the cron run. + sleep(1); + + // Run cron to release expired items. + $this->drupalPostForm(NULL, array(), t('Run cron manually to expire claims')); + + // Claim and delete each of the 5 items which should now be available. + for ($i = 1; $i <= 5; $i++) { + $edit = array('queue_name' => 'queue_example_first_queue', 'claim_time' => 0); + $this->drupalPostForm(NULL, $edit, t('Claim the next item and delete it')); + $this->assertPattern(t('%Claimed and deleted item id=.*string=@string for 0 seconds.%', array('@string' => "boogie$i"))); + } + // Verify that nothing is left to claim. + $edit = array('queue_name' => 'queue_example_first_queue', 'claim_time' => 0); + $this->drupalPostForm(NULL, $edit, t('Claim the next item from the queue')); + $this->assertText(t('There were no items in the queue available to claim')); + } + +} + +/** + * @} End of "addtogroup queue_example". + */ diff --git a/queue_example/queue_example.info.yml b/queue_example/queue_example.info.yml new file mode 100644 index 0000000..e7d94ff --- /dev/null +++ b/queue_example/queue_example.info.yml @@ -0,0 +1,5 @@ +name: Queue example +type: module +description: Examples of using the Drupal Queue API. +package: Example modules +core: 8.x diff --git a/queue_example/queue_example.module b/queue_example/queue_example.module new file mode 100644 index 0000000..7ba0ab4 --- /dev/null +++ b/queue_example/queue_example.module @@ -0,0 +1,68 @@ + array( + 'variables' => array('items' => NULL), + ), + ); +} + +/** + * Themes the queue display. + * + * Again, this is not part of the demonstration of the queue API, but is here + * just to make the user interface more understandable. + * + * @param $variables + * + * @return string + */ +function theme_queue_items($variables) { + $items = $variables['items']; + $rows = array(); + foreach ($items as &$item) { + if ($item['expire'] > 0) { + $item['expire'] = t("Claimed: expires %expire", array('%expire' => date('r', $item['expire']))); + } + else { + $item['expire'] = t('Unclaimed'); + } + $item['created'] = date('r', $item['created']); + $item['content'] = check_plain(unserialize($item['data'])); + unset($item['data']); + $rows[] = $item; + } + if (!empty($rows)) { + $header = array(t('Item ID'), t('Claimed/Expiration'), t('Created'), t('Content/Data')); + $output = theme('table', array('header' => $header, 'rows' => $rows)); + return $output; + } + else { + return t('There are no items in the queue.'); + } +} + + +/** + * @} End of "defgroup queue_example". + */ diff --git a/queue_example/queue_example.routing.yml b/queue_example/queue_example.routing.yml new file mode 100644 index 0000000..a07bacb --- /dev/null +++ b/queue_example/queue_example.routing.yml @@ -0,0 +1,6 @@ +queue_example: + path: 'queue_example/insert_remove' + defaults: + _form: '\Drupal\queue_example\Forms\QueueExampleForm' + requirements: + _access: 'TRUE'