function controller_revisions($args)
{
    page_canonical($args[0][0]);
    $page = $args[0][0];
    if (!page_exists($page)) {
        hotglue_error(404);
    }
    // get all revisions of page and determine the current revision's index
    load_modules('glue');
    $a = expl('.', $page);
    $revs = revisions_info(array('pagename' => $a[0], 'sort' => 'time'));
    $revs = $revs['#data'];
    $cur_rev = false;
    for ($i = 0; $i < count($revs); $i++) {
        if ($revs[$i]['revision'] == $a[1]) {
            $cur_rev = $i;
            break;
        }
    }
    if ($cur_rev === false) {
        // we didn't find the current revision
        hotglue_error(500);
    }
    default_html(true);
    html_add_css(base_url() . 'modules/revisions_browser/revisions_browser.css');
    if (USE_MIN_FILES) {
        html_add_js(base_url() . 'modules/revisions_browser/revisions_browser.min.js');
    } else {
        html_add_js(base_url() . 'modules/revisions_browser/revisions_browser.js');
    }
    html_add_js_var('$.glue.page', $page);
    $bdy =& body();
    elem_attr($bdy, 'id', 'revisions');
    render_page(array('page' => $page, 'edit' => false));
    body_append('<div id="revisions_browser_ctrl">');
    body_append('<div id="revisions_browser_prev">');
    if ($cur_rev + 1 < count($revs)) {
        body_append('<a href="' . base_url() . '?' . htmlspecialchars(urlencode($revs[$cur_rev + 1]['page']), ENT_COMPAT, 'UTF-8') . '/revisions">prev</a>');
    }
    body_append('</div><div id="revisions_browser_cur">');
    if (substr($revs[$cur_rev]['revision'], 0, 5) == 'auto-') {
        body_append(date('d M y H:i', $revs[$cur_rev]['time']));
    } else {
        body_append(htmlspecialchars($revs[$cur_rev]['revision'], ENT_NOQUOTES, 'UTF-8'));
    }
    body_append('<br>');
    if ($a[1] == 'head') {
        body_append('<a href="' . base_url() . '?' . htmlspecialchars(urlencode($page), ENT_COMPAT, 'UTF-8') . '/edit">back to editing mode</a>');
    } else {
        body_append('<a id="revisions_browser_revert_btn" href="#">revert</a>');
    }
    body_append('</div><div id="revisions_browser_next">');
    if (0 < $cur_rev) {
        body_append('<a href="' . base_url() . '?' . htmlspecialchars(urlencode($revs[$cur_rev - 1]['page']), ENT_COMPAT, 'UTF-8') . '/revisions">next</a>');
    }
    body_append('</div>');
    body_append('</div>');
    echo html_finalize();
}
Example #2
0
/**
 *	parse exactly one html element
 *
 *	this function decodes html's special characters except for the content 
 *	(when it too is not being parsed).
 *	this function is less fragile than html_parse() when it comes to 
 *	malformatted input.
 *	@param string $html input string (must start and end with the element's tag)
 *	@param bool $recursive also parse children elements
 *	@return array parsed representation
 */
function html_parse_elem($html, $recursive = false)
{
    global $single_tags;
    // from html.inc.php
    $quot = array('"', "'");
    $ret = array();
    // explode the tag
    $next = strpos($html, '>', 1);
    $a = expl_whitesp(substr($html, 1, $next - 1), true);
    if (count($a) < 1) {
        return $ret;
    } else {
        $ret['tag'] = strtolower($a[0]);
    }
    // attributes can end up in one to three fields
    // combine them
    for ($i = 1; $i < count($a); $i++) {
        if ($a[$i] == '=' && 1 < $i && $i + 1 < count($a)) {
            $a[$i] = $a[$i - 1] . '=' . $a[$i + 1];
            array_splice($a, $i - 1, 1);
            array_splice($a, $i, 1);
            $i--;
        } elseif (substr($a[$i], -1) == '=' && $i + 1 < count($a)) {
            $a[$i] .= $a[$i + 1];
            array_splice($a, $i + 1, 1);
            $i--;
        } elseif (substr($a[$i], 0, 1) == '=' && 1 < $i) {
            $a[$i] = $a[$i - 1] . $a[$i];
            array_splice($a, $i - 1, 1);
            $i--;
        }
    }
    // put attributes into array
    for ($i = 1; $i < count($a); $i++) {
        if (($equal = strpos($a[$i], '=')) === false) {
            $attr = strtolower(htmlspecialchars_decode($a[$i], ENT_QUOTES));
            $ret[$attr] = $attr;
        } else {
            $attr = strtolower(htmlspecialchars_decode(substr($a[$i], 0, $equal), ENT_QUOTES));
            $val = htmlspecialchars_decode(substr($a[$i], $equal + 1), ENT_QUOTES);
            // strip optional quotes
            if (in_array(substr($val, 0, 1), $quot) && substr($val, 0, 1) == substr($val, -1)) {
                $val = substr($val, 1, -1);
            }
            // special cases for certain attributes
            if ($attr == 'class') {
                $val = expl(' ', $val);
            } elseif ($attr == 'style') {
                $styles = expl(';', $val);
                $val = array();
                foreach ($styles as $style) {
                    $temp = expl(':', $style);
                    if (1 < count($temp)) {
                        $val[strtolower(trim($temp[0]))] = trim(implode(':', array_slice($temp, 1)));
                    }
                }
            }
            $ret[$attr] = $val;
        }
    }
    // handle content
    if (!in_array($ret['tag'], $single_tags)) {
        // check if there is actually a closing tag
        if (($last = strrpos($html, '<')) !== false) {
            // check if opening and closing tags match
            if (strtolower(substr($html, $last + 2, -1)) == $ret['tag']) {
                $ret['val'] = trim(substr($html, $next + 1, $last - $next - 1));
                if ($recursive) {
                    $ret['val'] = html_parse($ret['val'], true);
                }
            }
        }
    }
    return $ret;
}
Example #3
0
function page_upload($args)
{
    // only handle the file if the frontend wants us to
    if (empty($args['preferred_module']) || $args['preferred_module'] != 'page') {
        return false;
    }
    // check if supported file
    if (!in_array($args['mime'], array('image/jpeg', 'image/png', 'image/gif')) || $args['mime'] == '' && !in_array(filext($args['file']), array('jpg', 'jpeg', 'png', 'gif'))) {
        return false;
    }
    // check if there is already a background-image and delete it
    $obj = load_object(array('name' => $args['page'] . '.page'));
    if (!$obj['#error']) {
        $obj = $obj['#data'];
        if (!empty($obj['page-background-file'])) {
            delete_upload(array('pagename' => array_shift(expl('.', $args['page'])), 'file' => $obj['page-background-file'], 'max_cnt' => 1));
        }
    }
    // set as background-image in page object
    $obj = array();
    $obj['name'] = $args['page'] . '.page';
    $obj['page-background-file'] = $args['file'];
    $obj['page-background-mime'] = $args['mime'];
    // update page object
    load_modules('glue');
    $ret = update_object($obj);
    if ($ret['#error']) {
        log_msg('page_upload: error updating page object: ' . quot($ret['#data']));
        return false;
    } else {
        // we don't actually render the object here, but signal the
        // frontend that everything went okay
        return true;
    }
}
Example #4
0
function object_render_page_early($args)
{
    if ($args['edit']) {
        if (USE_MIN_FILES) {
            html_add_js(base_url() . 'modules/object/object-edit.min.js');
        } else {
            html_add_js(base_url() . 'modules/object/object-edit.js');
        }
        // add default colors
        html_add_js_var('$.glue.conf.object.default_colors', expl(' ', OBJECT_DEFAULT_COLORS));
    }
}
Example #5
0
function text_alter_save($args)
{
    $elem = $args['elem'];
    $obj =& $args['obj'];
    if (!elem_has_class($elem, 'text')) {
        return false;
    }
    // background-color
    if (elem_css($elem, 'background-color') !== NULL) {
        $obj['text-background-color'] = elem_css($elem, 'background-color');
    } else {
        unset($obj['text-background-color']);
    }
    // we don't handle content here
    // see the comments in $.glue.object.register_alter_pre_save (at text-edit.js)
    // font-color
    if (elem_css($elem, 'color') !== NULL) {
        $obj['text-font-color'] = elem_css($elem, 'color');
    } else {
        unset($obj['text-font-color']);
    }
    // font-family
    if (elem_css($elem, 'font-family') !== NULL) {
        $obj['text-font-family'] = elem_css($elem, 'font-family');
    } else {
        unset($obj['text-font-family']);
    }
    // font-size
    if (elem_css($elem, 'font-size') !== NULL) {
        $obj['text-font-size'] = elem_css($elem, 'font-size');
    } else {
        unset($obj['text-font-size']);
    }
    // font-style
    if (elem_css($elem, 'font-style') !== NULL) {
        $obj['text-font-style'] = elem_css($elem, 'font-style');
    } else {
        unset($obj['text-font-style']);
    }
    // font-weight
    if (elem_css($elem, 'font-weight') !== NULL) {
        $obj['text-font-weight'] = elem_css($elem, 'font-weight');
    } else {
        unset($obj['text-font-weight']);
    }
    // letter-spacing
    if (elem_css($elem, 'letter-spacing') !== NULL) {
        $obj['text-letter-spacing'] = elem_css($elem, 'letter-spacing');
    } else {
        unset($obj['text-letter-spacing']);
    }
    // line-height
    if (elem_css($elem, 'line-height') !== NULL) {
        $obj['text-line-height'] = elem_css($elem, 'line-height');
    } else {
        unset($obj['text-line-height']);
    }
    if (elem_css($elem, 'padding') !== NULL) {
        // parse padding
        // this is needed for Firefox
        $s = expl(' ', elem_css($elem, 'padding'));
        if (count($s) == 1) {
            // padding-x = padding-y
            $obj['text-padding-x'] = $s[0];
            $obj['text-padding-y'] = $s[0];
        } elseif (1 < count($s)) {
            // padding-x
            $obj['text-padding-x'] = $s[1];
            // padding-y
            $obj['text-padding-y'] = $s[0];
        }
    } else {
        // padding-x
        if (elem_css($elem, 'padding-left') !== NULL) {
            $obj['text-padding-x'] = elem_css($elem, 'padding-left');
        } else {
            unset($obj['text-padding-x']);
        }
        // padding-y
        if (elem_css($elem, 'padding-top') !== NULL) {
            $obj['text-padding-y'] = elem_css($elem, 'padding-top');
        } else {
            unset($obj['text-padding-y']);
        }
    }
    // text-align
    if (elem_css($elem, 'text-align') !== NULL) {
        $obj['text-align'] = elem_css($elem, 'text-align');
    } else {
        unset($obj['text-align']);
    }
    // word-spacing
    if (elem_css($elem, 'word-spacing') !== NULL) {
        $obj['text-word-spacing'] = elem_css($elem, 'word-spacing');
    } else {
        unset($obj['text-word-spacing']);
    }
    return true;
}
/**
 *	parse the QUERY_STRING server variable
 *
 *	@return array query-arguments array (key/value and numeric keys)
 */
function parse_query_string()
{
    // QUERY_STRING per se seems not to be affected by magic quotes, only
    // the derived $_GET, $_POST etc
    $q = $_SERVER['QUERY_STRING'];
    $args = array();
    $num_args = array();
    // strip a tailing slash
    if (substr($q, -1) == '/') {
        $q = substr($q, 0, -1);
    }
    // explode query string
    // this could also be done with parse_str() instead
    $temp = expl('&', $q);
    foreach ($temp as $a) {
        if (($p = strpos($a, '=')) !== false) {
            $args[urldecode(substr($a, 0, $p))] = urldecode(substr($a, $p + 1));
        } else {
            $num_args[] = urldecode($a);
        }
    }
    // merge $num_args into $args
    for ($i = 0; $i < count($num_args); $i++) {
        // explode slashes in arguments without a key
        if (($p = strpos($num_args[$i], '/')) !== false) {
            $args[$i] = expl('/', $num_args[$i]);
        } else {
            $args[$i] = $num_args[$i];
        }
    }
    return $args;
}
Example #7
0
function video_snapshot_symlink($args)
{
    $obj = $args['obj'];
    if (!isset($obj['type']) || $obj['type'] != 'video') {
        return false;
    }
    $dest_dir = CONTENT_DIR . '/' . array_shift(expl('.', $obj['name'])) . '/shared';
    $src_file = CONTENT_DIR . '/' . array_shift(expl('.', $args['origin'])) . '/shared/' . $obj['video-file'];
    if (($f = dir_has_same_file($dest_dir, $src_file)) !== false) {
        $obj['video-file'] = $f;
    } else {
        // copy file
        $dest_file = $dest_dir . '/' . unique_filename($dest_dir, $src_file);
        $m = umask(0111);
        if (!@copy($src_file, $dest_file)) {
            umask($m);
            log_msg('error', 'video_snapshot_symlink: error copying referenced file ' . quot($src_file) . ' to ' . quot($dest_file));
            return false;
        }
        umask($m);
        $obj['video-file'] = basename($dest_file);
        log_msg('info', 'video_snapshot_symlink: copied referenced file to ' . quot($dest_file));
    }
    $ret = save_object($obj);
    if ($ret['#error']) {
        log_msg('error', 'video_snapshot_symlink: error saving object ' . quot($obj['name']));
        return false;
    } else {
        return true;
    }
}
Example #8
0
/**
 *	get the extension of a file
 *
 *	@param string $s filename
 *	@return string
 */
function filext($s)
{
    $a = expl('.', $s);
    if (1 < count($a)) {
        return array_pop($a);
    } else {
        return '';
    }
}
Example #9
0
/**
 *	implements upload
 */
function image_upload($args)
{
    // check if supported file
    if (!in_array($args['mime'], array('image/jpeg', 'image/png', 'image/gif')) || $args['mime'] == '' && !in_array(filext($args['file']), array('jpg', 'jpeg', 'png', 'gif'))) {
        return false;
    }
    load_modules('glue');
    // create new object
    $obj = create_object($args);
    if ($obj['#error']) {
        return false;
    } else {
        $obj = $obj['#data'];
    }
    $obj['type'] = 'image';
    // this is for a potential future speedup
    $obj['module'] = 'image';
    $obj['image-file'] = $args['file'];
    $obj['image-file-mime'] = $args['mime'];
    // save original-{width,height} if we can calculate it
    if (_gd_available()) {
        $a = expl('.', $args['page']);
        $size = _gd_get_imagesize(CONTENT_DIR . '/' . $a[0] . '/shared/' . $obj['image-file']);
        $obj['image-file-width'] = $size[0];
        $obj['object-width'] = $size[0] . 'px';
        $obj['image-file-height'] = $size[1];
        $obj['object-height'] = $size[1] . 'px';
    }
    save_object($obj);
    // render object and return html
    $ret = render_object(array('name' => $obj['name'], 'edit' => true));
    log_msg('debug', 'image_upload: ' . print_r($ret, 1));
    if ($ret['#error']) {
        return false;
    } else {
        return $ret['#data'];
    }
}
Example #10
0
/**
 *	upload one or more files
 *
 *	@param array $args arguments
 *		key 'page' page to upload the files to (i.e. page.rev)
 *		key 'preferred_module' (optional) try first to invoke the upload method 
 *			on this module
 *	@return array response
 *		array of rendered, newly created objects
 */
function upload_files($args)
{
    if (empty($args['page'])) {
        return response('Required argument "page" missing or empty', 400);
    }
    if (!page_exists($args['page'])) {
        return response('Page ' . quot($args['page']) . ' does not exist', 404);
    }
    $ret = array();
    log_msg('debug', 'upload_files: $_FILES is ' . var_dump_inl($_FILES));
    foreach ($_FILES as $f) {
        $existed = false;
        $fn = upload_file($f['tmp_name'], $args['page'], $f['name'], $existed);
        if ($fn === false) {
            continue;
        } else {
            $args = array_merge($args, array('file' => $fn, 'mime' => $f['type'], 'size' => $f['size']));
            // clear mime type if set to default application/octet-stream
            if ($args['mime'] == 'application/octet-stream') {
                $args['mime'] = '';
            }
        }
        $s = false;
        // check preferred_module first
        if (!empty($args['preferred_module'])) {
            // make sure all modules are loaded
            load_modules();
            $func = $args['preferred_module'] . '_upload';
            if (is_callable($func)) {
                log_msg('debug', 'upload_files: invoking hook upload, calling ' . $func);
                $s = $func($args);
                if ($s !== false) {
                    log_msg('info', 'upload_object: ' . quot($fn) . ' was handled by ' . quot($args['preferred_module']));
                }
            }
        }
        // check all other modules next
        if ($s === false) {
            $r = invoke_hook_while('upload', false, $args);
            if (count($r) == 1) {
                $s = array_pop(array_values($r));
                log_msg('info', 'upload_object: ' . quot($fn) . ' was handled by ' . quot(array_pop(array_keys($r))));
            }
        }
        // check fallback hook last
        if ($s === false) {
            $r = invoke_hook_while('upload_fallback', false, $args);
            if (count($r) == 1) {
                $s = array_pop(array_values($r));
                log_msg('info', 'upload_object: ' . quot($fn) . ' was (fallback-) handled by ' . quot(array_pop(array_keys($r))));
            }
        }
        if ($s === false) {
            log_msg('warn', 'upload_files: nobody cared about file ' . quot($fn) . ', type ' . $f['type']);
            // delete file again unless it did already exist
            if (!$existed) {
                $a = expl('.', $args['page']);
                @unlink(CONTENT_DIR . '/' . $a[0] . '/shared/' . $fn);
            }
        } else {
            $ret[] = $s;
        }
    }
    return response($ret);
}
Example #11
0
/**
 *	check whether the string is a valid, canonical page name
 *
 *	the function does not check if the page exists or not.
 *	@param string $s string to check
 *	@return bool
 */
function valid_pagename($s)
{
    $a = expl('.', $s);
    if (count($a) != 2) {
        return false;
    } elseif (empty($a[0]) || empty($a[1])) {
        return false;
    } elseif (in_array($a[0], array('cache', 'shared'))) {
        // reserved page names
        // TODO (later): we're missing the log file here
        // TODO (later): we're also missing $arg0 of controllers here
        // TODO (later): we're missing all the files directly in the
        // content directory here (this might not be an issue on all
        // os)
        return false;
    } elseif (in_array($a[1], array('shared'))) {
        // reserved revision names
        return false;
    } elseif (is_file($a[0]) || is_dir($a[0]) || is_link($a[0])) {
        // same name as existing file names in the root directory
        // this is an issue when using the RewriteRule
        return false;
    } else {
        return true;
    }
}