Plugin Directory

Changeset 2145245


Ignore:
Timestamp:
08/26/2019 12:56:51 AM (6 years ago)
Author:
Will Brownsberger
Message:

version 4.5 work in progress

Location:
wp-issues-crm/trunk
Files:
1 added
30 edited

Legend:

Unmodified
Added
Removed
  • wp-issues-crm/trunk/css/dashboard.css

    r2036027 r2145245  
    6060}
    6161
     62
    6263/* used in email too */
    6364button.wic-dashboard-title {
     
    190191    font-weight: 250;
    191192    color: #999;
     193}
     194#wic-inner-dashboard-dashboard_overview {
     195    padding: 0 10px;
     196}
     197
     198table#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
     207table#wic-work-flow-status tr:nth-child(odd){
     208    background: #f0f0f0;
     209}
     210table#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;
    192248}
    193249
  • wp-issues-crm/trunk/css/email.css

    r2036326 r2145245  
    257257}
    258258
     259#wic-email-inbox-header #wic-filter-assigned-button,
    259260#wic-email-inbox-header .wic-form-button.page-link  {
    260261    margin: 0 10px 0 0;
     
    266267    vertical-align: middle;
    267268    white-space:nowrap;
    268     width: calc(98.88888% - 6.22222% - 480px);
     269    width: calc(98.88888% - 6.22222% - 510px);
    269270}
    270271
     
    333334    font-size: 1.2em;
    334335    display: inline-block;
    335     width: calc(90% - 256px);
     336    width: calc(90% - 296px);
    336337    overflow-x: hidden;
    337338    vertical-align: middle;
     
    679680    font-weight: 700;
    680681}
     682
     683.subject-line-item.subject.inbox-assigned-staff {
     684    font-weight: 700;
     685    color: green;
     686}
     687
     688
     689
    681690
    682691.subject-line-item.count {
  • wp-issues-crm/trunk/css/main.css

    r2069459 r2145245  
    130130}
    131131
     132#wic-unassigned-message {
     133    margin: 20px 0.88888%;
     134}
     135
     136#wic-unassigned-message h3 {
     137    color: red;
     138}
    132139/* body of form -- field group areas */
    133140#wic-form-body {
  • wp-issues-crm/trunk/js/constituent.js

    r1958251 r2145245  
    117117            wpIssuesCRM.loadActivityArea( true );
    118118            $("#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           
    119124        }
    120125    }
  • wp-issues-crm/trunk/js/dashboard.js

    r2036027 r2145245  
    88
    99    $( "#wp-issues-crm" ).on ( "initializeWICForm", function () {
    10         if ( $ ( "#dashboard_activity" )[0] ) {
     10        if ( $ ( "#dashboard_overview" )[0] ) {
    1111            wpIssuesCRM.loadDashboard();
    1212        }
     
    1414
    1515    // initialize directly (normally ajax triggered) in case access by get
    16     if ( $ ( "#dashboard_activity" )[0] ) {
     16    if ( $ ( "#dashboard_overview" )[0] ) {
    1717        wpIssuesCRM.loadDashboard();
    1818    }
  • wp-issues-crm/trunk/js/email-inbox.js

    r2033320 r2145245  
    5757        selectedPage = 'inbox';
    5858        selectedTab = '';
     59        selectedStaff = 0;
    5960    // define this inbox view variable as global for access in restoring undo
    6061    wpIssuesCRM.inboxAscending = {
     
    165166        // uncheck master checkbox for inbox
    166167        $( "#inbox-master-checkbox" ).prop("checked", false );
    167         /* on submit, if requests pending, notify user . . . intending to obsolesce this notice -- more annoying than helpful
    168             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         */
    174168        // set up loading UI
    175169        $( ".inbox-mode-button, .page-link" ).prop( "disabled", true ); // prevent double submits
     
    186180            page:       nextPage[selectedPage],
    187181            filter:     $( "#wic-email-inbox #subject" ).val(),
    188             tab:        selectedTab
     182            tab:        selectedTab,
     183            staff:      selectedStaff
    189184        }
    190185        wpIssuesCRM.ajaxPost( 'email_inbox', 'load_' + selectedPage,  0, inboxParms,  function( response ) {
     
    239234                }
    240235            }
     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
    241243            // alert if polling has been halted due to connection failures
    242244            if ( response.connection ) {
     
    400402        })
    401403
     404        .on( "click", "#wic-filter-assigned-button", function() {
     405            wpIssuesCRM.doFilterAssignmentPopup();
     406        })
     407
     408
    402409        .on( "click", ".page-link", function() {
    403410            if ( ! $( this ).hasClass( 'ui-state-disabled' ) ) {
     
    471478
    472479
     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
    473515
    474516}( window.wpIssuesCRM = window.wpIssuesCRM || {}, jQuery )); // end  namespace enclosure   
  • wp-issues-crm/trunk/js/email-message.js

    r2096077 r2145245  
    1414    // assigned staff
    1515    wpIssuesCRM.assignedStaff = false;
    16     wpIssuesCRM.preAssignedStaff = false; // assignment is on an existing constituent record
    1716    // parse details popup info
    1817    var parseDetails = false;
     
    2423    // cache to display transition line for reply preview image
    2524    wpIssuesCRM.replyTransitionLine = '';
     25    // message is a final draft reply from inbox
     26    wpIssuesCRM.replyFinal = 0;
     27
    2628    /*
    2729    *
     
    4850            forced_root_block : 'div',
    4951            init_instance_callback: function (editor) {
    50                 editor.on('change keyup', function (e) {
     52                editor.on('change keyup paste', function (e) {
    5153                    editor.save();
    5254                    editorChange();
     
    8486            subjectLineMoveEventCache = event;
    8587            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();
    9491        })
    9592        .on( "click", ".wic-form-button.scroll-button", wpIssuesCRM.handleScrollButton )
    9693        .on ( "click", "#wic-email-close-button", function(){
    9794            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();
    10698        })
    10799        //  info request handlers
    108         .on( "click", "#assigned-case-popup-button", wpIssuesCRM.handleAssignedCasePopupClick )
     100        .on( "click", "#assigned-case-popup-button", wpIssuesCRM.handleAssignmentPopupClick )   
    109101        .on( "click", "#parse-popup-button", wpIssuesCRM.doParseDetailsPopup )
    110102        .on( "click", "#view-issue-button", wpIssuesCRM.doIssuePeekPopup )
    111103        // change listeners ( in addition to tinymce form dirty change)
    112104        .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 )
    115107        // initialize action request handlers
    116108        // processEmail distinguishes the trigger buttons
     
    126118            setSaveButtonColor()
    127119            // 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            }
    129125        });
    130126    }
     
    222218        // get the message
    223219        wpIssuesCRM.ajaxPost( 'email_message', 'load_message_detail',  currentUID(), data,  function( response ) {
     220            /*
     221            * set approval button appearance
     222            */
     223            setThumbsUpDown( response.reply_is_final );
    224224            /*
    225225            * set up the title for the subject line group of message
     
    248248            // save assigned staff -- setting to 0 if unassigned
    249249            wpIssuesCRM.assignedStaff = response.assigned_staff;
    250             wpIssuesCRM.preAssignedStaff = response.assigned_staff > 0;
    251250            chooseStaffButtonColor();
     251            // save final draft status
     252            wpIssuesCRM.replyFinal = response.reply_is_final;
    252253            // update the scroll positions
    253254            $( "#wic-message-scroll-position" ).html( wpIssuesCRM.currentMessageVars.activeMessage + 1 )
     
    315316    *
    316317    **********/
    317 
    318     wpIssuesCRM.handleAssignedCasePopupClick = function () {
     318    wpIssuesCRM.handleAssignmentPopupClick = function () {
    319319        if ( 1 == wpIssuesCRM.currentMessageVars.countMessages ) {
    320             wpIssuesCRM.doAssignedCasePopup();
     320            wpIssuesCRM.doAssignmentPopup();
    321321        } else {
    322322            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 " +
    324324                 "(update the issue) OR just switch the inbox out of grouped mode (<b><em>1</em></b>).</p>" );
    325325        }
    326326    }
    327 
    328327    // informational popup
    329     wpIssuesCRM.doAssignedCasePopup = function() {
     328    wpIssuesCRM.doAssignmentPopup = function() {
    330329        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">' +
    332331            $( "#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>' +
    336333            '</div></div>';
    337334        dialog              = $.parseHTML( popupContent );
     
    356353        selectStaffButton.on( "change", function( event, ui ) {
    357354            wpIssuesCRM.assignedStaff = selectStaffButton.val();
     355            wpIssuesCRM.saveValueToInboxImage( 'staff', wpIssuesCRM.assignedStaff );
    358356            wpIssuesCRM.assignedCasePopupObject.fadeOut( 500, 'swing', function(){ wpIssuesCRM.assignedCasePopupObject.remove(); })     
    359357            chooseStaffButtonColor();
    360358        });
    361         if ( wpIssuesCRM.preAssignedStaff ) {
    362             selectStaffButton.next().prop( "disabled", true );
    363         } else {
    364             selectStaffButton.next().prop( "disabled", false );
    365         }
    366359    };
    367360
     
    531524        });
    532525        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 ) {     
    534527            $( "#message_view" ).html(
    535528                response.recipients_display_line +
     
    659652    *
    660653    */
     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   
    661683    wpIssuesCRM.saveConstituentToInboxImage = function(){
    662684        // disable processing buttons
    663685        processingButtons = $( "#wic-email-record-button, #wic-email-record-reply-button, #wic-inbox-sweep-button" );
     686        processingButtons.prop("disabled", true );
     687
    664688        // set up update values
    665689        var data = {
     
    669693        // do the update
    670694        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
    674696            // if in multi line mode, automatically update selected constituent_name in displayed address (since not changeable directly by user)
    675697            if ( response.constituent_name && 1 < wpIssuesCRM.currentMessageVars.countMessages ) {
     
    689711            color = '#ddd'
    690712        } else if ( wpIssuesCRM.assignedStaff > 0 ) {
    691             if ( wpIssuesCRM.preAssignedStaff ) {
    692                 color = "#333"
    693             } else {
    694                 color = 'green'
    695             }
     713            color = 'green'
    696714        } else {
    697715            color = '#999';
     
    701719
    702720    /*
     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    /*
    703752    * update current issue links
    704753    *
    705754    * 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 sequence
     755    * also fired on scroll message to set up issue link
    707756    *
    708757    * slight risk that fast user on slow system could select and then to the peek popup before this function returns, but
     
    729778        }
    730779    }
    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    //
    732799    /*
    733800    *
  • wp-issues-crm/trunk/js/email-process.js

    r2069459 r2145245  
    4141            eventVal = $( event.target ).closest( ".trigger-email-process-button" ).val();
    4242        }       
     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       
    4355        // handle sweep processing with different ui
    4456        if ( 'sweep' == eventVal ) {
     
    4658            return;
    4759        }
    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
    4962        lastDeleted     = wpIssuesCRM.activeLine.clone();
    5063        lastDeletedUids = wpIssuesCRM.activeLine.find( ".UIDs").text();
     
    425438    }
    426439
     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    }
    427460
    428461    // bulk action on delete or block from inbox level
  • wp-issues-crm/trunk/js/email-send.js

    r2096077 r2145245  
    2929
    3030    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   
    3137        // handle click on span within button 
    3238        var eventVal = $( event.target ).val();
  • wp-issues-crm/trunk/js/issue.js

    r1954508 r2145245  
    5050            wpIssuesCRM.loadActivityArea( true );
    5151        }
     52
     53        if ( !wpIssuesCRMSettings.canViewOthersAssigned ) {
     54            $( "#wic-inner-field-group-issue_management input").prop("disabled", true)
     55        }
     56
     57
    5258    }
    5359
  • wp-issues-crm/trunk/js/manage-storage.js

    r1594515 r2145245  
    5959                    } else {
    6060                        // require confirmation to go further
    61                         wpIssuesCRM.confirm (
     61                        wpIssuesCRM.confirm ( 
    6262                            function () {
    6363                                $( "#manage_storage_button" ).text( "Purging . . ." );
  • wp-issues-crm/trunk/php/admin/class-wic-admin-navigation.php

    r2033320 r2145245  
    1111    /*
    1212    *
    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
    1414    *       GET Requests
    1515    *           Wordpress menu and any page request get screened by wordpress at security levels defined in menu_setup
    1616    *               Page gets with additional parameters further controlled as to what parameters OK through do_page referencing $nav_array
    17     *           If has attachment ID will get served off admin_init to emit_stored_file before do_page -- check_security checks capability and nonce
    18     *
    19     *       All other requests run through check_security for both capability and nonce checking       
     17    *           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       
    2020    *           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)
    2326    *           Gmail API
     27    *           Upload requests (3)
    2428    *
    2529    *   THIS IS THE ONLY ROUTE TO EXECUTE PLUG-IN CODE EXCEPT FOR:
    2630    *       (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
    2832    *       (2) Interface transactions (which accept and sanitize external form records for constituents and activities)
    2933    *       (3) The metabox for post set up in WIC_Admin_Setup and other minor functions invoked wp-issues-crm.php
    3034    *       (4) ANY OTHER WORDPRESS PLUGIN THAT CHOSE TO EXECUTE PLUG-IN CODE!
    3135    *
     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    *       
    3241    */
    3342
     
    8493
    8594    /*
    86     *   All $_GET processing is mediated through Wordpress -- the menu and submenu page structure (exception gets for attachments)
     95    *   All $_GET processing is mediated through Wordpress -- the menu and submenu page structure (partial exceptions gets for attachments)
    8796    *   Two main functions -- menu_setup and do_page control this flow.
    8897    *       $nav_array is used in both and limits valid GET requests (otherwise, nav logic could attempt route to arbitrary class functions)   
    8998    *           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 requests
     99    *   
    91100    */
    92101    private $nav_array = array (
     
    95104            'default'   =>  array ( 'dashboard', 'dashboard' ),// default entity/action to invoke if $_GET does not contain permitted entity and actions
    96105            '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'    ),
    100106                                array ( 'constituent',      'id_search'     ),
    101107                                array ( 'constituent',      'new_blank_form'),
    102                                 array ( 'constituent',      'new_form'      ),
    103108                                array ( 'email_inbox',      'new_blank_form'),
    104109                                array ( 'issue',            'id_search'     ),
    105110                                array ( 'issue',            'new_blank_form'),
    106                                 array ( 'issue',            'new_form'      ),
    107111                                array ( 'search_log',       'id_search'     ),
    108112                                array ( 'search_log',       'id_search_to_form' ),
    109113                                array ( 'advanced_search',  'new_blank_form' ),
    110                                 array ( 'user',             'id_search'     ),
    111114                            ),
    112115            'mobile'    =>  true,                               // works on small screens
     
    174177            ),
    175178        );
    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 &raquo; Configure &raquo; Security</strong> determines which user roles have access to unassigned constituents and issues.</p>
     188        </div>';
    177189   
    178190    // set up menu and submenus with required capability levels defined from $nav_array -- WP is checking security on the page access
     
    180192           
    181193        $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( '' );
    183195        // need to run add setting  before add page -- too late to register if try not to do the work until on the page         
    184196        $wic_admin_settings = new WIC_Admin_Settings;
     
    204216
    205217    // 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)   
    209222        $page   = $_GET['page'];
    210223        $parms  = $this->nav_array[$page];
     
    228241            // processing allowed get strings or defaults for pages
    229242            if ( 0 < count ( $_GET ) ) {
     243                // if fully defined and OK string for page
    230244                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
    232246                    if ( in_array ( array ( $_GET['entity'] , $_GET['action'] ), $parms['permitted'] ) ) {
    233247                        $class_short = $_GET['entity'];
    234248                        $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
    236251                    }
    237252                }
     253                // if not fully defined or not OK
    238254                if ( !isset ( $class_short ) ) {
    239255                    $class_short = $parms['default'][0];
    240256                    $action = $parms['default'][1];
    241                     $args   = 'user' == $class_short ? array( 'id_requested' => get_current_user_id() ) : array();
     257                    $args   = array();
     258                    $id     = '';
    242259                }
     260               
     261                // cosmetics
    243262                if ( 'wp-issues-crm-main' == $page ) {
    244263                    $this->show_top_menu ( $class_short, $action );
    245264                }
    246                 // cosmetics
    247265                $showing_email = '';
    248266                if ( isset( $_GET['entity'] ) ) {
     
    251269                    }
    252270                }
     271               
    253272                // main action
    254273                echo '<div id="wic-main-form-html-wrapper" class="' . $showing_email . '">';   
    255274                    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                        }
    258281                    } else {
    259282                        $action[0]::{$action[1]}(); // static function in settings case -- php 7.1{}
     
    270293        if (preg_match('~MSIE|Internet Explorer~i', $ua) || (strpos($ua, 'Trident/7.0') !== false && strpos($ua, 'rv:11.0') !== false) ) {
    271294            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>';
    273296            return;
    274297        }           
    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         }
    278298
    279299    }
     
    320340    // a2b (array to button)
    321341    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   
    322350        $button_args = array (
    323351                'entity_requested'      =>  $top_menu_button[0],
     
    335363
    336364    /*
    337     *   AJAX Routing functions
     365    *   AJAX Routing functions --
    338366    *       FORM
    339367    *       Plain request
    340368    *
     369    *   Both are limited to Entity Classes
     370    *   Check security requires raised security for admin classes (identified in nav_array)
    341371    */
    342372
    343     public function route_ajax_form() { 
     373    public function route_ajax_form() {
    344374   
    345375        $this->dictionary_setup();
     
    347377        $control_array = explode( ',', $_POST['wic_form_button'] );
    348378       
    349         // check security and as a byproduct, get requesting page for use in history URL        
     379        // define terms     
    350380        $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;
    357381        $action = $control_array[1];
    358382        $id_requested = $control_array[2];
     383        $class  = 'WIC_Entity_' . $entity;
    359384        $args = array (
    360385            'id_requested'          =>  $id_requested,
    361386        );
    362387
    363         // do the request
     388        // check_security and die or do the request
    364389        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        }
    366395        $output = ob_get_clean();
    367396
     
    395424        }
    396425       
    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        }
    398439        $response->state = admin_url ( 'admin.php?page=' . $requesting_page . $resource );
    399    
     440
    400441        // send AJAX response
    401442        wp_die( json_encode ( $response ) );
     
    403444
    404445
    405     public function route_ajax () {
     446    public function route_ajax () { 
    406447   
    407448        $this->dictionary_setup();
     
    422463        */ 
    423464        $entity = $_POST['entity'];
    424         // check security -- dies on failure
    425         $this->check_security( $entity );   
    426        
    427         // respond to request ( response function terminate with wp_die and return json_encoded response )
    428465        $class = 'WIC_Entity_' . $entity;
    429466        $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        }               
    434477    }   
    435478
     
    438481    public function route_ajax_upload() {
    439482        $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        }
    442488    }
    443489    public function route_ajax_document_upload() {
    444490
    445491        $this->dictionary_setup();
    446         $this->check_security( 'constituent' );
    447 
    448492        // set values
    449493        $constituent_id = isset ( $_REQUEST['constituent_id'] ) ? $_REQUEST['constituent_id'] : 0 ;
    450494        $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        }
    453501    }
    454502    public function route_ajax_attachment_upload() {
    455503        $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        }
    459510    }
    460511   
     
    466517        // nonce is in the get string where it is expected by check_security
    467518        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            }
    470524        // nonce, along with blog ID is in the state returned
    471525        } elseif ( isset( $_GET['entity'] ) && $_GET['entity'] == 'email_oauth' && $_GET['action'] == 'redirect_from_gmail' ) {
     
    477531            // pack the nonce value for checking by check_surecity
    478532            $_GET['oauth_nonce'] = $security_array[1];
    479             self::check_security ( 'email_oauth' ); // includes nonce-checking
    480533            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            }
    481539        }
    482540    }
     
    497555        }
    498556
     557        // check access level to function
    499558        $parameters = explode (  ',', $_POST['wic-export-parameters'] );
    500         $this->check_security ( 'document' == $parameters[0] ? '' : 'download' ); // treat document downloads as part of general WP Issues CRM acccess
    501 
    502559        $class      = 'WIC_List_' . $parameters[0] . '_Export';
    503560        $method     = 'do_'       . $parameters[1] . '_download';
    504561        $type       = $parameters[2];
    505562        $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        }
    507569
    508570    }
     
    510572    *
    511573    * action to capture get requests for image and email attachment downloads
    512     *
     574    * 
    513575    */
    514576    public function emit_stored_file () {
     
    524586            return;
    525587        }
    526         // check security at the read email level
    527         self::check_security( 'email' );
    528        
     588
    529589        $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   
    614601
    615602}
  • wp-issues-crm/trunk/php/admin/class-wic-admin-settings.php

    r2134115 r2145245  
    6767            'security_settings' // settings section within page
    6868       );
     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       );
    6977           
    7078      add_settings_field(
     
    7886      add_settings_field(
    7987            'access_level_required_email', // field id
    80             'Read and reply to email', // field label
     88            'Send email', // field label
    8189            array( $this, 'access_level_required_email_callback' ), // field call back
    8290            'wp_issues_crm_settings_page', // page
     
    9199            'security_settings' // settings section within page
    92100       );
    93        
     101
    94102       // Privacy Settings
    95103      add_settings_section(
     
    501509            <table class="wp-issues-crm-stats">
    502510                <tbody>
    503                     <tr><td>Chrome -- strongly recommended and heavily tested.</td></tr>
    504                     <tr><td>Firefox -- less heavily tested; will generate 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>
    505513                    <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>
    507515            </table>
    508516        <p><em>The basic WP Issues CRM forms and lists are responsive for mobile browsers, but look better on a desktop.
     
    672680    public function security_settings_legend() {
    673681        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>' .
    676684            __(  '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>.
    677685            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>' .
     
    740748    }   
    741749
     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    }
    742769
    743770    // setting field call back 
     
    14671494      if( isset( $input['access_level_required_list_send'] ) ) {
    14681495            $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           
    14701501      // privacy settings
    14711502      if( isset( $input['all_posts_private'] ) ) {
  • wp-issues-crm/trunk/php/admin/class-wic-admin-setup.php

    r2096077 r2145245  
    286286                    'maxFileSize' => WIC_Entity_Upload_Upload::get_safe_file_size(),
    287287                    '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')),
    288290                )
    289291            );             
  • wp-issues-crm/trunk/php/db/class-wic-db-email-message-object.php

    r2028069 r2145245  
    162162            $message_object->is_my_constituent          =  $message_array[0]->is_my_constituent_guess; // translating from guess to non guess for save purposes
    163163            $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
    164170            // at time of parse, attempted to find matching constituent
    165171            if ( $message_array[0]->assigned_constituent ) {
  • wp-issues-crm/trunk/php/entity/class-wic-entity-activity.php

    r2037289 r2145245  
    129129            $buttons .= WIC_List_Activity::reassign_activities_button ( $search_parms, 'issue' );
    130130
    131             $required_capability = WIC_Admin_Navigation::check_required_capability( 'downloads' ); // downloads
     131            $required_capability = WIC_Admin_Access::check_required_capability( 'downloads' ); // downloads
    132132            if (current_user_can( $required_capability ) ) {
    133133                $buttons .= WIC_List_Activity::delete_activities_button ( $search_parms, 'issue');
  • wp-issues-crm/trunk/php/entity/class-wic-entity-dashboard.php

    r2033320 r2145245  
    2727        } else {
    2828            $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' );
    3030            $tall_list = array();
    3131        }   
    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        }
    4452        // sort the inventory
    4553        $sorted_dashboard_divs = array();
     
    7381    }
    7482
    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
    7984    public static function save_dashboard_preferences ( $dummy_id, $data ) {
    8085        return WIC_Entity_User::set_wic_user_preference ( 'wic_dashboard_config', $data );
    8186    }
    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) 
    84200    public static function dashboard_mycases( ) {
    85201       
     
    124240    }
    125241       
    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)
    127243    public static function dashboard_myissues() {
    128244       
     
    238354        );
    239355
    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   
    250370        if ( $follow_up_status !== '') {
    251371            $status_term = array (
  • wp-issues-crm/trunk/php/entity/class-wic-entity-email-account.php

    r2019577 r2145245  
    179179        *
    180180        * 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
    181184        *
    182185        * the category filter should catch every message -- messages with blank categories will not be displayed
     
    196199                'Promotions',
    197200                'Updates',
    198                 'Forums'
     201                'Forums',
     202                'Team',
     203                'Assigned',
     204                'Ready'
    199205            );
    200206        }
    201 
    202         return array ( 'General', 'Advocacy' );
     207 
     208        return array (  'General', 'Advocacy', 'Team', 'Assigned', 'Ready' );
    203209       
    204210    }
  • wp-issues-crm/trunk/php/entity/class-wic-entity-email-block.php

    r2012006 r2145245  
    113113
    114114    public static function load_block_list ( $dummy1, $dummy2 ) {
     115   
    115116        global $wpdb;
    116117        $filter_table = $wpdb->prefix . 'wic_inbox_incoming_filter';
  • wp-issues-crm/trunk/php/entity/class-wic-entity-email-inbox-parse.php

    r2070126 r2145245  
    181181        }
    182182
    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
    184184        // https://developer.wordpress.org/reference/functions/apply_filters/
    185185        $subject    =   apply_filters ( 'wp_issues_crm_local_subject_filter', $email_object->subject, $email_object ); 
     
    188188            $email_object->category = 'CATEGORY_GENERAL';
    189189        }
     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
    190194        $category   =   apply_filters ( 'wp_issues_crm_local_category_filter', $email_object->category, $email_object, 'Y' == $is_my_constituent );
    191195
  • wp-issues-crm/trunk/php/entity/class-wic-entity-email-inbox.php

    r2036102 r2145245  
    2121    // special version of this function to allow checking of settings before form display
    2222    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         }
    2923
    3024        $wic_settings = get_option( 'wp_issues_crm_plugin_options_array' );
     
    8074        *
    8175        */
    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' ;
    8680        }
    8781
     
    109103        *   (2) They all have content mapped to the same issue/pro-con, which agrees with the subject mapped result
    110104        *   (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
    111106        *   NOTE: There may or may not be a reply already assigned to the issue -- could be just recording
    112107        *
     
    141136        /*
    142137        * 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
    143140        */
    144141        $inbox_image_table = $wpdb->prefix . 'wic_inbox_image';
     
    149146            guess_mapped_pro_con = mapped_pro_con AND
    150147            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            ";
    152154
    153155        $filter = sanitize_text_field ( $data->filter );
    154156        $filter_where = self::filter_where ( $filter );
    155157
    156         // add tab selection terms -- CATEGORY_ADVOCACY is special, dynamically applied
     158        // add tab selection terms -- CATEGORY_ADVOCACY, CATEGORY_ASSIGNED, CATEGORY_READY special, dynamically applied
    157159        $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 ",
    159165                array ( $data->tab )
    160166            );
     
    169175
    170176        // 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, " : '';
    171178        $group_lines =
    172179            "GROUP BY BINARY
     
    176183                )
    177184
    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 . '';
    179186        $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            ";
    182232
    183233        /*
     
    190240        foreach ( $tabs_array  as $tab ) {
    191241            $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               
    201264        $tabs_count_sql =       
    202265            "
    203266            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
    205268            WHERE $other_where_terms           
    206269            "
    207         ;
     270        ;       
     271
    208272        $tab_counts = $wpdb->get_results ( $tabs_count_sql );
    209273
     
    219283            "
    220284            SELECT SQL_CALC_FOUND_ROWS
     285                inbox_defined_staff,
    221286                account_thread_latest,
    222287                subject,
     
    237302            "
    238303            SELECT SQL_CALC_FOUND_ROWS
     304                inbox_defined_staff,
    239305                max(account_thread_latest) as account_thread_latest,
    240306                subject,
     
    254320            ) .
    255321            "
    256             FROM $inbox_image_table
     322            FROM $inbox_image_table $assigned_subject_join $assigned_constituent_join
    257323            WHERE
    258324                $filter_where
     
    269335            "
    270336            ;
    271 
    272337        // get subjects array
    273338        $subjects_array = $wpdb->get_results( $subjects_array_sql );
     
    280345        $loaded_object = 'grouped' == $data->mode ? 'subject lines' :   'messages';
    281346        $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" ;
    283356
    284357        $count_subjects = 0;
     
    305378                    $trained_legend = '';
    306379                }
     380                // mark as assigned
     381                $assigned_staff_class =  $subject->inbox_defined_staff ? ' inbox-assigned-staff ' : '';
    307382                /*
    308383                * manage truncated UID list -- can't count on ability of user to set session value for group_concat_max_len over 1024
     
    335410                        '<li class = "subject-line-item from-summary' . ( 'Y' == $subject->mine ? ' includes-constituents ' : '' ) . '">' . $from_summary. '</li>' . // just display
    336411                        '<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 display
     412                        '<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
    338413                        '<li class = "subject-line-item oldest" title="Date of oldest">' . $subject->oldest . '</li>' . // just display
    339414                        '<li class = "subject-line-item UIDs">' . $uid_list . '</li>' . // *pass through for all processing*
     
    355430        } else {
    356431            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>';
    358433            } else {
    359434                if ( ! in_array( $tab_display, WIC_Entity_Email_Account::get_tabs() ) ) { 
     
    362437                    $output = $filter  ?
    363438                        ( '<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>');
    365440                }
    366441            }
     
    404479        );
    405480       
    406         return array ( 'response_code' => true, 'output' => $return_array  );
     481        return array ( 'response_code' => true, 'output' => $return_array );
    407482    }
    408483
     
    419494    }
    420495
     496
    421497    private static function load_sent_outbox ( $sent_ok, $is_draft, $data ) {
     498   
    422499        /*
    423500        * Return looks like inbox to js
     
    574651
    575652    public static function load_done ( $dummy_id, $data ) {
     653
    576654        /*
    577655        * looks like inbox to js and css
     
    710788   
    711789    public static function load_saved ( $dummy_id, $data ) {
     790
    712791        /*
    713792        * looks like inbox to js and css
  • wp-issues-crm/trunk/php/entity/class-wic-entity-email-message.php

    r2069344 r2145245  
    9292            *
    9393            * 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
    9595            */
    9696            if ( $data->switching ) {
    9797                // reset all to blank
    9898                $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:
    100100                if (
    101101                    $message->mapped_issue > 0 &&                                   // there is a mapped issue
     
    110110                    $data->template     = '<br/>' . WIC_Entity_User::get_current_user_sig();
    111111                }
     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               
    112118            }
    113119            // validate finalized issue -- still not trashed? -- parsed email object would not know that
     
    125131                'assigned_constituent_display'  => $message->assigned_constituent ? self::get_constituent_title ($message->assigned_constituent) : '',
    126132                '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,
    128135                'attachments_display_line'      => $attachments_display_line,
    129136                'from_email'                    => $message->from_email,
     
    161168    }
    162169   
    163     public static function load_full_message ( $selected_page, $ID ) {
     170    public static function load_full_message ( $ID, $selected_page ) {
    164171        if ( ! $message = WIC_DB_Email_Message_Object::build_from_id ( $selected_page, $ID ) ) {
    165172            return array ( 'response_code' => false, 'output' => "Could not find $selected_page message $ID on server." );
     
    497504        $output = (object) array (
    498505            '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,
    500506            'constituent_name' => ( $response_code && $data->assigned_constituent ) ? WIC_DB_Access_WIC::get_constituent_name( $data->assigned_constituent ) : ''
    501507        );
    502508        return array ( 'response_code' => $response_code, 'output' => $output );
    503509    }
     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
    504529   
    505530}
  • wp-issues-crm/trunk/php/entity/class-wic-entity-email-process.php

    r2096077 r2145245  
    2323    *                                           -- when sweeping, do not reply if template is '|*no_reply*|';
    2424    */
     25   
     26   
    2527    public static function handle_inbox_action_requests( $dummy_id, $data ) {
    2628
     
    338340
    339341    public static function setup_settings_form() {
     342
    340343        $settings_form_entity = new WIC_Entity_Email_Settings ( 'no_action', '' );
    341344        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  
    461461        }
    462462        // 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' );
    464464        if ( ! current_user_can( $required_capability ) ) {
    465465            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  
    3838        $menu_list = '<ul class = "wic-page-option-list" >';
    3939        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                ){
    4145                continue;
    4246            }
     
    7276                '<button class="wic-form-button page-link prev " type="button" value="prev" title = "Previous Page" ><span class="dashicons dashicons-arrow-left"></span></button>' .
    7377                '<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>' .
    7479                $data_array['subject']->form_control() .       
    7580                '<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>' .
     
    102107    protected static function setup_inbox( &$data_array ) {
    103108       
    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
    105113        $tab_list = '';
    106114        foreach ( $tabs_array  as $tab ) {
     
    357365            '<div id = "wic-email-subject-header"> '.
    358366                '<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>' .
    359368                '<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>' .
    360369                '<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  
    125125            $buttons .= WIC_List_Parent::search_inspection_button( $wic_query );
    126126            $buttons .= self::reassign_activities_button ( $wic_query, 'activities' );
    127             $required_capability = WIC_Admin_Navigation::check_required_capability( 'downloads' ); // downloads
     127            $required_capability = WIC_Admin_Access::check_required_capability( 'downloads' ); // downloads
    128128            if (current_user_can( $required_capability ) ) {
    129129                $buttons .= self::delete_activities_button ( $wic_query, 'activities' );
  • wp-issues-crm/trunk/php/list/class-wic-list-constituent.php

    r2036027 r2145245  
    9090            $buttons .= WIC_List_Parent::search_inspection_button( $wic_query );
    9191            // show delete button iff user has required capability
    92             $required_capability = WIC_Admin_Navigation::check_required_capability( 'downloads' ); // downloads
     92            $required_capability = WIC_Admin_Access::check_required_capability( 'downloads' ); // downloads
    9393            if (current_user_can( $required_capability ) ) {
    9494                $buttons .= $this->constituent_delete_button ( $wic_query );
  • wp-issues-crm/trunk/php/list/class-wic-list-parent.php

    r2021886 r2145245  
    148148       
    149149        // 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' );
    151151        if ( ! current_user_can( $required_capability ) ) {
    152152            return;
  • wp-issues-crm/trunk/sql/wic_structures.sql

    r2096077 r2145245  
    241241category varchar(255) NOT NULL,
    242242extended_message_id varchar(512) NOT NULL,
     243inbox_defined_staff bigint(20) NOT NULL,
     244inbox_defined_issue bigint(20) NOT NULL,
     245inbox_defined_pro_con varchar(255) NOT NULL,
     246inbox_defined_reply_text LONGTEXT NOT NULL,
     247inbox_defined_reply_is_final tinyint(1) NOT NULL,
    243248PRIMARY KEY  (ID),
    244249KEY in_folder (full_folder_string(100),no_longer_in_server_folder),
     
    249254KEY account_thread_id_key (account_thread_id(100)),
    250255KEY extended_message_id_key (extended_message_id(100)),
    251 KEY from_domain_key (from_domain(100))
     256KEY from_domain_key (from_domain(100)),
     257KEY staff_final_subject (inbox_defined_staff,inbox_defined_reply_is_final,subject(100))
    252258) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
    253259CREATE TABLE wp_wic_inbox_image_attachments (
  • wp-issues-crm/trunk/wp-issues-crm.php

    r2134249 r2145245  
    3636*
    3737*/
     38
    3839// set database version global;
    3940global $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';
    4142/*
    4243* set js_css version global -- three possibilities:
Note: See TracChangeset for help on using the changeset viewer.