-
Notifications
You must be signed in to change notification settings - Fork 16
Subscribe and unsubscribe routes #268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 12 commits
Commits
Show all changes
119 commits
Select commit
Hold shift + click to select a range
a21d45f
Add route definitions and Group access check service
damiankloip d0ce8cb
Start on SubscriptionController
damiankloip 590639a
More conversion in SubscriptionController
damiankloip 0f64119
Add initial subscription confirm form
damiankloip 954df49
Convert user access checking and form builder usage
damiankloip cf801aa
Use OgAccess in GroupCheck access
damiankloip 6e47f3a
Update cardinality and account usage in SubscriptionController::subsc…
damiankloip 2e8448b
Use optional feild_name parameter in subscribe route
damiankloip a2a545e
Add a GroupUnsubscribeConfirmForm
damiankloip 96edcc6
Use correct classes to build forms
damiankloip 438a8d7
Update OgMembershipInterface constants, misc tidyiny
damiankloip 5b9b1dc
Merge 8.x-1.x
amitaibu 06ac92b
Start moving classes around [skip ci]
amitaibu 065fdd6
Remove duplicate route [skip ci]
amitaibu c537f56
Move more classes [skip ci]
amitaibu e963011
Start adapting controller [skip ci]
amitaibu 01dcc87
Fix wrong negate [skip ci]
amitaibu c2fd194
Auto sniffer fixes [skip ci]
amitaibu d03412a
namespace fix [skip ci]
amitaibu 1bfc4dd
Fix subscribe route access [skip ci]
amitaibu 925dc81
Use correct t() method [skip ci]
amitaibu 06e3b28
More cleanup [skip ci]
amitaibu 03423f6
Add default form [skip ci]
amitaibu 0b904e6
Remove debug and add todo [skip ci]
amitaibu 1e40e8b
Rename membership type config [skip ci]
amitaibu 889a12d
Fix default value
amitaibu b5853cc
Back to confirm form.
amitaibu 340c9c7
Set state
amitaibu 322e2eb
Back to ContentEntityForm
amitaibu 96cf2ff
Keep confirm form
amitaibu 0d4fb12
Save the membership
amitaibu 078635b
Implement cancel
amitaibu 0969a91
Improve syntax
amitaibu 56cf2d8
getConfirmText
amitaibu a82a894
Cleanups
amitaibu 38e9ce5
Merge branch '8.x-1.x' into og-ui-subscribe-routes
amitaibu 1a56acf
Add docs [skip ci]
amitaibu 3e60d6e
Work in unsubscribe [skip ci]
amitaibu d4e007a
Add PHPdocs [skip ci]
amitaibu 53eaa1c
Sniffer fixes
amitaibu 1ed5855
Add membership request field
amitaibu f2226f0
Fix redirectReposnse [skip ci]
amitaibu 57e9a8b
Use ContentEntityForm instead of EntityConfirmFormBase
amitaibu ba626c9
Hide request field
amitaibu 9a6503d
Change field type
amitaibu 890d538
Revert "Change field type"
amitaibu 73793d4
Change fields to be string [skip ci]
amitaibu 27e93c9
Try to fix fields
amitaibu c531391
Fix file name
amitaibu be9cdf6
Allow passing multiple permissions
amitaibu df26bb5
Change Og::getMembership arguments order
amitaibu ff22372
Start worknig on unsubscribe
amitaibu c292fff
Define form name [skip ci]
amitaibu 9b242bb
Fix unsubscribe
amitaibu d80991b
Fix unsubscribe labelg
amitaibu ea02311
Use methods
amitaibu 2da713d
Swap arguments
amitaibu 81117db
Sniffer fixes
amitaibu 3737496
Fix failing tests due to scehma changes
amitaibu 59ed119
Set correct default state
amitaibu de6c792
Start adding tests
amitaibu 6656d9d
Cleanup
amitaibu 36918dd
Fix wrong indentation
amitaibu ba831fc
Sniffer fixes
amitaibu 5a586b2
Add Og::getRole
amitaibu 1f7027e
Use Og::getRole in test [skip ci]
amitaibu 6e575e4
Merge 8.x-1.x
amitaibu a9d331f
Merge branch 'resave-og-role' into og-ui-subscribe-routes
amitaibu 8e3e628
Add Og::getRole helper
amitaibu 9ae099f
Check ifAllowed correctly
amitaibu 026b261
Use correct state variable [skip ci]
amitaibu 22583aa
Check non-members access
amitaibu 61bb77a
Improve test
amitaibu c86f6f7
Sniffer fixes
amitaibu 5d4f732
Merge branch '8.x-1.x' into og-ui-subscribe-routes
amitaibu ce2fe35
Merge branch '279-non-member-access' into og-ui-subscribe-routes
amitaibu 740268c
Improve tests
amitaibu f16805e
Complete form test
amitaibu d0e8747
Improve comments [skip ci]
amitaibu 914ed02
Start adding unit tests
amitaibu 82e8f05
Add ::testNoGroup
amitaibu ba0d34b
Use isAllowed [skip ci]
amitaibu a7f22cd
Add more tests
amitaibu 53ae973
Add data provider
amitaibu 449a7d9
Add data provider to tests
amitaibu 022a645
Merge branch '8.x-1.x' into og-ui-subscribe-routes
amitaibu b626f0c
Merge branch '8.x-1.x' into og-ui-subscribe-routes
amitaibu 4e8212a
Add functional test
amitaibu 2bfcd9a
Debug test
amitaibu 6747f9f
Sniffer fixes
amitaibu 4975d04
Import classes
amitaibu e3bad9b
Test fixes
amitaibu c444279
Start fixing tests
amitaibu 707f249
Fix test
amitaibu 85059d7
Sniffer fixes
amitaibu 86c47a8
Confirm group label doesn't appear
amitaibu 17d375b
Add membership type interface
amitaibu 1845490
Merge branch '8.x-1.x' into og-ui-subscribe-routes
amitaibu 08b1a29
Use Og::createMembership
amitaibu 1962a35
Sniffer fixes
amitaibu c2a86dd
Improve docs
amitaibu c4cf934
Expand code coverage
amitaibu 42908bb
Reorganize code
amitaibu abd1bb2
Fix wrong assertion
amitaibu 3c08670
Improve scnearios
amitaibu d95dddd
Remove unused use
amitaibu a4d1ab9
Add comment about why not using provider [skip ci]
amitaibu e0bd094
Validate membership types
amitaibu 991e0b3
Start adding unsubscribe tests
amitaibu 9aae51a
Add member role
amitaibu a47fbc2
Fix tests
amitaibu 202b26a
Move permissions to Og core
amitaibu cce0e73
Remove service
amitaibu 3272a69
Remove module from test
amitaibu eba37fa
Prevent WSOD on wrong entity type
amitaibu 9b0511e
Add more test cases
amitaibu c950965
Fix notice
amitaibu 50e6891
Fix another notice
amitaibu a803648
Use valid random strings
amitaibu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| <?php | ||
|
|
||
| /** | ||
| * @file | ||
| * Contains \Drupal\og_ui\Access\GroupCheck. | ||
| */ | ||
|
|
||
| namespace Drupal\og_ui\Access; | ||
|
|
||
| use Drupal\Core\Access\AccessResult; | ||
| use Drupal\Core\Entity\EntityTypeManagerInterface; | ||
| use Drupal\Core\Routing\Access\AccessInterface; | ||
| use Drupal\Core\Session\AccountInterface; | ||
| use Drupal\og\Og; | ||
| use Drupal\og\OgAccess; | ||
| use Symfony\Component\Routing\Route; | ||
|
|
||
| /** | ||
| * Determines access to routes based on group access for the current user. | ||
| */ | ||
| class GroupCheck implements AccessInterface { | ||
|
|
||
| /** | ||
| * The entity type manager service. | ||
| * | ||
| * @var \Drupal\Core\Entity\EntityTypeManagerInterface | ||
| */ | ||
| protected $entityTypeManager; | ||
|
|
||
| /** | ||
| * Constructs a GroupCheck object. | ||
| * | ||
| * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager | ||
| */ | ||
| public function __construct(EntityTypeManagerInterface $entity_type_manager) { | ||
| $this->entityTypeManager = $entity_type_manager; | ||
| } | ||
|
|
||
| /** | ||
| * 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, $entity_type_id, $entity_id) { | ||
| // No access if the entity type doesn't exist. | ||
| if (!$this->entityTypeManager->getDefinition($entity_type_id, FALSE)) { | ||
| return AccessResult::forbidden(); | ||
| } | ||
|
|
||
| $entity_storage = $this->entityTypeManager->getStorage($entity_type_id); | ||
| $group = $entity_storage->load($entity_id); | ||
|
|
||
| // No access if no entity was loaded or it's not a group. | ||
| if (!$group || !Og::isGroup($entity_type_id, $group->bundle())) { | ||
| return AccessResult::forbidden(); | ||
| } | ||
|
|
||
| // @todo Convert when Role checking is added and has API to use. | ||
| // Verify the bundle has roles | ||
| // if (!og_roles($group_type, $bundle, $gid)) { | ||
| // return AccessResult::forbidden(); | ||
| // } | ||
|
|
||
| $permission = $permission = $route->getRequirement('_og_ui_user_access_group'); | ||
|
|
||
| return OgAccess::userAccess($group, $permission); | ||
| } | ||
|
|
||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| <?php | ||
|
|
||
| /** | ||
| * @file | ||
| * Contains \Drupal\og_ui\Controller\SubscriptionController. | ||
| */ | ||
|
|
||
| namespace Drupal\og_ui\Controller; | ||
|
|
||
| use Drupal\Core\Controller\ControllerBase; | ||
| use Drupal\Core\Field\FieldStorageDefinitionInterface; | ||
| use Drupal\Core\Url; | ||
| use Drupal\og\OgMembershipInterface; | ||
| use Drupal\user\Entity\User; | ||
| use Drupal\user\EntityOwnerInterface; | ||
| use Symfony\Component\HttpFoundation\RedirectResponse; | ||
| use Symfony\Component\HttpFoundation\Request; | ||
| use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | ||
| use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||
| use Drupal\og\Og; | ||
| use Drupal\og\OgAccess; | ||
|
|
||
| /** | ||
| * Controller for OG subscription routes. | ||
| */ | ||
| class SubscriptionController extends ControllerBase { | ||
|
|
||
| /** | ||
| * @param string $entity_type_id | ||
| * @param string|int $entity_id | ||
| * @param NULL|string $field_name | ||
| * | ||
| * @return mixed | ||
| */ | ||
| public function subscribe(Request $request, $entity_type_id, $entity_id, $field_name) { | ||
| // @todo We don't need to re-validate the entity type and entity group here, | ||
| // as it's already been done in the access check? | ||
| $entity_storage = $this->entityTypeManager()->getStorage($entity_type_id); | ||
| $entity_access = $this->entityTypeManager()->getAccessControlHandler($entity_type_id); | ||
| $group = $entity_storage->load($entity_id); | ||
|
|
||
| $account = User::load($this->currentUser()->id()); | ||
|
|
||
| if (empty($field_name)) { | ||
| $field_name = og_get_best_group_audience_field('user', $account, $entity_type_id, $group->bundle()); | ||
| if (empty($field_name)) { | ||
| throw new NotFoundHttpException(); | ||
| } | ||
| } | ||
|
|
||
| // @todo Requires FieldableEntityInterface/ContentEntityInterface | ||
| $field = $group->getFieldDefinition($field_name); | ||
|
|
||
| if (empty($instance) || !$entity_access->fieldAccess('view', $field, $account)) { | ||
| // Field name given is incorrect, or user doesn't have access to the field. | ||
| throw new NotFoundHttpException(); | ||
| } | ||
|
|
||
| if ($account->isAnonymous()) { | ||
| // Anonymous user can't request membership. | ||
| $destination = $this->getDestinationArray(); | ||
|
|
||
| $user_login_url = Url::fromRoute('user.login', [], $destination)->toString(); | ||
|
|
||
| // @todo I think this is correct? Other options are visitors or require | ||
| // approval both of which apply to the else instead of here. | ||
| if ($this->config('user.settings')->get('register') === USER_REGISTER_ADMINISTRATORS_ONLY) { | ||
| drupal_set_message($this->t('In order to join any group, you must <a href=":login">login</a>. After you have successfully done so, you will need to request membership again.', [':login' => $user_login_url])); | ||
| } | ||
| else { | ||
| $user_register_url = Url::fromRoute('user.register', [], $destination)->toString(); | ||
| drupal_set_message($this->t('In order to join any group, you must <a href=":login">login</a> or <a href=":register">register</a> a new account. After you have successfully done so, you will need to request membership again.', [':register' => $user_register_url, ':login' => $user_login_url])); | ||
| } | ||
|
|
||
| return new RedirectResponse(Url::fromRoute('user.page')->setAbsolute(TRUE)->toString()); | ||
| } | ||
|
|
||
| $redirect = FALSE; | ||
| $message = ''; | ||
| $params = [ | ||
| '@user' => $account->getDisplayName(), | ||
| ]; | ||
|
|
||
| // Show the group name only if user has access to it. | ||
| $params['@group'] = $group->access('view', $account) ? $group->label() : $this->t('Private group'); | ||
|
|
||
| if (Og::isMemberBlocked($group, $account)) { | ||
| // User is blocked, access denied. | ||
| throw new AccessDeniedHttpException(); | ||
| } | ||
|
|
||
| if (Og::isMemberPending($group, $account)) { | ||
| // User is pending, return them back. | ||
| $message = $this->t('@user already has a pending membership for the the group @group.', $params); | ||
| $redirect = TRUE; | ||
| } | ||
|
|
||
| if (Og::isMember($group, $account)) { | ||
| // User is already a member, return them back. | ||
| $message = $this->t('You are already a member of the group @group.', $params); | ||
| $redirect = TRUE; | ||
| } | ||
|
|
||
| $cardinality = $field->getStorageDefinition()->getCardinality(); | ||
|
|
||
| if (!$message && ($cardinality != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)) { | ||
| // Check if user is already registered as active or pending in the maximum | ||
| // allowed values. | ||
| if ($cardinality === 1) { | ||
| $count = $account->get($field_name)->value ? 1 : 0; | ||
| } | ||
| else { | ||
| $count = $account->get($field_name)->count(); | ||
| } | ||
|
|
||
| if ($count >= $cardinality) { | ||
| $message = $this->t('You cannot register to this group, as you have reached your maximum allowed subscriptions.'); | ||
| $redirect = TRUE; | ||
| } | ||
| } | ||
|
|
||
| if ($redirect) { | ||
| drupal_set_message($message, 'warning'); | ||
| return new RedirectResponse($group->toUrl()->setAbsolute(TRUE)->toString()); | ||
| } | ||
|
|
||
| if (OgAccess::userAccess($group, 'subscribe', $account) || OgAccess::userAccess($group, 'subscribe without approval', $account)) { | ||
| // Show the user a subscription confirmation. | ||
| // @todo Use buildForm() create our own form state object and attach | ||
| // field_name etc..? | ||
| return $this->formBuilder()->getForm('\Drupal\og_ui\Form\GroupSubscribeConfirmForm', $group, $account, $field_name); | ||
| } | ||
|
|
||
| throw new AccessDeniedHttpException(); | ||
| } | ||
|
|
||
| /** | ||
| * @param string $entity_type_id | ||
| * @param string|int $entity_id | ||
| */ | ||
| public function unsubscribe($entity_type_id, $entity_id) { | ||
| // @todo We don't need to re-validate the entity type and entity group here, | ||
| // as it's already been done in the access check? | ||
| $entity_storage = $this->entityTypeManager()->getStorage($entity_type_id); | ||
| $group = $entity_storage->load($entity_id); | ||
|
|
||
| $account = $this->currentUser(); | ||
|
|
||
| // Check the user isn't the manager of the group. | ||
| if (($group instanceof EntityOwnerInterface) && ($group->getOwnerId() !== $account->id())) { | ||
| if (Og::isMember($group, $account, [OgMembershipInterface::STATE_ACTIVE, OgMembershipInterface::STATE_PENDING])) { | ||
| // Show the user a subscription confirmation. | ||
| return $this->formBuilder()->getForm('\Drupal\og_ui\Form\GroupUnsubscribeConfirmForm', $group); | ||
| } | ||
|
|
||
| throw new AccessDeniedHttpException(); | ||
| } | ||
|
|
||
| drupal_set_message(t('As the manager of %group, you can not leave the group.', array('%group' => $group->label()))); | ||
|
|
||
| return new RedirectResponse($group->toUrl()->setAbsolute(TRUE)->toString()); | ||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| <?php | ||
|
|
||
| /** | ||
| * @file | ||
| * Contains \Drupal\og_ui\Form\GroupSubscribeConfirmForm. | ||
| */ | ||
|
|
||
| namespace Drupal\og_ui\Form; | ||
|
|
||
| use Drupal\Core\Entity\EntityConfirmFormBase; | ||
| use Drupal\Core\Form\FormStateInterface; | ||
| use Drupal\og\OgAccess; | ||
| use Drupal\og\OgMembershipInterface; | ||
|
|
||
| /** | ||
| * Provides a confirmation form for subscribing form a group. | ||
| */ | ||
| class GroupSubscribeConfirmForm extends EntityConfirmFormBase { | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function getFormId() { | ||
| return 'og_ui_subscribe_confirm_form'; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function getQuestion() { | ||
| return $this->t('Are you sure you want to join the group %title?', ['%title' => $this->entity->label()]); | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function getConfirmText() { | ||
| return $this->t('Join'); | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function getCancelUrl() { | ||
| // TODO: Implement getCancelUrl() method. | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function buildForm(array $form, FormStateInterface $form_state) { | ||
| // Indicate the OG membership state (active or pending). | ||
| $state = OgAccess::userAccess($this->entity, 'subscribe without approval') ? OgMembershipInterface::STATE_ACTIVE : OgMembershipInterface::STATE_PENDING; | ||
|
|
||
| if ($this->entity->access('view')) { | ||
| $label = $this->entity->label(); | ||
| } | ||
| else { | ||
| $label = $this->t('Private group'); | ||
|
|
||
| if ($state === OgMembershipInterface::STATE_ACTIVE) { | ||
| // Determine if a user can subscribe to a private group, when OG-access | ||
| // module is enabled, and the group is set to private. | ||
| $state = $this->config('og_ui.settings')->get('deny_subscribe_without_approval') ? OgMembershipInterface::STATE_PENDING : OgMembershipInterface::STATE_ACTIVE; | ||
| } | ||
| } | ||
|
|
||
| // // Add group membership form. | ||
| // $og_membership = og_membership_create($group_type, $gid, 'user', $account->uid, $field_name, array('state' => $state)); | ||
| // $form_state['og_membership'] = $og_membership; | ||
| // field_attach_form('og_membership', $og_membership, $form, $form_state); | ||
| // | ||
| // if ($state == OG_STATE_ACTIVE && !empty($form[OG_MEMBERSHIP_REQUEST_FIELD])) { | ||
| // // Hide the user request field. | ||
| // $form[OG_MEMBERSHIP_REQUEST_FIELD]['#access'] = FALSE; | ||
| // } | ||
| // $form['group_type'] = array('#type' => 'value', '#value' => $group_type); | ||
| // $form['gid'] = array('#type' => 'value', '#value' => $gid); | ||
| // $form['field_name'] = array('#type' => 'value', '#value' => $field_name); | ||
|
|
||
| return parent::buildForm($form, $form_state); | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function validateForm(array &$form, FormStateInterface $form_state) { | ||
| parent::validateForm($form, $form_state); | ||
|
|
||
| // @see entity_form_field_validate(). | ||
| $og_membership = $form_state['og_membership']; | ||
| field_attach_form_validate('og_membership', $og_membership, $form, $form_state); | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function submitForm(array &$form, FormStateInterface $form_state) { | ||
| $og_membership = $form_state['og_membership']; | ||
| field_attach_submit('og_membership', $og_membership, $form, $form_state); | ||
|
|
||
| $og_membership->save(); | ||
|
|
||
| if ($this->entity->access('view')) { | ||
| $form_state->setRedirectUrl($this->entity->toUrl()); | ||
| } | ||
| else { | ||
| // User doesn't have access to the group entity, so redirect to front page, | ||
| // with a message. | ||
| drupal_set_message($this->t('Your subscription request was sent.')); | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as @dawehner and @damiankloip suggested in #59 (comment) , I'll move those routes and the access services to OG core.