public function __viewIndex() { $this->setPageType('table'); $this->setTitle(__('%1$s – %2$s', array(__('Symphony'), __('Member Roles')))); if (is_null(extension_Members::getFieldHandle('role')) && !is_null(extension_Members::getMembersSection())) { $this->pageAlert(__('There is no Member: Role field in the active Members section. <a href="%s%d/">Add Member: Role field?</a>', array(SYMPHONY_URL . '/blueprints/sections/edit/', extension_Members::getMembersSection())), Alert::NOTICE); } $this->appendSubheading(__('Member Roles'), Widget::Anchor(__('Create New'), Administration::instance()->getCurrentPageURL() . 'new/', __('Create a Role'), 'create button', NULL, array('accesskey' => 'c'))); $roles = RoleManager::fetch(); $aTableHead = array(array(__('Name'), 'col'), array(__('Members'), 'col')); $aTableBody = array(); if (!is_array($roles) || empty($roles)) { $aTableBody = array(Widget::TableRow(array(Widget::TableData(__('None found.'), 'inactive', NULL, count($aTableHead))))); } else { if (is_null(extension_Members::getMembersSection())) { $aTableBody = array(Widget::TableRow(array(Widget::TableData(__('No Member section has been specified in <a href="%s">Preferences</a>. Please do this first.', array(SYMPHONY_URL . '/system/preferences/')), 'inactive', NULL, count($aTableHead))))); } else { $sectionManager = new SectionManager(Administration::instance()); $section = $sectionManager->fetch(extension_Members::getMembersSection()); $with_selected_roles = array(); $hasRoles = !is_null(extension_Members::getFieldHandle('role')); foreach ($roles as $role) { // Setup each cell $td1 = Widget::TableData(Widget::Anchor($role->get('name'), Administration::instance()->getCurrentPageURL() . 'edit/' . $role->get('id') . '/', null, 'content')); if ($role->get('id') != Role::PUBLIC_ROLE) { $td1->appendChild(Widget::Input("items[{$role->get('id')}]", null, 'checkbox')); } // Get the number of members for this role, as long as it's not the Public Role. if ($hasRoles && $role->get('id') != Role::PUBLIC_ROLE) { $member_count = Symphony::Database()->fetchVar('count', 0, sprintf("SELECT COUNT(*) AS `count` FROM `tbl_entries_data_%d` WHERE `role_id` = %d", extension_Members::getField('role')->get('id'), $role->get('id'))); $td2 = Widget::TableData(Widget::Anchor("{$member_count}", SYMPHONY_URL . '/publish/' . $section->get('handle') . '/?filter=' . extension_Members::getFieldHandle('role') . ':' . $role->get('id'))); } else { if ($role->get('id') == Role::PUBLIC_ROLE) { $td2 = Widget::TableData(__('This is the role assumed by the general public.')); } else { $td2 = Widget::TableData(__('None'), 'inactive'); } } // Add cells to a row $aTableBody[] = Widget::TableRow(array($td1, $td2)); if ($hasRoles && $role->get('id') != Role::PUBLIC_ROLE) { $with_selected_roles[] = array("move::" . $role->get('id'), false, $role->get('name')); } } } } $table = Widget::Table(Widget::TableHead($aTableHead), NULL, Widget::TableBody($aTableBody), 'selectable'); $this->Form->appendChild($table); $tableActions = new XMLElement('div'); $tableActions->setAttribute('class', 'actions'); $options = array(0 => array(null, false, __('With Selected...')), 2 => array('delete', false, __('Delete'), 'confirm'), 3 => array('delete-members', false, __('Delete Members'), 'confirm')); if (count($with_selected_roles) > 0) { $options[1] = array('label' => __('Move Members To'), 'options' => $with_selected_roles); } $tableActions->appendChild(Widget::Select('with-selected', $options)); $tableActions->appendChild(Widget::Input('action[apply]', __('Apply'), 'submit')); $this->Form->appendChild($tableActions); }
public function __construct($factory, $res) { parent::__construct($factory, $res); $field_ids = array(); if (!is_null(extension_Members::getFieldHandle('identity'))) { $field_ids[] = extension_Members::getField('identity')->get('id'); } if (!is_null(extension_Members::getFieldHandle('email'))) { $field_ids[] = extension_Members::getField('email')->get('id'); } // Get data about linking fields that point to the members // section AND to one of the linking fields (Username/Email) $sql_result = Symphony::Database()->fetch(sprintf("\n\t\t\t\t\tSELECT `child_section_id`, `child_section_field_id`\n\t\t\t\t\tFROM `tbl_sections_association`\n\t\t\t\t\tWHERE `parent_section_id` = %d\n\t\t\t\t\tAND `parent_section_field_id` IN ('%s')\n\t\t\t\t", extension_Members::getMembersSection(), implode("','", $field_ids))); $result = array(); if (is_array($sql_result)) { foreach ($sql_result as $data) { $result[$data['child_section_id']] = array('id' => $data['child_section_field_id']); } } $this->member_fields = $result; }
/** * Part 2 - Update Password, logs the user in * If the user changed their password, we need to login them back into the * system with their new password. */ public function filter_UpdatePasswordLogin(array $context) { // If the user didn't update their password, or no Identity field exists return if (empty($context['fields'][extension_Members::getFieldHandle('authentication')]['password'])) { return; } // Handle which is the Identity field, either the Member: Username or Member: Email field $identity = is_null(extension_Members::getFieldHandle('identity')) ? 'email' : 'identity'; $this->login(array(extension_Members::getFieldHandle($identity) => $context['entry']->getData(extension_Members::getField($identity)->get('id'), true)->value, extension_Members::getFieldHandle('authentication') => $context['fields'][extension_Members::getFieldHandle('authentication')]['password']), false); if (isset($_REQUEST['redirect'])) { redirect($_REQUEST['redirect']); } }
public function addMemberDetailsToPageParams(array $context = null) { if (!$this->isLoggedIn()) { return; } $this->initialiseMemberObject(); $context['params']['member-id'] = $this->getMemberID(); if (!is_null(extension_Members::getFieldHandle('role'))) { $role_data = $this->getMember()->getData(extension_Members::getField('role')->get('id')); $role = RoleManager::fetch($role_data['role_id']); if ($role instanceof Role) { $context['params']['member-role'] = $role->get('name'); } } if (!is_null(extension_Members::getFieldHandle('activation'))) { if ($this->getMember()->getData(extension_Members::getField('activation')->get('id'), true)->activated != "yes") { $context['params']['member-activated'] = 'no'; } } }
/** * This function will ensure that the user who has submitted the form (and * hence is requesting that an event be triggered) is actually allowed to * do this request. * There are 2 action types, creation and editing. Creation is a simple yes/no * affair, whereas editing has three levels of permission, None, Own Entries * or All Entries: * - None: This user can't do process this event * - Own Entries: If the entry the user is trying to update is their own * determined by if the `entry_id` or, in the case of a SBL or * similar field, the `entry_id` of the linked entry matches the logged in * user's id, process the event. * - All Entries: The user can update any entry in Symphony. * If there are no Roles in this system, or the event is set to ignore permissions * (by including a function, `ignoreRolePermissions` that returns `true`, it will * immediately proceed to processing any of the Filters attached to the event * before returning. * * @uses EventPreSaveFilter * * @param array $context * @return null */ public function checkEventPermissions(array &$context) { // If this system has no Roles, or the event is set to ignore role permissions // continue straight to processing the Filters if (!FieldManager::isFieldUsed(extension_Members::getFieldType('role')) || method_exists($context['event'], 'ignoreRolePermissions') && $context['event']->ignoreRolePermissions() == true) { $this->__processEventFilters($context); return null; } // Prior to Symphony 2.2.2, the EventPreSaveFilter delegate doesn't // pass the `$entry_id`. This can cause an issue when an Event has the // `allow_multiple` filter set as we can't determine the correct `$entry_id` // This will check to see if the `$entry_id` is set, otherwise fallback // to the previous logic. This will mean that using `allow_multiple` will // not be compatible without Symphony 2.2.2 and Members 1.1 // @see https://github.com/symphonycms/members/issues/167 if (isset($context['entry_id']) && is_numeric($context['entry_id'])) { $entry_id = (int) $context['entry_id']; $action = 'edit'; } else { if (isset($_POST['id']) && !empty($_POST['id'])) { $entry_id = (int) $_POST['id']; $action = 'edit'; } else { $action = 'create'; $entry_id = 0; } } $required_level = $action == 'create' ? EventPermissions::CREATE : EventPermissions::ALL_ENTRIES; $role_id = Role::PUBLIC_ROLE; $isLoggedIn = $this->getMemberDriver()->isLoggedIn(); if ($isLoggedIn && $this->getMemberDriver()->initialiseMemberObject()) { if ($this->getMemberDriver()->getMember() instanceof Entry) { $required_level = EventPermissions::OWN_ENTRIES; $role_data = $this->getMemberDriver()->getMember()->getData(extension_Members::getField('role')->get('id')); $role_id = $role_data['role_id']; if ($action == 'edit' && method_exists($context['event'], 'getSource')) { $section_id = $context['event']->getSource(); $isOwner = false; // If the event is the same section as the Members section, then for `$isOwner` // to be true, the `$entry_id` must match the currently logged in user. if ($section_id == $this->getMemberDriver()->getMember()->get('section_id')) { // Check the logged in member is the same as the `entry_id` that is about to // be updated. If so the user is the Owner and can modify EventPermissions::OWN_ENTRIES $isOwner = $this->getMemberDriver()->getMemberID() == $entry_id; } else { $field_ids = array(); // Get the ID's of the fields that may be used for Linking (Username/Email) if (!is_null(extension_Members::getFieldHandle('identity'))) { $field_ids[] = extension_Members::getField('identity')->get('id'); } if (!is_null(extension_Members::getFieldHandle('email'))) { $field_ids[] = extension_Members::getField('email')->get('id'); } // Query for the `field_id` of any linking fields that link to the members // section AND to one of the linking fields (Username/Email) $fields = Symphony::Database()->fetchCol('child_section_field_id', sprintf("\n\t\t\t\t\t\t\t\t\tSELECT `child_section_field_id`\n\t\t\t\t\t\t\t\t\tFROM `tbl_sections_association`\n\t\t\t\t\t\t\t\t\tWHERE `parent_section_id` = %d\n\t\t\t\t\t\t\t\t\tAND `child_section_id` = %d\n\t\t\t\t\t\t\t\t\tAND `parent_section_field_id` IN ('%s')\n\t\t\t\t\t\t\t\t", $this->getMemberDriver()->getMember()->get('section_id'), $section_id, implode("','", $field_ids))); // If there was a link found, get the `relation_id`, which is the `member_id` of // an entry in the active Members section. if (!empty($fields)) { foreach ($fields as $field_id) { if ($isOwner === true) { break; } $field = FieldManager::fetch($field_id); if ($field instanceof Field) { // So we are trying to find all entries that have selected the Member entry // to determine ownership. This check will use the `fetchAssociatedEntryIDs` // function, which typically works backwards, by accepting the `entry_id` (in // this case, our logged in Member ID). This will return an array of all the // linked entries, so we then just check that the current entry that is going to // be updated is in that array $member_id = $field->fetchAssociatedEntryIDs($this->getMemberDriver()->getMemberID()); $isOwner = in_array($entry_id, $member_id); } } } } // User is not the owner, so they can edit EventPermissions::ALL_ENTRIES if ($isOwner === false) { $required_level = EventPermissions::ALL_ENTRIES; } } } } $role = RoleManager::fetch($role_id); $event_handle = strtolower(preg_replace('/^event/i', NULL, get_class($context['event']))); $success = false; if ($role) { $success = $role->canProcessEvent($event_handle, $action, $required_level) ? true : false; } $context['messages'][] = array('permission', $success, $success === false ? __('You are not authorised to perform this action.') : null); // Process the Filters for this event. $this->__processEventFilters($context); }
protected function __trigger() { $result = new XMLElement(self::ROOTELEMENT); $fields = $_REQUEST['fields']; $this->driver = Symphony::ExtensionManager()->create('members'); $requested_identity = $fields[extension_Members::getFieldHandle('identity')]; // Add POST values to the Event XML $post_values = new XMLElement('post-values'); // Create the post data cookie element if (is_array($fields) && !empty($fields)) { General::array_to_xml($post_values, $fields, true); } // Set the section ID $result = $this->setMembersSection($result, $_REQUEST['members-section-id']); if ($result->getAttribute('result') === 'error') { // We are not calling notifyMembersPasswordResetFailure here, // because this is not an authentication error $result->appendChild($post_values); return $result; } // Trigger the EventPreSaveFilter delegate. We are using this to make // use of the XSS Filter extension that will ensure our data is ok to use $this->notifyEventPreSaveFilter($result, $fields, $post_values); if ($result->getAttribute('result') == 'error') { // We are not calling notifyMembersPasswordResetFailure here, // because this is not an authentication error return $result; } // Add any Email Templates for this event $this->addEmailTemplates('reset-password-template'); // Check that there is a row with this recovery code and that they // request a password reset $auth = $this->driver->getMemberDriver()->section->getField('authentication'); if (!$auth instanceof fieldMemberPassword) { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement('message', __('No Authentication field found.'), array('message-id' => MemberEventMessages::MEMBER_ERRORS))); $this->notifyMembersPasswordResetFailure($requested_identity); $result->appendChild($post_values); return $result; } // Check that either a Member: Username or Member: Email field // has been detected $identity = $this->driver->getMemberDriver()->setIdentityField($fields, false); if (!$identity instanceof Identity) { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement('message', __('No Identity field found.'), array('message-id' => MemberEventMessages::MEMBER_ERRORS))); $this->notifyMembersPasswordResetFailure($requested_identity); $result->appendChild($post_values); return $result; } if (!isset($fields[$this->driver->getMemberDriver()->section->getFieldHandle('authentication')]['recovery-code']) or empty($fields[$this->driver->getMemberDriver()->section->getFieldHandle('authentication')]['recovery-code'])) { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement('message', __('Member event encountered errors when processing.'), array('message-id' => MemberEventMessages::MEMBER_ERRORS))); $result->appendChild(new XMLElement($auth->get('element_name'), null, array('label' => $auth->get('label'), 'type' => 'missing', 'message-id' => EventMessages::FIELD_MISSING, 'message' => __('Recovery code is a required field.')))); $this->notifyMembersPasswordResetFailure($requested_identity); $result->appendChild($post_values); return $result; } $row = Symphony::Database()->fetchRow(0, sprintf("\n\t\t\t\t\tSELECT `entry_id`, `recovery-code`\n\t\t\t\t\tFROM tbl_entries_data_%d\n\t\t\t\t\tWHERE reset = 'yes'\n\t\t\t\t\tAND `recovery-code` = '%s'\n\t\t\t\t", $auth->get('id'), Symphony::Database()->cleanValue($fields[$this->driver->getMemberDriver()->section->getFieldHandle('authentication')]['recovery-code']))); if (empty($row)) { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement('message', __('Member encountered errors.'), array('message-id' => MemberEventMessages::MEMBER_ERRORS))); $result->appendChild(new XMLElement($auth->get('element_name'), null, array('label' => $auth->get('label'), 'type' => 'invalid', 'message-id' => EventMessages::FIELD_INVALID, 'message' => __('No recovery code found.')))); $this->notifyMembersPasswordResetFailure($requested_identity); } else { // Retrieve Member Entry record $entry = $this->driver->getMemberDriver()->fetchMemberFromID($row['entry_id']); // Check that the given Identity data matches the Member that the // recovery code is for $member_id = $identity->fetchMemberIDBy($fields[$identity->get('element_name')]); if (!$entry instanceof Entry || $member_id != $row['entry_id']) { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement('message', __('Member event encountered errors when processing.'), array('message-id' => MemberEventMessages::MEMBER_ERRORS))); $result->appendChild(new XMLElement($identity->get('element_name'), null, extension_Members::$_errors[$identity->get('element_name')])); $this->notifyMembersPasswordResetFailure($requested_identity); $result->appendChild($post_values); return $result; } // Check that the recovery code is still valid and has not expired if (is_null(Symphony::Database()->fetchVar('entry_id', 0, sprintf("\n\t\t\t\t\t\tSELECT `entry_id`\n\t\t\t\t\t\tFROM `tbl_entries_data_%d`\n\t\t\t\t\t\tWHERE `entry_id` = %d\n\t\t\t\t\t\tAND DATE_FORMAT(expires, '%%Y-%%m-%%d %%H:%%i:%%s') > '%s'\n\t\t\t\t\t\tLIMIT 1\n\t\t\t\t\t", $auth->get('id'), $member_id, DateTimeObj::get('Y-m-d H:i:s', strtotime('now - ' . $auth->get('code_expiry'))))))) { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement('message', __('Member event encountered errors when processing.'), array('message-id' => MemberEventMessages::MEMBER_ERRORS))); $result->appendChild(new XMLElement($auth->get('element_name'), null, array('label' => $auth->get('label'), 'type' => 'invalid', 'message-id' => MemberEventMessages::RECOVERY_CODE_INVALID, 'message' => __('Recovery code has expired.')))); $this->notifyMembersPasswordResetFailure($requested_identity); $result->appendChild($post_values); return $result; } // Create new password using the auth field so simulate the checkPostFieldData // and processRawFieldData functions. $message = ''; // For the purposes of this event, the auth field should ALWAYS be required // as we have to set a password (ie. handle the case where this field is // actually optional) RE: #193 $auth->set('required', 'yes'); $status = $auth->checkPostFieldData($fields[$auth->get('element_name')], $message, $member_id); if (Field::__OK__ != $status) { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement('message', __('Member event encountered errors when processing.'), array('message-id' => MemberEventMessages::MEMBER_ERRORS))); $result->appendChild(new XMLElement($auth->get('element_name'), null, array('type' => $status == Field::__MISSING_FIELDS__ ? 'missing' : 'invalid', 'message' => $message, 'message-id' => $status == Field::__MISSING_FIELDS__ ? EventMessages::FIELD_MISSING : EventMessages::FIELD_INVALID, 'label' => $auth->get('label')))); $this->notifyMembersPasswordResetFailure($requested_identity); $result->appendChild($post_values); return $result; } // processRawFieldData will encode the user's new password with the current one $status = Field::__OK__; $data = $auth->processRawFieldData(array('password' => Symphony::Database()->cleanValue($fields[$auth->get('element_name')]['password'])), $status); $data['recovery-code'] = null; $data['reset'] = 'no'; $data['expires'] = null; // Update the database with the new password, removing the recovery code and setting // reset to no. Symphony::Database()->update($data, 'tbl_entries_data_' . $auth->get('id'), ' `entry_id` = ' . $member_id); /** * Fired just after a Member has reset their password. * * @delegate MembersPostResetPassword * @param string $context * '/frontend/' * @param integer $member_id * The Member ID of the member who just reset their password */ Symphony::ExtensionManager()->notifyMembers('MembersPostResetPassword', '/frontend/', array('member_id' => $member_id)); // Trigger the EventFinalSaveFilter delegate. The Email Template Filter // and Email Template Manager extensions use this delegate to send any // emails attached to this event $this->notifyEventFinalSaveFilter($result, $fields, $post_values, $entry); if (extension_Members::getSetting('reset-password-auto-login') == "yes") { // Instead of replicating the same logic, call the UpdatePasswordLogin which will // handle relogging in the user. $this->driver->getMemberDriver()->filter_UpdatePasswordLogin(array('entry' => $entry, 'fields' => array($this->driver->getMemberDriver()->section->getFieldHandle('authentication') => array('password' => Symphony::Database()->cleanValue($fields[$auth->get('element_name')]['password']))))); } if (isset($_REQUEST['redirect'])) { redirect($_REQUEST['redirect']); } $result->setAttribute('result', 'success'); } $result->appendChild($post_values); return $result; }
protected function __trigger() { $result = new XMLElement(self::ROOTELEMENT); $fields = $_POST['fields']; // Add POST values to the Event XML $post_values = new XMLElement('post-values'); // Create the post data cookie element if (is_array($fields) && !empty($fields)) { General::array_to_xml($post_values, $fields, true); } // Trigger the EventPreSaveFilter delegate. We are using this to make // use of the XSS Filter extension that will ensure our data is ok to use $this->notifyEventPreSaveFilter($result, $fields, $post_values); if ($result->getAttribute('result') == 'error') { return $result; } // Add any Email Templates for this event $this->addEmailTemplates('activate-account-template'); // Do sanity checks on the incoming data $activation = extension_Members::getField('activation'); if (!$activation instanceof fieldMemberActivation) { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement('error', null, array('type' => 'invalid', 'message' => __('No Activation field found.')))); $result->appendChild($post_values); return $result; } // Check that either a Member: Username or Member: Email field has been detected $identity = SymphonyMember::setIdentityField($fields, false); if (!$identity instanceof Identity) { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement('error', null, array('type' => 'invalid', 'message' => __('No Identity field found.')))); $result->appendChild($post_values); return $result; } // Ensure that the Member: Activation field has been provided if (!isset($fields[$activation->get('element_name')]) or empty($fields[$activation->get('element_name')])) { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement($activation->get('element_name'), null, array('type' => 'missing', 'message' => __('%s is a required field.', array($activation->get('label'))), 'label' => $activation->get('label')))); $result->appendChild($post_values); return $result; } else { $fields[$activation->get('element_name')] = trim($fields[$activation->get('element_name')]); } // Check that a member exists first before proceeding. $member_id = $identity->fetchMemberIDBy($fields[$identity->get('element_name')]); if (is_null($member_id)) { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement($identity->get('element_name'), null, array('type' => 'invalid', 'message' => __('Member not found.'), 'label' => $identity->get('label')))); $result->appendChild($post_values); return $result; } // Retrieve Member's entry $driver = Symphony::ExtensionManager()->create('members'); $entry = $driver->getMemberDriver()->fetchMemberFromID($member_id); if ($entry->getData($activation->get('id'), true)->activated == 'yes') { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement($activation->get('element_name'), null, array('type' => 'invalid', 'message' => __('Member is already activated.'), 'label' => $activation->get('label')))); $result->appendChild($post_values); return $result; } // Make sure we dont accidently use an expired code $activation->purgeCodes(); $code = $activation->isCodeActive($member_id); if ($code['code'] != $fields[$activation->get('element_name')]) { $result->setAttribute('result', 'error'); $result->appendChild(new XMLElement($activation->get('element_name'), null, array('type' => 'invalid', 'message' => __('Activation error. Code was invalid or has expired.'), 'label' => $activation->get('label')))); $result->appendChild($post_values); return $result; } // Got to here, then everything is awesome. $status = Field::__OK__; $data = $activation->processRawFieldData(array('activated' => 'yes', 'timestamp' => DateTimeObj::get('Y-m-d H:i:s', time()), 'code' => null), $status); // Update the database setting activation to yes. Symphony::Database()->update($data, 'tbl_entries_data_' . $activation->get('id'), ' `entry_id` = ' . $member_id); // Update our `$entry` object with the new activation data $entry->setData($activation->get('id'), $data); // Simulate an array to login with. $data_fields = array_merge($fields, array(extension_Members::getFieldHandle('authentication') => $entry->getData(extension_Members::getField('authentication')->get('id'), true)->password)); // Only login if the Activation field allows auto login. if (extension_Members::getSetting('activate-account-auto-login') == 'no' || $driver->getMemberDriver()->login($data_fields, true)) { // Trigger the EventFinalSaveFilter delegate. The Email Template Filter // and Email Template Manager extensions use this delegate to send any // emails attached to this event $this->notifyEventFinalSaveFilter($result, $fields, $post_values, $entry); // If a redirect is set, redirect, the page won't be able to receive // the Event XML anyway if (isset($_REQUEST['redirect'])) { redirect($_REQUEST['redirect']); } $result->setAttribute('result', 'success'); } else { if (extension_Members::getSetting('activate-account-auto-login') == 'yes') { if (isset($_REQUEST['redirect'])) { redirect($_REQUEST['redirect']); } $result->setAttribute('result', 'error'); } } $result->appendChild($post_values); return $result; }