/** * {@inheritdoc} */ public function view(EntityInterface $node, $view_mode = 'full', $langcode = NULL) { $build = parent::view($node, $view_mode, $langcode); foreach ($node->uriRelationships() as $rel) { $url = $node->toUrl($rel); // Add link relationships if the user is authenticated or if the anonymous // user has access. Access checking must be done for anonymous users to // avoid traffic to inaccessible pages from web crawlers. For // authenticated users, showing the links in HTML head does not impact // user experience or security, since the routes are access checked when // visited and only visible via view source. This prevents doing // potentially expensive and hard to cache access checks on every request. // This means that the page will vary by user.permissions. We also rely on // the access checking fallback to ensure the correct cacheability // metadata if we have to check access. if ($this->currentUser->isAuthenticated() || $url->access($this->currentUser)) { // Set the node path as the canonical URL to prevent duplicate content. $build['#attached']['html_head_link'][] = array(array('rel' => $rel, 'href' => $url->toString()), TRUE); } if ($rel == 'canonical') { // Set the non-aliased canonical path as a default shortlink. $build['#attached']['html_head_link'][] = array(array('rel' => 'shortlink', 'href' => $url->setOption('alias', TRUE)->toString()), TRUE); } } // Given this varies by $this->currentUser->isAuthenticated(), add a cache // context based on the anonymous role. $build['#cache']['contexts'][] = 'user.roles:anonymous'; return $build; }
/** * Redirects login attempts on already-logged-in session to the destination. */ public function onRespond(FilterResponseEvent $event) { // Return early in most cases. if ($event->getRequest()->getMethod() !== 'POST') { return; } if (!$this->currentUser->isAuthenticated()) { return; } if (!$event->isMasterRequest()) { return; } if (!$event->getRequest()->query->has('destination')) { return; } if ($event->getResponse() instanceof RedirectResponse) { return; } // There has to be a better way to figure out if we landed on the 403/404 page. $page_403 = $this->configFactory->get('system.site')->get('page.403'); $page_404 = $this->configFactory->get('system.site')->get('page.404'); $path = $this->currentPath->getPath(); $route = $this->currentRouteMatch->getRouteName(); if ($route == 'system.403' || $page_403 && $path == $page_403 || $route == 'system.404' || $page_404 && $path == $page_404) { // RedirectResponseSubscriber will convert to absolute URL for us. $event->setResponse(new RedirectResponse($this->redirectDestination->get(), RedirectResponse::HTTP_SEE_OTHER)); } }
/** * Determine whether the page is configured to be offline. * * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event * The event to process. */ public function onKernelRequestMaintenance(GetResponseEvent $event) { $request = $event->getRequest(); $route_match = RouteMatch::createFromRequest($request); $path = $request->attributes->get('_system_path'); if ($this->maintenanceMode->applies($route_match)) { // If the site is offline, log out unprivileged users. if ($this->account->isAuthenticated() && !$this->maintenanceMode->exempt($this->account)) { user_logout(); // Redirect to homepage. $event->setResponse(new RedirectResponse($this->url('<front>', [], ['absolute' => TRUE]))); return; } if ($this->account->isAnonymous() && $path == 'user') { // Forward anonymous user to login page. $event->setResponse(new RedirectResponse($this->url('user.login', [], ['absolute' => TRUE]))); return; } } if ($this->account->isAuthenticated()) { if ($path == 'user/login') { // If user is logged in, redirect to 'user' instead of giving 403. $event->setResponse(new RedirectResponse($this->url('user.page', [], ['absolute' => TRUE]))); return; } if ($path == 'user/register') { // Authenticated user should be redirected to user edit page. $event->setResponse(new RedirectResponse($this->url('entity.user.edit_form', ['user' => $this->account->id()], ['absolute' => TRUE]))); return; } } }
/** * Determine whether the page is configured to be offline. * * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event * The event to process. */ public function onKernelRequestMaintenance(GetResponseEvent $event) { $request = $event->getRequest(); $route_match = RouteMatch::createFromRequest($request); $path = $request->attributes->get('_system_path'); if ($this->maintenanceMode->applies($route_match)) { // If the site is offline, log out unprivileged users. if ($this->account->isAuthenticated() && !$this->maintenanceMode->exempt($this->account)) { user_logout(); // Redirect to homepage. $event->setResponse(new RedirectResponse($this->url('<front>', [], ['absolute' => TRUE]))); return; } } if ($this->account->isAuthenticated()) { if ($path == 'user/login') { // If the user is already logged in, redirect to their profile page. $event->setResponse($this->redirect('entity.user.canonical', ['user' => $this->account->id()])); return; } if ($path == 'user/register') { // If the user is already registered, redirect to their edit page. $event->setResponse(new RedirectResponse($this->url('entity.user.edit_form', ['user' => $this->account->id()], ['absolute' => TRUE]))); return; } } }
/** * {@inheritdoc} */ public function getRouteName() { if ($this->currentUser->isAuthenticated()) { return 'user.logout'; } else { return 'user.login'; } }
/** * Updates the current user's last access time. * * @param \Symfony\Component\HttpKernel\Event\PostResponseEvent $event * The event to process. */ public function onKernelTerminate(PostResponseEvent $event) { if ($this->account->isAuthenticated() && REQUEST_TIME - $this->account->getLastAccessedTime() > Settings::get('session_write_interval', 180)) { // Do that no more than once per 180 seconds. /** @var \Drupal\user\UserStorageInterface $storage */ $storage = $this->entityManager->getStorage('user'); $storage->updateLastAccessTimestamp($this->account, REQUEST_TIME); } }
/** * Redirects anonymous users from user.page to user.login. * * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event * The event to process. */ public function onException(GetResponseForExceptionEvent $event) { $exception = $event->getException(); if ($exception instanceof AccessDeniedHttpException) { $route_name = RouteMatch::createFromRequest($event->getRequest())->getRouteName(); if ($route_name == 'user.page' && !$this->account->isAuthenticated()) { $event->setResponse($this->redirect('user.login')); } } }
/** * Logout users if site is in maintenance mode. * * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event * The event to process. */ public function onKernelRequestMaintenance(GetResponseEvent $event) { $request = $event->getRequest(); $route_match = RouteMatch::createFromRequest($request); if ($this->maintenanceMode->applies($route_match)) { // If the site is offline, log out unprivileged users. if ($this->account->isAuthenticated() && !$this->maintenanceMode->exempt($this->account)) { user_logout(); // Redirect to homepage. $event->setResponse($this->redirect($this->url('<front>'))); } } }
/** * Sets the 'is-active' class on links. * * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event * The response event. */ public function onResponse(FilterResponseEvent $event) { // Only care about HTML responses. if (stripos($event->getResponse()->headers->get('Content-Type'), 'text/html') === FALSE) { return; } // For authenticated users, the 'is-active' class is set in JavaScript. // @see system_page_attachments() if ($this->currentUser->isAuthenticated()) { return; } $response = $event->getResponse(); $response->setContent(static::setLinkActiveClass($response->getContent(), ltrim($this->currentPath->getPath(), '/'), $this->pathMatcher->isFrontPage(), $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_URL)->getId(), $event->getRequest()->query->all())); }
/** * The entry point for our subscriber. * * @param GetResponseEvent $event * The response event from the kernel. */ public function handle(GetResponseEvent $event) { // Don't do anything if this is a sub request and not a master request. if ($event->getRequestType() != HttpKernelInterface::MASTER_REQUEST) { return; } // Nothing to do if the user is already logged in. if ($this->currentUser->isAuthenticated()) { return; } // Some routes we don't want to run on. if ($this->isIgnoreableRoute()) { return; } // The service controller may have indicated that this current request // should not be automatically sent to CAS for authentication checking. // This is to prevent infinite redirect loops. $session = $this->requestStack->getCurrentRequest()->getSession(); if ($session->has('cas_temp_disable_auto_auth')) { $session->remove('cas_temp_disable_auto_auth'); $this->casHelper->log("Temp disable flag set, skipping CAS subscriber."); return; } // Check to see if we should require a forced login. It will set a response // on the event if so. if ($this->handleForcedPath($event)) { return; } // Check to see if we should initiate a gateway auth check. It will set a // response on the event if so. $this->handleGateway($event); }
/** * {@inheritdoc} */ public function setCache($form_build_id, $form, FormStateInterface $form_state) { // 6 hours cache life time for forms should be plenty. $expire = 21600; // Ensure that the form build_id embedded in the form structure is the same // as the one passed in as a parameter. This is an additional safety measure // to prevent legacy code operating directly with // \Drupal::formBuilder()->getCache() and \Drupal::formBuilder()->setCache() // from accidentally overwriting immutable form state. if (isset($form['#build_id']) && $form['#build_id'] != $form_build_id) { $this->logger->error('Form build-id mismatch detected while attempting to store a form in the cache.'); return; } // Cache form structure. if (isset($form)) { if ($this->currentUser->isAuthenticated()) { $form['#cache_token'] = $this->csrfToken->get(); } unset($form['#build_id_old']); $this->keyValueExpirableFactory->get('form')->setWithExpire($form_build_id, $form, $expire); } // Cache form state. if ($this->configFactory->get('system.performance')->get('cache.page.use_internal') && $this->isPageCacheable()) { $form_state->addBuildInfo('immutable', TRUE); } // Store the known list of safe strings for form re-use. // @todo Ensure we are not storing an excessively large string list in: // https://www.drupal.org/node/2295823 $form_state->addBuildInfo('safe_strings', SafeMarkup::getAll()); if ($data = $form_state->getCacheableArray()) { $this->keyValueExpirableFactory->get('form_state')->setWithExpire($form_build_id, $data, $expire); } }
/** * {@inheritdoc} */ public function setCache($form_build_id, $form, FormStateInterface $form_state) { // 6 hours cache life time for forms should be plenty. $expire = 21600; // Ensure that the form build_id embedded in the form structure is the same // as the one passed in as a parameter. This is an additional safety measure // to prevent legacy code operating directly with // \Drupal::formBuilder()->getCache() and \Drupal::formBuilder()->setCache() // from accidentally overwriting immutable form state. if (isset($form['#build_id']) && $form['#build_id'] != $form_build_id) { $this->logger->error('Form build-id mismatch detected while attempting to store a form in the cache.'); return; } // Cache form structure. if (isset($form)) { if ($this->currentUser->isAuthenticated()) { $form['#cache_token'] = $this->csrfToken->get(); } unset($form['#build_id_old']); $this->keyValueExpirableFactory->get('form')->setWithExpire($form_build_id, $form, $expire); } if ($data = $form_state->getCacheableArray()) { $this->keyValueExpirableFactory->get('form_state')->setWithExpire($form_build_id, $data, $expire); } }
/** * The entry point for our subscriber. * * @param GetResponseEvent $event * The response event from the kernel. */ public function handle(GetResponseEvent $event) { // Don't do anything if this is a sub request and not a master request. if ($event->getRequestType() != HttpKernelInterface::MASTER_REQUEST) { return; } // Nothing to do if the user is already logged in. if ($this->currentUser->isAuthenticated()) { return; } // Some routes we don't want to run on. if ($this->isIgnoreableRoute()) { return; } // Don't do anything if this is a request from cron, drush, crawler, etc. if ($this->isNotNormalRequest()) { return; } // The service controller may have indicated that this current request // should not be automatically sent to CAS for authentication checking. // This is to prevent infinite redirect loops. if (isset($_SESSION['cas_temp_disable'])) { unset($_SESSION['cas_temp_disable']); $this->casHelper->log("Temp disable flag set. Skip processing this request."); return; } // Check to see if we should require a forced login. It will set a response // on the event if so. if ($this->handleForcedPath($event)) { return; } // Check to see if we should initiate a gateway auth check. It will set a // response on the event if so. $this->handleGateway($event); }
/** * {@inheritdoc} */ public function getCountNewComments(EntityInterface $entity, $field_name = NULL, $timestamp = 0) { // @todo Replace module handler with optional history service injection // after http://drupal.org/node/2081585 if ($this->currentUser->isAuthenticated() && $this->moduleHandler->moduleExists('history')) { // Retrieve the timestamp at which the current user last viewed this entity. if (!$timestamp) { if ($entity->getEntityTypeId() == 'node') { $timestamp = history_read($entity->id()); } else { $function = $entity->getEntityTypeId() . '_last_viewed'; if (function_exists($function)) { $timestamp = $function($entity->id()); } else { // Default to 30 days ago. // @todo Remove once http://drupal.org/node/1029708 lands. $timestamp = COMMENT_NEW_LIMIT; } } } $timestamp = $timestamp > HISTORY_READ_LIMIT ? $timestamp : HISTORY_READ_LIMIT; // Use the timestamp to retrieve the number of new comments. $query = $this->queryFactory->get('comment')->condition('entity_type', $entity->getEntityTypeId())->condition('entity_id', $entity->id())->condition('created', $timestamp, '>')->condition('status', CommentInterface::PUBLISHED); if ($field_name) { // Limit to a particular field. $query->condition('field_name', $field_name); } return $query->count()->execute(); } return FALSE; }
/** * {@inheritdoc} */ public function collect(Request $request, Response $response, \Exception $exception = NULL) { $this->data['name'] = $this->currentUser->getDisplayName(); $this->data['authenticated'] = $this->currentUser->isAuthenticated(); $this->data['roles'] = []; $storage = $this->entityManager->getStorage('user_role'); foreach ($this->currentUser->getRoles() as $role) { $entity = $storage->load($role); $this->data['roles'][] = $entity->label(); } foreach ($this->providerCollector->getSortedProviders() as $provider_id => $provider) { if ($provider->applies($request)) { $this->data['provider'] = $provider_id; } } $this->data['anonymous'] = $this->configFactory->get('user.settings')->get('anonymous'); }
/** * {@inheritdoc} * * In addition to modifying the content key on entities, this implementation * will also set the comment entity key which all comments carry. * * @throws \InvalidArgumentException * Thrown when a comment is attached to an entity that no longer exists. */ public function buildComponents(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) { /** @var \Drupal\comment\CommentInterface[] $entities */ if (empty($entities)) { return; } // Pre-load associated users into cache to leverage multiple loading. $uids = array(); foreach ($entities as $entity) { $uids[] = $entity->getOwnerId(); } $this->entityManager->getStorage('user')->loadMultiple(array_unique($uids)); parent::buildComponents($build, $entities, $displays, $view_mode, $langcode); // A counter to track the indentation level. $current_indent = 0; foreach ($entities as $id => $entity) { if ($build[$id]['#comment_threaded']) { $comment_indent = count(explode('.', $entity->getThread())) - 1; if ($comment_indent > $current_indent) { // Set 1 to indent this comment from the previous one (its parent). // Set only one extra level of indenting even if the difference in // depth is higher. $build[$id]['#comment_indent'] = 1; $current_indent++; } else { // Set zero if this comment is on the same level as the previous one // or negative value to point an amount indents to close. $build[$id]['#comment_indent'] = $comment_indent - $current_indent; $current_indent = $comment_indent; } } // Commented entities already loaded after self::getBuildDefaults(). $commented_entity = $entity->getCommentedEntity(); $build[$id]['#entity'] = $entity; $build[$id]['#theme'] = 'comment__' . $entity->getFieldName() . '__' . $commented_entity->bundle(); $display = $displays[$entity->bundle()]; if ($display->getComponent('links')) { $callback = 'comment.post_render_cache:renderLinks'; $context = array('comment_entity_id' => $entity->id(), 'view_mode' => $view_mode, 'langcode' => $langcode, 'commented_entity_type' => $commented_entity->getEntityTypeId(), 'commented_entity_id' => $commented_entity->id(), 'in_preview' => !empty($entity->in_preview)); $placeholder = drupal_render_cache_generate_placeholder($callback, $context); $build[$id]['links'] = array('#post_render_cache' => array($callback => array($context)), '#markup' => $placeholder); } if (!isset($build[$id]['#attached'])) { $build[$id]['#attached'] = array(); } $build[$id]['#attached']['library'][] = 'comment/drupal.comment-by-viewer'; if ($this->moduleHandler->moduleExists('history') && $this->currentUser->isAuthenticated()) { $build[$id]['#attached']['library'][] = 'comment/drupal.comment-new-indicator'; // Embed the metadata for the comment "new" indicators on this node. $build[$id]['#post_render_cache']['history_attach_timestamp'] = array(array('node_id' => $commented_entity->id())); } } if ($build[$id]['#comment_threaded']) { // The final comment must close up some hanging divs. $build[$id]['#comment_indent_final'] = $current_indent; } }
/** * Redirects users when access is denied. * * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event * The event to process. */ public function onException(GetResponseForExceptionEvent $event) { $exception = $event->getException(); if ($exception instanceof AccessDeniedHttpException) { $route_name = RouteMatch::createFromRequest($event->getRequest())->getRouteName(); if ($this->account->isAuthenticated()) { switch ($route_name) { case 'user.login': // Redirect an authenticated user to the profile page. $event->setResponse($this->redirect('entity.user.canonical', ['user' => $this->account->id()])); break; case 'user.register': // Redirect an authenticated user to the profile form. $event->setResponse($this->redirect('entity.user.edit_form', ['user' => $this->account->id()])); break; } } elseif ($route_name === 'user.page') { $event->setResponse($this->redirect('user.login')); } } }
/** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items) { $elements = array(); $output = array(); $field_name = $this->fieldDefinition->getName(); $entity = $items->getEntity(); $status = $items->status; if ($status != CommentItemInterface::HIDDEN && empty($entity->in_preview) && !in_array($this->viewMode, array('search_result', 'search_index'))) { $comment_settings = $this->getFieldSettings(); // Only attempt to render comments if the entity has visible comments. // Unpublished comments are not included in // $entity->get($field_name)->comment_count, but unpublished comments // should display if the user is an administrator. $elements['#cache']['contexts'][] = 'user.permissions'; if ($this->currentUser->hasPermission('access comments') || $this->currentUser->hasPermission('administer comments')) { $output['comments'] = []; if ($entity->get($field_name)->comment_count || $this->currentUser->hasPermission('administer comments')) { $mode = $comment_settings['default_mode']; $comments_per_page = $comment_settings['per_page']; $comments = $this->storage->loadThread($entity, $field_name, $mode, $comments_per_page, $this->getSetting('pager_id')); if ($comments) { $build = $this->viewBuilder->viewMultiple($comments); $build['pager']['#type'] = 'pager'; if ($this->getSetting('pager_id')) { $build['pager']['#element'] = $this->getSetting('pager_id'); } $output['comments'] += $build; } } } // Append comment form if the comments are open and the form is set to // display below the entity. Do not show the form for the print view mode. if ($status == CommentItemInterface::OPEN && $comment_settings['form_location'] == CommentItemInterface::FORM_BELOW && $this->viewMode != 'print') { // Only show the add comment form if the user has permission. $elements['#cache']['contexts'][] = 'user.roles'; if ($this->currentUser->hasPermission('post comments')) { $output['comment_form'] = ['#lazy_builder' => ['comment.lazy_builders:renderForm', [$entity->getEntityTypeId(), $entity->id(), $field_name, $this->getFieldSetting('comment_type')]]]; // @todo Remove this in https://www.drupal.org/node/2543334. Until // then, \Drupal\Core\Render\Renderer::hasPoorCacheability() isn't // integrated with cache context bubbling, so this duplicates the // contexts added by \Drupal\comment\CommentForm::form(). $output['comment_form']['#cache']['contexts'][] = 'user.permissions'; $output['comment_form']['#cache']['contexts'][] = 'user.roles:authenticated'; if ($this->currentUser->isAuthenticated()) { $output['comment_form']['#cache']['contexts'][] = 'user'; } } } $elements[] = $output + array('#comment_type' => $this->getFieldSetting('comment_type'), '#comment_display_mode' => $this->getFieldSetting('default_mode'), 'comments' => array(), 'comment_form' => array()); } return $elements; }
/** * Checks access. * * @param \Symfony\Component\HttpFoundation\Request $request * The request object. * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * * @return \Drupal\Core\Access\AccessResultInterface * The access result. */ public function access(Request $request, AccountInterface $account) { $method = $request->getMethod(); // This check only applies if // 1. this is a write operation // 2. the user was successfully authenticated and // 3. the request comes with a session cookie. if (!in_array($method, array('GET', 'HEAD', 'OPTIONS', 'TRACE')) && $account->isAuthenticated() && $this->sessionConfiguration->hasSession($request)) { $csrf_token = $request->headers->get('X-CSRF-Token'); if (!\Drupal::csrfToken()->validate($csrf_token, 'rest')) { return AccessResult::forbidden()->setCacheMaxAge(0); } } // Let other access checkers decide if the request is legit. return AccessResult::allowed()->setCacheMaxAge(0); }
/** * {@inheritdoc} */ protected function checkAccess(EntityInterface $support_ticket, $operation, AccountInterface $account) { /** @var \Drupal\support_ticket\SupportTicketInterface $support_ticket */ // Fetch information from the support_ticket object if possible. $status = $support_ticket->isPublished(); $uid = $support_ticket->getOwnerId(); // Check if authors can view their own unpublished support tickets. if ($operation === 'view' && !$status && $account->hasPermission('view own unpublished support tickets') && $account->isAuthenticated() && $account->id() == $uid) { return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($support_ticket); } if ($operation === 'view') { return AccessResult::allowedIf($status)->cacheUntilEntityChanges($support_ticket); } // No opinion. return AccessResult::neutral(); }
/** * Checks access. * * @param \Symfony\Component\HttpFoundation\Request $request * The request object. * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * * @return string * A \Drupal\Core\Access\AccessInterface constant value. */ public function access(Request $request, AccountInterface $account) { $method = $request->getMethod(); $cookie = $request->attributes->get('_authentication_provider') == 'cookie'; // This check only applies if // 1. this is a write operation // 2. the user was successfully authenticated and // 3. the request comes with a session cookie. if (!in_array($method, array('GET', 'HEAD', 'OPTIONS', 'TRACE')) && $account->isAuthenticated() && $cookie) { $csrf_token = $request->headers->get('X-CSRF-Token'); if (!\Drupal::csrfToken()->validate($csrf_token, 'rest')) { return static::KILL; } } // Let other access checkers decide if the request is legit. return static::ALLOW; }
/** * {@inheritdoc} */ protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { switch ($operation) { case 'view': if ($account->hasPermission('access grade items') && $account->isAuthenticated() && $account->id() == $entity->getOwnerId()) { return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($entity); } case 'update': if ($account->hasPermission('administer grade items')) { return AccessResult::allowed()->cachePerPermissions(); } if (!$account->hasPermission('access grade items')) { return AccessResult::neutral()->cachePerPermissions(); } return AccessResult::allowedIf($account->hasPermission('customize grade items') && $entity == grade_scale_current_displayed_set($account))->cachePerPermissions()->cacheUntilEntityChanges($entity); case 'delete': return AccessResult::allowedIf($account->hasPermission('administer grade items') && $entity->id() != 'default')->cachePerPermissions(); default: // No opinion. return AccessResult::neutral(); } }
/** * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { /** @var \Drupal\comment\CommentInterface $comment */ $comment = $this->entity; $entity = $this->entityManager->getStorage($comment->getCommentedEntityTypeId())->load($comment->getCommentedEntityId()); $field_name = $comment->getFieldName(); $field_definition = $this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle())[$comment->getFieldName()]; $config = $this->config('user.settings'); // In several places within this function, we vary $form on: // - The current user's permissions. // - Whether the current user is authenticated or anonymous. // - The 'user.settings' configuration. // - The comment field's definition. $form['#cache']['contexts'][] = 'user.permissions'; $form['#cache']['contexts'][] = 'user.roles:authenticated'; $this->renderer->addCacheableDependency($form, $config); $this->renderer->addCacheableDependency($form, $field_definition->getConfig($entity->bundle())); // Use #comment-form as unique jump target, regardless of entity type. $form['#id'] = Html::getUniqueId('comment_form'); $form['#theme'] = array('comment_form__' . $entity->getEntityTypeId() . '__' . $entity->bundle() . '__' . $field_name, 'comment_form'); $anonymous_contact = $field_definition->getSetting('anonymous'); $is_admin = $comment->id() && $this->currentUser->hasPermission('administer comments'); if (!$this->currentUser->isAuthenticated() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT) { $form['#attached']['library'][] = 'core/drupal.form'; $form['#attributes']['data-user-info-from-browser'] = TRUE; } // If not replying to a comment, use our dedicated page callback for new // Comments on entities. if (!$comment->id() && !$comment->hasParentComment()) { $form['#action'] = $this->url('comment.reply', array('entity_type' => $entity->getEntityTypeId(), 'entity' => $entity->id(), 'field_name' => $field_name)); } $comment_preview = $form_state->get('comment_preview'); if (isset($comment_preview)) { $form += $comment_preview; } $form['author'] = array(); // Display author information in a details element for comment moderators. if ($is_admin) { $form['author'] += array('#type' => 'details', '#title' => $this->t('Administration')); } // Prepare default values for form elements. $author = ''; if ($is_admin) { if (!$comment->getOwnerId()) { $author = $comment->getAuthorName(); } $status = $comment->getStatus(); if (empty($comment_preview)) { $form['#title'] = $this->t('Edit comment %title', array('%title' => $comment->getSubject())); } } else { $status = $this->currentUser->hasPermission('skip comment approval') ? CommentInterface::PUBLISHED : CommentInterface::NOT_PUBLISHED; } $date = ''; if ($comment->id()) { $date = !empty($comment->date) ? $comment->date : DrupalDateTime::createFromTimestamp($comment->getCreatedTime()); } // The uid field is only displayed when a user with the permission // 'administer comments' is editing an existing comment from an // authenticated user. $owner = $comment->getOwner(); $form['author']['uid'] = ['#type' => 'entity_autocomplete', '#target_type' => 'user', '#default_value' => $owner->isAnonymous() ? NULL : $owner, '#selection_settings' => ['include_anonymous' => FALSE], '#title' => $this->t('Authored by'), '#description' => $this->t('Leave blank for %anonymous.', ['%anonymous' => $config->get('anonymous')]), '#access' => $is_admin]; // The name field is displayed when an anonymous user is adding a comment or // when a user with the permission 'administer comments' is editing an // existing comment from an anonymous user. $form['author']['name'] = array('#type' => 'textfield', '#title' => $is_admin ? $this->t('Name for @anonymous', ['@anonymous' => $config->get('anonymous')]) : $this->t('Your name'), '#default_value' => $author, '#required' => $this->currentUser->isAnonymous() && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT, '#maxlength' => 60, '#access' => $this->currentUser->isAnonymous() || $is_admin, '#size' => 30, '#attributes' => ['data-drupal-default-value' => $config->get('anonymous')]); if ($is_admin) { // When editing a comment only display the name textfield if the uid field // is empty. $form['author']['name']['#states'] = ['visible' => [':input[name="uid"]' => array('empty' => TRUE)]]; } // Add author email and homepage fields depending on the current user. $form['author']['mail'] = array('#type' => 'email', '#title' => $this->t('Email'), '#default_value' => $comment->getAuthorEmail(), '#required' => $this->currentUser->isAnonymous() && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT, '#maxlength' => 64, '#size' => 30, '#description' => $this->t('The content of this field is kept private and will not be shown publicly.'), '#access' => $comment->getOwner()->isAnonymous() && $is_admin || $this->currentUser->isAnonymous() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT); $form['author']['homepage'] = array('#type' => 'url', '#title' => $this->t('Homepage'), '#default_value' => $comment->getHomepage(), '#maxlength' => 255, '#size' => 30, '#access' => $is_admin || $this->currentUser->isAnonymous() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT); // Add administrative comment publishing options. $form['author']['date'] = array('#type' => 'datetime', '#title' => $this->t('Authored on'), '#default_value' => $date, '#size' => 20, '#access' => $is_admin); $form['author']['status'] = array('#type' => 'radios', '#title' => $this->t('Status'), '#default_value' => $status, '#options' => array(CommentInterface::PUBLISHED => $this->t('Published'), CommentInterface::NOT_PUBLISHED => $this->t('Not published')), '#access' => $is_admin); return parent::form($form, $form_state, $comment); }
/** * Checks access. * * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * @param \Symfony\Component\Routing\Route $route * The route to check against. * * @return \Drupal\Core\Access\AccessResultInterface * The access result. */ public function access(AccountInterface $account, Route $route) { $required_status = filter_var($route->getRequirement('_user_is_logged_in'), FILTER_VALIDATE_BOOLEAN); $actual_status = $account->isAuthenticated(); return AccessResult::allowedIf($required_status === $actual_status)->addCacheContexts(['user.roles:authenticated']); }
/** * {@inheritdoc} */ protected function checkAccess(EntityInterface $node, $operation, AccountInterface $account) { /** @var \Drupal\node\NodeInterface $node */ // Fetch information from the node object if possible. $status = $node->isPublished(); $uid = $node->getOwnerId(); // Check if authors can view their own unpublished nodes. if ($operation === 'view' && !$status && $account->hasPermission('view own unpublished content') && $account->isAuthenticated() && $account->id() == $uid) { return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($node); } // Evaluate node grants. return $this->grantStorage->access($node, $operation, $account); }
/** * {@inheritdoc} */ public function getTopics($tid, AccountInterface $account) { $config = $this->configFactory->get('forum.settings'); $forum_per_page = $config->get('topics.page_limit'); $sortby = $config->get('topics.order'); $header = array(array('data' => $this->t('Topic'), 'field' => 'f.title'), array('data' => $this->t('Replies'), 'field' => 'f.comment_count'), array('data' => $this->t('Last reply'), 'field' => 'f.last_comment_timestamp')); $order = $this->getTopicOrder($sortby); for ($i = 0; $i < count($header); $i++) { if ($header[$i]['field'] == $order['field']) { $header[$i]['sort'] = $order['sort']; } } $query = $this->connection->select('forum_index', 'f')->extend('Drupal\\Core\\Database\\Query\\PagerSelectExtender')->extend('Drupal\\Core\\Database\\Query\\TableSortExtender'); $query->fields('f'); $query->condition('f.tid', $tid)->addTag('node_access')->addMetaData('base_table', 'forum_index')->orderBy('f.sticky', 'DESC')->orderByHeader($header)->limit($forum_per_page); $count_query = $this->connection->select('forum_index', 'f'); $count_query->condition('f.tid', $tid); $count_query->addExpression('COUNT(*)'); $count_query->addTag('node_access'); $count_query->addMetaData('base_table', 'forum_index'); $query->setCountQuery($count_query); $result = $query->execute(); $nids = array(); foreach ($result as $record) { $nids[] = $record->nid; } if ($nids) { $nodes = $this->entityManager->getStorage('node')->loadMultiple($nids); $query = $this->connection->select('node_field_data', 'n')->extend('Drupal\\Core\\Database\\Query\\TableSortExtender'); $query->fields('n', array('nid')); $query->join('comment_entity_statistics', 'ces', "n.nid = ces.entity_id AND ces.field_name = 'comment_forum' AND ces.entity_type = 'node'"); $query->fields('ces', array('cid', 'last_comment_uid', 'last_comment_timestamp', 'comment_count')); $query->join('forum_index', 'f', 'f.nid = n.nid'); $query->addField('f', 'tid', 'forum_tid'); $query->join('users_field_data', 'u', 'n.uid = u.uid AND u.default_langcode = 1'); $query->addField('u', 'name'); $query->join('users_field_data', 'u2', 'ces.last_comment_uid = u2.uid AND u.default_langcode = 1'); $query->addExpression('CASE ces.last_comment_uid WHEN 0 THEN ces.last_comment_name ELSE u2.name END', 'last_comment_name'); $query->orderBy('f.sticky', 'DESC')->orderByHeader($header)->condition('n.nid', $nids, 'IN')->condition('n.default_langcode', 1); $result = array(); foreach ($query->execute() as $row) { $topic = $nodes[$row->nid]; $topic->comment_mode = $topic->comment_forum->status; foreach ($row as $key => $value) { $topic->{$key} = $value; } $result[] = $topic; } } else { $result = array(); } $topics = array(); $first_new_found = FALSE; foreach ($result as $topic) { if ($account->isAuthenticated()) { // A forum is new if the topic is new, or if there are new comments since // the user's last visit. if ($topic->forum_tid != $tid) { $topic->new = 0; } else { $history = $this->lastVisit($topic->id(), $account); $topic->new_replies = $this->commentManager->getCountNewComments($topic, 'comment_forum', $history); $topic->new = $topic->new_replies || $topic->last_comment_timestamp > $history; } } else { // Do not track "new replies" status for topics if the user is anonymous. $topic->new_replies = 0; $topic->new = 0; } // Make sure only one topic is indicated as the first new topic. $topic->first_new = FALSE; if ($topic->new != 0 && !$first_new_found) { $topic->first_new = TRUE; $first_new_found = TRUE; } if ($topic->comment_count > 0) { $last_reply = new \stdClass(); $last_reply->created = $topic->last_comment_timestamp; $last_reply->name = $topic->last_comment_name; $last_reply->uid = $topic->last_comment_uid; $topic->last_reply = $last_reply; } $topics[$topic->id()] = $topic; } return array('topics' => $topics, 'header' => $header); }
/** * {@inheritdoc} */ public function buildCommentedEntityLinks(FieldableEntityInterface $entity, array &$context) { $entity_links = array(); $view_mode = $context['view_mode']; if ($view_mode == 'search_index' || $view_mode == 'search_result' || $view_mode == 'print' || $view_mode == 'rss') { // Do not add any links if the entity is displayed for: // - search indexing. // - constructing a search result excerpt. // - print. // - rss. return array(); } $fields = $this->commentManager->getFields($entity->getEntityTypeId()); foreach ($fields as $field_name => $detail) { // Skip fields that the entity does not have. if (!$entity->hasField($field_name)) { continue; } $links = array(); $commenting_status = $entity->get($field_name)->status; if ($commenting_status != CommentItemInterface::HIDDEN) { // Entity has commenting status open or closed. $field_definition = $entity->getFieldDefinition($field_name); if ($view_mode == 'teaser') { // Teaser view: display the number of comments that have been posted, // or a link to add new comments if the user has permission, the // entity is open to new comments, and there currently are none. if ($this->currentUser->hasPermission('access comments')) { if (!empty($entity->get($field_name)->comment_count)) { $links['comment-comments'] = array('title' => $this->formatPlural($entity->get($field_name)->comment_count, '1 comment', '@count comments'), 'attributes' => array('title' => $this->t('Jump to the first comment.')), 'fragment' => 'comments', 'url' => $entity->urlInfo()); if ($this->moduleHandler->moduleExists('history')) { $links['comment-new-comments'] = array('title' => '', 'url' => Url::fromRoute('<current>'), 'attributes' => array('class' => 'hidden', 'title' => $this->t('Jump to the first new comment.'), 'data-history-node-last-comment-timestamp' => $entity->get($field_name)->last_comment_timestamp, 'data-history-node-field-name' => $field_name)); } } } // Provide a link to new comment form. if ($commenting_status == CommentItemInterface::OPEN) { $comment_form_location = $field_definition->getSetting('form_location'); if ($this->currentUser->hasPermission('post comments')) { $links['comment-add'] = array('title' => $this->t('Add new comments'), 'language' => $entity->language(), 'attributes' => array('title' => $this->t('Share your thoughts and opinions.')), 'fragment' => 'comment-form'); if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE) { $links['comment-add']['url'] = Url::fromRoute('comment.reply', ['entity_type' => $entity->getEntityTypeId(), 'entity' => $entity->id(), 'field_name' => $field_name]); } else { $links['comment-add'] += ['url' => $entity->urlInfo()]; } } elseif ($this->currentUser->isAnonymous()) { $links['comment-forbidden'] = array('title' => $this->commentManager->forbiddenMessage($entity, $field_name)); } } } else { // Entity in other view modes: add a "post comment" link if the user // is allowed to post comments and if this entity is allowing new // comments. if ($commenting_status == CommentItemInterface::OPEN) { $comment_form_location = $field_definition->getSetting('form_location'); if ($this->currentUser->hasPermission('post comments')) { // Show the "post comment" link if the form is on another page, or // if there are existing comments that the link will skip past. if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE || !empty($entity->get($field_name)->comment_count) && $this->currentUser->hasPermission('access comments')) { $links['comment-add'] = array('title' => $this->t('Add new comment'), 'attributes' => array('title' => $this->t('Share your thoughts and opinions.')), 'fragment' => 'comment-form'); if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE) { $links['comment-add']['url'] = Url::fromRoute('comment.reply', ['entity_type' => $entity->getEntityTypeId(), 'entity' => $entity->id(), 'field_name' => $field_name]); } else { $links['comment-add']['url'] = $entity->urlInfo(); } } } elseif ($this->currentUser->isAnonymous()) { $links['comment-forbidden'] = array('title' => $this->commentManager->forbiddenMessage($entity, $field_name)); } } } } if (!empty($links)) { $entity_links['comment__' . $field_name] = array('#theme' => 'links__entity__comment__' . $field_name, '#links' => $links, '#attributes' => array('class' => array('links', 'inline'))); if ($view_mode == 'teaser' && $this->moduleHandler->moduleExists('history') && $this->currentUser->isAuthenticated()) { $entity_links['comment__' . $field_name]['#cache']['contexts'][] = 'user'; $entity_links['comment__' . $field_name]['#attached']['library'][] = 'comment/drupal.node-new-comments-link'; // Embed the metadata for the "X new comments" link (if any) on this // entity. $entity_links['comment__' . $field_name]['#attached']['drupalSettings']['history']['lastReadTimestamps'][$entity->id()] = (int) history_read($entity->id()); $new_comments = $this->commentManager->getCountNewComments($entity); if ($new_comments > 0) { $page_number = $this->entityManager->getStorage('comment')->getNewCommentPageNumber($entity->{$field_name}->comment_count, $new_comments, $entity, $field_name); $query = $page_number ? ['page' => $page_number] : NULL; $value = ['new_comment_count' => (int) $new_comments, 'first_new_comment_link' => $entity->url('canonical', ['query' => $query, 'fragment' => 'new'])]; $parents = ['comment', 'newCommentsLinks', $entity->getEntityTypeId(), $field_name, $entity->id()]; NestedArray::setValue($entity_links['comment__' . $field_name]['#attached']['drupalSettings'], $parents, $value); } } } } return $entity_links; }
/** * Checks access. * * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * * @return \Drupal\Core\Access\AccessResultInterface * The access result. */ public function access(AccountInterface $account) { return AccessResult::allowedIf($account->isAuthenticated())->cachePerRole(); }
/** * Overrides Drupal\Core\Entity\EntityForm::form(). */ public function form(array $form, FormStateInterface $form_state) { /** @var \Drupal\comment\CommentInterface $comment */ $comment = $this->entity; $entity = $this->entityManager->getStorage($comment->getCommentedEntityTypeId())->load($comment->getCommentedEntityId()); $field_name = $comment->getFieldName(); $field_definition = $this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle())[$comment->getFieldName()]; // Use #comment-form as unique jump target, regardless of entity type. $form['#id'] = drupal_html_id('comment_form'); $form['#theme'] = array('comment_form__' . $entity->getEntityTypeId() . '__' . $entity->bundle() . '__' . $field_name, 'comment_form'); $anonymous_contact = $field_definition->getSetting('anonymous'); $is_admin = $comment->id() && $this->currentUser->hasPermission('administer comments'); if (!$this->currentUser->isAuthenticated() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT) { $form['#attached']['library'][] = 'core/drupal.form'; $form['#attributes']['data-user-info-from-browser'] = TRUE; } // If not replying to a comment, use our dedicated page callback for new // Comments on entities. if (!$comment->id() && !$comment->hasParentComment()) { $form['#action'] = $this->url('comment.reply', array('entity_type' => $entity->getEntityTypeId(), 'entity' => $entity->id(), 'field_name' => $field_name)); } $comment_preview = $form_state->get('comment_preview'); if (isset($comment_preview)) { $form += $comment_preview; } $form['author'] = array(); // Display author information in a details element for comment moderators. if ($is_admin) { $form['author'] += array('#type' => 'details', '#title' => $this->t('Administration')); } // Prepare default values for form elements. if ($is_admin) { $author = $comment->getAuthorName(); $status = $comment->getStatus(); if (empty($comment_preview)) { $form['#title'] = $this->t('Edit comment %title', array('%title' => $comment->getSubject())); } } else { if ($this->currentUser->isAuthenticated()) { $author = $this->currentUser->getUsername(); } else { $author = $comment->getAuthorName() ? $comment->getAuthorName() : ''; } $status = $this->currentUser->hasPermission('skip comment approval') ? CommentInterface::PUBLISHED : CommentInterface::NOT_PUBLISHED; } $date = ''; if ($comment->id()) { $date = !empty($comment->date) ? $comment->date : DrupalDateTime::createFromTimestamp($comment->getCreatedTime()); } // Add the author name field depending on the current user. $form['author']['name'] = array('#type' => 'textfield', '#title' => $this->t('Your name'), '#default_value' => $author, '#required' => $this->currentUser->isAnonymous() && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT, '#maxlength' => 60, '#size' => 30); if ($is_admin) { $form['author']['name']['#title'] = $this->t('Authored by'); $form['author']['name']['#description'] = $this->t('Leave blank for %anonymous.', array('%anonymous' => $this->config('user.settings')->get('anonymous'))); $form['author']['name']['#autocomplete_route_name'] = 'user.autocomplete'; } elseif ($this->currentUser->isAuthenticated()) { $form['author']['name']['#type'] = 'item'; $form['author']['name']['#value'] = $form['author']['name']['#default_value']; $form['author']['name']['#theme'] = 'username'; $form['author']['name']['#account'] = $this->currentUser; } elseif ($this->currentUser->isAnonymous()) { $form['author']['name']['#attributes']['data-drupal-default-value'] = $this->config('user.settings')->get('anonymous'); } $language_configuration = \Drupal::moduleHandler()->invoke('language', 'get_default_configuration', array('comment', $comment->getTypeId())); $form['langcode'] = array('#title' => t('Language'), '#type' => 'language_select', '#default_value' => $comment->getUntranslated()->language()->getId(), '#languages' => Language::STATE_ALL, '#access' => isset($language_configuration['language_show']) && $language_configuration['language_show']); // Add author email and homepage fields depending on the current user. $form['author']['mail'] = array('#type' => 'email', '#title' => $this->t('Email'), '#default_value' => $comment->getAuthorEmail(), '#required' => $this->currentUser->isAnonymous() && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT, '#maxlength' => 64, '#size' => 30, '#description' => $this->t('The content of this field is kept private and will not be shown publicly.'), '#access' => $is_admin || $this->currentUser->isAnonymous() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT); $form['author']['homepage'] = array('#type' => 'url', '#title' => $this->t('Homepage'), '#default_value' => $comment->getHomepage(), '#maxlength' => 255, '#size' => 30, '#access' => $is_admin || $this->currentUser->isAnonymous() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT); // Add administrative comment publishing options. $form['author']['date'] = array('#type' => 'datetime', '#title' => $this->t('Authored on'), '#default_value' => $date, '#size' => 20, '#access' => $is_admin); $form['author']['status'] = array('#type' => 'radios', '#title' => $this->t('Status'), '#default_value' => $status, '#options' => array(CommentInterface::PUBLISHED => $this->t('Published'), CommentInterface::NOT_PUBLISHED => $this->t('Not published')), '#access' => $is_admin); // Used for conditional validation of author fields. $form['is_anonymous'] = array('#type' => 'value', '#value' => $comment->id() ? !$comment->getOwnerId() : $this->currentUser->isAnonymous()); return parent::form($form, $form_state, $comment); }
/** * Checks access. * * @param \Symfony\Component\HttpFoundation\Request $request * The request object. * @param \Drupal\Core\Session\AccountInterface $account * The currently logged in account. * * @return \Drupal\Core\Access\AccessResultInterface * The access result. */ public function access(Request $request, AccountInterface $account) { $method = $request->getMethod(); // This check only applies if // 1. this is a write operation // 2. the user was successfully authenticated and // 3. the request comes with a session cookie. if (!in_array($method, array('GET', 'HEAD', 'OPTIONS', 'TRACE')) && $account->isAuthenticated() && $this->sessionConfiguration->hasSession($request)) { $csrf_token = $request->headers->get('X-CSRF-Token'); // @todo Remove validate call using 'rest' in 8.3. // Kept here for sessions active during update. if (!$this->csrfToken->validate($csrf_token, self::TOKEN_KEY) && !$this->csrfToken->validate($csrf_token, 'rest')) { return AccessResult::forbidden()->setReason('X-CSRF-Token request header is missing')->setCacheMaxAge(0); } } // Let other access checkers decide if the request is legit. return AccessResult::allowed()->setCacheMaxAge(0); }