示例#1
0
/**
 *	resize an image object
 *
 *	this function drops the reference to any currently resized version, 
 *	saves the resized image together with the original image in the page's 
 *	shared folder and updates the object file to use the resized version.
 *	@param array $args arguments
 *		key 'name' name of the objects
 *		key 'width' width in px
 *		key 'height' height in px
 *	@return array response
 *		true if the client is advised to reload the image, false if not
 */
function image_resize($args)
{
    // check for gd
    if (!_gd_available()) {
        return response('Host does not have gd', 500);
    }
    // set requested width & height
    if (($width = @intval($args['width'])) == 0) {
        return response('Required argument "width" is zero or does not exist', 400);
    }
    if (($height = @intval($args['height'])) == 0) {
        return response('Required argument "height" is zero or does not exist', 400);
    }
    load_modules('glue');
    // resolve symlinks
    $ret = object_get_symlink($args);
    if ($ret['#error']) {
        return $ret;
    } elseif ($ret['#data'] !== false) {
        log_msg('debug', 'image_resize: resolved object ' . quot($args['name']) . ' into ' . quot($ret['#data']));
        $args['name'] = $ret['#data'];
    }
    // load object
    $obj = load_object($args);
    if ($obj['#error']) {
        return $obj;
    } else {
        $obj = $obj['#data'];
    }
    if (@intval($obj['image-file-width']) == 0 || @intval($obj['image-file-height']) == 0) {
        return response('Original dimensions are not available', 500);
    }
    // set pagename
    $pn = array_shift(expl('.', $obj['name']));
    // resizing might not be necessary at all
    if (!empty($obj['image-resized-file']) && @intval($obj['image-resized-width']) == $width && @intval($obj['image-resized-height'] == $height)) {
        log_msg('debug', 'image_resize: width and height match the current resized file, no resize necessary');
        return response(false);
    }
    // else remove any currently resized file
    if (!empty($obj['image-resized-file'])) {
        log_msg('info', 'image_resize: dropping reference to previous resized file ' . quot($obj['image-resized-file']));
        delete_upload(array('pagename' => $pn, 'file' => $obj['image-resized-file'], 'max_cnt' => 1));
        unset($obj['image-resized-file']);
        unset($obj['image-resized-width']);
        unset($obj['image-resized-height']);
        // update object file as well
        $ret = object_remove_attr(array('name' => $obj['name'], 'attr' => array('image-resized-file', 'image-resized-width', 'image-resized-height')));
        if ($ret['#error']) {
            return $ret;
        }
        $was_resized = true;
    } else {
        $was_resized = false;
    }
    // check if width or height are larger than the original
    if (@intval($obj['image-file-width']) <= $width || @intval($obj['image-file-height']) <= $height) {
        log_msg('debug', 'image_resize: dimensions requested are larger or equal than the original file is, no resize necessary');
        // the client need not reload the the image if we were using the
        // original before
        if (!$was_resized) {
            return response(false);
        } else {
            return response(true);
        }
    }
    // check if we really have a source image
    if (empty($obj['image-file-mime']) && empty($obj['image-file'])) {
        return response(false);
    }
    // TODO (later): make this a generic function
    // load source file
    $ext = filext($obj['image-file']);
    $fn = CONTENT_DIR . '/' . $pn . '/shared/' . $obj['image-file'];
    if ($obj['image-file-mime'] == 'image/jpeg' || in_array($ext, array('jpg', 'jpeg'))) {
        $orig = @imagecreatefromjpeg($fn);
        $dest_ext = 'jpg';
    } elseif ($obj['image-file-mime'] == 'image/png' || $ext == 'png') {
        $orig = @imagecreatefrompng($fn);
        $dest_ext = 'png';
    } elseif (is_ani($fn)) {
        // animated images shall not be resized
        log_msg('debug', 'image_resize: animated image, not resizing');
        return response(true);
    } elseif ($obj['image-file-mime'] == 'image/gif' || $ext == 'gif') {
        $orig = @imagecreatefromgif($fn);
        // save gifs as png
        // TODO (later): check for animated gif (see php.net/manual/en/function.imagecreatefromgif.php)
        $dest_ext = 'png';
    } else {
        return response('Unsupported source file format ' . quot($obj['image-file']), 500);
    }
    if ($orig === false) {
        return response('Error loading source file ' . quot($obj['image-file']), 500);
    }
    // get source file dimensions
    $orig_size = @getimagesize($fn);
    // create resized image
    if (($resized = @imagecreatetruecolor($width, $height)) === false) {
        @imagedestroy($orig);
        return response('Error creating the resized image', 500);
    }
    // preserve any alpha channel
    @imagealphablending($resized, false);
    @imagesavealpha($resized, true);
    // try to resize
    if (!@imagecopyresampled($resized, $orig, 0, 0, 0, 0, $width, $height, $orig_size[0], $orig_size[1])) {
        @imagedestroy($resized);
        @imagedestroy($orig);
        return response('Error resizing the source image', 500);
    }
    // setup destination filename
    $a = expl('.', $obj['image-file']);
    if (1 < count($a)) {
        // throw the previous extension away
        $fn = CONTENT_DIR . '/' . $pn . '/shared/' . implode('.', array_slice($a, 0, -1)) . '-' . $width . 'x' . $height . '.' . $dest_ext;
    } else {
        $fn = CONTENT_DIR . '/' . $pn . '/shared/' . $a[0] . '-' . $width . 'x' . $height . '.' . $dest_ext;
    }
    $m = umask(0111);
    if ($dest_ext == 'jpg') {
        $ret = @imagejpeg($resized, $fn, IMAGE_JPEG_QUAL);
    } else {
        if ($dest_ext == 'png') {
            // preserve any alpha channel
            @imagealphablending($resized, false);
            @imagesavealpha($resized, true);
            $ret = @imagepng($resized, $fn, IMAGE_PNG_QUAL);
        }
    }
    umask($m);
    // destroy images again
    @imagedestroy($resized);
    @imagedestroy($orig);
    if (!$ret) {
        return response('Error saving the resized image', 500);
    } else {
        log_msg('info', 'image_resize: created a resized image of ' . quot($obj['name']) . ' -> ' . quot(basename($fn)));
    }
    // the code above can take a while, so read in the object anew via
    // update_object()
    $update = array();
    $update['name'] = $obj['name'];
    $update['image-resized-file'] = basename($fn);
    $update['image-resized-width'] = $width;
    $update['image-resized-height'] = $height;
    // we change width and height here as well since we are racing with the
    // save_object from the frontend after resize
    $update['object-width'] = $width . 'px';
    $update['object-height'] = $height . 'px';
    return update_object($update);
}
/**
 *	serve a resource associated with an object
 *
 *	the function might not return (e.g. when a module calls serve_file()).
 *	@param string $s object (e.g. page.rev.obj)
 *	@param bool $dl download file
 *	@return bool
 */
function serve_resource($s, $dl)
{
    load_modules('glue');
    // resolve symlinks
    $ret = object_get_symlink(array('name' => $s));
    if ($ret['#error'] == false && $ret['#data'] !== false) {
        log_msg('debug', 'controller: resolved resource ' . quot($s) . ' into ' . quot($ret['#data']));
        $s = $ret['#data'];
    }
    $obj = load_object(array('name' => $s));
    if ($obj['#error']) {
        return false;
    } else {
        $obj = $obj['#data'];
    }
    $ret = invoke_hook_while('serve_resource', false, array('obj' => $obj, 'dl' => $dl));
    // this is probably not needed as the module will most likely call
    // serve_file() on success, which does not return
    foreach ($ret as $key => $val) {
        if ($val !== false) {
            return true;
        }
    }
    return false;
}
示例#3
0
/**
 *	create a snapshot from a page
 *
 *	@param array $args arguments
 *		key 'page' page to shapshot (i.e. page.rev)
 *		key 'rev' (optional) new revision name (i.e. rev2) (if empty or not set 
 *			a revision starting with 'auto-' and the current date will be 
 *			created)
 *	@return array response (holding the page of the newly created revision 
 *		if successful)
 */
function snapshot($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);
    }
    // setup revision name
    $a = expl('.', $args['page']);
    if (empty($args['rev'])) {
        $args['rev'] = 'auto-' . date('YmdHis');
    } elseif (page_exists($a[0] . '.' . $args['rev'])) {
        return response('Revision ' . quot($args['rev']) . ' already exists', 400);
    } elseif (!valid_pagename($a[0] . '.' . $args['rev'])) {
        return response('Invalid revision ' . quot($args['rev']), 400);
    }
    // create revision
    $dest = CONTENT_DIR . '/' . $a[0] . '/' . $args['rev'];
    $m = umask(00);
    if (!@mkdir($dest)) {
        umask($m);
        return response('Error creating directory ' . quot($dest), 500);
    }
    umask($m);
    // copy files
    // we go through the files one by one in order to spot symlinks hiding
    $src = CONTENT_DIR . '/' . str_replace('.', '/', $args['page']);
    $files = scandir($src);
    foreach ($files as $f) {
        if ($f == '.' || $f == '..') {
            continue;
        } elseif (is_dir($src . '/' . $f) && substr($f, 0, 1) == '.') {
            // skip directories that start with a dot (like .svn) without a warning
            continue;
        } elseif (is_dir($src . '/' . $f)) {
            log_msg('warn', 'snapshot: skipping ' . quot($src . '/' . $f) . ' as we don\'t support directories inside pages');
        } elseif (is_link($src . '/' . $f) && is_file($src . '/' . $f)) {
            // a proper symlink, copy content
            $s = @file_get_contents($src . '/' . $f);
            $m = umask(0111);
            if (!@file_put_contents($dest . '/' . $f, $s)) {
                log_msg('error', 'snapshot: error writing to ' . quot($dest . '/' . $f) . ', skipping file');
            } else {
                log_msg('debug', 'snapshot: copied the content of symlink ' . quot($args['page'] . '.' . $f));
            }
            umask($m);
            // load the newly created snapshot and give modules a chance to
            // copy referenced files as well
            $dest_name = $a[0] . '.' . $args['rev'] . '.' . $f;
            $dest_obj = load_object(array('name' => $dest_name));
            if ($dest_obj['#error']) {
                log_msg('error', 'snapshot: error loading snapshotted object ' . quot($dest_name) . ', skipping hook');
            } else {
                $dest_obj = $dest_obj['#data'];
                // get the source object's target
                $src_name = $args['page'] . '.' . $f;
                $src_target = object_get_symlink(array('name' => $src_name));
                if ($src_target['#error']) {
                    log_msg('error', 'snapshot: error getting the symlink target of source object ' . quot($src_name) . ', skipping hook');
                } else {
                    $src_target = $src_target['#data'];
                    // hook
                    invoke_hook('snapshot_symlink', array('obj' => $dest_obj, 'origin' => implode('.', array_slice(expl('.', $src_target), 0, 2))));
                }
            }
        } elseif (is_file($src . '/' . $f)) {
            // copy file
            $m = umask(0111);
            if (!@copy($src . '/' . $f, $dest . '/' . $f)) {
                log_msg('error', 'snapshot: error copying ' . quot($src . '/' . $f) . ' to ' . quot($dest . '/' . $f) . ', skipping file');
            }
            umask($m);
        }
    }
    log_msg('info', 'snapshot: created snapshot ' . quot($a[0] . '.' . $args['rev']) . ' from ' . quot($args['page']));
    return response($a[0] . '.' . $args['rev']);
}