summaryrefslogtreecommitdiff
path: root/modules/gallery
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gallery')
-rw-r--r--modules/gallery/controllers/admin_advanced_settings.php2
-rw-r--r--modules/gallery/controllers/albums.php6
-rw-r--r--modules/gallery/controllers/movies.php2
-rw-r--r--modules/gallery/controllers/photos.php2
-rw-r--r--modules/gallery/controllers/quick.php10
-rw-r--r--modules/gallery/helpers/MY_url.php24
-rw-r--r--modules/gallery/helpers/gallery_rss.php4
-rw-r--r--modules/gallery/helpers/gallery_task.php4
-rw-r--r--modules/gallery/helpers/p.php39
-rw-r--r--modules/gallery/libraries/I18n.php20
-rw-r--r--modules/gallery/libraries/MY_ORM.php4
-rw-r--r--modules/gallery/libraries/SafeString.php177
-rw-r--r--modules/gallery/tests/SafeString_Test.php121
-rw-r--r--modules/gallery/tests/Xss_Security_Test.php351
-rw-r--r--modules/gallery/views/admin_advanced_settings.html.php8
-rw-r--r--modules/gallery/views/admin_block_log_entries.html.php2
-rw-r--r--modules/gallery/views/admin_block_photo_stream.html.php4
-rw-r--r--modules/gallery/views/admin_maintenance.html.php2
-rw-r--r--modules/gallery/views/admin_maintenance_show_log.html.php2
-rw-r--r--modules/gallery/views/after_install.html.php2
-rw-r--r--modules/gallery/views/l10n_client.html.php4
-rw-r--r--modules/gallery/views/move_tree.html.php8
-rw-r--r--modules/gallery/views/permissions_browse.html.php10
-rw-r--r--modules/gallery/views/permissions_form.html.php2
-rw-r--r--modules/gallery/views/simple_uploader.html.php67
25 files changed, 693 insertions, 184 deletions
diff --git a/modules/gallery/controllers/admin_advanced_settings.php b/modules/gallery/controllers/admin_advanced_settings.php
index 64007fdb..d727b654 100644
--- a/modules/gallery/controllers/admin_advanced_settings.php
+++ b/modules/gallery/controllers/admin_advanced_settings.php
@@ -46,7 +46,7 @@ class Admin_Advanced_Settings_Controller extends Admin_Controller {
module::set_var($module_name, $var_name, Input::instance()->post("value"));
message::success(
t("Saved value for %var (%module_name)",
- array("var" => p::clean($var_name), "module_name" => $module_name)));
+ array("var" => SafeString::of($var_name), "module_name" => $module_name)));
print json_encode(array("result" => "success"));
}
diff --git a/modules/gallery/controllers/albums.php b/modules/gallery/controllers/albums.php
index cdfa823d..ec3eb426 100644
--- a/modules/gallery/controllers/albums.php
+++ b/modules/gallery/controllers/albums.php
@@ -112,7 +112,7 @@ class Albums_Controller extends Items_Controller {
log::success("content", "Created an album",
html::anchor("albums/$new_album->id", "view album"));
message::success(
- t("Created album %album_title", array("album_title" => p::clean($new_album->title))));
+ t("Created album %album_title", array("album_title" => $new_album->title)));
print json_encode(
array("result" => "success",
@@ -145,7 +145,7 @@ class Albums_Controller extends Items_Controller {
log::success("content", "Added a photo", html::anchor("photos/$photo->id", "view photo"));
message::success(
- t("Added photo %photo_title", array("photo_title" => p::clean($photo->title))));
+ t("Added photo %photo_title", array("photo_title" => $photo->title)));
print json_encode(
array("result" => "success",
@@ -194,7 +194,7 @@ class Albums_Controller extends Items_Controller {
log::success("content", "Updated album", "<a href=\"albums/$album->id\">view</a>");
message::success(
- t("Saved album %album_title", array("album_title" => p::clean($album->title))));
+ t("Saved album %album_title", array("album_title" => $album->title)));
print json_encode(
array("result" => "success",
diff --git a/modules/gallery/controllers/movies.php b/modules/gallery/controllers/movies.php
index c8227d74..09b16759 100644
--- a/modules/gallery/controllers/movies.php
+++ b/modules/gallery/controllers/movies.php
@@ -93,7 +93,7 @@ class Movies_Controller extends Items_Controller {
log::success("content", "Updated photo", "<a href=\"photos/$photo->id\">view</a>");
message::success(
- t("Saved photo %photo_title", array("photo_title" => p::clean($photo->title))));
+ t("Saved photo %photo_title", array("photo_title" => $photo->title)));
print json_encode(
array("result" => "success",
diff --git a/modules/gallery/controllers/photos.php b/modules/gallery/controllers/photos.php
index 8ee24da8..3447b4c6 100644
--- a/modules/gallery/controllers/photos.php
+++ b/modules/gallery/controllers/photos.php
@@ -86,7 +86,7 @@ class Photos_Controller extends Items_Controller {
log::success("content", "Updated photo", "<a href=\"photos/$photo->id\">view</a>");
message::success(
- t("Saved photo %photo_title", array("photo_title" => p::clean($photo->title))));
+ t("Saved photo %photo_title", array("photo_title" => $photo->title)));
print json_encode(
array("result" => "success",
diff --git a/modules/gallery/controllers/quick.php b/modules/gallery/controllers/quick.php
index 82176e02..8fddb563 100644
--- a/modules/gallery/controllers/quick.php
+++ b/modules/gallery/controllers/quick.php
@@ -75,7 +75,7 @@ class Quick_Controller extends Controller {
access::required("view", $item->parent());
access::required("edit", $item->parent());
- $msg = t("Made <b>%title</b> this album's cover", array("title" => p::purify($item->title)));
+ $msg = t("Made <b>%title</b> this album's cover", array("title" => SafeString::purify($item->title)));
item::make_album_cover($item);
message::success($msg);
@@ -91,10 +91,10 @@ class Quick_Controller extends Controller {
if ($item->is_album()) {
print t(
"Delete the album <b>%title</b>? All photos and movies in the album will also be deleted.",
- array("title" => p::purify($item->title)));
+ array("title" => SafeString::purify($item->title)));
} else {
print t("Are you sure you want to delete <b>%title</b>?",
- array("title" => p::purify($item->title)));
+ array("title" => SafeString::purify($item->title)));
}
$form = item::get_delete_form($item);
@@ -108,9 +108,9 @@ class Quick_Controller extends Controller {
access::required("edit", $item);
if ($item->is_album()) {
- $msg = t("Deleted album <b>%title</b>", array("title" => p::purify($item->title)));
+ $msg = t("Deleted album <b>%title</b>", array("title" => SafeString::purify($item->title)));
} else {
- $msg = t("Deleted photo <b>%title</b>", array("title" => p::purify($item->title)));
+ $msg = t("Deleted photo <b>%title</b>", array("title" => SafeString::purify($item->title)));
}
$parent = $item->parent();
diff --git a/modules/gallery/helpers/MY_url.php b/modules/gallery/helpers/MY_url.php
index c4967c52..6092a9d8 100644
--- a/modules/gallery/helpers/MY_url.php
+++ b/modules/gallery/helpers/MY_url.php
@@ -30,7 +30,8 @@ class url extends url_Core {
if ($parts[0] == "albums" || $parts[0] == "photos") {
$uri = model_cache::get("item", $parts[1])->relative_path();
}
- return parent::site($uri . $query, $protocol);
+ $url = parent::site($uri . $query, $protocol);
+ return SafeString::of_safe_html($url);
}
static function parse_url() {
@@ -99,4 +100,25 @@ class url extends url_Core {
static function abs_current($qs=false) {
return self::abs_site(url::current($qs));
}
+
+ public static function base($index=false, $protocol=false) {
+ $url = parent::base($index, $protocol);
+ return SafeString::of_safe_html($url);
+ }
+
+ public static function current($qs=false) {
+ $url = parent::current($qs);
+ return SafeString::of_safe_html($url);
+ }
+
+ public static function file($file, $index=false) {
+ $url = parent::file($file, $index);
+ return SafeString::of_safe_html($url);
+ }
+
+ public static function merge(array $arguments) {
+ $url = parent::merge($arguments);
+ return SafeString::of_safe_html($url);
+ }
+
}
diff --git a/modules/gallery/helpers/gallery_rss.php b/modules/gallery/helpers/gallery_rss.php
index 8e887368..affb3101 100644
--- a/modules/gallery/helpers/gallery_rss.php
+++ b/modules/gallery/helpers/gallery_rss.php
@@ -53,9 +53,9 @@ class gallery_rss_Core {
->descendants($limit, $offset, array("type" => "photo"));
$feed->max_pages = ceil(
$item->viewable()->descendants_count(array("type" => "photo")) / $limit);
- $feed->title = p::purify($item->title);
+ $feed->title = SafeString::purify($item->title);
$feed->link = url::abs_site("albums/{$item->id}");
- $feed->description = nl2br(p::purify($item->description));
+ $feed->description = nl2br(SafeString::purify($item->description));
return $feed;
}
diff --git a/modules/gallery/helpers/gallery_task.php b/modules/gallery/helpers/gallery_task.php
index 9edc3acd..8c0e8aa8 100644
--- a/modules/gallery/helpers/gallery_task.php
+++ b/modules/gallery/helpers/gallery_task.php
@@ -64,10 +64,10 @@ class gallery_task_Core {
if (!$success) {
$ignored[$item->id] = 1;
$errors[] = t("Unable to rebuild images for '%title'",
- array("title" => p::purify($item->title)));
+ array("title" => SafeString::purify($item->title)));
} else {
$errors[] = t("Successfully rebuilt images for '%title'",
- array("title" => p::purify($item->title)));
+ array("title" => SafeString::purify($item->title)));
}
}
diff --git a/modules/gallery/helpers/p.php b/modules/gallery/helpers/p.php
deleted file mode 100644
index 862c769b..00000000
--- a/modules/gallery/helpers/p.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php defined("SYSPATH") or die("No direct script access.");
-/**
- * Gallery - a web based photo album viewer and editor
- * Copyright (C) 2000-2009 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 p_Core {
- private static $_purifier = null;
- static function clean($dirty_html) {
- return html::specialchars($dirty_html);
- }
-
- static function purify($dirty_html) {
- if (empty(self::$_purifier)) {
- require_once(dirname(__file__) . "/../lib/HTMLPurifier/HTMLPurifier.auto.php");
- $config = HTMLPurifier_Config::createDefault();
- foreach (Kohana::config('purifier') as $category => $key_value) {
- foreach ($key_value as $key => $value) {
- $config->set("$category.$key", $value);
- }
- }
- self::$_purifier = new HTMLPurifier($config);
- }
- return self::$_purifier->purify($dirty_html);
- }
-}
diff --git a/modules/gallery/libraries/I18n.php b/modules/gallery/libraries/I18n.php
index d0531b9a..c3336052 100644
--- a/modules/gallery/libraries/I18n.php
+++ b/modules/gallery/libraries/I18n.php
@@ -89,6 +89,12 @@ class I18n_Core {
/**
* Translates a localizable message.
+ *
+ * Security:
+ * The returned string is safe for use in HTML (it contains a safe subset of HTML and
+ * interpolation parameters are converted to HTML entities).
+ * For use in JavaScript, please call ->for_js() on it.
+ *
* @param $message String|array The message to be translated. E.g. "Hello world"
* or array("one" => "One album", "other" => "%count albums")
* @param $options array (optional) Options array for key value pairs which are used
@@ -115,7 +121,7 @@ class I18n_Core {
$entry = $this->interpolate($locale, $entry, $values);
- return $entry;
+ return SafeString::of_safe_html($entry);
}
private function lookup($locale, $message) {
@@ -184,17 +190,19 @@ class I18n_Core {
return is_array($message);
}
- private function interpolate($locale, $string, $values) {
+ private function interpolate($locale, $string, $key_values) {
// TODO: Handle locale specific number formatting.
// Replace x_y before replacing x.
- krsort($values, SORT_STRING);
+ krsort($key_values, SORT_STRING);
$keys = array();
- foreach (array_keys($values) as $key) {
+ $values = array();
+ foreach ($key_values as $key => $value) {
$keys[] = "%$key";
+ $values[] = new SafeString($value);
}
- return str_replace($keys, array_values($values), $string);
+ return str_replace($keys, $values, $string);
}
private function pluralize($locale, $entry, $count) {
@@ -419,4 +427,4 @@ class I18n_Core {
return $count == 1 ? 'one' : 'other';
}
}
-} \ No newline at end of file
+}
diff --git a/modules/gallery/libraries/MY_ORM.php b/modules/gallery/libraries/MY_ORM.php
index de8adc1d..2c9ad1d7 100644
--- a/modules/gallery/libraries/MY_ORM.php
+++ b/modules/gallery/libraries/MY_ORM.php
@@ -43,6 +43,10 @@ class ORM extends ORM_Core {
$this->original = clone $this;
}
+ if ($value instanceof SafeString) {
+ $value = $value->unescaped();
+ }
+
return parent::__set($column, $value);
}
diff --git a/modules/gallery/libraries/SafeString.php b/modules/gallery/libraries/SafeString.php
new file mode 100644
index 00000000..9614a213
--- /dev/null
+++ b/modules/gallery/libraries/SafeString.php
@@ -0,0 +1,177 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2009 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.
+ */
+
+/**
+ * Safe string representation (regarding security - cross site scripting).
+ */
+class SafeString_Core {
+ private $_raw_string;
+ protected $_is_safe_html = false;
+ protected $_is_purified_html = false;
+
+ private static $_purifier = null;
+
+ /** Constructor */
+ function __construct($string) {
+ if ($string instanceof SafeString) {
+ $this->_is_safe_html = $string->_is_safe_html;
+ $this->_is_purified_html = $string->_is_purified_html;
+ $string = $string->unescaped();
+ }
+ $this->_raw_string = (string) $string;
+ }
+
+ /**
+ * Factory method returning a new SafeString instance for the given string.
+ */
+ static function of($string) {
+ return new SafeString($string);
+ }
+
+ /**
+ * Factory method returning a new SafeString instance after HTML purifying
+ * the given string.
+ */
+ static function purify($string) {
+ if ($string instanceof SafeString) {
+ $string = $string->unescaped();
+ }
+ $safe_string = self::of_safe_html(self::_purify_for_html($string));
+ $safe_string->_is_purified_html = true;
+ return $safe_string;
+ }
+
+ /**
+ * Factory method returning a new SafeString instance which won't HTML escape.
+ */
+ static function of_safe_html($string) {
+ $safe_string = new SafeString($string);
+ $safe_string->_is_safe_html = true;
+ return $safe_string;
+ }
+
+ /**
+ * Safe for use in HTML.
+ * @see #for_html()
+ */
+ function __toString() {
+ if ($this->_is_safe_html) {
+ return $this->_raw_string;
+ } else {
+ return self::_escape_for_html($this->_raw_string);
+ }
+ }
+
+ /**
+ * Safe for use in HTML.
+ *
+ * Example:<pre>
+ * <div><?= $php_var ?>
+ * </pre>
+ * @return the string escaped for use in HTML.
+ */
+ function for_html() {
+ return $this;
+ }
+
+ /**
+ * Safe for use in JavaScript.
+ *
+ * Example:<pre>
+ * <script type="text/javascript>"
+ * var some_js_var = "<?= $php_var->for_js() ?>";
+ * </script>
+ * </pre>
+ * @return the string escaped for use in JavaScript.
+ */
+ function for_js() {
+ return self::_escape_for_js($this->_raw_string);
+ }
+
+ /**
+ * Safe for use in HTML element attributes.
+ *
+ * Assumes that the HTML element attribute is already
+ * delimited by single or double quotes
+ *
+ * Example:<pre>
+ * <a title="<?= $php_var->for_html_attr() ?>">;
+ * </script>
+ * </pre>
+ * @return the string escaped for use in HTML attributes.
+ */
+ function for_html_attr() {
+ $string = (string) $this->for_html();
+ return strtr($string,
+ array("'"=>"&#039;",
+ '"'=>'&quot;'));
+ }
+
+ /**
+ * Safe for use HTML (purified HTML)
+ *
+ * Example:<pre>
+ * <div><?= $php_var->purified_html() ?>
+ * </pre>
+ * @return the string escaped for use in HTML.
+ */
+ function purified_html() {
+ if ($this->_is_purified_html) {
+ return $this;
+ } else {
+ return self::purify($this);
+ }
+ }
+
+ /**
+ * Returns the raw, unsafe string. Do not use lightly.
+ */
+ function unescaped() {
+ return $this->_raw_string;
+ }
+
+ // Escapes special HTML chars ("<", ">", "&", etc.) to HTML entities.
+ private static function _escape_for_html($dirty_html) {
+ return html::specialchars($dirty_html);
+ }
+
+ // Escapes special chars (quotes, backslash, etc.) with a backslash sequence.
+ private static function _escape_for_js($string) {
+ // From Smarty plugins/modifier.escape.php
+ // Might want to be stricter here.
+ return strtr($string,
+ array('\\'=>'\\\\',"'"=>"\\'",'"'=>'\\"',"\r"=>'\\r',"\n"=>'\\n','</'=>'<\/'));
+ }
+
+ // Purifies the string, removing any potentially malicious or unsafe HTML / JavaScript.
+ private static function _purify_for_html($dirty_html) {
+ if (empty(self::$_purifier)) {
+ require_once(dirname(__file__) . "/../lib/HTMLPurifier/HTMLPurifier.auto.php");
+ $config = HTMLPurifier_Config::createDefault();
+ foreach (Kohana::config('purifier') as $category => $key_value) {
+ foreach ($key_value as $key => $value) {
+ $config->set("$category.$key", $value);
+ }
+ }
+ self::$_purifier = new HTMLPurifier($config);
+ }
+ return self::$_purifier->purify($dirty_html);
+ }
+}
diff --git a/modules/gallery/tests/SafeString_Test.php b/modules/gallery/tests/SafeString_Test.php
new file mode 100644
index 00000000..0fc7f6f3
--- /dev/null
+++ b/modules/gallery/tests/SafeString_Test.php
@@ -0,0 +1,121 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2009 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 SafeString_Test extends Unit_Test_Case {
+ public function toString_escapes_for_html_test() {
+ $safe_string = new SafeString("hello <p>world</p>");
+ $this->assert_equal("hello &lt;p&gt;world&lt;/p&gt;",
+ $safe_string);
+ }
+
+ public function toString_for_safe_string_test() {
+ $safe_string = SafeString::of_safe_html("hello <p>world</p>");
+ $this->assert_equal("hello <p>world</p>",
+ $safe_string);
+ }
+
+ public function for_html_test() {
+ $safe_string = new SafeString("hello <p>world</p>");
+ $this->assert_equal("hello &lt;p&gt;world&lt;/p&gt;",
+ $safe_string->for_html());
+ }
+
+ public function safestring_of_safestring_test() {
+ $safe_string = new SafeString("hello <p>world</p>");
+ $safe_string_2 = new SafeString($safe_string);
+ $this->assert_true($safe_string_2 instanceof SafeString);
+ $raw_string = $safe_string_2->unescaped();
+ $this->assert_false(is_object($raw_string));
+ $this->assert_equal("hello <p>world</p>", $raw_string);
+ $this->assert_equal("hello &lt;p&gt;world&lt;/p&gt;", $safe_string_2);
+ }
+
+ public function for_js_test() {
+ $safe_string = new SafeString('"<em>Foo</em>\'s bar"');
+ $js_string = $safe_string->for_js();
+ $this->assert_equal('\\"<em>Foo<\\/em>\\\'s bar\\"',
+ $js_string);
+ }
+
+ public function for_html_attr_test() {
+ $safe_string = new SafeString('"<em>Foo</em>\'s bar"');
+ $attr_string = $safe_string->for_html_attr();
+ $this->assert_equal('&quot;&lt;em&gt;Foo&lt;/em&gt;&#039;s bar&quot;',
+ $attr_string);
+ }
+
+ public function for_html_attr_with_safe_html_test() {
+ $safe_string = SafeString::of_safe_html('"<em>Foo</em>\'s bar"');
+ $attr_string = $safe_string->for_html_attr();
+ $this->assert_equal('&quot;<em>Foo</em>&#039;s bar&quot;',
+ $attr_string);
+ }
+
+ public function string_safestring_equality_test() {
+ $safe_string = new SafeString("hello <p>world</p>");
+ $this->assert_equal("hello <p>world</p>",
+ $safe_string->unescaped());
+ $escaped_string = "hello &lt;p&gt;world&lt;/p&gt;";
+ $this->assert_equal($escaped_string, $safe_string);
+
+ $this->assert_true($escaped_string == $safe_string);
+ $this->assert_false($escaped_string === $safe_string);
+ $this->assert_false("meow" == $safe_string);
+ }
+
+ public function of_test() {
+ $safe_string = SafeString::of("hello <p>world</p>");
+ $this->assert_equal("hello <p>world</p>", $safe_string->unescaped());
+ }
+
+ public function of_safe_html_test() {
+ $safe_string = SafeString::of_safe_html("hello <p>world</p>");
+ $this->assert_equal("hello <p>world</p>", $safe_string->for_html());
+ }
+
+ public function purify_test() {
+ $safe_string = SafeString::purify("hello <p >world</p>");
+ $this->assert_equal("hello <p>world</p>", $safe_string);
+ }
+
+ public function of_fluid_api_test() {
+ $escaped_string = SafeString::of("Foo's bar")->for_js();
+ $this->assert_equal("Foo\\'s bar", $escaped_string);
+ }
+
+ public function safestring_of_safestring_preserves_safe_status_test() {
+ $safe_string = SafeString::of_safe_html("hello's <p>world</p>");
+ $safe_string_2 = new SafeString($safe_string);
+ $this->assert_equal("hello's <p>world</p>", $safe_string_2);
+ $this->assert_equal("hello\\'s <p>world<\\/p>", $safe_string_2->for_js());
+ }
+
+ public function safestring_of_safestring_preserves_html_safe_status_test() {
+ $safe_string = SafeString::of_safe_html("hello's <p>world</p>");
+ $safe_string_2 = new SafeString($safe_string);
+ $this->assert_equal("hello's <p>world</p>", $safe_string_2);
+ $this->assert_equal("hello\\'s <p>world<\\/p>", $safe_string_2->for_js());
+ }
+
+ public function safestring_of_safestring_safe_status_override_test() {
+ $safe_string = new SafeString("hello <p>world</p>");
+ $safe_string_2 = SafeString::of_safe_html($safe_string);
+ $this->assert_equal("hello <p>world</p>", $safe_string_2);
+ }
+}
diff --git a/modules/gallery/tests/Xss_Security_Test.php b/modules/gallery/tests/Xss_Security_Test.php
index 9bde11dc..690dc760 100644
--- a/modules/gallery/tests/Xss_Security_Test.php
+++ b/modules/gallery/tests/Xss_Security_Test.php
@@ -19,87 +19,304 @@
*/
class Xss_Security_Test extends Unit_Test_Case {
public function find_unescaped_variables_in_views_test() {
+ $found = array();
foreach (glob("*/*/views/*.php") as $view) {
- $expr = null;
- $level = 0;
- $php = 0;
- $str = null;
- $in_p_clean = 0;
+ // List of all tokens without whitespace, simplifying parsing.
+ $tokens = array();
foreach (token_get_all(file_get_contents($view)) as $token) {
- if (false /* useful for debugging */) {
- if (is_array($token)) {
- printf("[$str] [$in_p_clean] %-15s %s\n", token_name($token[0]), $token[1]);
- } else {
- printf("[$str] [$in_p_clean] %-15s %s\n", "<char>", $token);
- }
- }
-
- // If we find a "(" after a "p::clean" then start counting levels of parens and assume
- // that we're inside a p::clean() call until we find the matching close paren.
- if ($token[0] == "(" && ($str == "p::clean" || $str == "p::purify")) {
- $in_p_clean = 1;
- } else if ($token[0] == "(" && $in_p_clean) {
- $in_p_clean++;
- } else if ($token[0] == ")" && $in_p_clean) {
- $in_p_clean--;
- }
-
- // Concatenate runs of strings for convenience, which we use above to figure out if we're
- // inside a p::clean() call or not
- if ($token[0] == T_STRING || $token[0] == T_DOUBLE_COLON) {
- $str .= $token[1];
- } else {
- $str = null;
- }
-
- // Scan for any occurrences of < ? = $variable ? > and store it in $expr
- if ($token[0] == T_OPEN_TAG_WITH_ECHO) {
- $php++;
- } else if ($php && $token[0] == T_CLOSE_TAG) {
- $php--;
- } else if ($php && $token[0] == T_VARIABLE) {
- if (!$expr) {
- $entry = array($token[2], $in_p_clean);
- }
- $expr .= $token[1];
- } else if ($expr) {
- if ($token[0] == T_OBJECT_OPERATOR) {
- $expr .= $token[1];
- } else if ($token[0] == T_STRING) {
- $expr .= $token[1];
- } else if ($token == "(") {
- $expr .= $token;
- $level++;
- } else if ($level > 0 && $token == ")") {
- $expr .= $token;
- $level--;
- } else if ($level > 0) {
- $expr .= is_array($token) ? $token[1] : $token;
- } else {
- $entry[] = $expr;
- $found[$view][] = $entry;
- $expr = null;
- $entry = null;
- }
- }
+ if (!is_array($token) || ($token[0] != T_WHITESPACE)) {
+ $tokens[] = $token;
+ }
+ }
+
+ $frame = null;
+ $script_block = 0;
+ $in_script_block = false;
+
+ for ($token_number = 0; $token_number < count($tokens); $token_number++) {
+ $token = $tokens[$token_number];
+
+ // Are we in a <script> ... </script> block?
+ if (is_array($token) && $token[0] == T_INLINE_HTML) {
+ $inline_html = $token[1];
+ // T_INLINE_HTML blocks can be split. Need to handle the case
+ // where one token has "<scr" and the next has "ipt"
+ while (self::_token_matches(array(T_INLINE_HTML), $tokens, $token_number + 1)) {
+ $token_number++;
+ $token = $tokens[$token_number];
+ $inline_html .= $token[1];
+ }
+
+ if ($frame) {
+ $frame->expr_append($inline_html);
+ }
+
+ // Note: This approach won't catch <script src="..."> blocks if the src
+ // URL is generated via < ? = url::site() ? > or some other PHP.
+ // Assume that all such script blocks with a src URL have an
+ // empty element body.
+ // But we'll catch closing tags for such blocks, so don't keep track
+ // of opening / closing tag count since it would be meaningless.
+
+ // Handle multiple start / end blocks on the same line?
+ $opening_script_pos = $closing_script_pos = 0;
+ if (preg_match_all('{</script>}i', $inline_html, $matches, PREG_OFFSET_CAPTURE)) {
+ $last_match = array_pop($matches[0]);
+ if (is_array($last_match)) {
+ $closing_script_pos = $last_match[1];
+ } else {
+ $closing_script_pos = $last_match;
+ }
+ }
+ if (preg_match('{<script\b[^>]*>}i', $inline_html, $matches, PREG_OFFSET_CAPTURE)) {
+ $last_match = array_pop($matches[0]);
+ if (is_array($last_match)) {
+ $opening_script_pos = $last_match[1];
+ } else {
+ $opening_script_pos = $last_match;
+ }
+ }
+ if ($opening_script_pos != $closing_script_pos) {
+ $in_script_block = $opening_script_pos > $closing_script_pos;
+ }
+ }
+
+ // Look and report each instance of < ? = ... ? >
+ if (!is_array($token)) {
+ // A single char token, e.g: ; ( )
+ if ($frame) {
+ $frame->expr_append($token);
+ }
+ } else if ($token[0] == T_OPEN_TAG_WITH_ECHO) {
+ // No need for a stack here - assume < ? = cannot be nested.
+ $frame = self::_create_frame($token, $in_script_block);
+ } else if ($frame && $token[0] == T_CLOSE_TAG) {
+ // Store the < ? = ... ? > block that just ended here.
+ $found[$view][] = $frame;
+ $frame = null;
+ } else if ($frame && $token[0] == T_VARIABLE) {
+ $frame->expr_append($token[1]);
+ } else if ($frame && $token[0] == T_STRING) {
+ $frame->expr_append($token[1]);
+ // t() and t2() are special in that they're guaranteed to return a SafeString().
+ if (in_array($token[1], array("t", "t2"))) {
+ if (self::_token_matches("(", $tokens, $token_number + 1)) {
+ $frame->is_safestring(true);
+ $frame->expr_append("(");
+
+ $token_number++;
+ $token = $tokens[$token_number];
+ }
+ } else if ($token[1] == "SafeString") {
+ // Looking for SafeString::of(...
+ if (self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1) &&
+ self::_token_matches(array(T_STRING), $tokens, $token_number + 2) &&
+ in_array($tokens[$token_number + 2][1], array("of", "of_safe_html", "purify")) &&
+ self::_token_matches("(", $tokens, $token_number + 3)) {
+ $frame->is_safestring(true);
+
+ $method = $tokens[$token_number + 2][1];
+ $frame->expr_append("::$method(");
+
+ $token_number += 3;
+ $token = $tokens[$token_number];
+ }
+ } else if ($token[1] == "json_encode") {
+ if (self::_token_matches("(", $tokens, $token_number + 1)) {
+ $frame->json_encode_called(true);
+ $frame->expr_append("(");
+
+ $token_number++;
+ $token = $tokens[$token_number];
+ }
+ } else if ($token[1] == "url") {
+ // url methods return a SafeString
+ if (self::_token_matches(array(T_DOUBLE_COLON, "::"), $tokens, $token_number + 1) &&
+ self::_token_matches(array(T_STRING), $tokens, $token_number + 2) &&
+ in_array($tokens[$token_number + 2][1],
+ array("site", "current", "base", "file", "abs_site", "abs_current",
+ "abs_file", "merge")) &&
+ self::_token_matches("(", $tokens, $token_number + 3)) {
+ $frame->is_safestring(true);
+
+ $method = $tokens[$token_number + 2][1];
+ $frame->expr_append("::$method(");
+
+ $token_number += 3;
+ $token = $tokens[$token_number];
+ }
+ }
+ } else if ($frame && $token[0] == T_OBJECT_OPERATOR) {
+ $frame->expr_append($token[1]);
+
+ if (self::_token_matches(array(T_STRING), $tokens, $token_number + 1) &&
+ in_array($tokens[$token_number + 1][1],
+ array("for_js", "for_html", "purified_html")) &&
+ self::_token_matches("(", $tokens, $token_number + 2)) {
+
+ $method = $tokens[$token_number + 1][1];
+ $frame->expr_append("$method(");
+
+ $token_number += 2;
+ $token = $tokens[$token_number];
+
+ if ("for_js" == $method) {
+ $frame->for_js_called(true);
+ } else if ("for_html" == $method) {
+ $frame->for_html_called(true);
+ } else if ("purified_html" == $method) {
+ $frame->purified_html_called(true);
+ }
+ }
+ } else if ($frame) {
+ $frame->expr_append($token[1]);
+ }
}
}
- $canonical = MODPATH . "gallery/tests/xss_data.txt";
+ /*
+ * Generate the report
+ *
+ * States for uses of < ? = X ? >:
+ * DIRTY_JS:
+ * In <script> block
+ * X can be anything without calling ->for_js()
+ * DIRTY:
+ * Outside <script> block:
+ * X can be anything without a call to ->for_html() or ->purified_html()
+ * CLEAN:
+ * Outside <script> block:
+ * X = is SafeString (t(), t2(), url::site())
+ * X = * and for_html() or purified_html() is called
+ * Inside <script> block:
+ * X = * with ->for_js() or json_encode(...)
+ */
$new = TMPPATH . "xss_data.txt";
$fd = fopen($new, "wb");
ksort($found);
- foreach ($found as $view => $entries) {
- foreach ($entries as $entry) {
- fwrite($fd,
- sprintf("%-60s %-3s %-5s %s\n",
- $view, $entry[0], $entry[1] ? "" : "DIRTY", $entry[2]));
+ foreach ($found as $view => $frames) {
+ foreach ($frames as $frame) {
+ $state = "DIRTY";
+ if ($frame->in_script_block()) {
+ $state = "DIRTY_JS";
+ if ($frame->for_js_called() || $frame->json_encode_called()) {
+ $state = "CLEAN";
+ }
+ } else {
+ if ($frame->is_safestring() || $frame->purified_html_called() || $frame->for_html_called()) {
+ $state = "CLEAN";
+ }
+ }
+
+ if ("CLEAN" == $state) {
+ // Don't print CLEAN instances - No need to update the golden
+ // file when adding / moving clean instances.
+ continue;
+ }
+
+ fprintf($fd, "%-60s %-3s %-8s %s\n",
+ $view, $frame->line(), $state, $frame->expr());
}
}
fclose($fd);
+ // Compare with the expected report from our golden file.
+ $canonical = MODPATH . "gallery/tests/xss_data.txt";
exec("diff $canonical $new", $output, $return_value);
$this->assert_false(
$return_value, "XSS golden file mismatch. Output:\n" . implode("\n", $output) );
}
+
+ private static function _create_frame($token, $in_script_block) {
+ return new Xss_Security_Test_Frame($token[2], $in_script_block);
+ }
+
+ private static function _token_matches($expected_token, &$tokens, $token_number) {
+ if (!isset($tokens[$token_number])) {
+ return false;
+ }
+
+ $token = $tokens[$token_number];
+
+ if (is_array($expected_token)) {
+ for ($i = 0; $i < count($expected_token); $i++) {
+ if ($expected_token[$i] != $token[$i]) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return $expected_token == $token;
+ }
+ }
+}
+
+class Xss_Security_Test_Frame {
+ private $_expr = "";
+ private $_in_script_block = false;
+ private $_is_safestring = false;
+ private $_for_js_called = false;
+ private $_for_html_called = false;
+ private $_purified_html_called = false;
+ private $_json_encode_called = false;
+ private $_line;
+
+ function __construct($line_number, $in_script_block) {
+ $this->_line = $line_number;
+ $this->in_script_block($in_script_block);
+ }
+
+ function expr() {
+ return $this->_expr;
+ }
+
+ function expr_append($append_value) {
+ return $this->_expr .= $append_value;
+ }
+
+ function in_script_block($new_val=NULL) {
+ if ($new_val !== NULL) {
+ $this->_in_script_block = (bool) $new_val;
+ }
+ return $this->_in_script_block;
+ }
+
+ function is_safestring($new_val=NULL) {
+ if ($new_val !== NULL) {
+ $this->_is_safestring = (bool) $new_val;
+ }
+ return $this->_is_safestring;
+ }
+
+ function json_encode_called($new_val=NULL) {
+ if ($new_val !== NULL) {
+ $this->_json_encode_called = (bool) $new_val;
+ }
+ return $this->_json_encode_called;
+ }
+
+ function for_js_called($new_val=NULL) {
+ if ($new_val !== NULL) {
+ $this->_for_js_called = (bool) $new_val;
+ }
+ return $this->_for_js_called;
+ }
+
+ function for_html_called($new_val=NULL) {
+ if ($new_val !== NULL) {
+ $this->_for_html_called = (bool) $new_val;
+ }
+ return $this->_for_html_called;
+ }
+
+ function purified_html_called($new_val=NULL) {
+ if ($new_val !== NULL) {
+ $this->_purified_html_called = (bool) $new_val;
+ }
+ return $this->_purified_html_called;
+ }
+
+ function line() {
+ return $this->_line;
+ }
}
diff --git a/modules/gallery/views/admin_advanced_settings.html.php b/modules/gallery/views/admin_advanced_settings.html.php
index b37c1c73..adc15b91 100644
--- a/modules/gallery/views/admin_advanced_settings.html.php
+++ b/modules/gallery/views/admin_advanced_settings.html.php
@@ -20,13 +20,13 @@
<? if ($var->module_name == "gallery" && $var->name == "_cache") continue ?>
<tr class="setting">
<td> <?= $var->module_name ?> </td>
- <td> <?= p::clean($var->name) ?> </td>
+ <td> <?= SafeString::of($var->name) ?> </td>
<td>
- <a href="<?= url::site("admin/advanced_settings/edit/$var->module_name/" . p::clean($var->name)) ?>"
+ <a href="<?= url::site("admin/advanced_settings/edit/$var->module_name/" . SafeString::of($var->name)) ?>"
class="gDialogLink"
- title="<?= t("Edit %var (%module_name)", array("var" => p::clean($var->name), "module_name" => $var->module_name)) ?>">
+ title="<?= t("Edit %var (%module_name)", array("var" => $var->name, "module_name" => $var->module_name)) ?>">
<? if ($var->value): ?>
- <?= p::clean($var->value) ?>
+ <?= SafeString::of($var->value) ?>
<? else: ?>
<i> <?= t("empty") ?> </i>
<? endif ?>
diff --git a/modules/gallery/views/admin_block_log_entries.html.php b/modules/gallery/views/admin_block_log_entries.html.php
index 44c1657f..b7afb22d 100644
--- a/modules/gallery/views/admin_block_log_entries.html.php
+++ b/modules/gallery/views/admin_block_log_entries.html.php
@@ -2,7 +2,7 @@
<ul>
<? foreach ($entries as $entry): ?>
<li class="<?= log::severity_class($entry->severity) ?>" style="direction: ltr">
- <a href="<?= url::site("user/$entry->user_id") ?>"><?= p::clean($entry->user->name) ?></a>
+ <a href="<?= url::site("user/$entry->user_id") ?>"><?= SafeString::of($entry->user->name) ?></a>
<?= gallery::date_time($entry->timestamp) ?>
<?= $entry->message ?>
<?= $entry->html ?>
diff --git a/modules/gallery/views/admin_block_photo_stream.html.php b/modules/gallery/views/admin_block_photo_stream.html.php
index 1e1329d1..732bdc38 100644
--- a/modules/gallery/views/admin_block_photo_stream.html.php
+++ b/modules/gallery/views/admin_block_photo_stream.html.php
@@ -2,9 +2,9 @@
<ul>
<? foreach ($photos as $photo): ?>
<li class="gItem gPhoto">
- <a href="<?= url::site("photos/$photo->id") ?>" title="<?= p::clean($photo->title) ?>">
+ <a href="<?= url::site("photos/$photo->id") ?>" title="<?= SafeString::of($photo->title) ?>">
<img <?= photo::img_dimensions($photo->width, $photo->height, 72) ?>
- src="<?= $photo->thumb_url() ?>" alt="<?= p::clean($photo->title) ?>" />
+ src="<?= $photo->thumb_url() ?>" alt="<?= SafeString::of($photo->title) ?>" />
</a>
</li>
<? endforeach ?>
diff --git a/modules/gallery/views/admin_maintenance.html.php b/modules/gallery/views/admin_maintenance.html.php
index 3649ea58..a0a6a19e 100644
--- a/modules/gallery/views/admin_maintenance.html.php
+++ b/modules/gallery/views/admin_maintenance.html.php
@@ -93,7 +93,7 @@
<?= $task->status ?>
</td>
<td>
- <?= p::clean($task->owner()->name) ?>
+ <?= SafeString::of($task->owner()->name) ?>
</td>
<td>
<? if ($task->state == "stalled"): ?>
diff --git a/modules/gallery/views/admin_maintenance_show_log.html.php b/modules/gallery/views/admin_maintenance_show_log.html.php
index 9d850986..209aef03 100644
--- a/modules/gallery/views/admin_maintenance_show_log.html.php
+++ b/modules/gallery/views/admin_maintenance_show_log.html.php
@@ -12,7 +12,7 @@ appendTo('body').submit().remove();
<div id="gTaskLogDialog">
<h1> <?= $task->name ?> </h1>
<div class="gTaskLog">
- <pre><?= p::purify($task->get_log()) ?></pre>
+ <pre><?= SafeString::purify($task->get_log()) ?></pre>
</div>
<button id="gCloseButton" class="ui-state-default ui-corner-all" onclick="dismiss()"><?= t("Close") ?></button>
<button id="gSaveButton" class="ui-state-default ui-corner-all" onclick="download()"><?= t("Save") ?></button>
diff --git a/modules/gallery/views/after_install.html.php b/modules/gallery/views/after_install.html.php
index bfce46f0..b77a1707 100644
--- a/modules/gallery/views/after_install.html.php
+++ b/modules/gallery/views/after_install.html.php
@@ -8,7 +8,7 @@
</p>
<p>
- <?= t("You're logged in to the <b>%user_name</b> account. The very first thing you should do is to change your password to something that you'll remember.", array("user_name" => p::clean($user->name))) ?>
+ <?= t("You're logged in to the <b>%user_name</b> account. The very first thing you should do is to change your password to something that you'll remember.", array("user_name" => $user->name)) ?>
</p>
<p>
diff --git a/modules/gallery/views/l10n_client.html.php b/modules/gallery/views/l10n_client.html.php
index 5ee7eca3..520fd79e 100644
--- a/modules/gallery/views/l10n_client.html.php
+++ b/modules/gallery/views/l10n_client.html.php
@@ -72,8 +72,8 @@
</div>
</div>
<script type="text/javascript">
- var MSG_TRANSLATE_TEXT = "<?= t("Translate Text") ?>";
- var MSG_CLOSE_X = "<?= t("X") ?>";
+ var MSG_TRANSLATE_TEXT = "<?= t("Translate Text")->for_js() ?>";
+ var MSG_CLOSE_X = "<?= t("X")->for_js() ?>";
var l10n_client_data = <?= json_encode($string_list) ?>;
var plural_forms = <?= json_encode($plural_forms) ?>;
</script>
diff --git a/modules/gallery/views/move_tree.html.php b/modules/gallery/views/move_tree.html.php
index 5f70cf67..7818a42a 100644
--- a/modules/gallery/views/move_tree.html.php
+++ b/modules/gallery/views/move_tree.html.php
@@ -1,18 +1,18 @@
<?php defined("SYSPATH") or die("No direct script access.") ?>
<?= $parent->thumb_img(array(), 25); ?>
<? if (!access::can("edit", $parent) || $source->is_descendant($parent)): ?>
-<a href="javascript:load_tree('<?= $parent->id ?>',1)"> <?= p::clean($parent->title) ?> <?= t("(locked)") ?> </a>
+<a href="javascript:load_tree('<?= $parent->id ?>',1)"> <?= SafeString::of($parent->title) ?> <?= t("(locked)") ?> </a>
<? else: ?>
-<a href="javascript:load_tree('<?= $parent->id ?>',0)"> <?= p::clean($parent->title) ?></a>
+<a href="javascript:load_tree('<?= $parent->id ?>',0)"> <?= SafeString::of($parent->title) ?></a>
<? endif ?>
<ul id="tree_<?= $parent->id ?>">
<? foreach ($children as $child): ?>
<li id="node_<?= $child->id ?>" class="node">
<?= $child->thumb_img(array(), 25); ?>
<? if (!access::can("edit", $child) || $source->is_descendant($child)): ?>
- <a href="javascript:load_tree('<?= $child->id ?>',1)"> <?= p::clean($child->title) ?> <?= t("(locked)") ?></a>
+ <a href="javascript:load_tree('<?= $child->id ?>',1)"> <?= SafeString::of($child->title) ?> <?= t("(locked)") ?></a>
<? else: ?>
- <a href="javascript:load_tree('<?= $child->id ?>',0)"> <?= p::clean($child->title) ?> </a>
+ <a href="javascript:load_tree('<?= $child->id ?>',0)"> <?= SafeString::of($child->title) ?> </a>
<? endif ?>
</li>
<? endforeach ?>
diff --git a/modules/gallery/views/permissions_browse.html.php b/modules/gallery/views/permissions_browse.html.php
index f990896c..90970112 100644
--- a/modules/gallery/views/permissions_browse.html.php
+++ b/modules/gallery/views/permissions_browse.html.php
@@ -33,21 +33,21 @@
</ul>
<? endif ?>
- <p>Edit permissions for album:</p>
+ <p><?= t("Edit permissions for album:") ?></p>
<ul class="gBreadcrumbs">
<? foreach ($parents as $parent): ?>
<li id="item-<?= $parent->id ?>">
<a href="javascript:show(<?= $parent->id ?>)">
- <?= p::purify($parent->title) ?>
+ <?= SafeString::purify($parent->title) ?>
</a>
</li>
<? endforeach ?>
<li class="active" id="item-<?= $item->id ?>">
<a href="javascript:show(<?= $item->id ?>)">
- <?= p::purify($item->title) ?></li>
- </a>
- </li>
+ <?= SafeString::purify($item->title) ?>
+ </a>
+ </li>
</ul>
<div id="gEditPermissionForm">
diff --git a/modules/gallery/views/permissions_form.html.php b/modules/gallery/views/permissions_form.html.php
index ee5e3a24..adc0496f 100644
--- a/modules/gallery/views/permissions_form.html.php
+++ b/modules/gallery/views/permissions_form.html.php
@@ -6,7 +6,7 @@
<tr>
<th> </th>
<? foreach ($groups as $group): ?>
- <th> <?= p::clean($group->name) ?> </th>
+ <th> <?= SafeString::of($group->name) ?> </th>
<? endforeach ?>
</tr>
diff --git a/modules/gallery/views/simple_uploader.html.php b/modules/gallery/views/simple_uploader.html.php
index 29a0dfe8..1f185780 100644
--- a/modules/gallery/views/simple_uploader.html.php
+++ b/modules/gallery/views/simple_uploader.html.php
@@ -6,7 +6,7 @@
<!-- hack to set the title for the dialog -->
<form id="gAddPhotosForm" action="<?= url::site("simple_uploader/finish?csrf=$csrf") ?>">
<fieldset>
- <legend> <?= t("Add photos to %album_title", array("album_title" => p::purify($item->title))) ?> </legend>
+ <legend> <?= t("Add photos to %album_title", array("album_title" => SafeString::purify($item->title))) ?> </legend>
</fieldset>
</form>
@@ -26,9 +26,9 @@
</p>
<ul class="gBreadcrumbs">
<? foreach ($item->parents() as $parent): ?>
- <li> <?= p::clean($parent->title) ?> </li>
+ <li> <?= SafeString::of($parent->title) ?> </li>
<? endforeach ?>
- <li class="active"> <?= p::purify($item->title) ?> </li>
+ <li class="active"> <?= SafeString::purify($item->title) ?> </li>
</ul>
<p>
@@ -82,27 +82,26 @@
<script type="text/javascript">
var swfu = new SWFUpload({
- flash_url: "<?= url::file("lib/swfupload/swfupload.swf") ?>",
- upload_url: "<?= url::site("simple_uploader/add_photo/$item->id") ?>",
- post_params: {
- "g3sid": "<?= Session::instance()->id() ?>",
- "user_agent": "<?= Input::instance()->server("HTTP_USER_AGENT") ?>",
- "csrf": "<?= $csrf ?>"
- },
- file_size_limit: "<?= ini_get("upload_max_filesize") ? num::convert_to_bytes(ini_get("upload_max_filesize"))."B" : "100MB" ?>",
+ flash_url: "<?= url::file("lib/swfupload/swfupload.swf")->for_js() ?>",
+ upload_url: "<?= url::site("simple_uploader/add_photo/$item->id")->for_js() ?>",
+ post_params: <?= json_encode(array(
+ "g3sid" => Session::instance()->id(),
+ "user_agent" => Input::instance()->server("HTTP_USER_AGENT"),
+ "csrf" => $csrf)) ?>,
+ file_size_limit: "<?= SafeString::of(ini_get("upload_max_filesize") ? num::convert_to_bytes(ini_get("upload_max_filesize"))."B" : "100MB")->for_js() ?>",
file_types: "*.gif;*.jpg;*.jpeg;*.png;*.flv;*.mp4;*.GIF;*.JPG;*.JPEG;*.PNG;*.FLV;*.MP4",
- file_types_description: "<?= t("Photos and Movies") ?>",
+ file_types_description: "<?= t("Photos and Movies")->for_js() ?>",
file_upload_limit: 1000,
file_queue_limit: 0,
custom_settings: { },
debug: false,
// Button settings
- button_image_url: "<?= url::file("themes/default/images/select-photos-backg.png") ?>",
+ button_image_url: "<?= url::file("themes/default/images/select-photos-backg.png")->for_js() ?>",
button_width: "202",
button_height: "45",
button_placeholder_id: "gChooseFilesButtonPlaceholder",
- button_text: '<span class="swfUploadFont"><?= t("Select photos...") ?></span>',
+ button_text: <?= json_encode('<span class="swfUploadFont">' . t("Select photos...") . '</span>') ?>,
button_text_style: ".swfUploadFont { color: #2E6E9E; font-size: 16px; font-family: Lucida Grande,Lucida Sans,Arial,sans-serif; font-weight: bold; }",
button_text_left_padding: 30,
button_text_top_padding: 10,
@@ -146,13 +145,13 @@
function file_queued(file) {
var fp = new File_Progress(file);
fp.title.html(file.name);
- fp.set_status("pending", "<?= t("Pending...") ?>");
+ fp.set_status("pending", "<?= t("Pending...")->for_js() ?>");
// @todo add cancel button to call this.cancelUpload(file.id)
}
function file_queue_error(file, error_code, message) {
if (error_code === SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED) {
- alert("<?= t("You have attempted to queue too many files.") ?>");
+ alert("<?= t("You have attempted to queue too many files.")->for_js() ?>");
return;
}
@@ -160,20 +159,20 @@
switch (error_code) {
case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT:
fp.title.html(file.name);
- fp.set_status("error", "<?= t("<strong>File is too big.</strong> A likely error source is a too low value for <em>upload_max_filesize</em> (%upload_max_filesize) in your <em>php.ini</em>.", array("upload_max_filesize" => ini_get("upload_max_filesize"))) ?>");
+ fp.set_status("error", "<?= t("<strong>File is too big.</strong> A likely error source is a too low value for <em>upload_max_filesize</em> (%upload_max_filesize) in your <em>php.ini</em>.", array("upload_max_filesize" => ini_get("upload_max_filesize")))->for_js() ?>");
break;
case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE:
fp.title.html(file.name);
- fp.set_status("error", "<?= t("Cannot upload empty files.") ?>");
+ fp.set_status("error", "<?= t("Cannot upload empty files.")->for_js() ?>");
break;
case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE:
fp.title.html(file.name);
- fp.set_status("error", "<?= t("Invalid file type.") ?>");
+ fp.set_status("error", "<?= t("Invalid file type.")->for_js() ?>");
break;
default:
if (file !== null) {
fp.title.html(file.name);
- fp.set_status("error", "<?= t("Unknown error") ?>");
+ fp.set_status("error", "<?= t("Unknown error")->for_js() ?>");
}
break;
}
@@ -194,7 +193,7 @@
// no uploadProgress events are called (limitation in the Linux Flash VM).
var fp = new File_Progress(file);
fp.title.html(file.name);
- fp.set_status("uploading", "<?= t("Uploading...") ?>");
+ fp.set_status("uploading", "<?= t("Uploading...")->for_js() ?>");
$("#gAddPhotosCanvas").scrollTo(fp.box, 1000);
return true;
// @todo add cancel button to call this.cancelUpload(file.id)
@@ -203,7 +202,7 @@
function upload_progress(file, bytes_loaded, bytes_total) {
var percent = Math.ceil((bytes_loaded / bytes_total) * 100);
var fp = new File_Progress(file);
- fp.set_status("uploading", "<?= t("Uploading...") ?>");
+ fp.set_status("uploading", "<?= t("Uploading...")->for_js() ?>");
fp.progress_bar.css("visibility", "visible");
fp.progress_bar.progressbar("value", percent);
}
@@ -211,42 +210,42 @@
function upload_success(file, serverData) {
var fp = new File_Progress(file);
fp.progress_bar.progressbar("value", 100);
- fp.set_status("complete", "<?= t("Complete.") ?>");
+ fp.set_status("complete", "<?= t("Complete.")->for_js() ?>");
}
function upload_error(file, error_code, message) {
var fp = new File_Progress(file);
switch (error_code) {
case SWFUpload.UPLOAD_ERROR.HTTP_ERROR:
- fp.set_status("error", "<?= t("Upload error: bad image file") ?>");
+ fp.set_status("error", "<?= t("Upload error: bad image file")->for_js() ?>");
break;
case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED:
- fp.set_status("error", "<?= t("Upload failed") ?>");
+ fp.set_status("error", "<?= t("Upload failed")->for_js() ?>");
break;
case SWFUpload.UPLOAD_ERROR.IO_ERROR:
- fp.set_status("error", "<?= t("Server error") ?>");
+ fp.set_status("error", "<?= t("Server error")->for_js() ?>");
break;
case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR:
- fp.set_status("error", "<?= t("Security error") ?>");
+ fp.set_status("error", "<?= t("Security error")->for_js() ?>");
break;
case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED:
- fp.set_status("error", "<?= t("Upload limit exceeded") ?>");
+ fp.set_status("error", "<?= t("Upload limit exceeded")->for_js() ?>");
break;
case SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED:
- fp.set_status("error", "<?= t("Failed validation. File skipped") ?>");
+ fp.set_status("error", "<?= t("Failed validation. File skipped")->for_js() ?>");
break;
case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED:
// If there aren't any files left (they were all cancelled) disable the cancel button
if (this.getStats().files_queued === 0) {
$("#gUploadCancel").hide();
}
- fp.set_status("error", "<?= t("Cancelled") ?>");
+ fp.set_status("error", "<?= t("Cancelled")->for_js() ?>");
break;
case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED:
- fp.set_status("error", "<?= t("Stopped") ?>");
+ fp.set_status("error", "<?= t("Stopped")->for_js() ?>");
break;
default:
- fp.set_status("error", "<?= t("Unknown error: ") ?>" + error_code);
+ fp.set_status("error", "<?= t("Unknown error: ")->for_js() ?>" + error_code);
break;
}
}
@@ -260,7 +259,7 @@
}
function get_completed_status_msg(stats) {
- var msg = "<?= t("Upload Queue (completed %completed of %total)", array("completed" => "__COMPLETED__", "total" => "__TOTAL__")) ?>";
+ var msg = "<?= t("Upload Queue (completed %completed of %total)", array("completed" => "__COMPLETED__", "total" => "__TOTAL__"))->for_js() ?>";
msg = msg.replace("__COMPLETED__", stats.successful_uploads);
msg = msg.replace("__TOTAL__", stats.files_queued + stats.successful_uploads +
stats.upload_errors + stats.upload_cancelled + stats.queue_errors);
@@ -269,7 +268,7 @@
// This event comes from the Queue Plugin
function queue_complete(num_files_uploaded) {
- var status_msg = "<?= t("Uploaded: __COUNT__") ?>";
+ var status_msg = "<?= t("Uploaded: __COUNT__")->for_js() ?>";
$("#gUploadStatus").html(status_msg.replace("__COUNT__", num_files_uploaded));
}
</script>