Changeset 2145245
- Timestamp:
- 08/26/2019 12:56:51 AM (6 years ago)
- Location:
- wp-issues-crm/trunk
- Files:
-
- 1 added
- 30 edited
-
css/dashboard.css (modified) (2 diffs)
-
css/email.css (modified) (4 diffs)
-
css/main.css (modified) (1 diff)
-
js/constituent.js (modified) (1 diff)
-
js/dashboard.js (modified) (2 diffs)
-
js/email-inbox.js (modified) (6 diffs)
-
js/email-message.js (modified) (15 diffs)
-
js/email-process.js (modified) (3 diffs)
-
js/email-send.js (modified) (1 diff)
-
js/issue.js (modified) (1 diff)
-
js/manage-storage.js (modified) (1 diff)
-
php/admin/class-wic-admin-access.php (added)
-
php/admin/class-wic-admin-navigation.php (modified) (21 diffs)
-
php/admin/class-wic-admin-settings.php (modified) (7 diffs)
-
php/admin/class-wic-admin-setup.php (modified) (1 diff)
-
php/db/class-wic-db-email-message-object.php (modified) (1 diff)
-
php/entity/class-wic-entity-activity.php (modified) (1 diff)
-
php/entity/class-wic-entity-dashboard.php (modified) (4 diffs)
-
php/entity/class-wic-entity-email-account.php (modified) (2 diffs)
-
php/entity/class-wic-entity-email-block.php (modified) (1 diff)
-
php/entity/class-wic-entity-email-inbox-parse.php (modified) (2 diffs)
-
php/entity/class-wic-entity-email-inbox.php (modified) (21 diffs)
-
php/entity/class-wic-entity-email-message.php (modified) (5 diffs)
-
php/entity/class-wic-entity-email-process.php (modified) (2 diffs)
-
php/entity/class-wic-entity-email-send.php (modified) (1 diff)
-
php/form/class-wic-form-email-inbox.php (modified) (4 diffs)
-
php/list/class-wic-list-activity.php (modified) (1 diff)
-
php/list/class-wic-list-constituent.php (modified) (1 diff)
-
php/list/class-wic-list-parent.php (modified) (1 diff)
-
sql/wic_structures.sql (modified) (2 diffs)
-
wp-issues-crm.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
wp-issues-crm/trunk/css/dashboard.css
r2036027 r2145245 60 60 } 61 61 62 62 63 /* used in email too */ 63 64 button.wic-dashboard-title { … … 190 191 font-weight: 250; 191 192 color: #999; 193 } 194 #wic-inner-dashboard-dashboard_overview { 195 padding: 0 10px; 196 } 197 198 table#wic-work-flow-status { 199 width: 100%; 200 color: #777; 201 border-top: 1px solid #ddd; 202 border-bottom: 1px solid #ddd; 203 padding: 10px; 204 line-height: 2.0em; 205 } 206 207 table#wic-work-flow-status tr:nth-child(odd){ 208 background: #f0f0f0; 209 } 210 table#wic-work-flow-status tr:nth-child(even){ 211 background: #fefefe; 212 } 213 214 #wic-work-flow-status td.wic-staff-overdue-assignment { 215 font-weight: 400; 216 color: #b66; 217 } 218 219 #wic-work-flow-status td:nth-child(1), 220 #wic-work-flow-status th:nth-child(1) { 221 width: 23%; 222 } 223 224 #wic-work-flow-status th { 225 font-size: 1.0em; 226 } 227 #wic-work-flow-status td { 228 font-size: 1.2em; 229 border-top: 1px solid #ddd; 230 } 231 232 .wic-email-dashboard-date { 233 font-style: italic; 234 font-weight: 300; 235 } 236 237 #wic-work-flow-status td, 238 #wic-work-flow-status th { 239 width: 11%; 240 padding: 3px; 241 text-align: center; 242 } 243 244 #wic-work-flow-status tr th:nth-child(1), 245 #wic-work-flow-status tr td:nth-child(1) { 246 text-align: left; 247 padding-left: 20px; 192 248 } 193 249 -
wp-issues-crm/trunk/css/email.css
r2036326 r2145245 257 257 } 258 258 259 #wic-email-inbox-header #wic-filter-assigned-button, 259 260 #wic-email-inbox-header .wic-form-button.page-link { 260 261 margin: 0 10px 0 0; … … 266 267 vertical-align: middle; 267 268 white-space:nowrap; 268 width: calc(98.88888% - 6.22222% - 480px);269 width: calc(98.88888% - 6.22222% - 510px); 269 270 } 270 271 … … 333 334 font-size: 1.2em; 334 335 display: inline-block; 335 width: calc(90% - 2 56px);336 width: calc(90% - 296px); 336 337 overflow-x: hidden; 337 338 vertical-align: middle; … … 679 680 font-weight: 700; 680 681 } 682 683 .subject-line-item.subject.inbox-assigned-staff { 684 font-weight: 700; 685 color: green; 686 } 687 688 689 681 690 682 691 .subject-line-item.count { -
wp-issues-crm/trunk/css/main.css
r2069459 r2145245 130 130 } 131 131 132 #wic-unassigned-message { 133 margin: 20px 0.88888%; 134 } 135 136 #wic-unassigned-message h3 { 137 color: red; 138 } 132 139 /* body of form -- field group areas */ 133 140 #wic-form-body { -
wp-issues-crm/trunk/js/constituent.js
r1958251 r2145245 117 117 wpIssuesCRM.loadActivityArea( true ); 118 118 $("#wic-constituent-delete-button").tooltip( {show: false, hide: false }); 119 120 if ( !wpIssuesCRMSettings.canViewOthersAssigned ) { 121 $( "#wic-inner-field-group-case input").prop("disabled", true) 122 } 123 119 124 } 120 125 } -
wp-issues-crm/trunk/js/dashboard.js
r2036027 r2145245 8 8 9 9 $( "#wp-issues-crm" ).on ( "initializeWICForm", function () { 10 if ( $ ( "#dashboard_ activity" )[0] ) {10 if ( $ ( "#dashboard_overview" )[0] ) { 11 11 wpIssuesCRM.loadDashboard(); 12 12 } … … 14 14 15 15 // initialize directly (normally ajax triggered) in case access by get 16 if ( $ ( "#dashboard_ activity" )[0] ) {16 if ( $ ( "#dashboard_overview" )[0] ) { 17 17 wpIssuesCRM.loadDashboard(); 18 18 } -
wp-issues-crm/trunk/js/email-inbox.js
r2033320 r2145245 57 57 selectedPage = 'inbox'; 58 58 selectedTab = ''; 59 selectedStaff = 0; 59 60 // define this inbox view variable as global for access in restoring undo 60 61 wpIssuesCRM.inboxAscending = { … … 165 166 // uncheck master checkbox for inbox 166 167 $( "#inbox-master-checkbox" ).prop("checked", false ); 167 /* on submit, if requests pending, notify user . . . intending to obsolesce this notice -- more annoying than helpful168 if ( wpIssuesCRM.pendingMailRequests && 'inbox' == selectedPage ) {169 wpIssuesCRM.alert (170 '<p>Inbox initiated reload before all action requests had completed processing -- may not be up to date. In case of doubt, refresh in a few seconds.</p>'171 )172 }173 */174 168 // set up loading UI 175 169 $( ".inbox-mode-button, .page-link" ).prop( "disabled", true ); // prevent double submits … … 186 180 page: nextPage[selectedPage], 187 181 filter: $( "#wic-email-inbox #subject" ).val(), 188 tab: selectedTab 182 tab: selectedTab, 183 staff: selectedStaff 189 184 } 190 185 wpIssuesCRM.ajaxPost( 'email_inbox', 'load_' + selectedPage, 0, inboxParms, function( response ) { … … 239 234 } 240 235 } 236 // enable or disable assignment selection button 237 if ( !['CATEGORY_ASSIGNED', 'CATEGORY_READY'].indexOf(inboxParms.tab) > -1 || !wpIssuesCRMSettings.canViewOthersAssigned ){ 238 $( '#wic-filter-assigned-button' ).prop( 'disabled',false ).removeClass('ui-state-disabled'); 239 } else { 240 $( '#wic-filter-assigned-button' ).prop( 'disabled',true ).addClass('ui-state-disabled'); 241 } 242 241 243 // alert if polling has been halted due to connection failures 242 244 if ( response.connection ) { … … 400 402 }) 401 403 404 .on( "click", "#wic-filter-assigned-button", function() { 405 wpIssuesCRM.doFilterAssignmentPopup(); 406 }) 407 408 402 409 .on( "click", ".page-link", function() { 403 410 if ( ! $( this ).hasClass( 'ui-state-disabled' ) ) { … … 471 478 472 479 480 // popup to select assigned person to filter by 481 wpIssuesCRM.doFilterAssignmentPopup = function() { 482 var popupWidth = 480; 483 var popupContent = '<div title="Select Assigned Staff for Message Display"><div id = "staff_filter_assignment_popup">' + 484 $( "#hidden-staff-assignment-control" ).html() + 485 '<p class="staff_assignment_popup-legend">Selection applies only when viewing Assigned or Ready tab.</p>' + 486 '</div></div>'; 487 dialog = $.parseHTML( popupContent ); 488 wpIssuesCRM.filterAssignedPopupObject = $( dialog ); 489 wpIssuesCRM.filterAssignedPopupObject.dialog({ 490 appendTo: "#wp-issues-crm", 491 closeOnEscape: true, 492 close: function ( event, ui ) { 493 wpIssuesCRM.filterAssignedPopupObject.remove(); 494 wpIssuesCRM.filterAssignedPopupObject = false; 495 }, 496 width: popupWidth, 497 height: 480, 498 position: { my: "right top", at: "right top", of: "#wic-filter-assigned-button" }, 499 show: { effect: "fadeIn", duration: 200 }, 500 buttons: [], 501 modal: true, 502 }); 503 selectStaffButton = $( "#staff_filter_assignment_popup #case_assigned" ); 504 wpIssuesCRM.setVal( selectStaffButton[0],selectedStaff, '' ); // empty string is ignored provided staff # exists 505 506 selectStaffButton.on( "change", function( event, ui ) { 507 selectedStaff = selectStaffButton.val(); 508 nextPage[selectedPage] = 0; 509 wpIssuesCRM.loadSelectedPage(); 510 wpIssuesCRM.filterAssignedPopupObject.fadeOut( 200, 'swing', function(){ wpIssuesCRM.filterAssignedPopupObject.remove(); }) 511 }); 512 }; 513 514 473 515 474 516 }( window.wpIssuesCRM = window.wpIssuesCRM || {}, jQuery )); // end namespace enclosure -
wp-issues-crm/trunk/js/email-message.js
r2096077 r2145245 14 14 // assigned staff 15 15 wpIssuesCRM.assignedStaff = false; 16 wpIssuesCRM.preAssignedStaff = false; // assignment is on an existing constituent record17 16 // parse details popup info 18 17 var parseDetails = false; … … 24 23 // cache to display transition line for reply preview image 25 24 wpIssuesCRM.replyTransitionLine = ''; 25 // message is a final draft reply from inbox 26 wpIssuesCRM.replyFinal = 0; 27 26 28 /* 27 29 * … … 48 50 forced_root_block : 'div', 49 51 init_instance_callback: function (editor) { 50 editor.on('change keyup ', function (e) {52 editor.on('change keyup paste', function (e) { 51 53 editor.save(); 52 54 editorChange(); … … 84 86 subjectLineMoveEventCache = event; 85 87 if ( wpIssuesCRM.formDirty ) { 86 wpIssuesCRM.confirm ( 87 subjectLineMove, 88 false, 89 '<p>Change subject without sending composed message?</p>' 90 ) 91 } else { 92 subjectLineMove(); 93 } 88 handleTemplateChange(); 89 } 90 subjectLineMove(); 94 91 }) 95 92 .on( "click", ".wic-form-button.scroll-button", wpIssuesCRM.handleScrollButton ) 96 93 .on ( "click", "#wic-email-close-button", function(){ 97 94 if ( wpIssuesCRM.formDirty ) { 98 wpIssuesCRM.confirm ( 99 wpIssuesCRM.closeMessageDetails, 100 false, 101 '<p>Close without sending composed message?</p>' 102 ) 103 } else { 104 wpIssuesCRM.closeMessageDetails(); 105 } 95 handleTemplateChange(); 96 } 97 wpIssuesCRM.closeMessageDetails(); 106 98 }) 107 99 // info request handlers 108 .on( "click", "#assigned-case-popup-button", wpIssuesCRM.handleAssign edCasePopupClick )100 .on( "click", "#assigned-case-popup-button", wpIssuesCRM.handleAssignmentPopupClick ) 109 101 .on( "click", "#parse-popup-button", wpIssuesCRM.doParseDetailsPopup ) 110 102 .on( "click", "#view-issue-button", wpIssuesCRM.doIssuePeekPopup ) 111 103 // change listeners ( in addition to tinymce form dirty change) 112 104 .on ( "change", "#wic-message-sender-constituent #constituent_id", wpIssuesCRM.saveConstituentToInboxImage ) 113 .on ( "change", "#wic-inbox-work-area #issue", synchIssueFields)114 .on ( "change", "#wic-inbox-work-area #pro_con", setLoadButtonColor)105 .on ( "change", "#wic-inbox-work-area #issue", handleIssueChange ) 106 .on ( "change", "#wic-inbox-work-area #pro_con", handleProConChange ) 115 107 // initialize action request handlers 116 108 // processEmail distinguishes the trigger buttons … … 126 118 setSaveButtonColor() 127 119 // 4.3.0 handle dirty 128 wpIssuesCRM.formDirty = true; 120 if ( ! wpIssuesCRM.formDirty ) { 121 wpIssuesCRM.formDirty = true; 122 // set timer for saving of template 123 setTimeout( handleTemplateChange, 5000 ); 124 } 129 125 }); 130 126 } … … 222 218 // get the message 223 219 wpIssuesCRM.ajaxPost( 'email_message', 'load_message_detail', currentUID(), data, function( response ) { 220 /* 221 * set approval button appearance 222 */ 223 setThumbsUpDown( response.reply_is_final ); 224 224 /* 225 225 * set up the title for the subject line group of message … … 248 248 // save assigned staff -- setting to 0 if unassigned 249 249 wpIssuesCRM.assignedStaff = response.assigned_staff; 250 wpIssuesCRM.preAssignedStaff = response.assigned_staff > 0;251 250 chooseStaffButtonColor(); 251 // save final draft status 252 wpIssuesCRM.replyFinal = response.reply_is_final; 252 253 // update the scroll positions 253 254 $( "#wic-message-scroll-position" ).html( wpIssuesCRM.currentMessageVars.activeMessage + 1 ) … … 315 316 * 316 317 **********/ 317 318 wpIssuesCRM.handleAssignedCasePopupClick = function () { 318 wpIssuesCRM.handleAssignmentPopupClick = function () { 319 319 if ( 1 == wpIssuesCRM.currentMessageVars.countMessages ) { 320 wpIssuesCRM.doAssign edCasePopup();320 wpIssuesCRM.doAssignmentPopup(); 321 321 } else { 322 322 wpIssuesCRM.alert ( 323 "<p> Case assignment only available for single messages.</p><p>Instead, you could assign the whole issue to a staff member " +323 "<p>Assignment only available for single messages.</p><p>Instead, you could assign the whole issue to a staff member " + 324 324 "(update the issue) OR just switch the inbox out of grouped mode (<b><em>1</em></b>).</p>" ); 325 325 } 326 326 } 327 328 327 // informational popup 329 wpIssuesCRM.doAssign edCasePopup = function() {328 wpIssuesCRM.doAssignmentPopup = function() { 330 329 var popupWidth = 480; 331 var popupContent = '<div title="Assign Staff to Message Sender"><div id = "staff_assignment_popup">' +330 var popupContent = '<div title="Assign Staff to Handle Message"><div id = "staff_assignment_popup">' + 332 331 $( "#hidden-staff-assignment-control" ).html() + 333 ( wpIssuesCRM.preAssignedStaff ? 334 '<p class="staff_assignment_popup-legend">Cannot reassign active case staff from inbox -- reassign from constituent update.</p>' : 335 '<p class="staff_assignment_popup-legend">Constituent case will be assigned when you record or reply and, if you reply, assigned staff will be copied.</p>' ) + 332 '<p class="staff_assignment_popup-legend">Email will be assigned for drafting of reply to selected staff.</p>' + 336 333 '</div></div>'; 337 334 dialog = $.parseHTML( popupContent ); … … 356 353 selectStaffButton.on( "change", function( event, ui ) { 357 354 wpIssuesCRM.assignedStaff = selectStaffButton.val(); 355 wpIssuesCRM.saveValueToInboxImage( 'staff', wpIssuesCRM.assignedStaff ); 358 356 wpIssuesCRM.assignedCasePopupObject.fadeOut( 500, 'swing', function(){ wpIssuesCRM.assignedCasePopupObject.remove(); }) 359 357 chooseStaffButtonColor(); 360 358 }); 361 if ( wpIssuesCRM.preAssignedStaff ) {362 selectStaffButton.next().prop( "disabled", true );363 } else {364 selectStaffButton.next().prop( "disabled", false );365 }366 359 }; 367 360 … … 531 524 }); 532 525 wpIssuesCRM.showLoader( 'message_view' ); 533 wpIssuesCRM.ajaxPost( 'email_message', 'load_full_message', selectedPage, ID, function( response ) {526 wpIssuesCRM.ajaxPost( 'email_message', 'load_full_message', ID, selectedPage, function( response ) { 534 527 $( "#message_view" ).html( 535 528 response.recipients_display_line + … … 659 652 * 660 653 */ 654 655 /* 656 * saveValueToInboxImage is used to pass values to 657 * inbox_defined_staff, 658 * inbox_defined_issue, 659 * inbox_defined_pro_con, 660 * inbox_defined_reply_text, 661 * inbox_defined_reply_is_final 662 * 663 * pass field to update as "staff" or "issue", etc. 664 * 665 */ 666 wpIssuesCRM.saveValueToInboxImage = function( field_to_update, field_value ){ 667 // no reason to disable processing buttons 668 669 // set up update values 670 var data = { 671 field_to_update: field_to_update, 672 field_value: field_value, 673 full_folder_string: wpIssuesCRM.lastLoadParms.folder 674 }; 675 // do the update 676 wpIssuesCRM.ajaxPost( 'email_message', 'quick_update_inbox_defined_item', currentUID(), data, function( response ) { 677 }); 678 } 679 680 681 682 661 683 wpIssuesCRM.saveConstituentToInboxImage = function(){ 662 684 // disable processing buttons 663 685 processingButtons = $( "#wic-email-record-button, #wic-email-record-reply-button, #wic-inbox-sweep-button" ); 686 processingButtons.prop("disabled", true ); 687 664 688 // set up update values 665 689 var data = { … … 669 693 // do the update 670 694 wpIssuesCRM.ajaxPost( 'email_message', 'quick_update_constituent_id', currentUID(), data, function( response ) { 671 // save assigned_staff on return 672 wpIssuesCRM.assignedStaff = response.case_assigned; 673 wpIssuesCRM.preAssignedStaff = response.case_assigned > 0; 695 674 696 // if in multi line mode, automatically update selected constituent_name in displayed address (since not changeable directly by user) 675 697 if ( response.constituent_name && 1 < wpIssuesCRM.currentMessageVars.countMessages ) { … … 689 711 color = '#ddd' 690 712 } else if ( wpIssuesCRM.assignedStaff > 0 ) { 691 if ( wpIssuesCRM.preAssignedStaff ) { 692 color = "#333" 693 } else { 694 color = 'green' 695 } 713 color = 'green' 696 714 } else { 697 715 color = '#999'; … … 701 719 702 720 /* 721 * 722 * these two functions allow split of synchIssueFields and setLoadButtonColor from update 723 * 724 */ 725 function handleIssueChange() { 726 synchIssueFields(); 727 wpIssuesCRM.saveValueToInboxImage ( 'issue', $( "#wic-inbox-work-area #issue").val() ); 728 } 729 730 function handleProConChange () { 731 setLoadButtonColor(); 732 wpIssuesCRM.saveValueToInboxImage ( 'pro_con', $( "#wic-inbox-work-area #pro_con").val() ); 733 734 } 735 /* 736 * 737 * isolated so can be called on timer and on leave 738 * 739 */ 740 function handleTemplateChange() { 741 if ( wpIssuesCRM.formDirty ) { 742 // reset dirty indicator to false before saving so that will start new timer on next keystroke after getContent -- last timer must always run 743 // However, note that risk exists if save is delayed on server side, could queue multiple unnecessary requests. Accept this risk rather than trying to abort earlier -- prioritize delivering the save 744 wpIssuesCRM.formDirty = false; 745 //proceed to save 746 if ( tinymce.activeEditor ) { 747 wpIssuesCRM.saveValueToInboxImage ( 'reply_text', tinymce.activeEditor.getContent() ); 748 } 749 } 750 } 751 /* 703 752 * update current issue links 704 753 * 705 754 * fired on change issue 706 * also fired on blur (to get to no issue assigned value), so can fire an extra time on the standard select/blur sequence755 * also fired on scroll message to set up issue link 707 756 * 708 757 * slight risk that fast user on slow system could select and then to the peek popup before this function returns, but … … 729 778 } 730 779 } 731 780 /* 781 * 782 * handle approval button 783 * 784 * 785 */ 786 // set to opposite of status -- note that status comes through as a string value 787 function setThumbsUpDown ( status ) { 788 if ( '1' == status ) { 789 $( "#wic-email-approve-button span").addClass('dashicons-thumbs-down'); 790 $( "#wic-email-approve-button span").removeClass('dashicons-thumbs-up'); 791 jQuery ( "#wic-email-approve-button" ).attr("title","Reject draft"); 792 } else { 793 $( "#wic-email-approve-button span").addClass('dashicons-thumbs-up'); 794 $( "#wic-email-approve-button span").removeClass('dashicons-thumbs-down'); 795 jQuery ( "#wic-email-approve-button" ).attr("title","Submit draft"); 796 } 797 } 798 // 732 799 /* 733 800 * -
wp-issues-crm/trunk/js/email-process.js
r2069459 r2145245 41 41 eventVal = $( event.target ).closest( ".trigger-email-process-button" ).val(); 42 42 } 43 44 // handle approval processing with different ui 45 if ( 'approve' == eventVal ) { 46 toggleApproval(); 47 return; 48 } 49 50 if( !wpIssuesCRMSettings.canSendEmail) { 51 wpIssuesCRM.alert( 'Your user role can only submit or unsubmit emails. Ask your supervisor for to upgrade your role so that you can send and take other actions.') 52 return; 53 } 54 43 55 // handle sweep processing with different ui 44 56 if ( 'sweep' == eventVal ) { … … 46 58 return; 47 59 } 48 // not sweeping, so cache the active line object and set up its uids for use in the next undo link 60 61 // not sweeping or approving, so cache the active line object and set up its uids for use in the next undo link 49 62 lastDeleted = wpIssuesCRM.activeLine.clone(); 50 63 lastDeletedUids = wpIssuesCRM.activeLine.find( ".UIDs").text(); … … 425 438 } 426 439 440 // non-process function to toggle approval status 441 toggleApproval = function() { 442 // alert regarding multi-message subjects 443 if ( wpIssuesCRM.currentMessageVars.countMessages > 1 ) { 444 wpIssuesCRM.alert ( 445 '<p>Cannot take approval or disapproval action when handling more than one message in a subject line.</p>' + 446 '<p>Switch the inbox out of grouped mode to manage approval status (<b><em>1</em></b>).</p>' 447 ) 448 } else { 449 // toggle approval status 450 newStatus = $( "#wic-email-approve-button span").hasClass('dashicons-thumbs-down') ? 0 : 1; 451 wpIssuesCRM.saveValueToInboxImage ( 'reply_is_final', newStatus ); 452 // move to next line -- toggled status should move message to new tab 453 var nextLine = wpIssuesCRM.activeLine.nextAll("li:not(.item-sending)")[0]; 454 wpIssuesCRM.activeLine.remove(); 455 wpIssuesCRM.jumpToSubjectLine( nextLine, 250 ); 456 } 457 458 459 } 427 460 428 461 // bulk action on delete or block from inbox level -
wp-issues-crm/trunk/js/email-send.js
r2096077 r2145245 29 29 30 30 wpIssuesCRM.handleComposeButton = function ( event ) { 31 32 if( !wpIssuesCRMSettings.canSendEmail) { 33 wpIssuesCRM.alert( 'Your user role can only submit or unsubmit emails for approval. Ask your supervisor for to upgrade your role so that you can send and take other actions.') 34 return; 35 } 36 31 37 // handle click on span within button 32 38 var eventVal = $( event.target ).val(); -
wp-issues-crm/trunk/js/issue.js
r1954508 r2145245 50 50 wpIssuesCRM.loadActivityArea( true ); 51 51 } 52 53 if ( !wpIssuesCRMSettings.canViewOthersAssigned ) { 54 $( "#wic-inner-field-group-issue_management input").prop("disabled", true) 55 } 56 57 52 58 } 53 59 -
wp-issues-crm/trunk/js/manage-storage.js
r1594515 r2145245 59 59 } else { 60 60 // require confirmation to go further 61 wpIssuesCRM.confirm ( 61 wpIssuesCRM.confirm ( 62 62 function () { 63 63 $( "#manage_storage_button" ).text( "Purging . . ." ); -
wp-issues-crm/trunk/php/admin/class-wic-admin-navigation.php
r2033320 r2145245 11 11 /* 12 12 * 13 * All user/form requests to the plugin are routed through this navigation class , which is light -- does no database access (except dictionary, needed on one non-wic page -- for post metabox in post edit)13 * All user/form requests to the plugin are routed through this navigation class 14 14 * GET Requests 15 15 * Wordpress menu and any page request get screened by wordpress at security levels defined in menu_setup 16 16 * Page gets with additional parameters further controlled as to what parameters OK through do_page referencing $nav_array 17 * If has attachment IDwill get served off admin_init to emit_stored_file before do_page -- check_security checks capability and nonce18 * 19 * A ll other requestsrun through check_security for both capability and nonce checking17 * If page request includes attachment ID, it will get served off admin_init to emit_stored_file before do_page -- check_security checks capability and nonce 18 * 19 * AJAX Requests to Entities run through check_security for both capability and nonce checking 20 20 * AJAX Form Posts -- all forms are submitted via AJAX to lower Wordpress Admin overhead 21 * AJAX Requests -- specific actions below the form level 22 * Download Button submits 21 * AJAX Requests -- specific actions below the entity level 22 * Only Entity Classes/methods are invoked by the AJAX endpoints -- check_security covers all entities, 23 * 24 * Special requests -- limited functions -- check security and nonce 25 * Download Button submits(2) 23 26 * Gmail API 27 * Upload requests (3) 24 28 * 25 29 * THIS IS THE ONLY ROUTE TO EXECUTE PLUG-IN CODE EXCEPT FOR: 26 30 * (0) DB setup (invoked in wp-issues-crm.php ) 27 * (1) Cron tasks (which route mail synch/parse/deliver through WP_Entity_Email_Account )31 * (1) Cron tasks for email and geocoding, protected by cron keys in config 28 32 * (2) Interface transactions (which accept and sanitize external form records for constituents and activities) 29 33 * (3) The metabox for post set up in WIC_Admin_Setup and other minor functions invoked wp-issues-crm.php 30 34 * (4) ANY OTHER WORDPRESS PLUGIN THAT CHOSE TO EXECUTE PLUG-IN CODE! 31 35 * 36 * Since 4.5, WIC_Admin_Access contains check_security which now also enforces access limits to assigned records -- all accesses hit this (ex the above exceptions) 37 * Client side access controls are all spoofable; so test all ajax calls here on the server side, although various buttons are hidden or disabled to avoid annoyance of failed access 38 * Ultimately dependent on Wordpress login security 39 * Default in all tests is to disallow without administrator access. 40 * 32 41 */ 33 42 … … 84 93 85 94 /* 86 * All $_GET processing is mediated through Wordpress -- the menu and submenu page structure ( exceptiongets for attachments)95 * All $_GET processing is mediated through Wordpress -- the menu and submenu page structure (partial exceptions gets for attachments) 87 96 * Two main functions -- menu_setup and do_page control this flow. 88 97 * $nav_array is used in both and limits valid GET requests (otherwise, nav logic could attempt route to arbitrary class functions) 89 98 * keys are pages as in add_menu_page or add_sub_menu_page; values are parameters governing the page display 90 * array also used for reverse look up for security level on AJAX requests99 * 91 100 */ 92 101 private $nav_array = array ( … … 95 104 'default' => array ( 'dashboard', 'dashboard' ),// default entity/action to invoke if $_GET does not contain permitted entity and actions 96 105 'permitted' => array ( // permissible entity/action pairs for the page on a GET request 97 array ( 'dashboard', 'my_cases' ),98 array ( 'dashboard', 'my_issues' ),99 array ( 'dashboard', 'search_log' ),100 106 array ( 'constituent', 'id_search' ), 101 107 array ( 'constituent', 'new_blank_form'), 102 array ( 'constituent', 'new_form' ),103 108 array ( 'email_inbox', 'new_blank_form'), 104 109 array ( 'issue', 'id_search' ), 105 110 array ( 'issue', 'new_blank_form'), 106 array ( 'issue', 'new_form' ),107 111 array ( 'search_log', 'id_search' ), 108 112 array ( 'search_log', 'id_search_to_form' ), 109 113 array ( 'advanced_search', 'new_blank_form' ), 110 array ( 'user', 'id_search' ),111 114 ), 112 115 'mobile' => true, // works on small screens … … 174 177 ), 175 178 ); 176 179 180 181 182 183 private $unassigned_message = 184 '<div id="wic-unassigned-message"> 185 <h3>You requested a record to which you have not been assigned or a function to which your role does not give you access.</h3> 186 <p>If you need access, consult your supervisor and request that the record be assigned to you or that your role be upgraded to a level that has access to unassigned constituents, issues and messages and/or to more functions.</p> 187 <p>Since Version 4.5 (August 2019), <strong>WP Issues CRM » Configure » Security</strong> determines which user roles have access to unassigned constituents and issues.</p> 188 </div>'; 177 189 178 190 // set up menu and submenus with required capability levels defined from $nav_array -- WP is checking security on the page access … … 180 192 181 193 $menu_position = '4.19544595294'; // pick arbitrary decimal number to avoid possibility of conflicts 182 $main_security_setting = self::check_required_capability( '' );194 $main_security_setting = WIC_Admin_Access::check_required_capability( '' ); 183 195 // need to run add setting before add page -- too late to register if try not to do the work until on the page 184 196 $wic_admin_settings = new WIC_Admin_Settings; … … 204 216 205 217 // format a page from menu ( or direct $_GET in which case, invoke entity/action based on $_GET; if not permitted entity/action pair route to default ) 206 public function do_page (){ 207 208 // get page parms from nav_array (page will always be in array since it covers all the pages that access the plugin) 218 // wordpress is doing the security checking on a GET 219 public function do_page (){ 220 221 // get page parms from nav_array (page will always be in array since it defines all the pages that access the plugin) 209 222 $page = $_GET['page']; 210 223 $parms = $this->nav_array[$page]; … … 228 241 // processing allowed get strings or defaults for pages 229 242 if ( 0 < count ( $_GET ) ) { 243 // if fully defined and OK string for page 230 244 if ( isset ( $_GET['entity'] ) && isset ( $_GET['action'] ) && isset ( $_GET['id_requested'] ) ) { 231 // filter allowed get actions 245 // filter allowed get actions. here is the page security check from $nav_array 232 246 if ( in_array ( array ( $_GET['entity'] , $_GET['action'] ), $parms['permitted'] ) ) { 233 247 $class_short = $_GET['entity']; 234 248 $action = $_GET['action']; 235 $args = array( 'id_requested' => ( 'user' == $class_short ? get_current_user_id() : $_GET['id_requested'] ) ); 249 $id = $_GET['id_requested']; 250 $args = array( 'id_requested' => $id ); // removed get path to user 236 251 } 237 252 } 253 // if not fully defined or not OK 238 254 if ( !isset ( $class_short ) ) { 239 255 $class_short = $parms['default'][0]; 240 256 $action = $parms['default'][1]; 241 $args = 'user' == $class_short ? array( 'id_requested' => get_current_user_id() ) : array(); 257 $args = array(); 258 $id = ''; 242 259 } 260 261 // cosmetics 243 262 if ( 'wp-issues-crm-main' == $page ) { 244 263 $this->show_top_menu ( $class_short, $action ); 245 264 } 246 // cosmetics247 265 $showing_email = ''; 248 266 if ( isset( $_GET['entity'] ) ) { … … 251 269 } 252 270 } 271 253 272 // main action 254 273 echo '<div id="wic-main-form-html-wrapper" class="' . $showing_email . '">'; 255 274 if ( $class_short ) { 256 $class = 'WIC_Entity_' . $class_short; 257 $class_action = new $class ( $action, $args ); 275 if ( WIC_Admin_Access::check_security ( $class_short, $action, $id, '', false ) ) { // false means no nonce to check 276 $class = 'WIC_Entity_' . $class_short; 277 $class_action = new $class ( $action, $args ); 278 } else { 279 echo $this->unassigned_message; 280 } 258 281 } else { 259 282 $action[0]::{$action[1]}(); // static function in settings case -- php 7.1{} … … 270 293 if (preg_match('~MSIE|Internet Explorer~i', $ua) || (strpos($ua, 'Trident/7.0') !== false && strpos($ua, 'rv:11.0') !== false) ) { 271 294 echo '<p><em>It appears you are using Internet Explorer. Internet Explorer does not support all modern javascript language constructs. 272 WP Issues CRM performs best using Chrome .</em></p>';295 WP Issues CRM performs best using Chrome or Firefox.</em></p>'; 273 296 return; 274 297 } 275 if ( false === stripos ( $ua, 'chrome' ) ) {276 echo '<p><em>It appears you are using a browser other than Chrome. WP Issues CRM performs best using Chrome.</em></p>';277 }278 298 279 299 } … … 320 340 // a2b (array to button) 321 341 private function a2b ( $top_menu_button ) { 342 343 if ( !current_user_can (WIC_Admin_Access::check_required_capability ('view_edit_unassigned') ) ) { 344 if ( in_array ( $top_menu_button[0], array ( 'issue', 'advanced_search', 'upload') ) ) { 345 return false; 346 } 347 } 348 349 322 350 $button_args = array ( 323 351 'entity_requested' => $top_menu_button[0], … … 335 363 336 364 /* 337 * AJAX Routing functions 365 * AJAX Routing functions -- 338 366 * FORM 339 367 * Plain request 340 368 * 369 * Both are limited to Entity Classes 370 * Check security requires raised security for admin classes (identified in nav_array) 341 371 */ 342 372 343 public function route_ajax_form() { 373 public function route_ajax_form() { 344 374 345 375 $this->dictionary_setup(); … … 347 377 $control_array = explode( ',', $_POST['wic_form_button'] ); 348 378 349 // check security and as a byproduct, get requesting page for use in history URL379 // define terms 350 380 $entity = $control_array[0]; 351 $requesting_page = $this->check_security ( $entity );352 if ( false === $requesting_page ) {353 $requesting_page = 'wp-issues-crm-main';354 }355 356 $class = 'WIC_Entity_' . $entity;357 381 $action = $control_array[1]; 358 382 $id_requested = $control_array[2]; 383 $class = 'WIC_Entity_' . $entity; 359 384 $args = array ( 360 385 'id_requested' => $id_requested, 361 386 ); 362 387 363 // do the request388 // check_security and die or do the request 364 389 ob_start(); 365 $new_entity = new $class ( $action, $args ); 390 if ( WIC_Admin_Access::check_security ( $entity, $action, $id_requested, '' ) ) { 391 $new_entity = new $class ( $action, $args ); 392 } else { 393 wp_die( $this->unassigned_message ); // no further state processing -- leave client page unaltered 394 } 366 395 $output = ob_get_clean(); 367 396 … … 395 424 } 396 425 397 // complete the push state instructions 426 // complete the push state url by adding in requesting page 427 $requesting_page = false; 428 foreach ( $this->nav_array as $page => $parms ) { 429 foreach ( $parms['permitted'] as $permitted ) { 430 if ( $entity == $permitted[0] ) { 431 $requesting_page = $page; 432 break(2); 433 } 434 } 435 } 436 if ( false === $requesting_page ) { 437 $requesting_page = 'wp-issues-crm-main'; 438 } 398 439 $response->state = admin_url ( 'admin.php?page=' . $requesting_page . $resource ); 399 440 400 441 // send AJAX response 401 442 wp_die( json_encode ( $response ) ); … … 403 444 404 445 405 public function route_ajax () { 446 public function route_ajax () { 406 447 407 448 $this->dictionary_setup(); … … 422 463 */ 423 464 $entity = $_POST['entity']; 424 // check security -- dies on failure425 $this->check_security( $entity );426 427 // respond to request ( response function terminate with wp_die and return json_encoded response )428 465 $class = 'WIC_Entity_' . $entity; 429 466 $method = $_POST['sub_action']; 430 $method_response = $class::$method( $_POST['id_requested'], json_decode ( stripslashes ( $_POST['wic_data'] ) ) ); 431 432 wp_die ( json_encode ( (object) $method_response ) ); 433 467 $id = $_POST['id_requested']; 468 $data = json_decode ( stripslashes ( $_POST['wic_data'] ) ); 469 470 // check seccurity 471 if ( !WIC_Admin_Access::check_security ( $entity, $method, $id, $data ) ) { 472 wp_die ( $this->unassigned_message ); 473 } else { 474 $method_response = $class::$method( $id, $data ); 475 wp_die ( json_encode ( (object) $method_response ) ); 476 } 434 477 } 435 478 … … 438 481 public function route_ajax_upload() { 439 482 $this->dictionary_setup(); 440 $this->check_security( 'constituent' ); 441 WIC_Entity_Upload_Upload::handle_upload(); 483 if( !WIC_Admin_Access::check_security( 'upload','','','' ) ) { 484 wp_die ( $this->unassigned_message ); 485 } else { 486 WIC_Entity_Upload_Upload::handle_upload(); 487 } 442 488 } 443 489 public function route_ajax_document_upload() { 444 490 445 491 $this->dictionary_setup(); 446 $this->check_security( 'constituent' );447 448 492 // set values 449 493 $constituent_id = isset ( $_REQUEST['constituent_id'] ) ? $_REQUEST['constituent_id'] : 0 ; 450 494 $issue = isset ( $_REQUEST['issue'] ) ? $_REQUEST['issue'] : 0; 451 452 WIC_Entity_Upload_Upload::handle_document_upload( $constituent_id, $issue ); 495 496 if ( !WIC_Admin_Access::check_security ( $constituent_id ? 'constituent' : 'issue', '', $constituent_id ? $constituent_id : $issue , '' ) ) { 497 wp_die ( $this->unassigned_message ); 498 } else { 499 WIC_Entity_Upload_Upload::handle_document_upload( $constituent_id, $issue ); 500 } 453 501 } 454 502 public function route_ajax_attachment_upload() { 455 503 $this->dictionary_setup(); 456 $this->check_security( 'constituent' ); 457 $draft_id = $_REQUEST['draft_id']; 458 WIC_Entity_Upload_Upload::handle_attachment_upload( $draft_id ); 504 if ( !WIC_Admin_Access::check_security( 'email_send', 'update_draft','','' ) ) { 505 wp_die ( $this->unassigned_message ); 506 } else { 507 $draft_id = $_REQUEST['draft_id']; 508 WIC_Entity_Upload_Upload::handle_attachment_upload( $draft_id ); // may not know constituent 509 } 459 510 } 460 511 … … 466 517 // nonce is in the get string where it is expected by check_security 467 518 if ( isset( $_GET['entity'] ) && $_GET['entity'] == 'email_oauth' && $_GET['action'] == 'redirect_to_gmail' ) { 468 self::check_security ( 'email_oauth' ); // includes nonce checking in get string 469 WIC_Entity_Email_OAUTH::redirect_to_gmail(); 519 if( !WIC_Admin_Access::check_security ( 'email_oauth', 'oauth','','' ) ) { // includes nonce checking in get string 520 wp_die ( $this->unassigned_message ); 521 } else { 522 WIC_Entity_Email_OAUTH::redirect_to_gmail(); 523 } 470 524 // nonce, along with blog ID is in the state returned 471 525 } elseif ( isset( $_GET['entity'] ) && $_GET['entity'] == 'email_oauth' && $_GET['action'] == 'redirect_from_gmail' ) { … … 477 531 // pack the nonce value for checking by check_surecity 478 532 $_GET['oauth_nonce'] = $security_array[1]; 479 self::check_security ( 'email_oauth' ); // includes nonce-checking480 533 WIC_Entity_Email_OAUTH::redirect_from_gmail(); 534 if( !WIC_Admin_Access::check_security ( 'email_oauth', 'oauth','','' ) ) { // includes nonce checking in get string 535 wp_die ( $this->unassigned_message ); 536 } else { 537 WIC_Entity_Email_OAUTH::redirect_from_gmail(); 538 } 481 539 } 482 540 } … … 497 555 } 498 556 557 // check access level to function 499 558 $parameters = explode ( ',', $_POST['wic-export-parameters'] ); 500 $this->check_security ( 'document' == $parameters[0] ? '' : 'download' ); // treat document downloads as part of general WP Issues CRM acccess501 502 559 $class = 'WIC_List_' . $parameters[0] . '_Export'; 503 560 $method = 'do_' . $parameters[1] . '_download'; 504 561 $type = $parameters[2]; 505 562 $id_data = $parameters[3]; 506 $class::$method ( $type, $id_data ); 563 564 if (! WIC_Admin_Access::check_security ( 'document' == $parameters[0] ? 'activity' : 'download','popup_save_update', $id_data,'' ) ) { // will not see $method in check_security for download (no array) 565 wp_die( $this->unassigned_message ) ; 566 } else { 567 $class::$method ( $type, $id_data ); 568 } 507 569 508 570 } … … 510 572 * 511 573 * action to capture get requests for image and email attachment downloads 512 * 574 * 513 575 */ 514 576 public function emit_stored_file () { … … 524 586 return; 525 587 } 526 // check security at the read email level 527 self::check_security( 'email' ); 528 588 529 589 $message_in_outbox = isset( $_GET['message_in_outbox'] ) ? $_GET['message_in_outbox'] : 0; 530 WIC_Entity_Email_Attachment::emit_stored_file ( $_GET['attachment_id'], $_GET['message_id'], $message_in_outbox ); 531 } 532 /* 533 * 534 * check capabilities and nonces for ajax functions, including posts ( note that capability checking done by WP on gets -- no nonces on our gets (no updates) ) 535 * 536 * if all security is passed, return requesting page (which may be false) -- false is not a security error -- die on security error 537 * 538 */ 539 private function check_security ( $entity ) { 540 541 // do a reverse lookup in the nav_array to find if entity has special security level ( e.g., settings or options or manage storage ) 542 $found_entity = false; 543 $entity_special_security = false; 544 $requesting_page = false; 545 foreach ( $this->nav_array as $page => $parms ) { 546 foreach ( $parms['permitted'] as $permitted ) { 547 if ( $entity == $permitted[0] ) { 548 $found_entity = true; 549 break; 550 } 551 } 552 if ( $found_entity ) { 553 $entity_special_security = $parms['security']; 554 $requesting_page = $page; 555 break; 556 } 557 } 558 559 /* 560 * 561 * set required capability based on entity special or based on security level for function 562 * function levels are '' for general wic access, 'email' for inbox functions (including attachments), 'downloads' for downloads (including documents, but not attachments) 563 * 564 * note that the fourth permission level list_send security is enforced by not displaying send button and also in the send function 565 */ 566 if ( $entity_special_security ) { 567 $required_capability = $entity_special_security; 568 } else { // downloads, general or email have own security levels 569 if ( 'email' == $entity ) { 570 $security_level = 'email'; 571 } elseif ( 'download' == $entity ) { 572 $security_level = 'downloads'; // note the 's' 573 } else { 574 $security_level = ''; 575 } 576 $required_capability = self::check_required_capability ( $security_level ); 577 } 578 579 // see if user can do function -- should never be failing this except as an application error or a deliberate security breach, so just die 580 if ( ! current_user_can ( $required_capability ) ) { 581 wp_die( sprintf( __( 'Please consult your administrator for access to %s functions', 'wp-issues-crm' ), $entity ) ); 582 } 583 584 // now check nonce -- either of four possible nonces are OK 585 $request_nonce = isset ( $_POST['wic_ajax_nonce'] ) ? $_POST['wic_ajax_nonce'] : ''; 586 $form_nonce = isset ( $_REQUEST['wp_issues_crm_post_form_nonce_field'] ) ? $_REQUEST['wp_issues_crm_post_form_nonce_field'] : '' ; 587 $oauth_nonce = isset ( $_GET['oauth_nonce'] ) ? $_GET['oauth_nonce'] : ''; 588 $attachment_nonce = isset( $_GET['attachment_nonce'] ) ? $_GET['attachment_nonce'] : ''; 589 $attachment_id = isset ( $_GET['attachment_id'] ) ? $_GET['attachment_id'] : ''; 590 if ( ! wp_verify_nonce ( $request_nonce, 'wic_ajax_nonce' ) && 591 ! wp_verify_nonce ( $form_nonce, 'wp_issues_crm_post' ) && 592 ! wp_verify_nonce ( $oauth_nonce, 'wic_oauth' ) && 593 ! wp_verify_nonce ( $attachment_nonce, 'attachment_' . $attachment_id ) 594 ) { 595 die ( __( 'Bad or expired security code. Try refreshing page.', 'wp_issues_crm' ) ); 596 } 597 598 return ( $requesting_page ); 599 600 } 601 602 // check download or general security level setting 603 public static function check_required_capability ( $type = '' ) { 604 // get security level setting 605 $wic_plugin_options = get_option( 'wp_issues_crm_plugin_options_array' ); 606 $option = $type > '' ? ( 'access_level_required_' . $type ) : 'access_level_required'; 607 $required_capability = isset( $wic_plugin_options[$option] ) ? $wic_plugin_options[$option] : 'edit_theme_options'; 608 // handle upgrade to 3.5 case in network setting, where old option selected was 'activate_plugins' 609 if ( 'activate_plugins' == $required_capability ) { 610 $required_capability = 'edit_theme_options'; 611 } 612 return $required_capability; 613 } 590 591 if (! WIC_Admin_Access::check_security ( 'email_message','load_full_message', $_GET['message_id'], $message_in_outbox ) ) { 592 wp_die( $this->unassigned_message ) ; 593 } else { 594 WIC_Entity_Email_Attachment::emit_stored_file ( $_GET['attachment_id'], $_GET['message_id'], $message_in_outbox ); 595 } 596 597 598 } 599 600 614 601 615 602 } -
wp-issues-crm/trunk/php/admin/class-wic-admin-settings.php
r2134115 r2145245 67 67 'security_settings' // settings section within page 68 68 ); 69 70 add_settings_field( 71 'access_level_required_view_edit_unassigned', // field id 72 'Access unassigned', // field label 73 array( $this, 'access_level_required_view_edit_unassigned_callback' ), // field call back 74 'wp_issues_crm_settings_page', // page 75 'security_settings' // settings section within page 76 ); 69 77 70 78 add_settings_field( … … 78 86 add_settings_field( 79 87 'access_level_required_email', // field id 80 ' Read and reply toemail', // field label88 'Send email', // field label 81 89 array( $this, 'access_level_required_email_callback' ), // field call back 82 90 'wp_issues_crm_settings_page', // page … … 91 99 'security_settings' // settings section within page 92 100 ); 93 101 94 102 // Privacy Settings 95 103 add_settings_section( … … 501 509 <table class="wp-issues-crm-stats"> 502 510 <tbody> 503 <tr><td>Chrome -- strongly recommended and heavily tested.</td></tr>504 <tr><td>Firefox -- less heavily tested; willgenerate errors when accessing browser history due to internal limits.</td></tr>511 <tr><td>Chrome -- performs best.</td></tr> 512 <tr><td>Firefox -- also recommended, and supports better privacy, but may generate errors when accessing browser history due to internal limits.</td></tr> 505 513 <tr><td>Safari -- not well supported; the open source editor we use for email composition, tinymce, does not perform perfectly in Safari.</td></tr> 506 <tr><td>Explorer -- NOT supported; Explorer does not support all modern Javascript language constructs that WP Issues CRM requires.</td></tr>514 <tr><td>Explorer/Edge -- NOT supported; Explorer does not support all modern Javascript language constructs that WP Issues CRM requires. Edge does not handle formatting consistently.</td></tr> 507 515 </table> 508 516 <p><em>The basic WP Issues CRM forms and lists are responsive for mobile browsers, but look better on a desktop. … … 672 680 public function security_settings_legend() { 673 681 echo'<p>' . __( 'Only logged-in users can access WP Issues CRM.') . '</p>' . 674 '<p>' . __( 'You can control who among your logged-in users can access (a) any WP Issues CRM functions; (b) bulk constituent and activity downloads and deletion; (c) email reading and replying.675 ( d) sending emails to lists. Administrators always have access to all four.') . '</p><p>' .682 '<p>' . __( 'You can control who among your logged-in users can access (a) any WP Issues CRM functions; (b) constituents, issues and emails that have not been assigned to them (c) bulk constituent and activity downloads and deletion; (d) email archiving and sending; 683 (e) sending emails to lists. Administrators always have access to WP Issues CRM and all four specific capabilities. Note that constituents created or last updated by a user may always be accessed by the user, even if not formally assigned to them.') . '</p><p>' . 676 684 __( 'You assign users to roles ("Administrator", "Editor" . . . ) as part of their user profiles. Here, you grant access to user roles with particular <a href="https://codex.wordpress.org/Roles_and_Capabilities" target = "_blank">Wordpress capabilities</a>. 677 685 Access is hierarchical. For example, if you grant access to "Authors", then those with additional capabilities, "Editors" and "Administators", will also have access, but "Contributors" and "Subscribers" will not.', 'wp-issues-crm' ) . '</p><p>' . … … 740 748 } 741 749 750 751 // setting field call back 752 public function access_level_required_view_edit_unassigned_callback() { 753 global $wic_db_dictionary; 754 $option_array = $wic_db_dictionary->lookup_option_values( 'capability_levels' ); 755 756 $value = isset ( $this->plugin_options['access_level_required_view_edit_unassigned'] ) ? $this->plugin_options['access_level_required_view_edit_unassigned'] : 'edit_theme_options'; 757 758 $args = array ( 759 'field_label' => '', 760 'option_array' => $option_array, 761 'input_class' => '', 762 'field_slug_css' => '', 763 'hidden' => '', 764 'field_slug' => 'wp_issues_crm_plugin_options_array[access_level_required_view_edit_unassigned]', 765 'value' => $value , 766 ); 767 echo WIC_Control_Select::create_control( $args ); 768 } 742 769 743 770 // setting field call back … … 1467 1494 if( isset( $input['access_level_required_list_send'] ) ) { 1468 1495 $new_input['access_level_required_list_send'] = sanitize_text_field( $input['access_level_required_list_send'] ); 1469 } 1496 } 1497 if( isset( $input['access_level_required_view_edit_unassigned'] ) ) { 1498 $new_input['access_level_required_view_edit_unassigned'] = sanitize_text_field( $input['access_level_required_view_edit_unassigned'] ); 1499 } 1500 1470 1501 // privacy settings 1471 1502 if( isset( $input['all_posts_private'] ) ) { -
wp-issues-crm/trunk/php/admin/class-wic-admin-setup.php
r2096077 r2145245 286 286 'maxFileSize' => WIC_Entity_Upload_Upload::get_safe_file_size(), 287 287 'dearToken' => WIC_Entity_Email_Send::dear_token, 288 'canSendEmail' => current_user_can (WIC_Admin_Access::check_required_capability ('email')), 289 'canViewOthersAssigned' => current_user_can (WIC_Admin_Access::check_required_capability ('view_edit_unassigned')), 288 290 ) 289 291 ); -
wp-issues-crm/trunk/php/db/class-wic-db-email-message-object.php
r2028069 r2145245 162 162 $message_object->is_my_constituent = $message_array[0]->is_my_constituent_guess; // translating from guess to non guess for save purposes 163 163 $message_object->display_date = $message_object->email_date_time; 164 $message_object->inbox_defined_staff = $message_array[0]->inbox_defined_staff; 165 $message_object->inbox_defined_issue = $message_array[0]->inbox_defined_issue; 166 $message_object->inbox_defined_pro_con = $message_array[0]->inbox_defined_pro_con; 167 $message_object->inbox_defined_reply_text = $message_array[0]->inbox_defined_reply_text; 168 $message_object->inbox_defined_reply_is_final = $message_array[0]->inbox_defined_reply_is_final; 169 164 170 // at time of parse, attempted to find matching constituent 165 171 if ( $message_array[0]->assigned_constituent ) { -
wp-issues-crm/trunk/php/entity/class-wic-entity-activity.php
r2037289 r2145245 129 129 $buttons .= WIC_List_Activity::reassign_activities_button ( $search_parms, 'issue' ); 130 130 131 $required_capability = WIC_Admin_ Navigation::check_required_capability( 'downloads' ); // downloads131 $required_capability = WIC_Admin_Access::check_required_capability( 'downloads' ); // downloads 132 132 if (current_user_can( $required_capability ) ) { 133 133 $buttons .= WIC_List_Activity::delete_activities_button ( $search_parms, 'issue'); -
wp-issues-crm/trunk/php/entity/class-wic-entity-dashboard.php
r2033320 r2145245 27 27 } else { 28 28 $sort_list = array(); 29 $wide_list = array( 'dashboard_ issues','dashboard_cases','dashboard_activity', 'dashboard_activity_type', 'dashboard_recent', 'dashboard_searches', 'dashboard_uploads' );29 $wide_list = array( 'dashboard_mycases', 'dashboard_myissues', 'dashboard_issues','dashboard_cases','dashboard_activity', 'dashboard_activity_type', 'dashboard_recent', 'dashboard_searches', 'dashboard_uploads' ); 30 30 $tall_list = array(); 31 31 } 32 33 // inventory of dashboard widgets and titles 34 $dashboard_divs = array ( 35 'dashboard_issues' => 'Assigned Issues', 36 'dashboard_cases' => 'Assigned Cases', 37 'dashboard_activity' => 'Constituents with Activity by Issue', 38 'dashboard_activity_type' => 'Activities by Issue and Type', 39 'dashboard_recent' => 'Recently Updated', 40 'dashboard_searches' => 'Search Log', 41 'dashboard_uploads' => 'Uploads', 42 ); 43 32 if ( current_user_can (WIC_Admin_Access::check_required_capability ('view_edit_unassigned') ) ) { 33 // inventory of dashboard widgets and titles 34 $dashboard_divs = array ( 35 'dashboard_overview' => 'Staff Work Status', 36 'dashboard_issues' => 'Assigned Issues', 37 'dashboard_cases' => 'Assigned Cases', 38 'dashboard_activity' => 'Constituents with Activity by Issue', 39 'dashboard_activity_type' => 'Activities by Issue and Type', 40 'dashboard_recent' => 'Recently Updated', 41 'dashboard_searches' => 'Search Log', 42 'dashboard_uploads' => 'Uploads', 43 ); 44 } else { 45 // inventory of dashboard widgets and titles 46 $dashboard_divs = array ( 47 'dashboard_overview' => 'Staff Work Status', 48 'dashboard_myissues' => 'Assigned Issues', 49 'dashboard_mycases' => 'Assigned Cases', 50 ); 51 } 44 52 // sort the inventory 45 53 $sorted_dashboard_divs = array(); … … 73 81 } 74 82 75 public static function dashboard_email ( $dummy_id, $dummy_data ) { 76 return array ( 'response_code' => true, 'output' => '<div class="dashboard-not-found">Email Stuff</div>' ); 77 } 78 83 79 84 public static function save_dashboard_preferences ( $dummy_id, $data ) { 80 85 return WIC_Entity_User::set_wic_user_preference ( 'wic_dashboard_config', $data ); 81 86 } 82 83 // display a list of CASES assigned to user -- OBSOLETE, BUT LEAVING IT HERE IN CASE WANT TO USE IT FOR LOWER LEVEL USERS AT SOME POINT (SEE ONLY OWN, NO OPTIONS) 87 88 public static function dashboard_overview ( $dummy_id, $dummy_data ) { 89 90 global $wpdb; 91 $constituent_table = $wpdb->prefix . 'wic_constituent'; 92 $post_meta_table = $wpdb->postmeta; 93 $inbox_image_table = $wpdb->prefix . 'wic_inbox_image'; 94 $user_table = $wpdb->users; 95 // get current folder 96 $folder = WIC_Entity_Email_Account::get_folder(); 97 98 $constituent_sql = " 99 SELECT case_assigned as user_id, count(id) as cases_open, sum(if( case_review_date < NOW(), 1, 0)) as cases_overdue FROM $constituent_table WHERE case_status = 1 GROUP BY case_assigned 100 "; 101 102 $issue_sql = " 103 SELECT m3.meta_value as user_id, count(m1.meta_id) as issues_open, sum(if(m2.meta_value < NOW() OR m2.meta_value is null, 1, 0) ) as issues_overdue 104 FROM $post_meta_table m1 105 LEFT JOIN $post_meta_table m2 on m2.post_id = m1.post_id and m2.meta_key = 'wic_data_review_date' 106 LEFT JOIN $post_meta_table m3 on m3.post_id = m1.post_id and m3.meta_key = 'wic_data_issue_staff' 107 WHERE m1.meta_key = 'wic_data_follow_up_status' AND m1.meta_value = 'open' 108 GROUP BY m3.meta_value 109 "; 110 111 $message_sql = " 112 SELECT 113 inbox_defined_staff as user_id, 114 count(id) as count_assigned_messages, 115 sum(if( 0 = inbox_defined_reply_is_final, 1, 0 )) as unfinalized_messages, 116 left( min(if( 0 = inbox_defined_reply_is_final, email_date_time, '9999-99-99' )), 10) as oldest_unanswered 117 FROM $inbox_image_table WHERE inbox_defined_staff > 0 AND 118 full_folder_string = '$folder' AND 119 no_longer_in_server_folder = 0 AND 120 to_be_moved_on_server = 0 AND 121 serialized_email_object > '' 122 GROUP BY inbox_defined_staff 123 "; 124 // get values 125 $case_assigned_array = $wpdb->get_results ( $constituent_sql ); 126 $issue_assigned_array = $wpdb->get_results ( $issue_sql ); 127 $message_assigned_array = $wpdb->get_results ( $message_sql ); 128 // construct array of user ids to report on 129 $user_ids = array(); 130 foreach ( $case_assigned_array as $line ) { 131 if ( !array_key_exists ( $line->user_id, $user_ids ) ) { 132 $user_ids[$line->user_id] = array(); 133 } 134 $user_ids[$line->user_id]['cases_open'] = $line->cases_open; 135 $user_ids[$line->user_id]['cases_overdue'] = $line->cases_overdue; 136 } 137 foreach ( $issue_assigned_array as $line ) { 138 if ( !array_key_exists ( $line->user_id, $user_ids ) ) { 139 $user_ids[$line->user_id] = array(); 140 } 141 $user_ids[$line->user_id]['issues_open'] = $line->issues_open; 142 $user_ids[$line->user_id]['issues_overdue'] = $line->issues_overdue; 143 } 144 foreach ( $message_assigned_array as $line ) { 145 if ( !array_key_exists ( $line->user_id, $user_ids ) ) { 146 $user_ids[$line->user_id] = array(); 147 } 148 $user_ids[$line->user_id]['count_assigned_messages'] = $line->count_assigned_messages; 149 $user_ids[$line->user_id]['unfinalized_messages'] = $line->unfinalized_messages; 150 $user_ids[$line->user_id]['oldest_unanswered'] = $line->oldest_unanswered; 151 152 } 153 154 if ( !count ( $user_ids ) ) { 155 return array ( 'response_code' => true, 'output' => '<div class="dashboard-not-found">No work assignments found.</div>' ); 156 } 157 158 $user_login_array = array(); 159 foreach ($user_ids as $user_id => $values ) { 160 if ( $user_id > 0 ) { 161 $user_data = get_userdata($user_id); 162 $user_display_name = $user_data->display_name ? $user_data->display_name : $user_data->user_login; 163 } else { 164 $user_display_name = 'Unassigned'; 165 } 166 $user_login_array[$user_display_name] = $values; 167 } 168 169 ksort ( $user_login_array ); 170 171 $output = '<table id="wic-work-flow-status"> 172 <tr> 173 <th>User Id</th> 174 <th>Open Cases</th> 175 <th>Overdue Cases</th> 176 <th>Open Issues</th> 177 <th>Overdue Issues</th> 178 <th>Assigned Messages</th> 179 <th>Unanswered Messages</th> 180 <th>Oldest Unanswered</th> 181 </tr>'; 182 foreach ( $user_login_array as $user_login => $values ) { 183 $output .= '<tr>' . 184 '<td>' . $user_login . '</td>' . 185 '<td>' . ( isset( $values['cases_open'] ) ? $values['cases_open']: 0 ) . '</td>' . 186 '<td class="' . ( isset( $values['cases_overdue'] ) && $values['cases_overdue'] > 0 ? 'wic-staff-overdue-assignment' : '' ) . '">' . ( isset( $values['cases_overdue'] ) ? $values['cases_overdue']: 0 ) . '</td>' . 187 '<td>' . ( isset( $values['issues_open'] ) ? $values['issues_open']: 0 ) . '</td>' . 188 '<td class="' . ( isset( $values['issues_overdue'] ) && $values['issues_overdue'] > 0 ? 'wic-staff-overdue-assignment' : '' ) . '">' . ( isset( $values['issues_overdue'] ) ? $values['issues_overdue']: 0 ) . '</td>' . 189 '<td>' . ( isset( $values['count_assigned_messages'] ) ? $values['count_assigned_messages']: 0 ) . '</td>' . 190 '<td class="' . ( isset( $values['unfinalized_messages'] ) && $values['unfinalized_messages'] > 0 ? 'wic-staff-overdue-assignment' : '' ) . '">' . ( isset( $values['unfinalized_messages'] ) ? $values['unfinalized_messages']: 0 ) . '</td>' . 191 '<td class="wic-email-dashboard-date">' . ( isset( $values['oldest_unanswered'] ) && $values['oldest_unanswered'] != '9999-99-99' ? $values['oldest_unanswered']: '--' ) . '</td>' . 192 '</tr>'; 193 } 194 $output .= "</table>"; 195 196 return array ( 'response_code' => true, 'output' => $output ); 197 } 198 199 // display a list of CASES assigned to user -- (SEE ONLY OWN, NO OPTIONS) 84 200 public static function dashboard_mycases( ) { 85 201 … … 124 240 } 125 241 126 // display a list of issues assigned to user -- OBSOLETE, BUT LEAVING IT HERE IN CASE WANT TO USE IT FOR LOWER LEVEL USERS AT SOME POINT(SEE ONLY OWN, NO OPTIONS)242 // display a list of issues assigned to user -- (SEE ONLY OWN, NO OPTIONS) 127 243 public static function dashboard_myissues() { 128 244 … … 238 354 ); 239 355 240 $search_array = array ( 241 array ( 242 'table' => 'issue', 243 'key' => 'issue_staff', 244 'value' => $issue_staff, 245 'compare' => $issue_staff ? '=' : '>', 246 'wp_query_parameter' => '', 247 ), 248 ); 249 356 if ( $issue_staff ) { 357 $search_array = array ( 358 array ( 359 'table' => 'issue', 360 'key' => 'issue_staff', 361 'value' => $issue_staff, 362 'compare' => '=', 363 'wp_query_parameter' => '', 364 ), 365 ); 366 } else { 367 $search_array = array(); 368 } 369 250 370 if ( $follow_up_status !== '') { 251 371 $status_term = array ( -
wp-issues-crm/trunk/php/entity/class-wic-entity-email-account.php
r2019577 r2145245 179 179 * 180 180 * the array should always include 'Advocacy' as this special tab is used dynamically to group emails with subject lines that are mapped to issues 181 * the array should always include 'Team' as this special tab is used dynamically to group emails from users having access to WP Issues CRM 182 * the array should always include 'Assigned' as this special tab is used dynamically to group emails assigned for response to current user 183 * the array should always include 'Ready' as this special tab is used dynamically to group emails that have drafts ready for response 181 184 * 182 185 * the category filter should catch every message -- messages with blank categories will not be displayed … … 196 199 'Promotions', 197 200 'Updates', 198 'Forums' 201 'Forums', 202 'Team', 203 'Assigned', 204 'Ready' 199 205 ); 200 206 } 201 202 return array ( 'General', 'Advocacy' );207 208 return array ( 'General', 'Advocacy', 'Team', 'Assigned', 'Ready' ); 203 209 204 210 } -
wp-issues-crm/trunk/php/entity/class-wic-entity-email-block.php
r2012006 r2145245 113 113 114 114 public static function load_block_list ( $dummy1, $dummy2 ) { 115 115 116 global $wpdb; 116 117 $filter_table = $wpdb->prefix . 'wic_inbox_incoming_filter'; -
wp-issues-crm/trunk/php/entity/class-wic-entity-email-inbox-parse.php
r2070126 r2145245 181 181 } 182 182 183 // subject and category local filters -- may be defined in a local plugin -- gmail supplies own category 183 // subject and category local filters -- may be defined in a local plugin -- gmail supplies own category, but local can override 184 184 // https://developer.wordpress.org/reference/functions/apply_filters/ 185 185 $subject = apply_filters ( 'wp_issues_crm_local_subject_filter', $email_object->subject, $email_object ); … … 188 188 $email_object->category = 'CATEGORY_GENERAL'; 189 189 } 190 // apply category filter for team members before local category function 191 $email_object->category = get_user_by( 'email', $email_object->from_email ) ? 'CATEGORY_TEAM' : $email_object->category; 192 193 // local category filter may or may not respect CATEGORY_TEAM 190 194 $category = apply_filters ( 'wp_issues_crm_local_category_filter', $email_object->category, $email_object, 'Y' == $is_my_constituent ); 191 195 -
wp-issues-crm/trunk/php/entity/class-wic-entity-email-inbox.php
r2036102 r2145245 21 21 // special version of this function to allow checking of settings before form display 22 22 protected function new_blank_form( $args = '', $guidance = '' ) { 23 24 $required_capability = WIC_Admin_Navigation::check_required_capability( 'email' );25 if ( ! current_user_can( $required_capability ) ) {26 echo '<div id="inbox-congrats">Check with your site administrator to upgrade your role if you need to access email functions.</div>';27 return;28 }29 23 30 24 $wic_settings = get_option( 'wp_issues_crm_plugin_options_array' ); … … 80 74 * 81 75 */ 82 83 $ required_capability = WIC_Admin_Navigation::check_required_capability( 'email');84 if ( ! current_user_can( $required_capability) ) {85 return array ( 'response_code' => false, 'output' => 'Security violation. Current user role not authorized to access inbox.' );76 // enforce limit on access to assigned only 77 $user_can_see_all = current_user_can ( WIC_Admin_Access::check_required_capability( 'view_edit_unassigned' ) ); 78 if ( ! $user_can_see_all && !in_array( $data->tab, array( 'CATEGORY_ASSIGNED', 'CATEGORY_READY') ) ) { 79 $data->tab = 'CATEGORY_ASSIGNED' ; 86 80 } 87 81 … … 109 103 * (2) They all have content mapped to the same issue/pro-con, which agrees with the subject mapped result 110 104 * (3) The content match meets both the required confidence percentage and the required word count percentage 105 * (4) HAVE NOT HAD ANY INDIVIDUAL MESSAGE INBOX DEFINITION ACTIVITY 111 106 * NOTE: There may or may not be a reply already assigned to the issue -- could be just recording 112 107 * … … 141 136 /* 142 137 * sweep_definition is critical concept that enforces strictness of automated reply processing per notes above 138 * 139 * added inbox_defined terms exclude items that have had individual attention from the inbox in any grouping 143 140 */ 144 141 $inbox_image_table = $wpdb->prefix . 'wic_inbox_image'; … … 149 146 guess_mapped_pro_con = mapped_pro_con AND 150 147 guess_mapped_issue_confidence >= $mapped_threshold AND 151 non_address_word_count >= $word_minimum_threshold "; 148 non_address_word_count >= $word_minimum_threshold AND 149 inbox_defined_staff = 0 AND 150 inbox_defined_issue = 0 AND 151 inbox_defined_pro_con = '' AND 152 inbox_defined_reply_text = '' 153 "; 152 154 153 155 $filter = sanitize_text_field ( $data->filter ); 154 156 $filter_where = self::filter_where ( $filter ); 155 157 156 // add tab selection terms -- CATEGORY_ADVOCACY isspecial, dynamically applied158 // add tab selection terms -- CATEGORY_ADVOCACY, CATEGORY_ASSIGNED, CATEGORY_READY special, dynamically applied 157 159 $category_where = $wpdb->prepare ( 158 " IF(mapped_issue > 0, 'CATEGORY_ADVOCACY', category ) = %s AND ", 160 "IF( 161 assigned_subject is NULL AND constituent_assigned_staff is NULL, 162 IF( mapped_issue > 0, 'CATEGORY_ADVOCACY', category ), 163 IF( subject_is_final, 'CATEGORY_READY', 'CATEGORY_ASSIGNED' ) 164 ) = %s AND ", 159 165 array ( $data->tab ) 160 166 ); … … 169 175 170 176 // key implementing language for group options (note that higher parse_quality number is worse parse, up to 6 with no email address 177 $sort_assigned_to_top = ( $data->tab == 'CATEGORY_ASSIGNED' || $data->tab == 'CATEGORY_READY' ) ? " if( inbox_defined_staff, 1, 0) DESC, " : ''; 171 178 $group_lines = 172 179 "GROUP BY BINARY … … 176 183 ) 177 184 178 ORDER BY min( if ( account_thread_latest > '', account_thread_latest, email_date_time ) ) " . $data->sort . ', min(email_date_time) ' . $data->sort . '';185 ORDER BY $sort_assigned_to_top min( if ( account_thread_latest > '', account_thread_latest, email_date_time ) ) " . $data->sort . ', min(email_date_time) ' . $data->sort . ''; 179 186 $ungroup_lines = 180 " ORDER BY IF ( account_thread_latest > '', account_thread_latest, email_date_time ) " . $data->sort . ', email_date_time ' . $data->sort . ''; 181 187 " ORDER BY $sort_assigned_to_top IF ( account_thread_latest > '', account_thread_latest, email_date_time ) " . $data->sort . ', email_date_time ' . $data->sort . ''; 188 189 /* 190 * 191 * joins to support Assigned and Ready tabs 192 * 193 */ 194 // does the user have access to the whole inbox? 195 $current_user_id = get_current_user_id(); 196 $user_subject_where_limit = $user_can_see_all ? 197 ( 198 " WHERE inbox_defined_staff " . 199 ( 200 $data->staff ? 201 $wpdb->prepare ( " = %s ", $data->staff ) : 202 " > '' " 203 ) 204 ) : 205 "WHERE inbox_defined_staff = $current_user_id "; 206 // join to identify assigned emails/subjects 207 $assigned_subject_join = 208 " 209 LEFT JOIN 210 ( 211 SELECT max(inbox_defined_reply_is_final) as subject_is_final, subject as assigned_subject 212 FROM $inbox_image_table 213 $user_subject_where_limit AND $other_where_terms 214 GROUP BY subject 215 ) assigned_subjects 216 ON subject = assigned_subject 217 "; 218 // join to identify_assigned_constituents 219 $constituent_table = $wpdb->prefix . 'wic_constituent'; 220 $user_constituent_where_limit = $user_can_see_all ? " WHERE case_assigned > '' " : "WHERE case_assigned = $current_user_id "; 221 $assigned_constituent_join = 222 " 223 LEFT JOIN 224 ( 225 SELECT c.id as constituent_id, case_assigned as constituent_assigned_staff FROM 226 $inbox_image_table INNER JOIN $constituent_table c ON c.id = assigned_constituent 227 $user_constituent_where_limit AND $other_where_terms 228 GROUP BY c.id 229 ) assigned_constituents 230 ON assigned_constituent = constituent_id 231 "; 182 232 183 233 /* … … 190 240 foreach ( $tabs_array as $tab ) { 191 241 $category = 'CATEGORY_' . strtoupper( $tab ); 192 $tabs_summary_sql .= 193 ", SUM(IF(" . 194 ( 195 'CATEGORY_ADVOCACY' == $category ? 196 "mapped_issue > 0 OR category = '$category'" : 197 "mapped_issue = 0 AND category = '$category'" 198 ) . 199 ", 1, 0)) as $category"; 200 } 242 $tabs_summary_sql .= ", SUM(IF(" ; 243 switch ( $category) { 244 case 'CATEGORY_READY': 245 $tabs_summary_sql .= 246 "( assigned_subject IS NOT NULL OR constituent_assigned_staff IS NOT NULL ) AND subject_is_final > 0, "; 247 break; 248 case 'CATEGORY_ASSIGNED': 249 $tabs_summary_sql .= 250 "( assigned_subject IS NOT NULL OR constituent_assigned_staff IS NOT NULL ) AND subject_is_final = 0, "; 251 break; 252 case 'CATEGORY_ADVOCACY': 253 $tabs_summary_sql .= 254 "( assigned_subject IS NULL AND constituent_assigned_staff IS NULL ) AND ( mapped_issue > 0 OR category = '$category' ), "; 255 break; 256 default: 257 $tabs_summary_sql .= 258 "( assigned_subject IS NULL AND constituent_assigned_staff IS NULL ) AND ( mapped_issue = 0 AND category = '$category' ), "; 259 } 260 $tabs_summary_sql .= "1, 0)) as $category"; 261 262 } 263 201 264 $tabs_count_sql = 202 265 " 203 266 SELECT count(ID) as all_inbox_messages_count $tabs_summary_sql 204 FROM $inbox_image_table 267 FROM $inbox_image_table $assigned_subject_join $assigned_constituent_join 205 268 WHERE $other_where_terms 206 269 " 207 ; 270 ; 271 208 272 $tab_counts = $wpdb->get_results ( $tabs_count_sql ); 209 273 … … 219 283 " 220 284 SELECT SQL_CALC_FOUND_ROWS 285 inbox_defined_staff, 221 286 account_thread_latest, 222 287 subject, … … 237 302 " 238 303 SELECT SQL_CALC_FOUND_ROWS 304 inbox_defined_staff, 239 305 max(account_thread_latest) as account_thread_latest, 240 306 subject, … … 254 320 ) . 255 321 " 256 FROM $inbox_image_table 322 FROM $inbox_image_table $assigned_subject_join $assigned_constituent_join 257 323 WHERE 258 324 $filter_where … … 269 335 " 270 336 ; 271 272 337 // get subjects array 273 338 $subjects_array = $wpdb->get_results( $subjects_array_sql ); … … 280 345 $loaded_object = 'grouped' == $data->mode ? 'subject lines' : 'messages'; 281 346 $filter_statement = $filter ? ' (filtered by "' . $filter . '")' : ''; 282 $view_statement = "Viewing $loaded_object, $sort_order first" . "$filter_statement. $max_count per page." ; 347 // define user limit statement 348 if ( ( ! $user_can_see_all || $data->staff ) && ( $data->tab == 'CATEGORY_ASSIGNED' || $data->tab == 'CATEGORY_READY' ) ) { 349 $user_data = get_userdata( $user_can_see_all ? $data->staff : $current_user_id ); 350 $user_display_name = $user_data->display_name ? $user_data->display_name : $user_data->user_login; 351 $user_limit_statement = ' Limited to messages assigned to ' . $user_display_name . '. '; 352 } else { 353 $user_limit_statement = ''; 354 } 355 $view_statement = "Viewing $loaded_object, $sort_order first" . "$filter_statement. $max_count per page. $user_limit_statement" ; 283 356 284 357 $count_subjects = 0; … … 305 378 $trained_legend = ''; 306 379 } 380 // mark as assigned 381 $assigned_staff_class = $subject->inbox_defined_staff ? ' inbox-assigned-staff ' : ''; 307 382 /* 308 383 * manage truncated UID list -- can't count on ability of user to set session value for group_concat_max_len over 1024 … … 335 410 '<li class = "subject-line-item from-summary' . ( 'Y' == $subject->mine ? ' includes-constituents ' : '' ) . '">' . $from_summary. '</li>' . // just display 336 411 '<li class = "subject-line-item count" title = "Message Count"><span class="inner-count">' . $uid_count . '</span></li>' . // *supports multiple UI elements* 337 '<li class = "subject-line-item subject' . $trained_class . '">' . $thread_child . $trained_legend . '<span class="actual-email-subject">' . $subject->subject . '</span><span class="wic-email-snippet">' . ( $subject->snippet ? ' -- ' : '' ).$subject->snippet .'</span></li>' . // just display412 '<li class = "subject-line-item subject' . $trained_class . $assigned_staff_class . '">' . $thread_child . $trained_legend . '<span class="actual-email-subject">' . $subject->subject . '</span><span class="wic-email-snippet">' . ( $subject->snippet ? ' -- ' : '' ).$subject->snippet .'</span></li>' . // just display 338 413 '<li class = "subject-line-item oldest" title="Date of oldest">' . $subject->oldest . '</li>' . // just display 339 414 '<li class = "subject-line-item UIDs">' . $uid_list . '</li>' . // *pass through for all processing* … … 355 430 } else { 356 431 if ( !$tab_counts[0]->all_inbox_messages_count ) { 357 $output = '<h3 class="all-clear-inbox-message">All clear -- done for now! </h3>';432 $output = '<h3 class="all-clear-inbox-message">All clear -- done for now! ' . $user_limit_statement . '</h3>'; 358 433 } else { 359 434 if ( ! in_array( $tab_display, WIC_Entity_Email_Account::get_tabs() ) ) { … … 362 437 $output = $filter ? 363 438 ( '<div id = "filtered-all-warning">No from email address or subject line containing "' . $filter . '" ' . ' in ' . $tab_display . '.</div>' ) : 364 ('<div id="inbox-congrats">All clear ' . ' in ' . $tab_display . '. </div>');439 ('<div id="inbox-congrats">All clear ' . ' in ' . $tab_display . '.' .$user_limit_statement . '</div>'); 365 440 } 366 441 } … … 404 479 ); 405 480 406 return array ( 'response_code' => true, 'output' => $return_array );481 return array ( 'response_code' => true, 'output' => $return_array ); 407 482 } 408 483 … … 419 494 } 420 495 496 421 497 private static function load_sent_outbox ( $sent_ok, $is_draft, $data ) { 498 422 499 /* 423 500 * Return looks like inbox to js … … 574 651 575 652 public static function load_done ( $dummy_id, $data ) { 653 576 654 /* 577 655 * looks like inbox to js and css … … 710 788 711 789 public static function load_saved ( $dummy_id, $data ) { 790 712 791 /* 713 792 * looks like inbox to js and css -
wp-issues-crm/trunk/php/entity/class-wic-entity-email-message.php
r2069344 r2145245 92 92 * 93 93 * if switching subject lines, always start fresh, otherwise, just carry through user's defined values 94 * for issue/pro_con/template 94 * for issue/pro_con/template as scroll through messages within group 95 95 */ 96 96 if ( $data->switching ) { 97 97 // reset all to blank 98 98 $data = self::reset_issue ( $data ); 99 // use available data from message object only under conditions conditions:99 // use available data from message object only under conditions: 100 100 if ( 101 101 $message->mapped_issue > 0 && // there is a mapped issue … … 110 110 $data->template = '<br/>' . WIC_Entity_User::get_current_user_sig(); 111 111 } 112 // always switching when go to a line with inbox defined values because load_inbox will not group if user defined values from inbox 113 // override with these values if they exist 114 $data->issue = $message->inbox_defined_issue ? $message->inbox_defined_issue : $data->issue; 115 $data->pro_con = ( $message->inbox_defined_pro_con || $message->inbox_defined_issue ) ? $message->inbox_defined_pro_con : $data->pro_con; 116 $data->template = $message->inbox_defined_reply_text ? $message->inbox_defined_reply_text : $data->template; 117 112 118 } 113 119 // validate finalized issue -- still not trashed? -- parsed email object would not know that … … 125 131 'assigned_constituent_display' => $message->assigned_constituent ? self::get_constituent_title ($message->assigned_constituent) : '', 126 132 'assigned_constituent' => $message->assigned_constituent, 127 'assigned_staff' => $message->assigned_constituent ? WIC_Entity_Constituent::get_assigned_staff ( $message->assigned_constituent ) : 0, 133 'assigned_staff' => $message->inbox_defined_staff, 134 'reply_is_final' => $message->inbox_defined_reply_is_final, 128 135 'attachments_display_line' => $attachments_display_line, 129 136 'from_email' => $message->from_email, … … 161 168 } 162 169 163 public static function load_full_message ( $ selected_page, $ID) {170 public static function load_full_message ( $ID, $selected_page ) { 164 171 if ( ! $message = WIC_DB_Email_Message_Object::build_from_id ( $selected_page, $ID ) ) { 165 172 return array ( 'response_code' => false, 'output' => "Could not find $selected_page message $ID on server." ); … … 497 504 $output = (object) array ( 498 505 'output' => $response_code ? 'Assigned constituent update OK.': 'Database error on assigned constituent update.', 499 'case_assigned' => ( $response_code && $data->assigned_constituent ) ? WIC_Entity_Constituent::get_assigned_staff( $data->assigned_constituent ) : 0,500 506 'constituent_name' => ( $response_code && $data->assigned_constituent ) ? WIC_DB_Access_WIC::get_constituent_name( $data->assigned_constituent ) : '' 501 507 ); 502 508 return array ( 'response_code' => $response_code, 'output' => $output ); 503 509 } 510 511 public static function quick_update_inbox_defined_item( $folder_uid, $data ) { 512 513 global $wpdb; 514 $inbox_image_table = $wpdb->prefix . 'wic_inbox_image'; 515 $field_to_update = 'inbox_defined_' . $data->field_to_update; 516 $sql = $wpdb->prepare ( 517 "UPDATE $inbox_image_table SET $field_to_update = %s WHERE full_folder_string = %s AND folder_uid = %d ", 518 array ( $data->field_value, $data->full_folder_string, $folder_uid ) 519 ); 520 $result = $wpdb->query ( $sql ); 521 $response_code = ( $result !== false ); 522 $output = (object) array ( 523 'output' => $response_code ? 'Assigned constituent update OK.': "Database error on $field_to_update update.", 524 ); 525 return array ( 'response_code' => $response_code, 'output' => $output ); 526 527 } 528 504 529 505 530 } -
wp-issues-crm/trunk/php/entity/class-wic-entity-email-process.php
r2096077 r2145245 23 23 * -- when sweeping, do not reply if template is '|*no_reply*|'; 24 24 */ 25 26 25 27 public static function handle_inbox_action_requests( $dummy_id, $data ) { 26 28 … … 338 340 339 341 public static function setup_settings_form() { 342 340 343 $settings_form_entity = new WIC_Entity_Email_Settings ( 'no_action', '' ); 341 344 return array ( 'response_code' => true, 'output' => $settings_form_entity->settings_form() ); -
wp-issues-crm/trunk/php/entity/class-wic-entity-email-send.php
r2096152 r2145245 461 461 } 462 462 // return error if user does not have authority to send -- possible only if security rules changed after was shown list 463 $required_capability = WIC_Admin_ Navigation::check_required_capability( 'list_send' );463 $required_capability = WIC_Admin_Access::check_required_capability( 'list_send' ); 464 464 if ( ! current_user_can( $required_capability ) ) { 465 465 return array ( 'response_code' => false, 'output' => "Contact your site administrator to upgrade your role to allow list sending."); -
wp-issues-crm/trunk/php/form/class-wic-form-email-inbox.php
r2134115 r2145245 38 38 $menu_list = '<ul class = "wic-page-option-list" >'; 39 39 foreach ( $options_array as $option ){ 40 if ( $option['value'] == 'inbox-synch' && ! $show_synch_option ) { 40 // limit options 41 if ( 42 ( $option['value'] != 'inbox' && ! current_user_can ( WIC_Admin_Access::check_required_capability( 'view_edit_unassigned' ) ) ) || 43 ( $option['value'] == 'inbox-synch' && ! $show_synch_option ) 44 ){ 41 45 continue; 42 46 } … … 72 76 '<button class="wic-form-button page-link prev " type="button" value="prev" title = "Previous Page" ><span class="dashicons dashicons-arrow-left"></span></button>' . 73 77 '<button class="wic-form-button page-link next " type="button" value="next" title = "Next Page" ><span class="dashicons dashicons-arrow-right"></span></button>' . 78 '<button class="wic-form-button" type="button" id="wic-filter-assigned-button" value="filter_assigned" title = "Filter by Assigned" ><span class="dashicons dashicons-admin-users"></span></button>' . 74 79 $data_array['subject']->form_control() . 75 80 '<button class="wic-form-button email-action-button trigger-email-process-button" id="wic-inbox-sweep-button" value="sweep" type="button" title= "Sweep already mapped"><span class="dashicons dashicons-controls-forward"></span></button>' . … … 102 107 protected static function setup_inbox( &$data_array ) { 103 108 104 $tabs_array = WIC_Entity_Email_Account::get_tabs(); 109 // limit tabs array to assigned read unless user can view_edit_unassigned 110 $tabs_array = current_user_can ( WIC_Admin_Access::check_required_capability( 'view_edit_unassigned' ) ) ? WIC_Entity_Email_Account::get_tabs() : array ( 'Assigned', 'Ready'); 111 112 // set up tab list 105 113 $tab_list = ''; 106 114 foreach ( $tabs_array as $tab ) { … … 357 365 '<div id = "wic-email-subject-header"> '. 358 366 '<div id = "wic-message-subject"></div>' . 367 '<button class="wic-form-button email-action-button trigger-email-process-button" id="wic-email-approve-button" type="button" value="approve" title="Reply ready" ><span class="dashicons dashicons-thumbs-down"></span></button>' . 359 368 '<button class="wic-form-button email-action-button trigger-email-process-button" id="wic-email-delete-button" type="button" value="delete" title="Archive" ><span class="dashicons dashicons-archive"></span></button>' . 360 369 '<button class="wic-form-button email-action-button trigger-email-process-button" id="wic-email-block-button" type="button" value="block" title = "Archive and block sender" ><span class="dashicons dashicons-warning"></span></button>' . -
wp-issues-crm/trunk/php/list/class-wic-list-activity.php
r2014297 r2145245 125 125 $buttons .= WIC_List_Parent::search_inspection_button( $wic_query ); 126 126 $buttons .= self::reassign_activities_button ( $wic_query, 'activities' ); 127 $required_capability = WIC_Admin_ Navigation::check_required_capability( 'downloads' ); // downloads127 $required_capability = WIC_Admin_Access::check_required_capability( 'downloads' ); // downloads 128 128 if (current_user_can( $required_capability ) ) { 129 129 $buttons .= self::delete_activities_button ( $wic_query, 'activities' ); -
wp-issues-crm/trunk/php/list/class-wic-list-constituent.php
r2036027 r2145245 90 90 $buttons .= WIC_List_Parent::search_inspection_button( $wic_query ); 91 91 // show delete button iff user has required capability 92 $required_capability = WIC_Admin_ Navigation::check_required_capability( 'downloads' ); // downloads92 $required_capability = WIC_Admin_Access::check_required_capability( 'downloads' ); // downloads 93 93 if (current_user_can( $required_capability ) ) { 94 94 $buttons .= $this->constituent_delete_button ( $wic_query ); -
wp-issues-crm/trunk/php/list/class-wic-list-parent.php
r2021886 r2145245 148 148 149 149 // show no button if does not have required capability 150 $required_capability = WIC_Admin_ Navigation::check_required_capability( 'list_send' );150 $required_capability = WIC_Admin_Access::check_required_capability( 'list_send' ); 151 151 if ( ! current_user_can( $required_capability ) ) { 152 152 return; -
wp-issues-crm/trunk/sql/wic_structures.sql
r2096077 r2145245 241 241 category varchar(255) NOT NULL, 242 242 extended_message_id varchar(512) NOT NULL, 243 inbox_defined_staff bigint(20) NOT NULL, 244 inbox_defined_issue bigint(20) NOT NULL, 245 inbox_defined_pro_con varchar(255) NOT NULL, 246 inbox_defined_reply_text LONGTEXT NOT NULL, 247 inbox_defined_reply_is_final tinyint(1) NOT NULL, 243 248 PRIMARY KEY (ID), 244 249 KEY in_folder (full_folder_string(100),no_longer_in_server_folder), … … 249 254 KEY account_thread_id_key (account_thread_id(100)), 250 255 KEY extended_message_id_key (extended_message_id(100)), 251 KEY from_domain_key (from_domain(100)) 256 KEY from_domain_key (from_domain(100)), 257 KEY staff_final_subject (inbox_defined_staff,inbox_defined_reply_is_final,subject(100)) 252 258 ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; 253 259 CREATE TABLE wp_wic_inbox_image_attachments ( -
wp-issues-crm/trunk/wp-issues-crm.php
r2134249 r2145245 36 36 * 37 37 */ 38 38 39 // set database version global; 39 40 global $wp_issues_crm_db_version; 40 $wp_issues_crm_db_version = '4. 4.0';41 $wp_issues_crm_db_version = '4.5.0.0.0.1'; 41 42 /* 42 43 * set js_css version global -- three possibilities:
Note: See TracChangeset
for help on using the changeset viewer.