├── .gitignore
├── assets
├── members.publish.css
├── members.roles.js
├── members.roles.css
└── members.events.js
├── LICENCE
├── lib
├── class.membersection.php
├── class.identity.php
├── class.membersevent.php
├── class.members.php
└── class.role.php
├── content
└── content.events.php
├── fields
├── field.membertimezone.php
├── field.memberusername.php
├── field.memberemail.php
├── field.memberrole.php
└── field.memberactivation.php
├── extension.meta.xml
├── lang
├── lang.de.php
├── lang.ru.php
├── lang.el.php
└── lang.it.php
├── events
├── event.members_regenerate_activation_code.php
├── event.members_generate_recovery_code.php
├── event.members_activate_account.php
└── event.members_reset_password.php
└── README.markdown
/.gitignore:
--------------------------------------------------------------------------------
1 | .AppleDouble
2 | ._*
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/assets/members.publish.css:
--------------------------------------------------------------------------------
1 | .secondary label > i {
2 | height: 16px;
3 | white-space: nowrap;
4 | }
5 | .secondary label > i:hover {
6 | white-space:normal;
7 | height: auto;
8 | background: rgba(255, 255, 255, 0.8);
9 | }
10 |
11 | .field-memberpassword .column {
12 | margin-bottom: 0;
13 | }
14 |
--------------------------------------------------------------------------------
/assets/members.roles.js:
--------------------------------------------------------------------------------
1 | jQuery(document).ready(function(){
2 |
3 | var $role_permissions = jQuery('table.role-permissions');
4 |
5 | // find columns containing form elements
6 | $role_permissions.find('thead th.new, thead th.edit')
7 | .each(function() {
8 | // append element for styling
9 | var text = jQuery(this).text();
10 |
11 | jQuery(this).html('' + text + '');
12 | })
13 | .bind('click', function() {
14 | var index = jQuery(this).index(),
15 | // toggle whether this column is all-on or all-off
16 | check_all = jQuery(this).toggleClass('checked').hasClass('checked');
17 |
18 | // remove toggle state from all other columns
19 | $role_permissions.find('th:not(:eq('+index+'))').removeClass('checked');
20 |
21 | // toggle form elements in this column ("active" rows only)
22 | $role_permissions.find('tbody tr:not(.inactive) td:nth-child(' + (index + 1) + ')').each(function() {
23 | if(check_all) {
24 | jQuery(this).find('input').attr('checked', 'checked');
25 | }
26 | else {
27 | jQuery(this).find('input').removeAttr('checked');
28 | }
29 | });
30 | });
31 |
32 | // disable form elements of "inactive" rows
33 | $role_permissions.find('tr.inactive input').attr('disabled', 'disabled').removeAttr('checked');
34 |
35 | });
36 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | All source code included in the "Members" archive is, unless otherwise
2 | specified, released under the MIT licence as follows:
3 |
4 | ----- begin license block -----
5 |
6 | Copyright 2011-2018 Symphony Team
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy
9 | of this software and associated documentation files (the "Software"), to deal
10 | in the Software without restriction, including without limitation the rights
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | copies of the Software, and to permit persons to whom the Software is
13 | furnished to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in
16 | all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | THE SOFTWARE.
25 |
26 | ----- end license block -----
--------------------------------------------------------------------------------
/assets/members.roles.css:
--------------------------------------------------------------------------------
1 | /* Role Permissions */
2 |
3 | table.role-permissions {
4 | text-align: center;
5 | width: 100%;
6 | margin-bottom: 2em;
7 | padding: 0;
8 | }
9 |
10 | table.role-permissions th {
11 | text-align: center;
12 | }
13 |
14 | table.role-permissions th {
15 | padding: 3px 6px;
16 | white-space: nowrap;
17 | }
18 |
19 | table.role-permissions th.name {
20 | text-align: left;
21 | }
22 |
23 | table.role-permissions th.new,
24 | table.role-permissions th.edit {
25 | cursor: pointer;
26 | }
27 |
28 | table.role-permissions th span {
29 | padding: 0 0 1px 0;
30 | border-bottom: 1px dotted #666;
31 | }
32 |
33 | table.role-permissions th.new:hover,
34 | table.role-permissions th.edit:hover {
35 | color: #333;
36 | }
37 |
38 | table.role-permissions tr td {
39 | padding: 0 6px;
40 | width: 1px;
41 | }
42 |
43 | table.role-permissions tr td.name {
44 | width: auto;
45 | }
46 |
47 | table.role-permissions td:first-child {
48 | text-align: left;
49 | border-left: 1px solid #E6E6E5;
50 | }
51 |
52 | table.role-permissions td:last-child {
53 | border-right: 1px solid #E6E6E5;
54 | }
55 |
56 | table.role-permissions label {
57 | padding: 6px 25px;
58 | margin: 0;
59 | width: auto;
60 | display: inline-block;
61 | }
62 |
63 | table.role-permissions td.create label {
64 | padding: 6px 50px;
65 | }
66 |
67 | table.role-permissions input {
68 | float: none;
69 | margin: 0;
70 | padding: 0;
71 | width: auto;
72 | }
73 |
74 | table.role-permissions td label span {
75 | color: #666;
76 | margin-left: 0.25em;
77 | display: none;
78 | }
79 |
80 | table.role-permissions td.create span {
81 | display: none;
82 | }
83 |
--------------------------------------------------------------------------------
/assets/members.events.js:
--------------------------------------------------------------------------------
1 | jQuery(document).ready(function() {
2 | var notifier = jQuery('#header').find('.notifier');
3 |
4 | Symphony.Language.add({
5 | 'Event updated at {$time}. Create another? View all Events': false,
6 | 'An error occurred while processing this form.': false
7 | });
8 |
9 | Symphony.Members = {
10 | memberEventSave: function() {
11 | jQuery.ajax({
12 | type: 'post',
13 | url: Symphony.Context.get('symphony') + '/extension/members/events/',
14 | async: false,
15 | data: jQuery('form').serialize(),
16 | beforeSend: function() {
17 | notifier.find('.members').trigger('detach.notify');
18 | },
19 | success: function(data, response) {
20 | if(response === "success") {
21 | notifier.trigger('attach.notify', [
22 | Symphony.Language.get('Event updated at {$time}. Create another? View all Events', {
23 | time: jQuery(data).find('timestamp').text(),
24 | new_url: Symphony.Context.get('symphony') + '/blueprints/events/new/',
25 | url: Symphony.Context.get('symphony') + '/blueprints/events/'
26 | }),
27 | 'success members'
28 | ]);
29 | }
30 | else {
31 | notifier.trigger('attach.notify', [
32 | Symphony.Language.get('An error occurred while processing this form.'),
33 | 'error members'
34 | ]);
35 | }
36 | },
37 | error: function(data, response) {
38 | notifier.trigger('attach.notify', [
39 | Symphony.Language.get('An error occurred while processing this form.'),
40 | 'error members'
41 | ]);
42 | }
43 | });
44 |
45 | return false;
46 | }
47 | };
48 |
49 | // Save our the Event's email preference
50 | jQuery('form').on('submit', Symphony.Members.memberEventSave);
51 | });
52 |
--------------------------------------------------------------------------------
/lib/class.membersection.php:
--------------------------------------------------------------------------------
1 | section_id = $section_id;
12 | $this->data = $data;
13 | }
14 |
15 | /**
16 | * Returns the data of this section as an object
17 | *
18 | * @return object
19 | */
20 | public function getData() {
21 | return (object)$this->data;
22 | }
23 |
24 | /**
25 | * Where `$name` is one of the following values, `role`, `timezone`,
26 | * `email`, `activation`, `authentication` and `identity`, this function
27 | * will return a Field instance. Typically this allows extensions to access
28 | * the Fields that are currently being used in the active Members section.
29 | *
30 | * @param string $type
31 | * @return Field|null
32 | * If `$type` is not given, or no Field was found, null will be returned.
33 | */
34 | public function getField($type = null) {
35 | if(is_null($type)) return null;
36 |
37 | $type = extension_Members::getFieldType($type);
38 |
39 | // Check to see if this name has been stored in our 'static' cache
40 | // If it hasn't, lets go find it (for better or worse)
41 | if(!isset($this->fields[$type])) {
42 | $this->initialiseField($type, $this->section_id);
43 | }
44 |
45 | // No field, return null
46 | if(!isset($this->fields[$type])) return null;
47 |
48 | // If it has, return it.
49 | return $this->fields[$type];
50 | }
51 |
52 | /**
53 | * Where `$name` is one of the following values, `role`, `timezone`,
54 | * `email`, `activation`, `authentication` and `identity`, this function
55 | * will return the Field's `element_name`. `element_name` is a handle
56 | * of the Field's label, used most commonly by events in `$_POST` data.
57 | * If no `$name` is given, an array of all Member field handles will
58 | * be returned.
59 | *
60 | * @param string $type
61 | * @return string
62 | */
63 | public function getFieldHandle($type = null) {
64 | if(is_null($type)) return null;
65 |
66 | $type = extension_Members::getFieldType($type);
67 |
68 | // Check to see if this name has been stored in our 'static' cache
69 | // If it hasn't, lets go find it (for better or worse)
70 | if(!isset($this->handles[$type])) {
71 | $this->initialiseField($type, $this->section_id);
72 | }
73 |
74 | // No field, return null
75 | if(!isset($this->handles[$type])) return null;
76 |
77 | // Return the handle
78 | return $this->handles[$type];
79 | }
80 |
81 | /**
82 | * Given a `$type` and potentially `$section_id`, fetch the Field
83 | * instance and populate the static `$fields` and `$handles` arrays
84 | *
85 | * @param string $type
86 | * @param integer $section_id
87 | */
88 | public function initialiseField($type, $section_id = null) {
89 | $field = FieldManager::fetch(null, $section_id, 'ASC', 'sortorder', $type);
90 |
91 | if(!empty($field)) {
92 | $field = current($field);
93 | $this->fields[$type] = $field;
94 | $this->handles[$type] = $field->get('element_name');
95 | }
96 | }
97 |
98 | }
--------------------------------------------------------------------------------
/content/content.events.php:
--------------------------------------------------------------------------------
1 | setHttpStatus(self::HTTP_STATUS_BAD_REQUEST);
11 | return;
12 | }
13 | // Check that the CONFIG is writable
14 | else if (!is_writable(CONFIG)) {
15 | $this->setHttpStatus(self::HTTP_STATUS_ERROR);
16 | $this->_Result->appendChild(
17 | new XMLElement('message', __('The Symphony configuration file, /manifest/config.php, is not writable. You will not be able to save changes to preferences.'))
18 | );
19 | return;
20 | }
21 |
22 | $settings = $_POST['members'];
23 |
24 | // Generate Recovery Code
25 | if(isset($settings['generate-recovery-code-template'])) {
26 | Symphony::Configuration()->set('generate-recovery-code-template', implode(',', array_filter($settings['generate-recovery-code-template'])), 'members');
27 | }
28 | // If no template was set, then the user selected nothing,
29 | // so remove the template preference
30 | else if($settings['event'] == 'generate-recovery-code') {
31 | Symphony::Configuration()->remove('generate-recovery-code-template', 'members');
32 | }
33 |
34 | // Reset Password
35 | if(isset($settings['reset-password-template'])) {
36 | Symphony::Configuration()->set('reset-password-template', implode(',', array_filter($settings['reset-password-template'])), 'members');
37 | }
38 | else if($settings['event'] == 'reset-password') {
39 | Symphony::Configuration()->remove('reset-password-template', 'members');
40 | }
41 |
42 | if($settings['event'] == 'reset-password') {
43 | Symphony::Configuration()->set('reset-password-auto-login', $settings['auto-login'], 'members');
44 | }
45 |
46 | // Regenerate Activation Code
47 | if(isset($settings['regenerate-activation-code-template'])) {
48 | Symphony::Configuration()->set('regenerate-activation-code-template', implode(',', array_filter($settings['regenerate-activation-code-template'])), 'members');
49 | }
50 | else if($settings['event'] == 'regenerate-activation-code') {
51 | Symphony::Configuration()->remove('regenerate-activation-code-template', 'members');
52 | }
53 |
54 | // Activate Account
55 | if(isset($settings['activate-account-template'])) {
56 | Symphony::Configuration()->set('activate-account-template', implode(',', array_filter($settings['activate-account-template'])), 'members');
57 | }
58 | else if($settings['event'] == 'activate-account') {
59 | Symphony::Configuration()->remove('activate-account-template', 'members');
60 | }
61 |
62 | if($settings['event'] == 'activate-account') {
63 | Symphony::Configuration()->set('activate-account-auto-login', $settings['auto-login'], 'members');
64 | }
65 |
66 | // Return successful
67 | if(Symphony::Configuration()->write()) {
68 | $this->setHttpStatus(self::HTTP_STATUS_OK);
69 | $this->_Result->appendChild(
70 | new XMLElement('message', __('Preferences saved.'))
71 | );
72 | $this->_Result->appendChild(
73 | new XMLElement('timestamp', 'generate() . ']]>')
74 | );
75 | }
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/lib/class.identity.php:
--------------------------------------------------------------------------------
1 | _required = true;
28 | $this->set('required', 'yes');
29 | }
30 |
31 | public function mustBeUnique() {
32 | return true;
33 | }
34 |
35 | public function canFilter(){
36 | return true;
37 | }
38 |
39 | public function allowDatasourceParamOutput(){
40 | return true;
41 | }
42 |
43 | public function canPrePopulate(){
44 | return true;
45 | }
46 |
47 | /*-------------------------------------------------------------------------
48 | Utilities:
49 | -------------------------------------------------------------------------*/
50 |
51 | /**
52 | * Given a Member ID, return Member
53 | *
54 | * @param integer $member_id
55 | * @return Entry
56 | */
57 | public function fetchMemberFromID($member_id){
58 | if(!(Identity::$driver instanceof Extension)){
59 | Identity::$driver = Symphony::ExtensionManager()->create('members');
60 | }
61 |
62 | return Identity::$driver->getMemberDriver()->initialiseMemberObject($member_id);
63 | }
64 |
65 | abstract public function fetchMemberIDBy($needle, $member_id = null);
66 |
67 | /*-------------------------------------------------------------------------
68 | Publish:
69 | -------------------------------------------------------------------------*/
70 |
71 | public function displayPublishPanel(XMLElement &$wrapper, $data = null, $error = null, $prefix = null, $postfix = null, $entry_id = null) {
72 | $field_id = $this->get('id');
73 | $handle = $this->get('element_name');
74 |
75 | // Identity
76 | $label = Widget::Label($this->get('label'));
77 | if(!($this->get('required') == 'yes')) $label->appendChild(new XMLElement('i', __('Optional')));
78 |
79 | $label->appendChild(Widget::Input(
80 | "fields{$prefix}[{$handle}]{$postfix}", $data['value']
81 | ));
82 |
83 | // Error?
84 | if(!is_null($error)) {
85 | $wrapper->appendChild(Widget::Error($label, $error));
86 | }
87 | else {
88 | $wrapper->appendChild($label);
89 | }
90 | }
91 |
92 | /*-------------------------------------------------------------------------
93 | Input:
94 | -------------------------------------------------------------------------*/
95 |
96 | public function processRawFieldData($data, &$status, &$message=null, $simulate=false, $entry_id=null){
97 | $status = self::__OK__;
98 |
99 | if(empty($data)) return array();
100 |
101 | return array(
102 | 'value' => trim($data),
103 | 'handle' => Lang::createHandle(trim($data))
104 | );
105 | }
106 |
107 | /*-------------------------------------------------------------------------
108 | Output:
109 | -------------------------------------------------------------------------*/
110 |
111 | public function prepareTableValue($data, XMLElement $link = null, $entry_id = null){
112 | if(empty($data)) return __('None');
113 |
114 | return parent::prepareTableValue(array('value' => General::sanitize($data['value'])), $link);
115 | }
116 |
117 | public function getParameterPoolValue(array $data, $entry_id = null) {
118 | return $data['value'];
119 | }
120 |
121 | /*-------------------------------------------------------------------------
122 | Filtering:
123 | -------------------------------------------------------------------------*/
124 |
125 | public function buildDSRetrievalSQL($data, &$joins, &$where, $andOperation=false){
126 |
127 | $field_id = $this->get('id');
128 |
129 | // Filter is an regexp.
130 | if(self::isFilterRegex($data[0])) {
131 | $this->buildRegexSQL($data[0], array('value', 'handle', 'entry_id'), $joins, $where);
132 | }
133 |
134 | // Filter has + in it.
135 | else if($andOperation) {
136 | foreach($data as $key => $bit){
137 | $bit = Symphony::Database()->cleanValue($bit);
138 | $joins .= " LEFT JOIN `tbl_entries_data_$field_id` AS `t$field_id$key` ON (`e`.`id` = `t$field_id$key`.entry_id) ";
139 | $where .= " AND (
140 | `t$field_id$key`.value = '$bit'
141 | OR `t$field_id$key`.handle = '$bit'
142 | OR `t$field_id$key`.entry_id = '$bit'
143 | ) ";
144 | }
145 | }
146 |
147 | // Normal
148 | else {
149 | if(!is_array($data)) {
150 | $data = array($data);
151 | }
152 | $data = array_map(array(Symphony::Database(), 'cleanValue'), $data);
153 | $joins .= " LEFT JOIN `tbl_entries_data_$field_id` AS `t$field_id` ON (`e`.`id` = `t$field_id`.entry_id) ";
154 | $where .= " AND (
155 | `t$field_id`.value IN ('".implode("', '", $data)."')
156 | OR `t$field_id`.handle IN ('".implode("', '", $data)."')
157 | OR `t$field_id`.entry_id IN ('".implode("', '", $data)."')
158 | ) ";
159 | }
160 |
161 | return true;
162 | }
163 |
164 | /*-------------------------------------------------------------------------
165 | Events:
166 | -------------------------------------------------------------------------*/
167 |
168 | public function getExampleFormMarkup(){
169 |
170 | $label = Widget::Label($this->get('label'));
171 | $label->appendChild(Widget::Input('fields['.$this->get('element_name').']'));
172 |
173 | return $label;
174 | }
175 |
176 | }
177 |
--------------------------------------------------------------------------------
/lib/class.membersevent.php:
--------------------------------------------------------------------------------
1 | Error
You cannot directly access this file
');
4 |
5 | Abstract Class MembersEvent extends Event {
6 |
7 | // For the delegates to populate
8 | public $filter_results = array();
9 | public $filter_errors = array();
10 |
11 | // Instance of the Members extension
12 | public $driver = null;
13 |
14 | // Don't allow a user to set permissions for any Members event
15 | // in the Roles interface.
16 | public function ignoreRolePermissions() {
17 | return true;
18 | }
19 |
20 | // The default filters for an event are the XSS Filter
21 | public $eParamFILTERS = array(
22 | 'xss-fail'
23 | );
24 |
25 | /*-------------------------------------------------------------------------
26 | Utilities:
27 | -------------------------------------------------------------------------*/
28 |
29 | /**
30 | * This function is directly copied from Symphony's default event
31 | * include. It takes a result from an Event filter and generates XML
32 | * to output with the custom events
33 | */
34 | public static function buildFilterElement($name, $status, $message=NULL, array $attr=NULL){
35 | $ret = new XMLElement('filter', (!$message || is_object($message) ? NULL : $message), array(
36 | 'name' => $name,
37 | 'status' => $status
38 | ));
39 |
40 | if(is_object($message)) $ret->appendChild($message);
41 |
42 | if(is_array($attr)) $ret->setAttributeArray($attr);
43 |
44 | return $ret;
45 | }
46 |
47 | protected function addEmailTemplates($template) {
48 | // Read the template from the Configuration if it exists
49 | // This is required for the Email Template Filter/Email Template Manager
50 | if(!is_null(extension_Members::getSetting($template))) {
51 | $this->eParamFILTERS = array_merge(
52 | $this->eParamFILTERS,
53 | explode(',',extension_Members::getSetting($template))
54 | );
55 | }
56 | }
57 |
58 | protected function setMembersSection(XMLElement $result, $member_section_id = null) {
59 | // Set the section ID
60 | if(isset($member_section_id) && $this->driver->setMembersSection($member_section_id) === false) {
61 | $result->setAttribute('result', 'error');
62 | $result->appendChild(
63 | new XMLElement('message', __('Invalid Members section ID given.'), array(
64 | 'message-id' => MemberEventMessages::MEMBER_ERRORS
65 | ))
66 | );
67 | }
68 |
69 | return $result;
70 | }
71 |
72 | /*-------------------------------------------------------------------------
73 | Delegates:
74 | -------------------------------------------------------------------------*/
75 |
76 | protected function notifyEventPreSaveFilter(XMLElement &$result, array $fields, XMLElement $post_values) {
77 | /**
78 | * @delegate EventPreSaveFilter
79 | * @param string $context
80 | * '/frontend/'
81 | * @param array $fields
82 | * @param string $event
83 | * @param array $messages
84 | * @param XMLElement $post_values
85 | */
86 | Symphony::ExtensionManager()->notifyMembers(
87 | 'EventPreSaveFilter',
88 | '/frontend/',
89 | array(
90 | 'fields' => &$fields,
91 | 'event' => &$this,
92 | 'messages' => &$this->filter_results,
93 | 'post_values' => &$post_values
94 | )
95 | );
96 |
97 | // Logic taken from `event.section.php` to fail should any `$this->filter_results`
98 | // be returned. This delegate can cause the event to exit early.
99 | if (is_array($this->filter_results) && !empty($this->filter_results)) {
100 | $can_proceed = true;
101 |
102 | foreach ($this->filter_results as $fr) {
103 | list($name, $status, $message, $attributes) = array_pad($fr, 4, null);
104 |
105 | $result->appendChild(
106 | MembersEvent::buildFilterElement($name, ($status ? 'passed' : 'failed'), $message, $attributes)
107 | );
108 |
109 | if($status === false) $can_proceed = false;
110 | }
111 |
112 | if ($can_proceed !== true) {
113 | $result->setAttribute('result', 'error');
114 | $result->appendChild($post_values);
115 | $result->appendChild(new XMLElement('message', __('Member event encountered errors when processing.'), array(
116 | 'message-id' => MemberEventMessages::FILTER_FAILED
117 | )));
118 | return $result;
119 | }
120 | }
121 | }
122 |
123 | protected function notifyEventFinalSaveFilter(XMLElement &$result, array $fields, XMLElement $post_values, Entry $entry) {
124 | // We now need to simulate the EventFinalSaveFilter which the EmailTemplateFilter
125 | // and EmailTemplateManager use to send emails.
126 | /**
127 | * @delegate EventFinalSaveFilter
128 | * @param string $context
129 | * '/frontend/'
130 | * @param array $fields
131 | * @param string $event
132 | * @param array $messages
133 | * @param array $errors
134 | * @param Entry $entry
135 | */
136 | Symphony::ExtensionManager()->notifyMembers(
137 | 'EventFinalSaveFilter', '/frontend/', array(
138 | 'fields' => $fields,
139 | 'event' => $this,
140 | 'messages' => $this->filter_results,
141 | 'errors' => &$this->filter_errors,
142 | 'entry' => $entry
143 | )
144 | );
145 |
146 | // Take the logic from `event.section.php` to append `$this->filter_errors`
147 | if(is_array($this->filter_errors) && !empty($this->filter_errors)){
148 | foreach($this->filter_errors as $fr){
149 | list($name, $status, $message, $attributes) = array_pad($fr, 4, null);
150 |
151 | $result->appendChild(
152 | MembersEvent::buildFilterElement($name, ($status ? 'passed' : 'failed'), $message, $attributes)
153 | );
154 | }
155 | }
156 | }
157 |
158 | protected function notifyMembersPasswordResetFailure($username) {
159 | /**
160 | * A failed password reset attempt
161 | *
162 | * @delegate MembersPasswordResetFailure
163 | * @param string $context
164 | * '/frontend/'
165 | * @param string $username
166 | * Should be the value of the identity field for which the password reset has been attempted
167 | */
168 | Symphony::ExtensionManager()->notifyMembers(
169 | 'MembersPasswordResetFailure',
170 | '/frontend/',
171 | array(
172 | 'username' => Symphony::Database()->cleanValue($username)
173 | )
174 | );
175 | }
176 | }
177 |
178 | /**
179 | * Basic lookup class for Event messages, allows for frontend developers
180 | * to localise and change event messages without relying on string
181 | * comparision.
182 | *
183 | * @since Symphony 2.4
184 | */
185 | class MemberEventMessages extends EventMessages
186 | {
187 | const MEMBER_ERRORS = 104;
188 | const MEMBER_INVALID = 105;
189 | const UNAUTHORIZED = 106;
190 |
191 | const SECTION_INVALID = 201;
192 |
193 | const ACTIVATION_PRE_COMPLETED = 303;
194 | const ACTIVATION_CODE_INVALID = 304;
195 | const RECOVERY_CODE_INVALID = 305;
196 | const AUTHENTICATION_INVALID = 306;
197 |
198 | const ALREADY_LOGGED_IN = 501;
199 | }
200 |
--------------------------------------------------------------------------------
/fields/field.membertimezone.php:
--------------------------------------------------------------------------------
1 | _name = __('Member: Timezone');
14 | $this->_showassociation = false;
15 | }
16 |
17 | public function mustBeUnique() {
18 | return true;
19 | }
20 |
21 | /*-------------------------------------------------------------------------
22 | Setup:
23 | -------------------------------------------------------------------------*/
24 |
25 | public static function createSettingsTable() {
26 | return Symphony::Database()->query("
27 | CREATE TABLE IF NOT EXISTS `tbl_fields_membertimezone` (
28 | `id` int(11) unsigned NOT NULL auto_increment,
29 | `field_id` int(11) unsigned NOT NULL,
30 | `available_zones` VARCHAR(255) DEFAULT NULL,
31 | PRIMARY KEY (`id`),
32 | UNIQUE KEY `field_id` (`field_id`)
33 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
34 | ");
35 | }
36 |
37 | /*-------------------------------------------------------------------------
38 | Utilities:
39 | -------------------------------------------------------------------------*/
40 |
41 | /**
42 | * This function will return the offset value for a particular `$member_id`
43 | * The offset is the number of hours +/- from GMT
44 | *
45 | * @param integer $member_id
46 | * @return string
47 | * ie. Africa/Asmara
48 | */
49 | public function getMemberTimezone($member_id) {
50 | return Symphony::Database()->fetchVar('value', 0, sprintf("
51 | SELECT `value`
52 | FROM `tbl_entries_data_%d`
53 | WHERE `entry_id` = '%s'
54 | LIMIT 1
55 | ", $this->get('id'), $member_id
56 | ));
57 | }
58 |
59 | /**
60 | * Creates a list of Timezones for the With Selected dropdown in the backend.
61 | * This list has the limitation that the timezones cannot be grouped as the
62 | * With Selected menu already uses `