1
0

Merge pull request #132 from shadlaws/master

New: image_optimizer; Updates: proofsheet, tag_cloud_html5, short_search_fix, register
This commit is contained in:
Bharat Mediratta 2012-12-12 16:00:22 -08:00
commit b616184ebe
43 changed files with 2971 additions and 750 deletions

View File

@ -0,0 +1,163 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2012 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class Admin_Image_Optimizer_Controller extends Admin_Controller {
public function index() {
// print screen from new form
$form = $this->_get_admin_form();
$this->_print_screen($form);
}
public function save() {
access::verify_csrf();
$form = $this->_get_admin_form();
if ($form->validate()) {
foreach (array('jpg', 'png', 'gif') as $type) {
module::set_var("image_optimizer", "path_".$type, $form->paths->{"path_".$type}->value);
module::set_var("image_optimizer", "optlevel_thumb_".$type, $form->thumb->{"optlevel_thumb_".$type}->value);
module::set_var("image_optimizer", "optlevel_resize_".$type, $form->resize->{"optlevel_resize_".$type}->value);
}
module::set_var("image_optimizer", "rotate_jpg", ($form->rotate->rotate_jpg->value == 1));
foreach (array('thumb', 'resize') as $target) {
module::set_var("image_optimizer", "convert_".$target."_gif", $form->$target->{"convert_".$target."_gif"}->value);
module::set_var("image_optimizer", "convert_".$target."_png", $form->$target->{"convert_".$target."_png"}->value);
module::set_var("image_optimizer", "metastrip_".$target, ($form->$target->{"metastrip_".$target}->value == 1));
module::set_var("image_optimizer", "progressive_".$target, ($form->$target->{"progressive_".$target}->value == 1));
// deal with enable changes
$enable_old = module::get_var("image_optimizer", "enable_".$target);
$enable_new = ($form->$target->{"enable_".$target}->value == 1);
if ($enable_new && !$enable_old) {
image_optimizer::add_image_optimizer_rule($target);
module::set_var("image_optimizer", "enable_".$target, true);
} elseif (!$enable_new && $enable_old) {
image_optimizer::remove_image_optimizer_rule($target);
module::set_var("image_optimizer", "enable_".$target, false);
}
// deal with update mode changes
$update_mode_old = module::get_var("image_optimizer", "update_mode_".$target);
$update_mode_new = ($form->$target->{"update_mode_".$target}->value == 1);
if ($update_mode_new && !$update_mode_old) {
image_optimizer::activate_update_mode($target);
} elseif (!$update_mode_new && $update_mode_old) {
image_optimizer::deactivate_update_mode($target);
}
// dirty images if needed
if ($form->$target->{"rebuild_".$target}->value == 1) {
image_optimizer::dirty($target);
}
}
// all done; redirect with message
message::success(t("Image optimizer settings updated successfully"));
url::redirect("admin/image_optimizer");
}
// not valid - print screen from existing form
$this->_print_screen($form);
}
private function _print_screen($form) {
// this part is a bit of a hack, but Forge doesn't seem to allow set_attr() for groups.
$form = $form->render();
$form = preg_replace("/<fieldset>/","<fieldset class=\"g-image-optimizer-admin-form-top\">",$form,2);
$form = preg_replace("/<fieldset>/","<fieldset class=\"g-image-optimizer-admin-form-left\">",$form,1);
$form = preg_replace("/<fieldset>/","<fieldset class=\"g-image-optimizer-admin-form-right\">",$form,1);
// make and print view
$view = new Admin_View("admin.html");
$view->page_title = t("Image optimizer settings");
$view->content = new View("admin_image_optimizer.html");
$view->content->form = $form;
// get module parameters
foreach (array('jpg', 'png', 'gif') as $type) {
$view->content->{"installed_path_".$type} = image_optimizer::tool_installed_path($type);
$view->content->{"version_".$type} = image_optimizer::tool_version($type);
}
print $view;
}
private function _get_admin_form() {
$form = new Forge("admin/image_optimizer/save", "", "post", array("id" => "g-image-optimizer-admin-form"));
$group_paths = $form->group("paths")->label(t("Toolkit paths"))->set_attr("id","g-image-optimizer-admin-form-paths");
foreach (array('jpg', 'png', 'gif') as $type) {
$path = strval(module::get_var("image_optimizer", "path_".$type, null));
$group_paths->input("path_".$type)
->label(t("Path for")." ".image_optimizer::tool_name($type)." (".t("no symlinks, default")." ".MODPATH."image_optimizer/lib/".image_optimizer::tool_name($type).")")
->value($path);
}
$group_rotate = $form->group("rotate")->label(t("Full-size image rotation"))->set_attr("id","g-image-optimizer-admin-form-rotate");
$group_rotate->checkbox("rotate_jpg")
->label(t("Override default toolkit and use")." ".image_optimizer::tool_name('jpg')." ".t("for rotation"))
->checked(module::get_var("image_optimizer", "rotate_jpg", null));
foreach (array('thumb', 'resize') as $target) {
${'group_'.$target} = $form->group($target)->label(ucfirst($target)." ".t("images optimization"))->set_attr("id","g-image-optimizer-admin-form-".$target);
${'group_'.$target}->checkbox("enable_".$target)
->label(t("Enable optimization"))
->checked(module::get_var("image_optimizer", "enable_".$target, null));
${'group_'.$target}->checkbox("update_mode_".$target)
->label(t("Enable update mode - deactivates all other graphics rules to allow fast optimization on existing images; MUST deactivate this after initial rebuild!"))
->checked(module::get_var("image_optimizer", "update_mode_".$target, null));
${'group_'.$target}->checkbox("rebuild_".$target)
->label(t("Mark all existing images for rebuild - afterward, go to Maintenace | Rebuild Images"))
->checked(false); // always set as false
${'group_'.$target}->dropdown("convert_".$target."_png")
->label(t("PNG conversion"))
->options(array(0=>t("none"),
"jpg"=>("JPG ".t("(not lossless)"))))
->selected(module::get_var("image_optimizer", "convert_".$target."_png", null));
${'group_'.$target}->dropdown("convert_".$target."_gif")
->label(t("GIF conversion"))
->options(array(0=>t("none"),
"jpg"=>("JPG ".t("(not lossless)")),
"png"=>("PNG ".t("(lossless)"))))
->selected(module::get_var("image_optimizer", "convert_".$target."_gif", null));
${'group_'.$target}->dropdown("optlevel_".$target."_jpg")
->label(t("JPG compression optimization (default: enabled)"))
->options(array(0=>t("disabled"),
1=>t("enabled")))
->selected(module::get_var("image_optimizer", "optlevel_".$target."_jpg", null));
${'group_'.$target}->dropdown("optlevel_".$target."_png")
->label(t("PNG compression optimization (default: level 2)"))
->options(array(0=>t("disabled"),
1=>t("level 1: 1 trial"),
2=>t("level 2: 8 trials"),
3=>t("level 3: 16 trials"),
4=>t("level 4: 24 trials"),
5=>t("level 5: 48 trials"),
6=>t("level 6: 120 trials"),
7=>t("level 7: 240 trials")))
->selected(module::get_var("image_optimizer", "optlevel_".$target."_png", null));
${'group_'.$target}->dropdown("optlevel_".$target."_gif")
->label(t("GIF compression optimization (default: enabled)"))
->options(array(0=>t("disabled"),
1=>t("enabled")))
->selected(module::get_var("image_optimizer", "optlevel_".$target."_gif", null));
${'group_'.$target}->checkbox("metastrip_".$target)
->label(t("Remove all meta data"))
->checked(module::get_var("image_optimizer", "metastrip_".$target, null));
${'group_'.$target}->checkbox("progressive_".$target)
->label(t("Make images progressive/interlaced"))
->checked(module::get_var("image_optimizer", "progressive_".$target, null));
}
$form->submit("")->value(t("Save"));
return $form;
}
}

View File

@ -0,0 +1,17 @@
#g-content fieldset {
display: block;
}
#g-content fieldset.g-image-optimizer-admin-form-top {
width: 80%;
clear: both;
}
#g-content fieldset.g-image-optimizer-admin-form-left {
width: 45%;
float: left;
clear: left;
margin-right: 2%
}
#g-content fieldset.g-image-optimizer-admin-form-right {
width: 45%;
clear: right;
}

View File

@ -0,0 +1,72 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2012 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class gallery_graphics extends gallery_graphics_Core {
// this is copied from modules/gallery/helpers/gallery_graphics.php
/**
* Rotate an image. Valid options are degrees
*
* @param string $input_file
* @param string $output_file
* @param array $options
*/
static function rotate($input_file, $output_file, $options) {
graphics::init_toolkit();
module::event("graphics_rotate", $input_file, $output_file, $options);
// BEGIN mod to original function
$image_info = getimagesize($input_file); // [0]=w, [1]=h, [2]=type (1=GIF, 2=JPG, 3=PNG)
if (module::get_var("image_optimizer","rotate_jpg") || $image_info[2] == 2) {
// rotate_jpg enabled, the file is a jpg. get args
$path = module::get_var("image_optimizer", "path_jpg");
$exec_args = " -rotate ";
$exec_args .= $options["degrees"] > 0 ? $options["degrees"] : $options["degrees"]+360;
$exec_args .= " -copy all -optimize -outfile ";
// run it - from input_file to tmp_file
$tmp_file = image_optimizer::make_temp_name($output_file);
exec(escapeshellcmd($path) . $exec_args . escapeshellarg($tmp_file) . " " . escapeshellarg($input_file), $exec_output, $exec_status);
if ($exec_status || !filesize($tmp_file)) {
// either a blank/nonexistant file or an error - log an error and pass to normal function
Kohana_Log::add("error", "image_optimizer rotation failed on ".$output_file);
unlink($tmp_file);
} else {
// worked - move temp to output
rename($tmp_file, $output_file);
$status = true;
}
}
if (!$status) {
// we got here if we weren't supposed to use jpegtran or if jpegtran failed
// END mod to original function
Image::factory($input_file)
->quality(module::get_var("gallery", "image_quality"))
->rotate($options["degrees"])
->save($output_file);
// BEGIN mod to original function
}
// END mod to original function
module::event("graphics_rotate_completed", $input_file, $output_file, $options);
}
}

View File

@ -0,0 +1,343 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2012 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class image_optimizer_Core {
/**
* These functions deal with the toolkit installations
*/
// define the tool names
static function tool_name($type) {
$type = strtolower($type);
switch($type) {
case "jpg":
$tool = "jpegtran";
break;
case "png":
$tool = "optipng";
break;
case "gif":
$tool = "gifsicle";
break;
default:
$tool = "";
}
return $tool;
}
// find server-installed versions of the tools
static function tool_installed_path($type) {
$type = strtolower($type);
$path = exec('which '.image_optimizer::tool_name($type));
$path = is_file($path) ? $path : "(not found)";
return $path;
}
// return status of tool path and attempt to fix permissions if not executable
static function tool_status($type) {
$type = strtolower($type);
$path = module::get_var("image_optimizer","path_".$type);
if (is_file($path) && !is_link($path)) {
if (is_executable($path)) {
$status = true;
} else {
// try to fix its permissions. return false if it doesn't work.
$status = chmod($path,0755);
}
} else {
$status = false;
}
// set module variable
module::set_var("image_optimizer", "configstatus_".$type, $status);
return $status;
}
// return tool version as string
static function tool_version($type) {
$type = strtolower($type);
if (image_optimizer::tool_status($type)) {
switch($type) {
case "jpg":
$path = module::get_var("image_optimizer","path_".$type);
// jpegtran is weird as it doesn't have a version flag. so, we run in verbose mode with a fake file and catch stderr, which exec() can't do.
// this is sort of a hack, but since there's no clean way available...
$cmd = escapeshellcmd($path)." -verbose ".MODPATH."image_optimizer/this_file_does_not_exist.jpg";
$output = image_optimizer::get_pipe($cmd, 2);
$output = "Correctly configured! " . substr($output, 0, strpos($output, "\n"));
break;
case "png":
case "gif":
$path = module::get_var("image_optimizer","path_".$type);
exec(escapeshellcmd($path)." --version", $output);
$output = "Correctly configured! " . $output[0];
break;
default:
$output = t("Only jpg/png/gif supported");
}
} else {
$output = t("Invalid configuration");
}
return $output;
}
/**
* These functions supplement the rule functions in modules/gallery/helpers/graphics.php
* Note that all rule-changing functions in graphics.php trigger all images to be marked as dirty for rebuild, which we often want to avoid.
*/
// add image_optimizer rules without marking for dirty (based on add_rule)
static function add_image_optimizer_rule($target) {
// to prevent duplicates, remove any existing instances first
image_optimizer::remove_image_optimizer_rule($target);
// then add the new one
$rule = ORM::factory("graphics_rule");
$rule->module_name = "image_optimizer";
$rule->target = $target;
$rule->operation = 'image_optimizer::optimize';
$rule->priority = 999999999; // this MUST be larger than all others to be last
$rule->args = serialize($target); // this isn't typical for other graphics rules
$rule->active = true;
$rule->save();
}
// remove image_optimizer rules without marking for dirty (based on remove_rule)
static function remove_image_optimizer_rule($target) {
db::build()
->delete("graphics_rules")
->where("target", "=", $target)
->where("module_name", "=", "image_optimizer")
->execute();
}
// activate update mode - disactivates all currently-active rules except those of image_optimizer without marking for dirty
// sets update_mode_thumb/resize variable with serialized list of deactivated rule ids
static function activate_update_mode($target) {
// find all currently active non-image-optimizer rules
$rules = db::build()
->from("graphics_rules")
->select("id")
->where("active", "=", true)
->where("target", "=", $target)
->where("module_name", "!=", "image_optimizer")
->execute();
// use found rules to build ids array and deactivate rules
$ids = array();
foreach ($rules as $rule) {
$ids[] = $rule->id;
db::build()
->update("graphics_rules")
->where("id", "=", $rule->id)
->set("active", false) // deactivation!
->execute();
}
// set module variable as deactivated rule ids
module::set_var("image_optimizer", "update_mode_".$target, serialize($ids));
// display a warning that we're in update mode
site_status::warning(
t("Image optimizer is in thumb/resize update mode - remember to exit <a href=\"%url\">here</a> after rebuild!",
array("url" => html::mark_clean(url::site("admin/image_optimizer")))),
"image_optimizer_update_mode");
}
// deactivate update mode - re-activates rules marked in the update_mode_thumb/resize variable as previously deactivated
static function deactivate_update_mode($target) {
// get deactivated rule ids
$ids = unserialize(module::get_var("image_optimizer", "update_mode_".$target));
// activate them
foreach ($ids as $id) {
db::build()
->update("graphics_rules")
->where("id", "=", $id)
->set("active", true) // activation!
->execute();
}
// reset module variable
module::set_var("image_optimizer", "update_mode_".$target, false);
// clear update mode warning
if (!module::get_var("image_optimizer", "update_mode_thumb") && !module::get_var("image_optimizer", "update_mode_resize")) {
site_status::clear("image_optimizer_update_mode");
}
}
// mark all as dirty (in similar syntax to above)
static function dirty($target) {
graphics::mark_dirty($target == "thumb", $target == "resize");
}
/**
* the main optimize function
*
* the function arguments are the same format as other graphics rules. the only "option" is $target, hence why it's renamed in the function def.
*
* NOTE: unlike other graphics transformations, this only uses the output file! if it isn't already there, we don't do anything.
* among other things, this means that the original, full-size images are never touched.
*/
static function optimize($input_file, $output_file, $target, $item=null) {
// see if output file exists and is writable
if (is_writable($output_file)) {
// see if input is a supported file type. if not, return without doing anything.
$image_info = getimagesize($input_file); // [0]=w, [1]=h, [2]=type (1=GIF, 2=JPG, 3=PNG)
switch ($image_info[2]) {
case 1:
$type_old = "gif";
$convert = module::get_var("image_optimizer", "convert_".$target."_gif");
break;
case 2:
$type_old = "jpg";
$convert = 0; // no conversion possible here...
break;
case 3:
$type_old = "png";
$convert = module::get_var("image_optimizer", "convert_".$target."_png");
break;
default:
// not a supported file type
return;
}
} else {
// file doesn't exist or isn't writable
return;
}
// set new file type
$type = $convert ? $convert : $type_old;
// convert image type (if applicable). this isn't necessarily lossless.
if ($convert) {
$output_file_new = legal_file::change_extension($output_file, $type);
// perform conversion using standard Gallery toolkit (GD/ImageMagick/GraphicsMagick)
// note: if input was a GIF, this will kill animation
$image = Image::factory($output_file)
->quality(module::get_var("gallery", "image_quality"))
->save($output_file_new);
// if filenames are different, move the new on top of the old
if ($output_file != $output_file_new) {
/**
* HACK ALERT! Gallery3 is still broken with regard to treating thumb/resizes with proper extensions. This doesn't try to fix that.
* Normal Gallery setup:
* photo thumb -> keep photo type, keep photo extension
* album thumb -> keep source photo thumb type, change extension to jpg (i.e. ".album.jpg" even for png/gif)
* Also, missing_photo.png is similarly altered...
*
* Anyway, to avoid many rewrites of core functions (and not-easily-reversible database changes), this module also forces the extension to stay the same.
* With image optimizer conversion:
* photo thumb -> change type, keep photo extension (i.e. "photo.png" photo becomes "photo.png" thumb even if type has changed)
* album thumb -> keep source photo thumb type, change extension to jpg (i.e. ".album.jpg" even for png/gif)
*/
rename($output_file_new, $output_file);
}
}
// get module variables
$configstatus = module::get_var("image_optimizer", "configstatus_".$type);
$path = module::get_var("image_optimizer", "path_".$type);
$opt = module::get_var("image_optimizer", "optlevel_".$target."_".$type);
$meta = module::get_var("image_optimizer", "metastrip_".$target);
$prog = module::get_var("image_optimizer", "progressive_".$target);
// make sure the toolkit is configured correctly and we want to use it - if not, return without doing anything.
if ($configstatus) {
if (!$prog && !$meta && !$opt) {
// nothing to do!
return;
}
} else {
// not configured correctly
return;
}
/**
* do the actual optimization
*/
// set parameters
switch ($type) {
case "jpg":
$exec_args = $opt ? " -optimize" : "";
$exec_args .= $meta ? " -copy none" : " -copy all";
$exec_args .= $prog ? " -progressive" : "";
$exec_args .= " -outfile ";
break;
case "png":
$exec_args = $opt ? " -o".$opt : "";
$exec_args .= $meta ? " -strip all" : "";
$exec_args .= $prog ? " -i 1" : "";
$exec_args .= " -quiet -out ";
break;
case "gif":
$exec_args = $opt ? " --optimize=3" : ""; // levels 1 and 2 don't really help us
$exec_args .= $meta ? " --no-comments --no-extensions --no-names" : " --same-comments --same-extensions --same-names";
$exec_args .= $prog ? " --interlace" : " --same-interlace";
$exec_args .= " --careful --output ";
break;
}
// run it - from output_file to tmp_file.
$tmp_file = image_optimizer::make_temp_name($output_file);
exec(escapeshellcmd($path) . $exec_args . escapeshellarg($tmp_file) . " " . escapeshellarg($output_file), $exec_output, $exec_status);
if ($exec_status || !filesize($tmp_file)) {
// either a blank/nonexistant file or an error - do nothing to the output, but log an error and delete the temp (if any)
Kohana_Log::add("error", "image_optimizer optimization failed on ".$output_file);
unlink($tmp_file);
} else {
// worked - move temp to output
rename($tmp_file, $output_file);
}
}
/**
* make a unique temporary filename. this bit is inspired/copied from
* the system/libraries/Image.php save function and the system/libraries/drivers/Image/ImageMagick.php process function
*/
static function make_temp_name($file) {
// Separate the directory and filename
$dir = pathinfo($file, PATHINFO_DIRNAME);
$file = pathinfo($file, PATHINFO_BASENAME);
// Normalize the path
$dir = str_replace('\\', '/', realpath($dir)).'/';
// Unique temporary filename
$tmp_file = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.'));
return $tmp_file;
}
/**
* get stdin, stdout, or stderr from shell command. php commands like exec() don't do this.
* this is only used to get jpegtran's version info in the admin screen.
*
* see http://stackoverflow.com/questions/2320608/php-stderr-after-exec
*/
static function get_pipe($cmd, $pipe) {
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w"), // stderr
);
$process = proc_open($cmd, $descriptorspec, $pipes);
$output = stream_get_contents($pipes[$pipe]);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return $output;
}
}

View File

@ -0,0 +1,29 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2012 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class image_optimizer_event_Core {
static function admin_menu($menu, $theme) {
$menu->get("settings_menu")
->append(
Menu::factory("link")
->id("image_optimizer")
->label(t("Image optimizer"))
->url(url::site("admin/image_optimizer")));
}
}

View File

@ -0,0 +1,75 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2012 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class image_optimizer_installer {
static function install() {
$defaults = array('jpg' => '1', 'png' => '2', 'gif' => '1');
foreach ($defaults as $type => $optlevel) {
// set default path as the pre-compiled versions in the lib
module::set_var("image_optimizer", "path_".$type, MODPATH."image_optimizer/lib/".image_optimizer::tool_name($type));
// check config status (also sets configstatus_ variables and ensures that the permissions are set correctly)
image_optimizer::tool_status($type);
// set default optimization levels
module::set_var("image_optimizer", "optlevel_thumb_".$type, $optlevel);
module::set_var("image_optimizer", "optlevel_resize_".$type, $optlevel);
}
module::set_var("image_optimizer", "rotate_jpg", true);
module::set_var("image_optimizer", "enable_thumb", true);
module::set_var("image_optimizer", "enable_resize", true);
module::set_var("image_optimizer", "update_mode_thumb", false);
module::set_var("image_optimizer", "update_mode_resize", false);
module::set_var("image_optimizer", "metastrip_thumb", true);
module::set_var("image_optimizer", "convert_thumb_png", "jpg");
module::set_var("image_optimizer", "convert_resize_png", false);
module::set_var("image_optimizer", "convert_thumb_gif", "jpg");
module::set_var("image_optimizer", "convert_resize_gif", false);
module::set_var("image_optimizer", "metastrip_resize", false);
module::set_var("image_optimizer", "progressive_thumb", false);
module::set_var("image_optimizer", "progressive_resize", true);
module::set_version("image_optimizer", 1);
image_optimizer::add_image_optimizer_rule("thumb");
image_optimizer::add_image_optimizer_rule("resize");
}
static function activate() {
// add graphics rules if enabled
if (module::get_var("image_optimizer", "enable_thumb")) {
image_optimizer::add_image_optimizer_rule("thumb");
}
if (module::get_var("image_optimizer", "enable_resize")) {
image_optimizer::add_image_optimizer_rule("resize");
}
}
static function deactivate() {
// ensure that update modes are disabled
image_optimizer::deactivate_update_mode("thumb");
image_optimizer::deactivate_update_mode("resize");
// remove graphics rules
image_optimizer::remove_image_optimizer_rule("thumb");
image_optimizer::remove_image_optimizer_rule("resize");
}
static function uninstall() {
// deactivate
module::deactivate("image_optimizer");
// delete vars from database
module::clear_all_vars("image_optimizer");
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,7 @@
name = Image Optimizer
description = "Use jpegtran, optipng, and gifsicle for lossless optimization and rotation."
version = 1
author_name = "Shad Laws"
author_url = ""
info_url = "http://codex.gallery2.org/Gallery3:Modules:image_optimizer"
discuss_url = "http://gallery.menalto.com/node/110262"

View File

@ -0,0 +1,51 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<style>
@import "<?= url::file("modules/image_optimizer/css/admin_image_optimizer.css"); ?>";
</style>
<div id="g-image-optimizer-admin">
<h2>
<?= t("Image optimizer settings") ?>
</h2>
<p>
<?= t("This module uses three underlying toolkits, all performing <b>lossless</b> transformations.")." ".
t("You can set the paths to each of the three toolkits below.")." ".
t("By default, this module uses it's own pre-compiled versions, included as libraries.")." ".
t("Additionally, your server may have copies installed elsewhere (see table below).") ?><br/>
<table>
<tr>
<td></td>
<td><b><?= t("Name and author") ?></b></td>
<td><b><?= t("Server-installed path") ?></b></td>
<td><b><?= t("Configuration status") ?></b></td>
</tr>
<tr>
<td>JPG</td>
<td><a href='http://jpegclub.org/droppatch.v09.tar.gz'>Jpegtran</a> <?= t("by") ?> <a href='http://jpegclub.org/jpegtran'>Jpegclub</a></td>
<td><?= $installed_path_jpg ?></td>
<td><?= $version_jpg ?></td>
</tr>
<tr>
<td>PNG</td>
<td><a href='http://shanebishop.net/uploads/optipng.tar.gz'>OptiPNG</a> <?= t("by") ?> <a href='http://optipng.sourceforge.net'>Cosmin Truta et al.</a></td>
<td><?= $installed_path_png ?></td>
<td><?= $version_png ?></td>
</tr>
<tr>
<td>GIF</td>
<td><a href='http://shanebishop.net/uploads/gifsicle.tar.gz'>GIFsicle</a> <?= t("by") ?> <a href='http://www.lcdf.org/gifsicle'>LCDF</a></td>
<td><?= $installed_path_gif ?></td>
<td><?= $version_gif ?></td>
</tr>
</table>
<?= t("This module was inspired by the WordPress module")." <a href='http://wordpress.org/extend/plugins/ewww-image-optimizer'>EWWW Image Optimizer</a> ".t("and the Gallery3 module")." <a href='http://codex.gallery2.org/Gallery3:Modules:jpegtran'>Jpegtran</a>." ?>
</p>
<p>
<b><?= t("Notes:") ?></b><br/>
<b>1. <?= t("Remove all meta data") ?></b>: <?= t("recommended for thumb images - 80% size reduction typical, which drastically changes page load speed") ?><br/>
<b>2. <?= t("Make images progressive/interlaced") ?></b>: <?= t("recommended for resize images - provides preview of photo during download") ?><br/>
<b>3. <?= t("Conversion") ?></b>: <?= t("recommended for thumb images - converting PNG/GIF to JPG can reduce the size by a huge amount, again helping page load speed") ?><br/>
<b>4. <?= t("Update mode") ?></b>: <?= t("used to speed up initial rebuild to optimize existing images by deactivating all other graphics rules - MUST ensure that no new images are created while this is enabled (use maintenance mode if needed), and MUST disable once initial rebuild is done.") ?><br/>
<b>5. <?= t("Windows / Linux") ?></b>: <?= t("the module's lib directory includes both Windows and Linux versions of the toolkits. For security reasons, Windows should not be used on production sites. Both versions are provided here to enable development copies to still run Windows without issue.") ?><br/>
</p>
<?= $form ?>
</div>

View File

@ -1,441 +1,446 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2012 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* Generate a PDF proof sheet on-the-fly of the current album or tag.
* By Shad Laws. Version 5, 2012/04/05
*
* 2012/04/05, version 5
* Added ability to include GIF thumbnails if GD is installed (FPDF uses GD)
* Changed behavior of unhandled file types - now provides missing image icon instead of throwing an exception
* 2012/03/30, version 4
* Major rewrite. Output is similar, but everything "under the hood" is much cleaner and (I hope) more easily understood and tweakable by other users.
* Header link is now an icon.
* Fixed encoding problems with diacritic marks and special characters.
* Now includes FPDF as a library instead of requiring a separate installtion.
* 2012/03/28, version 3
* Made sizing configuration more flexible
* Prettified code so it's easier to understand and tweak as desired
* Added header link
* First version properly documented and linked to Gallery wiki
* 2012/03/27, version 2
* Determines jpg/png type by file header, not extension, which makes it robust against misnamed extensions
* (N.B.: there's a bug in some movie modules that copy missing_movie.png as a jpg thumbnail!)
* Made caption size limits to prevent overrun
* 2012/03/27, version 1
* Initial release
*/
/**
* Include php library FPDF v1.7 by Olivier Plathey.
* This gives us the tools to make PDFs without GhostScript.
* The license for FPDF has no usage restrictions and thus
* can be included in this code.
* More information can be found in the tutorials/docs
* included in the library directory, and at:
* http://www.fpdf.org
*/
require_once(MODPATH . 'proofsheet/lib/fpdf/fpdf.php');
class proofsheet_Controller extends Controller {
public function makepdf($page_type, $container_type, $id) {
/**
* This part is largely copied from downloadalbum,
* but does have some additions (headertext/headerlink)
*/
switch($container_type) {
case "album":
$container = ORM::factory("item", $id);
if (!$container->is_album()) {
throw new Kohana_Exception('container is not an album: '.$container->relative_path());
}
$pdfname = (empty($container->name))
? 'Gallery.pdf' // @todo purified_version_of($container->title).'.pdf'
: $container->name.'.pdf';
$headerText = $container->title;
$headerLink = $container->abs_url();
break;
case "tag":
// @todo: if the module is not installed, it crash
$container = ORM::factory("tag", $id);
if (is_null($container->name)) {
throw new Kohana_Exception('container is not a tag: '.$id);
}
$pdfname = $container->name.'.pdf';
$headerText = $container->name;
$headerLink = $container->abs_url();
break;
default:
throw new Kohana_Exception('unhandled container type: '.$container_type);
}
$files = $this->getFilesList($container);
/**
* Configure PDF file. These are all of the parameters that are used to
* format the proof sheet. If you'd like to tweak the formatting, here's
* where to do it.
*/
switch($page_type) {
case "ltr":
// Setup for LTR 8.5" x 11" paper (215.9mm x 279.4mm)
$cfg = array(
'pageW' => 215.9, // mm
'pageH' => 279.4, // mm
'imageNumW' => 5, // integer number
'imageNumH' => 5, // integer number
'imageSizeW' => 36, // mm
'imageSizeH' => 36, // mm
'marginL' => 10, // mm
'marginR' => 10, // mm
'marginT' => 21, // mm (header goes in here)
'marginB' => 20, // mm (footer goes in here)
'headerSpace' => 2, // mm (header to top row of images and to link icon)
'footerSpace' => 2, // mm (bottom row of captions to footer)
'captionSpace' => 1, // mm (bottom of image to caption)
'headerFont' => array(
'name' => 'Arial', // included are Arial/Helvetica, Courier, Times, Symbol, ZapfDingbats
'size' => 14, // pt
'style' => 'B', // combo of B, I, U
'posn' => 'L', // combo of L, C, R
'r' => 0, // Red 0-255
'g' => 0, // Green 0-255
'b' => 0),// Blue 0-255
'footerFont' => array(
'name' => 'Arial', // included are Arial/Helvetica, Courier, Times, Symbol, ZapfDingbats
'size' => 12, // pt
'style' => 'B', // combo of B, I, U
'posn' => 'R', // combo of L, C, R
'r' => 0, // Red 0-255
'g' => 0, // Green 0-255
'b' => 0),// Blue 0-255
'captionFont' => array(
'name' => 'Arial', // included are Arial/Helvetica, Courier, Times, Symbol, ZapfDingbats
'size' => 8, // pt
'style' => 'U', // combo of B, I, U
'posn' => 'C', // combo of L, C, R
'r' => 0, // Red 0-255
'g' => 0, // Green 0-255
'b' => 255),// Blue 0-255
);
break;
case "a4":
// Setup for A4 210mm x 297mm paper (8.27" x 11.69")
$cfg = array(
'pageW' => 210, // mm
'pageH' => 297, // mm
'imageNumW' => 5, // integer number
'imageNumH' => 6, // integer number
'imageSizeW' => 36, // mm
'imageSizeH' => 36, // mm
'marginL' => 8, // mm
'marginR' => 8, // mm
'marginT' => 19, // mm (header goes in here)
'marginB' => 18, // mm (footer goes in here)
'headerSpace' => 2, // mm (header to top row of images and to link icon)
'footerSpace' => 2, // mm (bottom row of captions to footer)
'captionSpace' => 1, // mm (bottom of image to caption)
'headerFont' => array(
'name' => 'Arial', // included are Arial/Helvetica, Courier, Times, Symbol, ZapfDingbats
'size' => 14, // pt
'style' => 'B', // combo of B, I, U
'posn' => 'L', // combo of L, C, R
'r' => 0, // Red 0-255
'g' => 0, // Green 0-255
'b' => 0),// Blue 0-255
'footerFont' => array(
'name' => 'Arial', // included are Arial/Helvetica, Courier, Times, Symbol, ZapfDingbats
'size' => 12, // pt
'style' => 'B', // combo of B, I, U
'posn' => 'R', // combo of L, C, R
'r' => 0, // Red 0-255
'g' => 0, // Green 0-255
'b' => 0),// Blue 0-255
'captionFont' => array(
'name' => 'Arial', // included are Arial/Helvetica, Courier, Times, Symbol, ZapfDingbats
'size' => 8, // pt
'style' => 'U', // combo of B, I, U
'posn' => 'C', // combo of L, C, R
'r' => 0, // Red 0-255
'g' => 0, // Green 0-255
'b' => 255),// Blue 0-255
);
break;
default:
throw new Kohana_Exception('unhandled page type: '.$page_type);
}
// Here are some other parameters that need defining
$cfg['footerTextPage'] = 'Page '; // Note that this text isn't autofixed by translate module
$cfg['footerTextSlash'] = ' / ';
$cfg['headerLinkIconPath'] = MODPATH . 'proofsheet/images/ico-link.png';
$cfgImageMissing['iconPath'] = MODPATH . 'proofsheet/images/image_missing.png';
$cfgImageMissing['iconType'] = 'PNG'; // PNG or JPG is most robust, GIF okay only if GD is installed
$pt2mm = 25.4/72; // 25.4mm=1in=72pt
// Derive a bunch more parameters. These are all dependent on the above stuff.
$cfg['headerH'] = $pt2mm * $cfg['headerFont']['size'];
$cfg['footerH'] = $pt2mm * $cfg['footerFont']['size'];
$cfg['captionH'] = $pt2mm * $cfg['captionFont']['size'];
$cfg['imageSpaceW'] = ($cfg['pageW']-$cfg['marginL']-$cfg['marginR']-$cfg['imageNumW']*$cfg['imageSizeW']) / ($cfg['imageNumW']-1);
$cfg['imageSpaceH'] = ($cfg['pageH']-$cfg['marginT']-$cfg['marginB']-$cfg['imageNumH']*$cfg['imageSizeH']-$cfg['captionH']-$cfg['captionSpace']) / ($cfg['imageNumH']-1);
$linkInfo = getimagesize($cfg['headerLinkIconPath']);
$cfg['headerLinkH'] = $cfg['headerH']; // I'm defining this to be the same as the text, but you can change it here.
$cfg['headerLinkW'] = $linkInfo[0] / $linkInfo[1] * $cfg['headerLinkH'];
$cfg['headerW'] = $cfg['pageW']-$cfg['marginL']-$cfg['marginR']-$cfg['headerLinkW']-$cfg['headerSpace'];
$cfg['footerW'] = $cfg['pageW']-$cfg['marginL']-$cfg['marginR'];
$cfg['captionW'] = $cfg['imageSizeW']; // I'm defining this to be the same as the image, but you can change it here.
$cfg['headerX'] = $cfg['marginL'];
$cfg['headerLinkX'] = $cfg['marginL']+$cfg['headerW'];
$cfg['footerX'] = $cfg['marginL'];
$cfg['headerY'] = $cfg['marginT']-$cfg['headerH']-$cfg['headerSpace'];
$cfg['headerLinkY'] = $cfg['marginT']-$cfg['headerLinkH']-$cfg['headerSpace'];
$cfg['footerY'] = $cfg['pageH']-$cfg['marginB']+$cfg['footerSpace'];
$cfg['imageNum'] = $cfg['imageNumW']*$cfg['imageNumH'];
$cfgImageMissing['iconInfo'] = getimagesize($cfgImageMissing['iconPath']);
$cfgImageMissing['GDflag'] = graphics::detect_toolkits()->gd>installed; // FPDF uses GD to convert GIFs
/**
* Initialize and build PDF... the main routine. Note that almost all of the
* useful configuration parameters are already defined above.
*/
// Initialize PDF, disable automatic margins and page breaks
$pdf = new proofsheet_PDF('P', 'mm', array($cfg['pageW'],$cfg['pageH']) );
$pdf->SetMargins(0,0);
$pdf->SetAutoPageBreak(0);
// Build the PDF
$numpages = floor(count($files)/$cfg['imageNum'])+1;
$i = 0;
foreach($files as $f_path => $f_info) {
// Initialize new pages, add header and footer
if (($i % $cfg['imageNum'])==0) {
$pdf->AddPage();
$pdf->printText($headerText, $cfg['headerFont'], $cfg['headerX'], $cfg['headerY'], $cfg['headerW'], $cfg['headerH']);
$pdf->printImage($cfg['headerLinkIconPath'], $cfg['headerLinkX'], $cfg['headerLinkY'], $cfg['headerLinkW'], $cfg['headerLinkH'], $headerLink, $cfgImageMissing);
$footerText = $cfg['footerTextPage'] . strval(floor($i/$cfg['imageNum'])+1) . $cfg['footerTextSlash'] . strval($numpages);
$pdf->printText($footerText, $cfg['footerFont'], $cfg['footerX'], $cfg['footerY'], $cfg['footerW'], $cfg['footerH']);
}
// Add thumbnail and caption
$x = $cfg['marginL'] + ($cfg['imageSizeW']+$cfg['imageSpaceW']) * ( $i % $cfg['imageNumW']);
$y = $cfg['marginT'] + ($cfg['imageSizeH']+$cfg['imageSpaceH']) * (floor($i/$cfg['imageNumW']) % $cfg['imageNumH']);
$pdf->printImage($f_path, $x, $y, $cfg['imageSizeW'], $cfg['imageSizeH'], null, $cfgImageMissing);
$pdf->printText($f_info['name'], $cfg['captionFont'], $x, $y+$cfg['imageSizeH']+$cfg['captionSpace'], $cfg['captionW'], $cfg['captionH'], $f_info['url']);
// Increment index and loop
$i++;
}
/**
* Output the PDF file. I wrote it in two versions (one should always be commented out).
*/
// Using a method similar to downloadalbum
$pdfstring = $pdf->Output('','S');
$this->prepareOutput();
$this->sendHeaders($pdfname, strlen($pdfstring));
echo $pdfstring;
// Using FPDF directly
//$pdf->Output($pdfname,'I');
}
/**
* Return the files that must be included in the archive.
* This is largely borrowed from downloadalbum, but does have
* significant modifications.
*/
private function getFilesList($container) {
$files = array();
if( $container instanceof Item_Model && $container->is_album() ) {
$items = $container->viewable()
->descendants(null, null, array(array("type", "<>", "album")));
foreach($items as $i) {
if (!access::can('view_full', $i)) {
continue;
}
$thumb_realpath = realpath($i->thumb_path());
if (!is_readable($thumb_realpath)) {
continue;
}
$files[$thumb_realpath] = array('url' => $i->abs_url(), 'name' => $i->title);
}
} else if( $container instanceof Tag_Model ) {
$items = $container->items();
foreach($items as $i) {
if (!access::can('view_full', $i)) {
continue;
}
if( $i->is_album() ) {
foreach($this->getFilesList($i) as $f_name => $f_info) {
$files[$f_name] = $f_info;
}
} else {
$thumb_realpath = realpath($i->thumb_path());
if (!is_readable($thumb_realpath)) {
continue;
}
$files[$thumb_realpath] = array('url' => $i->abs_url(), 'name' => $i->title);
}
}
}
if (count($files) === 0) {
throw new Kohana_Exception('no thumb files in ['.$container->name.']');
}
return $files;
}
/**
* See system/helpers/download.php
* This is borrowed from downloadalbum without changes.
*/
private function prepareOutput() {
// Close output buffers
Kohana::close_buffers(FALSE);
// Clear any output
Event::add('system.display', create_function('', 'Kohana::$output = "";'));
}
/**
* See system/helpers/download.php
* This is borrowed from downloadalbum without changes.
*/
private function sendHeaders($filename, $filesize = null) {
if (!is_null($filesize)) {
header('Content-Length: '.$filesize);
}
// Retrieve MIME type by extension
$mime = Kohana::config('mimes.'.strtolower(substr(strrchr($filename, '.'), 1)));
$mime = empty($mime) ? 'application/octet-stream' : $mime[0];
header("Content-Type: $mime");
header('Content-Transfer-Encoding: binary');
// Send headers necessary to invoke a "Save As" dialog
header('Content-Disposition: attachment; filename="'.$filename.'"');
// Prevent caching
header('Expires: Thu, 01 Jan 1970 00:00:00 GMT');
$pragma = 'no-cache';
$cachecontrol = 'no-cache, max-age=0';
// request::user_agent('browser') seems bugged
if (request::user_agent('browser') === 'Internet Explorer'
|| stripos(request::user_agent(), 'msie') !== false
|| stripos(request::user_agent(), 'internet explorer') !== false)
{
if (request::protocol() === 'https') {
// See http://support.microsoft.com/kb/323308/en-us
$pragma = 'cache';
$cachecontrol = 'private';
} else if (request::user_agent('version') <= '6.0') {
$pragma = '';
$cachecontrol = 'must-revalidate, post-check=0, pre-check=0';
}
}
header('Pragma: '.$pragma);
header('Cache-Control: '.$cachecontrol);
}
}
class proofsheet_PDF extends FPDF {
/**
* Print text (header, footer, or caption) with link, formatted as in cfg.
* It converts UTF-8 back to CP1252, which is used by FPDF.
* This will trim formatted text to fit and add ellipsis if needed.
*/
function printText($text, $font, $x, $y, $w, $h, $link = null) {
$ellipsis = '…'; // ASCII character 133
// Convert from UTF-8 back to CP1252
$text = iconv('utf-8','cp1252',$text);
// Set color, font, and position
$this->SetTextColor($font['r'],$font['g'],$font['b']);
$this->SetFont($font['name'],$font['style'],$font['size']);
$this->SetXY($x, $y);
// Trim text if needed
if (($this->GetStringWidth($text)) > $w) {
// Keep trimming until the size, with ellipsis, is small enough
while (($this->GetStringWidth($text.$ellipsis)) > $w) {
$text = substr($text,0,strlen($text)-1);
}
// Add the ellipsis to the shortened text
$text = $text.$ellipsis;
}
// Create text cell
$this->Cell($w,$h,$text,0,0,$font['posn'],false,$link);
}
/**
* Print image. This is basically a wrapper around the FPDF image function,
* except that it determines the file type independent of the file extension
* and automatically resizes to main aspect ratio within the defined space.
* Note that this provides robustness for images with incorrect filenames, such
* as missing_movie.png being called a jpg when copied as a thumbnail in v3.0.2.
*/
function printImage($imagePath, $x, $y, $w, $h, $link = null, $cfgImageMissing) {
$imageInfo = getimagesize($imagePath); // [0]=w, [1]=h, [2]=type (1=GIF, 2=JPG, 3=PNG)
// Figure out the filetype
switch($imageInfo[2]) {
case 3:
$imageType = 'PNG';
break;
case 2:
$imageType = 'JPG';
break;
case 1:
if ($cfgImageMissing['GDflag']) {
$imageType = 'GIF';
break;
}
default:
// use the missing image icon instead
$imagePath = $cfgImageMissing['iconPath'];
$imageType = $cfgImageMissing['iconType'];
$imageInfo = $cfgImageMissing['iconInfo'];
}
// Determine image orientation and create image
$ratioWH = ($imageInfo[0]/$w) / ($imageInfo[1]/$h);
if ($ratioWH>1) {
$this->image($imagePath, $x, $y+(1-1/$ratioWH)*$h/2, $w, 0, $imageType, $link);
} else {
$this->image($imagePath, $x+(1-$ratioWH)*$w/2, $y, 0, $h, $imageType, $link);
}
}
}
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2012 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* Generate a PDF proof sheet on-the-fly of the current album or tag.
* By Shad Laws. Version 7, 2012/10/05
*
* 2012/10/04, version 7
* Fixed bug related to the URL for proofsheets of tags (as opposed to albums)
* 2012/06/15, version 6
* Fixed bug that could cause a crash when trying to use GD or GIF files (a typo from version 5)
* 2012/04/05, version 5
* Added ability to include GIF thumbnails if GD is installed (FPDF uses GD)
* Changed behavior of unhandled file types - now provides missing image icon instead of throwing an exception
* 2012/03/30, version 4
* Major rewrite. Output is similar, but everything "under the hood" is much cleaner and (I hope) more easily understood and tweakable by other users.
* Header link is now an icon.
* Fixed encoding problems with diacritic marks and special characters.
* Now includes FPDF as a library instead of requiring a separate installtion.
* 2012/03/28, version 3
* Made sizing configuration more flexible
* Prettified code so it's easier to understand and tweak as desired
* Added header link
* First version properly documented and linked to Gallery wiki
* 2012/03/27, version 2
* Determines jpg/png type by file header, not extension, which makes it robust against misnamed extensions
* (N.B.: there's a bug in some movie modules that copy missing_movie.png as a jpg thumbnail!)
* Made caption size limits to prevent overrun
* 2012/03/27, version 1
* Initial release
*/
/**
* Include php library FPDF v1.7 by Olivier Plathey.
* This gives us the tools to make PDFs without GhostScript.
* The license for FPDF has no usage restrictions and thus
* can be included in this code.
* More information can be found in the tutorials/docs
* included in the library directory, and at:
* http://www.fpdf.org
*/
require_once(MODPATH . 'proofsheet/lib/fpdf/fpdf.php');
class proofsheet_Controller extends Controller {
public function makepdf($page_type, $container_type, $id) {
/**
* This part is largely copied from downloadalbum,
* but does have some additions (headertext/headerlink)
*/
switch($container_type) {
case "album":
$container = ORM::factory("item", $id);
if (!$container->is_album()) {
throw new Kohana_Exception('container is not an album: '.$container->relative_path());
}
$pdfname = (empty($container->name))
? 'Gallery.pdf' // @todo purified_version_of($container->title).'.pdf'
: $container->name.'.pdf';
$headerText = $container->title;
$headerLink = $container->abs_url();
break;
case "tag":
// @todo: if the module is not installed, it crash
$container = ORM::factory("tag", $id);
if (is_null($container->name)) {
throw new Kohana_Exception('container is not a tag: '.$id);
}
$pdfname = $container->name.'.pdf';
$headerText = $container->name;
//$headerLink = $container->abs_url();
$headerLink = url::abs_site("tag/{$container->id}/" . urlencode($container->name));
break;
default:
throw new Kohana_Exception('unhandled container type: '.$container_type);
}
$files = $this->getFilesList($container);
/**
* Configure PDF file. These are all of the parameters that are used to
* format the proof sheet. If you'd like to tweak the formatting, here's
* where to do it.
*/
switch($page_type) {
case "ltr":
// Setup for LTR 8.5" x 11" paper (215.9mm x 279.4mm)
$cfg = array(
'pageW' => 215.9, // mm
'pageH' => 279.4, // mm
'imageNumW' => 5, // integer number
'imageNumH' => 5, // integer number
'imageSizeW' => 36, // mm
'imageSizeH' => 36, // mm
'marginL' => 10, // mm
'marginR' => 10, // mm
'marginT' => 21, // mm (header goes in here)
'marginB' => 20, // mm (footer goes in here)
'headerSpace' => 2, // mm (header to top row of images and to link icon)
'footerSpace' => 2, // mm (bottom row of captions to footer)
'captionSpace' => 1, // mm (bottom of image to caption)
'headerFont' => array(
'name' => 'Arial', // included are Arial/Helvetica, Courier, Times, Symbol, ZapfDingbats
'size' => 14, // pt
'style' => 'B', // combo of B, I, U
'posn' => 'L', // combo of L, C, R
'r' => 0, // Red 0-255
'g' => 0, // Green 0-255
'b' => 0),// Blue 0-255
'footerFont' => array(
'name' => 'Arial', // included are Arial/Helvetica, Courier, Times, Symbol, ZapfDingbats
'size' => 12, // pt
'style' => 'B', // combo of B, I, U
'posn' => 'R', // combo of L, C, R
'r' => 0, // Red 0-255
'g' => 0, // Green 0-255
'b' => 0),// Blue 0-255
'captionFont' => array(
'name' => 'Arial', // included are Arial/Helvetica, Courier, Times, Symbol, ZapfDingbats
'size' => 8, // pt
'style' => 'U', // combo of B, I, U
'posn' => 'C', // combo of L, C, R
'r' => 0, // Red 0-255
'g' => 0, // Green 0-255
'b' => 255),// Blue 0-255
);
break;
case "a4":
// Setup for A4 210mm x 297mm paper (8.27" x 11.69")
$cfg = array(
'pageW' => 210, // mm
'pageH' => 297, // mm
'imageNumW' => 5, // integer number
'imageNumH' => 6, // integer number
'imageSizeW' => 36, // mm
'imageSizeH' => 36, // mm
'marginL' => 8, // mm
'marginR' => 8, // mm
'marginT' => 19, // mm (header goes in here)
'marginB' => 18, // mm (footer goes in here)
'headerSpace' => 2, // mm (header to top row of images and to link icon)
'footerSpace' => 2, // mm (bottom row of captions to footer)
'captionSpace' => 1, // mm (bottom of image to caption)
'headerFont' => array(
'name' => 'Arial', // included are Arial/Helvetica, Courier, Times, Symbol, ZapfDingbats
'size' => 14, // pt
'style' => 'B', // combo of B, I, U
'posn' => 'L', // combo of L, C, R
'r' => 0, // Red 0-255
'g' => 0, // Green 0-255
'b' => 0),// Blue 0-255
'footerFont' => array(
'name' => 'Arial', // included are Arial/Helvetica, Courier, Times, Symbol, ZapfDingbats
'size' => 12, // pt
'style' => 'B', // combo of B, I, U
'posn' => 'R', // combo of L, C, R
'r' => 0, // Red 0-255
'g' => 0, // Green 0-255
'b' => 0),// Blue 0-255
'captionFont' => array(
'name' => 'Arial', // included are Arial/Helvetica, Courier, Times, Symbol, ZapfDingbats
'size' => 8, // pt
'style' => 'U', // combo of B, I, U
'posn' => 'C', // combo of L, C, R
'r' => 0, // Red 0-255
'g' => 0, // Green 0-255
'b' => 255),// Blue 0-255
);
break;
default:
throw new Kohana_Exception('unhandled page type: '.$page_type);
}
// Here are some other parameters that need defining
$cfg['footerTextPage'] = 'Page '; // Note that this text isn't autofixed by translate module
$cfg['footerTextSlash'] = ' / ';
$cfg['headerLinkIconPath'] = MODPATH . 'proofsheet/images/ico-link.png';
$cfgImageMissing['iconPath'] = MODPATH . 'proofsheet/images/image_missing.png';
$cfgImageMissing['iconType'] = 'PNG'; // PNG or JPG is most robust, GIF okay only if GD is installed
$pt2mm = 25.4/72; // 25.4mm=1in=72pt
// Derive a bunch more parameters. These are all dependent on the above stuff.
$cfg['headerH'] = $pt2mm * $cfg['headerFont']['size'];
$cfg['footerH'] = $pt2mm * $cfg['footerFont']['size'];
$cfg['captionH'] = $pt2mm * $cfg['captionFont']['size'];
$cfg['imageSpaceW'] = ($cfg['pageW']-$cfg['marginL']-$cfg['marginR']-$cfg['imageNumW']*$cfg['imageSizeW']) / ($cfg['imageNumW']-1);
$cfg['imageSpaceH'] = ($cfg['pageH']-$cfg['marginT']-$cfg['marginB']-$cfg['imageNumH']*$cfg['imageSizeH']-$cfg['captionH']-$cfg['captionSpace']) / ($cfg['imageNumH']-1);
$linkInfo = getimagesize($cfg['headerLinkIconPath']);
$cfg['headerLinkH'] = $cfg['headerH']; // I'm defining this to be the same as the text, but you can change it here.
$cfg['headerLinkW'] = $linkInfo[0] / $linkInfo[1] * $cfg['headerLinkH'];
$cfg['headerW'] = $cfg['pageW']-$cfg['marginL']-$cfg['marginR']-$cfg['headerLinkW']-$cfg['headerSpace'];
$cfg['footerW'] = $cfg['pageW']-$cfg['marginL']-$cfg['marginR'];
$cfg['captionW'] = $cfg['imageSizeW']; // I'm defining this to be the same as the image, but you can change it here.
$cfg['headerX'] = $cfg['marginL'];
$cfg['headerLinkX'] = $cfg['marginL']+$cfg['headerW'];
$cfg['footerX'] = $cfg['marginL'];
$cfg['headerY'] = $cfg['marginT']-$cfg['headerH']-$cfg['headerSpace'];
$cfg['headerLinkY'] = $cfg['marginT']-$cfg['headerLinkH']-$cfg['headerSpace'];
$cfg['footerY'] = $cfg['pageH']-$cfg['marginB']+$cfg['footerSpace'];
$cfg['imageNum'] = $cfg['imageNumW']*$cfg['imageNumH'];
$cfgImageMissing['iconInfo'] = getimagesize($cfgImageMissing['iconPath']);
$cfgImageMissing['GDflag'] = graphics::detect_toolkits()->gd->installed; // FPDF uses GD to convert GIFs
/**
* Initialize and build PDF... the main routine. Note that almost all of the
* useful configuration parameters are already defined above.
*/
// Initialize PDF, disable automatic margins and page breaks
$pdf = new proofsheet_PDF('P', 'mm', array($cfg['pageW'],$cfg['pageH']) );
$pdf->SetMargins(0,0);
$pdf->SetAutoPageBreak(0);
// Build the PDF
$numpages = floor(count($files)/$cfg['imageNum'])+1;
$i = 0;
foreach($files as $f_path => $f_info) {
// Initialize new pages, add header and footer
if (($i % $cfg['imageNum'])==0) {
$pdf->AddPage();
$pdf->printText($headerText, $cfg['headerFont'], $cfg['headerX'], $cfg['headerY'], $cfg['headerW'], $cfg['headerH']);
$pdf->printImage($cfg['headerLinkIconPath'], $cfg['headerLinkX'], $cfg['headerLinkY'], $cfg['headerLinkW'], $cfg['headerLinkH'], $headerLink, $cfgImageMissing);
$footerText = $cfg['footerTextPage'] . strval(floor($i/$cfg['imageNum'])+1) . $cfg['footerTextSlash'] . strval($numpages);
$pdf->printText($footerText, $cfg['footerFont'], $cfg['footerX'], $cfg['footerY'], $cfg['footerW'], $cfg['footerH']);
}
// Add thumbnail and caption
$x = $cfg['marginL'] + ($cfg['imageSizeW']+$cfg['imageSpaceW']) * ( $i % $cfg['imageNumW']);
$y = $cfg['marginT'] + ($cfg['imageSizeH']+$cfg['imageSpaceH']) * (floor($i/$cfg['imageNumW']) % $cfg['imageNumH']);
$pdf->printImage($f_path, $x, $y, $cfg['imageSizeW'], $cfg['imageSizeH'], null, $cfgImageMissing);
$pdf->printText($f_info['name'], $cfg['captionFont'], $x, $y+$cfg['imageSizeH']+$cfg['captionSpace'], $cfg['captionW'], $cfg['captionH'], $f_info['url']);
// Increment index and loop
$i++;
}
/**
* Output the PDF file. I wrote it in two versions (one should always be commented out).
*/
// Using a method similar to downloadalbum
$pdfstring = $pdf->Output('','S');
$this->prepareOutput();
$this->sendHeaders($pdfname, strlen($pdfstring));
echo $pdfstring;
// Using FPDF directly
//$pdf->Output($pdfname,'I');
}
/**
* Return the files that must be included in the archive.
* This is largely borrowed from downloadalbum, but does have
* significant modifications.
*/
private function getFilesList($container) {
$files = array();
if( $container instanceof Item_Model && $container->is_album() ) {
$items = $container->viewable()
->descendants(null, null, array(array("type", "<>", "album")));
foreach($items as $i) {
if (!access::can('view_full', $i)) {
continue;
}
$thumb_realpath = realpath($i->thumb_path());
if (!is_readable($thumb_realpath)) {
continue;
}
$files[$thumb_realpath] = array('url' => $i->abs_url(), 'name' => $i->title);
}
} else if( $container instanceof Tag_Model ) {
$items = $container->items();
foreach($items as $i) {
if (!access::can('view_full', $i)) {
continue;
}
if( $i->is_album() ) {
foreach($this->getFilesList($i) as $f_name => $f_info) {
$files[$f_name] = $f_info;
}
} else {
$thumb_realpath = realpath($i->thumb_path());
if (!is_readable($thumb_realpath)) {
continue;
}
$files[$thumb_realpath] = array('url' => $i->abs_url(), 'name' => $i->title);
}
}
}
if (count($files) === 0) {
throw new Kohana_Exception('no thumb files in ['.$container->name.']');
}
return $files;
}
/**
* See system/helpers/download.php
* This is borrowed from downloadalbum without changes.
*/
private function prepareOutput() {
// Close output buffers
Kohana::close_buffers(FALSE);
// Clear any output
Event::add('system.display', create_function('', 'Kohana::$output = "";'));
}
/**
* See system/helpers/download.php
* This is borrowed from downloadalbum without changes.
*/
private function sendHeaders($filename, $filesize = null) {
if (!is_null($filesize)) {
header('Content-Length: '.$filesize);
}
// Retrieve MIME type by extension
$mime = Kohana::config('mimes.'.strtolower(substr(strrchr($filename, '.'), 1)));
$mime = empty($mime) ? 'application/octet-stream' : $mime[0];
header("Content-Type: $mime");
header('Content-Transfer-Encoding: binary');
// Send headers necessary to invoke a "Save As" dialog
header('Content-Disposition: attachment; filename="'.$filename.'"');
// Prevent caching
header('Expires: Thu, 01 Jan 1970 00:00:00 GMT');
$pragma = 'no-cache';
$cachecontrol = 'no-cache, max-age=0';
// request::user_agent('browser') seems bugged
if (request::user_agent('browser') === 'Internet Explorer'
|| stripos(request::user_agent(), 'msie') !== false
|| stripos(request::user_agent(), 'internet explorer') !== false)
{
if (request::protocol() === 'https') {
// See http://support.microsoft.com/kb/323308/en-us
$pragma = 'cache';
$cachecontrol = 'private';
} else if (request::user_agent('version') <= '6.0') {
$pragma = '';
$cachecontrol = 'must-revalidate, post-check=0, pre-check=0';
}
}
header('Pragma: '.$pragma);
header('Cache-Control: '.$cachecontrol);
}
}
class proofsheet_PDF extends FPDF {
/**
* Print text (header, footer, or caption) with link, formatted as in cfg.
* It converts UTF-8 back to CP1252, which is used by FPDF.
* This will trim formatted text to fit and add ellipsis if needed.
*/
function printText($text, $font, $x, $y, $w, $h, $link = null) {
$ellipsis = '…'; // ASCII character 133
// Convert from UTF-8 back to CP1252
$text = iconv('utf-8','cp1252',$text);
// Set color, font, and position
$this->SetTextColor($font['r'],$font['g'],$font['b']);
$this->SetFont($font['name'],$font['style'],$font['size']);
$this->SetXY($x, $y);
// Trim text if needed
if (($this->GetStringWidth($text)) > $w) {
// Keep trimming until the size, with ellipsis, is small enough
while (($this->GetStringWidth($text.$ellipsis)) > $w) {
$text = substr($text,0,strlen($text)-1);
}
// Add the ellipsis to the shortened text
$text = $text.$ellipsis;
}
// Create text cell
$this->Cell($w,$h,$text,0,0,$font['posn'],false,$link);
}
/**
* Print image. This is basically a wrapper around the FPDF image function,
* except that it determines the file type independent of the file extension
* and automatically resizes to main aspect ratio within the defined space.
* Note that this provides robustness for images with incorrect filenames, such
* as missing_movie.png being called a jpg when copied as a thumbnail in v3.0.2.
*/
function printImage($imagePath, $x, $y, $w, $h, $link = null, $cfgImageMissing) {
$imageInfo = getimagesize($imagePath); // [0]=w, [1]=h, [2]=type (1=GIF, 2=JPG, 3=PNG)
// Figure out the filetype
switch($imageInfo[2]) {
case 3:
$imageType = 'PNG';
break;
case 2:
$imageType = 'JPG';
break;
case 1:
if ($cfgImageMissing['GDflag']) {
$imageType = 'GIF';
break;
}
default:
// use the missing image icon instead
$imagePath = $cfgImageMissing['iconPath'];
$imageType = $cfgImageMissing['iconType'];
$imageInfo = $cfgImageMissing['iconInfo'];
}
// Determine image orientation and create image
$ratioWH = ($imageInfo[0]/$w) / ($imageInfo[1]/$h);
if ($ratioWH>1) {
$this->image($imagePath, $x, $y+(1-1/$ratioWH)*$h/2, $w, 0, $imageType, $link);
} else {
$this->image($imagePath, $x+(1-$ratioWH)*$w/2, $y, 0, $h, $imageType, $link);
}
}
}

View File

@ -17,32 +17,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* Generate a PDF proof sheet on-the-fly of the current album or tag.
* By Shad Laws. Version 5, 2012/04/05
*
* 2012/04/05, version 5
* Added ability to include GIF thumbnails if GD is installed (FPDF uses GD)
* Changed behavior of unhandled file types - now provides missing image icon instead of throwing an exception
* 2012/03/30, version 4
* Major rewrite. Output is similar, but everything "under the hood" is much cleaner and (I hope) more easily understood and tweakable by other users.
* Header link is now an icon.
* Fixed encoding problems with diacritic marks and special characters.
* Now includes FPDF as a library instead of requiring a separate installtion.
* 2012/03/28, version 3
* Made sizing configuration more flexible
* Prettified code so it's easier to understand and tweak as desired
* Added header link
* First version properly documented and linked to Gallery wiki
* 2012/03/27, version 2
* Determines jpg/png type by file header, not extension, which makes it robust against misnamed extensions
* (N.B.: there's a bug in some movie modules that copy missing_movie.png as a jpg thumbnail!)
* Made caption size limits to prevent overrun
* 2012/03/27, version 1
* Initial release
*/
class proofsheet_event_Core {
/**

View File

@ -1,6 +1,6 @@
name = "ProofSheet"
description = "Displays links to generate a PDF proof sheet of the current album in A4 or LTR size."
version = 5
version = 7
author_name = "Shad Laws"
author_url = ""
info_url = "http://codex.gallery2.org/Gallery3:Modules:proofsheet"

View File

@ -35,11 +35,17 @@ class Admin_register_Controller extends Admin_Controller {
$post->add_rules("policy", "required");
$post->add_rules("group", array($this, "passthru"));
$post->add_rules("email_verification", array($this, "passthru"));
// added Shad Laws, v2
$post->add_rules("admin_notify", array($this, "passthru"));
$post->add_rules("subject_prefix", array($this, "passthru"));
$group_list = array();
if ($post->validate()) {
module::set_var("registration", "policy", $post->policy);
module::set_var("registration", "default_group", $post->group);
module::set_var("registration", "email_verification", !empty($post->email_verification));
// added Shad Laws, v2
module::set_var("registration", "admin_notify", !empty($post->admin_notify));
module::set_var("registration", "subject_prefix", $post->subject_prefix);
message::success(t("Registration defaults have been updated."));
@ -108,6 +114,12 @@ class Admin_register_Controller extends Admin_Controller {
if (empty($admin->email)) {
module::set_var("registration", "email_verification", false);
}
// below lines added Shad Laws, v2
$v->content->disable_admin_notify =
empty($admin->email) || $form["policy"] !== "admin_approval" ? "disabled" : "";
if (empty($admin->email)) {
module::set_var("registration", "admin_notify", false);
}
$v->content->group_list = array();
foreach (identity::groups() as $group) {
@ -134,7 +146,10 @@ class Admin_register_Controller extends Admin_Controller {
private function _get_form() {
$form = array("policy" => module::get_var("registration", "policy"),
"group" => module::get_var("registration", "default_group"),
"email_verification" => module::get_var("registration", "email_verification"));
"email_verification" => module::get_var("registration", "email_verification"),
// added Shad Laws, v2
"subject_prefix" => module::get_var("registration", "subject_prefix"),
"admin_notify" => module::get_var("registration", "admin_notify"));
$errors = array_fill_keys(array_keys($form), "");
return array($form, $errors);

View File

@ -51,9 +51,15 @@ class register_Controller extends Controller {
} else if ($pending_user->state == 1) {
site_status::warning(
t("There are pending user registration. <a href=\"%url\">Review now!</a>",
array("url" => html::mark_clean(url::site("admin/register")))),
// modified by Shad Laws, v2
// array("url" => html::mark_clean(url::site("admin/register")))),
array("url" => html::mark_clean(url::site("admin/register")), "locale" => module::get_var("gallery", "default_locale"))),
"pending_user_registrations");
message::success(t("Your registration request is awaiting administrator approval"));
// added by Shad Laws, v2
if (module::get_var("registration", "admin_notify") == 1) {
register::send_admin_notify($pending_user);
}
} else {
register::send_confirmation($pending_user);
message::success(t("A confirmation email has been sent to your email address."));
@ -84,9 +90,15 @@ class register_Controller extends Controller {
} else {
site_status::warning(
t("There are pending user registration. <a href=\"%url\">Review now!</a>",
array("url" => html::mark_clean(url::site("admin/register")))),
// modified by Shad Laws, v2
// array("url" => html::mark_clean(url::site("admin/register")))),
array("url" => html::mark_clean(url::site("admin/register")), "locale" => module::get_var("gallery", "default_locale"))),
"pending_user_registrations");
message::success(t("Your registration request is awaiting administrator approval"));
// added by Shad Laws, v2
if (module::get_var("registration", "admin_notify") == 1) {
register::send_admin_notify($pending_user);
}
}
} else {
message::error(t("Your registration request is no longer valid, Please re-register."));
@ -144,7 +156,9 @@ class register_Controller extends Controller {
->rules("required|valid_email|length[1,255]");
$group->input("email2")->label(t("Confirm email"))->id("g-email2")
->matches($group->email);
$group->input("url")->label(t("URL"))->id("g-url")
// modified by Shad Laws, v2
// $group->input("url")->label(t("URL"))->id("g-url")
$group->input("url")->label(t("URL")." (".t("optional").")")->id("g-url")
->rules("valid_url");
module::event("register_add_form", $form);

View File

@ -0,0 +1 @@
#g-register-form { width: 350px; }

View File

@ -47,14 +47,35 @@ class register_Core {
$message->user = $user;
$message->site_url = $requires_first ? url::abs_site("register/first/{$user->hash}") :
url::abs_site("");
self::_sendemail($user->email, t("Your userid has been created"), $message);
// added Shad Laws, v2
$message->subject_prefix = module::get_var("registration", "subject_prefix");
$message->locale = $user->locale; // as stored in pending_users table
$message->subject = t("Welcome", array("locale" => $message->locale));
// modified Shad Laws, v2
self::_sendemail($user->email, $message->subject_prefix.$message->subject, $message);
}
static function send_confirmation($user) {
$message = new View("confirm_registration.html");
$message->confirm_url = url::abs_site("register/confirm/{$user->hash}");
$message->user = $user;
self::_sendemail($user->email, t("User registration confirmation"), $message);
// added Shad Laws, v2
$message->subject_prefix = module::get_var("registration", "subject_prefix");
$message->locale = $user->locale; // as stored in pending_users table
$message->subject = t("User registration confirmation", array("locale" => $message->locale));
// modified Shad Laws, v2
self::_sendemail($user->email, $message->subject_prefix.$message->subject, $message);
}
// function added Shad Laws, v2
static function send_admin_notify($user) {
$message = new View("register_admin_notify.html");
$message->admin_register_url = url::abs_site("admin/register");
$message->user = $user;
$message->subject_prefix = module::get_var("registration", "subject_prefix");
$message->locale = module::get_var("gallery", "default_locale"); // as Gallery default
$message->subject = t("New pending user registration", array("locale" => $message->locale));
self::_sendemail(module::get_var("gallery", "email_reply_to"), $message->subject_prefix.$message->subject, $message);
}
static function create_pending_request($form) {
@ -66,6 +87,8 @@ class register_Core {
$user->email = $form->register_user->inputs["email"]->value;
$user->url = $form->register_user->inputs["url"]->value;
$user->request_date = time();
// added by Shad Laws, v2
$user->locale = locales::locale_from_http_request() ? locales::locale_from_http_request() : module::get_var("gallery", "default_locale"); // sets default locale based on browser
if (!$email_verification) {
$user->state = 1;
@ -98,7 +121,8 @@ class register_Core {
->to($email)
->subject($subject)
->header("Mime-Version", "1.0")
->header("Content-type", "text/html; charset=iso-8859-1")
// modified by Shad Laws, v2
->header("Content-type", "text/html; charset=utf-8")
->message($message->render())
->send();
}

View File

@ -37,11 +37,34 @@ class register_installer {
module::set_var("registration", "policy", "admin_only");
module::set_var("registration", "default_group", "");
module::set_var("registration", "email_verification", false);
// added Shad Laws, v2
module::set_var("registration", "admin_notify", false);
module::set_var("registration", "subject_prefix", "");
$db->query("ALTER TABLE {pending_users} ADD `locale` varchar(32) default NULL;");
// changed Shad Laws, v2
module::set_version("register", 2);
}
module::set_version("register", 1);
// function added Shad Laws, v2
static function upgrade() {
if (module::get_version("register") < 1) {
module::install("register");
}
if (is_null(module::get_var("registration", "admin_notify")) ||
is_null(module::get_var("registration", "subject_prefix")) ||
(module::get_version("register") < 2) ) {
module::set_var("registration", "admin_notify", false);
module::set_var("registration", "subject_prefix", "");
$db = Database::instance();
$db->query("ALTER TABLE {pending_users} ADD `locale` varchar(32) default NULL;");
}
module::set_version("register", 2);
}
static function uninstall() {
Database::instance()->query("DROP TABLE IF EXISTS {pending_users};");
// added Shad Laws, v2
module::clear_all_vars("registration");
}
}

View File

@ -25,4 +25,9 @@ class register_theme_Core {
return new View("register_welcome_message_loader.html");
}
}
// added Shad Laws, v2
static function head($theme) {
return $theme->css("register.css");
}
}

View File

@ -1,7 +1,7 @@
name = "User Registration"
description = "Allow guests to register as users."
version = 1
author_name = ""
version = 2
author_name = "revised to v2 by Shad Laws"
author_url = ""
info_url = "http://codex.gallery2.org/Gallery3:Modules:register"
discuss_url = "http://gallery.menalto.com/forum_module_register"

View File

@ -7,6 +7,12 @@
} else {
$(":checkbox[name=email_verification]").removeAttr("disabled");
}
if ($(this).val() !== "admin_approval") {
$(":checkbox[name=admin_notify]").attr("disabled", "disabled");
} else {
$(":checkbox[name=admin_notify]").removeAttr("disabled");
}
});
});
</script>
@ -27,6 +33,14 @@
<?= form::checkbox("email_verification", "true", !empty($form["email_verification"]), $disable_email) ?>
<?= form::label("email_verification", t("Require e-mail verification when a visitor creates an account")) ?>
</li>
<li>
<?= form::checkbox("admin_notify", "true", !empty($form["admin_notify"]), $disable_admin_notify) ?>
<?= form::label("admin_notify", t("Send a pending user registration notification email to the site 'reply to' email address")) ?>
</li>
<li>
<?= form::input("subject_prefix", $form["subject_prefix"]) ?>
<?= form::label("subject_prefix", t("Email subject line prefix, with trailing spaces as needed (e.g. '[Gallery3] ')")) ?>
</li>
<li>
<? if (!empty($group_list)): ?>
<label for="group" class="g-left"> <?= t("Default group: ") ?></label>

View File

@ -1,17 +1,18 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<html>
<head>
<title><?= t("User registration confirmation") ?> </title>
<title><?= ($subject_prefix.$subject) ?></title>
</head>
<body>
<h2><?= t("User registration confirmation") ?> </h2>
<h2><?= $subject ?></h2>
<p>
<?= t("Hello, %name,", array("name" => $user->full_name ? $user->full_name : $user->name)) ?>
<?= t("Hello %name,", array("name" => $user->full_name ? $user->full_name : $user->name, "locale" => $locale)) ?>
</p>
<p>
<?= t("We received a request to to create a user with this email. If you made this request, you can confirm it by <a href=\"%confirm_url\">clicking this link</a>. If you didn't request this password reset, it's ok to ignore this mail.",
<?= t("We received a request to to create a user with this email. If you made this request, you can confirm it by <a href=\"%confirm_url\">clicking this link</a>. If you didn't request this password reset, it's ok to ignore this mail.",
array("site_url" => html::mark_clean(url::base(false, "http")),
"confirm_url" => $confirm_url)) ?>
"confirm_url" => $confirm_url,
"locale" => $locale)) ?>
</p>
</body>
</html>

View File

@ -0,0 +1,15 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<html>
<head>
<title><?= ($subject_prefix.$subject) ?></title>
</head>
<body>
<h2><?= $subject ?></h2>
<p>
<?= t("There's a new pending user registration from %name.<br/>You can access the site by <a href=\"%site_url\">clicking this link</a>, after which you can review and approve this request.",
array("name" => $user->full_name ? $user->full_name : $user->name,
"locale" => $locale,
"site_url" => html::mark_clean($admin_register_url))) ?>
</p>
</body>
</html>

View File

@ -1,16 +1,17 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<html>
<head>
<title><?= t("Welcome to Gallery3") ?> </title>
<title><?= ($subject_prefix.$subject) ?></title>
</head>
<body>
<h2><?= t("Welcome") ?> </h2>
<h2><?= $subject ?></h2>
<p>
<?= t("Hello, %name,", array("name" => $user->full_name ? $user->full_name : $user->name)) ?>
<?= t("Hello %name,", array("name" => $user->full_name ? $user->full_name : $user->name, "locale" => $locale)) ?>
</p>
<p>
<?= t("The user account you requested as been created.<br/>You can access the site by <a href=\"%site_url\">clicking this link</a> and you will be prompted to set your password at this point.",
array("site_url" => html::mark_clean($site_url))) ?>
<?= t("The user account you requested as been created.<br/>You can access the site by <a href=\"%site_url\">clicking this link</a> and you will be prompted to set your password at this point.",
array("site_url" => html::mark_clean($site_url),
"locale" => $locale)) ?>
</p>
</body>
</html>

View File

@ -23,36 +23,101 @@ class search extends search_Core {
* few terms in the query.
*/
static function add_query_terms($q) {
$MAX_TERMS = 5;
$MAX_TERMS = 5; // used to limit the max number of extra terms
$prefix = module::get_var("short_search_fix","search_prefix"); // short search fix prefix
// strip leading, trailing, and extra whitespaces
$terms = preg_replace('/^\s+/', '', $q);
$terms = preg_replace('/\s+$/', '', $terms);
$terms = preg_replace('/\s\s+/', ' ', $terms);
$terms = explode(" ", $terms, $MAX_TERMS);
//$terms = explode(" ", $q, $MAX_TERMS); // commented out from original function
for ($i = 0; $i < min(count($terms), $MAX_TERMS - 1); $i++) {
// Don't wildcard quoted or already wildcarded terms
if ((substr($terms[$i], 0, 1) != '"') && (substr($terms[$i], -1, 1) != "*")) {
$terms[] = rtrim($terms[$i], "s") . "*";
// explode terms, initialize the loop
$terms = explode(" ", $terms); // array
$termsextra = ""; // string, not array
$numtermsextra = 0;
$flagwild = 1;
$countquote = 0;
$countparen = 0;
// run the loop for each term
foreach ($terms as &$term) {
$countprefix = 0;
$countsuffix = 0;
$flagopenparen = 0;
$flagcloseparen = 0;
// set flagwild to 0 if we're over MAX_TERMS (only runs if we're not in the middle of parens/quotes)
if ($countparen == 0 && $countquote == 0 && ($numtermsextra >= ($MAX_TERMS - 1))) {
$flagwild = 0;
}
// find opening special characters
while ((substr($term, $countprefix, 1) == "(" ||
substr($term, $countprefix, 1) == '"' ||
substr($term, $countprefix, 1) == "+" ||
substr($term, $countprefix, 1) == "-" ||
substr($term, $countprefix, 1) == "~" ||
substr($term, $countprefix, 1) == "<" ||
substr($term, $countprefix, 1) == ">") &&
($countprefix+$countsuffix) < strlen($term)) {
if (substr($term, $countprefix, 1) == '"') {
$countquote++;
$flagwild = 0;
}
if (substr($term, $countprefix, 1) == "(") {
$countparen++;
$flagopenparen = 1;
}
$countprefix++;
}
// reset flagwild to 1 if we're under MAX_TERMS (only runs if we're not in the middle of quotes, and forced to run if we're still in paren)
if ($countquote == 0 && ($countparen > 0 || $numtermsextra < ($MAX_TERMS - 1))) {
$flagwild = 1;
}
// find closing special characters
while ((substr($term, -$countsuffix-1, 1) == ")" ||
substr($term, -$countsuffix-1, 1) == '"') &&
($countprefix+$countsuffix) < strlen($term)) {
if (substr($term, -$countsuffix-1, 1) == '"') {
$countquote = max(0, $countquote-1);
}
if (substr($term, -$countsuffix-1, 1) == ")") {
$countparen = max(0, $countparen-1);
$flagcloseparen = 1;
}
$countsuffix++;
}
// split term
$termprefix = substr($term, 0, $countprefix);
$termterm = substr($term."A", $countprefix, -$countsuffix-1); // artificial padded A assures that the third argument is always negative
$termsuffix = substr($term, -$countsuffix, $countsuffix);
// add extra terms with wildcards
if ($flagwild == 1 &&
substr($termterm, -1, 1) != "*" &&
strlen($termterm) > 0) {
// @todo: make this i18n friendly with the plural character (only works here with s)
$termsextra = $termsextra . $termprefix . $prefix . rtrim($termterm, "s") . "*" . $termsuffix . " ";
$numtermsextra++;
} elseif ($flagopenparen == 1 && $flagcloseparen == 0) {
$termsextra = $termsextra . str_replace('"', '', $termprefix);
} elseif ($flagopenparen == 0 && $flagcloseparen == 1) {
$termsextra = preg_replace('/\s+$/', '', $termsextra) . ") ";
}
// add short search prefixes
if (strlen($termterm) > 0) {
$term = $termprefix . $prefix . $termterm . $termsuffix;
}
}
//return implode(" ", $terms); // commented out from original function
/**
* Add the search prefix to the start of every word.
*/
$prefix = module::get_var("short_search_fix","search_prefix");
// implode terms, trim termsextra trailing space (if it exists)
$terms = implode(" ", $terms);
$terms = preg_replace('/^\s+/', '', $terms); // the implode seems to add this back in
// add the prefixes
if (preg_match('/\w/',$terms) > 0) {
$terms = ' ' . $terms;
$terms = str_replace(' ', ' '.$prefix, $terms);
$terms = str_replace(' '.$prefix.'"', ' '.'"'.$prefix, $terms);
$terms = substr($terms,1);
$termsextra = preg_replace('/\s+$/', '', $termsextra);
// add extra closing quotes and parentheses
while ($countquote > 0) {
$terms = $terms.'"';
$termsextra = $termsextra.'"';
$countquote--;
}
return $terms;
while ($countparen > 0) {
$terms = $terms.")";
$termsextra = $termsextra.")";
$countparen--;
}
// all done!
return ($terms." ".$termsextra);
}
}
}

View File

@ -1,6 +1,6 @@
name = "Short Search Fix"
description = "Allows 2-3 letter searches to be performed without requiring SQL system variable modification (useful for shared hosting)."
version = 1
version = 2
author_name = "Shad Laws"
author_url = ""
info_url = "http://codex.gallery2.org/Gallery3:Modules:short_search_fix"

View File

@ -29,111 +29,88 @@ class Admin_Tag_Cloud_Html5_Controller extends Admin_Controller {
$cfg = $this->_get_config();
$form = $this->_get_admin_form();
if ($form->validate()) {
if ($form->options_general->load_defaults->value) {
if ($form->general->reset_defaults->value) {
// reset all to defaults, redirect with message
module::install("tag_cloud_html5");
message::success(t("Tag cloud options reset successfully"));
url::redirect("admin/tag_cloud_html5");
} else {
$valid = true;
// run checks on various inputs
$options_general = $form->options_general;
if ($options_general->height_sidebar->value < 0) {
$form->options_general->height_sidebar->add_error("not_valid", 1);
$valid = false;
}
foreach ($cfg['groups'] as $groupname => $grouptext) {
${"options".$groupname} = $form->{"options".$groupname};
if ($options_general->{"maxtags".$groupname}->value < 0) {
$form->options_general->{"maxtags".$groupname}->add_error("not_valid", 1);
$valid = false;
}
if (${"options".$groupname}->{"maxSpeed".$groupname}->value < 0) {
$form->{"options".$groupname}->{"maxSpeed".$groupname}->add_error("not_valid", 1);
$valid = false;
}
if ((${"options".$groupname}->{"initialX".$groupname}->value < -1) || (${"options".$groupname}->{"initialX".$groupname}->value > 1)) {
$form->{"options".$groupname}->{"initialX".$groupname}->add_error("not_valid", 1);
$valid = false;
}
if ((${"options".$groupname}->{"initialY".$groupname}->value < -1) || (${"options".$groupname}->{"initialY".$groupname}->value > 1)) {
$form->{"options".$groupname}->{"initialY".$groupname}->add_error("not_valid", 1);
$valid = false;
}
if ((${"options".$groupname}->{"deadZone".$groupname}->value < 0) || (${"options".$groupname}->{"deadZone".$groupname}->value > 1)) {
$form->{"options".$groupname}->{"deadZone".$groupname}->add_error("not_valid", 1);
$valid = false;
}
if (${"options".$groupname}->{"zoom".$groupname}->value < 0) {
$form->{"options".$groupname}->{"zoom".$groupname}->add_error("not_valid", 1);
$valid = false;
}
if ((${"options".$groupname}->{"depth".$groupname}->value < 0) || (${"options".$groupname}->{"depth".$groupname}->value > 1)) {
$form->{"options".$groupname}->{"depth".$groupname}->add_error("not_valid", 1);
$valid = false;
}
if (${"options".$groupname}->{"outlineOffset".$groupname}->value < 0) {
$form->{"options".$groupname}->{"outlineOffset".$groupname}->add_error("not_valid", 1);
$valid = false;
}
if (preg_match("/^#[0-9A-Fa-f]{6}$/", ${"options".$groupname}->{"outlineColour".$groupname}->value) == 0) {
$form->{"options".$groupname}->{"outlineColour".$groupname}->add_error("not_valid", 1);
$valid = false;
}
if ((preg_match("/^#[0-9A-Fa-f]{6}$/", ${"options".$groupname}->{"textColour".$groupname}->value) == 0) && (strcmp(${"options".$groupname}->{"textColour".$groupname}->value, "") != 0) ) {
$form->{"options".$groupname}->{"textColour".$groupname}->add_error("not_valid", 1);
$valid = false;
}
if (${"options".$groupname}->{"textHeight".$groupname}->value < 0) {
$form->{"options".$groupname}->{"textHeight".$groupname}->add_error("not_valid", 1);
$valid = false;
}
}
if ($valid) {
// all inputs passed tests above; save them
module::set_var("tag_cloud_html5", "show_wholecloud_link", ($options_general->show_wholecloud_link->value == 1));
module::set_var("tag_cloud_html5", "show_add_tag_form", ($options_general->show_add_tag_form->value == 1));
module::set_var("tag_cloud_html5", "height_sidebar", $options_general->height_sidebar->value);
module::set_var("tag_cloud_html5", "show_wholecloud_list", ($options_general->show_wholecloud_list->value == 1));
foreach ($cfg['groups'] as $groupname => $grouptext) {
module::set_var("tag_cloud_html5", "maxtags".$groupname, $options_general->{"maxtags".$groupname}->value);
$optionsarray = array();
$optionsarray['maxSpeed'] = ${"options".$groupname}->{"maxSpeed".$groupname}->value;
$optionsarray['deadZone'] = ${"options".$groupname}->{"deadZone".$groupname}->value;
$optionsarray['initial'] = array(${"options".$groupname}->{"initialX".$groupname}->value, ${"options".$groupname}->{"initialY".$groupname}->value);
$optionsarray['initialDecel'] = (${"options".$groupname}->{"initialDecel".$groupname}->value == 1);
$optionsarray['zoom'] = ${"options".$groupname}->{"zoom".$groupname}->value;
$optionsarray['depth'] = ${"options".$groupname}->{"depth".$groupname}->value;
$optionsarray['outlineMethod'] = ${"options".$groupname}->{"outlineMethod".$groupname}->value;
$optionsarray['outlineOffset'] = ${"options".$groupname}->{"outlineOffset".$groupname}->value;
$optionsarray['outlineColour'] = ${"options".$groupname}->{"outlineColour".$groupname}->value;
$optionsarray['textColour'] = ${"options".$groupname}->{"textColour".$groupname}->value;
$optionsarray['textFont'] = ${"options".$groupname}->{"textFont".$groupname}->value;
$optionsarray['textHeight'] = ${"options".$groupname}->{"textHeight".$groupname}->value;
$optionsarray['frontSelect'] = (${"options".$groupname}->{"frontSelect".$groupname}->value == 1);
$optionsarray['wheelZoom'] = false; // note that this is locked - otherwise scrolling through the page screws everything up
module::set_var("tag_cloud_html5", "options".$groupname, json_encode($optionsarray));
}
// all done; redirect with message
message::success(t("Tag cloud options updated successfully"));
url::redirect("admin/tag_cloud_html5");
}
}
// save the new inputs
module::set_var("tag_cloud_html5", "show_wholecloud_link", ($form->general->show_wholecloud_link->value == 1));
module::set_var("tag_cloud_html5", "show_add_tag_form", ($form->general->show_add_tag_form->value == 1));
module::set_var("tag_cloud_html5", "show_wholecloud_list", ($form->general->show_wholecloud_list->value == 1));
foreach ($cfg['groups'] as $groupname => $grouptext) {
module::set_var("tag_cloud_html5", "maxtags".$groupname, $form->{"size".$groupname}->{"maxtags".$groupname}->value);
module::set_var("tag_cloud_html5", "width".$groupname, $form->{"size".$groupname}->{"width".$groupname}->value);
module::set_var("tag_cloud_html5", "height".$groupname, $form->{"size".$groupname}->{"height".$groupname}->value);
$optionsarray = array();
// group size
$optionsarray['shape'] = $form->{"size".$groupname}->{"shape".$groupname}->value;
$optionsarray['zoom'] = $form->{"size".$groupname}->{"zoom".$groupname}->value;
$optionsarray['stretchX'] = $form->{"size".$groupname}->{"stretchX".$groupname}->value;
$optionsarray['stretchY'] = $form->{"size".$groupname}->{"stretchY".$groupname}->value;
// group motion
$optionsarray['maxSpeed'] = $form->{"motion".$groupname}->{"maxSpeed".$groupname}->value;
$optionsarray['minSpeed'] = $form->{"motion".$groupname}->{"minSpeed".$groupname}->value;
$optionsarray['deadZone'] = $form->{"motion".$groupname}->{"deadZone".$groupname}->value;
$optionsarray['decel'] = $form->{"motion".$groupname}->{"decel".$groupname}->value;
$optionsarray['initial'] = array($form->{"motion".$groupname}->{"initialX".$groupname}->value, $form->{"motion".$groupname}->{"initialY".$groupname}->value);
$optionsarray['maxInputZone'] = $form->{"motion".$groupname}->{"maxInputZone".$groupname}->value;
// group select
$optionsarray['outlineMethod'] = $form->{"select".$groupname}->{"outlineMethod".$groupname}->value;
$optionsarray['outlineOffset'] = $form->{"select".$groupname}->{"outlineOffset".$groupname}->value;
$optionsarray['outlineColour'] = $form->{"select".$groupname}->{"outlineColour".$groupname}->value;
$optionsarray['frontSelect'] = ($form->{"select".$groupname}->{"frontSelect".$groupname}->value == 1);
// group appearance
$optionsarray['textHeight'] = $form->{"appearance".$groupname}->{"textHeight".$groupname}->value;
$optionsarray['textColour'] = $form->{"appearance".$groupname}->{"textColour".$groupname}->value;
$optionsarray['textFont'] = $form->{"appearance".$groupname}->{"textFont".$groupname}->value;
$optionsarray['depth'] = $form->{"appearance".$groupname}->{"depth".$groupname}->value;
// options that are not explicitly defined in admin menu
$optionsarray['wheelZoom'] = false; // otherwise scrolling through the page screws everything up (was a problem in v1)
$optionsarray['initialDecel'] = true; // this was an option in v4, but it's sorta useless - use minSpeed for a related but better effect
$optionsarray['physModel'] = true; // this is the big enhancement for v5, and is a major modification that I did to TagCanvas
switch ($optionsarray['shape']) {
case "hcylinder":
// keep it horizontal - lock x-axis rotation
$optionsarray['lock'] = "x";
break;
case "vcylinder":
// keep it vertical - lock y-axis rotation
$optionsarray['lock'] = "y";
break;
default:
// do not lock either axis
$optionsarray['lock'] = "";
}
module::set_var("tag_cloud_html5", "options".$groupname, json_encode($optionsarray));
}
// all done; redirect with message
message::success(t("Tag cloud options updated successfully"));
url::redirect("admin/tag_cloud_html5");
}
// print screen from existing form - you wind up here if something wasn't validated
// not valid - print screen from existing form
$this->_print_screen($form);
}
private function _get_config() {
// these define the two variable name groups, along with their labels which are always shown with t() for i18n.
$cfg['groups'] = array("_sidebar"=>"Sidebar", "_wholecloud"=>"Whole cloud");
$cfg['groups'] = array("_sidebar"=>t("Sidebar"), "_wholecloud"=>t("Whole cloud"));
// this defines the separator that's used between the group name and the attribute, and is *not* put through t().
$cfg['sep'] = ": ";
$cfg['sep'] = " : ";
// this is used in the labels of the width/height parameters
$cfg['size'] = array("_sidebar"=>t("as fraction of sidebar width, e.g. 'g-block-content' class"), "_wholecloud"=>t("as fraction of browser window height"));
return $cfg;
}
private function _print_screen($form) {
// this part is a bit of a hack, but Forge doesn't seem to allow set_attr() for groups.
$form = $form->render();
$form = preg_replace("/<fieldset>/","<fieldset class=\"g-tag-cloud-html5-admin-form-top\">",$form,1);
$form = preg_replace("/<fieldset>/","<fieldset class=\"g-tag-cloud-html5-admin-form-left\">",$form,4);
$form = preg_replace("/<fieldset>/","<fieldset class=\"g-tag-cloud-html5-admin-form-right\">",$form,4);
$view = new Admin_View("admin.html");
$view->content = new View("admin_tag_cloud_html5.html");
$view->content->form = $form;
@ -144,12 +121,12 @@ class Admin_Tag_Cloud_Html5_Controller extends Admin_Controller {
$cfg = $this->_get_config();
$sep = $cfg['sep'];
// Make the form. This form has three groups: group_general, group_sidebar, and group_wholecloud.
// Make the main form. This form has *nine* groups: general, then size, motion, select, and appearance for _sidebar and _wholecloud.
$form = new Forge("admin/tag_cloud_html5/edit", "", "post", array("id" => "g-tag-cloud-html5-admin-form"));
// group_general
$group_general = $form->group("options_general")->label(t("Tag cloud options").$sep.t("General"));
$group_general->checkbox("load_defaults")
// group general
$group_general = $form->group("general")->label(t("General"))->set_attr("id","g-tag-cloud-html5-admin-form-general");
$group_general->checkbox("reset_defaults")
->label(t("Reset all to default values"))
->checked(false);
$group_general->checkbox("show_wholecloud_link")
@ -158,94 +135,115 @@ class Admin_Tag_Cloud_Html5_Controller extends Admin_Controller {
$group_general->checkbox("show_add_tag_form")
->label(t("Show 'Add tag to album' form in sidebar (when permitted and applicable)"))
->checked(module::get_var("tag_cloud_html5", "show_add_tag_form", null));
$group_general->input("height_sidebar")
->label(t("Height of sidebar (as fraction of width)"))
->value(round(module::get_var("tag_cloud_html5", "height_sidebar", null),3)) // round or else it gets 6 decimal places...
->error_message("not_valid", t("Height of sidebar must be a 1-5 digit number"))
->rules("required|valid_numeric|length[1,5]");
$group_general->checkbox("show_wholecloud_list")
->label(t("Show tag list under cloud on 'View whole cloud' page"))
->label(t("Show inline tag list under cloud on 'View whole cloud' page")." {hideTags}")
->checked(module::get_var("tag_cloud_html5", "show_wholecloud_list", null));
foreach ($cfg['groups'] as $groupname => $grouptext) {
// maxtags - note that this is displayed under group_general!
$maxtags = module::get_var("tag_cloud_html5", "maxtags".$groupname, null);
$group_general->input("maxtags".$groupname)
->label(t($grouptext).$sep.t("max tags shown"))
->value($maxtags)
->error_message("not_valid", t("Max tags must be a 1-4 digit number"))
->rules("required|valid_numeric|length[1,4]");
// group_sidebar and group_wholecloud
$maxtags = strval(module::get_var("tag_cloud_html5", "maxtags".$groupname, null));
$width = strval(module::get_var("tag_cloud_html5", "width".$groupname, null));
$height = strval(module::get_var("tag_cloud_html5", "height".$groupname, null));
$options = json_decode(module::get_var("tag_cloud_html5", "options".$groupname, null),true);
${"group".$groupname} = $form->group("options".$groupname)->label(t("Tag cloud options").$sep.t($grouptext));
${"group".$groupname}->input("maxSpeed".$groupname)
->label(t($grouptext).$sep.t("max speed (typically 0.01-0.20)"))
->value($options['maxSpeed'])
->error_message("not_valid", t("Max speed must be a 1-5 digit number"))
->rules("required|valid_numeric|length[1,5]");
${"group".$groupname}->input("initialX".$groupname)
->label(t($grouptext).$sep.t("initial horizontal speed (between +/-1.0, as fraction of max speed)"))
->value($options['initial'][0])
->error_message("not_valid", t("Initial horizontal speed must be a 1-4 digit number"))
->rules("required|valid_numeric|length[1,4]");
${"group".$groupname}->input("initialY".$groupname)
->label(t($grouptext).$sep.t("initial vertical speed (between +/-1.0, as fraction of max speed)"))
->value($options['initial'][1])
->error_message("not_valid", t("Initial vertical speed must be a 1-4 digit number"))
->rules("required|valid_numeric|length[1,4]");
${"group".$groupname}->checkbox("initialDecel".$groupname)
->label(t($grouptext).$sep.t("initial deceleration (if false, the initial speed is held until a mouseover event)"))
->checked($options['initialDecel']);
${"group".$groupname}->input("deadZone".$groupname)
->label(t($grouptext).$sep.t("dead zone (0.0-1.0, where 0.0 is no dead zone and 1.0 is no active zone)"))
->value($options['deadZone'])
->error_message("not_valid", t("Dead zone must be a 1-4 digit number"))
->rules("required|valid_numeric|length[1,4]");
${"group".$groupname}->input("zoom".$groupname)
->label(t($grouptext).$sep.t("zoom (<1.0 is zoom out, >1.0 is zoom in)"))
// group size/shape
${"group_size".$groupname} = $form->group("size".$groupname)->label(t("Size and shape").$sep.$grouptext);
${"group_size".$groupname}->input("maxtags".$groupname)
->label(t("maximum tags shown"))
->value($maxtags)
->rules("required|numrange[0]");
${"group_size".$groupname}->input("width".$groupname)
->label(t("width")." (".$cfg['size'][$groupname].")")
->value($width)
->rules("required|numrange[0]");
${"group_size".$groupname}->input("height".$groupname)
->label(t("height")." (".$cfg['size'][$groupname].")")
->value($height)
->rules("required|numrange[0]");
${"group_size".$groupname}->dropdown("shape".$groupname)
->label(t("shape of cloud")." {shape,lock}")
->options(array("sphere"=>t("sphere"),"hcylinder"=>t("horizontal cylinder"),"vcylinder"=>t("vertical cylinder")))
->selected($options['shape']);
${"group_size".$groupname}->input("zoom".$groupname)
->label(t("zoom (<1.0 is zoom out, >1.0 is zoom in)")." {zoom}")
->value($options['zoom'])
->error_message("not_valid", t("Zoom must be a 1-4 digit number"))
->rules("required|valid_numeric|length[1,4]");
${"group".$groupname}->input("depth".$groupname)
->label(t($grouptext).$sep.t("depth (0.0-1.0)"))
->value($options['depth'])
->error_message("not_valid", t("Depth must be a 1-4 digit number"))
->rules("required|valid_numeric|length[1,4]");
${"group".$groupname}->dropdown("outlineMethod".$groupname)
->label(t($grouptext).$sep.t("outline method (mouseover event)"))
->rules("required|numrange[0]");
${"group_size".$groupname}->input("stretchX".$groupname)
->label(t("x-axis stretch factor (<1.0 squishes, >1.0 stretches)")." {stretchX}")
->value($options['stretchX'])
->rules("required|numrange[0]");
${"group_size".$groupname}->input("stretchY".$groupname)
->label(t("y-axis stretch factor (<1.0 squishes, >1.0 stretches)")." {stretchY}")
->value($options['stretchY'])
->rules("required|numrange[0]");
// group motion
${"group_motion".$groupname} = $form->group("motion".$groupname)->label(t("Motion").$sep.$grouptext);
${"group_motion".$groupname}->input("maxSpeed".$groupname)
->label(t("max speed (typically 0.01-0.20)")." {maxSpeed}")
->value($options['maxSpeed'])
->rules("required|numrange[0]");
${"group_motion".$groupname}->input("minSpeed".$groupname)
->label(t("no mouseover speed (typically 0.00-0.01)")." {minSpeed}")
->value($options['minSpeed'])
->rules("required|numrange[0]");
${"group_motion".$groupname}->input("deadZone".$groupname)
->label(t("dead zone size (0.0-1.0 - 0.0 is none and 1.0 is entire cloud)")." {deadZone}")
->value($options['deadZone'])
->rules("required|numrange[0,1]");
${"group_motion".$groupname}->input("decel".$groupname)
->label(t("inertia (0.0-1.0 - 0.0 changes velocity instantly and 1.0 never changes)")." {decel}")
->value($options['decel'])
->rules("required|numrange[0,1]");
${"group_motion".$groupname}->input("initialX".$groupname)
->label(t("initial horizontal speed (between +/-1.0, as fraction of max speed)")." {initial}")
->value($options['initial'][0])
->rules("required|numrange[-1,1]");
${"group_motion".$groupname}->input("initialY".$groupname)
->label(t("initial vertical speed (between +/-1.0, as fraction of max speed)")." {initial}")
->value($options['initial'][1])
->rules("required|numrange[-1,1]");
${"group_motion".$groupname}->input("maxInputZone".$groupname)
->label(t("mouseover region beyond cloud (as fraction of cloud - 0.0 is tight around cloud)")." {maxInputZone}")
->value($options['maxInputZone'])
->rules("required|numrange[0]");
// group select
${"group_select".$groupname} = $form->group("select".$groupname)->label(t("Tag selection").$sep.$grouptext);
${"group_select".$groupname}->dropdown("outlineMethod".$groupname)
->label(t("change of display for selected tag")." {outlineMethod}")
->options(array("colour"=>t("change text color"),"outline"=>t("add outline around text"),"block"=>t("add block behind text")))
->selected($options['outlineMethod']);
${"group".$groupname}->input("outlineOffset".$groupname)
->label(t($grouptext).$sep.t("outline offset (mouseover region size around text, in pixels)"))
${"group_select".$groupname}->input("outlineOffset".$groupname)
->label(t("mouseover region around tag text (in pixels - 0 is tight around text)")." {outlineOffset}")
->value($options['outlineOffset'])
->error_message("not_valid", t("Outline offset must be a 1-2 digit number"))
->rules("required|valid_numeric|length[1,2]");
${"group".$groupname}->input("outlineColour".$groupname)
->label(t($grouptext).$sep.t("outline color (mouseover color, as #hhhhhh)"))
->rules("required|numrange[0]");
${"group_select".$groupname}->input("outlineColour".$groupname)
->label(t("color used for change of display (as #hhhhhh)")." {outlineColour}")
->value($options['outlineColour'])
->error_message("not_valid", t("Outline color must be specified as #hhhhhh"))
->rules("required|length[7]");
${"group".$groupname}->input("textColour".$groupname)
->label(t($grouptext).$sep.t("text color (as #hhhhhh, or empty to use theme color)"))
->value($options['textColour'])
->error_message("not_valid", t("Text color must be specified as empty or #hhhhhh"))
->rules("length[0,7]");
${"group".$groupname}->input("textFont".$groupname)
->label(t($grouptext).$sep.t("text font family (empty to use theme font)"))
->value($options['textFont'])
->error_message("not_valid", t("Text font must be empty or a 0-40 character string"))
->rules("length[0,40]");
${"group".$groupname}->input("textHeight".$groupname)
->label(t($grouptext).$sep.t("text height (in pixels)"))
->value($options['textHeight'])
->error_message("not_valid", t("Text height must be a 1-2 digit number"))
->rules("required|valid_numeric|length[1,2]");
${"group".$groupname}->checkbox("frontSelect".$groupname)
->label(t($grouptext).$sep.t("only allow tags in front to be selected"))
->rules('required|color');
${"group_select".$groupname}->checkbox("frontSelect".$groupname)
->label(t("only allow tags in front to be selected")." {frontSelect}")
->checked($options['frontSelect']);
}
// group appearance
${"group_appearance".$groupname} = $form->group("appearance".$groupname)->label(t("Appearance").$sep.$grouptext);
${"group_appearance".$groupname}->input("textHeight".$groupname)
->label(t("text height (in pixels)")." {textHeight}")
->value($options['textHeight'])
->rules("required|numrange[0]");
${"group_appearance".$groupname}->input("textColour".$groupname)
->label(t("text color (as #hhhhhh, or empty to use theme color)")." {textColour}")
->value($options['textColour'])
->rules('color');
${"group_appearance".$groupname}->input("textFont".$groupname)
->label(t("text font family (empty to use theme font family)")." {textFont}")
->value($options['textFont'])
->rules("length[0,60]");
${"group_appearance".$groupname}->input("depth".$groupname)
->label(t("depth/perspective of cloud (0.0-1.0 - 0.0 is none and >0.9 gets strange)")." {depth}")
->value($options['depth'])
->rules("required|numrange[0,1]");
}
$form->submit("")->value(t("Save"));
return $form;

View File

@ -26,6 +26,8 @@ class Tag_Cloud_Controller extends Controller {
// Get settings
$options = module::get_var("tag_cloud_html5", "options_wholecloud", null);
$maxtags = module::get_var("tag_cloud_html5", "maxtags_wholecloud", null);
$width = module::get_var("tag_cloud_html5", "width_wholecloud", null);
$height = module::get_var("tag_cloud_html5", "height_wholecloud", null);
$options = json_decode($options, true);
$options['hideTags'] = !module::get_var("tag_cloud_html5", "show_wholecloud_list", true);
@ -37,8 +39,76 @@ class Tag_Cloud_Controller extends Controller {
$template->content->title = t("Tag cloud");
$template->content->cloud = tag::cloud($maxtags);
$template->content->options = $options;
$template->content->width = $width;
$template->content->height = $height;
// Display the page.
print $template;
}
}
public function embed() {
/**
* This is used to embed the tag cloud in other things. New in version 7.
*
* It expects the url to be in the form:
* tag_cloud/embed/optionsbase/option1/value1/option2/value2/.../optionN/valueN
* Where:
* optionsbase = "sidebar" or "wholecloud" (takes settings from this config)
* optionX = option name (either "maxtags" or any of the TagCanvas parameters - no name verification performed!)
* valueX = value of option (no value verification performed here!)
* Here's how the tag cloud is built:
* 1. Load "maxtags" and "options" variables for optionbase (as defined in admin menu or admin/advanced variables)
* Note: width and height are ignored, and the add tag form, wholecloud link, and inline tags are not shown.
* 2. Use option/value pairs to override and/or append those loaded above.
* 3. Build tag cloud, using 100% of the size from its parent.
* Correspondingly, the optionsbase is required, but the options and values are not.
*/
// Require view permission for the root album for security purposes.
$album = ORM::factory("item", 1);
access::required("view", $album);
// get the function arguments
$args = func_get_args();
// get/check the number of arguments - must be odd
$countargs = count($args);
if ($countargs % 2 == 0) {
return;
}
// get/check the first argument - must be sidebar or wholecloud
$optionsbase = $args[0];
if (!(in_array($optionsbase, array("sidebar", "wholecloud")))) {
return;
}
// get and override/append options/values
$maxtags = module::get_var("tag_cloud_html5", "maxtags_".$optionsbase, null);
$options = module::get_var("tag_cloud_html5", "options_".$optionsbase, null);
$options = json_decode($options, true);
for ($i = 0; $i < ($countargs-1)/2; $i++) {
$option = $args[2*$i+1];
$value = $args[2*$i+2];
if ($option == "maxtags") {
// assign to maxtags
$maxtags = $value;
} elseif (substr($option,-6) == 'Colour') {
// assign to options with a hash in front
$options[$option] = '#'.$value;
} else {
// assign to options
$options[$option] = $value;
}
}
$options = json_encode($options);
// Set up and display the actual page.
$template = new View("tag_cloud_html5_embed.html");
$template->cloud = tag::cloud($maxtags);
$template->options = $options;
// Display the page.
print $template;
}
}

View File

@ -1,30 +1,17 @@
#g-content fieldset li {
#g-content fieldset {
display: block;
}
#g-content fieldset.g-tag-cloud-html5-admin-form-top {
width: 80%;
clear: both;
height: 24px;
}
#g-content fieldset input {
display: inline;
#g-content fieldset.g-tag-cloud-html5-admin-form-left {
width: 45%;
float: left;
height: 18px;
margin-right: 0.8em;
width: 250px;
clear: left;
margin-right: 2%
}
#g-content fieldset select {
display: inline;
float: left;
height: 24px;
margin-right: 0.8em;
width: 256px;
}
#g-content fieldset label {
display: inline;
line-height: 24px;
}
#g-content fieldset label input.checkbox {
float: left;
height: 24px;
margin-right: 0.8em;
width: 20px;
margin-left: 236px;
#g-content fieldset.g-tag-cloud-html5-admin-form-right {
width: 45%;
clear: right;
}

View File

@ -32,6 +32,7 @@ class tag_cloud_html5_block {
$maxtags = module::get_var("tag_cloud_html5", "maxtags_sidebar", null);
$showlink = module::get_var("tag_cloud_html5", "show_wholecloud_link", null);
$showaddtag = module::get_var("tag_cloud_html5", "show_add_tag_form", null);
$width = module::get_var("tag_cloud_html5", "width_sidebar", null);
$height = module::get_var("tag_cloud_html5", "height_sidebar", null);
// make the block
@ -41,6 +42,7 @@ class tag_cloud_html5_block {
$block->content = new View("tag_cloud_html5_block.html");
$block->content->cloud = tag::cloud($maxtags);
$block->content->options = $options;
$block->content->width = $width;
$block->content->height = $height;
// add the 'View whole cloud' link if needed

View File

@ -22,12 +22,19 @@ class tag_cloud_html5_installer {
// clear and reset default values. this is also called in the admin menu for
// 'reset all to default values' and if the upgrader sees variables missing.
module::clear_all_vars("tag_cloud_html5");
module::set_var("tag_cloud_html5", "show_wholecloud_link", true);
module::set_var("tag_cloud_html5", "show_add_tag_form", true);
module::set_var("tag_cloud_html5", "show_wholecloud_list", true);
module::set_var("tag_cloud_html5", "height_sidebar", 0.8);
module::set_var("tag_cloud_html5", "maxtags_sidebar", 30);
module::set_var("tag_cloud_html5", "width_sidebar", 1.00);
module::set_var("tag_cloud_html5", "height_sidebar", 0.8);
module::set_var("tag_cloud_html5", "maxtags_wholecloud", 500);
module::set_var("tag_cloud_html5", "width_wholecloud", 0.95);
module::set_var("tag_cloud_html5", "height_wholecloud", 0.75);
module::set_var("tag_cloud_html5", "options_sidebar", json_encode(array(
"maxSpeed" => 0.05,
"deadZone" => 0.25,
@ -42,8 +49,17 @@ class tag_cloud_html5_installer {
"textFont" => "",
"textHeight" => 12,
"frontSelect" => true,
"wheelZoom" => false
"wheelZoom" => false,
"shape" => "sphere",
"lock" => "",
"stretchX" => 1.0,
"stretchY" => 1.0,
"decel" => 0.92,
"physModel" => true,
"maxInputZone" => 0.25,
"minSpeed" => 0.002
)));
module::set_var("tag_cloud_html5", "options_wholecloud", json_encode(array(
"maxSpeed" => 0.05,
"deadZone" => 0.25,
@ -58,17 +74,100 @@ class tag_cloud_html5_installer {
"textFont" => "",
"textHeight" => 13,
"frontSelect" => true,
"wheelZoom" => false
"wheelZoom" => false,
"shape" => "sphere",
"lock" => "",
"stretchX" => 1.0,
"stretchY" => 1.0,
"decel" => 0.92,
"physModel" => true,
"maxInputZone" => 0.15,
"minSpeed" => 0.002
)));
module::set_version("tag_cloud_html5", 4);
module::set_version("tag_cloud_html5", 7);
}
static function upgrade() {
if (is_null(module::get_var("tag_cloud_html5", "options_sidebar")) ||
is_null(module::get_var("tag_cloud_html5", "options_wholecloud")) ||
(module::get_version("tag_cloud_html5") < 4) ) {
(module::get_version("tag_cloud_html5") < 1) ) {
module::install("tag_cloud_html5");
}
if (module::get_version("tag_cloud_html5") < 2) {
// added wheelZoom, which is not accessible from admin menu
$options = json_decode(module::get_var("tag_cloud_html5", "options_sidebar"),true);
$options["wheelZoom"] = false;
module::set_var("tag_cloud_html5", "options_sidebar", json_encode($options));
$options = json_decode(module::get_var("tag_cloud_html5", "options_wholecloud"),true);
$options["wheelZoom"] = false;
module::set_var("tag_cloud_html5", "options_wholecloud", json_encode($options));
}
if (module::get_version("tag_cloud_html5") < 3) {
// added deadZone, initial, and initialDecel
module::set_var("tag_cloud_html5", "show_add_tag_form", true);
$options = json_decode(module::get_var("tag_cloud_html5", "options_sidebar"),true);
$options["deadZone"] = 0.25;
$options["initial"] = array(0.8,-0.3);
$options["initialDecel"] = true;
module::set_var("tag_cloud_html5", "options_sidebar", json_encode($options));
$options = json_decode(module::get_var("tag_cloud_html5", "options_wholecloud"),true);
$options["deadZone"] = 0.25;
$options["initial"] = array(0.8,-0.3);
$options["initialDecel"] = true;
module::set_var("tag_cloud_html5", "options_wholecloud", json_encode($options));
}
if (module::get_version("tag_cloud_html5") < 4) {
// added height_sidebar, then scaled back zoom and textHeight for consistency
module::set_var("tag_cloud_html5", "height_sidebar", 0.8);
$options = json_decode(module::get_var("tag_cloud_html5", "options_sidebar"),true);
$options["zoom"] = $options["zoom"] / 0.8;
$options["textHeight"] = $options["textHeight"] * 0.8;
module::set_var("tag_cloud_html5", "options_sidebar", json_encode($options));
}
if (module::get_version("tag_cloud_html5") < 5) {
// added lots of options that are on admin menu
// added physModel, lock, and initialDecel as options not on admin menu
// (previously initialDecel was on menu, so reset here)
module::set_var("tag_cloud_html5", "width_sidebar", 1.00);
module::set_var("tag_cloud_html5", "width_wholecloud", 0.95);
module::set_var("tag_cloud_html5", "height_wholecloud", 0.75);
$options = json_decode(module::get_var("tag_cloud_html5", "options_sidebar"),true);
$options["deadZone"] = $options["deadZone"];
$options["shape"] = "sphere";
$options["lock"] = "";
$options["stretchX"] = 1.0;
$options["stretchY"] = 1.0;
$options["decel"] = 0.92;
$options["physModel"] = true;
$options["maxInputZone"] = 0.25;
$options["minSpeed"] = 0.002;
$options["initialDecel"] = true;
module::set_var("tag_cloud_html5", "options_sidebar", json_encode($options));
$options = json_decode(module::get_var("tag_cloud_html5", "options_wholecloud"),true);
$options["deadZone"] = $options["deadZone"];
$options["shape"] = "sphere";
$options["lock"] = "";
$options["stretchX"] = 1.0;
$options["stretchY"] = 1.0;
$options["decel"] = 0.92;
$options["physModel"] = true;
$options["maxInputZone"] = 0.15;
$options["minSpeed"] = 0.002;
$options["initialDecel"] = true;
module::set_var("tag_cloud_html5", "options_wholecloud", json_encode($options));
}
// note: there are no variable changes for v6 and v7 upgrades
module::set_version("tag_cloud_html5", 7);
}
static function uninstall() {

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,58 @@
<?php defined("SYSPATH") or die("No direct script access.");
/**
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2011 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
class Form_Input extends Form_Input_Core {
/**
* Custom validation rule: numrange
* 0 args : returns error if not numeric
* 1 arg : returns error if not numeric OR if below min
* 2 args : returns error if not numeric OR if below min OR if above max
*/
protected function rule_numrange($min = null, $max = null) {
if (is_numeric($this->value)) {
if (!is_null($min) && ($this->value < $min)) {
// below min
$this->errors['numrange'] = true;
$this->error_messages['numrange'] = t('Value is below minimum of').' '.$min;
} elseif (!is_null($max) && ($this->value > $max)) {
// above max
$this->errors['numrange'] = true;
$this->error_messages['numrange'] = t('Value is above maximum of').' '.$max;;
}
} else {
// not numeric
$this->errors['numrange'] = true;
$this->error_messages['numrange'] = t('Value is not numeric');
}
}
/**
* Custom validation rule: color
* returns no error if string is formatted as #hhhhhh OR if string is empty
* to exclude the empty case, add "required" as another rule
*/
protected function rule_color() {
if (preg_match("/^#[0-9A-Fa-f]{6}$|^$/", $this->value) == 0) {
$this->errors['color'] = true;
$this->error_messages['color'] = t('Color is not in #hhhhhh format');
}
}
}

View File

@ -1,6 +1,6 @@
name = "Tag Cloud HTML5"
description = "HTML5-compliant tag cloud. Functions as non-Flash replacements for both 'tag_cloud' and 'tag_cloud_page' modules, with some extra features added."
version = 4
version = 7
author_name = "Shad Laws"
author_url = ""
info_url = "http://codex.gallery2.org/Gallery3:Modules:tag_cloud_html5"

View File

@ -7,10 +7,23 @@
<?= t("Tag cloud HTML5 settings") ?>
</h2>
<p>
<?= t("This module uses TagCanvas, a non-flash, HTML5-compliant jQuery plugin. It also uses <a href=\"http://excanvas.sourceforge.net/\">excanvas</a> to maintain compatibility with pre-9.0 Internet Explorer.") ?>
<b><?= t("Underlying JS libraries:") ?></b><br/>
<?= "1. <a href='http://www.goat1000.com/tagcanvas.php'>TagCanvas</a>: ".t("a non-flash, HTML5-compliant jQuery plugin.") ?><br/>
<?= "2. <a href='http://excanvas.sourceforge.net'>excanvas</a>: ".t("maintains canvas compatibility with pre-9.0 Internet Explorer, and does not load if not needed.") ?><br/>
<?= t("The module author, Shad Laws, has modified TagCanvas to add a physics-based model for motion and some extra parameters.") ?>
<?= t("Although this module loads a minified version of the JS library, the full-sized one is included in the JS directory for reference.") ?>
</p>
<p>
<?= t("More details on the options below are given at TagCanvas's homepage <a href='http://www.goat1000.com/tagcanvas.php'>here</a>.") ?>
<b><?= t("How sizing works in TagCanvas:") ?></b><br/>
<?= "1. ".t("make a square the size of the minimum of width and height (as determined by width and height parameters)") ?><br/>
<?= "2. ".t("scale result by the stretch factors, possibility resulting in a non-square shape") ?><br/>
<?= "3. ".t("set text into result at defined text height") ?><br/>
<?= "4. ".t("scale result by the zoom, scaling both cloud and text height (e.g. text height 12 and zoom 1.25 results in 15pt font)") ?>
</p>
<p>
<b><?= t("Legend:") ?></b><br/>
<?= t("&nbsp;&nbsp;&nbsp;option name (more info on option) {related TagCanvas parameters}") ?><br/>
<?= t("More details on TagCanvas parameters given at TagCanvas's homepage or in the above-mentioned JS library.") ?>
</p>
<?= $form ?>
</div>

View File

@ -1,12 +1,14 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<!--[if lt IE 9]><?= $theme->script("excanvas.compiled.js"); ?><![endif]-->
<!--[if lt IE 9]>
<?= html::script(gallery::find_file("js", "excanvas.compiled.js", false)) ?>
<![endif]-->
<script type="text/javascript">
$(document).ready(function() {
// set g-tag-cloud-html5-canvas size
$("#g-tag-cloud-html5-canvas").attr({
'width': $("#g-tag-cloud-html5").width(),
'height': $("#g-tag-cloud-html5").width()*<?= $height ?>
'width' : Math.floor($("#g-tag-cloud-html5").width()*<?= $width ?>),
'height': Math.floor($("#g-tag-cloud-html5").width()*<?= $height ?>)
});
// start g-tag-cloud-html5-canvas

View File

@ -0,0 +1,59 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<!--[if lt IE 9]>
<?= html::script(gallery::find_file("js", "excanvas.compiled.js", false)) ?>
<![endif]-->
<script type="text/javascript">
// define flag variables if not already defined elsewhere
if (typeof(jQueryScriptFlag) == 'undefined') {
var jQueryScriptFlag = false;
};
if (typeof(jQueryTagCanvasScriptFlag) == 'undefined') {
var jQueryTagCanvasScriptFlag = false;
};
function initScripts() {
// load scripts if not already loaded
if (typeof(jQuery) == 'undefined') {
if (!jQueryScriptFlag) {
// load both scripts
jQueryScriptFlag = true;
jQueryTagCanvasScriptFlag = true;
document.write("<scr" + "ipt type=\"text/javascript\" src=\"<?= url::base(false).gallery::find_file("js", "jquery.js", false) ?>\"></scr" + "ipt>");
document.write("<scr" + "ipt type=\"text/javascript\" src=\"<?= url::base(false).gallery::find_file("js", "jquery.tagcanvas.mod.min.js", false) ?>\"></scr" + "ipt>");
};
setTimeout("initScripts()", 50);
} else if (typeof(jQuery().tagcanvas) == 'undefined') {
if (!jQueryTagCanvasScriptFlag) {
// load one script
jQueryTagCanvasScriptFlag = true;
document.write("<scr" + "ipt type=\"text/javascript\" src=\"<?= url::base(false).gallery::find_file("js", "jquery.tagcanvas.mod.min.js", false) ?>\"></scr" + "ipt>");
};
setTimeout("initScripts()", 50);
} else {
// libraries loaded - run actual code
function redraw() {
// set g-tag-cloud-html5-embed-canvas size
$("#g-tag-cloud-html5-embed-canvas").attr({
'width' : $("#g-tag-cloud-html5-embed").parent().width(),
'height': $("#g-tag-cloud-html5-embed").parent().height()
});
// start g-tag-cloud-html5-embed-canvas
if(!$('#g-tag-cloud-html5-embed-canvas').tagcanvas(<?= $options ?>,'g-tag-cloud-html5-embed-tags')) {
// something went wrong, hide the canvas container g-tag-cloud-html5-embed
$('#g-tag-cloud-html5-embed').hide();
};
};
// resize and redraw the canvas
$(document).ready(redraw);
$(window).resize(redraw);
};
};
initScripts();
</script>
<div id="g-tag-cloud-html5-embed">
<canvas id="g-tag-cloud-html5-embed-canvas">
<? echo t('Tag cloud loading...'); ?>
</canvas>
</div>
<div id="g-tag-cloud-html5-embed-tags" style="display: none">
<?= $cloud ?>
</div>

View File

@ -1,12 +1,14 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<!--[if lt IE 9]><?= $theme->script("excanvas.compiled.js"); ?><![endif]-->
<!--[if lt IE 9]>
<?= html::script(gallery::find_file("js", "excanvas.compiled.js", false)) ?>
<![endif]-->
<script type="text/javascript">
function redraw() {
// set g-tag-cloud-html5-page-canvas size
$("#g-tag-cloud-html5-page-canvas").attr({
'height': Math.min( $(window).height()*0.75, $("#g-tag-cloud-html5-page").width() ),
'width': Math.min( $(window).height()*0.95, $("#g-tag-cloud-html5-page").width() )
'width' : Math.floor(Math.min( $(window).height()*<?= $width ?>, $("#g-tag-cloud-html5-page").width() )),
'height': Math.floor( $(window).height()*<?= $height ?> )
});
// start g-tag-cloud-html5-page-canvas
@ -35,4 +37,4 @@
<div id="g-tag-cloud-html5-page-tags">
<?= $cloud ?>
</div>
<?= $theme->dynamic_bottom() ?>
<?= $theme->dynamic_bottom() ?>