<?php
defined( 'ABSPATH' ) or exit;   // show a blank page if try to access this file directly

require_once( EWZ_PLUGIN_DIR . "classes/ewz-base.php");
require_once( EWZ_PLUGIN_DIR . "classes/ewz-exception.php");

class Ewz_Permission {
    /* Using current_user_can('manage_options') to identify admin, who should be able to run the permissions page */

    /**
     * @var array<string>
     */
    static $ewz_caps = array(
        'ewz_can_edit_layout',        // change anything to do with a layout
        'ewz_can_assign_layout',      // assign/change the layout for a webform
        'ewz_can_edit_webform',       // change anything to do with a webform
        'ewz_can_manage_webform',     // change anything for a webform except the layout
        'ewz_can_manage_webform_L',   // manage any webform with a given layout
        'ewz_can_download_webform',   // download anything from (ie uploaded via) a webform
        'ewz_can_download_webform_L', // download anything from a webform with a given layout
        'ewz_can_see_user_data',      // allows seing user data without giving list_user capability,
                                      // ( which is often used as a proxy for "is admin", and gives too much power )

        'ewz_can_edit_rating_form',   // edit a given rating form
    );

    /**
     * Does the user have any sort of ewz permission?
     *
     * Used to determine if the ewz menu should appear at all.
     * Return true if user has any of the above listed ewz permissions,
     * or if user can manage options or edit users.
     *
     * @return  boolean
     */
    public static function has_ewz_cap() {
        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }
        if ( current_user_can( 'edit_users' ) ) {
            return true;
        }
        $uid = get_current_user_id();
        $has = false;
        foreach ( self::$ewz_caps as $cap ) {
            if ( get_user_meta( $uid, $cap, true ) ) {
                $has = true;
                break;
            }
        }
        return $has;
    }

    /**
     * Assign a permission to a user
     * Requires manage_options wordpress capability
     *
     * @param   int    $user_id  the user
     * @param   string $cap      the permission to assign ( one of $ewz_caps )
     * @param   array<string>  $value    the value to be stored ( array of (string)layout or webform id's ),
     *                       which will be serialized by update_user_meta
     * @return  mixed     int or boolean  umeta_id on update, true if no change, false on failure.
     */
    public static function add_perm( $user_id, $cap, $value ) {

        if ( current_user_can( 'manage_options' ) ||  current_user_can( 'edit_users' )) {
            $curr = get_user_meta( $user_id, $cap, true );
            if ( $curr == $value ) {
                return true;
            } else {
                return update_user_meta( $user_id, $cap, $value );
            }
        } else {
            throw new EWZ_Exception( "No authority to add a permission" );
        }
    }

    /**
     * Remove permissions for layouts or webforms that do not exist
     * In upgrade to 1.3.8 -- earlier versions did not delete permissions attached to deleted webforms or layouts
     * @return void
     */
    public static function upgrade_delete_outdated_permissions( ) {
        global $wpdb;

        if ( current_user_can( 'update_plugins' ) ) {
            $exist_layouts = Ewz_Layout::get_layout_order();
            $lperms = $wpdb->get_results( "SELECT user_id, meta_key, meta_value FROM $wpdb->usermeta " .     // no tainted data
                                          " WHERE meta_key IN ('ewz_can_edit_layout',  " .
                                          "                    'ewz_can_assign_layout', " .
                                          "                    'ewz_can_manage_webform_L', " .
                                          "                    'ewz_can_download_webform_L' " .
                                          "                   )",
                                          OBJECT );     // for deletion

            foreach ( $lperms as $key=>$perm ) {
                $changed = false;
                $layout_ids = Ewz_Base::check_unserial_arr( $perm->meta_value );
                foreach( $layout_ids as $n => $lid ){
                    if( $lid != "-1" && !array_key_exists( $lid, $exist_layouts )  ){
                        unset($layout_ids[$n]);
                        $changed = true;
                    }
                }
                if( $changed ){
                    if( $layout_ids ){
                        update_user_meta( $perm->user_id, $perm->meta_key, array_values($layout_ids ));
                    } else {
                       delete_user_meta( $perm->user_id, $perm->meta_key );
                    }
                }
            }

            $exist_webforms = Ewz_Webform::get_webform_order( false );
            $wperms = $wpdb->get_results( "SELECT user_id, meta_key, meta_value FROM $wpdb->usermeta " .     // no tainted data
                                          " WHERE meta_key IN ('ewz_can_edit_webform', " .
                                          "                     'ewz_can_manage_webform', " .
                                          "                     'ewz_can_download_webform' " .
                                          "                   )",
                                          OBJECT );      // for deletion
            foreach ( $wperms as $key=>$perm ) {
                $changed = false;
                $webform_ids = Ewz_Base::check_unserial_arr( $perm->meta_value );
                foreach( $webform_ids as $n => $wid ){
                    if( $wid != "-1" && !array_key_exists( $wid, $exist_webforms )  ){
                        unset($webform_ids[$n]);
                        $changed = true;
                    }
                }
                if( $changed ){
                    if( $webform_ids ){
                        update_user_meta( $perm->user_id, $perm->meta_key, array_values( $webform_ids ) );
                    } else {
                        delete_user_meta( $perm->user_id, $perm->meta_key );
                    }
                }
            }
        } else {
            throw new EWZ_Exception( "No authority to clean up permissions." );
        }
    }

    /**
     * Delete all permissions for the webform_id
     * Called when deleting the webform
     *
     * @param int $webform_id
     * @return void
     */
    public static function delete_webform_permissions( $webform_id ) {
        global $wpdb;
        if ( current_user_can( 'manage_options' ) || current_user_can( 'edit_users' ) || self::can_edit_webform( $webform_id )) {
/* NB: Older versions of php require no spaces after the semicolon at the end of a heredoc */
            $query = <<<EOQ
                SELECT user_id, meta_key, meta_value
                  FROM $wpdb->usermeta
                 WHERE meta_key IN ( 'ewz_can_edit_webform',
                                     'ewz_can_manage_webform',
                                     'ewz_can_download_webform'
                                   )
                   AND meta_value LIKE '%%"%d"%%'
EOQ;
/* NB: Older versions of php require no spaces after the semicolon at the end of a heredoc */
            $perms = $wpdb->get_results( $wpdb->prepare( $query, $webform_id ),  OBJECT );     // for deletion

            foreach ( $perms as $key=>$perm ) {
                $webform_ids = Ewz_Base::check_unserial_arr( $perm->meta_value );
                if ( ( $key = array_search( $webform_id, $webform_ids )) !== false ) {
                    unset( $webform_ids[$key] );
                }
                if( $webform_ids ){
                    update_user_meta( $perm->user_id, $perm->meta_key, array_values( $webform_ids ) );
                } else {
                    delete_user_meta( $perm->user_id, $perm->meta_key );
                }
            }
        } else {
            throw new EWZ_Exception( "No authority to remove a webform permission." );
        }
    }

    /**
     * Delete all permissions for the layout_id
     * Called when deleting the layout
     *
     * @param int  $layout_id
     * @return void
     */
    public static function delete_layout_permissions( $layout_id ) {
        global $wpdb;
        if ( current_user_can( 'manage_options' ) || current_user_can( 'edit_users' ) || self::can_edit_layout( $layout_id ) ) {

/* NB: Older versions of php require no spaces after the semicolon at the end of a heredoc */
            $query = <<<EOQ
                SELECT user_id, meta_key, meta_value
                  FROM $wpdb->usermeta
                 WHERE meta_key IN ( 'ewz_can_edit_layout',
                                     'ewz_can_assign_layout',
                                     'ewz_can_manage_webform_L',
                                     'ewz_can_download_webform_L'
                                   )
                   AND meta_value LIKE '%%"%d"%%'
EOQ;
/* NB: Older versions of php require no spaces after the semicolon at the end of a heredoc */
            $perms = $wpdb->get_results( $wpdb->prepare($query, $layout_id),  OBJECT );     // for deletion

            foreach ( $perms as $key=>$perm ) {
                $layout_ids = Ewz_Base::check_unserial_arr( $perm->meta_value );
                if ( ( $key = array_search( $layout_id, $layout_ids )) !== false ) {
                    unset( $layout_ids[$key] );
                }
                if( $layout_ids ){
                    update_user_meta( $perm->user_id, $perm->meta_key, array_values( $layout_ids ) );
                } else {
                    delete_user_meta( $perm->user_id, $perm->meta_key );
                }
            }
        } else {
            throw new EWZ_Exception( "No authority to remove a layout permission." );
        }
    }

    /**
     * Remove a permission
     *
     * @param   int    $user_id  the user
     * @param   string $cap      the permission to assign ( one of $ewz_caps )
     * @return  void
     */

    public static function remove_perm( $user_id, $cap ) {

        if ( current_user_can( 'manage_options' )  || current_user_can('edit_users') ) {
            $curr = get_user_meta( $user_id, $cap, true );
            if ( $curr ) {
                delete_user_meta( $user_id, $cap );
            }
        } else {
            throw new EWZ_Exception( "No authority to remove a permission." );
        }
    }

    /**
     * Get all the assigned permissions (for all users)
     *
     * For the Permissions page
     *
     * @return array of objects {'user_id':int,'meta_key':string,'meta_value':array<int>}
     *         consisting of the user_id, the permissions key and the corresponding meta_value
     *         The meta_value is itself an array of layout or webform id's
     */
    public static function get_all_ewz_permissions() {
        global $wpdb;
        if ( current_user_can( 'manage_options' ) || current_user_can( 'edit_users') ) {
            $perm = '';
            $perms = $wpdb->get_results( "SELECT user_id, meta_key, meta_value FROM $wpdb->usermeta " .     // no tainted data
                                         " WHERE meta_key like 'ewz_can_%'", OBJECT );     // for permission

            usort( $perms, array( 'Ewz_Permission', 'perm_sort' ) );
            foreach ( $perms as $key=>$perm ) {
                if( 'ewz_can_see_user_data' == $perm->meta_key ){
                    if( 1 == $perm->meta_value ){
                        $perms[$key]->meta_value = 1;
                    }
                } else {
                    $perms[$key]->meta_value = Ewz_Base::check_unserial_arr( $perm->meta_value );
                }
            }
            return $perms;
        } else {
            throw new EWZ_Exception( "No authority to view permissions" );
        }
    }

    /**
     * @param object $a
     * @param object $b
     * @return int
     */
   public static function perm_sort( $a, $b ){
        if( $a->meta_key == $b->meta_key ){
            return $a->user_id < $b->user_id ? -1 : ( $a->user_id == $b->user_id ? 0 : -1 ) ;
        }
        $ak = array_search( $a->meta_key, self::$ewz_caps);
        $bk = array_search( $b->meta_key, self::$ewz_caps );

        if( $ak === false ){
            return 1;
        }
        if( $bk === false ){
            return -1;
        }
        return ( $ak < $bk ? -1 : 1 );
    }


    /**
     * Get all the assigned permissions for input user ( or current user )
     *
     * @param  int  $user_id
     * @return array<string,array<int>> consisting of the  the permissions key and the corresponding meta_value
     *         The meta_value is itself an array of layout or webform id's
     */
    public static function get_ewz_permissions_for_user( $user_id = null ) {
        global $wpdb;
        if ( isset( $user_id ) ) {
            $user_id = (int)$user_id;
        } else {
            $user_id = get_current_user_id();
        }
        if ( current_user_can( 'manage_options' ) || current_user_can( 'edit_users') || get_current_user_id() == $user_id  ) {
            $perms = $wpdb->get_results( "SELECT meta_key, meta_value FROM $wpdb->usermeta " .                   // no tainted data
                                         " WHERE user_id = $user_id AND meta_key LIKE 'ewz_can_%'", OBJECT );  // for permission

            $allowed = array( );
            foreach ( self::$ewz_caps as $cap ) {
                $allowed[$cap] = array( );
            }
            foreach ( $perms as $perm ) {
                if( 'ewz_can_see_user_data' == $perm->meta_key && 1 == $perm->meta_value ){
                    $allowed[$perm->meta_key] = 1;
                } else {
                    $perm_arr = Ewz_Base::check_unserial_arr( $perm->meta_value );
                    foreach ( $perm_arr as $seq ) {
                        array_push( $allowed[$perm->meta_key], $seq );
                    }
                }
            }
            return $allowed;
        } else {
            throw new EWZ_Exception( "No authoritiy to view permissions");
        }
    }

    /**
     * Is there any webform the current user is allowed to see
     * (and user is thus allowed to see the webforms page)
     *
     * @return boolean
     */
    public static function can_see_webform_page() {
        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }
        $perms_for_user = self::get_ewz_permissions_for_user();
        if ( $perms_for_user['ewz_can_download_webform'] ) {
            return true;
        }
        if ( $perms_for_user['ewz_can_download_webform_L'] ) {
            return true;
        }
        if ( $perms_for_user['ewz_can_edit_webform'] ) {
            return true;
        }
        if ( $perms_for_user['ewz_can_manage_webform'] ) {
            return true;
        }
        if ( $perms_for_user['ewz_can_manage_webform_L'] ) {
            return true;
        }
        return false;
    }

    /**
     * Is the current user allowed to see at least one layout
     * (and is thus allowed to see the layouts page)
     *
     * @return boolean
     */
    public static function can_see_layout_page() {
        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }
        $perms_for_user = self::get_ewz_permissions_for_user();
        if ( $perms_for_user['ewz_can_edit_layout'] ) {
            return true;
        }
        return false;
    }

    /**
     * Is the current user allowed to edit all webforms ( needed to create a webform )
     *
     * @return boolean
     */
    public static function can_edit_all_webforms() {
        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }
        $perms_for_user = self::get_ewz_permissions_for_user();
        if ( in_array( -1, $perms_for_user['ewz_can_edit_webform'] ) ) {
            return true;
        }
        return false;
    }

    /**
     * Is the current user allowed to manage all webforms
     *
     * @return boolean
     */
    public static function can_manage_all_webforms() {
        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }
        $perms_for_user = self::get_ewz_permissions_for_user();
        if ( in_array( -1, $perms_for_user['ewz_can_manage_webform'] ) ) {
            return true;
        }
        if ( in_array( -1, $perms_for_user['ewz_can_edit_webform'] ) ) {
            return true;
        }
        return false;
    }
    /**
     * Is the current user allowed to manage/edit any webform
     *
     * @return boolean
     */
    public static function can_manage_some_webform() {
        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }
        $perms_for_user = self::get_ewz_permissions_for_user();
        if ( $perms_for_user['ewz_can_manage_webform'] )  {
            return true;
        }
        if ( $perms_for_user['ewz_can_manage_webform_L'] )  {
            return true;
        }
        if ( $perms_for_user['ewz_can_edit_webform'] )  {
            return true;
        }
        return false;
    }

    /**
     * Is the current user allowed to edit all layouts ( needed to create a layout )
     *
     * @return boolean
     */
    public static function can_edit_all_layouts() {
        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }
        $perms_for_user = self::get_ewz_permissions_for_user();
        if ( in_array( -1, $perms_for_user['ewz_can_edit_layout'] ) ) {
            return true;
        }
        return false;
    }

    /**
     * Is the current user allowed to download either from the specified webform
     * or from any form with its layout
     *
     * @param  int   $webform_id
     * @param  int   $layout_id    obtained from database if not set
     * @return boolean
     */
    public static function can_download( $webform_id, $layout_id = 0 ) {
        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }

        $perms_for_user = self::get_ewz_permissions_for_user();
        if ( in_array( -1, $perms_for_user['ewz_can_download_webform'] ) ) {
            return true;
        }
        if ( in_array( $webform_id, $perms_for_user['ewz_can_download_webform'] ) ) {
            return true;
        }
        if( 0 < $layout_id ){
            if ( in_array( $layout_id, $perms_for_user['ewz_can_download_webform_L'] ) ) {
                return true;
            }
        } else {
            if ( in_array( Ewz_Webform::get_layout_id( $webform_id ), $perms_for_user['ewz_can_download_webform_L'] ) ) {
                return true;
            }
        }
        return false;
    }

    /**
     * Is the current user allowed to see the specified webform
     *
     * @param  int      $webform_id
     * @param  int      $layout_id    obtained from database if not set
     * @return boolean
     */
    public static function can_view_webform( $webform_id, $layout_id = 0 ) {

        if ( self::can_manage_webform( $webform_id, $layout_id ) ) {
            return true;
        }
        if ( self::can_download( $webform_id, $layout_id ) ) {
            return true;
        }
        return false;
    }

    /**
     * Is the current user allowed to edit the specified webform
     *
     * @param  int    $webform_id
     * @return boolean
     */
    public static function can_edit_webform( $webform_id ) {

        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }
        $perms_for_user = self::get_ewz_permissions_for_user();
        if ( in_array( -1, $perms_for_user['ewz_can_edit_webform'] ) ) {
            return true;
        }
        if ( in_array( $webform_id, $perms_for_user['ewz_can_edit_webform'] ) ) {
            return true;
        }
        return false;
    }

    /**
     * Is the current user allowed to manage either the specified webform,
     *        any webform, or any webform with the specified webform's layout
     *
     * @param  int    $webform_id
     * @param  int    $layout_id    if 0 or null, obtained from database
     * @return boolean
     */
    public static function can_manage_webform( $webform_id , $layout_id  = 0 ) {

        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }

        $perms_for_user = self::get_ewz_permissions_for_user();
        if ( in_array( -1, $perms_for_user['ewz_can_edit_webform'] ) ) {
            return true;
        }
        if ( in_array( -1, $perms_for_user['ewz_can_manage_webform'] ) ) {
            return true;
        }

        if ( in_array( $webform_id, $perms_for_user['ewz_can_edit_webform'] ) ) {
            return true;
        }
        if ( in_array( $webform_id, $perms_for_user['ewz_can_manage_webform'] ) ) {
            return true;
        }
        if( 0 < $layout_id ){
            if ( in_array( $layout_id, $perms_for_user['ewz_can_manage_webform_L'] ) ) {
                return true;
            }
        } else {
            if ( in_array( Ewz_Webform::get_layout_id( $webform_id ), $perms_for_user['ewz_can_manage_webform_L'] ) ) {
                return true;
            }
        }
        return false;
    }

    /**
     * Is the current user allowed to edit the specified layout
     *
     * @param  int $layout_id
     *
     * @return boolean
     */
    public static function can_edit_layout( $layout_id ) {

        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }
        $perms_for_user = self::get_ewz_permissions_for_user();
        if ( in_array( -1, $perms_for_user['ewz_can_edit_layout'] ) ) {
            return true;
        }
        if ( in_array( $layout_id, $perms_for_user['ewz_can_edit_layout'] ) ) {
            return true;
        }
        return false;
    }

    /**
     * Is the current user allowed to assign the specified layout to a webform
     *        (permission is also required to edit the webform)
     *
     * @param  int       $layout_id
     * @return boolean
     */
    public static function can_assign_layout( $layout_id ) {

        if ( current_user_can( 'manage_options' ) ) {
            return true;
        }
        $perms_for_user = self::get_ewz_permissions_for_user();
        if ( in_array( -1, $perms_for_user['ewz_can_assign_layout'] ) ) {
            return true;
        }
        if ( in_array( $layout_id, $perms_for_user['ewz_can_assign_layout'] ) ) {
            return true;
        }
        return false;
    }

    /**
     * Is the current user allowed to see  user data
     * As of version 1.3.17 don't insist on list_users capability, since it is often
     *    used to indicate admin status, and may give more power than needed.
     * @return boolean
     */
    public static function can_see_users(){
        if( current_user_can( 'list_users' ) ){
            return true;
        }
        $perms_for_user = self::get_ewz_permissions_for_user();
        if ( $perms_for_user['ewz_can_see_user_data'] ) {
            return true;
        }
        return false;
    }

    /**
     * @return boolean
     */
    public static function truefunc()
    {
        return true;
    }

}
