Changeset 3444373
- Timestamp:
- 01/21/2026 07:32:42 PM (2 months ago)
- Location:
- wired-impact-volunteer-management/trunk
- Files:
-
- 6 edited
-
README.txt (modified) (2 diffs)
-
cypress/support/commands.js (modified) (6 diffs)
-
includes/class-volunteer.php (modified) (14 diffs)
-
includes/class-wi-volunteer-management.php (modified) (1 diff)
-
languages/wired-impact-volunteer-management.pot (modified) (2 diffs)
-
wivm.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
wired-impact-volunteer-management/trunk/README.txt
r3429767 r3444373 5 5 Tested up to: 6.9 6 6 Requires PHP: 5.2.4 7 Stable tag: 2.8 7 Stable tag: 2.8.1 8 8 License: GPLv2 or later 9 9 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 127 127 == Changelog == 128 128 129 = 2.8.1 = 130 * Fixed vulnerability where unauthenticated volunteer form submissions could overwrite first name, last name and phone number data for non-volunteer roles. 131 129 132 = 2.8 = 130 133 * Updated Cypress tests to work with WordPress 6.9. -
wired-impact-volunteer-management/trunk/cypress/support/commands.js
r3242201 r3444373 46 46 cy.exec(Cypress.env('wpCLIPrefix') + ' post delete $(' + Cypress.env('wpCLIPrefix') + ' post list --post_type=volunteer_opp --format=ids) --force'); 47 47 cy.exec(Cypress.env('wpCLIPrefix') + ' post create --post_type=volunteer_opp --post_title="Clean up Trash" --post_status="publish"').then((result) => { 48 48 49 49 let stdOutParts = result.stdout.slice(0, -1); 50 50 stdOutParts = stdOutParts.split(' '); … … 79 79 */ 80 80 Cypress.Commands.add('trashAllGravityFormsForms', () => { 81 81 82 82 cy.exec(Cypress.env('wpCLIPrefix') + ' gf form delete $(' + Cypress.env('wpCLIPrefix') + ' gf form list --format=ids)'); 83 83 }); … … 94 94 95 95 const formJSON = {"fields":[{"type":"name","id":1,"formId":6,"label":"Name","adminLabel":"","isRequired":false,"size":"large","errorMessage":"","visibility":"visible","nameFormat":"advanced","inputs":[{"id":"1.2","label":"Prefix","name":"","autocompleteAttribute":"honorific-prefix","choices":[{"text":"Dr.","value":"Dr."},{"text":"Miss","value":"Miss"},{"text":"Mr.","value":"Mr."},{"text":"Mrs.","value":"Mrs."},{"text":"Ms.","value":"Ms."},{"text":"Mx.","value":"Mx."},{"text":"Prof.","value":"Prof."},{"text":"Rev.","value":"Rev."}],"isHidden":true,"inputType":"radio"},{"id":"1.3","label":"First","name":"","autocompleteAttribute":"given-name"},{"id":"1.4","label":"Middle","name":"","autocompleteAttribute":"additional-name","isHidden":true},{"id":"1.6","label":"Last","name":"","autocompleteAttribute":"family-name"},{"id":"1.8","label":"Suffix","name":"","autocompleteAttribute":"honorific-suffix","isHidden":true}],"description":"","allowsPrepopulate":false,"inputMask":false,"inputMaskValue":"","inputMaskIsCustom":"","maxLength":"","inputType":"","labelPlacement":"","descriptionPlacement":"","subLabelPlacement":"","placeholder":"","cssClass":"","inputName":"","noDuplicates":false,"defaultValue":"","enableAutocomplete":false,"autocompleteAttribute":"","choices":"","conditionalLogic":"","productField":"","layoutGridColumnSpan":"","enableEnhancedUI":0,"layoutGroupId":"b7373b44","fields":"","displayOnly":""},{"type":"phone","id":3,"formId":6,"label":"Phone","adminLabel":"","isRequired":false,"size":"large","errorMessage":"","visibility":"visible","inputs":null,"phoneFormat":"international","autocompleteAttribute":"tel","description":"","allowsPrepopulate":false,"inputMask":false,"inputMaskValue":"","inputMaskIsCustom":"","maxLength":"","inputType":"","labelPlacement":"","descriptionPlacement":"","subLabelPlacement":"","placeholder":"","cssClass":"","inputName":"","noDuplicates":false,"defaultValue":"","enableAutocomplete":false,"choices":"","conditionalLogic":"","productField":"","layoutGridColumnSpan":12,"enableEnhancedUI":0,"layoutGroupId":"33f7781e","fields":"","displayOnly":""},{"type":"email","id":4,"formId":6,"label":"Email","adminLabel":"","isRequired":false,"size":"large","errorMessage":"","visibility":"visible","inputs":null,"autocompleteAttribute":"email","description":"","allowsPrepopulate":false,"inputMask":false,"inputMaskValue":"","inputMaskIsCustom":"","maxLength":"","inputType":"","labelPlacement":"","descriptionPlacement":"","subLabelPlacement":"","placeholder":"","cssClass":"","inputName":"","noDuplicates":false,"defaultValue":"","enableAutocomplete":false,"choices":"","conditionalLogic":"","productField":"","layoutGridColumnSpan":12,"emailConfirmEnabled":"","enableEnhancedUI":0,"layoutGroupId":"9467c1ea","fields":"","displayOnly":""},{"type":"select","id":6,"formId":6,"label":"T-Shirt Size","adminLabel":"","isRequired":false,"size":"large","errorMessage":"","visibility":"visible","validateState":true,"inputs":null,"choices":[{"text":"Extra Small","value":"Extra Small","isSelected":false,"price":""},{"text":"Small","value":"Small","isSelected":false,"price":""},{"text":"Medium","value":"Medium","isSelected":false,"price":""},{"text":"Large","value":"Large","isSelected":false,"price":""},{"text":"Extra Large","value":"Extra Large","isSelected":false,"price":""}],"description":"","allowsPrepopulate":false,"inputMask":false,"inputMaskValue":"","inputMaskIsCustom":false,"maxLength":"","inputType":"","labelPlacement":"","descriptionPlacement":"","subLabelPlacement":"","placeholder":"","cssClass":"","inputName":"","noDuplicates":false,"defaultValue":"","enableAutocomplete":false,"autocompleteAttribute":"","conditionalLogic":"","productField":"","layoutGridColumnSpan":12,"enablePrice":"","enableEnhancedUI":0,"layoutGroupId":"73e95759","multipleFiles":false,"maxFiles":"","calculationFormula":"","calculationRounding":"","enableCalculation":"","disableQuantity":false,"displayAllCategories":false,"useRichTextEditor":false,"errors":[],"fields":""},{"type":"textarea","id":5,"formId":6,"label":"Why do you want to volunteer with us?","adminLabel":"","isRequired":false,"size":"medium","errorMessage":"","visibility":"visible","inputs":null,"description":"","allowsPrepopulate":false,"inputMask":false,"inputMaskValue":"","inputMaskIsCustom":false,"maxLength":"","inputType":"","labelPlacement":"","descriptionPlacement":"","subLabelPlacement":"","placeholder":"","cssClass":"","inputName":"","noDuplicates":false,"defaultValue":"","enableAutocomplete":false,"autocompleteAttribute":"","choices":"","conditionalLogic":"","productField":"","layoutGridColumnSpan":12,"form_id":"","useRichTextEditor":false,"enableEnhancedUI":0,"layoutGroupId":"a96865a7","multipleFiles":false,"maxFiles":"","calculationFormula":"","calculationRounding":"","enableCalculation":"","disableQuantity":false,"displayAllCategories":false,"errors":[],"fields":""}],"button":{"type":"text","text":"","imageUrl":"","width":"auto","location":"bottom","layoutGridColumnSpan":12},"title":"Volunteer Signup","description":"","version":"2.7.17.1","id":6,"markupVersion":2,"nextFieldId":7,"useCurrentUserAsAuthor":true,"postContentTemplateEnabled":false,"postTitleTemplateEnabled":false,"postTitleTemplate":"","postContentTemplate":"","lastPageButton":null,"pagination":null,"firstPageCssClass":null,"confirmations":[{"id":"65734ead4e58d","name":"Default Confirmation","isDefault":true,"type":"message","message":"Thanks for signing up to volunteer!","url":"","pageId":"","queryString":"","event":"","disableAutoformat":false,"page":"","conditionalLogic":[]}],"notifications":[{"id":"65734ead4e18f","isActive":true,"to":"{admin_email}","name":"Admin Notification","event":"form_submission","toType":"email","subject":"New submission from {form_title}","message":"{all_fields}"}]}; 96 96 97 97 cy.exec(Cypress.env('wpCLIPrefix') + ' gf form create "Volunteer Signup" "" --form-json=\'' + JSON.stringify(formJSON) + '\'').then((result) => { 98 98 99 99 const stdOutParts = result.stdout.split(' '); 100 100 const formID = stdOutParts[stdOutParts.length - 1]; … … 114 114 */ 115 115 Cypress.Commands.add('deleteAllMailCatcherEmails', () => { 116 116 117 117 cy.request('DELETE', 'http://127.0.0.1:1080/messages').then((response) => { 118 118 expect(response.status).to.eq(204); … … 121 121 122 122 /** 123 * Get the WordPress iframe content editor so we can test inside of it. 123 * Get the WordPress block editor content area. 124 * 125 * In WordPress 6.3+, the post editor may or may not use an iframe depending 126 * on theme type and other conditions. This command handles both cases. 124 127 * 125 128 * You must also set chromeWebSecurity to false in cypress.config.js 126 129 * to work with iframes. 127 * 130 * 131 * @see https://make.wordpress.org/core/2023/07/18/miscellaneous-editor-changes-in-wordpress-6-3/ 128 132 * @see https://github.com/cypress-io/cypress-example-recipes/tree/master/examples/blogs__iframes 129 * @see https://on.cypress.io/wrap130 133 */ 131 134 Cypress.Commands.add('getBlockEditorIFrameBody', () => { … … 133 136 cy.log('getBlockEditorIFrameBody'); 134 137 135 return cy 136 .get('iframe[name="editor-canvas"]', { log: false }) 137 .its('0.contentDocument.body', { log: false }).should('not.be.empty') 138 .then((body) => cy.wrap(body, { log: false })) 138 return cy.get('body').then(($body) => { 139 const $iframe = $body.find('iframe[name="editor-canvas"]'); 140 141 if ($iframe.length) { 142 // Editor is iframed 143 return cy 144 .get('iframe[name="editor-canvas"]', { log: false }) 145 .its('0.contentDocument.body', { log: false }).should('not.be.empty') 146 .then((body) => cy.wrap(body, { log: false })); 147 } else { 148 // Editor is not iframed 149 return cy.get('.editor-styles-wrapper', { log: false }); 150 } 151 }); 139 152 }); -
wired-impact-volunteer-management/trunk/includes/class-volunteer.php
r3008933 r3444373 70 70 */ 71 71 public function set_meta(){ 72 $user_data = get_userdata( $this->ID );72 $user_data = get_userdata( $this->ID ); 73 73 $this->meta = array( 74 'first_name' => $user_data->first_name,75 'last_name' => $user_data->last_name,76 'email' => $user_data->user_email,77 'phone' => $this->format_phone_number( get_user_option( 'phone', $this->ID ) ),78 'notes' => esc_textarea( get_user_option( 'notes', $this->ID ) ),79 'num_volunteer_opps' => $this->get_num_volunteer_opps(),80 'first_volunteer_opp_time' => $this->get_first_volunteer_opp_time()74 'first_name' => $user_data->first_name, 75 'last_name' => $user_data->last_name, 76 'email' => $user_data->user_email, 77 'phone' => $this->format_phone_number( get_user_option( 'phone', $this->ID ) ), 78 'notes' => esc_textarea( get_user_option( 'notes', $this->ID ) ), 79 'num_volunteer_opps' => $this->get_num_volunteer_opps(), 80 'first_volunteer_opp_time' => $this->get_first_volunteer_opp_time() 81 81 ); 82 82 } … … 86 86 * 87 87 * @todo Remove duplicates of this method that exist in other classes 88 * 89 * @param int $unform mated_number Phone number in only integers88 * 89 * @param int $unformatted_number Phone number in only integers. 90 90 * @return string Phone number formatted to look nice. 91 91 */ 92 public function format_phone_number( $unformatted_number ) {92 public function format_phone_number( $unformatted_number ) { 93 93 $formatted_number = ''; 94 94 … … 107 107 /** 108 108 * Get the number of volunteer opportunities this volunteer has signed up for. 109 * 109 * 110 110 * @return int Number of volunteer opportunities signed up for 111 111 */ … … 130 130 * This is used to display the year the person started volunteering within the admin. It's worth 131 131 * noting that this isn't the time of the opportunity, but rather when they RSVPed. 132 * 132 * 133 133 * @return string The date and time of the first volunteer RSVP. 134 134 */ … … 151 151 * Get an array with all of the volunteer opportunities this person has signed up for. 152 152 * 153 * First we pull only the IDs of the posts that have been signed up for with the most recent one they signed 153 * First we pull only the IDs of the posts that have been signed up for with the most recent one they signed 154 154 * up for first. Then we create a new WI_Volunteer_Management_Opportunity object for each and return it. 155 155 * … … 163 163 164 164 switch( $type ){ 165 // All Volunteer Opportunities165 // All Volunteer Opportunities. 166 166 case 'all': 167 168 167 $query = " 169 168 SELECT post_id … … 174 173 175 174 $query_values = array( $this->ID, 1 ); 176 175 177 176 break; 178 177 179 // One-Time Volunteer Opportunities180 // For this query we joined the postmeta table on itself in order to use two meta values.178 // One-Time Volunteer Opportunities. 179 // For this query we joined the postmeta table on itself in order to use two meta values. 181 180 case 'one-time': 182 183 181 $query = " 184 182 SELECT rsvps.post_id … … 196 194 break; 197 195 198 // Flexible Volunteer Opportunities196 // Flexible Volunteer Opportunities. 199 197 case 'flexible': 200 201 198 $query = " 202 199 SELECT rsvps.post_id … … 214 211 $volunteer_opps = $wpdb->get_results( $wpdb->prepare( $query, $query_values ) ); 215 212 216 // Use post id to grab a bunch info on each opportunity and store in the same variable using &.217 foreach ( $volunteer_opps as &$opp ){213 // Use post id to grab a bunch info on each opportunity and store in the same variable using &. 214 foreach ( $volunteer_opps as &$opp ) { 218 215 $opp = new WI_Volunteer_Management_Opportunity( $opp->post_id ); 219 216 } … … 224 221 /** 225 222 * Remove an RSVP for a user for a specific volunteer opportunity. This is usually done through AJAX. 226 * 227 * @param int $post_id ID of the volunteer opportunity to have its RSVP removed 223 * 224 * @param int $post_id ID of the volunteer opportunity to have its RSVP removed. 228 225 * @return int|bool The number of rows updated or false if error 229 226 */ … … 231 228 global $wpdb; 232 229 233 $status = $wpdb->update( 234 $wpdb->prefix . 'volunteer_rsvps', 230 $status = $wpdb->update( 231 $wpdb->prefix . 'volunteer_rsvps', 235 232 array( //Data to update 236 233 'rsvp' => 0 237 ), 234 ), 238 235 array( //Where 239 236 'user_id' => $this->ID, 240 237 'post_id' => $post_id 241 ), 238 ), 242 239 array( '%d' ), //Data formats 243 240 array( '%d', '%d' ) //Where formats … … 253 250 * volunteer including the contact info, notes on them and which volunteer opportunities they signed up for. 254 251 * 255 * @param int $user_id The volunteer's ID.256 252 * @return string The URL needed to view this volunteer's information. 257 253 */ … … 287 283 $user_id = wp_insert_user( $userdata ); 288 284 289 } else { // If the user already exists, update the user based on their email address. 290 291 $userdata['ID'] = $existing_user; 292 293 $user_id = wp_update_user( $userdata ); 285 // Update phone for new users. 286 update_user_option( $user_id, 'phone', preg_replace( '/[^0-9]/', '', $form_fields['wivm_phone'] ) ); 287 288 } else { 289 290 // User already exists: only update their data if they have the volunteer role. 291 // This prevents unauthenticated users from overwriting data for admins or other roles. 292 $user_id = $existing_user; 293 $should_update = $this->should_update_existing_user( $existing_user ); 294 295 if ( $should_update ) { 296 297 $userdata['ID'] = $existing_user; 298 wp_update_user( $userdata ); 299 update_user_option( $user_id, 'phone', preg_replace( '/[^0-9]/', '', $form_fields['wivm_phone'] ) ); 300 } 294 301 295 302 // On multisite we need to add the user to this site if they don't have access. 296 if ( is_multisite() && ! is_user_member_of_blog( $ userdata['ID']) ) {297 298 add_user_to_blog( get_current_blog_id(), $ userdata['ID'], 'volunteer' );299 update_user_option( $ userdata['ID'], 'notes', '' );303 if ( is_multisite() && ! is_user_member_of_blog( $existing_user ) ) { 304 305 add_user_to_blog( get_current_blog_id(), $existing_user, 'volunteer' ); 306 update_user_option( $existing_user, 'notes', '' ); 300 307 } 301 308 } 302 309 303 // Update custom user meta for new and existing volunteers.304 update_user_option( $user_id, 'phone', preg_replace( '/[^0-9]/', '', $form_fields['wivm_phone'] ) );305 306 310 $this->ID = $user_id; 307 311 … … 310 314 311 315 /** 316 * Check if an existing user's data should be updated after volunteer form submission. 317 * 318 * Only users with the "volunteer" role should have their data updated. 319 * This protects admin accounts and other user types from having their 320 * data overwritten by unauthenticated form submissions. 321 * 322 * @param int $user_id The ID of the existing user. 323 * @return bool True if the user's data should be updated, false otherwise. 324 */ 325 private function should_update_existing_user( $user_id ) { 326 327 $user = get_userdata( $user_id ); 328 329 if ( ! $user ) { 330 331 return false; 332 } 333 334 // Only allow updates for users with the volunteer role. 335 $should_update = in_array( 'volunteer', (array) $user->roles, true ); 336 337 /** 338 * Filter whether an existing user's data should be updated after volunteer form submission. 339 * 340 * @param bool $should_update Whether the user's data should be updated. 341 * @param int $user_id The ID of the existing user. 342 * @param WP_User $user The user object. 343 */ 344 return apply_filters( 'wivm_should_update_existing_volunteer', $should_update, $user_id, $user ); 345 } 346 347 /** 312 348 * Delete RSVPs for this user. 313 349 * 314 350 * This is typically done right before the user is deleted from WordPress entirely. 315 * 351 * 316 352 * @return int|bool Int for number of rows updated of false on error 317 353 */ 318 public function delete_rsvps() {354 public function delete_rsvps() { 319 355 global $wpdb; 320 356 321 357 $delete_info = $wpdb->delete( 322 $wpdb->prefix . "volunteer_rsvps",323 array( 'user_id' => $this->ID ),324 array( '%d' )358 $wpdb->prefix . 'volunteer_rsvps', 359 array( 'user_id' => $this->ID ), 360 array( '%d' ) 325 361 ); 326 362 327 363 return $delete_info; 328 364 } 329 330 365 } //class WI_Volunteer_Management_Volunteer -
wired-impact-volunteer-management/trunk/includes/class-wi-volunteer-management.php
r3429767 r3444373 69 69 70 70 $this->plugin_name = 'wired-impact-volunteer-management'; 71 $this->version = '2.8 ';71 $this->version = '2.8.1'; 72 72 73 73 $this->load_dependencies(); -
wired-impact-volunteer-management/trunk/languages/wired-impact-volunteer-management.pot
r3429767 r3444373 1 # Copyright (C) 202 5Wired Impact1 # Copyright (C) 2026 Wired Impact 2 2 # This file is distributed under the GPL-2.0+. 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Wired Impact Volunteer Management 2.8 \n"5 "Project-Id-Version: Wired Impact Volunteer Management 2.8.1\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wired-impact-volunteer-management\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 202 5-12-30T18:09:42+00:00\n"12 "POT-Creation-Date: 2026-01-21T17:56:37+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.11.0\n" -
wired-impact-volunteer-management/trunk/wivm.php
r3429767 r3444373 17 17 * Plugin URI: https://wiredimpact.com/wordpress-plugins-for-nonprofits/volunteer-management/ 18 18 * Description: A simple, free way to keep track of your nonprofit’s volunteers and opportunities. 19 * Version: 2.8 19 * Version: 2.8.1 20 20 * Requires at least: 6.3 21 21 * Requires PHP: 5.2.4
Note: See TracChangeset
for help on using the changeset viewer.