Beispiel #1
0
function Q_errors_native($params)
{
    echo Q::view('Q/errors.php', $params);
    $app = Q_Config::expect('Q', 'app');
    Q::log("{$app}: Errors in " . ceil(Q::milliseconds()) . "ms\n");
    Q::log($params);
}
Beispiel #2
0
function log_shard_query($params)
{
    foreach ($params['queries'] as $shard => $query) {
        if ($query->className === 'Users_Session') {
            continue;
        }
        $connection = $query->db->connectionName();
        if ($begin = $query->getClause('BEGIN') and $query->nestedTransactionCount == 1) {
            Q::log($begin);
        }
        $duration = ceil($query->endedTime - $query->startedTime);
        Q::log("Query {$connection} on shard \"{$shard}\":\n{$params['sql']}\n(duration: {$duration} ms)\n\n");
        if ($commit = $query->getClause('COMMIT') and $query->nestedTransactionCount == 0) {
            Q::log($commit);
        }
        if (!empty($params['exception'])) {
            Q::log("ROLLBACK (due to exception)");
            Q::log("query was: " . $params['sql']);
            Q::log($params['exception']);
        } else {
            if ($rollback = $query->getClause('ROLLBACK')) {
                Q::log($rollback);
            }
        }
    }
}
Beispiel #3
0
 /**
  * Excecute web request
  * @method execute
  * @static
  */
 static function execute()
 {
     // Fixes for different platforms:
     if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
         // ISAPI 3.0
         $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL'];
     }
     // Get the base URL
     $base_url = Q_Request::baseUrl();
     if (Q::$controller === 'Q_ActionController') {
         // we detected action.php in the URL, but
         // a misconfigured web server executed index.php instead
         return Q_ActionController::execute();
     }
     // Set the controller that is being used
     if (!isset(Q::$controller)) {
         Q::$controller = 'Q_WebController';
     }
     try {
         $slots = Q_Request::slotNames(false);
         $slots = $slots ? ' slots: (' . implode(',', $slots) . ') from' : '';
         $method = Q_Request::method();
         Q::log("{$method}{$slots} url: " . Q_Request::url(true), null, null, array('maxLength' => 10000));
         Q_Dispatcher::dispatch();
         $dispatchResult = Q_Dispatcher::result();
         if (!isset($dispatchResult)) {
             $dispatchResult = 'Ran dispatcher';
         }
         $uri = Q_Request::uri();
         $module = $uri->module;
         $action = $uri->action;
         if ($module and $action) {
             $slotNames = Q_Request::slotNames();
             $returned_slots = empty($slotNames) ? '' : implode(',', $slotNames);
             Q::log("~" . ceil(Q::milliseconds()) . 'ms+' . ceil(memory_get_peak_usage() / 1000) . 'kb.' . " {$dispatchResult} for {$module}/{$action}" . " ({$returned_slots})", null, null, array('maxLength' => 10000));
         } else {
             Q::log("~" . ceil(Q::milliseconds()) . 'ms+' . ceil(memory_get_peak_usage() / 1000) . 'kb.' . " {$dispatchResult} No route for " . $_SERVER['REQUEST_URI'], null, null, array('maxLength' => 10000));
         }
     } catch (Exception $exception) {
         /**
          * @event Q/exception
          * @param {Exception} exception
          */
         Q::event('Q/exception', compact('exception'));
     }
 }
Beispiel #4
0
 /**
  * Logs a message.
  *
  * @param  $sMessage @type string The message.
  */
 public function log($sMessage)
 {
     Q::log(trim($sMessage), 'apns');
 }
Beispiel #5
0
function Q_exception_native($params)
{
    extract($params);
    /**
     * @var Exception $exception 
     */
    if ($is_ajax = Q_Request::isAjax()) {
        $json = @Q::json_encode(array('errors' => Q_Exception::toArray(array($exception))));
        $callback = Q_Request::callback();
        switch (strtolower($is_ajax)) {
            case 'iframe':
                // Render an HTML layout for ajax
                if (!Q_Response::$batch) {
                    header("Content-type: text/html");
                }
                echo <<<EOT
<!doctype html><html lang=en>
<head><meta charset=utf-8><title>Q Result</title></head>
<body>
<script type="text/javascript">
window.result = function () { return {$json} };
</script>
</body>
</html>
EOT;
                break;
            case 'json':
                // Render a JSON layout for ajax
            // Render a JSON layout for ajax
            default:
                header("Content-type: " . ($callback ? "application/javascript" : "application/json"));
                echo $callback ? "{$callback}({$json})" : $json;
        }
    } else {
        if (Q::textMode()) {
            echo Q_Exception::coloredString($exception);
            exit;
        }
        $message = $exception->getMessage();
        $file = $exception->getFile();
        $line = $exception->getLine();
        if (is_callable(array($exception, 'getTraceAsStringEx'))) {
            $trace_string = $exception->getTraceAsStringEx();
        } else {
            $trace_string = $exception->getTraceAsString();
        }
        if ($exception instanceof Q_Exception_PhpError or !empty($exception->messageIsHtml)) {
            // do not sanitize $message
        } else {
            $message = Q_Html::text($message);
        }
        $content = "<h1 class='exception_message'>{$message}</h1>";
        if (Q_Config::get('Q', 'exception', 'showFileAndLine', true)) {
            $content .= "<h3 class='exception_fileAndLine'>in {$file} ({$line})</h3>";
        }
        if (Q_Config::get('Q', 'exception', 'showTrace', true)) {
            $content .= "<pre class='exception_trace'>{$trace_string}</pre>";
        }
        $content .= str_repeat(' ', 512);
        // because of chrome
        $title = "Exception occurred";
        $dashboard = "";
        echo Q::view('Q/layout/html.php', compact('content', 'dashboard', 'title'));
    }
    $app = Q_Config::get('Q', 'app', null);
    $colored = Q_Exception::coloredString($exception);
    Q::log("{$app}: Exception in " . ceil(Q::milliseconds()) . "ms:\n\n{$colored}\n", null, true, array('maxLength' => 10000));
}
Beispiel #6
0
/**
 * Used to create a new stream
 *
 * @param {array} $_REQUEST 
 * @param {String} [$_REQUEST.title] Required. The title of the interest.
 * @param {String} [$_REQUEST.publisherId] Optional. Defaults to the app name.
 * @param {String} [$_REQUEST.subscribe] Optional. Defauls to false. Whether to subscribe rather than just join the interest stream.
 * @return {void}
 */
function Streams_interest_post()
{
    $user = Users::loggedInUser(true);
    $title = Q::ifset($_REQUEST, 'title', null);
    if (!isset($title)) {
        throw new Q_Exception_RequiredField(array('field' => 'title'));
    }
    $app = Q_Config::expect('Q', 'app');
    $publisherId = Q::ifset($_REQUEST, 'publisherId', $app);
    $name = 'Streams/interest/' . Q_Utils::normalize($title);
    $stream = Streams::fetchOne(null, $publisherId, $name);
    if (!$stream) {
        $stream = Streams::create($publisherId, $publisherId, 'Streams/interest', array('name' => $name, 'title' => $title));
        $parts = explode(': ', $title, 2);
        $keywords = implode(' ', $parts);
        try {
            $data = Q_Image::pixabay($keywords, array('orientation' => 'horizontal', 'min_width' => '500', 'safesearch' => 'true', 'image_type' => 'photo'), true);
        } catch (Exception $e) {
            Q::log("Exception during Streams/interest post: " . $e->getMessage());
            $data = null;
        }
        if (!empty($data)) {
            $sizes = Q_Config::expect('Streams', 'icons', 'sizes');
            ksort($sizes);
            $params = array('data' => $data, 'path' => "plugins/Streams/img/icons", 'subpath' => $name, 'save' => $sizes, 'skipAccess' => true);
            Q_Image::save($params);
            $stream->icon = $name;
        }
        $stream->save();
    }
    $subscribe = !!Q::ifset($_REQUEST, 'subscribe', false);
    if ($subscribe) {
        if (!$stream->subscription($user->id)) {
            $stream->subscribe();
        }
    } else {
        $stream->join();
    }
    $myInterestsName = 'Streams/user/interests';
    $myInterests = Streams::fetchOne($user->id, $user->id, $myInterestsName);
    if (!$myInterests) {
        $myInterests = new Streams_Stream();
        $myInterests->publisherId = $user->id;
        $myInterests->name = $myInterestsName;
        $myInterests->type = 'Streams/category';
        $myInterests->title = 'My Interests';
        $myInterests->save();
    }
    Streams::relate($user->id, $user->id, 'Streams/user/interests', 'Streams/interest', $publisherId, $name, array('weight' => '+1'));
    Q_Response::setSlot('publisherId', $publisherId);
    Q_Response::setSlot('streamName', $name);
    /**
     * Occurs when the logged-in user has successfully added an interest via HTTP
     * @event Streams/interest/post {after}
     * @param {string} publisherId The publisher of the interest stream
     * @param {string} title The title of the interest
     * @param {boolean} subscribe Whether the user subscribed to the interest stream
     * @param {Users_User} user The logged-in user
     * @param {Streams_Stream} stream The interest stream
     * @param {Streams_Stream} myInterests The user's "Streams/user/interests" stream
     */
    Q::event("Streams/interest/add", compact('publisherId', 'title', 'subscribe', 'user', 'stream', 'myInterests'), 'after');
}
Beispiel #7
0
 /**
  * @method shutdownFunction
  * @static
  */
 static function shutdownFunction()
 {
     if ($error = error_get_last()) {
         Q::log($error, 'fatal');
         header('PHP Fatal Error', true, 500);
         // do not expose the error contents
     }
     /**
      * @event Q/shutdown {before}
      */
     Q::event('Q/shutdown', compact('error'), 'before');
     Q_Cache::shutdownFunction();
     if (Q_Session::id()) {
         session_write_close();
     }
 }
Beispiel #8
0
 /**
  * @method writeHandler
  * @static
  * @param {string} $id
  * @param {string} $sess_data
  * @return {boolean}
  */
 static function writeHandler($id, $sess_data)
 {
     try {
         // if the request is AJAX request that came without session cookie, then do not write session, ignore it
         if (Q_Request::isAjax() && !isset($_COOKIE[self::name()])) {
             return false;
         }
         // don't save sessions when running from command-line (cli)
         if (php_sapi_name() == 'cli') {
             return false;
         }
         $our_SESSION = $_SESSION;
         $old_data = self::$sess_data;
         $changed = $sess_data !== $old_data;
         $result = false;
         /**
          * @event Q/session/write {before}
          * @param {string} id
          * @param {string} sess_data
          * @param {string} old_data
          * @param {boolean} changed
          * @return {boolean}
          */
         if (false === Q::event('Q/session/write', compact('id', 'sess_data', 'old_data', 'changed'), 'before')) {
             return false;
         }
         if (empty(self::$session_save_path)) {
             self::$session_save_path = self::savePath();
         }
         if (!empty(self::$session_db_connection)) {
             // Create a new row to be saved in the session table
             $db_row_class = self::$session_db_row_class;
             // Make sure it has a primary key!
             if (count(self::$session_db_row->getPrimaryKey()) != 1) {
                 throw new Q_Exception("The primary key of " . self::$session_db_row_class . " has to consist of exactly 1 field!");
             }
             $id_field = self::$session_db_id_field;
             $data_field = self::$session_db_data_field;
             $updated_field = self::$session_db_updated_field;
             $duration_field = self::$session_db_duration_field;
             $row = self::$session_db_row;
             $row->{$id_field} = $id;
         } else {
             $duration_name = self::durationName();
             $id1 = substr($id, 0, 4);
             $id2 = substr($id, 4);
             $ssp = self::$session_save_path;
             $sess_file = $ssp . DS . "{$duration_name}/{$id1}/{$id2}";
             $dir = $ssp . DS . "{$duration_name}/{$id1}/";
         }
         if ($changed) {
             // Apparently, we want to save some changes.
             // The convention to avoid locking is that everything
             // stored in sessions must be mergeable using the
             // Q_Tree merge algorithm.
             // So we will retrieve the latest session data again,
             // merge our changes over it, and save.
             $params = array('id_field' => $id_field, 'data_field' => $data_field, 'updated_field' => $updated_field, 'duration_field' => $duration_field, 'changed' => $changed, 'sess_data' => $sess_data, 'old_data' => $old_data);
             if (!empty(self::$session_db_connection)) {
                 $row->retrieve();
                 $existing_data = Q::ifset($row, $data_field, "");
                 $params['row'] = $row;
             } else {
                 if (!is_dir($dir)) {
                     mkdir($dir, fileperms($ssp), true);
                 }
                 if (!is_writable($dir)) {
                     // alert the developer to this problem
                     Q::log("{$sess_file} is not writable", 'fatal');
                     die("{$sess_file} is not writable");
                 }
                 $file = fopen($sess_file, "w");
                 if (!$file) {
                     return false;
                 }
                 $params['row'] = $row;
                 $maxlength = Q_Config::get('Q', 'session', 'maxlength', 4095);
                 $existing_data = fread($file, $maxlength);
             }
             $_SESSION = session_decode($existing_data);
             if (!$_SESSION) {
                 $_SESSION = array();
             }
             $t = new Q_Tree($_SESSION);
             $t->merge($our_SESSION);
             $_SESSION = $t->getAll();
             $params['existing_data'] = $existing_data;
             $params['merged_data'] = $merged_data = session_encode();
             /**
              * @event Q/session/save {before}
              * @param {string} sess_data
              * @param {string} old_data
              * @param {string} existing_data
              * @param {string} merged_data
              * @param {boolean} changed
              * @param {Db_Row} row
              * @return {boolean}
              */
             if (false === Q::event('Q/session/save', $params, 'before')) {
                 return false;
             }
             if (!empty(self::$session_db_connection)) {
                 $row->{$data_field} = $merged_data;
                 $row->{$duration_field} = Q_Config::get('Q', 'session', 'durations', Q_Request::formFactor(), Q_Config::expect('Q', 'session', 'durations', 'session'));
                 $row->save();
                 $result = true;
             } else {
                 $result = fwrite($file, $merged_data);
                 fclose($file);
             }
         }
         /**
          * @event Q/session/write {after}
          * @param {string} id
          * @param {boolean} changed
          * @param {string} sess_data
          * @param {string} old_data
          * @param {string} existing_data
          * @param {string} merged_data
          * @param {string} data_field
          * @param {string} updated_field
          * @param {string} duration_field
          * @param {string} sess_file
          * @param {integer} row
          * @return {mixed}
          */
         $result = Q::event('Q/session/write', compact('id', 'data_field', 'updated_field', 'duration_field', 'sess_file', 'row', 'changed', 'sess_data', 'old_data', 'existing_data', 'merged_data'), 'after');
         return $result;
     } catch (Exception $e) {
         Q::log("Exception when writing session {$id}: " . $e->getMessage());
         throw $e;
     }
 }
Beispiel #9
0
 /**
  * @method setCookie
  * @static
  * @param {string} $name
  * @param {string} $value
  * @param {string} [$expires=0]
  * @param {string} [$path=false]
  * @return {string}
  */
 static function setCookie($name, $value, $expires = 0, $path = false)
 {
     if (empty($_SERVER['HTTP_HOST'])) {
         Q::log('Warning: Ignoring call to Q_Response::setCookie() without $_SERVER["HTTP_HOST"]' . PHP_EOL);
         return false;
     }
     if (isset($_COOKIE[$name]) and $_COOKIE[$name] === $value) {
         return;
     }
     $parts = parse_url(Q_Request::baseUrl());
     $path = $path ? $path : !empty($parts['path']) ? $parts['path'] : '/';
     $domain = '.' . $parts['host'];
     setcookie($name, $value, $expires, $path, $domain);
     $_COOKIE[$name] = $value;
     $header = 'P3P: CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA"';
     $header = Q::event('Q/Response/setCookie', compact('name', 'value', 'expires', 'path', 'header'), 'before', false, $header);
     header($header);
     return $value;
 }
Beispiel #10
0
 /**
  * @method sendMessage
  * @param {string} $view
  *  The name of a view for the message. Fields are passed to this array.
  * @param {array} $fields=array()
  *  The fields referenced in the subject and/or view
  * @param {array} $options=array()
  *  Array of options. Can include:<br/>
  *  "delay" => A delay, in milliseconds, to wait until sending email. Only works if Node server is listening.
  * @return {boolean}
  * @throws {Q_Exception_WrongType}
  *	If phone number is invalid
  */
 function sendMessage($view, $fields = array(), $options = array())
 {
     /**
      * @event Users/sms/sendMessage {before}
      * @param {string} view
      * @param {array} fields
      * @param {array} options
      * @return {boolean}
      */
     $result = Q::event('Users/sms/sendMessage', compact('view', 'fields', 'options'), 'before');
     if (isset($result)) {
         return $result;
     }
     if (!Q_Valid::phone($this->number, $number)) {
         throw new Q_Exception_WrongType(array('field' => '$this->number', 'type' => 'mobile number', 'mobileNumber' => $this->number));
     }
     $app = Q_Config::expect('Q', 'app');
     $body = Q::view($view, $fields);
     $overrideLog = Q::event('Users/mobile/log', compact('mobileNumber', 'body'), 'before');
     if (is_null($overrideLog) and $key = Q_Config::get('Users', 'mobile', 'log', 'key', null)) {
         Q::log("\nSent mobile message to {$this->number}:\n{$body}", $key);
     }
     $sent = false;
     if (!empty($options['delay'])) {
         // Try to use Node.js to send the message
         $sent = Q_Utils::sendToNode(array("Q/method" => "Users/sendMessage", "delay" => $options['delay'], "mobileNumber" => $number, "body" => $body, "options" => $options));
     }
     if (!$sent) {
         $from = Q::ifset($options, 'from', Q_Config::get('Users', 'mobile', 'from', null));
         if (!isset($from)) {
             // deduce from base url
             $url_parts = parse_url(Q_Request::baseUrl());
             $domain = $url_parts['host'];
             $from = array("notifications@{$domain}", $domain);
         }
         $sid = Q_Config::get('Users', 'mobile', 'twilio', 'sid', null);
         $token = Q_Config::get('Users', 'mobile', 'twilio', 'token', null);
         if ($sid and $token) {
             $client = new Services_Twilio($sid, $token);
             $message = $client->account->sms_messages->create($from, $number, Q::view($view, $fields));
         } else {
             if (!Q_Config::get('Users', 'email', 'smtp', null)) {
                 Q_Response::setNotice("Q/mobile", "Please set up transport in Users/mobile/twilio as in docs", false);
                 return true;
             }
             if (!is_array($from)) {
                 $from = array($from, "{$app} activation");
             }
             // Set up the default mail transport
             $host = Q_Config::get('Users', 'email', 'smtp', 'host', 'sendmail');
             if ($host === 'sendmail') {
                 $transport = new Zend_Mail_Transport_Sendmail('-f' . reset($from));
             } else {
                 if (is_array($host)) {
                     $smtp = $host;
                     $host = $smtp['host'];
                     unset($smtp['host']);
                 } else {
                     $smtp = null;
                 }
                 $transport = new Zend_Mail_Transport_Smtp($host, $smtp);
             }
             $mail = new Zend_Mail();
             $from_name = reset($from);
             $mail->setFrom(next($from), $from_name);
             $gateways = Q_Config::get('Users', 'mobile', 'gateways', array('at&t' => 'txt.att.net', 'sprint' => 'messaging.sprintpcs.com', 'verizon' => 'vtext.com', 't-mobile' => 'tmomail.net'));
             $number2 = substr($this->number, 2);
             foreach ($gateways as $k => $v) {
                 $mail->addTo($number2 . '@' . $v);
             }
             $mail->setBodyText($body);
             try {
                 $mail->send($transport);
             } catch (Exception $e) {
                 throw new Users_Exception_MobileMessage(array('error' => $e->getMessage()));
             }
         }
     }
     /**
      * @event Users/sms/sendMessage {after}
      * @param {string} view
      * @param {array} fields
      * @param {array} options
      * @param {string} mail
      */
     Q::event('Users/email/sendMessage', compact('view', 'fields', 'options', 'mail', 'app'), 'after');
     return true;
 }
Beispiel #11
0
 /**
  * Gets the key into the associative $pdo_array
  * corresponding to some database credentials.
  * @method pdo
  * @protected
  * @static
  * @param {string} $dsn The dsn to create PDO
  * @param {string} $username Username for connection
  * @param {string} $password Passwork for connection
  * @param {array} $driver_options Driver options
  * @return {PDO}
  */
 protected static function pdo($dsn, $username, $password, $driver_options)
 {
     $key = $dsn . $username . $password . serialize($driver_options);
     if (isset(self::$pdo_array[$key])) {
         return self::$pdo_array[$key];
     }
     // Make a new connection to a database!
     try {
         self::$pdo_array[$key] = @new PDO($dsn, $username, $password, $driver_options);
     } catch (Exception $e) {
         if (class_exists('Q_Config') and Q_Config::get('Db', 'exceptions', 'log', true)) {
             Q::log($e);
         }
         throw $e;
     }
     return self::$pdo_array[$key];
 }
Beispiel #12
0
 /**
  * Renders a different tag based on what you specified.
  * @method smartTag
  * @static
  * @param {string} $type The type of the tag. Could be one of
  *  'static', 'boolean', 'text', 'email', 'tel', 
  *  'textarea', 'password', 'select', 
  *  'radios', 'checkboxes', 'buttons', 'submit_buttons',
  *  'submit', 'hidden', 'image', or the name of a tag.
  * @param {array|string} [$attributes=array()] The attributes for the resulting element. Should at least include the name. You can also just pass the name as a string here.
  * @param {array} [$value=null] The value to start out with in the resulting element. If there are options present, this should be the value of one of the options.
  * @param {array} [$options=null] Associative array of options, used if the tag type is 'select', 'radios' or 'checkboxes'.
  * @param {array} [$params=array()] Additional parameters to pass to the corresponding function
  * @return {string} The generated markup
  */
 static function smartTag($type, $attributes = array(), $value = null, $options = null, $params = array())
 {
     if (!isset($type)) {
         throw new Q_Exception_RequiredField(array('field' => 'type'));
     }
     if (is_string($attributes)) {
         $attributes = array('name' => $attributes);
     }
     if (!is_array($attributes)) {
         $attributes = array();
     }
     $id = isset($attributes['id']) ? $attributes['id'] : null;
     switch ($type) {
         case 'hidden':
             return self::hidden($value, isset($attributes['name']) ? $attributes['name'] : null, isset($params[0]) ? $params[0] : true);
         case 'static':
             unset($attributes['name']);
             if (empty($options['date'])) {
                 $display = isset($options[$value]) ? $options[$value] : $value;
             } else {
                 $v = is_numeric($value) ? $value : strtotime($value);
                 $display = (!empty($v) and substr($v, 0, 4) !== '0000') ? date($options['date'], $v) : '';
                 Q::log("\n\n{$v}\n{$display}\n\n");
             }
             return self::tag('span', $attributes, $display);
         case 'boolean':
             $attributes['type'] = 'checkbox';
             if (!empty($value)) {
                 $attributes['checked'] = 'checked';
             }
             return self::tag('input', $attributes);
         case 'text':
         case 'submit':
         case 'email':
         case 'tel':
             $attributes['type'] = $type;
             $attributes['value'] = $value;
             return self::tag('input', $attributes);
         case 'textarea':
             if (!isset($attributes['rows'])) {
                 $attributes['rows'] = 5;
             }
             if (!isset($attributes['cols'])) {
                 $attributes['cols'] = 20;
             }
             return self::tag('textarea', $attributes, self::text($value));
         case 'password':
             $attributes['type'] = 'password';
             $attributes['maxlength'] = 64;
             $attributes['value'] = '';
             // passwords should be cleared
             return self::tag('input', $attributes);
         case 'select':
             return self::tag('select', $attributes) . self::options($options, $id, $value, isset($params[0]) ? $params[0] : null, isset($params[1]) ? $params[1] : '', isset($params[2]) ? $params[2] : array()) . "</select>";
         case 'radios':
             unset($attributes['value']);
             return "<div>" . self::radios($attributes['name'], $options, $id, $value, "</div><div>", $attributes, isset($params[0]) ? $params[0] : array()) . "</div>";
         case 'checkboxes':
             unset($attributes['value']);
             return "<div>" . self::checkboxes($attributes['name'], $options, $id, $value, "</div><div>", $attributes) . "</div>";
         case 'buttons':
             unset($attributes['value']);
             return "<div>" . self::buttons($attributes['name'], $options, $id, '', $attributes) . "</div>";
         case 'submit_buttons':
             unset($attributes['value']);
             $attributes['type'] = 'submit';
             return "<div>" . self::buttons($attributes['name'], $options, $id, '', $attributes) . "</div>";
         case 'image':
             $attributes['src'] = $value;
             $attributes['alt'] = $type;
             return self::tag('img', $attributes);
         case 'date':
             return self::date($attributes['name'], $value, $options, $attributes);
         default:
             return self::tag($type, $attributes, $value);
     }
 }
Beispiel #13
0
 /**
  * @method setCookie
  * @static
  * @param {string} $name The name of the cookie
  * @param {string} $value The value of the cookie
  * @param {string} [$expires=0] The number of seconds since the epoch, 0 means never expires
  * @param {string} [$path=false] You can specify a path on the server here for the cookie
  * @return {string}
  */
 static function setCookie($name, $value, $expires = 0, $path = false)
 {
     if (empty($_SERVER['HTTP_HOST'])) {
         Q::log('Warning: Ignoring call to Q_Response::setCookie() without $_SERVER["HTTP_HOST"]' . PHP_EOL);
         return false;
     }
     if (isset($_COOKIE[$name]) and $_COOKIE[$name] === $value) {
         return;
     }
     if (Q_Dispatcher::$startedResponse) {
         throw new Q_Exception("Q_Response::setCookie must be called before Q/response event");
     }
     // see https://bugs.php.net/bug.php?id=38104
     self::$cookies[$name] = array($value, $expires, $path);
     $_COOKIE[$name] = $value;
     return $value;
 }
Beispiel #14
0
/**
 * Front controller for Q
 */
include dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Q.inc.php';
//
// Handle batch request
//
$urls = Q::ifset($_REQUEST['urls'], array());
Q::log("Batch request for " . count($urls) . " urls");
header("Content-type: application/json");
Q_Response::$batch = true;
echo "[";
$original_request = $_REQUEST;
foreach ($urls as $i => $url) {
    $request = parse_url($url);
    parse_str($request['query'], $_REQUEST);
    $request = explode('?', $url);
    echo "[";
    if (!empty($request[0])) {
        Q_ActionController::execute($request[0]);
    }
    echo "]";
    if (isset($urls[$i + 1])) {
        echo ',';
    }
}
$_REQUEST = $original_request;
echo "]";
Q::log("~" . ceil(Q::milliseconds()) . 'ms+' . ceil(memory_get_peak_usage() / 1000) . 'kb.' . " batch complete.");
Beispiel #15
0
function Streams_before_Q_objects()
{
    $token = Q_Request::special('Streams.token', null);
    if ($token === null) {
        return;
    }
    $invite = Streams_Invite::fromToken($token);
    if (!$invite) {
        throw new Q_Exception_MissingRow(array('table' => 'invite', 'criteria' => "token = '{$token}"), 'token');
    }
    // did invite expire?
    $ts = Streams_Invite::db()->select("CURRENT_TIMESTAMP")->fetchAll(PDO::FETCH_NUM);
    if (isset($invite->expireTime) and $invite->expireTime < $ts[0][0]) {
        $invite->state = 'expired';
        $invite->save();
    }
    // is invite still pending?
    if ($invite->state !== 'pending') {
        switch ($invite->state) {
            case 'expired':
                $exception = new Streams_Exception_AlreadyExpired(null, 'token');
                break;
            case 'accepted':
                $exception = new Streams_Exception_AlreadyAccepted(null, 'token');
                break;
            case 'declined':
                $exception = new Streams_Exception_AlreadyDeclined(null, 'token');
                break;
            case 'forwarded':
                $exception = new Streams_Exception_AlreadyForwarded(null, 'token');
                break;
            default:
                $exception = new Q_Exception("This invite has already been " . $invite->state, 'token');
                break;
        }
        $shouldThrow = Q::event('Streams/objects/inviteException', compact('invite', 'exception'), 'before');
        if ($shouldThrow === null) {
            Q_Response::setNotice('Streams/objects', $exception->getMessage(), true);
        } else {
            if ($shouldThrow === true) {
                throw $exception;
            }
        }
    }
    // now process the invite
    $invitedUser = Users_User::fetch($invite->userId, true);
    $stream = Streams::fetchOne($invitedUser->id, $invite->publisherId, $invite->streamName);
    if (!$stream) {
        throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => "publisherId = '{$invite->publisherId}', name = '{$invite->streamName}'"));
    }
    $byUser = Users_User::fetch($invite->invitingUserId, true);
    $byStream = Streams::fetchOne($byUser->id, $invite->publisherId, $invite->streamName);
    if (!$byStream) {
        throw new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => "publisherId = '{$invite->publisherId}', name = '{$invite->streamName}'"));
    }
    $access = new Streams_Access();
    $access->publisherId = $byStream->publisherId;
    $access->streamName = $byStream->name;
    $access->ofUserId = $invite->userId;
    $specified_access = false;
    foreach (array('readLevel', 'writeLevel', 'adminLevel') as $level_type) {
        $access->{$level_type} = -1;
        if (empty($invite->{$level_type})) {
            continue;
        }
        // Give access level from the invite.
        // However, if inviting user has a lower access level now,
        // then give that level instead, unless it is lower than
        // what the invited user would have had otherwise.
        $min = min($invite->{$level_type}, $byStream->get($level_type, 0));
        if ($min > $stream->get($level_type, 0)) {
            $access->{$level_type} = $min;
            $specified_access = true;
        }
    }
    if ($specified_access) {
        $access->save(true);
    }
    // now log invited user in
    $user = Users::loggedInUser();
    if (empty($user) or $user->id !== $invite->userId) {
        $user = new Users_User();
        $user->id = $invite->userId;
        if (!$user->retrieve()) {
            // The user who was invited doesn't exist
            // This shouldn't happen. We just silently log it and return.
            Q::log("Sanity check failed: invite with {$invite->token} pointed to nonexistent user");
            return;
        }
        Users::setLoggedInUser($user);
    }
    // accept invite and autosubscribe if first time
    if ($invite->accept() and !$stream->subscription($user->id)) {
        $stream->subscribe();
    }
    // retain the invite object for further processing
    Streams::$followedInvite = $invite;
}
Beispiel #16
0
 /**
  * Send e-mail message
  * @method sendMessage
  * @param {string} $subject
  *  The subject. May contain variable references to members
  *  of the $fields array.
  * @param {string} $view
  *  The name of a view for the body. Fields are passed to it.
  * @param {array} $fields=array()
  *  The fields referenced in the subject and/or view
  * @param {array} $options=array()
  *  Array of options. Can include:<br/>
  *  "html" => Defaults to false. Whether to send as HTML email.<br/>
  *  "name" => A human-readable name in addition to the address.<br/>
  *  "from" => An array of (emailAddress, human_readable_name)<br/>
  *  "delay" => A delay, in milliseconds, to wait until sending email. Only works if Node server is listening.
  */
 function sendMessage($subject, $view, $fields = array(), $options = array())
 {
     /**
      * @event Users/email/sendMessage {before}
      * @param {string} subject
      * @param {string} view
      * @param {array} fields
      * @param {array} options
      * @return {boolean}
      */
     $result = Q::event('Users/email/sendMessage', compact('subject', 'view', 'fields', 'options'), 'before');
     if (isset($result)) {
         return $result;
     }
     if (!Q_Valid::email($this->address, $emailAddress)) {
         throw new Q_Exception_WrongType(array('field' => '$this->address', 'type' => 'email address', 'emailAddress' => $this->address));
     }
     $app = Q_Config::expect('Q', 'app');
     $subject = Q_Handlebars::renderSource($subject, $fields);
     $body = Q::view($view, $fields);
     if (!Q_Config::get('Users', 'email', 'smtp', 'sendmail')) {
         Q_Response::setNotice("Q/email", "Please set up SMTP in Users/email/smtp as in docs.", false);
         return true;
     }
     $overrideLog = Q::event('Users/email/log', compact('emailAddress', 'subject', 'body'), 'before');
     if (!isset($overrideLog) and $key = Q_Config::get('Users', 'email', 'log', 'key', null)) {
         Q::log("\nSent email message to {$emailAddress}:\n{$subject}\n{$body}", $key);
     }
     $from = Q::ifset($options, 'from', Q_Config::get('Users', 'email', 'from', null));
     if (!isset($from)) {
         // deduce from base url
         $url_parts = parse_url(Q_Request::baseUrl());
         $domain = $url_parts['host'];
         $from = array("email@{$domain}", $domain);
     }
     if (!is_array($from)) {
         throw new Q_Exception_WrongType(array('field' => '$options["from"]', 'type' => 'array'));
     }
     $sent = false;
     if (!empty($options['delay'])) {
         // Try to use Node.js to send the message
         $sent = Q_Utils::sendToNode(array("Q/method" => "Users/sendMessage", "delay" => $options['delay'], "emailAddress" => $emailAddress, "subject" => $subject, "body" => $body, "options" => $options));
     }
     if (!$sent) {
         // Set up the default mail transport
         $smtp = Q_Config::get('Users', 'email', 'smtp', array('host' => 'sendmail'));
         $host = Q::ifset($smtp, 'host', 'sendmail');
         if ($host === 'sendmail') {
             $transport = new Zend_Mail_Transport_Sendmail('-f' . reset($from));
         } else {
             if (is_array($smtp)) {
                 $host = $smtp['host'];
                 unset($smtp['host']);
             } else {
                 if (is_string($smtp)) {
                     $host = $smtp;
                     $smtp = null;
                 }
             }
             $transport = new Zend_Mail_Transport_Smtp($host, $smtp);
         }
         $mail = new Zend_Mail();
         $mail->setFrom(reset($from), next($from));
         if (isset($options['name'])) {
             $mail->addTo($emailAddress, $options['name']);
         } else {
             $mail->addTo($emailAddress);
         }
         $mail->setSubject($subject);
         if (empty($options['html'])) {
             $mail->setBodyText($body);
         } else {
             $mail->setBodyHtml($body);
         }
         try {
             $mail->send($transport);
         } catch (Exception $e) {
             throw new Users_Exception_EmailMessage(array('error' => $e->getMessage()));
         }
     }
     /**
      * @event Users/email/sendMessage {after}
      * @param {string} subject
      * @param {string} view
      * @param {array} fields
      * @param {array} options
      * @param {string} mail
      */
     Q::event('Users/email/sendMessage', compact('subject', 'view', 'fields', 'options', 'mail', 'app'), 'after');
     return true;
 }
Beispiel #17
0
 /**
  * Instantiates a particular tool.
  * Also generates javascript around it.
  * @method tool
  * @static
  * @param {string} $name
  *  The name of the tool, of the form "$moduleName/$toolName"
  *  The handler is found in handlers/$moduleName/tool/$toolName
  *  Also can be an array of $toolName => $toolOptions, in which case the
  *  following parameter, $options, is skipped.
  * @param {array} $options=array()
  *  The options passed to the tool (or array of options arrays passed to the tools).
  * @param {array} [$extra=array()] Options used by Qbix when rendering the tool.
  * @param {string} [$extra.id]
  *    An additional ID to distinguish tools instantiated
  *    side-by-side from each other, within the same parent HTMLElement.
  * @param {boolean} [$extra.cache=false]
  *    If true, then the Qbix front end will not replace existing tools with same id
  *    during Q.loadUrl when this tool appears in the rendered HTML
  * @param {boolean} [$extra.merge=false]
  *    If true, the element for this tool is merged with the element of the tool
  *    already being rendered (if any), producing one element with markup
  *    for both tools and their options. This can be used more than once, merging
  *    multiple tools in one element.
  *    As part of the mege, the content this tool (if any) is prepended
  *    to the content of the tool which is already being rendered.
  * @throws {Q_Exception_WrongType}
  * @throws {Q_Exception_MissingFile}
  */
 static function tool($name, $options = array(), $extra = array())
 {
     if (is_string($name)) {
         $info = array($name => $options);
     } else {
         $info = $name;
         $extra = $options;
     }
     $oldToolName = self::$toolName;
     /**
      * @event Q/tool/render {before}
      * @param {string} info
      *  An array of $toolName => $options pairs
      * @param {array} extra
      *  Options used by Qbix when rendering the tool. Can include:<br/>
      *  "id" =>
      *    an additional ID to distinguish tools instantiated
      *    side-by-side from each other. Usually numeric.<br/>
      *  "cache" =>
      *    if true, then the Qbix front end will not replace existing tools with same id
      *    during Q.loadUrl when this tool appears in the rendered HTML
      * @return {string|null}
      *  If set, override the method return
      */
     $returned = Q::event('Q/tool/render', array('info' => $info, 'extra' => &$extra), 'before');
     $result = '';
     $exception = null;
     foreach ($info as $name => $options) {
         Q::$toolName = $name;
         $toolHandler = "{$name}/tool";
         $options = is_array($options) ? $options : array();
         if (is_array($returned)) {
             $options = array_merge($returned, $options);
         }
         try {
             /**
              * Renders the tool
              * @event $toolHandler
              * @param {array} $options
              *  The options passed to the tool
              * @return {string}
              *	The rendered tool content
              */
             $result .= Q::event($toolHandler, $options);
             // render the tool
         } catch (Q_Exception_MissingFile $e) {
             /**
              * Renders the 'Missing Tool' content
              * @event Q/missingTool
              * @param {array} name
              *  The name of the tool
              * @return {string}
              *	The rendered content
              */
             $params = $e->params();
             if ($params['filename'] === str_replace('/', DS, "handlers/{$toolHandler}.php")) {
                 $result .= self::event('Q/missingTool', compact('name', 'options'));
             } else {
                 $exception = $e;
             }
         } catch (Exception $e) {
             $exception = $e;
         }
         if ($exception) {
             Q::log($exception);
             $result .= $exception->getMessage();
         }
         Q::$toolName = $name;
         // restore the current tool name
     }
     // Even if the tool rendering throws an exception,
     // it is important to run the "after" handlers
     /**
      * @event Q/tool/render {after}
      * @param {string} info
      *  An array of $toolName => $options pairs
      * @param {array} 'extra'
      *  Options used by Qbix when rendering the tool. Can include:<br/>
      *  "id" =>
      *    an additional ID to distinguish tools instantiated
      *    side-by-side from each other. Usually numeric.<br/>
      *  "cache" =>
      *    if true, then the Qbix front end will not replace existing tools with same id
      *    during Q.loadUrl when this tool appears in the rendered HTML
      */
     Q::event('Q/tool/render', compact('info', 'extra'), 'after', false, $result);
     Q::$toolName = $oldToolName;
     return $result;
 }
Beispiel #18
0
 /**
  * The standard action front controller
  * @method execute
  * @static
  * @throws {Q_Exception_BadUrl}
  * @throws {Q_Exception}
  * @throws {Q_Exception_MissingConfig}
  */
 static function execute($url = null)
 {
     // Fixes for different platforms:
     if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
         // ISAPI 3.0
         $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL'];
     }
     // Set the controller that is being used
     if (!isset(Q::$controller)) {
         Q::$controller = 'Q_ActionController';
     }
     try {
         $slots = Q_Request::slotNames(false);
         $slots = $slots ? ' slots: (' . implode(',', $slots) . ') from' : '';
         $method = Q_Request::method();
         Q::log("{$method}{$slots} url: " . Q_Request::url(true));
         $tail = Q_Request::tail($url);
         if (!isset($tail)) {
             // Bad url was requested somehow
             $url = Q_Request::url(true);
             $base_url = Q_Request::baseUrl(true);
             throw new Q_Exception_BadUrl(compact('base_url', 'url'));
         }
         $parts = explode('/', $tail);
         $parts_len = count($parts);
         if ($parts_len >= 1) {
             $module = $parts[0];
         }
         if ($parts_len >= 2) {
             $action = $parts[1];
         }
         if (empty($module) or empty($action)) {
             throw new Q_Exception("Not implemented");
         }
         // Make sure the 'Q'/'web' config fields are set,
         // otherwise URLs will be formed pointing to the wrong
         // controller script.
         $ar = Q_Config::get('Q', 'web', 'appRootUrl', null);
         if (!isset($ar)) {
             throw new Q_Exception_MissingConfig(array('fieldpath' => 'Q/web/appRootUrl'));
         }
         // Dispatch the request
         $uri = Q_Uri::from(compact('module', 'action'));
         Q_Dispatcher::dispatch($uri);
         $dispatchResult = Q_Dispatcher::result();
         if (!isset($dispatchResult)) {
             $dispatchResult = 'Ran dispatcher';
         }
         if ($module and $action) {
             $slotNames = Q_Request::slotNames();
             $requestedSlots = empty($slotNames) ? '' : implode(',', $slotNames);
             Q::log("~" . ceil(Q::milliseconds()) . 'ms+' . ceil(memory_get_peak_usage() / 1000) . 'kb.' . " {$dispatchResult} for {$module}/{$action}" . " ({$requestedSlots})");
         } else {
             Q::log("~" . ceil(Q::milliseconds()) . 'ms+' . ceil(memory_get_peak_usage() / 1000) . 'kb.' . " No route for " . $_SERVER['REQUEST_URI']);
         }
     } catch (Exception $exception) {
         /**
          * @event Q/exception
          * @param {Exception} exception
          */
         Q::event('Q/exception', compact('exception'));
     }
 }
Beispiel #19
0
 /**
  * @method writeHandler
  * @static
  * @param {string} $id
  * @param {string} $sess_data
  * @return {boolean}
  */
 static function writeHandler($id, $sess_data)
 {
     try {
         // if the request is AJAX request that came without session cookie, then do not write session, ignore it
         if (Q_Request::isAjax() && !isset($_COOKIE[self::name()])) {
             return false;
         }
         // don't save sessions when running from command-line (cli)
         if (php_sapi_name() == 'cli') {
             return false;
         }
         /**
          * @event Q/session/write {before}
          * @param {string} id
          * @param {string} sess_data
          * @return {boolean}
          */
         if (false === Q::event('Q/session/write', compact('id', 'sess_data'), 'before')) {
             return false;
         }
         if (empty(self::$session_save_path)) {
             self::$session_save_path = self::savePath();
         }
         if (!empty(self::$session_db_connection)) {
             // Create a new row to be saved in the session table
             $db_row_class = self::$session_db_row_class;
             // Make sure it has a primary key!
             if (count(self::$session_db_row->getPrimaryKey()) != 1) {
                 throw new Q_Exception("The primary key of " . self::$session_db_row_class . " has to consist of exactly 1 field!");
             }
             $id_field = self::$session_db_id_field;
             $data_field = self::$session_db_data_field;
             $updated_field = self::$session_db_updated_field;
             $duration_field = self::$session_db_duration_field;
             $row = self::$session_db_row;
             if (!$row->wasRetrieved()) {
                 $row->{$id_field} = $id;
             }
             $row->{$data_field} = $sess_data;
             $row->{$duration_field} = Q_Config::get('Q', 'session', 'durations', Q_Request::formFactor(), Q_Config::expect('Q', 'session', 'durations', 'session'));
             /**
              * @event Q/session/save {before}
              * @param {string} id
              * @param {string} sess_data
              * @return {boolean}
              */
             if (false === Q::event('Q/session/save', array('row' => $row, 'id_field' => $id_field, 'data_field' => $data_field, 'updated_field' => $updated_field, 'duration_field' => $duration_field), 'before')) {
                 return false;
             }
             $row->save(false, true);
             $result = true;
         } else {
             $duration_name = self::durationName();
             $id1 = substr($id, 0, 4);
             $id2 = substr($id, 4);
             $ssp = self::$session_save_path;
             $sess_file = $ssp . DS . "{$duration_name}/{$id1}/{$id2}";
             $dir = $ssp . DS . "{$duration_name}/{$id1}/";
             if (!is_dir($dir)) {
                 mkdir($dir, fileperms($ssp), true);
             }
             if (!is_writable($dir)) {
                 // alert the developer to this problem
                 Q::log("{$sess_file} is not writable", 'fatal');
                 die("{$sess_file} is not writable");
             }
             $fp = fopen($sess_file, "w");
             if (!$fp) {
                 return false;
             }
             $result = fwrite($fp, $sess_data);
             fclose($fp);
         }
         $changed = $sess_data !== self::$sess_data;
         $old_data = self::$sess_data;
         /**
          * @event Q/session/write {after}
          * @param {string} id
          * @param {string} sess_data
          * @param {string} data_field
          * @param {string} updated_field
          * @param {string} duration_field
          * @param {string} sess_file
          * @param {integer} row
          * @param {boolean} changed
          * @param {string} old_data
          * @return {mixed}
          */
         $result = Q::event('Q/session/write', compact('id', 'sess_data', 'data_field', 'updated_field', 'duration_field', 'sess_file', 'row', 'changed', 'old_data'), 'after');
         return $result;
     } catch (Exception $e) {
         Q::log("Exception when writing session {$id}: " . $e->getMessage());
         throw $e;
     }
 }