<?php

/*
   Copyright (c) 2021, Daily Data, Inc. Redistribution and use in 
   source and binary forms, with or without modification, are permitted
   provided that the following conditions are met:

   * Redistributions of source code must retain the above copyright 
     notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright 
     notice, this list of conditions and the following disclaimer in the 
     documentation and/or other materials provided with the distribution.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/


// Let's make sure we have the Users class loaded.
require_once( 'Users.class.php' );


/**
 * User Permissions Login class
 * 
 * IMPORTANT: Requires a data source. See UsersDataSourceMySQLi.class.php
 * for code which provides this for MySQLi
 * 
 * UsersPermissions extends the Users class to include boolean
 * permissions.
 * 
 * @author R. W. Rodolico <rodo@unixservertech.com>
 * 
 * @version 1.0.0 (beta)
 * @copyright 2021 Daily Data, Inc.
 * 
 */

class UsersPermissions extends Users {
   
   /**
    * Extends Users::_construct
    * 
    * Adds additional fields needed for permissions. However, this
    * information is not actually used in the code, but is included here
    * for documentation and/or possible extensions
    * 
    * @param string[] $customFields Array of definition modifications to underlying structure
    */
   public function __construct( $customFields = array() ) {
      $permissions = array(
         // taken from jsfiddle.net/pynhA/2/
         'html input fields' => array(
            'permissions' => "<li><input type='checkbox' id='%s' name='%s' title='%s' ~~%s~~><label for='%s'>%s</label>\n</li>\n",
            'permissions block' => "<fieldset class='login_permissions_group'>\n<legend>%s</legend>\n<ul class='login_permissions'>\n%s\n</ul></fieldset>"
            )
      );
      $this->configuration = array_merge_recursive( $this->configuration, $permissions );
      parent::__construct( $customFields );
   } // constructor
   
   
   /**
    * Extends Users::validate
    * 
    * Loads permissions if user was validated
    * 
    * @param string $username
    * @param string $password
    * @param usersPermissionsDataSource $connection
    * 
    * @returns boolean true if username/password are correct
    */
   protected function validate( $username, $password, $connection ) {
      if ( parent::validate( $username, $password, $connection ) ) {
         $this->data['permissions'] = $connection->getPermissions( $this->data['id'] );
         return true;
      }
      return false;
   }
   
   /**
    * Extends Users::editScreen
    * 
    * Adds permissions in div of class category
    * 
    * @param usersPermissionsDataSource $connection
    * 
    * @returns string HTML display screen
    */
   public function editScreen( $connection ) {
      $divDef = "<div class='category'>\n";
      $return = array();
      $return[] = parent::editScreen( $connection );
      if ( $this->isAdmin() ) {
         $values = $connection->getFullPermissions( $this->workingOn['id'] );
         $category = array();
         foreach ( $values as $thisEntry ) {
            $field = $thisEntry['permission'];
            $temp = sprintf( $this->configuration['html input fields']['permissions'], 
                     $this->configuration['input prefix'] . $field, // id
                     $this->configuration['input prefix'] . $field, // name
                     'Place a check here to allow user to ' . $thisEntry['description'], // title
                     $field, // replaced with 'checked' if set
                     $this->configuration['input prefix'] . $field, // for=
                     $thisEntry['description'] // label
                  );
            
            $category[$thisEntry['category']][] = preg_replace( "/~~$field~~/", $thisEntry['value'] ? 'checked' : '', $temp );
         }
         
         foreach ( $category as $name => $entries ) {
            $return[] = sprintf( $this->configuration['html input fields']['permissions block'],
                           $name,  // fieldset legend
                           implode( "\n", $entries ) // all the entries from first block
                        );
         }
      }
      return implode( "\n", $return );
   }
   
   /**
    * Adds/Updates existing record
    * 
    * Extending Users::addEdit by updating the permissions
    * 
    * @param usersPermissionsDataSource $connection
    * 
    * @returns string Message saying whether the update/insert worked
    */
   protected function addEdit( $connection ) {
      $return = parent::addEdit( $connection );
      $data = array();
      // now we process all of the permissions
      if ( $return != 'Error' ) {
         if ( $this->workingOn['id'] == -1 ) {
            $newUserID = $connection->getUserID( $_REQUEST['admin_login'] );
         }
         foreach ( $this->workingOn['permissions'] as $name => $value ) {
            $htmlFieldName = $this->configuration['input prefix'] . $name;
            // if a new user, the permission is whatever is in the form
            if ( $this->workingOn['id'] == -1 && isset( $_REQUEST[$htmlFieldName] ) ) {
               $data[$name] = 1;
            // otherwise, if it is not a new user, only do an update if it has changed
            } elseif ( $this->workingOn['id'] > 0 && isset( $_REQUEST[$htmlFieldName] ) != $this->workingOn['permissions'][$name] ) {
               $data[$name] = isset( $_REQUEST[$htmlFieldName] ) ? 1 : 0;
            }
         } // foreach
         $connection->updatePermissions( ($this->workingOn['id'] == -1 ? $newUserID : $this->workingOn['id'] ),$data );
      } // if not an error
      return $return;
   } // addEdit
   
   /**
    * Initializes $this->workingOn
    * 
    * This is a modification to Users::initWorkingOn to add permissions
    */
   protected function initWorkingOn( $connection, $id ) {
      parent::initWorkingOn( $connection, $id );
      if ( ! isset( $this->workingOn['permissions'] ) ) {
         $this->workingOn['permissions'] = $connection->getPermissions( $id );
      }
   }
   
   /**
    * returns boolean as to whether the user has the passed in permission
    * 
    * If user is an admin, they automatically have all permissions, otherwise
    * we check for the permission. NOTE: the existence of a permission
    * is not validated. If a permission does not exist, will return false
    * for anyone but an admin
    * 
    * @param string $permission short form of permission
    * @returns boolean
    */
   public function isAuthorized ( $permission ) {
      return $this->isAdmin() || $this->data['permissions'][$permission];
   }

   /**
    * Adds a permission to the permissions table
    * 
    * This simply adds a permission to the permissions table, adding the 
    * category if it doesn't exist already. Since this is already written
    * in usersPermissionsDataSource, we simply call it.
    * 
    * @parameter string $category Category to place the permission into
    * @parameter string $name The short name of the permission
    * @parameter string $description The long (display) name for the permission
    * @parameter boolean $defaultValue The default value for the permission
    */
   public function addPermission ( $connection, $category, $name, $description, $defaultValue = 0 ) {
      $connection->addPermission ( $category, $name, $description, $defaultValue = 0 );
   }
   
   /**
    * returns the array of permissions
    * 
    * The keys are the keyname, the value is true or false
    * 
    * @return boolean[]
    */
   
   public function getPermissionList() {
      return $this->data['permissions'] ?? array();
   }

}

?>
