CORE-POS/IS4C

View on GitHub
fannie/classlib2.0/FanniePage.php

Summary

Maintainability
D
2 days
Test Coverage
F
54%
<?php
/*******************************************************************************

    Copyright 2012 Whole Foods Co-op

    This file is part of CORE-POS.

    IT CORE is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    IT CORE is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    in the file license.txt along with IT CORE; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*********************************************************************************/

/**
  @class FanniePage
  Class for drawing screens
*/
class FanniePage extends \COREPOS\common\ui\CorePage 
{
    /**
      Page uses newer bootstrap based UI
    */
    public $themed = true;

    /** force users to login immediately */
    protected $must_authenticate = false;
    /** name of the logged in user (or False is no one is logged in) */
    protected $current_user = false;
    /** list of either auth_class(es) or array(auth_class, start, end) tuple(s) */
    protected $auth_classes = array();

    protected $title = 'Page window title';
    protected $header = 'Page displayed header';

    public $default_db = false;

    /** wrapper around $_SESSION superglobal **/
    protected $session;

    /**
     * Validated means the request includes a random token matching the session token
     * Auto validation means the individual page does not want to manage
     * token checks manually.
     */
    protected $validated = false;
    protected $autoValidate = false;

    /**
      Include javascript necessary to integrate linea
      scanner device
    */
    protected $enable_linea = false;

    public function __construct()
    {
        $this->start_timestamp = microtime(true);

        $auth_default = FannieConfig::config('AUTH_DEFAULT', false);
        $coop_id = FannieConfig::config('COOP_ID');
        if ($auth_default && !$this->must_authenticate) {
            $this->must_authenticate = $auth_default;
        }
        if (isset($coop_id) && $coop_id == 'WEFC_Toronto') {
            $this->auth_classes[] = 'admin';
        }

        $path = realpath(__DIR__ . '/../');
        $this->session = new COREPOS\common\NamedSession($path);
    }

    public function preprocess()
    {
        $ret = parent::preprocess();
        /**
          Global setting overrides default behavior
          to force the menu to appear.
        */
        if ($this->config->get('WINDOW_DRESSING')) {
            $this->window_dressing = true;
        }

        return $ret;
    }

    protected function getMessages()
    {
        if (!$this->current_user || !is_object($this->connection)) {
            return '';
        }
        $uid = FannieAuth::getUID($this->current_user);
        $msg = new COREPOS\Fannie\API\auth\Notifications($this->connection, $this->config);
        $msgs = $msg->getMessages($uid);
        $ret = '';
        foreach ($msgs as $m) {
            $ret .= '<div class="alert alert-info">';
            if ($m['url']) {
                $ret .= '<a href="' . $url . '">';
            }
            $ret .= $m['message'];
            if ($m['url']) {
                $ret .= '</a>';
            }
            $ret .= '</div>';
        }

        return $ret;
    }

    /**
      Get the standard header
      @return An HTML string
    */
    public function getHeader()
    {
        $url = $this->config->get('URL');
        ob_start();
        $page_title = $this->title;
        $header = $this->header;
        $headerConfig = $this->config;
        $BACKEND_NAME = $this->config->get('BACKEND_NAME', 'Fannie');
        if ($this->themed) {
            include(dirname(__FILE__) . '/../src/header.bootstrap.html');
            $this->addJQuery();
            if (!$this->addBootstrap()) {
                echo '<em>Warning: bootstrap does not appear to be installed. Try running composer update</em>';
            }
            if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
                // windows has trouble with symlinks
                $this->addScript($url . 'src/javascript/jquery-ui-1.10.4/js/jquery-ui-1.10.4.min.js');
            } else {
                $this->addScript($url . 'src/javascript/jquery-ui.js');
            }
            $this->addScript($url . 'src/javascript/calculator.js');
            $this->addScript($url . 'src/javascript/core.js?date=20200312');
            if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
                // windows has trouble with symlinks
                $this->addCssFile($url . 'src/javascript/jquery-ui-1.10.4/css/smoothness/jquery-ui.min.css?id=20140625');
            } else {
                $this->addCssFile($url . 'src/javascript/jquery-ui.css?id=20140625');
            }
            $this->addCssFile($url . 'src/javascript/fontawesome-5.15.3/css/all.min.css');
            $this->addCssFile($url . 'src/css/configurable.php');
            $this->addCssFile($url . 'src/css/core.css');
            $this->addCssFile($url . 'src/css/print.css');
            $this->add_onload_command('standardFieldMarkup();');
            $this->addOnloadCommand("logJsErrors('{$url}');");
        } else {
            include(dirname(__FILE__) . '/../src/header.html');
        }

        if ($this->enable_linea) {
            $this->addScript($url . 'src/javascript/linea/cordova-2.2.0.js');
            $this->addScript($url . 'src/javascript/linea/ScannerLib-Linea-2.0.0.js');
            if (strpos(filter_input(INPUT_SERVER, 'HTTP_USER_AGENT'), 'iPod touch')) {
                $this->addScript($url . 'src/javascript/linea/WebHub.js');
            }
            $this->addScript($url . 'src/javascript/linea/core.js?date=20200311');
        }

        echo $this->getMessages();

        return ob_get_clean();
    }

    /**
      Add css and js files required for bootstrap.
      If a version installed via composer is present, that
      is the version used.
      @return [boolean] success
    */
    public function addBootstrap()
    {
        $url = $this->config->get('URL');
        $path1 = dirname(__FILE__) . '/../src/javascript/composer-components/';
        $path2 = dirname(__FILE__) . '/../src/javascript/';
        if (file_exists($path1 . 'bootstrap/js/bootstrap.min.js')) {
            $this->addScript($url . 'src/javascript/composer-components/bootstrap/js/bootstrap.min.js');
        } elseif (file_exists($path2 . 'bootstrap/js/bootstrap.min.js')) {
            $this->addScript($url . 'src/javascript/bootstrap/js/bootstrap.min.js');
        } else {
            return false; // bootstrap not found!
        }

        return true;
    }

    /**
      Add jquery js file to the page
      If present, the version installed via composer is used
      @return [boolean] success
    */
    public function addJQuery()
    {
        $url = $this->config->get('URL');
        $path1 = dirname(__FILE__) . '/../src/javascript/composer-components/';
        $path2 = dirname(__FILE__) . '/../src/javascript/';
        if (file_exists($path1 . 'jquery/jquery.min.js')) {
            $this->addFirstScript($url . 'src/javascript/composer-components/jquery/jquery.min.js');
        } elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
            // windows has trouble with symlinks
            $this->addFirstScript($url . 'src/javascript/jquery-1.11.1/jquery-1.11.1.min.js');
        } elseif (file_exists($path2 . 'jquery.js')) {
            $this->addFirstScript($url . 'src/javascript/jquery.js');
        } else {
            return false;
        }

        return true;
    }

    /**
      Get the standard footer
      @return An HTML string
    */
    public function getFooter()
    {
        $FANNIE_AUTH_ENABLED = $this->config->get('AUTH_ENABLED');
        $FANNIE_URL = $this->config->get('URL');
        ob_start();
        $START_TIMESTAMP = $this->start_timestamp;
        if ($this->themed) {
            include(dirname(__FILE__) . '/../src/footer.bootstrap.html');
            $modal = $this->helpModal();
            if ($modal) {
                echo "\n" . $modal . "\n";
            }
        } else {
            include(dirname(__FILE__) . '/../src/footer.html');
        }

        return ob_get_clean();
    }

    protected function lineaJS()
    {
        ob_start();
        ?>
function lineaBarcode(upc, selector, callback) {
    upc = upc.substring(0,upc.length-1);
    if ($(selector).length > 0){
        $(selector).val(upc);
        if (typeof callback === 'function') {
            callback();
        } else {
            $(selector).closest('form').submit();
        }
    }
}
var IPC_PARAMS = { selector: false, callback: false };
function ipcWrapper(upc, typeID, typeStr) {
    lineaBarcode(upc, IPC_PARAMS.selector, IPC_PARAMS.callback);
}
/**
  Enable linea scanner on page
  @param selector - jQuery selector for the element where
    barcode data should be entered
  @param callback [optional] function called after
    barcode scan

  If the callback is omitted, the parent <form> of the
  selector's element is submitted.
*/
function enableLinea(selector, callback)
{
    Device = new ScannerDevice({
        barcodeData: function(data, type) {
            var upc = data.substring(0,data.length-1);
            if (upc.length == 6) {
                upc = '0' + upc;
            }
            if ($(selector).length > 0){
                $(selector).val(upc);
                if (typeof callback === 'function') {
                    callback();
                } else {
                    $(selector).closest('form').submit();
                }
            }
        },
        magneticCardData: function (track1, track2, track3){
        },
        magneticCardRawData: function (data){
        },
        buttonPressed: function (){
        },
        buttonReleased: function (){
        },
        connectionState: function (state){
        }
    });
    ScannerDevice.registerListener(Device);

    if (typeof WebBarcode == 'object') {
        WebBarcode.onBarcodeScan(function(ev) {
            var data = ev.value;
            lineaBarcode(data, selector, callback);
        });
    }

    // for webhub
    WebHub.Settings.set({
        barcodeFunction: function (upc, typeID, typeStr) {
            upc = upc.substring(0,upc.length-1);
            if ($(selector).length > 0){
                $(selector).val(upc);
                if (typeof callback === 'function') {
                    callback();
                } else {
                    $(selector).closest('form').submit();
                }
            }
        }
    });

    function lineaSilent()
    {
        if (typeof cordova.exec != 'function') {
            setTimeout(lineaSilent, 100);
        } else {
            if (Device) {
                Device.setScanBeep(false, []);
            }
        }
    }
    lineaSilent();
}
        <?php

        return ob_get_clean();
    }

    /**
      Send user to login page
    */
    public function loginRedirect()
    {
        $redirect = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
        $url = $this->config->get('URL') . 'auth/ui/loginform.php';
        header('Location: '.$url.'?redirect='.$redirect);
    }

    public function login_redirect()
    {
        $this->loginRedirect();
    }

    /**
      Check if the user is logged in
    */
    public function checkAuth()
    {
        foreach($this->auth_classes as $class) {
            $try = false;
            if (is_array($class) && count($class) == 3) {
                $try = FannieAuth::validateUserQuiet($class[0],$class[1],$class[2]);
            } else {
                $try = FannieAuth::validateUserQuiet($class);
            }
            if ($try) {
                $this->current_user = $try;
                return true;
            }
        }
        $try = FannieAuth::checkLogin();
        if ($try && empty($this->auth_classes)) {
            $this->current_user = $try;
            return true;
        }

        return false;
    }

    public function check_auth()
    {
        return $this->checkAuth();
    }

    /**
      Helper method to wrap helpContent()
      in markup for a bootstrap modal dialog.
    */
    protected function helpModal()
    {
        $help = $this->helpContent();
        if (!$help) {
            return false;
        }
        $BACKEND_NAME = $this->config->get('BACKEND_NAME', 'Fannie');
        $this->addOnloadCommand("\$('.modal').draggable({handle:'.modal-header'});\n");

        return '
            <div class="modal" id="help-modal" role="modal">
                <div class="modal-dialog modal-lg">
                    <div class="modal-content">
                        <div class="modal-header">
                            <button type="button" class="close close-btn" data-dismiss="modal">
                                <span aria-hidden="true">&times;</span><span class="sr-only">Close</span>
                            </button>
                            <h4>' . 
                                preg_replace('/^Fannie(.*)$/', $BACKEND_NAME . '$1', $this->title) . '
                            </h4>
                        </div>
                        <div class="modal-body">' . $help . '</div>
                        <div id="help-feedback" class="container col-sm-6 collapse">
                            <input type="hidden" name="page" class="help-feedback" value="' . filter_input(INPUT_SERVER, 'PHP_SELF') . '" />
                            <div class="form-group">
                                <label>Email</label>
                                <input type="email" name="email" class="help-feedback form-control" 
                                    placeholder="Optional; if blank you won\'t get a response" />
                            </div>
                            <div class="form-group">
                                <label>How could "Help" on this page be improved</label>
                                <textarea name="comments" class="form-control help-feedback" rows="10"></textarea>
                            </div>
                            <div class="form-group">
                                <button type="button" class="btn btn-default" onclick="
                                    $.ajax({ method: \'post\', url: \'' . $this->config->URL . 'admin/HelpPopup.php\',
                                        data: $(\'.help-feedback\').serialize() })
                                    .always(function() { $(\'#help-feedback\').hide(); $(\'#help-feedback-done\').show(); });
                                ">
                                    Send Feedback
                                </button>
                            </div>
                        </div>
                        <div id="help-feedback-done" class="collapse">
                            <div class="alert alert-success">Feedback submitted</div>
                        </div>
                        <div class="modal-footer">
                            <button type="button" id="feedback-btn" class="btn btn-default" 
                                onclick="$(\'#help-feedback\').show();$(this).hide();">Feedback</button>
                            <button type="button" class="btn btn-default" data-dismiss="modal"
                                onclick="var helpWindow=window.open(\''. $this->config->URL . 'admin/HelpPopup.php\',
                                \'CORE Help\', \'height=500,width=300,scrollbars=1,resizable=1\');"
                                id="popout-btn">Pop Out</button>
                            <button type="button" class="btn btn-default close-btn" data-dismiss="modal">Close</button>
                        </div>
                    </div>
                </div>
            </div>';
    }

    /**
      User-facing help text explaining how to 
      use a page.
      @return [string] html content
    */
    public function helpContent()
    {
        return '<!-- need doc -->
            <h3>Oh no!</h3>
            <p>This page hasn\'t been documented</p>';
    }

    public function preFlight()
    {
        if (!($this->config instanceof FannieConfig)) {
            $this->config = FannieConfig::factory();
        }

        if (!$this->checkAuth() && $this->must_authenticate) {
            $this->loginRedirect();
            exit;
        }

        if (!isset($_COOKIE['__perma'])) {
            $perma = uniqid('', true);
            setcookie('__perma', $perma, time()+60*60*24*999,'/');
        } elseif (isset($_COOKIE['__perma']) && !isset($this->session->__perma)) {
            $redis = $this->getRedis();
            if ($redis) {
                $vals = $redis->get($_COOKIE['__perma']);
                if ($vals) {
                    $vals = unserialize($vals);
                    foreach ($vals as $k => $v) {
                        $this->session->{$k} = $v;
                    }
                    $this->session->__perma = true;
                }
            }
        }

        if (FormLib::get('_token_') && isset($this->session->csrfToken)) {
            $this->validated = FormLib::get('_token_') === $this->session->csrfToken;
            if (!$this->validated && $this->autoValidate) {
                echo 'HTTP 400';
                exit;
            }
        } elseif (!isset($this->session->csrfToken)) {
            $this->session->csrfToken = COREPOS\common\FormLib::generateToken();
        }
        $this->addOnloadCommand("if (typeof appendTokens == 'function') { appendTokens('{$this->session->csrfToken}'); }");
    }

    protected function postFlight()
    {
        if ($this->session->changed() && isset($_COOKIE['__perma'])) {
            $redis = $this->getRedis();
            if ($redis) {
                $data = $this->session->getPerma();
                $data = serialize($data);
                $res = $redis->setEx($_COOKIE['__perma'], 60*60*24*30, $data);
            }
        }
    }

    protected function getRedis()
    {
        $conf = $this->config->get('PLUGIN_SETTINGS');
        $redis_host = isset($conf['SatelliteRedis']) ? $conf['SatelliteRedis'] : '';
        if ($redis_host === '') {
            return false;
        }
        try {
            $redis = new \Predis\Client($redis_host);
            return $redis;
        } catch (Exception $ex) {
            return false;
        }
    }

    public function setPermissions($p)
    {
        $this->auth_classes = array($p);
    }

    protected $twig = null;
    public function setTwig($t)
    {
        $this->twig = $t;
    }
}