<?php
#BEGIN_LICENSE
#-------------------------------------------------------------------------
# Module: MAMS (c) 2020-2021 by CMS Made Simple Foundation
#  An add-on module for CMS Made Simple to provide useful functions
#  and commonly used gui capabilities to other modules.
#-------------------------------------------------------------------------
# A fork of:
#
# Module: FrontEndUsers (c) 2008-2014 by Robert Campbell
#         (calguy1000@cmsmadesimple.org)
#
#-------------------------------------------------------------------------
#
# CMSMS - CMS Made Simple is (c) 2006 - 2021 by CMS Made Simple Foundation
# CMSMS - CMS Made Simple is (c) 2005 by Ted Kulp (wishy@cmsmadesimple.org)
# Visit the CMSMS Homepage at: http://www.cmsmadesimple.org
#
#-------------------------------------------------------------------------
#
# This program 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.
#
# However, as a special exception to the GPL, this software is distributed
# as an addon module to CMS Made Simple.  You may not use this software
# in any Non GPL version of CMS Made simple, or in any version of CMS
# Made simple that does not indicate clearly and obviously in its admin
# section that the site was built with CMS Made simple.
#
# This program 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
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Or read it online: http://www.gnu.org/licenses/licenses.html#GPL
#
#-------------------------------------------------------------------------
#END_LICENSE
namespace MAMS;
use CmsException;
use xt_utils;

final class utils
{
    private static $_mod;
    private static $_userfiles;

    private function __construct() {}

    public static function get_mod()
    {
        if( !self::$_mod )
        {
          self::$_mod = \cms_utils::get_module(\MOD_MAMS);
        }
        
        return self::$_mod;
    }
  
  public static function generate_random_printable_string()
  {
    $code = \strtoupper(\substr(\md5(__FILE__ . \session_id() . \rand() . \microtime(TRUE)), 0, 25));
    
    return $code;
  }

    public static function using_std_consumer()
    {
        $mams = self::get_mod();
        $name = $mams->GetPreference('auth_module','');
        if('' == $name || '__BUILTIN__' == $name) { return TRUE; }
        
        return FALSE;
    }

    public static function &get_std_consumer()
    {
        $obj = new \mams_std_consumer();
        return $obj;
    }

    public static function get_auth_consumer()
    {
        $mams = self::get_mod();
        $name = $mams->GetPreference('auth_module','');
        
        if('' == $name || '__BUILTIN__' == $name)
        {
            return self::get_std_consumer();
        }

        $module = xt_utils::get_module($name);
        $res = null;
        if( \is_object($module) )
        {
            $obj = $module->GetMAMSAuthConsumer();
            
            return $obj;
        }

        return self::get_std_consumer();
    }
  
  public static function get_userlist($none = NULL)
  {
    static $list;
    if(!\is_array($list))
    {
      $list = [];
      
      // now add in mams users
      $mams = self::get_mod();
      if(\is_object($mams))
      {
        $query = new \mams_user_query;
        
        $query->add_and_opt(\mams_user_query_OPT::MATCH_NOTEXPIRED);
        $query->add_and_opt(\mams_user_query_OPT::MATCH_NOTDISABLED);
        $query->set_sortby(\mams_user_query::RESULT_SORTBY_USERNAME);
        $query->set_sortorder(\mams_user_query::RESULT_SORTORDER_ASC);
        
        $rs = $query->execute();
        
        if($rs->get_found_rows() > 0 && $none)
        {
          $list[''] = $mams->Lang('none');
        }
        
        while(!$rs->EOF())
        {
          $list[$rs->fields['id']] = $rs->fields['username'];
          $rs->MoveNext();
        }
      }
    }
    
    return $list;
  }
  
  public static function get_template($params, $key, $typename)
  {
    if($key)
    {
      $tpl = \xt_param::get_string($params, $key);
      if($tpl) { return $tpl; }
    }
    
    $tpl = \CmsLayoutTemplate::load_dflt_by_type($typename);
    
    if($tpl) { return $tpl->get_name(); }
    
    audit('', 'MAMS', 'No default template of type ' . $typename . ' found');
    
    return '';
  }
  
  public static function checkUpload($key)
  {
    $mod = self::get_mod();
    
    if(!isset($_FILES[$key]) || !isset($_FILES))
    {
      return [FALSE, $mod->Lang('error_missing_upload')];
    }
    
    $file = $_FILES[$key];
    
    if(!isset($file['name'], $file['size']) || 0 === $file['size'])
    {
      return [FALSE, $mod->Lang('error_problem_upload')];
    }
    
    if(!isset ($file['type'])) { $file['type'] = ''; }
    
   // if(!isset ($file['size'])) { $file['size'] = ''; } # redundant?
    
    if(!isset ($file['tmp_name']))
    {
      return [FALSE, $mod->Lang('error_problem_upload')];
    }
    
    # recently changed: keep an eye on the next two lines (JM)
    $allowedChars = 'a-zA-Z0-9.$%\'`\-@{}~!#()&_^+=*';
    $file['name'] = \preg_replace('/[^' . $allowedChars . ']/', '_', \str_replace([' ', '%20'], '_', $file['name']));

    // check the filename
    if(!$mod->is_allowed_upload($file['name']))
    {
      return [FALSE, $mod->Lang('error_invalidfileextension')];
    }
    
    return [TRUE];
  }
  
  public static function get_properties_for_groups($grouplist)
  {
    $out = [];
    foreach($grouplist as $gid)
    {
      $gid = (int)$gid;
      if($gid < 1) { continue; }
      
      $relations = \mams_utils::get_mod()->GetGroupPropertyRelations($gid);
      $out       = \MAMS\RRUtils::array_merge_by_name_required($out, $relations);
    }
    
    \uasort($out, ['xt_array', 'compare_elements_by_sortorder_key']);
    
    return $out;
  }
  
  public static function get_users_from_filter($filter, $offset, &$total_matches)
  {
    $limit          = $filter['limit'];
    $groupid        = $filter['group'];
    $userregex      = $filter['regex'];
    $property       = $filter['propsel'];
    $propregex      = $filter['propval'];
    $loggedinonly   = $filter['loggedinonly'];
    $sort           = $filter['sortby'];
    $disabledstatus = $filter['disabledstatus'] ?? NULL;
    
    // todo: this should go into the consumer.
    $query = new \mams_user_query();
    $query->set_result_type(\mams_user_query::RESULT_TYPE_FULL);
    
    if((int)$limit > 0) { $query->set_pagelimit($limit); }
    
    if((int)$offset > 0) { $query->set_offset($offset); }
    
    if($groupid > 0)
    {
      $query->add_and_opt(\mams_user_query_opt::MATCH_GROUPID, $groupid);
    }
    
    if($userregex)
    {
      $query->add_and_opt(\mams_user_query_opt::MATCH_USERNAME_RE, $userregex);
    }
    
    if($property)
    {
      $defn = self::get_mod()->GetPropertyDefn($property);
      $obj  = NULL;
      
      # changed recently keep an eye on this
      if(($defn['type'] == \MAMS::FIELDTYPE_CHECKBOX) && '0' === \trim($propregex))
      {
        // need to test if the property is NOT set
        $obj = new \mams_user_query_opt(\mams_user_query_opt::MATCH_NOTHASPROPERTY, $property);
      }
      
      if(!$obj && $propregex)
      {
        $obj = new \mams_user_query_opt(\mams_user_query_opt::MATCH_PROPERTY_RE, $property, $propregex);
      }
      
      if($obj) { $query->add_and_opt_obj($obj); }
    }
    
    if($loggedinonly)
    {
      $query->add_and_opt(\mams_user_query_opt::MATCH_LOGGEDIN, 1);
    }
    
    switch($disabledstatus)
    {
      case 'en':
        $query->add_and_opt(\mams_user_query_opt::MATCH_NOTDISABLED);
      break;
      case 'dis':
        $query->add_and_opt(\mams_user_query_opt::MATCH_DISABLED);
      break;
    }
    
    switch($sort)
    {
      case 'username':
      case 'username asc':
        $query->set_sortby(\mams_user_query::RESULT_SORTBY_USERNAME);
        $query->set_sortorder(\mams_user_query::RESULT_SORTORDER_ASC);
      break;
      case 'username desc':
        $query->set_sortby(\mams_user_query::RESULT_SORTBY_USERNAME);
        $query->set_sortorder(\mams_user_query::RESULT_SORTORDER_DESC);
      break;
      case 'createdate':
      case 'createdate asc':
        $query->set_sortby(\mams_user_query::RESULT_SORTBY_CREATED);
        $query->set_sortorder(\mams_user_query::RESULT_SORTORDER_ASC);
      break;
      case 'createdate desc':
        $query->set_sortby(\mams_user_query::RESULT_SORTBY_CREATED);
        $query->set_sortorder(\mams_user_query::RESULT_SORTORDER_DESC);
      break;
      case 'expires':
      case 'expires asc':
        $query->set_sortby(\mams_user_query::RESULT_SORTBY_EXPIRES);
        $query->set_sortorder(\mams_user_query::RESULT_SORTORDER_ASC);
      break;
      case 'expires desc':
        $query->set_sortby(\mams_user_query::RESULT_SORTBY_EXPIRES);
        $query->set_sortorder(\mams_user_query::RESULT_SORTORDER_DESC);
      break;
    }
    
    $rs            = $query->execute();
    $total_matches = $rs->get_found_rows();
    $data          = [];
    while(!$rs->EOF)
    {
      $data[] = $rs->fields;
      $rs->MoveNext();
    }
    
    return $data;
  }
  
  public static function resolve_preftpl_to_page($prefname, $uid, $dflt = NULL)
  {
    $prefname = \trim($prefname);
    $uid      = (int)$uid;
    
    if(!$prefname || $uid < 1) { return $dflt; }
    
    // should throw an exception maybe.
    $mams = self::get_mod();
    $res  = $mams->GetUserInfo($uid);
    
    if(!\is_array($res) || FALSE == $res[0]) { return $dflt; }
    
    $uinfo   = $res[1];
    $tpldata = $mams->GetPreference($prefname);
    
    if(!$tpldata) { return $dflt; }
    
    $smarty  = cmsms()->GetSmarty();
    $tmp_tpl = $smarty->CreateTemplate('string:' . $tpldata);
    
    $tmp_tpl->assign('username', $uinfo['username']);
    $tmp_tpl->assign('uid', $uid);
    
    $groups    = $mams->GetMemberGroupsArray($uid);
    $groupname = NULL;
    
    if(\count($groups))
    {
      $groupname = $mams->GetGroupName($groups[0]['groupid']);
    }
    
    $tmp_tpl->assign('groupname', $groupname);
    $page = $tmp_tpl->fetch();
    $dest = $mams->resolve_alias_or_id($page);
    
    if($dest) { return $dest; }
    
    return $dflt;
  }
  
  public static function save_temp_logindata($username, $password, $onlygroups)
  {
    $obj             = new \StdClass;
    $obj->username   = $username;
    $obj->password   = $password; // plain text
    $obj->onlygroups = $onlygroups;
    $key             = \md5(__CLASS__ . __FILE__);
    $data            = \xt_encrypt::encrypt($key, \serialize($obj));
    $mams            = self::get_mod();
    $mams->session_put($key, $data);
  }

    public static function retrieve_temp_logindata()
    {
        $key = \md5(__CLASS__ . __FILE__);
        $mams = self::get_mod();
        $data = $mams->session_get( $key );
        
        if( !$data ) { return []; }
        
        $mams->session_clear( $key );
        $data = \unserialize(\xt_encrypt::decrypt($key, $data ) );
        
        return $data;
    }
    
  /**
   * A hook that is triggered when a user is deleted.
   *
   * This hook checks if there are files to delete for this user and that they can be deleted.
   * If there is an error, it throws a RuntimeException.
   *
   * @param array $params The parameters passed to the hook.
   * @throws \RuntimeException If there is an insufficient permission to remove a user file or thumbnail.
   * @return array The modified parameters.
   */
  public static function hook_ondeleteuser($params)
  {
    self::$_userfiles = NULL;
    $uid              = (int)$params['id'];
    
    if($uid < 1) { return $params; }
    
    $files = self::get_mod()->GetUserFilesFull($uid, TRUE);
    
    if(!$files) { return $params; }
    
    $path = self::get_mod()->get_upload_dirname();
    
    foreach($files as $file)
    {
      $fn = "$path/$file";
      $tn = "$path/thumb_{$file}";
      
      if(\is_file($fn) && !\is_writable($fn))
      {
        throw new \RuntimeException("Insufficient permission to remove user file $file");
      }
      
      if(\is_file($tn) && !\is_writable($tn))
      {
        throw new \RuntimeException("Insufficient permission to remove user thumbnail for $file");
      }
    }
    
    self::$_userfiles = $files;
    
    return $params;
  }
  
  /**
   * A hook that is triggered when a user is deleted.
   *
   * This hook checks if there are files to delete for this user and that they can be deleted.
   *
   * @param array $params The parameters passed to the hook.
   * @return array The modified parameters.
   */
  public static function hook_afterdeleteuser($params)
  {
    $uid = (int)$params['id'];
    
    if($uid < 1) { return $params; }
    if(!self::$_userfiles) { return $params; }
    
    $files = self::$_userfiles;
    $path  = self::get_mod()->get_upload_dirname();
    
    foreach($files as $file)
    {
      $fn = "$path/$file";
      $tn = "$path/thumb_{$file}";
      
      if(\is_file($fn)) { @\unlink($fn); }
      
      if(\is_file($tn)) { @\unlink($tn); }
    }
    
    self::$_userfiles = NULL;
    
    return $params;
  }
  
  public static function log($message = '')
  {
    $message = empty($message) ? '[empty message]' : $message;
    $root = \dirname(__FILE__, 4);
    $path = $root;
    $file_name = 'MAMS.log';
    $fn = cms_join_path($path, $file_name);
    
    $datetime = \date('Y-m-d H:i:s');
    $text     = $datetime . ' :: ';
    $text     .= $message . PHP_EOL;
    $tmp_max  = \ini_set('log_errors_max_len', 2048);
    \error_log($text, 3, $fn);
    // reset to the old value;
    \ini_set('log_errors_max_len', $tmp_max);
  }
  
  /**
   *
   * @param      $email
   * @param int  $uid
   * @param bool $check_existing
   *
   * @return array
   */
  public static function is_valid_email_address($email, $uid = -1, $check_existing = FALSE)
  {
    $module = self::get_mod();
    $result = [];
    
    if(!is_email($email))
    {
      $result[0] = FALSE;
      $result[1] = $module->Lang('error_improperemailformat');
      
      return $result;
    }
    
    $db = cmsms()->GetDb();
    
    if($check_existing)
    {
      if($module->GetPreference('username_is_email'))
      {
        $q    = 'SELECT username FROM ' . CMS_DB_PREFIX . 'module_mams_users WHERE username = ?';
        $parm = [$email];
        
        if($uid > -1)
        {
          $q      .= ' AND id != ?';
          $parm[] = $uid;
        }
        
        $dbresult = $db->Execute($q, $parm);
        
        if($dbresult && $dbresult->RecordCount())
        {
          $result[0] = FALSE;
          $result[1] = $module->Lang('error_emailalreadyused');
          
          return $result;
        }
      }
      else if(!$module->GetPreference('allow_duplicate_emails'))
      {
        $q     = "SELECT data FROM " . CMS_DB_PREFIX . "module_mams_propdefn," .
                 CMS_DB_PREFIX . "module_mams_properties WHERE name=title AND type=2 AND data LIKE ?";
        $parms = [$email];
        
        if($uid > -1)
        {
          $q       .= ' AND userid != ?';
          $parms[] = $uid;
        }
        
        $dbresult = $db->Execute($q, [$email]);
        
        if($dbresult && $dbresult->RecordCount())
        {
          $result[0] = FALSE;
          $result[1] = $module->Lang('error_emailalreadyused');
          
          return $result;
        }
      }
    }
    
    $result[0] = TRUE;
    
    return $result;
  }
  
  /**
   * Convert a string of parameters into an associative array.
   * Used mainly to convert a pretty url
   * with a format of /param1/value1/param2/value2/(...)
   * into an array of parameters.
   * The $params array should contain the key 'prms'
   * with the value being the string to convert.
   * The key 'prms' will then be unset before the $params are returned
   *
   * @param array|null $params - The array of existing parameters.
   * @param bool|null $override - Flag indicating whether to override existing parameters.
   *
   * @return array - The updated array of parameters.
   */
  public static function prms_to_params($params = NULL, $override = NULL)
  {
    $prms     = $params['prms'] ?? [];
    $params   = empty($params) ? [] : $params;
    $override = $override ?? FALSE;
    $pairs    = \explode('/', $prms);
    
    unset($params['prms']);
    
    for ($i = 0, $iMax = \count($pairs); $i < $iMax; $i += 2)
    {
      $key = $pairs[$i];
      $value = $pairs[$i + 1] ?? NULL;
      
      if(!$override && isset($params[$key])) { continue; }
      
      $params[$key] = $value;
    }
    
    return $params;
  }
  
  
  
  /***
   * temporary functions
   */
  
} // class

#
# EOF
#
