/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $io = new DrupalStyle($input, $output); $modules = $input->getArgument('module'); if (!$this->lock->acquire('cron', 900.0)) { $io->warning($this->trans('commands.cron.execute.messages.lock')); return 1; } if (in_array('all', $modules)) { $modules = $this->moduleHandler->getImplementations('cron'); } foreach ($modules as $module) { if (!$this->moduleHandler->implementsHook($module, 'cron')) { $io->warning(sprintf($this->trans('commands.cron.execute.messages.module-invalid'), $module)); continue; } try { $io->info(sprintf($this->trans('commands.cron.execute.messages.executing-cron'), $module)); $this->moduleHandler->invoke($module, 'cron'); } catch (\Exception $e) { watchdog_exception('cron', $e); $io->error($e->getMessage()); } } $this->state->set('system.cron_last', REQUEST_TIME); $this->lock->release('cron'); $this->chainQueue->addCommand('cache:rebuild', ['cache' => 'all']); $io->success($this->trans('commands.cron.execute.messages.success')); return 0; }
/** * Tests the destruct method. * * @covers ::destruct */ public function testDestruct() { $this->libraryDiscoveryParser->expects($this->once())->method('buildByExtension')->with('test')->will($this->returnValue($this->libraryData)); $lock_key = 'library_info:Drupal\\Core\\Cache\\CacheCollector'; $this->lock->expects($this->once())->method('acquire')->with($lock_key)->will($this->returnValue(TRUE)); $this->cache->expects($this->exactly(2))->method('get')->with('library_info')->will($this->returnValue(FALSE)); $this->cache->expects($this->once())->method('set')->with('library_info', array('test' => $this->libraryData), Cache::PERMANENT, array('library_info')); $this->lock->expects($this->once())->method('release')->with($lock_key); // This should get data and persist the key. $this->libraryDiscoveryCollector->get('test'); $this->libraryDiscoveryCollector->destruct(); }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $io = new DrupalStyle($input, $output); try { $this->lock->release('cron'); $io->info($this->trans('commands.cron.release.messages.released')); } catch (Exception $e) { $io->error($e->getMessage()); return 1; } $this->chainQueue->addCommand('cache:rebuild', ['cache' => 'all']); return 0; }
/** * {@inheritdoc} */ protected function setUp() { $this->storage = $this->getMock('Drupal\\locale\\StringStorageInterface'); $this->cache = $this->getMock('Drupal\\Core\\Cache\\CacheBackendInterface'); $this->lock = $this->getMock('Drupal\\Core\\Lock\\LockBackendInterface'); $this->lock->expects($this->never())->method($this->anything()); $this->user = $this->getMock('Drupal\\Core\\Session\\AccountInterface'); $this->user->expects($this->any())->method('getRoles')->will($this->returnValue(array('anonymous'))); $this->configFactory = $this->getConfigFactoryStub(array('locale.settings' => array('cache_strings' => FALSE))); $this->languageManager = $this->getMock('Drupal\\Core\\Language\\LanguageManagerInterface'); $container = new ContainerBuilder(); $container->set('current_user', $this->user); \Drupal::setContainer($container); }
/** * {@inheritdoc} */ public function run() { // Allow execution to continue even if the request gets cancelled. @ignore_user_abort(TRUE); // Prevent session information from being saved while cron is running. $original_session_saving = $this->sessionManager->isEnabled(); $this->sessionManager->disable(); // Force the current user to anonymous to ensure consistent permissions on // cron runs. $original_user = $this->currentUser->getAccount(); $this->currentUser->setAccount(new AnonymousUserSession()); // Try to allocate enough time to run all the hook_cron implementations. drupal_set_time_limit(240); $return = FALSE; // Try to acquire cron lock. if (!$this->lock->acquire('cron', 240.0)) { // Cron is still running normally. watchdog('cron', 'Attempting to re-run cron while it is already running.', array(), WATCHDOG_WARNING); } else { $this->invokeCronHandlers(); $this->setCronLastTime(); // Release cron lock. $this->lock->release('cron'); // Return TRUE so other functions can check if it did run successfully $return = TRUE; } // Process cron queues. $this->processQueues(); // Restore the user. $this->currentUser->setAccount($original_user); if ($original_session_saving) { $this->sessionManager->enable(); } return $return; }
/** * {@inheritdoc} */ public function rebuild() { if ($this->building) { throw new \RuntimeException('Recursive router rebuild detected.'); } if (!$this->lock->acquire('router_rebuild')) { // Wait for another request that is already doing this work. // We choose to block here since otherwise the routes might not be // available, resulting in a 404. $this->lock->wait('router_rebuild'); return FALSE; } $this->building = TRUE; $collection = new RouteCollection(); foreach ($this->getRouteDefinitions() as $routes) { // The top-level 'routes_callback' is a list of methods in controller // syntax, see \Drupal\Core\Controller\ControllerResolver. These methods // should return a set of \Symfony\Component\Routing\Route objects, either // in an associative array keyed by the route name, which will be iterated // over and added to the collection for this provider, or as a new // \Symfony\Component\Routing\RouteCollection object, which will be added // to the collection. if (isset($routes['route_callbacks'])) { foreach ($routes['route_callbacks'] as $route_callback) { $callback = $this->controllerResolver->getControllerFromDefinition($route_callback); if ($callback_routes = call_user_func($callback)) { // If a RouteCollection is returned, add the whole collection. if ($callback_routes instanceof RouteCollection) { $collection->addCollection($callback_routes); } else { foreach ($callback_routes as $name => $callback_route) { $collection->add($name, $callback_route); } } } } unset($routes['route_callbacks']); } foreach ($routes as $name => $route_info) { $route_info += array('defaults' => array(), 'requirements' => array(), 'options' => array()); $route = new Route($route_info['path'], $route_info['defaults'], $route_info['requirements'], $route_info['options']); $collection->add($name, $route); } } // DYNAMIC is supposed to be used to add new routes based upon all the // static defined ones. $this->dispatcher->dispatch(RoutingEvents::DYNAMIC, new RouteBuildEvent($collection)); // ALTER is the final step to alter all the existing routes. We cannot stop // people from adding new routes here, but we define two separate steps to // make it clear. $this->dispatcher->dispatch(RoutingEvents::ALTER, new RouteBuildEvent($collection)); $this->checkProvider->setChecks($collection); $this->dumper->addRoutes($collection); $this->dumper->dump(); $this->lock->release('router_rebuild'); $this->dispatcher->dispatch(RoutingEvents::FINISHED, new Event()); $this->building = FALSE; $this->rebuildNeeded = FALSE; return TRUE; }
/** * {@inheritdoc} */ public function run() { // Allow execution to continue even if the request gets cancelled. @ignore_user_abort(TRUE); // Force the current user to anonymous to ensure consistent permissions on // cron runs. $this->accountSwitcher->switchTo(new AnonymousUserSession()); // Try to allocate enough time to run all the hook_cron implementations. drupal_set_time_limit(240); $return = FALSE; // Try to acquire cron lock. if (!$this->lock->acquire('cron', 900.0)) { // Cron is still running normally. $this->logger->warning('Attempting to re-run cron while it is already running.'); } else { $this->invokeCronHandlers(); $this->setCronLastTime(); // Release cron lock. $this->lock->release('cron'); // Return TRUE so other functions can check if it did run successfully $return = TRUE; } // Process cron queues. $this->processQueues(); // Restore the user. $this->accountSwitcher->switchBack(); return $return; }
/** * Creates a lock that will persist across requests. * * @param string $lock_name * The name of the persistent lock to acquire. * * @return string * The text to display. */ public function lockPersist($lock_name) { if ($this->persistentLock->acquire($lock_name)) { return ['#markup' => 'TRUE: Lock successfully acquired in SystemTestController::lockPersist()']; } else { return ['#markup' => 'FALSE: Lock not acquired in SystemTestController::lockPersist()']; } }
/** * Executes a callback. * * @param \Drupal\feeds\FeedInterface $feeds_feed * The Feed we are executing a job for. * @param \Symfony\Component\HttpFoundation\Request $request * The request object to grab POST params from. * * @todo Configure a time limit. * @todo Really awesome error handling. */ public function execute(FeedInterface $feeds_feed, Request $request) { $cid = 'feeds_feed:' . $feeds_feed->id(); if ($token = $request->request->get('token') && ($job = $this->state->get($cid))) { if ($job['token'] == $token && ($lock = $this->lockBackend->acquire($cid))) { $method = $job['method']; $this->state->delete($cid); ignore_user_abort(TRUE); set_time_limit(0); while ($feeds_feed->{$method}() != StateInterface::BATCH_COMPLETE) { // Reset static caches in between runs to avoid memory leaks. drupal_reset_static(); } $this->lockBackend->release($cid); } } }
/** * {@inheritdoc} */ public function getMails($limit = self::UNLIMITED, $conditions = array()) { $messages = array(); // Continue to support 'nid' as a condition. if (!empty($conditions['nid'])) { $conditions['entity_type'] = 'node'; $conditions['entity_id'] = $conditions['nid']; unset($conditions['nid']); } // Add default status condition if not set. if (!isset($conditions['status'])) { $conditions['status'] = array(SpoolStorageInterface::STATUS_PENDING, SpoolStorageInterface::STATUS_IN_PROGRESS); } // Special case for the status condition, the in progress actually only // includes spool items whose locking time has expired. So this need to build // an OR condition for them. $status_or = new Condition('OR'); $statuses = is_array($conditions['status']) ? $conditions['status'] : array($conditions['status']); foreach ($statuses as $status) { if ($status == SpoolStorageInterface::STATUS_IN_PROGRESS) { $status_or->condition((new Condition('AND'))->condition('status', $status)->condition('s.timestamp', $this->getExpirationTime(), '<')); } else { $status_or->condition('status', $status); } } unset($conditions['status']); $query = $this->connection->select('simplenews_mail_spool', 's')->fields('s')->condition($status_or)->orderBy('s.timestamp', 'ASC'); // Add conditions. foreach ($conditions as $field => $value) { $query->condition($field, $value); } /* BEGIN CRITICAL SECTION */ // The semaphore ensures that multiple processes get different message ID's, // so that duplicate messages are not sent. if ($this->lock->acquire('simplenews_acquire_mail')) { // Get message id's // Allocate messages if ($limit > 0) { $query->range(0, $limit); } foreach ($query->execute() as $message) { if (Unicode::strlen($message->data)) { $message->data = unserialize($message->data); } else { $message->data = simplenews_subscriber_load_by_mail($message->mail); } $messages[$message->msid] = $message; } if (count($messages) > 0) { // Set the state and the timestamp of the messages $this->updateMails(array_keys($messages), array('status' => SpoolStorageInterface::STATUS_IN_PROGRESS)); } $this->lock->release('simplenews_acquire_mail'); } /* END CRITICAL SECTION */ return new SpoolList($messages); }
/** * Deletes data from the store for a given key and releases the lock on it. * * @param string $key * The key of the data to delete. */ public function delete($key) { if (!$this->lockBackend->acquire($key)) { $this->lockBackend->wait($key); if (!$this->lockBackend->acquire($key)) { throw new TempStoreException(String::format("Couldn't acquire lock to delete item %key from %collection temporary storage.", array('%key' => $key, '%collection' => $this->storage->getCollectionName()))); } } $this->storage->delete($key); $this->lockBackend->release($key); }
/** * Tests the deleteIfOwner() method. * * @covers ::deleteIfOwner() */ public function testDeleteIfOwner() { $this->lock->expects($this->once())->method('acquire')->with('test_2')->will($this->returnValue(TRUE)); $this->keyValue->expects($this->at(0))->method('get')->with('test_1')->will($this->returnValue(FALSE)); $this->keyValue->expects($this->at(1))->method('get')->with('test_2')->will($this->returnValue($this->ownObject)); $this->keyValue->expects($this->at(2))->method('delete')->with('test_2'); $this->keyValue->expects($this->at(3))->method('get')->with('test_3')->will($this->returnValue($this->otherObject)); $this->assertTrue($this->tempStore->deleteIfOwner('test_1')); $this->assertTrue($this->tempStore->deleteIfOwner('test_2')); $this->assertFalse($this->tempStore->deleteIfOwner('test_3')); }
/** * Tests \Drupal\Core\Routing\RouteBuilder::rebuildIfNeeded() method. */ public function testRebuildIfNeeded() { $this->lock->expects($this->once())->method('acquire')->with('router_rebuild')->will($this->returnValue(TRUE)); $this->lock->expects($this->once())->method('release')->with('router_rebuild'); $this->yamlDiscovery->expects($this->any())->method('findAll')->will($this->returnValue(array())); $this->routeBuilder->setRebuildNeeded(); // This will trigger a successful rebuild. $this->assertTrue($this->routeBuilder->rebuildIfNeeded()); // This will not trigger a rebuild. $this->assertFalse($this->routeBuilder->rebuildIfNeeded()); }
/** * Deletes data from the store for a given key and releases the lock on it. * * @param string $key * The key of the data to delete. */ public function delete($key) { if (!$this->lockBackend->acquire($key)) { $this->lockBackend->wait($key); if (!$this->lockBackend->acquire($key)) { throw new TempStoreException("Couldn't acquire lock to delete item '{$key}' from {$this->storage->getCollectionName()} temporary storage."); } } $this->storage->delete($key); $this->lockBackend->release($key); }
/** * Perform menu-specific rebuilding. */ protected function menuLinksRebuild() { if ($this->lock->acquire(__FUNCTION__)) { $transaction = db_transaction(); try { // Ensure the menu links are up to date. $this->menuLinkManager->rebuild(); // Ignore any database replicas temporarily. db_ignore_replica(); } catch (\Exception $e) { $transaction->rollback(); watchdog_exception('menu', $e); } $this->lock->release(__FUNCTION__); } else { // Wait for another request that is already doing this work. // We choose to block here since otherwise the router item may not // be available during routing resulting in a 404. $this->lock->wait(__FUNCTION__); } }
/** * Perform menu-specific rebuilding. */ protected function menuLinksRebuild() { if ($this->lock->acquire(__FUNCTION__)) { $transaction = db_transaction(); try { // Ensure the menu links are up to date. menu_link_rebuild_defaults(); // Clear the menu cache. menu_cache_clear_all(); // Track which menu items are expanded. _menu_update_expanded_menus(); } catch (\Exception $e) { $transaction->rollback(); watchdog_exception('menu', $e); } $this->lock->release(__FUNCTION__); } else { // Wait for another request that is already doing this work. // We choose to block here since otherwise the router item may not // be available during routing resulting in a 404. $this->lock->wait(__FUNCTION__); } }
/** * Tests \Drupal\Core\Routing\RouteBuilder::rebuildIfNeeded() method. */ public function testRebuildIfNecessary() { $this->lock->expects($this->once())->method('acquire')->with('router_rebuild')->will($this->returnValue(TRUE)); $this->lock->expects($this->once())->method('release')->with('router_rebuild'); $this->routeBuilderIndicator->expects($this->once())->method('setRebuildNeeded'); $this->routeBuilderIndicator->expects($this->once())->method('setRebuildDone'); $this->routeBuilderIndicator->expects($this->exactly(2))->method('isRebuildNeeded')->will($this->onConsecutiveCalls(TRUE, FALSE)); $this->yamlDiscovery->expects($this->any())->method('findAll')->will($this->returnValue(array())); $this->routeBuilder->setRebuildNeeded(); // This will trigger a successful rebuild. $this->assertTrue($this->routeBuilder->rebuildIfNeeded()); // This will not trigger a rebuild. $this->assertFalse($this->routeBuilder->rebuildIfNeeded()); }
/** * Tests the destruct method. * * @covers ::destruct */ public function testDestruct() { $this->activeTheme = $this->getMockBuilder('Drupal\\Core\\Theme\\ActiveTheme')->disableOriginalConstructor()->getMock(); $this->themeManager->expects($this->once())->method('getActiveTheme')->willReturn($this->activeTheme); $this->activeTheme->expects($this->once())->method('getName')->willReturn('kitten_theme'); $this->libraryDiscoveryCollector = new LibraryDiscoveryCollector($this->cache, $this->lock, $this->libraryDiscoveryParser, $this->themeManager); $this->libraryDiscoveryParser->expects($this->once())->method('buildByExtension')->with('test')->willReturn($this->libraryData); $lock_key = 'library_info:kitten_theme:Drupal\\Core\\Cache\\CacheCollector'; $this->lock->expects($this->once())->method('acquire')->with($lock_key)->will($this->returnValue(TRUE)); $this->cache->expects($this->exactly(2))->method('get')->with('library_info:kitten_theme')->willReturn(FALSE); $this->cache->expects($this->once())->method('set')->with('library_info:kitten_theme', array('test' => $this->libraryData), Cache::PERMANENT, ['library_info']); $this->lock->expects($this->once())->method('release')->with($lock_key); // This should get data and persist the key. $this->libraryDiscoveryCollector->get('test'); $this->libraryDiscoveryCollector->destruct(); }
/** * Deletes data from the store for a given key and releases the lock on it. * * @param string $key * The key of the data to delete. * * @return bool * TRUE if the object was deleted or does not exist, FALSE if it exists but * is not owned by $this->owner. */ public function delete($key) { $key = $this->createkey($key); if (!($object = $this->storage->get($key))) { return TRUE; } elseif ($object->owner != $this->getOwner()) { return FALSE; } if (!$this->lockBackend->acquire($key)) { $this->lockBackend->wait($key); if (!$this->lockBackend->acquire($key)) { throw new TempStoreException(SafeMarkup::format("Couldn't acquire lock to delete item %key from %collection temporary storage.", array('%key' => $key, '%collection' => $this->storage->getCollectionName()))); } } $this->storage->delete($key); $this->lockBackend->release($key); return TRUE; }
/** * Deletes data from the store for a given key and releases the lock on it. * * @param string $key * The key of the data to delete. * * @return bool * TRUE if the object was deleted or does not exist, FALSE if it exists but * is not owned by $this->owner. */ public function delete($key) { $key = $this->createkey($key); if (!($object = $this->storage->get($key))) { return TRUE; } elseif ($object->owner != $this->getOwner()) { return FALSE; } if (!$this->lockBackend->acquire($key)) { $this->lockBackend->wait($key); if (!$this->lockBackend->acquire($key)) { throw new TempStoreException("Couldn't acquire lock to delete item '{$key}' from '{$this->storage->getCollectionName()}' temporary storage."); } } $this->storage->delete($key); $this->lockBackend->release($key); return TRUE; }
/** * Writes a value to the persistent cache immediately. * * @param bool $lock * (optional) Whether to acquire a lock before writing to cache. Defaults to * TRUE. */ protected function updateCache($lock = TRUE) { $data = array(); foreach ($this->keysToPersist as $offset => $persist) { if ($persist) { $data[$offset] = $this->storage[$offset]; } } if (empty($data) && empty($this->keysToRemove)) { return; } // Lock cache writes to help avoid stampedes. $cid = $this->getCid(); $lock_name = $this->normalizeLockName($cid . ':' . __CLASS__); if (!$lock || $this->lock->acquire($lock_name)) { // Set and delete operations invalidate the cache item. Try to also load // an eventually invalidated cache entry, only update an invalidated cache // entry if the creation date did not change as this could result in an // inconsistent cache. if ($cache = $this->cache->get($cid, $this->cacheInvalidated)) { if ($this->cacheInvalidated && $cache->created != $this->cacheCreated) { // We have invalidated the cache in this request and got a different // cache entry. Do not attempt to overwrite data that might have been // changed in a different request. We'll let the cache rebuild in // later requests. $this->cache->delete($cid); $this->lock->release($lock_name); return; } $data = array_merge($cache->data, $data); } // Remove keys marked for deletion. foreach ($this->keysToRemove as $delete_key) { unset($data[$delete_key]); } $this->cache->set($cid, $data, Cache::PERMANENT, $this->tags); if ($lock) { $this->lock->release($lock_name); } } $this->keysToPersist = array(); $this->keysToRemove = array(); }
/** * Tests getCid() * * @covers ::getCid */ public function testGetCid() { $data = $this->provider()[1]; /** @var \Symfony\Component\HttpFoundation\Request $request */ $request = $data[0]; /** @var \Symfony\Component\Routing\Route $route */ $route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT); $route->setPath('/test/{b}/{a}'); $request->attributes->get('_raw_variables')->add(['b' => 1, 'a' => 0]); $this->requestStack->push($request); $this->menuLinkManager->expects($this->any())->method('loadLinksbyRoute')->with('baby_llama')->will($this->returnValue($data[1])); $expected_link = $data[3]; $expected_trail = $data[4]; $expected_trail_ids = array_combine($expected_trail, $expected_trail); $this->menuLinkManager->expects($this->any())->method('getParentIds')->will($this->returnValueMap(array(array($expected_link->getPluginId(), $expected_trail_ids)))); $this->assertSame($expected_trail_ids, $this->menuActiveTrail->getActiveTrailIds($data[2])); $this->cache->expects($this->once())->method('set')->with('active-trail:route:baby_llama:route_parameters:' . serialize(['a' => 0, 'b' => 1])); $this->lock->expects($this->any())->method('acquire')->willReturn(TRUE); $this->menuActiveTrail->destruct(); }
/** * {@inheritdoc} */ public function sendSpool($limit = SpoolStorageInterface::UNLIMITED, array $conditions = array()) { $check_counter = 0; // Send pending messages from database cache. $spool = $this->spoolStorage->getMails($limit, $conditions); if (count($spool) > 0) { // Switch to the anonymous user. $anonymous_user = new AnonymousUserSession(); $this->accountSwitcher->switchTo($anonymous_user); $count_fail = $count_success = 0; $sent = array(); $this->startTimer(); while ($mail = $spool->nextMail()) { $mail->setKey('node'); $result = $this->sendMail($mail); // Update spool status. // This is not optimal for performance but prevents duplicate emails // in case of PHP execution time overrun. foreach ($spool->getProcessed() as $msid => $row) { $row_result = isset($row->result) ? $row->result : $result; $this->spoolStorage->updateMails(array($msid), $row_result); if ($row_result['status'] == SpoolStorageInterface::STATUS_DONE) { $count_success++; if (!isset($sent[$row->entity_type][$row->entity_id][$row->langcode])) { $sent[$row->entity_type][$row->entity_id][$row->langcode] = 1; } else { $sent[$row->entity_type][$row->entity_id][$row->langcode]++; } } if ($row_result['error']) { $count_fail++; } } // Check every n emails if we exceed the limit. // When PHP maximum execution time is almost elapsed we interrupt // sending. The remainder will be sent during the next cron run. if (++$check_counter >= static::SEND_CHECK_INTERVAL && ini_get('max_execution_time') > 0) { $check_counter = 0; // Break the sending if a percentage of max execution time was exceeded. $elapsed = $this->getCurrentExecutionTime(); if ($elapsed > static::SEND_TIME_LIMIT * ini_get('max_execution_time')) { $this->logger->warning('Sending interrupted: PHP maximum execution time almost exceeded. Remaining newsletters will be sent during the next cron run. If this warning occurs regularly you should reduce the !cron_throttle_setting.', array('!cron_throttle_setting' => \Drupal::l(t('Cron throttle setting'), new Url('simplenews.settings_mail')))); break; } } } // It is possible that all or at the end some results failed to get // prepared, report them separately. foreach ($spool->getProcessed() as $msid => $row) { $row_result = $row->result; $this->spoolStorage->updateMails(array($msid), $row_result); if ($row_result['status'] == SpoolStorageInterface::STATUS_DONE) { $count_success++; if (isset($row->langcode)) { if (!isset($sent[$row->entity_type][$row->entity_id][$row->langcode])) { $sent[$row->entity_type][$row->entity_id][$row->langcode] = 1; } else { $sent[$row->entity_type][$row->entity_id][$row->langcode]++; } } } if ($row_result['error']) { $count_fail++; } } // Update subscriber count. if ($this->lock->acquire('simplenews_update_sent_count')) { foreach ($sent as $entity_type => $ids) { foreach ($ids as $entity_id => $languages) { \Drupal::entityManager()->getStorage($entity_type)->resetCache(array($entity_id)); $entity = entity_load($entity_type, $entity_id); foreach ($languages as $langcode => $count) { $translation = $entity->getTranslation($langcode); $translation->simplenews_issue->sent_count = $translation->simplenews_issue->sent_count + $count; } $entity->save(); } } $this->lock->release('simplenews_update_sent_count'); } // Report sent result and elapsed time. On Windows systems getrusage() is // not implemented and hence no elapsed time is available. if (function_exists('getrusage')) { $this->logger->notice('%success emails sent in %sec seconds, %fail failed sending.', array('%success' => $count_success, '%sec' => round($this->getCurrentExecutionTime(), 1), '%fail' => $count_fail)); } else { $this->logger->notice('%success emails sent, %fail failed.', array('%success' => $count_success, '%fail' => $count_fail)); } $this->state->set('simplenews.last_cron', REQUEST_TIME); $this->state->set('simplenews.last_sent', $count_success); $this->accountSwitcher->switchBack(); return $count_success; } }
/** * Generates a derivative, given a style and image path. * * After generating an image, transfer it to the requesting agent. * * @param \Symfony\Component\HttpFoundation\Request $request * The request object. * @param string $scheme * The file scheme, defaults to 'private'. * @param \Drupal\image\ImageStyleInterface $image_style * The image style to deliver. * * @return \Symfony\Component\HttpFoundation\BinaryFileResponse|\Symfony\Component\HttpFoundation\Response * The transferred file as response or some error response. * * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException * Thrown when the user does not have access to the file. * @throws \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException * Thrown when the file is still being generated. */ public function deliver(Request $request, $scheme, ImageStyleInterface $image_style) { $target = $request->query->get('file'); $image_uri = $scheme . '://' . $target; // Check that the style is defined, the scheme is valid, and the image // derivative token is valid. Sites which require image derivatives to be // generated without a token can set the // 'image.settings:allow_insecure_derivatives' configuration to TRUE to // bypass the latter check, but this will increase the site's vulnerability // to denial-of-service attacks. To prevent this variable from leaving the // site vulnerable to the most serious attacks, a token is always required // when a derivative of a style is requested. // The $target variable for a derivative of a style has // styles/<style_name>/... as structure, so we check if the $target variable // starts with styles/. $valid = !empty($image_style) && file_stream_wrapper_valid_scheme($scheme); if (!$this->config('image.settings')->get('allow_insecure_derivatives') || strpos(ltrim($target, '\\/'), 'styles/') === 0) { $valid &= $request->query->get(IMAGE_DERIVATIVE_TOKEN) === $image_style->getPathToken($image_uri); } if (!$valid) { throw new AccessDeniedHttpException(); } $derivative_uri = $image_style->buildUri($image_uri); $headers = array(); // If using the private scheme, let other modules provide headers and // control access to the file. if ($scheme == 'private') { if (file_exists($derivative_uri)) { return parent::download($request, $scheme); } else { $headers = $this->moduleHandler()->invokeAll('file_download', array($image_uri)); if (in_array(-1, $headers) || empty($headers)) { throw new AccessDeniedHttpException(); } } } // Don't try to generate file if source is missing. if (!file_exists($image_uri)) { // If the image style converted the extension, it has been added to the // original file, resulting in filenames like image.png.jpeg. So to find // the actual source image, we remove the extension and check if that // image exists. $path_info = pathinfo($image_uri); $converted_image_uri = $path_info['dirname'] . DIRECTORY_SEPARATOR . $path_info['filename']; if (!file_exists($converted_image_uri)) { $this->logger->notice('Source image at %source_image_path not found while trying to generate derivative image at %derivative_path.', array('%source_image_path' => $image_uri, '%derivative_path' => $derivative_uri)); return new Response($this->t('Error generating image, missing source file.'), 404); } else { // The converted file does exist, use it as the source. $image_uri = $converted_image_uri; } } // Don't start generating the image if the derivative already exists or if // generation is in progress in another thread. $lock_name = 'image_style_deliver:' . $image_style->id() . ':' . Crypt::hashBase64($image_uri); if (!file_exists($derivative_uri)) { $lock_acquired = $this->lock->acquire($lock_name); if (!$lock_acquired) { // Tell client to retry again in 3 seconds. Currently no browsers are // known to support Retry-After. throw new ServiceUnavailableHttpException(3, $this->t('Image generation in progress. Try again shortly.')); } } // Try to generate the image, unless another thread just did it while we // were acquiring the lock. $success = file_exists($derivative_uri) || $image_style->createDerivative($image_uri, $derivative_uri); if (!empty($lock_acquired)) { $this->lock->release($lock_name); } if ($success) { $image = $this->imageFactory->get($derivative_uri); $uri = $image->getSource(); $headers += array('Content-Type' => $image->getMimeType(), 'Content-Length' => $image->getFileSize()); // \Drupal\Core\EventSubscriber\FinishResponseSubscriber::onRespond() // sets response as not cacheable if the Cache-Control header is not // already modified. We pass in FALSE for non-private schemes for the // $public parameter to make sure we don't change the headers. return new BinaryFileResponse($uri, 200, $headers, $scheme !== 'private'); } else { $this->logger->notice('Unable to generate the derived image located at %path.', array('%path' => $derivative_uri)); return new Response($this->t('Error generating image.'), 500); } }
/** * Determines if a import is already running. * * @return bool * TRUE if an import is already running, FALSE if not. */ public function alreadyImporting() { return !$this->lock->lockMayBeAvailable(static::LOCK_NAME); }
/** * Generates a derivative, given a style and image path. * * After generating an image, transfer it to the requesting agent. * * @param \Symfony\Component\HttpFoundation\Request $request * The request object. * @param string $scheme * The file scheme, defaults to 'private'. * @param \Drupal\image\ImageStyleInterface $image_style * The image style to deliver. * * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException * Thrown when the user does not have access to the file. * @throws \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException * Thrown when the file is still being generated. * * @return \Symfony\Component\HttpFoundation\BinaryFileResponse|\Symfony\Component\HttpFoundation\Response * The transferred file as response or some error response. */ public function deliver(Request $request, $scheme, ImageStyleInterface $image_style) { $target = $request->query->get('file'); $image_uri = $scheme . '://' . $target; // Check that the style is defined, the scheme is valid, and the image // derivative token is valid. Sites which require image derivatives to be // generated without a token can set the // 'image.settings:allow_insecure_derivatives' configuration to TRUE to // bypass the latter check, but this will increase the site's vulnerability // to denial-of-service attacks. $valid = !empty($image_style) && file_stream_wrapper_valid_scheme($scheme); if (!$this->config('image.settings')->get('allow_insecure_derivatives')) { $valid &= $request->query->get(IMAGE_DERIVATIVE_TOKEN) === $image_style->getPathToken($image_uri); } if (!$valid) { throw new AccessDeniedHttpException(); } $derivative_uri = $image_style->buildUri($image_uri); $headers = array(); // If using the private scheme, let other modules provide headers and // control access to the file. if ($scheme == 'private') { if (file_exists($derivative_uri)) { return parent::download($request, $scheme); } else { $headers = $this->moduleHandler()->invokeAll('file_download', array($image_uri)); if (in_array(-1, $headers) || empty($headers)) { throw new AccessDeniedHttpException(); } } } // Don't try to generate file if source is missing. if (!file_exists($image_uri)) { watchdog('image', 'Source image at %source_image_path not found while trying to generate derivative image at %derivative_path.', array('%source_image_path' => $image_uri, '%derivative_path' => $derivative_uri)); return new Response($this->t('Error generating image, missing source file.'), 404); } // Don't start generating the image if the derivative already exists or if // generation is in progress in another thread. $lock_name = 'image_style_deliver:' . $image_style->id() . ':' . Crypt::hashBase64($image_uri); if (!file_exists($derivative_uri)) { $lock_acquired = $this->lock->acquire($lock_name); if (!$lock_acquired) { // Tell client to retry again in 3 seconds. Currently no browsers are // known to support Retry-After. throw new ServiceUnavailableHttpException(3, $this->t('Image generation in progress. Try again shortly.')); } } // Try to generate the image, unless another thread just did it while we // were acquiring the lock. $success = file_exists($derivative_uri) || $image_style->createDerivative($image_uri, $derivative_uri); if (!empty($lock_acquired)) { $this->lock->release($lock_name); } if ($success) { drupal_page_is_cacheable(FALSE); $image = $this->imageFactory->get($derivative_uri); $uri = $image->getSource(); $headers += array('Content-Type' => $image->getMimeType(), 'Content-Length' => $image->getFileSize()); return new BinaryFileResponse($uri, 200, $headers); } else { watchdog('image', 'Unable to generate the derived image located at %path.', array('%path' => $derivative_uri)); return new Response($this->t('Error generating image.'), 500); } }