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(); }
/** * 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; }
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; } }
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)); } }
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; }
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; } }
/** * 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 ''; } }
/** * 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']; } }
/** * 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); }
/** * 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; } }