Beispiel #1
0
function Streams_interests_response()
{
    // serve a javascript file and tell client to cache it
    $app = Q_Config::expect('Q', 'app');
    $communityId = Q::ifset($_REQUEST, 'communityId', $app);
    $tree = new Q_Tree();
    $tree->load("files/Streams/interests/{$communityId}.json");
    $categories = $tree->getAll();
    foreach ($categories as $category => &$v1) {
        foreach ($v1 as $k2 => &$v2) {
            if (!Q::isAssociative($v2)) {
                ksort($v1);
                break;
            }
            ksort($v2);
        }
    }
    header('Content-Type: text/javascript');
    header("Pragma: ", true);
    // 1 day
    header("Cache-Control: public, max-age=86400");
    // 1 day
    $expires = date("D, d M Y H:i:s T", time() + 86400);
    header("Expires: {$expires}");
    // 1 day
    $json = Q::json_encode($categories, true);
    echo "Q.setObject(['Q', 'Streams', 'Interests', 'all', '{$communityId}'], {$json});";
    return false;
}
Beispiel #2
0
/**
 * Generates a form with inputs that modify various streams
 * @class Streams form
 * @constructor
 * @param {array} $options
 *  An associative array of parameters, containing:
 * @param {array} [$options.fields] an associative array of $id => $fieldinfo pairs,
 *   where $id is the id to append to the tool's id, to generate the input's id,
 *   and fieldinfo is either an associative array with the following fields,
 *   or a regular array consisting of fields in the following order:
 *     "publisherId" => Required. The id of the user publishing the stream
 *     "streamName" => Required. The name of the stream
 *     "field" => The stream field to edit, or "attribute:$attributeName" for an attribute.
 *     "input" => The type of the input (@see Q_Html::smartTag())
 *     "attributes" => Additional attributes for the input
 *     "options" => options for the input (if type is "select", "checkboxes" or "radios")
 *     "params" => array of extra parameters to Q_Html::smartTag
 */
function Streams_form_tool($options)
{
    $fields = Q::ifset($options, 'fields', array());
    $defaults = array('publisherId' => null, 'streamName' => null, 'field' => null, 'type' => 'text', 'attributes' => array(), 'value' => array(), 'options' => array(), 'params' => array());
    $sections = array();
    $hidden = array();
    $contents = '';
    foreach ($fields as $id => $field) {
        if (Q::isAssociative($field)) {
            $r = Q::take($field, $defaults);
        } else {
            $c = count($field);
            if ($c < 4) {
                throw new Q_Exception("Streams/form tool: field needs at least 4 values");
            }
            $r = array('publisherId' => $field[0], 'streamName' => $field[1], 'field' => $field[2], 'type' => $field[3], 'attributes' => isset($field[4]) ? $field[4] : array(), 'value' => isset($field[5]) ? $field[5] : '', 'options' => isset($field[6]) ? $field[6] : null, 'params' => isset($field[7]) ? $field[7] : null);
        }
        $r['attributes']['name'] = "input_{$id}";
        if (!isset($r['type'])) {
            var_dump($r['type']);
            exit;
        }
        $stream = Streams::fetchOne(null, $r['publisherId'], $r['streamName']);
        if ($stream) {
            if (substr($r['field'], 0, 10) === 'attribute:') {
                $attribute = trim(substr($r['field'], 10));
                $value = $stream->get($attribute, $r['value']);
            } else {
                $field = $r['field'];
                $value = $stream->{$field};
            }
        } else {
            $value = $r['value'];
        }
        $tag = Q_Html::smartTag($r['type'], $r['attributes'], $value, $r['options'], $r['params']);
        $class1 = 'publisherId_' . Q_Utils::normalize($r['publisherId']);
        $class2 = 'streamName_' . Q_Utils::normalize($r['streamName']);
        $contents .= "<span class='Q_before {$class1} {$class2}'></span>" . Q_Html::tag('span', array('data-publisherId' => $r['publisherId'], 'data-streamName' => $r['streamName'], 'data-field' => $r['field'], 'data-type' => $r['type'], 'class' => "{$class1} {$class2}"), $tag);
        $hidden[$id] = array(!!$stream, $r['publisherId'], $r['streamName'], $r['field']);
    }
    $contents .= Q_Html::hidden(array('inputs' => Q::json_encode($hidden)));
    return Q_Html::form('Streams/form', 'post', array(), $contents);
    //
    // $fields = array('onSubmit', 'onResponse', 'onSuccess', 'slotsToRequest', 'loader', 'contentElements');
    // Q_Response::setToolOptions(Q::take($options, $fields));
    // Q_Response::addScript('plugins/Q/js/tools/form.js');
    // Q_Response::addStylesheet('plugins/Q/css/form.css');
    // return $result;
}
Beispiel #3
0
 static function getExtendClasses($type)
 {
     static $result = array();
     if (isset($result[$type])) {
         return $result[$type];
     }
     $extend = Q_Config::get('Streams', 'types', $type, 'extend', null);
     if (is_string($extend)) {
         $extend = array($extend);
     }
     $classes = array();
     if ($extend) {
         if (!Q::isAssociative($extend)) {
             $temp = array();
             foreach ($extend as $k) {
                 $temp[$k] = true;
             }
             $extend = $temp;
         }
         foreach ($extend as $k => $v) {
             if (!class_exists($k, true)) {
                 throw new Q_Exception_MissingClass(array('className' => $k));
             }
             if (!is_subclass_of($k, 'Db_Row')) {
                 throw new Q_Exception_BadValue(array('internal' => "Streams/types/{$type}/extend", 'problem' => "{$k} must extend Db_Row"));
             }
             if ($v === true) {
                 $v = call_user_func(array('Base_' . $k, 'fieldNames'));
                 $v = array_diff($v, array('publisherId', 'streamName', 'name'));
             } else {
                 if (Q::isAssociative($v)) {
                     $v = array_keys($v);
                 }
             }
             $classes[$k] = $v;
         }
     }
     return $result[$type] = $classes;
 }
Beispiel #4
0
 /**
  * If an array is not associative, then makes an associative array
  * with the keys taken from the values of the regular array
  * @param {array} $array
  * @param {array} [$value=true] The value to assign to each item in the generated array
  * @return {array}
  */
 static function makeAssociative($array, $value = true)
 {
     if (Q::isAssociative($array)) {
         return $array;
     }
     $result = array();
     foreach ($array as $item) {
         $result[$item] = $value;
     }
     return $result;
 }
Beispiel #5
0
 /**
  * Sets one of the attributes of a style to a value.
  * @method setStyle
  * @static
  * @param {string|array} $keys Can be a key or array of keys in the style of Q_Tree->set
  * @param {mixed} [$value=null]
  * @param {string} [$slotName=null]
  */
 static function setStyle($keys, $value = null, $slotName = null)
 {
     if (Q::isAssociative($keys)) {
         foreach ($keys as $k => $v) {
             self::setStyle($k, $v, $value);
         }
         return;
     }
     $args = is_array($keys) ? $keys : array($keys);
     $args[] = $value;
     $p = new Q_Tree(self::$styles);
     call_user_func_array(array($p, 'set'), $args);
     // Now, for the slot
     if (!isset($slotName)) {
         $slotName = isset(self::$slotName) ? self::$slotName : '';
     }
     if (!isset(self::$stylesForSlot[$slotName])) {
         self::$stylesForSlot[$slotName] = array();
     }
     $p = new Q_Tree(self::$stylesForSlot[$slotName]);
     call_user_func_array(array($p, 'set'), $args);
 }
Beispiel #6
0
 /**
  * Post (potentially) multiple messages to multiple streams.
  * @method postMessages
  * @static
  * @param {string} $asUserId
  *  The user to post the message as
  * @param {string} $messages
  *  Array indexed as follows:
  *  array($publisherId => array($streamName => $message))
  *  where $message are either Streams_Message objects, 
  *  or arrays containing all the fields of messages that will need to be posted.
  * @param {booleam} $skipAccess=false
  *  If true, skips the access checks and just posts the message.
  * @return {array}
  *  Returns an array(array(Streams_Message), array(Streams_Stream))
  */
 static function postMessages($asUserId, $messages, $skipAccess = false)
 {
     if (!isset($asUserId)) {
         $asUserId = Users::loggedInUser();
         if (!$asUserId) {
             $asUserId = "";
         }
     }
     if ($asUserId instanceof Users_User) {
         $asUserId = $asUserId->id;
     }
     // Build arrays we will need
     foreach ($messages as $publisherId => $arr) {
         if (!is_array($arr)) {
             throw new Q_Exception_WrongType(array('field' => "messages", 'type' => 'array of publisherId => streamName => message'));
         }
         foreach ($arr as $streamName => &$message) {
             if (!is_array($message)) {
                 if (!$message instanceof Streams_Message) {
                     throw new Q_Exception_WrongType(array('field' => "message under {$publisherId} => {$streamName}", 'type' => 'array or Streams_Message'));
                 }
                 $message = $message->fields;
             }
         }
     }
     // Check if there are any messages to post
     $atLeastOne = false;
     foreach ($messages as $publisherId => $arr) {
         foreach ($arr as $streamName => $m) {
             if (!$m) {
                 continue;
             }
             $atLeastOne = true;
             break 2;
         }
     }
     if (!$atLeastOne) {
         return array(array(), array());
     }
     // Start posting messages, publisher by publisher
     $eventParams = array();
     $posted = array();
     $streams = array();
     $messages2 = array();
     $totals2 = array();
     $updates = array();
     $clientId = Q_Request::special('clientId', '');
     $sendToNode = true;
     foreach ($messages as $publisherId => $arr) {
         $streamNames = array_keys($messages[$publisherId]);
         $streams[$publisherId] = $fetched = Streams::fetch($asUserId, $publisherId, $streamNames, '*', array('refetch' => true, 'begin' => true));
         foreach ($arr as $streamName => $m) {
             // Get the Streams_Stream object
             if (!isset($fetched[$streamName])) {
                 $p = new Q_Exception_MissingRow(array('table' => 'stream', 'criteria' => "publisherId {$publisherId} and name {$streamName}"));
                 $updates[$publisherId]['missingRow'][] = $streamName;
                 continue;
             }
             $p =& $posted[$publisherId][$streamName];
             $p = array();
             if (!$m) {
                 $updates[$publisherId]['noMessages'][] = $streamName;
                 continue;
             }
             $messages3 = is_array($m) && !Q::isAssociative($m) ? $m : array($m);
             $count = count($messages3);
             $updates[$publisherId][$count][] = $streamName;
             $i = 0;
             foreach ($messages3 as $message) {
                 ++$i;
                 $type = isset($message['type']) ? $message['type'] : 'text/small';
                 $content = isset($message['content']) ? $message['content'] : '';
                 $instructions = isset($message['instructions']) ? $message['instructions'] : '';
                 $weight = isset($message['weight']) ? $message['weight'] : 1;
                 if (!isset($message['byClientId'])) {
                     $message['byClientId'] = $clientId ? substr($clientId, 0, 255) : '';
                 }
                 if (is_array($instructions)) {
                     $instructions = Q::json_encode($instructions);
                 }
                 $byClientId = $message['byClientId'];
                 $stream = $fetched[$streamName];
                 // Make a Streams_Message object
                 $message = new Streams_Message();
                 $message->publisherId = $publisherId;
                 $message->streamName = $streamName;
                 $message->insertedTime = new Db_Expression("CURRENT_TIMESTAMP");
                 $message->sentTime = new Db_Expression("CURRENT_TIMESTAMP");
                 $message->byUserId = $asUserId;
                 $message->byClientId = $byClientId ? substr($byClientId, 0, 31) : '';
                 $message->type = $type;
                 $message->content = $content;
                 $message->instructions = $instructions;
                 $message->weight = $weight;
                 $message->ordinal = $stream->messageCount + $i;
                 // thanks to transaction
                 // Set up some parameters for the event hooks
                 $params = $eventParams[$publisherId][$streamName][] = array('publisherId' => $publisherId, 'message' => $message, 'skipAccess' => $skipAccess, 'sendToNode' => &$sendToNode, 'stream' => $stream);
                 /**
                  * @event Streams/post/$streamType {before}
                  * @param {string} publisherId
                  * @param {Streams_Stream} stream
                  * @param {string} message
                  * @return {false} To cancel further processing
                  */
                 if (Q::event("Streams/post/{$stream->type}", $params, 'before') === false) {
                     $results[$stream->name] = false;
                     continue;
                 }
                 /**
                  * @event Streams/message/$messageType {before}
                  * @param {string} publisherId
                  * @param {Streams_Stream} stream
                  * @param {string} message
                  * @return {false} To cancel further processing
                  */
                 if (Q::event("Streams/message/{$type}", $params, 'before') === false) {
                     $results[$stream->name] = false;
                     continue;
                 }
                 if (!$skipAccess && !$stream->testWriteLevel('post')) {
                     $p[] = new Users_Exception_NotAuthorized();
                     /**
                      * @event Streams/notAuthorized {before}
                      * @param {string} publisherId
                      * @param {Streams_Stream} stream
                      * @param {string} message
                      */
                     Q::event("Streams/notAuthorized", $params, 'after');
                     continue;
                 }
                 // if we are still here, mark the message as "in the database"
                 $message->wasRetrieved(true);
                 $p[] = $message;
                 // build the arrays of rows to insert
                 $messages2[] = $mf = $message->fields;
             }
             $totals2[$count][] = array('publisherId' => $mf['publisherId'], 'streamName' => $mf['streamName'], 'messageType' => $mf['type'], 'messageCount' => $count);
         }
     }
     foreach ($totals2 as $count => $rows) {
         Streams_Total::insertManyAndExecute($rows, array('onDuplicateKeyUpdate' => array('messageCount' => new Db_Expression("messageCount + {$count}"))));
     }
     if ($messages2) {
         Streams_Message::insertManyAndExecute($messages2);
     }
     // time to update the stream rows and commit the transaction
     // on all the shards where the streams were fetched.
     foreach ($updates as $publisherId => $arr) {
         foreach ($arr as $count => $streamNames) {
             $suffix = is_numeric($count) ? " + {$count}" : '';
             Streams_Stream::update()->set(array('messageCount' => new Db_Expression('messageCount' . $suffix)))->where(array('publisherId' => $publisherId, 'name' => $streamNames))->commit()->execute();
         }
     }
     // handle all the events for successfully posting
     foreach ($posted as $publisherId => $arr) {
         foreach ($arr as $streamName => $messages3) {
             $stream = $streams[$publisherId][$streamName];
             foreach ($messages3 as $i => $message) {
                 $params = $eventParams[$publisherId][$streamName][$i];
                 /**
                  * @event Streams/message/$messageType {after}
                  * @param {string} publisherId
                  * @param {Streams_Stream} stream
                  * @param {string} message
                  */
                 Q::event("Streams/message/{$message->type}", $params, 'after', false);
                 /**
                  * @event Streams/post/$streamType {after}
                  * @param {string} publisherId
                  * @param {Streams_Stream} stream
                  * @param {string} message
                  */
                 Q::event("Streams/post/{$stream->type}", $params, 'after', false);
             }
         }
     }
     /**
      * @event Streams/postMessages {after}
      * @param {string} publisherId
      * @param {Streams_Stream} stream
      * @param {string} posted
      */
     Q::event("Streams/postMessages", array('streams' => $streams, 'messages' => $messages, 'skipAccess' => $skipAccess, 'posted' => $posted), 'after', false);
     if ($sendToNode) {
         Q_Utils::sendToNode(array("Q/method" => "Streams/Message/postMessages", "posted" => Q::json_encode($messages2), "streams" => Q::json_encode(Db::exportArray($streams))));
     }
     return array($posted, $streams);
 }
Beispiel #7
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' => '');
     if (!Q::isAssociative($save)) {
         throw new Q_Exception_WrongType(array('field' => 'save', 'type' => 'associative array'));
     }
     // 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);
         imagesavealpha($thumb, true);
         imagealphablending($thumb, false);
         $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;
 }
Beispiel #8
0
 /**
  * Method is called before setting the field and verifies that, if it is a string,
  * it contains a JSON array.
  * @method beforeSet_permissions
  * @param {string} $value
  * @return {array} An array of field name and value
  * @throws {Exception} An exception is thrown if $value is not string or is exceedingly long
  */
 function beforeSet_permissions($value)
 {
     if (is_string($value)) {
         $decoded = Q::json_decode($value, true);
         if (!is_array($decoded) or Q::isAssociative($decoded)) {
             throw new Q_Exception_WrongValue(array('field' => 'permissions', 'range' => 'JSON array'));
         }
     }
     return parent::beforeSet_permissions($value);
 }