Example #1
0
function Q_before_Q_autoload($params, &$filename)
{
    // TODO: Integrate with Composer
    // require "vendor/autoload.php", implementing PSR-4
    // look at optimizations:
    // Workaround for Zend Framework, because it has require_once
    // in various places, instead of just relying on autoloading.
    // As a result, we need to add some more directories to the path.
    // The trigger is that we will be loading a file beginning with "classes/Zend".
    // This is handled natively inside this method for the purpose of speed.
    $paths = array('classes/Zend/' => 'classes');
    static $added_paths = array();
    foreach ($paths as $prefix => $new_path) {
        if (substr($filename, 0, strlen($prefix)) != $prefix) {
            continue;
        }
        if (isset($added_paths[$new_path])) {
            break;
        }
        $abs_filename = Q::realPath($filename);
        $new_path_parts = array();
        $prev_part = null;
        foreach (explode(DS, $abs_filename) as $part) {
            if ($prev_part == 'classes' and $part == 'Zend') {
                break;
            }
            $prev_part = $part;
            $new_path_parts[] = $part;
        }
        $new_path = implode(DS, $new_path_parts);
        $paths = array($new_path, get_include_path());
        set_include_path(implode(PS, $paths));
        $added_paths[$new_path] = true;
    }
}
Example #2
0
 /**
  * Saves a file, usually sent by the client
  * @method save
  * @static
  * @param {array} $params 
  * @param {string} [$params.data] the file data
  * @param {string} [$params.path="uploads"] parent path under web dir (see subpath)
  * @param {string} [$params.subpath=""] subpath that should follow the path, to save the image under
  * @param {string} [$params.name] override the name of the file, after the subpath
  * @param {string} [$params.skipAccess=false] if true, skips the check for authorization to write files there
  * @param {boolean} [$params.audio] set this to true if the file is an audio file
  * @return {array} Returns array containing ($name => $tailUrl) pair
  */
 static function save($params)
 {
     if (empty($params['data'])) {
         throw new Q_Exception(array('field' => 'file'), 'data');
     }
     // check whether we can write to this path, and create dirs if needed
     $data = $params['data'];
     $audio = $params['audio'];
     $path = isset($params['path']) ? $params['path'] : 'uploads';
     $subpath = isset($params['subpath']) ? $params['subpath'] : '';
     $realPath = Q::realPath(APP_WEB_DIR . DS . $path);
     if ($realPath === false) {
         throw new Q_Exception_MissingFile(array('filename' => APP_WEB_DIR . DS . $path));
     }
     $name = isset($params['name']) ? $params['name'] : 'file';
     if (!preg_match('/^[\\w.-]+$/', $name)) {
         $info = pathinfo($name);
         $name = Q_Utils::normalize($info['filename']) . '.' . $info['extension'];
     }
     // TODO: recognize some extensions maybe
     $writePath = $realPath . ($subpath ? DS . $subpath : '');
     $lastChar = substr($writePath, -1);
     if ($lastChar !== DS and $lastChar !== '/') {
         $writePath .= DS;
     }
     $skipAccess = !empty($params['skipAccess']);
     Q_Utils::canWriteToPath($writePath, $skipAccess ? null : true, true);
     file_put_contents($writePath . $name, $data);
     $size = filesize($writePath . $name);
     $tailUrl = $subpath ? "{$path}/{$subpath}/{$name}" : "{$path}/{$name}";
     /**
      * @event Q/file/save {after}
      * @param {string} user the user
      * @param {string} path the path in the url
      * @param {string} subpath the subpath in the url
      * @param {string} name the actual name of the file
      * @param {string} writePath the actual folder where the path is written
      * @param {string} data the data written to the file
      * @param {string} tailUrl consists of $path/[$subpath/]$name
      * @param {integer} size the size of the file that was written
      * @param {boolean} skipAccess whether we are skipping access checks
      * @param {boolean} audio whether the file is audio
      */
     Q::event('Q/file/save', compact('path', 'subpath', 'name', 'writePath', 'data', 'tailUrl', 'size', 'skipAccess', 'audio'), 'after');
     return array($name => $tailUrl);
 }
Example #3
0
 /**
  * Saves a file, usually sent by the client
  * @method save
  * @static
  * @param {array} $params 
  * @param {string} [$params.data] the file data
  * @param {string} [$params.path="uploads"] parent path under web dir (see subpath)
  * @param {string} [$params.subpath=""] subpath that should follow the path, to save the image under
  * @param {string} [$params.name] override the name of the file, after the subpath
  * @param {string} [$params.skipAccess=false] if true, skips the check for authorization to write files there
  * @return {array} Returns array containing ($name => $tailUrl) pair
  */
 static function save($params)
 {
     if (empty($params['data'])) {
         throw new Q_Exception(array('field' => 'file'), 'data');
     }
     // check whether we can write to this path, and create dirs if needed
     $data = $params['data'];
     $path = isset($params['path']) ? $params['path'] : 'uploads';
     $subpath = isset($params['subpath']) ? $params['subpath'] : '';
     $realPath = Q::realPath(APP_WEB_DIR . DS . $path);
     if ($realPath === false) {
         throw new Q_Exception_MissingFile(array('filename' => APP_WEB_DIR . DS . $path));
     }
     $name = isset($params['name']) ? $params['name'] : 'file';
     if (!preg_match('/^[\\w.-]+$/', $name)) {
         $info = pathinfo($name);
         $name = Q_Utils::normalize($info['filename']) . '.' . $info['extension'];
     }
     // TODO: recognize some extensions maybe
     $writePath = $realPath . ($subpath ? DS . $subpath : '');
     $lastChar = substr($writePath, -1);
     if ($lastChar !== DS and $lastChar !== '/') {
         $writePath .= DS;
     }
     $throwIfNotWritable = empty($params['skipAccess']) ? true : null;
     Q_Utils::canWriteToPath($writePath, $throwIfNotWritable, true);
     file_put_contents($writePath . DS . $name, $data);
     $tailUrl = $subpath ? "{$path}/{$subpath}/{$name}" : "{$path}/{$name}";
     /**
      * @event Q/file/save {after}
      * @param {string} user
      * @param {string} path
      * @param {string} subpath
      * @param {string} name
      * @param {string} writePath
      * @param {string} data
      */
     Q::event('Q/file/save', compact('path', 'subpath', 'name', 'writePath', 'data', 'tailUrl'), 'after');
     return array($name => $tailUrl);
 }
Example #4
0
 /**
  * Saves parameters to a file
  * @method save
  * @param {string} $filename Name of file to save to. If tree was loaded, you can leave this blank to update that file.
  * @param {array} [$array_path=array()] Array of keys identifying the path of the config subtree to save
  * @return {boolean} Returns true if saved, otherwise false;
  **/
 function save($filename = null, $array_path = array(), $prefix_path = null)
 {
     if (empty($filename) and !empty($this->filename)) {
         $filename = $this->filename;
     }
     if (!($filename2 = Q::realPath($filename))) {
         $filename2 = $filename;
     }
     if (empty($array_path)) {
         $array_path = array();
         $toSave = $this->parameters;
     } else {
         $array_path[] = null;
         $toSave = call_user_func_array(array($this, 'get'), $array_path);
     }
     if (is_null($prefix_path)) {
         $prefix_path = $array_path;
     }
     $prefix_path = array_reverse($prefix_path);
     foreach ($prefix_path as $ap) {
         if ($ap) {
             $toSave = array($ap => $toSave);
         }
     }
     $mask = umask(Q_Config::get('Q', 'internal', 'umask', 00));
     $success = file_put_contents($filename2, !empty($toSave) ? Q::json_encode($toSave, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) : '{}', LOCK_EX);
     clearstatcache(true, $filename2);
     umask($mask);
     if ($success) {
         self::$cache[$filename] = $toSave;
         Q_Cache::set("Q_Tree\t{$filename}", $toSave);
         // no need to check result - on failure Q_Cache is disabled
     }
     return $success;
 }
Example #5
0
 /**
  * Dispatches a URI for internal processing.
  * Usually called by a front controller.
  * @method dispatch
  * @static
  * @param {mixed} [$uri=null] You can pass a custom URI to dispatch. Otherwise, Qbix will attempt
  *  to route the requested URL, if any.
  * @param {array} [$check=array('accessible')] Pass array() to skip checking whether the URI can be obtained
  *  as a result of routing some URL.
  * @return {boolean}
  * @throws {Q_Exception_MethodNotSupported}
  * @throws {Q_Exception_Recursion}
  * @throws {Q_Exception_DispatcherErrors}
  * @throws {Q_Exception_DispatcherForward}
  */
 static function dispatch($uri = null, $check = array('accessible'))
 {
     if (!is_array($check)) {
         $check = array('accessible');
     }
     if (isset($uri)) {
         if (in_array('accessible', $check)) {
             if (!Q_Uri::url($uri)) {
                 // We shouldn't dispatch to this URI
                 $uri = Q_Uri::from(array());
             }
         }
         self::$uri = Q_Uri::from($uri);
     } else {
         $request_uri = Q_Request::uri();
         self::$uri = clone $request_uri;
     }
     // if file or dir is requested, try to serve it
     $served = false;
     $skip = Q_Config::get('Q', 'dispatcherSkipFilename', false);
     $filename = $skip ? false : Q_Request::filename();
     if ($filename) {
         if (is_dir($filename)) {
             /**
              * @event Q/dir
              * @param {string} filename
              * @param {string} routed_uri
              * @return {boolean}
              */
             $served = Q::event("Q/dir", compact('filename', 'routed_uri'));
             $dir_was_served = true;
         } else {
             /**
              * @event Q/file
              * @param {string} filename
              * @param {string} routed_uri
              * @return {boolean}
              */
             $served = Q::event("Q/file", compact('filename', 'routed_uri'));
             $dir_was_served = false;
         }
     }
     // if response was served, then return
     if ($served) {
         self::result($dir_was_served ? "Dir served" : "File served");
         return true;
     }
     // This loop is for forwarding
     $max_forwards = Q_Config::get('Q', 'maxForwards', 10);
     for ($try = 0; $try < $max_forwards; ++$try) {
         // Make an array from the routed URI
         $routed_uri_array = array();
         if (self::$uri instanceof Q_Uri) {
             $routed_uri_array = self::$uri->toArray();
         }
         // If no module was found, then respond with noModule and return
         if (!isset(self::$uri->module)) {
             /**
              * @event Q/noModule
              * @param {array} $routed_uri_array
              */
             Q::event("Q/noModule", $routed_uri_array);
             // should echo things
             self::result("No module");
             return false;
         }
         $module = self::$uri->module;
         try {
             // Implement restricting of modules we are allowed to access
             $routed_modules = Q_Config::get('Q', 'routedModules', null);
             if (isset($routed_modules)) {
                 if (!in_array($module, $routed_modules)) {
                     /**
                      * @event Q/notFound
                      * @param {array} $routed_uri_array
                      */
                     Q::event('Q/notFound', $routed_uri_array);
                     // should echo things
                     self::result("Unknown module");
                     return false;
                 }
             } else {
                 if (!Q::realPath("handlers/{$module}")) {
                     Q::event('Q/notFound', $routed_uri_array);
                     // should echo things
                     self::result("Unknown module");
                     return false;
                 }
             }
             // Implement notFound if action was not found
             if (empty(self::$uri->action)) {
                 Q::event('Q/notFound', $routed_uri_array);
                 // should echo things
                 self::result("Unknown action");
                 return false;
             }
             // Fire a pure event, for aggregation etc
             if (!isset(self::$skip['Q/prepare'])) {
                 /**
                  * @event Q/prepare
                  * @param {array} $routed_uri_array
                  */
                 Q::event('Q/prepare', $routed_uri_array, true);
             }
             // Perform validation
             if (!isset(self::$skip['Q/validate'])) {
                 /**
                  * @event Q/validate
                  * @param {array} $routed_uri_array
                  */
                 Q::event('Q/validate', $routed_uri_array);
                 if (!isset(self::$skip['Q/errors'])) {
                     // Check if any errors accumulated
                     if (Q_Response::getErrors()) {
                         // There were validation errors -- render a response
                         self::result('Validation errors');
                         self::errors(null, $module, null);
                         return false;
                     }
                 }
             }
             // Time to instantiate some app objects from the request
             if (!isset(self::$skip['Q/objects'])) {
                 /**
                  * @event Q/objects
                  * @param {array} $routed_uri_array
                  */
                 Q::event('Q/objects', $routed_uri_array, true);
             }
             // We might want to reroute the request
             if (!isset(self::$skip['Q/reroute'])) {
                 /**
                  * @event Q/reroute
                  * @param {array} $routed_uri_array
                  * @return {boolean} whether to stop the dispatch
                  */
                 $stop_dispatch = Q::event('Q/reroute', $routed_uri_array, true);
                 if ($stop_dispatch) {
                     self::result("Stopped dispatch");
                     return false;
                 }
             }
             // Make some changes to server state, possibly
             $method = Q_Request::method();
             if ($method != 'GET') {
                 $methods = Q_Config::get('Q', 'methods', array('POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD'));
                 if (!in_array($method, $methods)) {
                     throw new Q_Exception_MethodNotSupported(compact('method'));
                 }
                 $method_event = 'Q/' . strtolower($method);
                 if (!isset(self::$skip['Q/method']) and !isset(self::$skip[$method_event])) {
                     if (!Q::canHandle($method_event)) {
                         throw new Q_Exception_MethodNotSupported(compact('method'));
                     }
                     Q::event($method_event);
                 }
             }
             // You can calculate some analytics here, and store them somewhere
             if (!isset(self::$skip['Q/analytics'])) {
                 /**
                  * @event Q/analytics
                  * @param {array} $routed_uri_array
                  */
                 Q::event('Q/analytics', $routed_uri_array, true);
             }
             if (!isset(self::$skip['Q/errors'])) {
                 // Check if any errors accumulated
                 if (Q_Response::getErrors()) {
                     // There were processing errors -- render a response
                     self::result('Processing errors');
                     self::errors(null, $module, null);
                     return false;
                 }
             }
             // When handling all further events, you should probably
             // refrain from changing server state, and only do reading.
             // That is because GET in HTTP is not supposed to have side effects
             // for which the client is responsible.
             // Start buffering the response, unless otherwise requested
             $handler = Q_Response::isBuffered();
             if ($handler !== false) {
                 $ob = new Q_OutputBuffer($handler);
             }
             // Generate and render a response
             /**
              * @event Q/response
              * @param {array} $routed_uri_array
              */
             self::$response_started = true;
             Q::event("Q/response", $routed_uri_array);
             if (!empty($ob)) {
                 $ob->endFlush();
             }
             self::result("Served response");
             return true;
         } catch (Q_Exception_DispatcherForward $e) {
             if (!empty($ob)) {
                 $ob->getClean();
             }
             self::handleForwardException($e);
         } catch (Q_Exception_DispatcherErrors $e) {
             if (!empty($ob)) {
                 $partial_response = $ob->getClean();
             } else {
                 $partial_response = null;
             }
             self::errors(null, $module, $partial_response);
             self::result("Rendered errors");
             return true;
         } catch (Exception $exception) {
             if (!empty($ob)) {
                 $partial_response = $ob->getClean();
             } else {
                 $partial_response = null;
             }
             $message = $exception->getMessage();
             $file = $exception->getFile();
             $line = $exception->getLine();
             if (is_callable(array($exception, 'getTraceAsStringEx'))) {
                 $trace_string = $exception->getTraceAsStringEx();
             } else {
                 $trace_string = $exception->getTraceAsString();
             }
             $colored = Q_Exception::coloredString($message, $file, $line, $trace_string);
             self::result("Exception occurred:\n\n{$colored}");
             try {
                 self::errors($exception, $module, $partial_response);
             } catch (Exception $e) {
                 if (!empty($forwarding_to_error_action)) {
                     // Looks like there were errors in the error action
                     // So show the default one with the original exception
                     throw $exception;
                 }
                 if (get_class($e) === 'Q_Exception_DispatcherForward') {
                     $forwarding_to_error_action = true;
                     self::handleForwardException($e);
                     continue;
                 } else {
                     throw $e;
                 }
             }
             return false;
         }
     }
     // If we are here, we have done forwarding too much
     throw new Q_Exception_Recursion(array('function_name' => 'Dispatcher::forward()'));
 }
Example #6
0
 /**
  * Loads the configuration and plugins in the right order
  * @method configure
  * @static
  * @param boolean [$force_reload=false] If true, forces the reload of the cache.
  *  Otherwise it happens only if Q/configServer/interval seconds
  *  have passed since the last time.
  * @throws {Q_Exception_MissingPlugin}
  */
 static function configure($force_reload = false)
 {
     $app_tree = new Q_Tree();
     // check if config need to be reloaded
     if (Q_Cache::connected()) {
         // we need to know reload interval
         $app_tree->load('config/Q.json');
         $app_tree->load('config/app.json');
         $app_tree->load('local/app.json');
         $config_files = $app_tree->get('Q', 'configFiles', array());
         foreach ($config_files as $cf) {
             $app_tree->merge(Q_Config::getFromServer($cf));
         }
         // second round to catch configFiles inside configFiles
         $config_files = $app_tree->get('Q', 'configFiles', array());
         foreach ($config_files as $cf) {
             $app_tree->merge(Q_Config::getFromServer($cf));
         }
         $interval = $app_tree->get('Q', 'configServer', 'interval', 60);
         // reload each minute by default
         $app_tree->clear(null);
         $timestamp = Q_Cache::get("Q_Config\tupdate_time");
         if (!isset($timestamp) || time() - $timestamp > $interval) {
             $force_reload = true;
         }
     }
     if ($force_reload) {
         $old_setting = Q_Cache::ignore(true);
     }
     Q_Config::clear(null);
     // clear the config
     Q_Config::load('config/Q.json');
     // Get the app config, but don't load it yet
     $app_tree->load('config/app.json');
     $app_tree->load('local/app.json');
     // Load all the plugin config files first
     $paths = explode(PS, get_include_path());
     $plugins = $app_tree->get('Q', 'plugins', array());
     if (!in_array('Q', $plugins)) {
         array_unshift($plugins, 'Q');
     }
     global $Q_Bootstrap_config_plugin_limit;
     $i = 0;
     foreach ($plugins as $k => $v) {
         ++$i;
         if (isset($Q_Bootstrap_config_plugin_limit) and $i > $Q_Bootstrap_config_plugin_limit) {
             continue;
         }
         $plugin = is_numeric($k) ? $v : $k;
         $plugin_path = Q::realPath('plugins' . DS . $v);
         if (!$plugin_path) {
             throw new Q_Exception_MissingPlugin(compact('plugin'));
         }
         Q_Config::load($plugin_path . DS . 'config' . DS . 'plugin.json');
         array_splice($paths, 1, 0, array($plugin_path));
         $PLUGIN = strtoupper($plugin);
         if (!defined($PLUGIN . '_PLUGIN_DIR')) {
             define($PLUGIN . '_PLUGIN_DIR', $plugin_path);
         }
         if (!defined($PLUGIN . '_PLUGIN_CONFIG_DIR')) {
             define($PLUGIN . '_PLUGIN_CONFIG_DIR', $plugin_path . DS . 'config');
         }
         if (!defined($PLUGIN . '_PLUGIN_CLASSES_DIR')) {
             define($PLUGIN . '_PLUGIN_CLASSES_DIR', $plugin_path . DS . 'classes');
         }
         if (!defined($PLUGIN . '_PLUGIN_FILES_DIR')) {
             define($PLUGIN . '_PLUGIN_FILES_DIR', $plugin_path . DS . 'files');
         }
         if (!defined($PLUGIN . '_PLUGIN_HANDLERS_DIR')) {
             define($PLUGIN . '_PLUGIN_HANDLERS_DIR', $plugin_path . DS . 'handlers');
         }
         if (!defined($PLUGIN . '_PLUGIN_PLUGINS_DIR')) {
             define($PLUGIN . '_PLUGIN_PLUGINS_DIR', $plugin_path . DS . 'plugins');
         }
         if (!defined($PLUGIN . '_PLUGIN_SCRIPTS_DIR')) {
             define($PLUGIN . '_PLUGIN_SCRIPTS_DIR', $plugin_path . DS . 'scripts');
         }
         if (!defined($PLUGIN . '_PLUGIN_VIEWS_DIR')) {
             define($PLUGIN . '_PLUGIN_VIEWS_DIR', $plugin_path . DS . 'views');
         }
         if (!defined($PLUGIN . '_PLUGIN_TESTS_DIR')) {
             define($PLUGIN . '_PLUGIN_TESTS_DIR', $plugin_path . DS . 'tests');
         }
         if (!defined($PLUGIN . '_PLUGIN_WEB_DIR')) {
             define($PLUGIN . '_PLUGIN_WEB_DIR', $plugin_path . DS . 'web');
         }
         self::$plugins[$plugin] = $plugin_path;
     }
     $paths = array_unique($paths);
     set_include_path(implode(PS, $paths));
     // Now, we can merge in our app's config
     Q_Config::merge($app_tree);
     // Now, load any other files we were supposed to load
     $config_files = Q_Config::get('Q', 'configFiles', array());
     foreach ($config_files as $cf) {
         Q_Config::merge(Q_Config::getFromServer($cf));
     }
     // second round to catch configFiles inside configFiles
     $config_files = Q_Config::get('Q', 'configFiles', array());
     foreach ($config_files as $cf) {
         Q_Config::merge(Q_Config::getFromServer($cf));
     }
     $script_files = Q_Config::get('Q', 'scriptFiles', array());
     foreach ($script_files as $cf) {
         Q::includeFile($cf);
     }
     error_reporting(Q_Config::get('Q', 'errorReporting', E_ALL));
     if (isset($old_setting)) {
         Q_Cache::ignore($old_setting);
     }
     set_time_limit(Q_Config::get('Q', 'internal', 'phpTimeout', 30));
     self::setDefaultTimezone();
 }
Example #7
0
 /**
  * Invites a user (or a future user) to a stream .
  * @method invite
  * @static
  * @param {string} $publisherId The id of the stream publisher
  * @param {string} $streamName The name of the stream the user will be invited to
  * @param {array} $who Array that can contain the following keys:
  * @param {string|array} [$who.userId] user id or an array of user ids
  * @param {string|array} [$who.fb_uid]  fb user id or array of fb user ids
  * @param {string|array} [$who.label]  label or an array of labels, or tab-delimited string
  * @param {string|array} [$who.identifier]  identifier or an array of identifiers, or tab-delimited string
  * @param {integer} [$who.newFutureUsers] the number of new Users_User objects to create via Users::futureUser in order to invite them to this stream. This typically is used in conjunction with passing the "html" option to this function.
  * @param {array} [$options=array()]
  *  @param {string|array} [$options.label] label or an array of labels for adding publisher's contacts
  *  @param {string|array} [$options.myLabel] label or an array of labels for adding logged-in user's contacts
  *  @param {integer} [$options.readLevel] => the read level to grant those who are invited
  *  @param {integer} [$options.writeLevel] => the write level to grant those who are invited
  *  @param {integer} [$options.adminLevel] => the admin level to grant those who are invited
  *	@param {string} [$options.displayName] => the display name to use to represent the inviting user
  *  @param {string} [$options.appUrl] => Can be used to override the URL to which the invited user will be redirected and receive "Q.Streams.token" in the querystring.
  *	@param {array} [$options.html] => an array of ($template, $batchName) such as ("MyApp/foo.handlebars", "foo") for generating html snippets which can then be viewed from and printed via the action Streams/invitations?batchName=$batchName
  * @param {array} [$options.asUserId=null] Invite as this user id
  * @see Users::addLink()
  * @return {array} returns array with keys "success", "invited", "statuses", "identifierTypes", "alreadyParticipating"
  */
 static function invite($publisherId, $streamName, $who, $options = array())
 {
     if (isset($options['asUserId'])) {
         $asUserId = $options['asUserId'];
         $asUser = Users_User::fetch($asUserId);
     } else {
         $asUser = Users::loggedInUser(true);
         $asUserId = $asUser->id;
     }
     // Fetch the stream as the logged-in user
     $stream = Streams::fetch($asUserId, $publisherId, $streamName);
     if (!$stream) {
         throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => 'with that name'), 'streamName');
     }
     $stream = reset($stream);
     // Do we have enough admin rights to invite others to this stream?
     if (!$stream->testAdminLevel('invite') || !$stream->testWriteLevel('join')) {
         throw new Users_Exception_NotAuthorized();
     }
     if (isset($options['html'])) {
         $html = $options['html'];
         if (!is_array($html) or count($html) < 2) {
             throw new Q_Exception_WrongType(array('field' => "options.html", 'type' => 'array of 2 strings'));
         }
         list($template, $batchName) = $html;
         // validate these paths
         $filename = APP_VIEWS_DIR . DS . $template;
         if (!Q::realPath($filename)) {
             throw new Q_Exception_MissingFile(compact('filename'));
         }
         $ext = $pathinfo = pathinfo($template, PATHINFO_EXTENSION);
         if ($ext !== 'handlebars') {
             throw new Q_Exception_WrongValue(array('field' => 'options.html[0]', 'range' => 'a filename with extension .handlebars'));
         }
         $path = Streams::invitationsPath($asUserId) . DS . $batchName;
         Q_Utils::canWriteToPath($path, true, true);
     }
     // get user ids if any to array, throw if user not found
     $raw_userIds = isset($who['userId']) ? Users_User::verifyUserIds($who['userId'], true) : array();
     // merge labels if any
     if (isset($who['label'])) {
         $label = $who['label'];
         if (is_string($label)) {
             $label = array_map('trim', explode("\t", $labels));
         }
         $raw_userIds = array_merge($raw_userIds, Users_User::labelsToIds($asUserId, $label));
     }
     // merge identifiers if any
     $identifierType = null;
     $statuses = null;
     if (isset($who['identifier'])) {
         $identifier = $who['identifier'];
         if (is_string($identifier)) {
             if (Q_Valid::email($who['identifier'])) {
                 $identifierType = 'email';
             } else {
                 if (Q_Valid::phone($who['identifier'])) {
                     $identifierType = 'mobile';
                 }
             }
             $identifier = array_map('trim', explode("\t", $identifier));
         }
         $statuses = array();
         $identifier_ids = Users_User::idsFromIdentifiers($identifier, $statuses);
         $raw_userIds = array_merge($raw_userIds, $identifier_ids);
     }
     // merge fb uids if any
     if (isset($who['fb_uid'])) {
         $fb_uids = $who['fb_uid'];
         if (is_string($fb_uids)) {
             $fb_uids = array_map('trim', explode("\t", $fb_uids));
         }
         $raw_userIds = array_merge($raw_userIds, Users_User::idsFromFacebook($fb_uids));
     }
     if (!empty($who['newFutureUsers'])) {
         $nfu = $who['newFutureUsers'];
         for ($i = 0; $i < $nfu; ++$i) {
             $raw_userIds[] = Users::futureUser('none', null)->id;
         }
     }
     // ensure that each userId is included only once
     // and remove already participating users
     $raw_userIds = array_unique($raw_userIds);
     $total = count($raw_userIds);
     $userIds = Streams_Participant::filter($raw_userIds, $stream);
     $to_invite = count($userIds);
     $appUrl = !empty($options['appUrl']) ? $options['appUrl'] : Q_Request::baseUrl() . '/' . Q_Config::get("Streams", "types", $stream->type, "invite", "url", "plugins/Streams/stream");
     // now check and define levels for invited user
     $readLevel = isset($options['readLevel']) ? $options['readLevel'] : null;
     if (isset($readLevel)) {
         if (!$stream->testReadLevel($readLevel)) {
             // We can't assign greater read level to other people than we have ourselves!
             throw new Users_Exception_NotAuthorized();
         }
     }
     $writeLevel = isset($options['writeLevel']) ? $options['writeLevel'] : null;
     if (isset($writeLevel)) {
         if (!$stream->testWriteLevel($writeLevel)) {
             // We can't assign greater write level to other people than we have ourselves!
             throw new Users_Exception_NotAuthorized();
         }
     }
     $adminLevel = isset($options['adminLevel']) ? $options['adminLevel'] : null;
     if (isset($adminLevel)) {
         if (!$stream->testAdminLevel($adminLevel + 1)) {
             // We can't assign an admin level greater, or equal, to our own!
             // A stream's publisher can assign owners. Owners can assign admins.
             // Admins can confer powers to invite others, to some people.
             // Those people can confer the privilege to publish a message re this stream.
             // But admins can't assign other admins, and even stream owners
             // can't assign other owners.
             throw new Users_Exception_NotAuthorized();
         }
     }
     // calculate expiry time
     $duration = Q_Config::get("Streams", "types", $stream->type, "invite", "duration", false);
     $expiry = $duration ? strtotime($duration) : null;
     // let node handle the rest, and get the result
     $params = array("Q/method" => "Streams/Stream/invite", "invitingUserId" => $asUserId, "username" => $asUser->username, "userIds" => Q::json_encode($userIds), "stream" => Q::json_encode($stream->toArray()), "appUrl" => $appUrl, "label" => Q::ifset($options, 'label', null), "myLabel" => Q::ifset($options, 'myLabel', null), "readLevel" => $readLevel, "writeLevel" => $writeLevel, "adminLevel" => $adminLevel, "displayName" => isset($options['displayName']) ? $options['displayName'] : Streams::displayName($asUser), "expiry" => $expiry);
     if ($template) {
         $params['template'] = $template;
         $params['batchName'] = $batchName;
     }
     $result = Q_Utils::queryInternal('Q/node', $params);
     return array('success' => $result, 'invited' => $userIds, 'statuses' => $statuses, 'identifierType' => $identifierType, 'alreadyParticipating' => $total - $to_invite);
 }
Example #8
0
 /**
  * Saves an image, usually sent by the client, in one or more sizes.
  * @method save
  * @static
  * @param {array} $params 
  * @param {string} [$params.data] the image data
  * @param {string} [$params.path="uploads"] parent path under web dir (see subpath)
  * @param {string} [$params.subpath=""] subpath that should follow the path, to save the image under
  * @param {string} [$params.merge=""] path under web dir for an optional image to use as a background
  * @param {string} [$params.crop] array with keys "x", "y", "w", "h" to crop the original image
  * @param {string} [$params.save=array("x" => "")] array of $size => $basename pairs
  *  where the size is of the format "WxH", and either W or H can be empty.
  * @param {string} [$params.skipAccess=false] if true, skips the check for authorization to write files there
  * @return {array} an array of ($size => $fullImagePath) pairs
  */
 static function save($params)
 {
     if (empty($params['data'])) {
         throw new Q_Exception("Image data is missing");
     }
     $imageData = $params['data'];
     $image = imagecreatefromstring($imageData);
     if (!$image) {
         throw new Q_Exception("Image type not supported");
     }
     // image dimensions
     $maxW = Q_Config::get('Q', 'uploads', 'limits', 'image', 'width', 5000);
     $maxH = Q_Config::get('Q', 'uploads', 'limits', 'image', 'height', 5000);
     $iw = imagesx($image);
     $ih = imagesy($image);
     if ($maxW and $iw > $maxW) {
         throw new Q_Exception("Uploaded image width exceeds {$maxW}");
     }
     if ($maxH and $iw > $maxH) {
         throw new Q_Exception("Uploaded image width exceeds {$maxH}");
     }
     // check whether we can write to this path, and create dirs if needed
     $path = isset($params['path']) ? $params['path'] : 'uploads';
     $subpath = isset($params['subpath']) ? $params['subpath'] : '';
     $realPath = Q::realPath(APP_WEB_DIR . DS . $path);
     if ($realPath === false) {
         throw new Q_Exception_MissingFile(array('filename' => APP_WEB_DIR . DS . $path));
     }
     $writePath = $realPath . ($subpath ? DS . $subpath : '');
     $lastChar = substr($writePath, -1);
     if ($lastChar !== DS and $lastChar !== '/') {
         $writePath .= DS;
     }
     $throwIfNotWritable = empty($params['skipAccess']) ? true : null;
     Q_Utils::canWriteToPath($writePath, $throwIfNotWritable, true);
     // check if exif is available
     if (self::isJPEG($imageData)) {
         $exif = exif_read_data("data://image/jpeg;base64," . base64_encode($imageData));
         // rotate original image if necessary (hopefully it's not too large).
         if (!empty($exif['Orientation'])) {
             switch ($exif['Orientation']) {
                 case 3:
                     $image = imagerotate($image, 180, 0);
                     break;
                 case 6:
                     $image = imagerotate($image, -90, 0);
                     break;
                 case 8:
                     $image = imagerotate($image, 90, 0);
                     break;
             }
         }
     }
     $crop = isset($params['crop']) ? $params['crop'] : array();
     $save = !empty($params['save']) ? $params['save'] : array('x' => '');
     // crop parameters - size of source image
     $isw = isset($crop['w']) ? $crop['w'] : $iw;
     $ish = isset($crop['h']) ? $crop['h'] : $ih;
     $isx = isset($crop['x']) ? $crop['x'] : 0;
     $isy = isset($crop['y']) ? $crop['y'] : 0;
     // process requested thumbs
     $data = array();
     $merge = null;
     $m = isset($params['merge']) ? $params['merge'] : null;
     if (isset($m) && strtolower(substr($m, -4)) === '.png') {
         $mergePath = Q::realPath(APP_WEB_DIR . DS . implode(DS, explode('/', $m)));
         if ($mergePath) {
             $merge = imagecreatefrompng($mergePath);
             $mw = imagesx($merge);
             $mh = imagesy($merge);
         }
     }
     foreach ($save as $size => $name) {
         if (empty($name)) {
             // generate a filename
             do {
                 $name = Q_Utils::unique(8) . '.png';
             } while (file_exists($writePath . $name));
         }
         if (strrpos($name, '.') === false) {
             $name .= '.png';
         }
         list($n, $ext) = explode('.', $name);
         $sw = $isw;
         $sh = $ish;
         $sx = $isx;
         $sy = $isy;
         // determine destination image size
         if (!empty($size)) {
             $sa = explode('x', $size);
             if (count($sa) > 1) {
                 if ($sa[0] === '') {
                     if ($sa[1] === '') {
                         $dw = $sw;
                         $dh = $sh;
                     } else {
                         $dh = intval($sa[1]);
                         $dw = $sw * $dh / $sh;
                     }
                 } else {
                     $dw = intval($sa[0]);
                     if ($sa[1] === '') {
                         $dh = $sh * $dw / $sw;
                     } else {
                         $dh = intval($sa[1]);
                     }
                 }
             } else {
                 $dw = $dh = intval($sa[0]);
             }
             // calculate the origin point of source image
             // we have a cropped image of dimension $sw, $sh and need to make new with dimension $dw, $dh
             if ($dw / $sw < $dh / $sh) {
                 // source is wider then destination
                 $new = $dw / $dh * $sh;
                 $sx += round(($sw - $new) / 2);
                 $sw = round($new);
             } else {
                 // source is narrower then destination
                 $new = $dh / $dw * $sw;
                 $sy += round(($sh - $new) / 2);
                 $sh = round($new);
             }
         } else {
             $size = '';
             $dw = $sw;
             $dh = $sh;
         }
         // create destination image
         $maxWidth = Q_Config::get('Q', 'images', 'maxWidth', null);
         $maxHeight = Q_Config::get('Q', 'images', 'maxHeight', null);
         if (isset($maxWidth) and $dw > $maxWidth) {
             throw new Q_Exception("Image width exceeds maximum width of {$dw}");
         }
         if (isset($maxHeight) and $dh > $maxHeight) {
             throw new Q_Exception("Image height exceeds maximum height of {$dh}");
         }
         $thumb = imagecreatetruecolor($dw, $dh);
         $res = $sw === $dw && $sh === $dh ? imagecopy($thumb, $image, 0, 0, $sx, $sy, $sw, $sh) : imagecopyresampled($thumb, $image, 0, 0, $sx, $sy, $dw, $dh, $sw, $sh);
         if (!$res) {
             throw new Q_Exception("Failed to save image file of type '{$ext}'");
         }
         if ($merge) {
             $mergethumb = imagecreatetruecolor($mw, $mh);
             imagesavealpha($mergethumb, false);
             imagealphablending($mergethumb, false);
             if (imagecopyresized($mergethumb, $merge, 0, 0, 0, 0, $dw, $dh, $mw, $mh)) {
                 imagecopy($thumb, $mergethumb, 0, 0, 0, 0, $dw, $dh);
             }
         }
         switch ($ext) {
             case 'jpeg':
             case 'jpeg':
                 $func = 'imagejpeg';
                 break;
             case 'gif':
                 $func = 'imagegif';
                 break;
             case 'png':
             default:
                 $func = 'imagepng';
                 break;
         }
         if ($res = call_user_func($func, $thumb, $writePath . $name)) {
             $data[$size] = $subpath ? "{$path}/{$subpath}/{$name}" : "{$path}/{$name}";
         }
     }
     $data[''] = $subpath ? "{$path}/{$subpath}" : "{$path}";
     /**
      * @event Q/image/save {after}
      * @param {string} user
      * @param {string} path
      * @param {string} subpath
      * @param {string} writePath
      * @param {string} data
      */
     Q::event('Q/image/save', compact('path', 'subpath', 'writePath', 'data', 'save', 'crop'), 'after');
     return $data;
 }
Example #9
0
 /**
  * Append a message to the main log
  * @method log
  * @static
  * @param {mixed} $message
  *  the message to append. Usually a string.
  * @param {string} $key=null
  *  The name of log file. Defaults to "$app_name.log"
  * @param {bool} $timestamp=true
  *  whether to prepend the current timestamp
  * @param {array} $options
  * @param {integer} [$options.maxLength=ini_get('log_errors_max_len')]
  * @param {integer} [$options.maxDepth=3]
  * @throws {Q_Exception_MissingFile}
  *	If unable to create directory or file for the log
  */
 static function log($message, $key = null, $timestamp = true, $options = array())
 {
     if (is_array($timestamp)) {
         $options = $timestamp;
         $timestamp = true;
     }
     if (is_array($key)) {
         $options = $key;
         $key = null;
         $timestamp = true;
     }
     if (false === Q::event('Q/log', compact('message', 'timestamp', 'error_log_arguments'), 'before')) {
         return;
     }
     if (!is_string($message)) {
         $maxDepth = Q::ifset($options, 'maxDepth', 3);
         if (!is_object($message)) {
             $message = Q::var_dump($message, $maxDepth, '$', 'text');
         } else {
             if (!is_callable(array($message, '__toString'))) {
                 $message = Q::var_dump($message, $maxDepth, '$', 'text');
             }
         }
     }
     $app = Q_Config::get('Q', 'app', null);
     if (!isset($app)) {
         $app = defined('APP_DIR') ? basename(APP_DIR) : 'Q App';
     }
     $message = "(" . (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : "cli") . ") {$app}: {$message}";
     $max_len = Q::ifset($options, 'maxLength', Q_Config::get('Q', 'log', 'maxLength', ini_get('log_errors_max_len')));
     $path = (defined('APP_FILES_DIR') ? APP_FILES_DIR : Q_FILES_DIR) . DS . 'Q' . DS . Q_Config::get('Q', 'internal', 'logDir', 'logs');
     $mask = umask(00);
     if (!($realPath = Q::realPath($path)) and !($realPath = Q::realPath($path, true))) {
         if (!@mkdir($path, 0777, true)) {
             throw new Q_Exception_FilePermissions(array('action' => 'create', 'filename' => $path, 'recommendation' => ' Please set the app files directory to be writable.'));
         }
         $realPath = Q::realPath($path, true);
     }
     $filename = (isset($key) ? $key : $app) . '.log';
     $toSave = "\n" . ($timestamp ? '[' . date('Y-m-d h:i:s') . '] ' : '') . substr($message, 0, $max_len);
     file_put_contents($realPath . DS . $filename, $toSave, FILE_APPEND);
     umask($mask);
 }
Example #10
0
 /**
  * Adds inline template to the response
  * @method addTemplate
  * @static
  * @param {string} $name The location of the template file relative to the "views" folder
  * @param {string} [$type="handlebars"]
  * @param {array} [$params=array()] Optional array of parameters to pass to PHP
  * @param {array} [$slotName=null] A way to override the slot name. Pass "" here to
  *  have the script lines be returned first by Q_Response::scriptLines.
  *  The other special value, "Q", is intended for internal use.
  * @return {boolean} returns false if template was already added, else returns true
  */
 static function addTemplate($name, $type = 'handlebars', $params = array(), $slotName = null)
 {
     self::$templates[] = compact('name', 'type');
     // Now, for the slot
     if (!isset($slotName)) {
         $slotName = isset(self::$slotName) ? self::$slotName : '';
     }
     if (!isset(self::$inlineTemplates[$slotName])) {
         self::$inlineTemplates[$slotName] = array();
     }
     foreach (self::$inlineTemplates[$slotName] as $template) {
         Q::log("=====\n\n" . $template['name']);
         if ($template['name'] == $name && $template['type'] == $type) {
             return false;
             // already added
         }
     }
     $filename = Q::realPath("views/{$name}.{$type}");
     if (!$filename) {
         throw new Q_Exception_MissingFile(array('filename' => "views/{$name}.{$type}"));
     }
     if ($type === 'php') {
         $ob = new Q_OutputBuffer();
         Q::includeFile($filename, $params, false);
         $content = $ob->getClean();
     } else {
         $content = file_get_contents($filename);
     }
     if (!$content) {
         throw new Q_Exception("Failed to load template '{$name}'");
     }
     self::$inlineTemplates[$slotName][] = compact('name', 'content', 'type');
     return true;
 }
Example #11
0
 /**
  * Modify a config file by clearing some data
  * Config file is searched in APP_DIR/files forder. If config server url is defined
  * the filename is searched on config server
  * @method clearOnServer
  * @static
  * @param {string} $filename The name of the config file. If config server is defined, file is changed there
  * @param {string|array} [$args=null] OA key or an array of keys for traversing the tree.
  *	If keys are not supplied the file is cleared
  *	If all-but-last keys point to plain array, last key is interpreted as a member
  *	of that array and only this array member is removed
  *	If all-but-last keys point to associative array (A) and last key is plain array (B)
  *	all keys from array A which are in array B are unset
  * @param {boolean} [$noSave=false] Weather result shall be returned or saved. Shall be of type boolean
  * @return {boolean} Wheather data was successfuly cleared. If some key does not exist still true
  * @throws {Q_Exception}
  */
 static function clearOnServer($filename, $args = null, $noSave = false)
 {
     if (!isset($args) || $args === 'null') {
         $args = array();
     }
     if (is_string($args)) {
         $args = array($args);
     }
     if (is_string($noSave)) {
         $noSave = json_decode($noSave);
     }
     $noSave = !!$noSave;
     if ($cs = self::serverInfo()) {
         // request config server
         if (!empty($cs['url'])) {
             if (!empty($cs['internal'])) {
                 // query "internal" Qbix server
                 return Q_Utils::queryInternal('Q/Config', array('Q/method' => 'clear', 'filename' => $filename, 'args' => $args, 'noSave' => $noSave), $cs['url']);
             } else {
                 // query "external" Qbix server
                 return Q_Utils::queryExternal('Q/Config', array('Q/method' => 'clear', 'filename' => $filename, 'args' => $args, 'noSave' => $noSave), $cs['url']);
             }
         }
     }
     // modify local file
     if (defined('APP_DIR')) {
         $filename = Q::realPath(APP_DIR . DS . 'files' . DS . $filename);
     } else {
         throw new Q_Exception("'APP_DIR' is not defined");
     }
     if (!$filename) {
         return true;
     }
     $tree = new Q_Tree();
     if (count($args)) {
         $tree->load($filename);
         // if not loaded we consider three empty
         if (count($args) > 1) {
             $last = call_user_func_array(array($tree, "get"), $args);
         } else {
             $last = $tree->getAll();
         }
         if (is_array($last)) {
             if (array_keys($last) === range(0, count($last) - 1)) {
                 // it's plain array and we remove it's member
                 $search = array_pop($args);
                 if (!is_array($search)) {
                     $search = array($search);
                 }
                 foreach ($search as $value) {
                     $keys = array_keys($last, $value);
                     for ($deleted = 0, $i = 0; $i < count($keys); $i++) {
                         array_splice($last, $keys[$i] - $deleted++, 1);
                     }
                 }
                 call_user_func_array(array($tree, "clear"), $args);
                 if (count($last)) {
                     array_push($args, $last);
                     call_user_func_array(array($tree, "set"), $args);
                 }
             } else {
                 // $last is associative array
                 $search = array_pop($args);
                 if (!is_array($search)) {
                     $search = array($search);
                 }
                 foreach ($search as $value) {
                     call_user_func_array(array($tree, "clear"), array_merge($args, array($value)));
                 }
             }
         }
     } else {
         $tree = new Q_Tree();
     }
     return $noSave ? $tree->getAll() : $tree->save($filename);
 }