aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSem <sembrestels@riseup.net>2013-11-09 16:22:50 +0100
committerSem <sembrestels@riseup.net>2013-11-09 16:22:50 +0100
commitf448cd91f9ecebea37daf40aada2fa1a20200817 (patch)
tree4636059da6a1be133a4974e5098b7c1e08944dd5
parent52335325e5ea8c89c711e1ba40d9ac1657004e61 (diff)
parent324f39c3a9aa41e9e23bafba38882a2b72f62441 (diff)
downloadelgg-f448cd91f9ecebea37daf40aada2fa1a20200817.tar.gz
elgg-f448cd91f9ecebea37daf40aada2fa1a20200817.tar.bz2
Add 'mod/lightpics/' from commit '324f39c3a9aa41e9e23bafba38882a2b72f62441'
git-subtree-dir: mod/lightpics git-subtree-mainline: 52335325e5ea8c89c711e1ba40d9ac1657004e61 git-subtree-split: 324f39c3a9aa41e9e23bafba38882a2b72f62441
-rw-r--r--mod/lightpics/CONFIG.txt73
-rw-r--r--mod/lightpics/FAQ.txt31
-rw-r--r--mod/lightpics/actions/photos/admin/create_thumbnails.php70
-rw-r--r--mod/lightpics/actions/photos/admin/imtest.php18
-rw-r--r--mod/lightpics/actions/photos/admin/settings.php29
-rw-r--r--mod/lightpics/actions/photos/admin/upgrade.php52
-rw-r--r--mod/lightpics/actions/photos/album/save.php48
-rw-r--r--mod/lightpics/actions/photos/album/set_cover.php23
-rw-r--r--mod/lightpics/actions/photos/album/sort.php21
-rw-r--r--mod/lightpics/actions/photos/batch/edit.php42
-rw-r--r--mod/lightpics/actions/photos/delete.php48
-rw-r--r--mod/lightpics/actions/photos/image/save.php40
-rw-r--r--mod/lightpics/actions/photos/image/upload.php147
-rw-r--r--mod/lightpics/activate.php51
-rw-r--r--mod/lightpics/classes/TidypicsAlbum.php375
-rw-r--r--mod/lightpics/classes/TidypicsImage.php406
-rw-r--r--mod/lightpics/contributions.txt442
-rw-r--r--mod/lightpics/deactivate.php10
-rw-r--r--mod/lightpics/fonts/LiberationSerif-Regular.ttfbin0 -> 172964 bytes
-rw-r--r--mod/lightpics/fonts/License.txt13
-rw-r--r--mod/lightpics/graphics/empty_album.pngbin0 -> 1389 bytes
-rw-r--r--mod/lightpics/graphics/icons/album.gifbin0 -> 1411 bytes
-rw-r--r--mod/lightpics/graphics/icons/general.jpgbin0 -> 7036 bytes
-rw-r--r--mod/lightpics/graphics/icons/river_icon_album.gifbin0 -> 398 bytes
-rw-r--r--mod/lightpics/graphics/icons/river_icon_image.gifbin0 -> 945 bytes
-rw-r--r--mod/lightpics/graphics/icons/river_icon_tag.gifbin0 -> 184 bytes
-rw-r--r--mod/lightpics/graphics/image_error_large.pngbin0 -> 2208 bytes
-rw-r--r--mod/lightpics/graphics/image_error_small.pngbin0 -> 2351 bytes
-rw-r--r--mod/lightpics/graphics/image_error_thumb.pngbin0 -> 759 bytes
-rw-r--r--mod/lightpics/graphics/loader.gifbin0 -> 2545 bytes
-rw-r--r--mod/lightpics/graphics/spacer.gifbin0 -> 43 bytes
-rw-r--r--mod/lightpics/languages/ca.php210
-rw-r--r--mod/lightpics/languages/da.php169
-rw-r--r--mod/lightpics/languages/de.php248
-rw-r--r--mod/lightpics/languages/en.php262
-rw-r--r--mod/lightpics/languages/es.php210
-rw-r--r--mod/lightpics/languages/fr.php140
-rw-r--r--mod/lightpics/languages/he.php135
-rw-r--r--mod/lightpics/languages/it.php113
-rw-r--r--mod/lightpics/languages/pl.php96
-rw-r--r--mod/lightpics/languages/ru.php101
-rw-r--r--mod/lightpics/languages/tr.php86
-rw-r--r--mod/lightpics/lib/exif.php110
-rw-r--r--mod/lightpics/lib/migrate.php301
-rw-r--r--mod/lightpics/lib/resize.php522
-rw-r--r--mod/lightpics/lib/tidypics.php365
-rw-r--r--mod/lightpics/lib/upload.php119
-rw-r--r--mod/lightpics/manifest.xml30
-rw-r--r--mod/lightpics/pages/lists/highestrated.php65
-rw-r--r--mod/lightpics/pages/lists/highestvotecount.php50
-rw-r--r--mod/lightpics/pages/lists/mostcommentedimages.php42
-rw-r--r--mod/lightpics/pages/lists/mostcommentedimagesthismonth.php50
-rw-r--r--mod/lightpics/pages/lists/mostcommentedimagestoday.php50
-rw-r--r--mod/lightpics/pages/lists/mostrecentimages.php56
-rw-r--r--mod/lightpics/pages/lists/mostviewedimages.php79
-rw-r--r--mod/lightpics/pages/lists/mostviewedimageslastmonth.php50
-rw-r--r--mod/lightpics/pages/lists/mostviewedimagesthismonth.php50
-rw-r--r--mod/lightpics/pages/lists/mostviewedimagesthisyear.php50
-rw-r--r--mod/lightpics/pages/lists/mostviewedimagestoday.php50
-rw-r--r--mod/lightpics/pages/lists/recentlycommented.php61
-rw-r--r--mod/lightpics/pages/lists/recentlyviewed.php60
-rw-r--r--mod/lightpics/pages/lists/recentvotes.php52
-rw-r--r--mod/lightpics/pages/photos/album/add.php34
-rw-r--r--mod/lightpics/pages/photos/album/edit.php48
-rw-r--r--mod/lightpics/pages/photos/album/sort.php56
-rw-r--r--mod/lightpics/pages/photos/album/view.php63
-rw-r--r--mod/lightpics/pages/photos/all.php38
-rw-r--r--mod/lightpics/pages/photos/batch/edit.php44
-rw-r--r--mod/lightpics/pages/photos/friends.php34
-rw-r--r--mod/lightpics/pages/photos/image/download.php41
-rw-r--r--mod/lightpics/pages/photos/image/edit.php54
-rw-r--r--mod/lightpics/pages/photos/image/thumbnail.php38
-rw-r--r--mod/lightpics/pages/photos/image/upload.php64
-rw-r--r--mod/lightpics/pages/photos/image/view.php61
-rw-r--r--mod/lightpics/pages/photos/owner.php56
-rw-r--r--mod/lightpics/start.php421
-rw-r--r--mod/lightpics/upgrades/2009082901.php29
-rw-r--r--mod/lightpics/upgrades/2010073101.php27
-rw-r--r--mod/lightpics/upgrades/2010102801.php26
-rw-r--r--mod/lightpics/upgrades/2012020901.php26
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/README.md73
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/cors/postmessage.html75
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/cors/result.html20
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/css/jquery.fileupload-ui.css107
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/css/style.css92
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/img/loading.gifbin0 -> 3897 bytes
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/img/progressbar.gifbin0 -> 3323 bytes
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/index.html234
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/cors/jquery.postmessage-transport.js117
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/cors/jquery.xdr-transport.js85
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload-fp.js219
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload-jui.js141
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload-ui.js736
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload.js972
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/jquery.iframe-transport.js171
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/locale.js29
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/main.js93
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/vendor/canvas-to-blob.min.js1
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/vendor/jquery.ui.widget.js282
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/vendor/load-image.min.js1
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/js/vendor/tmpl.min.js1
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/package.json56
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/gae-go/app.yaml12
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/gae-go/app/main.go361
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/gae-go/resize/resize.go247
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/favicon.icobin0 -> 1150 bytes
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/robots.txt2
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/gae-python/app.yaml16
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/gae-python/main.py149
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/favicon.icobin0 -> 1150 bytes
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/robots.txt2
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/node/.gitignore2
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/node/package.json41
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/node/public/files/thumbnail/.gitignore0
-rwxr-xr-xmod/lightpics/vendors/jquery-file-upload/server/node/server.js285
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/node/tmp/.gitignore0
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/php/files/.htaccess4
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/php/index.php46
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/php/thumbnails/.htaccess0
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/server/php/upload.class.php436
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/test/index.html146
-rw-r--r--mod/lightpics/vendors/jquery-file-upload/test/test.js1279
-rw-r--r--mod/lightpics/version.php7
-rw-r--r--mod/lightpics/views/default/admin/settings/photos.php71
-rw-r--r--mod/lightpics/views/default/admin/settings/photos/help.php14
-rw-r--r--mod/lightpics/views/default/admin/settings/photos/image_lib.php38
-rw-r--r--mod/lightpics/views/default/admin/settings/photos/server_config.php14
-rw-r--r--mod/lightpics/views/default/admin/settings/photos/server_info.php120
-rw-r--r--mod/lightpics/views/default/admin/settings/photos/settings.php18
-rw-r--r--mod/lightpics/views/default/admin/settings/photos/thumbnail.php54
-rw-r--r--mod/lightpics/views/default/forms/photos/admin/settings.php26
-rw-r--r--mod/lightpics/views/default/forms/photos/admin/settings/activity.php31
-rw-r--r--mod/lightpics/views/default/forms/photos/admin/settings/image_lib.php20
-rw-r--r--mod/lightpics/views/default/forms/photos/admin/settings/main.php39
-rw-r--r--mod/lightpics/views/default/forms/photos/admin/settings/thumbnails.php33
-rw-r--r--mod/lightpics/views/default/forms/photos/album/save.php48
-rw-r--r--mod/lightpics/views/default/forms/photos/album/sort.php28
-rw-r--r--mod/lightpics/views/default/forms/photos/basic_upload.php63
-rw-r--r--mod/lightpics/views/default/forms/photos/batch/edit.php33
-rw-r--r--mod/lightpics/views/default/forms/photos/batch/edit/image.php35
-rw-r--r--mod/lightpics/views/default/forms/photos/image/save.php48
-rw-r--r--mod/lightpics/views/default/icon/object/album.php41
-rw-r--r--mod/lightpics/views/default/icon/object/image.php62
-rw-r--r--mod/lightpics/views/default/js/photos/tidypics.php43
-rw-r--r--mod/lightpics/views/default/js/photos/upload.php63
-rw-r--r--mod/lightpics/views/default/object/album.php22
-rw-r--r--mod/lightpics/views/default/object/album/full.php59
-rw-r--r--mod/lightpics/views/default/object/album/gallery.php36
-rw-r--r--mod/lightpics/views/default/object/album/list.php42
-rw-r--r--mod/lightpics/views/default/object/image.php211
-rw-r--r--mod/lightpics/views/default/object/image/full.php62
-rw-r--r--mod/lightpics/views/default/object/image/navigation.php34
-rw-r--r--mod/lightpics/views/default/object/image/summary.php41
-rw-r--r--mod/lightpics/views/default/photos/css.php470
-rw-r--r--mod/lightpics/views/default/photos/group_module.php45
-rw-r--r--mod/lightpics/views/default/photos/sidebar.php18
-rw-r--r--mod/lightpics/views/default/photos/sidebar/exif.php20
-rw-r--r--mod/lightpics/views/default/photos/sidebar/quota.php27
-rw-r--r--mod/lightpics/views/default/river/object/album/create.php40
-rw-r--r--mod/lightpics/views/default/river/object/image/create.php41
-rw-r--r--mod/lightpics/views/default/river/object/tidypics_batch/create.php66
-rw-r--r--mod/lightpics/views/default/widgets/album_view/content.php17
-rw-r--r--mod/lightpics/views/default/widgets/album_view/edit.php25
-rw-r--r--mod/lightpics/views/default/widgets/latest_photos/content.php18
-rw-r--r--mod/lightpics/views/default/widgets/latest_photos/edit.php25
-rw-r--r--mod/lightpics/views/rss/extensions/photos/xmlns.php9
-rw-r--r--mod/lightpics/views/rss/object/album.php17
-rw-r--r--mod/lightpics/views/rss/object/album/full.php19
-rw-r--r--mod/lightpics/views/rss/object/album/summary.php33
-rw-r--r--mod/lightpics/views/rss/object/image.php42
170 files changed, 16002 insertions, 0 deletions
diff --git a/mod/lightpics/CONFIG.txt b/mod/lightpics/CONFIG.txt
new file mode 100644
index 000000000..0bfb8513d
--- /dev/null
+++ b/mod/lightpics/CONFIG.txt
@@ -0,0 +1,73 @@
+
+Troubleshooting
+=================
+If you are experiencing errors, please check your server error log. Tidypics
+writes warnings for most upload related problems. You will see something
+along these lines: "Tidypics warning: user exceeded post limit on image upload"
+
+
+GD PHP Image Library
+=====================
+Elgg requires GD for image resizing of profile avatars. Tidypics uses GD by
+default for resizing. There are two versions GD and GD2. It generally does not matter
+which one you are using and it isn't always apparent which one you are
+running anyway. GD is a memory hog and will cause problems with large images.
+
+imagick PHP Image Library
+=============================
+A significantly better image library than GD. Not as common as GD. You may need
+to ask your system administrator to install this. The server info tab in Tidypics
+settings will tell you whether it is installed.
+
+ImageMagick command line tools
+===============================
+This requires that the PHP function exec() is available which is often disbaled
+for security reasons. You can find ImageMagick on your server by running "which convert".
+
+Memory Available to PHP
+=========================
+The amount of RAM available to a single PHP process. This can have an effect on
+how large an image you can resize (especially if you are running Tidypics with GD).
+The best place to change it is .htaccess.
+
+Memory Used to Load This Page
+==============================
+This gives you a baseline of how much memory is being used to load the Elgg
+framework and plugins. Subtract this number from the total amount of memory available
+for image processing. Remember that GD requires a lot of memory - more than just the
+size of the image.
+
+Max File Upload Size
+=======================
+The largest file that can be uploaded. You can adjust this in .htaccess.
+
+Max Post Size
+===============
+The largest amount of data that can be submitted. For Tidypics, this affects
+how many large images can be uploaded in a single upload with the basic uploader.
+If this is exceeded, nothing is uploaded and the user gets an error message.
+It can be adjusted in .htaccess.
+
+Max Input Time
+===============
+This is the amount of time PHP will wait for an upload to finish. Your users may
+experience failures if they are uploading large images on a poor connection or if
+your server does not have a lot of bandwidth.
+
+Max Execution Time
+===================
+The longest time a PHP script can run on your server. If a user uploads many
+large images, the resize code may take a long time to complete and will be
+stopped by PHP. If you are seeing problems with this, you probably have a slow server.
+
+GD imagejpeg
+===============
+This tells you whether you can resize JPEG images with GD
+
+GD imagegif
+===============
+This tells you whether you can resize GIF images with GD
+
+GD imagepng
+===============
+This tells you whether you can resize PNG images with GD
diff --git a/mod/lightpics/FAQ.txt b/mod/lightpics/FAQ.txt
new file mode 100644
index 000000000..2f0de8165
--- /dev/null
+++ b/mod/lightpics/FAQ.txt
@@ -0,0 +1,31 @@
+White screen when uploading images
+==================================
+Tidypics tries to calculate the maximum size of an image that your server will support. If it
+guesses incorrectly and someone uploads a photo that is too large, the script may crash when
+resizing the image if you are using GD. The easiest way to test this is to set display_errors
+to 1 in your .htaccess file and upload large images. If this causes a problem, a php memory error
+should display on the screen. You can increased your php memory limit. A better option is to use
+imagick if your server supports it (again see the docs directory).
+
+If it is not a memory issue, you should see some other error appear. Once you have fixed the error,
+change display_error back to 0.
+
+
+Question mark images appear
+===========================
+If you see question mark images when you look at your albums, this means the resizing of the images
+failed. This could be due to the memory limits as described above. There are other causes. Tidypics
+tries to detect these problems and write the cause to the error log. You should check your server
+error log right after an upload that results in a question mark for these error messages. The messages
+will begin with "Tidypics warning:". It is possible if you have turned off php warnings that you will
+not see these warnings.
+
+Another possible cause is using command line ImageMagick when your server does not
+support it or specifying the wrong path to the ImageMagick executables.
+
+
+Unable to save settings
+=======================
+If you are unable to settings, Apache may be configured to block pages that use
+file paths as Tidypics does when setting the location of the ImageMagick executable.
+In this case, leave that field blank.
diff --git a/mod/lightpics/actions/photos/admin/create_thumbnails.php b/mod/lightpics/actions/photos/admin/create_thumbnails.php
new file mode 100644
index 000000000..dfb5d4ed1
--- /dev/null
+++ b/mod/lightpics/actions/photos/admin/create_thumbnails.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Tidypics Thumbnail Creation Test
+ *
+ * Called through ajax, but registered as an Elgg action.
+ *
+ */
+
+elgg_load_library('tidypics:resize');
+
+$guid = get_input('guid');
+$image = get_entity($guid);
+
+if (!$image || !($image instanceof TidypicsImage)) {
+ register_error(elgg_echo('tidypics:thumbnail_tool:unknown_image'));
+ forward(REFERER);
+}
+
+$filename = $image->getFilename();
+$container_guid = $image->container_guid;
+if (!$filename || !$container_guid) {
+ register_error(elgg_echo('tidypics:thumbnail_tool:invalid_image_info'));
+ forward(REFERER);
+}
+
+$title = $image->getTitle();
+$prefix = "image/$container_guid/";
+$filestorename = substr($filename, strlen($prefix));
+
+$image_lib = elgg_get_plugin_setting('image_lib', 'tidypics');
+if (!$image_lib) {
+ $image_lib = "GD";
+}
+
+// ImageMagick command line
+if ($image_lib == 'ImageMagick') {
+ if (!tp_create_im_cmdline_thumbnails($image, $prefix, $filestorename)) {
+ trigger_error('Tidypics warning: failed to create thumbnails - ImageMagick command line', E_USER_WARNING);
+ register_error(elgg_echo('tidypics:thumbnail_tool:create_failed'));
+ forward(REFERER);
+ }
+
+// imagick PHP extension
+} else if ($image_lib == 'ImageMagickPHP') {
+ if (!tp_create_imagick_thumbnails($image, $prefix, $filestorename)) {
+ trigger_error('Tidypics warning: failed to create thumbnails - ImageMagick PHP', E_USER_WARNING);
+ register_error(elgg_echo('tidypics:thumbnail_tool:create_failed'));
+ forward(REFERER);
+ }
+// gd
+} else {
+ if (!tp_create_gd_thumbnails($image, $prefix, $filestorename)) {
+ trigger_error('Tidypics warning: failed to create thumbnails - GD', E_USER_WARNING);
+ register_error(elgg_echo('tidypics:thumbnail_tool:create_failed'));
+ forward(REFERER);
+ }
+}
+
+$url = elgg_normalize_url("photos/thumbnail/$guid/large");
+system_message(elgg_echo('tidypics:thumbnail_tool:created'));
+
+if (elgg_is_xhr()) {
+ echo json_encode(array(
+ 'guid' => $guid,
+ 'title' => $title,
+ 'thumbnail_src' => $url
+ ));
+}
+
+forward(REFERER); \ No newline at end of file
diff --git a/mod/lightpics/actions/photos/admin/imtest.php b/mod/lightpics/actions/photos/admin/imtest.php
new file mode 100644
index 000000000..a58643d0e
--- /dev/null
+++ b/mod/lightpics/actions/photos/admin/imtest.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Tidypics ImageMagick Location Test
+ *
+ * Called through ajax. Not a registered Elgg action.
+ */
+
+$location = $_GET['location'];
+
+$command = $location . "convert -version";
+
+$result = system($command, $return_val);
+
+if ($return_val == 0) {
+ echo $result;
+} else {
+ echo "Unable to run ImageMagick. Please check the path.";
+}
diff --git a/mod/lightpics/actions/photos/admin/settings.php b/mod/lightpics/actions/photos/admin/settings.php
new file mode 100644
index 000000000..aa607756d
--- /dev/null
+++ b/mod/lightpics/actions/photos/admin/settings.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Save Tidypics plugin settings
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$params = get_input('params');
+foreach ($params as $k => $v) {
+ if (!elgg_set_plugin_setting($k, $v, 'lightpics')) {
+ register_error(elgg_echo('plugins:settings:save:fail', array('lightpics')));
+ forward(REFERER);
+ }
+}
+
+// image sizes
+$image_sizes = array();
+$image_sizes['large_image_width'] = get_input('large_image_width');
+$image_sizes['large_image_height'] = get_input('large_image_height');
+$image_sizes['small_image_width'] = get_input('small_image_width');
+$image_sizes['small_image_height'] = get_input('small_image_height');
+$image_sizes['tiny_image_width'] = get_input('tiny_image_width');
+$image_sizes['tiny_image_height'] = get_input('tiny_image_height');
+elgg_set_plugin_setting('image_sizes', serialize($image_sizes), 'lightpics');
+
+
+system_message(elgg_echo('tidypics:settings:save:ok'));
+forward(REFERER);
diff --git a/mod/lightpics/actions/photos/admin/upgrade.php b/mod/lightpics/actions/photos/admin/upgrade.php
new file mode 100644
index 000000000..db09ed035
--- /dev/null
+++ b/mod/lightpics/actions/photos/admin/upgrade.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Tidypics upgrade action
+ */
+
+$plugins_path = elgg_get_plugins_path();
+
+require_once "{$plugins_path}lightpics/version.php";
+
+$local_version = elgg_get_plugin_setting('version', 'tidypics');
+
+$local_version = 2009082901;
+if ($version <= $local_version) {
+ register_error('No upgrade required');
+ forward(REFERER);
+}
+
+set_time_limit(0);
+
+$base_dir = "{$plugins_path}lightpics/upgrades";
+
+
+// taken from engine/lib/version.php
+if ($handle = opendir($base_dir)) {
+ $upgrades = array();
+
+ while ($updatefile = readdir($handle)) {
+ // Look for upgrades and add to upgrades list
+ if (!is_dir("$base_dir/$updatefile")) {
+ if (preg_match('/^([0-9]{10})\.(php)$/', $updatefile, $matches)) {
+ $plugin_version = (int) $matches[1];
+ if ($plugin_version > $local_version) {
+ $upgrades[] = "$base_dir/$updatefile";
+ }
+ }
+ }
+ }
+
+ // Sort and execute
+ asort($upgrades);
+
+ if (sizeof($upgrades) > 0) {
+ foreach ($upgrades as $upgrade) {
+ include($upgrade);
+ }
+ }
+}
+
+elgg_set_plugin_setting('version', $version, 'tidypics');
+
+system_message("Tidypics has been upgraded");
+forward(REFERER);
diff --git a/mod/lightpics/actions/photos/album/save.php b/mod/lightpics/actions/photos/album/save.php
new file mode 100644
index 000000000..cc7181678
--- /dev/null
+++ b/mod/lightpics/actions/photos/album/save.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Save album action
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+
+// Get input data
+$title = get_input('title');
+$description = get_input('description');
+$tags = get_input('tags');
+$access_id = get_input('access_id');
+$container_guid = get_input('container_guid', elgg_get_logged_in_user_guid());
+$guid = get_input('guid');
+
+elgg_make_sticky_form('tidypics');
+
+if (empty($title)) {
+ register_error(elgg_echo("album:blank"));
+ forward(REFERER);
+}
+
+if ($guid) {
+ $album = get_entity($guid);
+} else {
+ $album = new TidypicsAlbum();
+}
+
+$album->container_guid = $container_guid;
+$album->owner_guid = elgg_get_logged_in_user_guid();
+$album->access_id = $access_id;
+$album->title = $title;
+$album->description = $description;
+if ($tags) {
+ $album->tags = string_to_tag_array($tags);
+}
+
+if (!$album->save()) {
+ register_error(elgg_echo("album:error"));
+ forward(REFERER);
+}
+
+elgg_clear_sticky_form('tidypics');
+
+system_message(elgg_echo("album:created"));
+forward($album->getURL());
diff --git a/mod/lightpics/actions/photos/album/set_cover.php b/mod/lightpics/actions/photos/album/set_cover.php
new file mode 100644
index 000000000..b37bb9c90
--- /dev/null
+++ b/mod/lightpics/actions/photos/album/set_cover.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Set album cover image
+ */
+
+// Get input data
+$album_guid = get_input('album_guid');
+$image_guid = get_input('image_guid');
+
+$album = get_entity($album_guid);
+
+if (!elgg_instanceof($album, 'object', 'album')) {
+ register_error(elgg_echo('album:invalid_album'));
+ forward(REFERER);
+}
+
+if ($album->setCoverImageGuid($image_guid)) {
+ system_message(elgg_echo('album:save_cover_image'));
+ forward(REFERER);
+} else {
+ register_error(elgg_echo('album:cannot_save_cover_image'));
+ forward(REFERER);
+} \ No newline at end of file
diff --git a/mod/lightpics/actions/photos/album/sort.php b/mod/lightpics/actions/photos/album/sort.php
new file mode 100644
index 000000000..fd62a7ba7
--- /dev/null
+++ b/mod/lightpics/actions/photos/album/sort.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Sorting album action - takes a comma separated list of image guids
+ */
+
+$album_guid = get_input('album_guid');
+$album = get_entity($album_guid);
+if (!$album) {
+
+}
+
+$guids = get_input('guids');
+$guids = explode(',', $guids);
+
+if ($album->setImageList($guids)) {
+ system_message(elgg_echo('tidypics:album:sorted', array($album->getTitle())));
+} else {
+ register_error(elgg_echo('tidypics:album:could_not_sort', array($album->getTitle())));
+}
+
+forward($album->getURL()); \ No newline at end of file
diff --git a/mod/lightpics/actions/photos/batch/edit.php b/mod/lightpics/actions/photos/batch/edit.php
new file mode 100644
index 000000000..6d376a9db
--- /dev/null
+++ b/mod/lightpics/actions/photos/batch/edit.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Edit the images in a batch
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$guids = get_input('guid');
+$titles = get_input('title');
+$captions = get_input('caption');
+$tags = get_input('tags');
+
+$not_updated = array();
+foreach ($guids as $key => $guid) {
+ $image = get_entity($guid);
+
+ if ($image->canEdit()) {
+
+ // set title appropriately
+ if ($titles[$key]) {
+ $image->title = $titles[$key];
+ } else {
+ $image->title = substr($image->originalfilename, 0, strrpos($image->originalfilename, '.'));
+ }
+
+ // set description appropriately
+ $image->description = $captions[$key];
+ $image->tags = string_to_tag_array($tags[$key]);
+
+ if (!$image->save()) {
+ array_push($not_updated, $image->getGUID());
+ }
+ }
+}
+
+if (count($not_updated) > 0) {
+ register_error(elgg_echo("images:notedited"));
+} else {
+ system_message(elgg_echo("images:edited"));
+}
+forward($image->getContainerEntity()->getURL());
diff --git a/mod/lightpics/actions/photos/delete.php b/mod/lightpics/actions/photos/delete.php
new file mode 100644
index 000000000..f8d194e55
--- /dev/null
+++ b/mod/lightpics/actions/photos/delete.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Delete album or image
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$guid = (int) get_input('guid');
+$entity = get_entity($guid);
+if (!$entity) {
+ // unable to get Elgg entity
+ register_error(elgg_echo("tidypics:deletefailed"));
+ forward(REFERER);
+}
+
+if (!$entity->canEdit()) {
+ // user doesn't have permissions
+ register_error(elgg_echo("tidypics:deletefailed"));
+ forward(REFERER);
+}
+
+$container = $entity->getContainerEntity();
+
+$subtype = $entity->getSubtype();
+switch ($subtype) {
+ case 'album':
+ if (elgg_instanceof($container, 'user')) {
+ $forward_url = "photos/owner/$container->username";
+ } else {
+ $forward_url = "photos/group/$container->guid/all";
+ }
+ break;
+ case 'image':
+ $forward_url = $container->getURL();
+ break;
+ default:
+ forward(REFERER);
+ break;
+}
+
+if ($entity->delete()) {
+ system_message(elgg_echo("tidypics:deleted"));
+} else {
+ register_error(elgg_echo("tidypics:deletefailed"));
+}
+
+forward($forward_url);
diff --git a/mod/lightpics/actions/photos/image/save.php b/mod/lightpics/actions/photos/image/save.php
new file mode 100644
index 000000000..1cb65b1e8
--- /dev/null
+++ b/mod/lightpics/actions/photos/image/save.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Save image action
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+// Get input data
+$title = get_input('title');
+$description = get_input('description');
+$tags = get_input('tags');
+$access_id = get_input('access_id');
+$guid = get_input('guid');
+
+elgg_make_sticky_form('tidypics');
+
+if (empty($title)) {
+ register_error(elgg_echo("image:blank"));
+ forward(REFERER);
+}
+
+$image = get_entity($guid);
+
+$image->access_id = $access_id;
+$image->title = $title;
+$image->description = $description;
+if ($tags) {
+ $image->tags = string_to_tag_array($tags);
+}
+
+if (!$image->save()) {
+ register_error(elgg_echo("image:error"));
+ forward(REFERER);
+}
+
+elgg_clear_sticky_form('tidypics');
+
+system_message(elgg_echo("image:saved"));
+forward($image->getURL());
diff --git a/mod/lightpics/actions/photos/image/upload.php b/mod/lightpics/actions/photos/image/upload.php
new file mode 100644
index 000000000..268712666
--- /dev/null
+++ b/mod/lightpics/actions/photos/image/upload.php
@@ -0,0 +1,147 @@
+<?php
+/**
+ * Multi-image uploader action
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+elgg_load_library('tidypics:upload');
+$img_river_view = elgg_get_plugin_setting('img_river_view', 'tidypics');
+
+$guid = (int) get_input('guid');
+$album = get_entity($guid);
+if (!$album) {
+ register_error(elgg_echo('tidypics:baduploadform'));
+ forward(REFERER);
+}
+
+// post limit exceeded
+if (count($_FILES) == 0) {
+ trigger_error('Tidypics warning: user exceeded post limit on image upload', E_USER_WARNING);
+ register_error(elgg_echo('tidypics:exceedpostlimit'));
+ forward(REFERER);
+}
+
+// test to make sure at least 1 image was selected by user
+$num_images = 0;
+foreach($_FILES['images']['name'] as $name) {
+ if (!empty($name)) {
+ $num_images++;
+ }
+}
+if ($num_images == 0) {
+ // have user try again
+ register_error(elgg_echo('tidypics:noimages'));
+ forward(REFERER);
+}
+
+// create the image object for each upload
+$uploaded_images = array();
+$not_uploaded = array();
+$error_msgs = array();
+foreach ($_FILES['images']['name'] as $index => $value) {
+ $data = array();
+ foreach ($_FILES['images'] as $key => $values) {
+ $data[$key] = $values[$index];
+ }
+
+ if (empty($data['name'])) {
+ continue;
+ }
+ $name = htmlspecialchars($data['name'], ENT_QUOTES, 'UTF-8', false);
+
+ $mime = tp_upload_get_mimetype($name);
+
+ $image = new TidypicsImage();
+ $image->title = $name;
+ $image->container_guid = $album->getGUID();
+ $image->setMimeType($mime);
+ $image->access_id = $album->access_id;
+
+ try {
+ $result = $image->save($data);
+ } catch (Exception $e) {
+ array_push($not_uploaded, $name);
+ array_push($error_msgs, $e->getMessage());
+ }
+
+ if ($result) {
+ array_push($uploaded_images, $image->getGUID());
+
+ if ($img_river_view == "all") {
+ add_to_river('river/object/image/create', 'create', $image->getOwnerGUID(), $image->getGUID());
+ }
+ }
+}
+
+if (count($uploaded_images)) {
+ // Create a new batch object to contain these photos
+ $batch = new ElggObject();
+ $batch->subtype = "tidypics_batch";
+ $batch->access_id = $album->access_id;
+ $batch->container_guid = $album->getGUID();
+ if ($batch->save()) {
+ foreach ($uploaded_images as $uploaded_guid) {
+ add_entity_relationship($uploaded_guid, "belongs_to_batch", $batch->getGUID());
+ }
+ }
+
+ $album->prependImageList($uploaded_images);
+
+ // "added images to album" river
+ if ($img_river_view == "batch" && $album->new_album == false) {
+ add_to_river('river/object/tidypics_batch/create', 'create', $batch->getOwnerGUID(), $batch->getGUID());
+ }
+
+ // "created album" river
+ if ($album->new_album) {
+ $album->new_album = false;
+ $album->first_upload = true;
+
+ add_to_river('river/object/album/create', 'create', $album->getOwnerGUID(), $album->getGUID());
+
+ // "created album" notifications
+ // we throw the notification manually here so users are not told about the new album until
+ // there are at least a few photos in it
+ if ($album->shouldNotify()) {
+ object_notifications('create', 'object', $album);
+ $album->last_notified = time();
+ }
+ } else {
+ // "added image to album" notifications
+ if ($album->first_upload) {
+ $album->first_upload = false;
+ }
+
+ if ($album->shouldNotify()) {
+ object_notifications('create', 'object', $album);
+ $album->last_notified = time();
+ }
+ }
+}
+
+if (count($not_uploaded) > 0) {
+ if (count($uploaded_images) > 0) {
+ $error = sprintf(elgg_echo("tidypics:partialuploadfailure"), count($not_uploaded), count($not_uploaded) + count($uploaded_images)) . '<br />';
+ } else {
+ $error = elgg_echo("tidypics:completeuploadfailure") . '<br />';
+ }
+
+ $num_failures = count($not_uploaded);
+ for ($i = 0; $i < $num_failures; $i++) {
+ $error .= "{$not_uploaded[$i]}: {$error_msgs[$i]} <br />";
+ }
+ register_error($error);
+
+ if (count($uploaded_images) == 0) {
+ //upload failed, so forward to previous page
+ forward(REFERER);
+ } else {
+ // some images did upload so we fall through
+ }
+} else {
+ system_message(elgg_echo('tidypics:upl_success'));
+}
+
+forward("photos/edit/$batch->guid");
diff --git a/mod/lightpics/activate.php b/mod/lightpics/activate.php
new file mode 100644
index 000000000..752420a4c
--- /dev/null
+++ b/mod/lightpics/activate.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Activate Tidypics
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+// register classes
+if (get_subtype_id('object', 'album')) {
+ update_subtype('object', 'album', 'TidypicsAlbum');
+} else {
+ add_subtype('object', 'album', 'TidypicsAlbum');
+}
+if (get_subtype_id('object', 'image')) {
+ update_subtype('object', 'image', 'TidypicsImage');
+} else {
+ add_subtype('object', 'image', 'TidypicsImage');
+}
+
+// set default settings
+
+$image_sizes = array();
+$image_sizes['large_image_width'] = $image_sizes['large_image_height'] = 600;
+$image_sizes['small_image_width'] = $image_sizes['small_image_height'] = 153;
+$image_sizes['tiny_image_width'] = $image_sizes['tiny_image_height'] = 60;
+$image_sizes = serialize($image_sizes);
+
+$defaults = array(
+ 'tagging' => true,
+ 'view_count' => true,
+ 'uploader' => true,
+ 'exif' => true,
+ 'download_link' => true,
+
+ 'maxfilesize' => 5,
+ 'image_lib' => 'GD',
+
+ 'img_river_view' => 'batch',
+ 'album_river_view' => 'set',
+
+ 'image_sizes' => $image_sizes,
+
+ 'notify_interval' => 60 * 60 * 24,
+);
+
+foreach ($defaults as $name => $value) {
+ if (elgg_get_plugin_setting($name, 'lightpics') === null) {
+ elgg_set_plugin_setting($name, $value, 'lightpics');
+ }
+}
diff --git a/mod/lightpics/classes/TidypicsAlbum.php b/mod/lightpics/classes/TidypicsAlbum.php
new file mode 100644
index 000000000..0cb8bd84e
--- /dev/null
+++ b/mod/lightpics/classes/TidypicsAlbum.php
@@ -0,0 +1,375 @@
+<?php
+/**
+ * Tidypics Album class
+ *
+ * @package TidypicsAlbum
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+
+class TidypicsAlbum extends ElggObject {
+ /**
+ * Sets the internal attributes
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['subtype'] = "album";
+ }
+
+ /**
+ * Constructor
+ * @param mixed $guid
+ */
+ public function __construct($guid = null) {
+ parent::__construct($guid);
+ }
+
+ /**
+ * Save an album
+ *
+ * @return bool
+ */
+ public function save() {
+
+ if (!isset($this->new_album)) {
+ $this->new_album = true;
+ }
+
+ if (!isset($this->last_notified)) {
+ $this->last_notified = 0;
+ }
+
+ if (!parent::save()) {
+ return false;
+ }
+
+ mkdir(tp_get_img_dir() . $this->guid, 0755, true);
+
+ elgg_trigger_event('create', 'album', $this);
+
+ return true;
+ }
+
+ /**
+ * Delete album
+ *
+ * @return bool
+ */
+ public function delete() {
+
+ $this->deleteImages();
+ $this->deleteAlbumDir();
+
+ return parent::delete();
+ }
+
+ /**
+ * Get the title of the photo album
+ *
+ * @return string
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * Get the URL for this album
+ *
+ * @return string
+ */
+ public function getURL() {
+ $title = elgg_get_friendly_title($this->getTitle());
+ $url = "photos/album/$this->guid/$title";
+ return elgg_normalize_url($url);
+ }
+
+ /**
+ * Get an array of image objects
+ *
+ * @param int $limit
+ * @param int $offset
+ * @return array
+ */
+ public function getImages($limit, $offset = 0) {
+ $imageList = $this->getImageList();
+ if ($offset > count($imageList)) {
+ return array();
+ }
+
+ $imageList = array_slice($imageList, $offset, $limit);
+
+ $images = array();
+ foreach ($imageList as $guid) {
+ $images[] = get_entity($guid);
+ }
+ return $images;
+ }
+
+ /**
+ * View a list of images
+ *
+ * @param array $options Options to pass to elgg_view_entity_list()
+ * @return string
+ */
+ public function viewImages(array $options = array()) {
+ $count = $this->getSize();
+
+ if ($count == 0) {
+ return '';
+ }
+
+ $defaults = array(
+ 'count' => $count,
+ 'limit' => 16,
+ 'offset' => max(get_input('offset'), 0),
+ 'full_view' => false,
+ 'list_type' => 'gallery',
+ 'list_type_toggle' => false,
+ 'pagination' => true,
+ 'gallery_class' => 'tidypics-gallery elgg-lightbox-gallery',
+ );
+
+ $options = array_merge($defaults, (array) $options);
+ $images = $this->getImages($options['limit'], $options['offset']);
+
+ if (count($images) == 0) {
+ return '';
+ }
+
+ return elgg_view_entity_list($images, $options);
+ }
+
+ /**
+ * Returns the cover image entity
+ * @return TidypicsImage
+ */
+ public function getCoverImage() {
+ return get_entity($this->getCoverImageGuid());
+ }
+
+ /**
+ * Get the GUID of the album cover
+ *
+ * @return int
+ */
+ public function getCoverImageGuid() {
+ if ($this->getSize() == 0) {
+ return 0;
+ }
+
+ $guid = $this->cover;
+ $imageList = $this->getImageList();
+ if (!in_array($guid, $imageList)) {
+ // select random photo to be cover
+ $index = array_rand($imageList, 1);
+ $guid = $imageList[$index];
+ $this->cover = $guid;
+ }
+ return $guid;
+ }
+
+ /**
+ * Set the GUID for the album cover
+ *
+ * @param int $guid
+ * @return bool
+ */
+ public function setCoverImageGuid($guid) {
+ $imageList = $this->getImageList();
+ if (!in_array($guid, $imageList)) {
+ return false;
+ }
+ $this->cover = $guid;
+ return true;
+ }
+
+ /**
+ * Get the number of photos in the album
+ *
+ * @return int
+ */
+ public function getSize() {
+ return count($this->getImageList());
+ }
+
+ /**
+ * Returns an order list of image guids
+ *
+ * @return array
+ */
+ public function getImageList() {
+ $listString = $this->orderedImages;
+ if (!$listString) {
+ return array();
+ }
+ $list = unserialize($listString);
+
+ // if empty don't need to check the permissions.
+ if (!$list) {
+ return array();
+ }
+
+ // check access levels
+ $guidsString = implode(',', $list);
+
+ $options = array(
+ 'wheres' => array("e.guid IN ($guidsString)"),
+ 'order_by' => "FIELD(e.guid, $guidsString)",
+ 'callback' => 'tp_guid_callback',
+ 'limit' => ELGG_ENTITIES_NO_VALUE
+ );
+
+ $list = elgg_get_entities($options);
+ return $list;
+ }
+
+ /**
+ * Sets the album image order
+ *
+ * @param array $list An indexed array of image guids
+ * @return bool
+ */
+ public function setImageList($list) {
+ // validate data
+ foreach ($list as $guid) {
+ if (!filter_var($guid, FILTER_VALIDATE_INT)) {
+ return false;
+ }
+ }
+
+ $listString = serialize($list);
+ $this->orderedImages = $listString;
+ return true;
+ }
+
+ /**
+ * Add new images to the front of the image list
+ *
+ * @param array $list An indexed array of image guids
+ * @return bool
+ */
+ public function prependImageList($list) {
+ $currentList = $this->getImageList();
+ $list = array_merge($list, $currentList);
+ return $this->setImageList($list);
+ }
+
+ /**
+ * Get the previous image in the album. Wraps around to the last image if given the first.
+ *
+ * @param int $guid GUID of the current image
+ * @return TidypicsImage
+ */
+ public function getPreviousImage($guid) {
+ $imageList = $this->getImageList();
+ $key = array_search($guid, $imageList);
+ if ($key === FALSE) {
+ return null;
+ }
+ $key--;
+ if ($key < 0) {
+ return get_entity(end($imageList));
+ }
+ return get_entity($imageList[$key]);
+ }
+
+ /**
+ * Get the next image in the album. Wraps around to the first image if given the last.
+ *
+ * @param int $guid GUID of the current image
+ * @return TidypicsImage
+ */
+ public function getNextImage($guid) {
+ $imageList = $this->getImageList();
+ $key = array_search($guid, $imageList);
+ if ($key === FALSE) {
+ return null;
+ }
+ $key++;
+ if ($key >= count($imageList)) {
+ return get_entity($imageList[0]);
+ }
+ return get_entity($imageList[$key]);
+ }
+
+ /**
+ * Get the index into the album for a particular image
+ *
+ * @param int $guid GUID of the image
+ * @return int
+ */
+ public function getIndex($guid) {
+ return array_search($guid, $this->getImageList()) + 1;
+ }
+
+ /**
+ * Remove an image from the album list
+ *
+ * @param int $imageGuid
+ * @return bool
+ */
+ public function removeImage($imageGuid) {
+ $imageList = $this->getImageList();
+ $key = array_search($imageGuid, $imageList);
+ if ($key === false) {
+ return false;
+ }
+
+ unset($imageList[$key]);
+ $this->setImageList($imageList);
+
+ return true;
+ }
+
+ /**
+ * Has enough time elapsed between the last_notified and notify_interval setting?
+ *
+ * @return bool
+ */
+ public function shouldNotify() {
+ return time() - $this->last_notified > elgg_get_plugin_setting('notify_interval', 'tidypics');
+ }
+
+ /**
+ * Delete all the images in this album
+ *
+ * @todo ElggBatch?
+ */
+ protected function deleteImages() {
+ $images = elgg_get_entities(array(
+ "type=" => "object",
+ "subtype" => "image",
+ "container_guid" => $this->guid,
+ "limit" => ELGG_ENTITIES_NO_VALUE,
+ ));
+ if ($images) {
+ foreach ($images as $image) {
+ if ($image) {
+ $image->delete();
+ }
+ }
+ }
+ }
+
+ /**
+ * Delete the album directory on disk
+ */
+ protected function deleteAlbumDir() {
+ $tmpfile = new ElggFile();
+ $tmpfile->setFilename('image/' . $this->guid . '/._tmp_del_tidypics_album_');
+ $tmpfile->subtype = 'image';
+ $tmpfile->owner_guid = $this->owner_guid;
+ $tmpfile->container_guid = $this->guid;
+ $tmpfile->open("write");
+ $tmpfile->write('');
+ $tmpfile->close();
+ $tmpfile->save();
+ $albumdir = eregi_replace('/._tmp_del_tidypics_album_', '', $tmpfile->getFilenameOnFilestore());
+ $tmpfile->delete();
+ if (is_dir($albumdir)) {
+ rmdir($albumdir);
+ }
+ }
+}
diff --git a/mod/lightpics/classes/TidypicsImage.php b/mod/lightpics/classes/TidypicsImage.php
new file mode 100644
index 000000000..5a8d42ccc
--- /dev/null
+++ b/mod/lightpics/classes/TidypicsImage.php
@@ -0,0 +1,406 @@
+<?php
+/**
+ * Tidypics Image class
+ *
+ * @package TidypicsImage
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+
+class TidypicsImage extends ElggFile {
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['subtype'] = "image";
+ }
+
+ public function __construct($guid = null) {
+ parent::__construct($guid);
+ }
+
+ /**
+ * Save the image
+ *
+ * @warning container_guid must be set first
+ *
+ * @param array $data
+ * @return bool
+ */
+ public function save($data = null) {
+
+ if (!parent::save()) {
+ return false;
+ }
+
+ if ($data) {
+ // new image
+ $this->simpletype = "image";
+ $this->saveImageFile($data);
+ $this->saveThumbnails();
+ $this->extractExifData();
+ }
+
+ return true;
+ }
+
+ /**
+ * Delete image
+ *
+ * @return bool
+ */
+ public function delete() {
+
+ // check if batch should be deleted
+ $batch = elgg_get_entities_from_relationship(array(
+ 'relationship' => 'belongs_to_batch',
+ 'relationship_guid' => $this->guid,
+ 'inverse_relationship' => false,
+ ));
+ if ($batch) {
+ $batch = $batch[0];
+ $count = elgg_get_entities_from_relationship(array(
+ 'relationship' => 'belongs_to_batch',
+ 'relationship_guid' => $batch->guid,
+ 'inverse_relationship' => true,
+ 'count' => true,
+ ));
+ if ($count == 1) {
+ // last image so delete batch
+ $batch->delete();
+ }
+ }
+
+ $album = get_entity($this->container_guid);
+ if ($album) {
+ $album->removeImage($this->guid);
+ }
+
+ $this->removeThumbnails();
+
+ // update quota
+ $owner = $this->getOwnerEntity();
+ $owner->image_repo_size = (int)$owner->image_repo_size - $this->size();
+
+ return parent::delete();
+ }
+
+ /**
+ * Get the title of the image
+ *
+ * @return string
+ */
+ public function getTitle() {
+ if ($this->title) {
+ return $this->title;
+ } else {
+ return $this->originalfilename;
+ }
+ }
+
+ /**
+ * Get the URL for the web page of this image
+ *
+ * @return string
+ */
+ public function getURL() {
+ $title = elgg_get_friendly_title($this->getTitle());
+ $url = "photos/image/$this->guid/$title";
+ return elgg_normalize_url($url);
+ }
+
+ /**
+ * Get the src URL for the image
+ *
+ * @return string
+ */
+ public function getIconURL($size = 'small') {
+ if ($size == 'tiny') {
+ $size = 'thumb';
+ }
+ return elgg_normalize_url("photos/thumbnail/$this->guid/$size/");
+ }
+
+ /**
+ * Get the view information for this image
+ *
+ * @param $viewer_guid The guid of the viewer
+ * @return array with number of views, number of unique viewers, and number of views for this viewer
+ */
+ public function getViewInfo($viewer_guid = 0) {
+ if ($viewer_guid == 0) {
+ $viewer_guid = elgg_get_logged_in_user_guid();
+ }
+
+ $views = elgg_get_annotations(array(
+ 'guid' => $this->getGUID(),
+ 'annotation_name' => 'tp_view',
+ 'limit' => 0,
+ ));
+ if ($views) {
+ $total_views = count($views);
+
+ if ($this->getOwnerGUID() == $viewer_guid) {
+ // get unique number of viewers
+ $diff_viewers = array();
+ foreach ($views as $view) {
+ $diff_viewers[$view->owner_guid] = 1;
+ }
+ $unique_viewers = count($diff_viewers);
+ } else if ($viewer_guid) {
+ // get the number of times this user has viewed the photo
+ $my_views = 0;
+ foreach ($views as $view) {
+ if ($view->owner_guid == $viewer_guid) {
+ $my_views++;
+ }
+ }
+ }
+
+ $view_info = array("total" => $total_views, "unique" => $unique_viewers, "mine" => $my_views);
+ }
+ else {
+ $view_info = array("total" => 0, "unique" => 0, "mine" => 0);
+ }
+
+ return $view_info;
+ }
+
+ /**
+ * Add a view to this image
+ *
+ * @param $viewer_guid
+ * @return void
+ */
+ public function addView($viewer_guid = 0) {
+ if ($viewer_guid == 0) {
+ $viewer_guid = elgg_get_logged_in_user_guid();
+ }
+
+ if ($viewer_guid != $this->owner_guid && tp_is_person()) {
+ create_annotation($this->getGUID(), "tp_view", "1", "integer", $viewer_guid, ACCESS_PUBLIC);
+ }
+ }
+
+
+ /**
+ * Set the internal filenames
+ */
+ protected function setOriginalFilename($originalName) {
+ $prefix = "image/" . $this->container_guid . "/";
+ $filestorename = elgg_strtolower(time() . $originalName);
+ $this->setFilename($prefix . $filestorename);
+ $this->originalfilename = $originalName;
+ }
+
+ /**
+ * Save the uploaded image
+ *
+ * @param array $data
+ */
+ protected function saveImageFile($data) {
+ $this->checkUploadErrors($data);
+
+ // we need to make sure the directory for the album exists
+ // @note for group albums, the photos are distributed among the users
+ $dir = tp_get_img_dir() . $this->getContainerGUID();
+ if (!file_exists($dir)) {
+ mkdir($dir, 0755, true);
+ }
+
+ // move the uploaded file into album directory
+ $this->setOriginalFilename($data['name']);
+ $filename = $this->getFilenameOnFilestore();
+ $result = move_uploaded_file($data['tmp_name'], $filename);
+ if (!$result) {
+ return false;
+ }
+
+ $owner = $this->getOwnerEntity();
+ $owner->image_repo_size = (int)$owner->image_repo_size + $this->size();
+
+ return true;
+ }
+
+ /**
+ * Need to restore sanity to this function
+ * @param type $data
+ */
+ protected function checkUploadErrors($data) {
+ // check for upload errors
+ if ($data['error']) {
+ if ($data['error'] == 1) {
+ trigger_error('Tidypics warning: image exceeded server php upload limit', E_USER_WARNING);
+ throw new Exception(elgg_echo('tidypics:image_mem'));
+ } else {
+ throw new Exception(elgg_echo('tidypics:unk_error'));
+ }
+ }
+
+ // must be an image
+ if (!tp_upload_check_format($data['type'])) {
+ throw new Exception(elgg_echo('tidypics:not_image'));
+ }
+
+ // make sure file does not exceed memory limit
+ if (!tp_upload_check_max_size($data['size'])) {
+ throw new Exception(elgg_echo('tidypics:image_mem'));
+ }
+
+ // make sure the in memory image size does not exceed memory available
+ $imginfo = getimagesize($data['tmp_name']);
+ if (!tp_upload_memory_check($image_lib, $imginfo[0] * $imginfo[1])) {
+ trigger_error('Tidypics warning: image memory size too large for resizing so rejecting', E_USER_WARNING);
+ throw new Exception(elgg_echo('tidypics:image_pixels'));
+ }
+
+ // make sure file fits quota
+ if (!tp_upload_check_quota($data['size'], elgg_get_logged_in_user_guid())) {
+ throw new Exception(elgg_echo('tidypics:cannot_upload_exceeds_quota'));
+ }
+ }
+
+ /**
+ * Save the image thumbnails
+ */
+ protected function saveThumbnails() {
+ elgg_load_library('tidypics:resize');
+
+ $imageLib = elgg_get_plugin_setting('image_lib', 'tidypics');
+
+ $prefix = "image/" . $this->container_guid . "/";
+ $filename = $this->getFilename();
+ $filename = substr($filename, strrpos($filename, '/') + 1);
+
+ if ($imageLib == 'ImageMagick') {
+ // ImageMagick command line
+ if (tp_create_im_cmdline_thumbnails($this, $prefix, $filename) != true) {
+ trigger_error('Tidypics warning: failed to create thumbnails - ImageMagick command line', E_USER_WARNING);
+ }
+ } else if ($imageLib == 'ImageMagickPHP') {
+ // imagick php extension
+ if (tp_create_imagick_thumbnails($this, $prefix, $filename) != true) {
+ trigger_error('Tidypics warning: failed to create thumbnails - ImageMagick PHP', E_USER_WARNING);
+ }
+ } else {
+ if (tp_create_gd_thumbnails($this, $prefix, $filename) != true) {
+ trigger_error('Tidypics warning: failed to create thumbnails - GD', E_USER_WARNING);
+ }
+ }
+ }
+
+ /**
+ * Get the image data of a thumbnail
+ *
+ * @param string $size
+ * @return string
+ */
+ public function getThumbnail($size) {
+ switch ($size) {
+ case 'thumb':
+ $thumb = $this->thumbnail;
+ break;
+ case 'small':
+ $thumb = $this->smallthumb;
+ break;
+ case 'large':
+ $thumb = $this->largethumb;
+ break;
+ default:
+ return '';
+ break;
+ }
+
+ if (!$thumb) {
+ return '';
+ }
+
+ $file = new ElggFile();
+ $file->owner_guid = $this->getOwnerGUID();
+ $file->setFilename($thumb);
+ return $file->grabFile();
+ }
+
+ public function getImage() {
+ return $this->grabFile();
+ }
+
+ /**
+ * Extract EXIF Data from image
+ *
+ * @warning image file must be saved first
+ */
+ public function extractExifData() {
+ elgg_load_library('tidypics:exif');
+ td_get_exif($this);
+ }
+
+ /**
+ * Has the photo been tagged with "in this photo" tags
+ *
+ * @return true/false
+ */
+ public function isPhotoTagged() {
+ $num_tags = count_annotations($this->getGUID(), 'object', 'image', 'phototag');
+ if ($num_tags > 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get an array of photo tag information
+ *
+ * @return array
+ */
+ public function getPhotoTags() {
+
+ $tags = array();
+ $annotations = elgg_get_annotations(array(
+ 'guid' => $this->getGUID(),
+ 'annotation_name' => 'phototag',
+ ));
+ foreach ($annotations as $annotation) {
+ $tag = unserialize($annotation->value);
+ $tag->annotation_id = $annotation->id;
+ $tags[] = $tag;
+ }
+
+ return $tags;
+ }
+
+ /**
+ * Remove thumbnails - usually in preparation for deletion
+ *
+ * The thumbnails are not actually ElggObjects so we create
+ * temporary objects to delete them.
+ */
+ protected function removeThumbnails() {
+ $thumbnail = $this->thumbnail;
+ $smallthumb = $this->smallthumb;
+ $largethumb = $this->largethumb;
+
+ //delete standard thumbnail image
+ if ($thumbnail) {
+ $delfile = new ElggFile();
+ $delfile->owner_guid = $this->getOwnerGUID();
+ $delfile->setFilename($thumbnail);
+ $delfile->delete();
+ }
+ //delete small thumbnail image
+ if ($smallthumb) {
+ $delfile = new ElggFile();
+ $delfile->owner_guid = $this->getOwnerGUID();
+ $delfile->setFilename($smallthumb);
+ $delfile->delete();
+ }
+ //delete large thumbnail image
+ if ($largethumb) {
+ $delfile = new ElggFile();
+ $delfile->owner_guid = $this->getOwnerGUID();
+ $delfile->setFilename($largethumb);
+ $delfile->delete();
+ }
+ }
+}
diff --git a/mod/lightpics/contributions.txt b/mod/lightpics/contributions.txt
new file mode 100644
index 000000000..f12a4713b
--- /dev/null
+++ b/mod/lightpics/contributions.txt
@@ -0,0 +1,442 @@
+Changelog and contributors list
+
+------------------------------------------------------------------------
+Version 1.8.0 RC1 Change List
+Release Date: 07/14/2012
+------------------------------------------------------------------------
+BEGIN VERSION 1.8.0 RC1 CHANGES
+------------------------------------------------------------------------
+ * Updated for Elgg 1.8
+Contributors:
+ * Cash Costello
+ * Brett Profitt
+------------------------------------------------------------------------
+END VERSION 1.7.3 CHANGES
+------------------------------------------------------------------------
+
+------------------------------------------------------------------------
+Version 1.7.3 Change List
+Release Date: 11/09/2011
+------------------------------------------------------------------------
+BEGIN VERSION 1.7.3 CHANGES
+------------------------------------------------------------------------
+ * Removed admin settings in favor of Tidypics Administration
+ * Add two river strings for those using Elgg 1.8
+ * Fixed bug where the batch object was not being deleted when had 0 images
+------------------------------------------------------------------------
+END VERSION 1.7.3 CHANGES
+------------------------------------------------------------------------
+
+------------------------------------------------------------------------
+Version 1.7.2 Change List
+Release Date: 10/11/2011
+------------------------------------------------------------------------
+BEGIN VERSION 1.7.2 CHANGES
+------------------------------------------------------------------------
+ * Fixed bug with admin tabs
+ * Fixed bug with pagination on all site albums page
+ * New installs get default settings automatically added
+ * Added a server check for cookie-only sessions
+------------------------------------------------------------------------
+END VERSION 1.7.2 CHANGES
+------------------------------------------------------------------------
+
+------------------------------------------------------------------------
+Version 1.7.1 Change List
+Release Date: 07/10/2011
+------------------------------------------------------------------------
+BEGIN VERSION 1.7.1 CHANGES
+------------------------------------------------------------------------
+ * Added a toggle for enabling the Flash uploader
+ * Fixed bug with slideshow not displaying all images in an album
+ * Fixed bug with only 8 images showing on an album page. Default is 16.
+------------------------------------------------------------------------
+END VERSION 1.7.1 CHANGES
+------------------------------------------------------------------------
+
+------------------------------------------------------------------------
+Version 1.7.0 Change List
+Final Release Date: 06/05/2011
+Beta 1 Release Date: 11/06/2010
+Contributors:
+ * Cash Costello
+ * Jeff Tilson
+ * Original Flash code by Torsten Wesolek
+ * language files submitted by gillie and lord55
+------------------------------------------------------------------------
+BEGIN VERSION 1.7.0 CHANGES
+------------------------------------------------------------------------
+ * Add Flash uploader
+ * Added album sorting
+ * Added batch upload river entries
+ * Fixed bug where old installs of Tidypics would not upgrade
+ * Fixed bug in activity river with link on image entry
+ * Fixed tag pop-up box display issue
+ * Fixed display of avatars in tag river entries
+ * Fixed 2 widget bugs
+ * Fixed bug with images that have national characters in filename
+------------------------------------------------------------------------
+END VERSION 1.7.0 CHANGES
+------------------------------------------------------------------------
+
+------------------------------------------------------------------------
+Version 1.6.8 Change List
+Release Date: 05/01/2010
+------------------------------------------------------------------------
+BEGIN VERSION 1.6.8 CHANGES
+------------------------------------------------------------------------
+ * New language strings: tidypics:nophotosingroup, tidypics:baduploadform
+ tidypics:settings:heading:img_lib, tidypics:settings:heading:main,
+ tidypics:settings:heading:river, tidypics:settings:heading:sizes,
+ tidypics:settings:heading:groups
+ * Fixed bug in titles of albums RSS
+ * Added a new css element: tidypics_line_break
+ * Fixed layout of album covers due to long titles or other text
+ * Fixed some bugs with image resizing when images are wide and short
+ * Improved interface of admin settings
+------------------------------------------------------------------------
+END VERSION 1.6.8 CHANGES
+------------------------------------------------------------------------
+
+------------------------------------------------------------------------
+Version 1.6.7 Change List
+Release Date: 04/24/2010
+------------------------------------------------------------------------
+BEGIN VERSION 1.6.7 CHANGES
+------------------------------------------------------------------------
+ * Added categories integration
+ * Added a better quota display message - includes percentage now
+ * Tweaked the css so that upload list is enumerated (#tidypics_image_upload_list)
+------------------------------------------------------------------------
+END VERSION 1.6.7 CHANGES
+------------------------------------------------------------------------
+
+------------------------------------------------------------------------
+Version 1.6.6 Change List
+Release Date: 03/06/2010
+------------------------------------------------------------------------
+BEGIN VERSION 1.6.6 CHANGES
+------------------------------------------------------------------------
+ * Tweaked slideshow start code
+ * Fixed bug with delete tags menu item
+ * Fixed bug in en.php
+ * Better error checking for ImageMagick resizing
+------------------------------------------------------------------------
+END VERSION 1.6.6 CHANGES
+------------------------------------------------------------------------
+
+------------------------------------------------------------------------
+Version 1.6.5 Change List
+Release Date: 12/12/2009
+------------------------------------------------------------------------
+BEGIN VERSION 1.6.5 CHANGES
+------------------------------------------------------------------------
+ * Bug fixes
+ * Better plugin hooks for adding slideshows and lightboxes
+ * Better display of album pages and tweaked css
+ * Slideshows available on more pages
+ * Made a change to parameters in tp_list_entities - added container
+ * Added Danish translation from erikernstm
+ * Added French translation from Fx Nion
+------------------------------------------------------------------------
+END VERSION 1.6.5 CHANGES
+------------------------------------------------------------------------
+
+
+------------------------------------------------------------------------
+Version 1.6.4 Change List
+Release Date: 10/05/2009
+------------------------------------------------------------------------
+BEGIN VERSION 1.6.4 CHANGES
+------------------------------------------------------------------------
+ * Bug fixes
+ * Improved watermarking
+ * Add slideshow code by Torsten
+------------------------------------------------------------------------
+END VERSION 1.6.4 CHANGES
+------------------------------------------------------------------------
+
+------------------------------------------------------------------------
+Version 1.6.3 Change List
+Release Date: 09/13/2009
+------------------------------------------------------------------------
+BEGIN VERSION 1.6.3 CHANGES
+------------------------------------------------------------------------
+ * Bug fixes
+ * Redo image resize if problem occurred
+ * Grab title from image by default
+ * New German language file by alfalive
+ * Preview of watermarking
+------------------------------------------------------------------------
+END VERSION 1.6.3 CHANGES
+------------------------------------------------------------------------
+
+------------------------------------------------------------------------
+Version 1.6.2 Change List
+Release Date: 09/03/2009
+------------------------------------------------------------------------
+BEGIN VERSION 1.6.2 CHANGES
+------------------------------------------------------------------------
+ * Added widget for user's latest photos
+ * Added notifications to a user if that user is tagged in a photo
+ * Fixed some issues with tagging people in albums that belong to a closed group
+ * Added a test page for ImageMagick commandline
+ * Began restructuring the code for future capabilities
+ * Many more bug fixes and tweaks
+------------------------------------------------------------------------
+END VERSION 1.6.2 CHANGES
+------------------------------------------------------------------------
+
+
+------------------------------------------------------------------------
+Version 1.6.1 Change List
+Release Date: 08/04/2009
+------------------------------------------------------------------------
+BEGIN VERSION 1.6.1 CHANGES
+------------------------------------------------------------------------
+ * Fixed css bug that caused problems with tagging
+ * Added Spanish language file from mylamilagros
+------------------------------------------------------------------------
+END VERSION 1.6.1 CHANGES
+------------------------------------------------------------------------
+
+
+------------------------------------------------------------------------
+Version 1.6.0 Change List
+Final Release Date: 08/02/2009
+Beta 4 Release Date: 07/17/2009
+Beta 3 Release Date: 07/16/2009
+Beta 2 Release Date: 07/10/2009
+Beta 1 Release Date: 06/30/2009
+Contributors:
+ * Cash Costello
+ * Greg Froese
+ * tagging based on code by Pedro Prez
+ * RSS improvements by Torsten Wesolek
+ * language files submitted by Toto and Susan Tsairi
+------------------------------------------------------------------------
+BEGIN VERSION 1.6.0 CHANGES
+------------------------------------------------------------------------
+ * Photo tagging
+ * View counter
+ * EXIF view
+ * ImageMagick support
+ * Extensive admin settings
+ * Improved CSS
+ * Image ratings
+ * Upload quotas
+ * Better album RSS feed
+ * better almost everything
+------------------------------------------------------------------------
+END VERSION 1.6.0 CHANGES
+------------------------------------------------------------------------
+
+
+------------------------------------------------------------------------
+Version 1.5.1 Change List
+Release Date: 03/29/2009
+Contributors:
+ * Gabriel Monge-Franco (http://gabriel.mongefranco.com)
+ * Cash Costello
+ * VeniVidiVinco (http://community.elgg.org/pg/profile/VeniVidiVinco)
+------------------------------------------------------------------------
+BEGIN VERSION 1.5.1 CHANGES
+------------------------------------------------------------------------
+* Updated Turkish language file
+* Fixed permissions bug on all content caused by group override
+* Added back the create album button to widgets
+------------------------------------------------------------------------
+END VERSION 1.5.1 CHANGES
+------------------------------------------------------------------------
+
+
+------------------------------------------------------------------------
+Version 1.5 Change List
+Release Date: 03/28/2009
+Contributors:
+ * Gabriel Monge-Franco (http://gabriel.mongefranco.com)
+ * Cash Costello
+ * alfalive (http://community.elgg.org/pg/profile/alfalive)
+ * VeniVidiVinco (http://community.elgg.org/pg/profile/VeniVidiVinco)
+
+Also thank you to those who tested the code in svn and provided feedback
+------------------------------------------------------------------------
+BEGIN VERSION 1.5 CHANGES
+------------------------------------------------------------------------
+* Fixed submenus on sidebar of album pages to make them consistent
+* Fixed display issues for Elgg 1.5
+* Using default site access now
+* Pushing new album creation to river
+* Fixed German language file
+* Added Turkish
+* Added notifications for new album creation
+* Rewrote most of the page handlers to make cleaner
+* Fixed the access level bug on images
+* Cleaned up css
+* Fixed the group access bug on albums
+------------------------------------------------------------------------
+END VERSION 1.5 CHANGES
+------------------------------------------------------------------------
+
+
+
+------------------------------------------------------------------------
+Version 1.08.2 Change List
+Release Date: 02/13/2008
+Contributors:
+ * Gabriel Monge-Franco (http://gabriel.mongefranco.com)
+------------------------------------------------------------------------
+BEGIN VERSION 1.08.2 CHANGES
+------------------------------------------------------------------------
+* Fixed bug introduced in v1.08.1 that broke the picture upload (thanks to Karsten Schulze).
+* Fixed empty variables in several files that were supposed to send the user back to the previous page, but didn't.
+* Cleaned up change log (contributions.txt).
+* Finally fixed the Polish language file!!! Yay!! :)
+
+* TODO: Work around large image (high resolution) limit when creating thumbnails (an Elgg limitation... can probably be fixed by using PHP GD functions directly or ImageMagic if available)
+ - The problem is with get_resized_image_from_existing_file(). Submitted a ticket to the Elgg tracking bug system.
+ - Edit actions/upload.php when done. There is a dirty hack in place for now that needs to be removed once the above bug is fixed.
+* TODO: disable public by default (input/accessRead.php) -- add setting to admin page that lets the site admin decide whether pictures can be public or not.
+* TODO: allow group members to add pictures to group photo albums
+* TODO: implement photo anotations
+* TODO: look into replacing individual image views with lightbox2 or jQuery lightbox album views.
+* TODO: create a new widget to show pictures (in slideshow) in a user's profile.
+* TODO: add settings for group albums so that the group owner can select the number of albums to show.
+* TODO: implement a way to move pictures from one album to another (should be pretty easy).
+* TODO: add an "Add Photos" option to the user widget similar to the one in the group widget.
+* TODO: implement automagic file resizing to meet the admin's maximum resolution and file size.
+
+* OF NOTE: It is currently not possible to allow group members to edit an album created by another group member. This is a bug with group permissions and it will probably not be fixed until [at least] Elgg v1.5 comes out.
+------------------------------------------------------------------------
+END VERSION 1.08.2 CHANGES
+------------------------------------------------------------------------
+
+
+------------------------------------------------------------------------
+Version 1.08.1 Change List
+Release Date: 02/13/2008
+Contributors:
+ * Gabriel Monge-Franco (http://gabriel.mongefranco.com)
+ * LKLaRose (http://community.elgg.org/pg/profile/lauren)
+ * Webgalli (http://community.elgg.org/pg/profile/webgalli)
+ * Karsten Schulze (http://ks.frinx.eu)
+ * Cash (http://community.elgg.org/pg/profile/costelloc)
+------------------------------------------------------------------------
+BEGIN VERSION 1.08.1 CHANGES
+------------------------------------------------------------------------
+* Fixed bug introduced in v1.08 that caused fake files to be created during thumbnail creation (thanks to Karsten Schulze).
+* Reverted to old directory structure that saves pictures to /image/ALBUMGUID/picGUID (v1.08 saved to /image/ALBUMGUID__picGUID, thus keeping everything in one folder).
+* Album directory is now deleted when the album is deleted (along with every picture inside of the album).
+* Reverted thumbnail and small thumbnail to use picture cropping instead of scaling, so that sites that already use Tidypics can keep a consisting look and feel.
+* Completed River integration by moving album and image directories to an "object" sub-directory (thanks to LKLaRose).
+* Added status messages/graphics to give the user some feedback.
+* Fixed bug in profile widget whereby the selected number of albums had no effect (thanks to @Webgalli and @cheltenham).
+* Fixed bug introduced in v1.06 that prevented anonymous users from viewing a user's album (thanks to Karsten Schulze).
+* Fixed more language file bugs (thanks to Karsten Schulze).
+* Administrators can now set a file size limit from the plugin configuration (it has to be in Kilobytes).
+
+* JUST PLAIN WEIRD: Fixed Polish language file (thanks to @Vazco, Ian Medlock and Brett Profitt) and saved as UTF8 without BOM. However, it still doesn't work. Can somebody try saving it on a Polish box for me? :)
+
+* TODO: fix Polish language file -- it seems to break the whole Elgg site! Maybe it needs to use HTML codes in place of international characters?
+* TODO: Work around large image (high resolution) limit when creating thumbnails (an Elgg limitation... can probably be fixed by using PHP GD functions directly or ImageMagic if available)
+ - The problem is with get_resized_image_from_existing_file(). Submitted a ticket to the Elgg tracking bug system.
+ - Edit actions/upload.php when done. There is a dirty hack in place for now that needs to be removed once the above bug is fixed.
+* TODO: disable public by default (input/accessRead.php) -- add setting to admin page that lets the site admin decide whether pictures can be public or not.
+* TODO: allow group members to add pictures to group photo albums
+* TODO: implement photo anotations
+* TODO: look into replacing individual image views with lightbox2 or jQuery lightbox album views.
+* TODO: create a new widget to show pictures (in slideshow) in a user's profile.
+* TODO: add settings for group albums so that the group owner can select the number of albums to show.
+* TODO: implement a way to move pictures from one album to another (should be pretty easy).
+
+* OF NOTE: It is currently not possible to allow group members to edit an album created by another group member. This is a bug with group permissions and it will probably not be fixed until [at least] Elgg v1.5 comes out.
+------------------------------------------------------------------------
+END VERSION 1.08.1 CHANGES
+------------------------------------------------------------------------
+
+
+------------------------------------------------------------------------
+Version 1.08 Change List
+Release Date: 02/08/2008
+Contributors:
+ * Gabriel Monge-Franco (http://gabriel.mongefranco.com)
+------------------------------------------------------------------------
+BEGIN VERSION 1.08 CHANGES
+------------------------------------------------------------------------
+* Merged most (safe) changes made by other contributors in v1.07.
+ - Did not include SWFuploader or JQuery due to the number of bugs reported.
+* Changed ugly "new album" picture into something better looking.
+* Fixed bug whereby image objects were deleted from the database, but they physical files remained on the server.
+ - Since deleting directories was not possible without the use of unsafe file operations, albums no longer create new directories. Instead, all pictures are saved as /image/ALBUMGUID__picturename.
+* Fixed some bugs in the English, Spanish and German language files and added missing error messages.
+* Thumbnails now display the default error image (same as new album) when the requested image cannot be found.
+* Fixed a bug in the multi-edit form that did not allow image titles to be saved.
+* Removed some customizations made to edit.php action in v1.07 since they removed the ability to add titles to images.
+* Fixed bug introduced in v1.07 that prevented images from being set as album covers.
+* Fixed miscellaneous bugs in group albums widget.
+* Removed jQuery for now since it was making the albums too slow. In the future, we should look into replacing individual image views with lightbox2 or jQuery lightbox album views.
+
+
+* TODO: fix Polish language file -- it seems to break the whole Elgg site! Maybe it needs to use HTML codes in place of international characters?
+* TODO: Work around 0.9MB source file size limit when creating thumbnails (an Elgg limitation... can probably be fixed by using PHP GD functions directly)
+ - The problem is with get_resized_image_from_existing_file(). Submitted a ticket to the Elgg tracking bug system.
+ - Edit actions/upload.php when done. There is a dirty hack in place for now that needs to be removed once the above bug is fixed.
+* TODO: disable public by default (input/accessRead.php) -- add setting to admin page that lets the site admin decide whether pictures can be public or not.
+* TODO: allow group members to add pictures to group photo albums
+* TODO: implement photo anotations
+* TODO: look into replacing individual image views with lightbox2 or jQuery lightbox album views.
+
+* OF NOTE: It is currently not possible to allow group members to edit an album created by another group member. This is a bug with group permissions and it will probably not be fixed until [at least] Elgg v1.5 comes out.
+------------------------------------------------------------------------
+END VERSION 1.08 CHANGES
+------------------------------------------------------------------------
+
+
+------------------------------------------------------------------------
+Version 1.07 Change List
+Release Date: 02/03/2008
+Contributors:
+ * vazco (http://community.elgg.org/pg/profile/vazco)
+ * simophin (http://community.elgg.org/pg/profile/simophin)
+------------------------------------------------------------------------
+BEGIN VERSION 1.07 CHANGES
+------------------------------------------------------------------------
+* About 10 missing translations added
+* Translated to polish
+* When no album is present, user can add a new album straight from the widget if he has the required rights
+* Added some missing <p></p>
+* Changed some <? to <?php
+* Unauthorized album edit taken care of
+* Added jQuery Lightbox support, but commented out just in case someone won't want to use it (there may be some problems with the lightbox, since I'm not using it myself and I didn't test it)
+------------------------------------------------------------------------
+END VERSION 1.07 CHANGES
+------------------------------------------------------------------------
+
+
+------------------------------------------------------------------------
+Version 1.06 Change List
+Release Date: 02/03/2008
+Contributors:
+ * Gabriel Monge-Franco (http://gabriel.mongefranco.com)
+------------------------------------------------------------------------
+BEGIN VERSION 1.06 CHANGES
+------------------------------------------------------------------------
+* Forward all non-logged in users to World (public) pictures. This fixes a bug in which anonymous users would see a list of pictures titled, "'s pictures" (without a name).
+* Forward requests to "owned" without an user ID to "owned/userid" when a user is logged in. This fixes a bug in which all users would see a list of pictures titled, "'s pictures" (without a name).
+* Replaced several hard-coded strings to elgg_echo() functions so they can be translated.
+* Re-enabled title entry in picture editing. Titles are only optional, so there is no reason to hide them, especially if some users may find them useful. Moreover, lack of image titles makes list views awkward.
+* Fixed multi-picture editing bug in which editing was disabled after uploading multiple pictures.
+* Fixed several tags not conforming to standard PHP opening tags.
+* Fixed some bugs in the English language file.
+* Added Spanish and German translations (if you speak German, please double-check the language file!!!).
+* Added a download link at the bottom of each picture to download the original file.
+* Updated some icons and created missing ones.
+
+* TODO: disable public by default (input/accessRead.php) -- add setting to admin page that lets the site admin decide whether pictures can be public or not.
+* TODO: change ugly "new album" picture into something better looking.
+* TODO: re-organize files to clean up the plug-in directory (e.g., move world.php to views/default)
+* TODO: Fix bug whereby image objects are deleted from the database, but the physical files remain on the server. The path can be found with $file->getFilename(), but would it be safe to execute shell commands directly???
+------------------------------------------------------------------------
+END VERSION 1.06 CHANGES
+------------------------------------------------------------------------
+
diff --git a/mod/lightpics/deactivate.php b/mod/lightpics/deactivate.php
new file mode 100644
index 000000000..55191a9e4
--- /dev/null
+++ b/mod/lightpics/deactivate.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Deactive Tidypics
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+update_subtype('object', 'album');
+update_subtype('object', 'image');
diff --git a/mod/lightpics/fonts/LiberationSerif-Regular.ttf b/mod/lightpics/fonts/LiberationSerif-Regular.ttf
new file mode 100644
index 000000000..1dd4aaa92
--- /dev/null
+++ b/mod/lightpics/fonts/LiberationSerif-Regular.ttf
Binary files differ
diff --git a/mod/lightpics/fonts/License.txt b/mod/lightpics/fonts/License.txt
new file mode 100644
index 000000000..e972b52de
--- /dev/null
+++ b/mod/lightpics/fonts/License.txt
@@ -0,0 +1,13 @@
+LICENSE AGREEMENT AND LIMITED PRODUCT WARRANTY
+LIBERATION FONT SOFTWARE
+
+This agreement governs the use of the Software and any updates to the Software, regardless of the delivery mechanism. Subject to the following terms, Red Hat, Inc. ("Red Hat") grants to the user ("Client") a license to this work pursuant to the GNU General Public License v.2 with the exceptions set forth below and such other terms as our set forth in this End User License Agreement.
+
+ 1.The Software and License Exception. LIBERATION font software (the "Software") consists of TrueType-OpenType formatted font software for rendering LIBERATION typefaces in sans serif, serif, and monospaced character styles. You are licensed to use, modify, copy, and distribute the Software pursuant to the GNU General Public License v.2 with the following exceptions:
+(a)As a special exception, if you create a document which uses this font, and embed this font or unaltered portions of this font into the document, this font does not by itself cause the resulting document to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the document might be covered by the GNU General Public License. If you modify this font, you may extend this exception to your version of the font, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version.
+(b)As a further exception, any distribution of the object code of the Software in a physical product must provide you the right to access and modify the source code for the Software and to reinstall that modified version of the Software in object code form on the same physical product on which you received it.
+ 2.Intellectual Property Rights. The Software and each of its components, including the source code, documentation, appearance, structure and organization are owned by Red Hat and others and are protected under copyright and other laws. Title to the Software and any component, or to any copy, modification, or merged portion shall remain with the aforementioned, subject to the applicable license. The "LIBERATION" trademark is a trademark of Red Hat, Inc. in the U.S. and other countries. This agreement does not permit Client to distribute modified versions of the Software using Red Hat's trademarks. If Client makes a redistribution of a modified version of the Software, then Client must modify the files names to remove any reference to the Red Hat trademarks and must not use the Red Hat trademarks in any way to reference or promote the modified Software.
+ 3.Limited Warranty. To the maximum extent permitted under applicable law, the Software is provided and licensed "as is" without warranty of any kind, expressed or implied, including the implied warranties of merchantability, non-infringement or fitness for a particular purpose. Red Hat does not warrant that the functions contained in the Software will meet Client's requirements or that the operation of the Software will be entirely error free or appear precisely as described in the accompanying documentation.
+ 4.Limitation of Remedies and Liability. To the maximum extent permitted by applicable law, Red Hat or any Red Hat authorized dealer will not be liable to Client for any incidental or consequential damages, including lost profits or lost savings arising out of the use or inability to use the Software, even if Red Hat or such dealer has been advised of the possibility of such damages.
+ 5.General. If any provision of this agreement is held to be unenforceable, that shall not affect the enforceability of the remaining provisions. This agreement shall be governed by the laws of the State of North Carolina and of the United States, without regard to any conflict of laws provisions, except that the United Nations Convention on the International Sale of Goods shall not apply.
+Copyright © 2007 Red Hat, Inc. All rights reserved. LIBERATION is a trademark of Red Hat, Inc.
diff --git a/mod/lightpics/graphics/empty_album.png b/mod/lightpics/graphics/empty_album.png
new file mode 100644
index 000000000..0c041d0a2
--- /dev/null
+++ b/mod/lightpics/graphics/empty_album.png
Binary files differ
diff --git a/mod/lightpics/graphics/icons/album.gif b/mod/lightpics/graphics/icons/album.gif
new file mode 100644
index 000000000..73ce91356
--- /dev/null
+++ b/mod/lightpics/graphics/icons/album.gif
Binary files differ
diff --git a/mod/lightpics/graphics/icons/general.jpg b/mod/lightpics/graphics/icons/general.jpg
new file mode 100644
index 000000000..23494eff2
--- /dev/null
+++ b/mod/lightpics/graphics/icons/general.jpg
Binary files differ
diff --git a/mod/lightpics/graphics/icons/river_icon_album.gif b/mod/lightpics/graphics/icons/river_icon_album.gif
new file mode 100644
index 000000000..e1bbfedaa
--- /dev/null
+++ b/mod/lightpics/graphics/icons/river_icon_album.gif
Binary files differ
diff --git a/mod/lightpics/graphics/icons/river_icon_image.gif b/mod/lightpics/graphics/icons/river_icon_image.gif
new file mode 100644
index 000000000..cf391898e
--- /dev/null
+++ b/mod/lightpics/graphics/icons/river_icon_image.gif
Binary files differ
diff --git a/mod/lightpics/graphics/icons/river_icon_tag.gif b/mod/lightpics/graphics/icons/river_icon_tag.gif
new file mode 100644
index 000000000..510696147
--- /dev/null
+++ b/mod/lightpics/graphics/icons/river_icon_tag.gif
Binary files differ
diff --git a/mod/lightpics/graphics/image_error_large.png b/mod/lightpics/graphics/image_error_large.png
new file mode 100644
index 000000000..4316f459d
--- /dev/null
+++ b/mod/lightpics/graphics/image_error_large.png
Binary files differ
diff --git a/mod/lightpics/graphics/image_error_small.png b/mod/lightpics/graphics/image_error_small.png
new file mode 100644
index 000000000..2f814fd81
--- /dev/null
+++ b/mod/lightpics/graphics/image_error_small.png
Binary files differ
diff --git a/mod/lightpics/graphics/image_error_thumb.png b/mod/lightpics/graphics/image_error_thumb.png
new file mode 100644
index 000000000..0eeb797dc
--- /dev/null
+++ b/mod/lightpics/graphics/image_error_thumb.png
Binary files differ
diff --git a/mod/lightpics/graphics/loader.gif b/mod/lightpics/graphics/loader.gif
new file mode 100644
index 000000000..7ac990cf0
--- /dev/null
+++ b/mod/lightpics/graphics/loader.gif
Binary files differ
diff --git a/mod/lightpics/graphics/spacer.gif b/mod/lightpics/graphics/spacer.gif
new file mode 100644
index 000000000..e565824aa
--- /dev/null
+++ b/mod/lightpics/graphics/spacer.gif
Binary files differ
diff --git a/mod/lightpics/languages/ca.php b/mod/lightpics/languages/ca.php
new file mode 100644
index 000000000..282e63bf3
--- /dev/null
+++ b/mod/lightpics/languages/ca.php
@@ -0,0 +1,210 @@
+<?php
+$language = array (
+ 'image' => 'Imatge',
+ 'images' => 'Imatges',
+ 'caption' => 'Títol',
+ 'photos' => 'Fotos',
+ 'album' => 'Àlbum de fotos',
+ 'albums' => 'Àlbums de fotos',
+ 'tidypics:disabled' => 'Desactivat',
+ 'tidypics:enabled' => 'Activat',
+ 'admin:settings:photos' => 'Lightpics',
+ 'photos:add' => 'Crear àlbum',
+ 'images:upload' => 'Pujar fotos',
+ 'album:slideshow' => 'Presentació de diapositives',
+ 'album:yours' => 'Els teus àlbums de fotos',
+ 'album:yours:friends' => 'Àlbums de les teves amigues',
+ 'album:user' => 'Àlbums de fotos de %s',
+ 'album:friends' => 'Àlbums de fotos de les amigues',
+ 'album:all' => 'Tots els àlbums de fotos',
+ 'photos:group' => 'Fotos del grup',
+ 'item:object:image' => 'Fotos',
+ 'item:object:album' => 'Àlbums',
+ 'tidypics:uploading:images' => 'Si us plau espera. Pujant imatges.',
+ 'tidypics:enablephotos' => 'Activar àlbums de fotos del grup',
+ 'tidypics:editprops' => 'Edita propietats de la imatge',
+ 'tidypics:mostcommented' => 'Imatges més comentades',
+ 'tidypics:mostcommentedthismonth' => 'Més comentades aquest més',
+ 'tidypics:mostcommentedtoday' => 'Més comentades avui',
+ 'tidypics:mostviewed' => 'Imatges més vistes',
+ 'tidypics:mostvieweddashboard' => 'Escriptori més vist',
+ 'tidypics:mostviewedthisyear' => 'Més vistes aquest any',
+ 'tidypics:mostviewedthismonth' => 'Més vistes aquest mes',
+ 'tidypics:mostviewedlastmonth' => 'Més vistes l\'últim mes',
+ 'tidypics:mostviewedtoday' => 'Més vistes avui',
+ 'tidypics:recentlyviewed' => 'Imatges vistes recentment',
+ 'tidypics:recentlycommented' => 'Imatges comentades recentment',
+ 'tidypics:mostrecent' => 'Imatges més recents',
+ 'tidypics:yourmostviewed' => 'Les imatges que més has vist',
+ 'tidypics:yourmostrecent' => 'Les teves imatges més recents',
+ 'tidypics:friendmostviewed' => 'Les imatges més vistes de %s',
+ 'tidypics:friendmostrecent' => 'Les imatges més recents de %s',
+ 'tidypics:highestrated' => 'Imatges més valorades',
+ 'tidypics:views' => '%s vistes',
+ 'tidypics:viewsbyowner' => 'per %s usuàries (sense incloure\'t)',
+ 'tidypics:viewsbyothers' => '(%s per tu)',
+ 'tidypics:administration' => 'Administració de Tidypics',
+ 'tidypics:stats' => 'Estadístiques',
+ 'tidypics:nophotosingroup' => 'Aquest grup no té cap foto encara',
+ 'tidypics:upgrade' => 'Millorar',
+ 'tidypics:sort' => 'Ordena l\'àlbum de %s',
+ 'tidypics:none' => 'No hi ha àlbums de fotos',
+ 'tidypics:upload:error' => 'Error:',
+ 'tidypics:upload:maxfilesize' => 'L\'arxiu és massa gran',
+ 'tidypics:upload:minfilesize' => 'L\'arxiu és massa petit',
+ 'tidypics:upload:acceptfiletypes' => 'Tipus d\'arxiu no permès',
+ 'tidypics:upload:maxnumberoffiles' => 'Has excedit el nombre màxim d\'arxius',
+ 'tidypics:settings' => 'Opcions',
+ 'tidypics:settings:main' => 'Opcions principals',
+ 'tidypics:settings:image_lib' => 'Biblioteca d\'imatges',
+ 'tidypics:settings:thumbnail' => 'Creació de vistes prèvies',
+ 'tidypics:settings:help' => 'Ajuda',
+ 'tidypics:settings:download_link' => 'Mostra enllaç per baixar-se-la',
+ 'tidypics:settings:photo_ratings' => 'Activar valoracions de les fotos (requereix l\'extensió de valoració de Miguel Montes o un de compatible)',
+ 'tidypics:settings:exif' => 'Mostra les dades EXIF',
+ 'tidypics:settings:view_count' => 'Mostra el número de visions',
+ 'tidypics:settings:grp_perm_override' => 'Permetre els membres del grup accés complet als àlbums del grup',
+ 'tidypics:settings:maxfilesize' => 'Mida màxima d\'imatge en megabytes (MB):',
+ 'tidypics:settings:quota' => 'Quota d\'usuària (MB) - 0 vol dir que no tens límit',
+ 'tidypics:settings:im_path' => 'Introdueix la ruta als teus comandaments d\'ImageMagick',
+ 'tidypics:settings:img_river_view' => 'Quantes entrades d\'activitat per cada paquet d\'imatges pujades',
+ 'tidypics:settings:album_river_view' => 'Mostra la caràtula o un grup de fotos per al nou àlbum',
+ 'tidypics:settings:largesize' => 'Mida de la imatge principal',
+ 'tidypics:settings:smallsize' => 'Mida de les imatges de la vista d\'àlbum',
+ 'tidypics:settings:tinysize' => 'Mida de la previsualització',
+ 'tidypics:settings:sizes:instructs' => 'Pot fer falta que canviïs el CSS si canvies les mides predefinides',
+ 'tidypics:settings:im_id' => 'ID d\'imatge',
+ 'tidypics:settings:heading:img_lib' => 'Opcions de la biblioteca d\'imatges',
+ 'tidypics:settings:heading:main' => 'Opcions més importants',
+ 'tidypics:settings:heading:river' => 'Opcions d\'integració de l\'activitat',
+ 'tidypics:settings:heading:sizes' => 'Mida de la previsualització',
+ 'tidypics:settings:heading:groups' => 'Opcions del grup',
+ 'tidypics:option:all' => 'Totes',
+ 'tidypics:option:none' => 'Cap',
+ 'tidypics:option:cover' => 'Tapa',
+ 'tidypics:option:set' => 'Posa',
+ 'tidypics:server_info' => 'Informació del servidor',
+ 'tidypics:server_info:gd_desc' => 'L\'Elgg necessita que es carregui l\'extensió GD',
+ 'tidypics:server_info:exec_desc' => 'Es necessita per la línia de comandament d\'ImageMagick',
+ 'tidypics:server_info:memory_limit_desc' => 'Canvia memory_limit per incrementar-ho',
+ 'tidypics:server_info:peak_usage_desc' => 'Això és aproximadament el mínim per pàgina',
+ 'tidypics:server_info:upload_max_filesize_desc' => 'Mida màxima de una imatge pujada',
+ 'tidypics:server_info:post_max_size_desc' => 'Mida màxima de l\'entrada = suma de les imatges + formulari html',
+ 'tidypics:server_info:max_input_time_desc' => 'Temps que un script espera la pujada per terminar',
+ 'tidypics:server_info:max_execution_time_desc' => 'Temps màxim que un script estarà funcionant',
+ 'tidypics:server_info:php_version' => 'Versió del PHP',
+ 'tidypics:server_info:memory_limit' => 'Memòria disponible per al PHP',
+ 'tidypics:server_info:peak_usage' => 'Memòria utilitzada per carregar aquesta pàgina',
+ 'tidypics:server_info:upload_max_filesize' => 'Mida màxima dels arxius pujats',
+ 'tidypics:server_info:post_max_size' => 'Mida màxima de les entrades',
+ 'tidypics:server_info:max_input_time' => 'Temps màxim d\'entrada',
+ 'tidypics:server_info:max_execution_time' => 'Temps màxim d\'execució',
+ 'tidypics:server_info:use_only_cookies' => 'Sessions només amb galetes',
+ 'tidypics:server_config' => 'Configuració del servidor',
+ 'tidypics:server_configuration_doc' => 'Documentació de la configuració del servidor',
+ 'tidypics:lib_tools:testing' => 'Tidypics necessita saber la localització dels executables d\'ImageMagick si l\'has sel·leccionat com a biblioteca d\'imatges. El teu servei de hosting podria proporcionar-te això. Pots provar si la localització és correcta més avall. Si és així, hauria de mostrar la versió d\'ImageMagick que hi ha instal·lada al teu servidor.',
+ 'tidypics:thumbnail_tool' => 'Creació de previsualitzacions',
+ 'tidypics:thumbnail_tool_blurb' => 'Aquesta pàgina et permet crear previsualitzacions per les imatges quan la creació de previsualitzacions falla durant la pujada. Pots tenir alguns problemes amb la creació de vistes prèvies si la teva biblioteca d\'imatges no està configurada adequadament o si no hi ha prou memòria per a que la biblioteca GD carregui i canviï la mida d\'una imatge. Si les teves usuàries han tingut problemes amb la creació de previsualitzacions i tens configurada correctament l\'aplicació, pots provar de tornar a fer les vistes prèvies. Troba l\'identificador únic de la foto (és el número aprop del final de la URL quan veus una foto) i escriu-lo aquí a sota.',
+ 'tidypics:thumbnail_tool:unknown_image' => 'Impossible aconseguir la imatge original',
+ 'tidypics:thumbnail_tool:invalid_image_info' => 'Error aconseguint informació sobre la imatge',
+ 'tidypics:thumbnail_tool:create_failed' => 'Ha fallat la creació de les previsualitzacions',
+ 'tidypics:thumbnail_tool:created' => 'Previsualitzacions creades.',
+ 'album:create' => 'Crea nou àlbum',
+ 'album:add' => 'Afegeix àlbum de fotos',
+ 'album:addpix' => 'Afegeix fotos a l\'àlbum',
+ 'album:edit' => 'Edita àlbum',
+ 'album:delete' => 'Esborra àlbum',
+ 'album:sort' => 'Ordena',
+ 'image:edit' => 'Edita imatge',
+ 'image:delete' => 'Esborra imatge',
+ 'image:download' => 'Descarrega imatge',
+ 'album:title' => 'Títol',
+ 'album:desc' => 'Descripció',
+ 'album:tags' => 'Etiquetes',
+ 'album:cover' => 'Fer aquesta imatge la caràtula de l\'àlbum?',
+ 'album:cover_link' => 'Fer caràtula',
+ 'tidypics:title:quota' => 'Espai disponible',
+ 'tidypics:quota' => 'Ús d\'espai',
+ 'tidypics:uploader:choose' => 'Escull fotos',
+ 'tidypics:uploader:upload' => 'Puja fotos',
+ 'tidypics:uploader:describe' => 'Descriu fotos',
+ 'tidypics:uploader:filedesc' => 'Tipus d\'arxiu (jpeg, png, gif)',
+ 'tidypics:uploader:help' => 'Consell: utilitza <code>Ctrl</code> o <code>Shift</code> per seleccionar més d\'un arxiu. A més pots arrossegar fotos des de l\'escriptori.',
+ 'tidypics:sort:instruct' => 'Ordena les fotos de l\'àlbum arrossegant-les. Fes clic després al botó de desar.',
+ 'tidypics:sort:no_images' => 'No s\'han trobat imatges per ordenar. Puja imatges amb l\'enllaç de més a dalt.',
+ 'album:num' => 'Fotos de %s',
+ 'image:total' => 'Images a l\'àlbum:',
+ 'image:by' => 'Imatges afegida per',
+ 'album:by' => 'Àlbum creat per',
+ 'album:created:on' => 'Creat',
+ 'image:none' => 'No s\'han afegit imatges encara',
+ 'image:back' => 'Previ',
+ 'image:next' => 'Següent',
+ 'image:index' => '%u de %s',
+ 'tidypics:posted' => 'Ha penjat una foto:',
+ 'tidypics:widget:albums' => 'Àlbums de fotos',
+ 'tidypics:widget:album_descr' => 'Mostra els teus àlbums',
+ 'tidypics:widget:num_albums' => 'Nombre d\'àlbums a mostrar',
+ 'tidypics:widget:latest' => 'Últimes fotos',
+ 'tidypics:widget:latest_descr' => 'Mostra les teves últimes fotos',
+ 'tidypics:widget:num_latest' => 'Nombre d\'imatges a mostrar',
+ 'album:more' => 'Veure tots els àlbums',
+ 'river:create:object:image' => '%s ha pujat la foto %s',
+ 'image:river:created' => '%s ha afegit una foto a l\'àlbum %s',
+ 'image:river:created:multiple' => '%s ha afegit %u fotos a l\'àlbum %s',
+ 'image:river:item' => 'una foto',
+ 'image:river:annotate' => 'un comentari a la foto',
+ 'river:create:object:album' => '%s ha creat un nou àlbum %s',
+ 'album:river:group' => 'al grup',
+ 'album:river:item' => 'un àlbum',
+ 'album:river:annotate' => 'un comentari a l\'àlbum',
+ 'river:comment:object:image' => '%s ha comentat a la foto %s',
+ 'river:comment:object:album' => '%s ha comentat a la foto %s',
+ 'tidypics:newalbum_subject' => 'Nou àlbum de fotos',
+ 'tidypics:newalbum' => '%s ha creat un nou àlbum de fotos',
+ 'tidypics:updatealbum' => '%s ha pujat noves fotos a l\'àlbum %s',
+ 'tidypics:upl_success' => 'Les teves imatges s\'han pujat correctament.',
+ 'image:saved' => 'La teva imatge s\'ha desat correctament.',
+ 'images:saved' => 'Totes les imatges s\'han desat correctament.',
+ 'image:deleted' => 'La teva imatges s\'ha esborrat correctament.',
+ 'image:delete:confirm' => 'Estàs segur de voler esborrar aquesta imatge?',
+ 'images:edited' => 'Les teves imatges s\'han actualitzat correctament.',
+ 'album:edited' => 'El teu àlbum s\'ha actualitzat correctament.',
+ 'album:saved' => 'El teu àlbum s\'ha desat correctament.',
+ 'album:deleted' => 'El teu àlbum s\'ha esborrat correctament.',
+ 'album:delete:confirm' => 'Estàs segur de voler esborrar l\'àlbum?',
+ 'album:created' => 'El teu nou àlbum s\'ha creat.',
+ 'album:save_cover_image' => 'S\'ha desat la caràtula.',
+ 'tidypics:settings:save:ok' => 'S\'han desat correctament les opcions del complement Tidypics',
+ 'tidypics:album:sorted' => 'L\'àlbum %s està ordenat',
+ 'tidypics:album:could_not_sort' => 'No es pot ordenar l\'àlbum %s. Assegura\'t que hi ha imatges a l\'àlbum i torna-ho a intentar.',
+ 'tidypics:upgrade:success' => 'Millora dels Tidypics ha estat correcta',
+ 'tidypics:baduploadform' => 'Hi ha hagut algun error amb el formulari de pujades',
+ 'tidypics:partialuploadfailure' => 'Hi ha hagut errors pujant algunes de les imatges (%s de %s).',
+ 'tidypics:completeuploadfailure' => 'La pujada de les imatges ha fallat',
+ 'tidypics:exceedpostlimit' => 'Són massa imatges o massa grans - prova pujant-ne menys o més petites.',
+ 'tidypics:noimages' => 'No s\'han seleccionat imatges',
+ 'tidypics:image_mem' => 'La imatge és massa gran - massa bytes',
+ 'tidypics:image_pixels' => 'La imatge té massa píxels',
+ 'tidypics:unk_error' => 'Error de pujada desconegut',
+ 'tidypics:save_error' => 'Error desconegut en desar la imatge al servidor',
+ 'tidypics:not_image' => 'No és un tipus d\'imatge reconegut',
+ 'tidypics:deletefailed' => 'Ho sentim, ha fallat en esborrar.',
+ 'tidypics:deleted' => 'Esborrat correcte',
+ 'tidypics:nosettings' => 'L\'administradora del lloc no ha establert configuració per a l\'àlbum de fotos',
+ 'tidypics:exceed_quota' => 'Has excedit la quota que t\'ha assignat l\'administradora',
+ 'tidypics:cannot_upload_exceeds_quota' => 'Imatge no pujada. L\'arxiu excedeix la teva quota disponible',
+ 'album:none' => 'No s\'ha creat cap àlbum encara.',
+ 'album:uploadfailed' => 'Ho sento, no podem desar el teu àlbum.',
+ 'album:deletefailed' => 'El teu àlbum no pot ser esborrat.',
+ 'album:blank' => 'Si us plau posa un títol a l\'àlbum',
+ 'album:invalid_album' => 'Àlbum invàlid',
+ 'album:cannot_save_cover_image' => 'No es pot desar la caràtula',
+ 'image:downloadfailed' => 'Ho sentim, aquesta imatge no està disponible',
+ 'images:notedited' => 'No totes les imatges s\'han actualitzat correctament',
+ 'image:blank' => 'Si us plau posa un títol a la imatge',
+ 'image:error' => 'No es pot desar la imatge.',
+ 'tidypics:upgrade:failed' => 'La millora de Tidypics ha fallat',
+ 'untitled' => 'Sense t&iacute;tol',
+);
+add_translation("ca", $language); \ No newline at end of file
diff --git a/mod/lightpics/languages/da.php b/mod/lightpics/languages/da.php
new file mode 100644
index 000000000..3e454540c
--- /dev/null
+++ b/mod/lightpics/languages/da.php
@@ -0,0 +1,169 @@
+<?php
+/**
+* Elgg tidypics plugin danish language pack
+*
+*/
+
+
+$danish = array(
+
+ // Menu items and titles
+ 'image' => "Billede",
+ 'images' => "Billeder",
+ 'caption' => "Beskrivelse",
+ 'photos' => "Fotos",
+ 'album' => "Fotoalbum",
+ 'albums' => "Fotoalbums",
+ 'album:slideshow' => "Se slideshow",
+ 'album:yours' => "Dine Fotoalbums",
+ 'album:yours:friends' => "Dine venners fotoalbums",
+ 'album:user' => "%s's Fotoalbums",
+ 'album:friends' => "%s's venners fotoalbum",
+ 'album:all' => "Alle Fotoalbums",
+ 'album:group' => "Gruppe-Fotoalbum",
+ 'item:object:image' => "Fotos",
+ 'item:object:album' => "Album",
+ 'tidypics:uploading:images' => "Billederne er uploadet",
+ 'tidypics:enablephotos' => 'Skift gruppealbum',
+ 'tidypics:editprops' => 'Rediger billede',
+ 'tidypics:mostcommented' => 'Mest kommenterede',
+ 'tidypics:mostcommentedthismonth' => 'Mest kommenterede i denne måned',
+ 'tidypics:mostcommentedtoday' => 'Mest kommenterede i dag',
+ 'tidypics:mostviewed' => 'Mest besøgte billeder',
+ 'tidypics:mostvieweddashboard' => 'Mest besøgte instrumentpanel',
+ 'tidypics:mostviewedthisyear' => 'Mest besøgte i år',
+ 'tidypics:mostviewedthismonth' => 'Mest besøgte i denne måned',
+ 'tidypics:mostviewedlastmonth' => 'Mest besøgte sidste måned',
+ 'tidypics:mostviewedtoday' => 'Set flest gange i dag',
+ 'tidypics:recentlyviewed' => 'Senest viste billeder',
+ 'tidypics:recentlycommented' => 'Senest kommenterede billeder',
+ 'tidypics:mostrecent' => 'Nyeste billeder ifællesskabet',
+ 'tidypics:yourmostviewed' => 'Dine mest sete',
+ 'tidypics:yourmostrecent' => 'Sidst uploaded',
+ 'tidypics:friendmostviewed' => "%s's mest sete",
+ 'tidypics:friendmostrecent' => "%s's seneste billeder ",
+ 'tidypics:highestrated' => "Højest bedømte billeder",
+ 'tidypics:viewsbyowner' => "Visninger: %s af %s medlemmer (ikke inklusive dig selv)",
+ 'tidypics:viewsbyothers' => "Visninger: %s (%s af dig)",
+ 'tidypics:administration' => 'Tidypics administration',
+ 'tidypics:stats' => 'Statistik',
+ 'tidypics:upgrade' => 'Opgrader',
+ 'tidypics:sort' => 'Sortér %s album',
+
+ //settings
+ 'tidypics:settings' => 'Indstillinger',
+ 'tidypics:settings:server:analysis' => 'Kør server analyse',
+ 'tidypics:admin:instructions' => 'Dette er de centrale Tidypics indstillinger. Tilpas dem til din opsætning og klik derefter på Gem.',
+ 'tidypics:settings:image_lib' => "Foto arkiv: ",
+ 'tidypics:settings:download_link' => "Vis download-link",
+ 'tidypics:settings:photo_ratings' => "Tillad karakterergivning af foto (kræver plugin af Miguel Montes eller andet kompatibelt plugin)",
+ 'tidypics:settings:exif' => "Vis EXIF data",
+ 'tidypics:settings:view_count' => "Vis tæller",
+ 'tidypics:settings:grp_perm_override' => "Giv gruppens medlemmer fuld adgang til gruppe-album",
+ 'tidypics:settings:maxfilesize' => "Maksimal billedstørrelse i megabytes (MB):",
+ 'tidypics:settings:quota' => "Tildelt plads til brugere / grupper (MB) (0 = Ingen plads)",
+ 'tidypics:settings:im_path' => "Angiv stien til ImageMagick kommandoer (som slutter med et Slash/)",
+ 'tidypics:settings:img_river_view' => "Hvor mange poster i aktivitetslisten for hvert parti af uploadede billeder",
+ 'tidypics:settings:album_river_view' => "Vis albumcover eller et sæt af fotos til nye album",
+ 'tidypics:settings:largesize' => "Størrelse på billede",
+ 'tidypics:settings:smallsize' => "Tumbnailstørrelse på album",
+ 'tidypics:settings:thumbsize' => "Thumbnailstørrelse på billede",
+
+
+ //actions
+ 'album:create' => "Skab album",
+ 'album:add' => "Tilføj nyt album",
+ 'album:addpix' => "Tilføj fotos",
+ 'album:edit' => "Rediger album",
+ 'album:delete' => "Slet album",
+ 'album:sort' => "Sortér album",
+ 'image:edit' => "Rediger billede",
+ 'image:delete' => "Slet billede",
+ 'image:download' => "Download billede",
+
+ //forms
+ 'album:title' => "Titel",
+ 'album:desc' => "Beskrivelse",
+ 'album:tags' => "Nøgleord",
+ 'album:cover' => "Billedet skal bruges som albumcover",
+ 'tidypics:quota' => "Plads:",
+ 'tidypics:uploader:choose' => "Vælg fotos",
+ 'tidypics:uploader:upload' => "Upload fotos",
+ 'tidypics:uploader:describe' => "Beskriv fotos",
+ 'tidypics:uploader:basic' => 'Du kan uploade op til 10 fotos ad gangen (%s MB maksimum pr. foto)',
+ 'tidypics:sort:instruct' => 'Sortér fotos i albummet ved hjælp af drag and drop. Klik derefter på knappen Gem.',
+
+ //views
+ 'image:total' => "Billeder i album:",
+ 'image:by' => "Billede tilføjet af",
+ 'album:by' => "Album oprettet af:",
+ 'album:created:on' => "Oprettet",
+ 'image:none' => "Ingen billeder tilføjet.",
+ 'image:back' => "Forrige",
+ 'image:next' => "Næste",
+
+ //rss
+ 'tidypics:posted' => 'Billedet indstilles individuelt:',
+
+ //widgets
+ 'tidypics:widget:albums' => "Fotoalbum",
+ 'tidypics:widget:album_descr' => "Viser seneste album",
+ 'tidypics:widget:num_albums' => "Antal albums",
+ 'tidypics:widget:latest' => "Nyeste billeder",
+ 'tidypics:widget:latest_descr' => "Vis seneste billeder",
+ 'tidypics:widget:num_latest' => "Antal billeder",
+ 'album:more' => "Vis alle albums",
+
+ //river
+ 'image:river:created' => "%s har tilføjet et billede %s i album %s",
+ 'image:river:created:multiple' => "%s added %u photos to album %s",
+ 'image:river:item' => "et billede",
+ 'image:river:annotate' => "en kommentar til billedet",
+ 'album:river:created' => "%s har tilføjet et nyt album",
+ 'album:river:group' => "i gruppen",
+ 'album:river:item' => "et album",
+ 'album:river:annotate' => "en kommentar til albummet",
+
+ //notifications
+ 'tidypics:newalbum' => 'Nye Fotoalbum',
+
+
+ // Status messages
+ 'tidypics:upl_success' => "Billedet er uploadet med succes",
+ 'image:saved' => "Billedet blev gemt",
+ 'images:saved' => "Alle billeder er gemt",
+ 'image:deleted' => "Billedet blev slettet",
+ 'image:delete:confirm' => "Ønsker du at slette dette billede?",
+ 'images:edited' => "Billedet er blevet opdateret",
+ 'album:edited' => "Albummet er blevet opdateret",
+ 'album:saved' => "Albummet blev gemt",
+ 'album:deleted' => "Albummet er blevet slettet",
+ 'album:delete:confirm' => "Ønsker du at slette dette album?",
+ 'album:created' => "Deres nye album er skabt",
+ 'tidypics:settings:save:ok' => 'Tidypics Indstillingerne er gemt',
+ 'tidypics:album:sorted' => 'Albummet %s er sorteret',
+ 'tidypics:upgrade:success' => 'Tidypics blev opgraderet med succes',
+
+ //Error messages
+ 'tidypics:partialuploadfailure' => "Der opstod en fejl under uploadingen af billeder (%s af %s billeder)",
+ 'tidypics:completeuploadfailure' => "Upload af billede mislykkedes",
+ 'tidypics:exceedpostlimit' => "Alt for mange store billeder på én gang - forsøg evt. at overføre færre eller mindre billeder",
+ 'tidypics:noimages' => "Ingen billeder udvalgt til upload",
+ 'tidypics:image_mem' => "Billedet er for stort",
+ 'tidypics:image_pixels' => "Billedet har for mange pixels",
+ 'tidypics:unk_error' => "Ukendt fejl opstod under upload",
+ 'tidypics:save_error' => "Ukendt fejl opstod, da billedet skulle gemmes på serveren",
+ 'tidypics:not_image' => "Type af billede kan ikke genkendes",
+ 'image:deletefailed' => "Dit billede kunne ikke slettes",
+ 'image:downloadfailed' => "Fejl: Billedet er ikke tilgængeligt i øjeblikket",
+ 'tidypics:nosettings' => "Administratoren af webstedet, har ikke foretaget justeringer for fotoalbum",
+ 'tidypics:exceed_quota' => "Din tildelte plads er opbrugt!",
+ 'images:notedited' => "Ikke alle billeder er blevet opdateret",
+ 'album:none' => "Ingen albums skabt endnu",
+ 'album:uploadfailed' => "Dit album kunne ikke gemmes",
+ 'album:deletefailed' => "Dit album kunne ikke slettes",
+ 'album:blank' => "Giv venligst dette album en titel samt beskrivelse",
+ 'tidypics:upgrade:failed' => "Opgraderingen af Tidypics mislykkedes",
+);
+
+add_translation("da", $danish); \ No newline at end of file
diff --git a/mod/lightpics/languages/de.php b/mod/lightpics/languages/de.php
new file mode 100644
index 000000000..ac8b99bcb
--- /dev/null
+++ b/mod/lightpics/languages/de.php
@@ -0,0 +1,248 @@
+<?php
+
+$german = array(
+ // hack for core bug
+ 'untitled' => "Unbenannt",
+
+ // Menu items and titles
+ 'image' => "Bild",
+ 'images' => "Bilder",
+ 'caption' => "Beschreibung",
+ 'photos' => "Bilder",
+ 'album' => "Bilderalbum",
+ 'albums' => "Bilderalben",
+ 'tidypics:disabled' => 'Deaktiviert',
+ 'tidypics:enabled' => 'Aktiviert',
+ 'admin:settings:photos' => 'Lightpics',
+
+ 'photos:add' => "Album hinzufügen",
+ 'images:upload' => "Bilder hochladen",
+
+ 'album:slideshow' => "Diashow ansehen",
+ 'album:yours' => "Deine Bilderalben",
+ 'album:yours:friends' => "Bilderalben Deiner Freunde",
+ 'album:user' => "Bilderalben von %s",
+ 'album:friends' => "Bilderalben von Freunden",
+ 'album:all' => "Alle Bilderalben",
+ 'photos:group' => "Gruppen-Bilder",
+ 'item:object:image' => "Bilder",
+ 'item:object:album' => "Alben",
+ 'tidypics:uploading:images' => "Die Bilder werden hochgeladen.",
+ 'tidypics:enablephotos' => 'Gruppen-Alben aktivieren',
+ 'tidypics:editprops' => 'Bild bearbeiten',
+ 'tidypics:mostcommented' => 'Meist kommentierte Bilder',
+ 'tidypics:mostcommentedthismonth' => 'Meist kommentierte Bilder des Monats',
+ 'tidypics:mostcommentedtoday' => 'Meist kommentierte Bilder des Tages',
+ 'tidypics:mostviewed' => 'Am häufigsten angesehene Bilder',
+ 'tidypics:mostvieweddashboard' => 'Am häufigsten angesehene Bilder auf Dashboard',
+ 'tidypics:mostviewedthisyear' => 'Am häufigsten angesehene Bilder des Jahres',
+ 'tidypics:mostviewedthismonth' => 'Am häufigsten angesehene Bilder des Monats',
+ 'tidypics:mostviewedlastmonth' => 'Am häufigsten angesehene Bilder im letzten Monat',
+ 'tidypics:mostviewedtoday' => 'Am häufigsten angesehene Bilder des Tages',
+ 'tidypics:recentlyviewed' => 'Zuletzt angesehene Bilder',
+ 'tidypics:recentlycommented' => 'Zuletzt kommentierte Bilder',
+ 'tidypics:mostrecent' => 'Neue Bilder der Community-Seite',
+ 'tidypics:yourmostviewed' => 'Deine am häufigsten angesehenen Bilder',
+ 'tidypics:yourmostrecent' => 'Zuletzt hochgeladene Bilder',
+ 'tidypics:friendmostviewed' => "Am häufigsten angesehene Bilder von %s",
+ 'tidypics:friendmostrecent' => "Zuletzt hochgeladene Bilder von %s",
+ 'tidypics:highestrated' => "Am höchsten bewertete Bilder",
+ 'tidypics:views' => "%s Zugriffe",
+ 'tidypics:viewsbyowner' => "Zugriffe: %s von %s Mitglieder (Dich nicht mitgerechnet)",
+ 'tidypics:viewsbyothers' => "Zugriffe: %s (%s von Dir)",
+ 'tidypics:administration' => 'Tidypics-Administration',
+ 'tidypics:stats' => 'Statistik',
+ 'tidypics:nophotosingroup' => 'Diese Gruppe hat noch keine Bilder.',
+ 'tidypics:upgrade' => 'Aktualisieren',
+ 'tidypics:sort' => 'Umsortieren des %s-Bilderalbums',
+ 'tidypics:none' => 'Keine Bilderalben vorhanden.',
+
+ //settings
+ 'tidypics:settings' => 'Einstellungen',
+ 'tidypics:settings:main' => 'Grundeinstellungen',
+ 'tidypics:settings:image_lib' => "Image Library",
+ 'tidypics:settings:thumbnail' => "Erzeugung von Vorschaubildern",
+ 'tidypics:settings:help' => "Hilfe",
+ 'tidypics:settings:download_link' => "Zeige Download-Link",
+ 'tidypics:settings:photo_ratings' => "Bewerten von Bildern erlauben (benötigt das Elggx Fivestar-Plugin oder ein vergleichbares)",
+ 'tidypics:settings:exif' => "EXIF-Daten anzeigen",
+ 'tidypics:settings:view_count' => "Zugriffszähler anzeigen",
+ 'tidypics:settings:grp_perm_override' => "Gruppen-Mitgliedern unbeschränkten Zugriff auf die Verwaltung von Gruppen-Alben geben",
+ 'tidypics:settings:maxfilesize' => "Maximal erlaubte Bildgröße in Megabytes (MB):",
+ 'tidypics:settings:quota' => "Quota für Benutzer in Megabytes (MB) (0 = kein Quota)",
+ 'tidypics:settings:im_path' => "Der Pfad zu den ImageMagick-Kommandozeilentools auf dem Server",
+ 'tidypics:settings:img_river_view' => "Anzahl der Einträge im River beim gleichzeitigen Hochladen mehrerer Bilder",
+ 'tidypics:settings:album_river_view' => "Anzeige des Albumcovers oder eines Sets von Bildern für neue Alben",
+ 'tidypics:settings:largesize' => "Bildgröße in der Hauptansicht",
+ 'tidypics:settings:smallsize' => "Bildgröße in der Alben-Ansicht",
+ 'tidypics:settings:tinysize' => "Bildgröße von Vorschaubildern",
+ 'tidypics:settings:sizes:instructs' => 'Du mußt eventuell die CSS-Einstellungen anpassen, falls Du die vorgegebenen Standard-Bildgrößen änderst.',
+ 'tidypics:settings:im_id' => "Bilder-ID",
+ 'tidypics:settings:heading:img_lib' => "Image-Library-Einstellungen",
+ 'tidypics:settings:heading:main' => "Grundeinstellungen",
+ 'tidypics:settings:heading:river' => "Einstellungen für die River-Integration",
+ 'tidypics:settings:heading:sizes' => "Größe der Vorschaubilder",
+ 'tidypics:settings:heading:groups' => "Einstellungen für die Gruppen-Integration",
+ 'tidypics:option:all' => 'Alle',
+ 'tidypics:option:none' => 'Keine',
+ 'tidypics:option:cover' => 'Cover',
+ 'tidypics:option:set' => 'Set',
+
+ // server analysis
+ 'tidypics:server_info' => 'Informationen über den Server',
+ 'tidypics:server_info:gd_desc' => 'Elgg setzt voraus, dass die GD-PHP-Erweiterung verfügbar ist.',
+ 'tidypics:server_info:exec_desc' => 'Benötigt für die ImageMagick-Kommandozeilentools.',
+ 'tidypics:server_info:memory_limit_desc' => 'Passe die PHP-Variable memory_limit an, um mehr Speicher zur Verfügung zu stellen.',
+ 'tidypics:server_info:peak_usage_desc' => 'Dies ist ungefähr der Mindest-Speicherbedarf pro Seite.',
+ 'tidypics:server_info:upload_max_filesize_desc' => 'Maximal erlaubte Dateigröße für ein einzelnes hochzuladendes Bild.',
+ 'tidypics:server_info:post_max_size_desc' => '"Max post size" abzüglich der Größe der HTML-Form = Maximal mögliche Gesamtgröße von gleichzeitig hochzuladenden Bildern.',
+ 'tidypics:server_info:max_input_time_desc' => 'Maximale erlaubte Dauer, in der das Hochladen von Daten auf den Server abgeschlossen sein muss.',
+ 'tidypics:server_info:max_execution_time_desc' => 'Maximal erlaubte Ausführdauer für ein Skript auf dem Server.',
+
+ 'tidypics:server_info:php_version' => 'PHP-Version',
+ 'tidypics:server_info:memory_limit' => 'Für PHP verfügbarer Speicher',
+ 'tidypics:server_info:peak_usage' => 'Genutzer Speicher, um diese Seite zu laden',
+ 'tidypics:server_info:upload_max_filesize' => 'PHP-Einstellung von upload_max_filesize',
+ 'tidypics:server_info:post_max_size' => 'PHP-Einstellung von post_max_size',
+ 'tidypics:server_info:max_input_time' => 'PHP-Einstellung von max_input_time',
+ 'tidypics:server_info:max_execution_time' => 'PHP-Einstellung von max_execution_time',
+ 'tidypics:server_info:use_only_cookies' => 'PHP-Einstellung von session.use_only_cookies',
+
+ 'tidypics:server_config' => 'Server-Konfiguration',
+ 'tidypics:server_configuration_doc' => 'Dokumentation über die Konfigurierung des Servers',
+
+ // ImageMagick test
+ 'tidypics:lib_tools:testing' =>
+ 'Tidypics benötigt den Pfad auf dem Server zu den ImageMagick-Kommandozeilentools, falls Du diese als Image-Library verwenden willst. Dein Webhoster sollte Dir diese Information mitteilen können. Du kannst im folgenden prüfen, ob dieser Pfad korrekt ist. Wenn der Test erfolgreich ist, sollten Informationen über die auf dem Server installierte Version von ImageMagick angezeigt werden.',
+
+ // thumbnail tool
+ 'tidypics:thumbnail_tool' => 'Vorschaubild-Erzeugung',
+ 'tidypics:thumbnail_tool_blurb' =>
+ 'Im Folgenden kannst Du Vorschaubilder von Bildern erzeugen, falls dies beim Hochladen der Bilder fehlgeschlagen ist. Die Erzeugung der Vorschaubilder kann fehlschlagen, wenn die Image-Library nicht richtig eingerichtet ist oder falls für die GD-PHP-Erweiterung nicht genügend Arbeitsspeicher verfügbar ist, um das Bild zu laden und die Größe zu ändern. Falls die Mitglieder Deiner Seite bei der Erzeugung der Vorschaubilder für ihre hochgeladenen Bilder Probleme hatten, kannst Du im folgenden die Erzeugung der Vorschaubilder wiederholen, nachdem Du den Server bzw. Tidypics richtig konfiguriert hast. Bestimme die GUID des Bildes, für das Du Vorschaubilder erzeugen willst (dies ist die Nummer, die fast am Ende der URL enthalten ist, wenn Du dieses Bilder auf Deiner Seite anschaust) und gebe diese Nummer im folgenden Eingabefeld ein.',
+ 'tidypics:thumbnail_tool:unknown_image' => 'Das Originalbild kann nicht gefunden werden.',
+ 'tidypics:thumbnail_tool:invalid_image_info' => 'Beim Abrufen Bildinformationen ist ein Fehler aufgetreten.',
+ 'tidypics:thumbnail_tool:create_failed' => 'Die Erzeugung der Vorschaubilder ist fehlgeschlagen.',
+ 'tidypics:thumbnail_tool:created' => 'Die Vorschaubilder wurden erzeugt.',
+
+ //actions
+ 'album:create' => "Neues Album hinzufügen",
+ 'album:add' => "Neues Album hinzufügen",
+ 'album:addpix' => "Bilder hinzufügen",
+ 'album:edit' => "Album bearbeiten",
+ 'album:delete' => "Album löschen",
+ 'album:sort' => "Umsortieren",
+ 'image:edit' => "Bild bearbeiten",
+ 'image:delete' => "Bild löschen",
+ 'image:download' => "Bild herunterladen",
+
+ //forms
+ 'album:title' => "Titel",
+ 'album:desc' => "Beschreibung",
+ 'album:tags' => "Tags",
+ 'album:cover' => "Dieses Bild als Albumcover verwenden?",
+ 'album:cover_link' => 'Zum Albumcover machen',
+ 'tidypics:title:quota' => 'Quota',
+ 'tidypics:quota' => "Quota-Ausnutzung:",
+ 'tidypics:uploader:choose' => "Wähle die Bilder aus",
+ 'tidypics:uploader:upload' => "Lade die Bilder hoch",
+ 'tidypics:uploader:describe' => "Gebe Beschreibungen für die Bilder ein",
+ 'tidypics:uploader:filedesc' => 'Bilddateien (jpeg, png, gif)',
+ 'tidypics:uploader:instructs' => 'Das Hochladen von Bilder erfolgt in drei einfachen Schritten: Bilder zum Hochladen auswählen, diese Bilder hochladen und dann Beschreibungen für diese Bilder eingeben. Pro Bilddatei gibt es eine Größenbeschränkung von %s MB. Falls Du auf Deinem Computer Flash nicht installiert hast, ist auch ein <a href="%s">Alternativer Uploader</a> verfügbar.',
+ 'tidypics:uploader:basic' => 'Du kannst bis zu 10 Bilder auf einmal hochladen (maximale Dteigröße %s MB pro Bild).',
+ 'tidypics:sort:instruct' => 'Sortiere die Bilder im Album durch Drag-and-Drop um. Dann speichere Deine Änderungen.',
+ 'tidypics:sort:no_images' => 'Es wurden keine Bilder gefunden, die umsortiert werden könnten. Lade Bilder hoch, indem Du dem obigen Link folgst.',
+
+ // albums
+ 'album:num' => '%s Bilder',
+
+ //views
+ 'image:total' => "Bilder im Album:",
+ 'image:by' => "Bild hinzugefügt von",
+ 'album:by' => "Album erstellt von",
+ 'album:created:on' => "Erstellt",
+ 'image:none' => "Es wurden noch keine Bilder hinzugefügt.",
+ 'image:back' => "Vorheriges",
+ 'image:next' => "Nächstes",
+ 'image:index' => "%u von %u",
+
+ //rss
+ 'tidypics:posted' => 'hat ein Bild hinzugefügt:',
+
+ //widgets
+ 'tidypics:widget:albums' => "Bilderalben",
+ 'tidypics:widget:album_descr' => "Auflistung Deiner neuesten Bilderalben",
+ 'tidypics:widget:num_albums' => "Anzahl der anzuzeigenden Alben",
+ 'tidypics:widget:latest' => "Neueste Bilder",
+ 'tidypics:widget:latest_descr' => "Auflistung Deiner neuesten Bilder",
+ 'tidypics:widget:num_latest' => "Anzahl der anzuzeigenden Bilder",
+ 'album:more' => "Alle Alben anzeigen",
+
+ //river
+ 'river:create:object:image' => "%s hat das Bild %s hochgeladen.",
+ 'image:river:created' => "%s hat ein Bild zum Album %s hinzugefügt.",
+ 'image:river:created:multiple' => "%s hat %u Bilder zum Album %s hinzugefügt.",
+ 'image:river:item' => "ein Bild",
+ 'image:river:annotate' => "einen Kommentar zum Bild",
+ 'river:create:object:album' => "%s hat das neue Bilderalbum %s hinzugefügt.",
+ 'album:river:group' => "in der Gruppe",
+ 'album:river:item' => "ein Album",
+ 'album:river:annotate' => "einen Kommentar zum Bilderalbum",
+ 'river:comment:object:image' => '%s hat einen Kommentar zum Bild %s geschrieben.',
+ 'river:comment:object:album' => '%s hat einen Kommentar zum Bilderalbum %s geschrieben.',
+
+ // notifications
+ 'tidypics:newalbum_subject' => 'Neues Bilderalbum',
+ 'tidypics:newalbum' => '%s hat ein neues Bilderalbum hinzugefügt.',
+ 'tidypics:updatealbum' => "%s hat neue Bilder zum Album %s hinzugefügt.",
+
+ // Status messages
+ 'tidypics:upl_success' => "Deine Bilder wurden hochgeladen.",
+ 'image:saved' => "Das Bild wurde gespeichert.",
+ 'images:saved' => "Alle Bilder wurden gespeichert.",
+ 'image:deleted' => "Das Bild wurde gelöscht.",
+ 'image:delete:confirm' => "Bist Du sicher, dass Du das Bild löschen willst?",
+ 'images:edited' => "Die Einstellungen für Deine Bilder wurden aktualisiert.",
+ 'album:edited' => "Die Einstellungen für Dein Album wurden aktualisiert.",
+ 'album:saved' => "Das Album wurde hinzugefügt.",
+ 'album:deleted' => "Das Album wurde gelöscht.",
+ 'album:delete:confirm' => "Bist Du sicher, dass Du das Album löschen willst?",
+ 'album:created' => "Dein neues Album wurde hinzugefügt.",
+ 'album:save_cover_image' => 'Das Albumcover wurde gespeichert.',
+ 'tidypics:settings:save:ok' => 'Die Einstellungen für das Tidypics-Plugin wurden gespeichert.',
+ 'tidypics:album:sorted' => 'Das Bilderalbum %s wurde umsortiert.',
+ 'tidypics:album:could_not_sort' => 'Das Bilderalbum %s konnte nicht umsortiert werden. Vergewissere Dich, dass in diesem Album Bilder vorhanden sind und versuche es noch einmal.',
+ 'tidypics:upgrade:success' => 'Das Upgrade des Tidypics-Plugins war erfolgreich.',
+
+ //Error messages
+ 'tidypics:baduploadform' => "Es gab ein unerwartetes Problem mit dem Uploader.",
+ 'tidypics:partialuploadfailure' => "Beim Hochladen einiger Bilder sind Fehler aufgetreten (bei %s von %s Bildern).",
+ 'tidypics:completeuploadfailure' => "Das Hochladen der Bilder ist fehlgeschlagen.",
+ 'tidypics:exceedpostlimit' => "Zu viele große Bilder auf einmal - versuche weniger Bilder auf einmal oder kleinere Bilder hochzuladen.",
+ 'tidypics:noimages' => "Es wurden keine Bilder zum Hochladen ausgewählt.",
+ 'tidypics:image_mem' => "Die Bilddatei ist zu groß.",
+ 'tidypics:image_pixels' => "Das Bild hat zu viele Pixel.",
+ 'tidypics:unk_error' => "Unbekannter Fehler beim Hochladen.",
+ 'tidypics:save_error' => "Unbekannter Fehler beim Speichern des Bildes auf dem Server.",
+ 'tidypics:not_image' => "Das Dateiformat des Bildes wurde nicht erkannt oder wird nicht unterstützt.",
+ 'tidypics:deletefailed' => "Beim Löschen ist ein Fehler aufgetreten.",
+ 'tidypics:deleted' => "Das Löschen war erfolgreich.",
+ 'tidypics:nosettings' => "Der Administrator dieser Community-Seite hat leider bisher keine Einstellungen für Bilderalben vorgenommen.",
+ 'tidypics:exceed_quota' => "Du hast die von Administrator dieser Community-Seite gesetze Quota für Bilder überschritten.",
+ 'tidypics:cannot_upload_exceeds_quota' => 'Das Bild wurde nicht hochgeladen. Die Dateigröße übersteigt die verfügbare Quota.',
+
+ 'album:none' => "Es wurden noch keine Alben hinzugefügt.",
+ 'album:uploadfailed' => "Beim Hinzufügen des Bilderalbums ist ein Fehler aufgetreten.",
+ 'album:deletefailed' => "Beim Löschen des Bilderalbums ist ein Fehler aufgetreten.",
+ 'album:blank' => "Bitte gib diesem Album einen Titel.",
+ 'album:invalid_album' => 'Unzulässiges Bilderalbum.',
+ 'album:cannot_save_cover_image' => 'Das Albumcover kann nicht gespeichert werden.',
+
+ 'image:downloadfailed' => "Entschuldigung. Das Bild ist nicht verfügbar.",
+ 'images:notedited' => "Es konnten nicht alle Bilder fehlerfrei hochgeladen werden.",
+ 'image:blank' => 'Bitte gib diesem Bild einen Titel.',
+ 'image:error' => 'Das Bild konnte nicht gespeichert werden.',
+
+ 'tidypics:upgrade:failed' => "Das Upgrade des Tidypics-Plugins ist gescheitert",
+ );
+
+ add_translation("de",$german);
diff --git a/mod/lightpics/languages/en.php b/mod/lightpics/languages/en.php
new file mode 100644
index 000000000..f4c95825f
--- /dev/null
+++ b/mod/lightpics/languages/en.php
@@ -0,0 +1,262 @@
+<?php
+
+$english = array(
+ // hack for core bug
+ 'untitled' => "untitled",
+
+ // Menu items and titles
+ 'image' => "Image",
+ 'images' => "Images",
+ 'caption' => "Caption",
+ 'photos' => "Photos",
+ 'album' => "Photo Album",
+ 'albums' => "Photo Albums",
+ 'tidypics:disabled' => 'Disabled',
+ 'tidypics:enabled' => 'Enabled',
+ 'admin:settings:photos' => 'Lightpics',
+
+ 'photos:add' => "Create album",
+ 'images:upload' => "Upload photos",
+
+ 'album:slideshow' => "Slideshow",
+ 'album:yours' => "Your photo albums",
+ 'album:yours:friends' => "Your friends' photo albums",
+ 'album:user' => "%s's photo albums",
+ 'album:friends' => "Friends' photo albums",
+ 'album:all' => "All site photo albums",
+ 'photos:group' => "Group photos",
+ 'item:object:image' => "Photos",
+ 'item:object:album' => "Albums",
+ 'tidypics:uploading:images' => "Please wait. Uploading images.",
+ 'tidypics:enablephotos' => 'Enable group photo albums',
+ 'tidypics:editprops' => 'Edit Image Properties',
+ 'tidypics:mostcommented' => 'Most commented images',
+ 'tidypics:mostcommentedthismonth' => 'Most commented this month',
+ 'tidypics:mostcommentedtoday' => 'Most commented today',
+ 'tidypics:mostviewed' => 'Most viewed images',
+ 'tidypics:mostvieweddashboard' => 'Most viewed dashboard',
+ 'tidypics:mostviewedthisyear' => 'Most viewed this year',
+ 'tidypics:mostviewedthismonth' => 'Most viewed this month',
+ 'tidypics:mostviewedlastmonth' => 'Most viewed last month',
+ 'tidypics:mostviewedtoday' => 'Most viewed today',
+ 'tidypics:recentlyviewed' => 'Recently viewed images',
+ 'tidypics:recentlycommented' => 'Recently commented images',
+ 'tidypics:mostrecent' => 'Most recent images',
+ 'tidypics:yourmostviewed' => 'Your most viewed images',
+ 'tidypics:yourmostrecent' => 'Your most recent images',
+ 'tidypics:friendmostviewed' => "%s's most viewed images",
+ 'tidypics:friendmostrecent' => "%s's most recent images",
+ 'tidypics:highestrated' => "Highest rated images",
+ 'tidypics:views' => "%s views",
+ 'tidypics:viewsbyowner' => "by %s users (not including you)",
+ 'tidypics:viewsbyothers' => "(%s by you)",
+ 'tidypics:administration' => 'Tidypics Administration',
+ 'tidypics:stats' => 'Stats',
+ 'tidypics:nophotosingroup' => 'This groups does not have any photos yet',
+ 'tidypics:upgrade' => 'Upgrade',
+ 'tidypics:sort' => 'Sorting the %s album',
+ 'tidypics:none' => 'No photo albums',
+
+ //upload
+ 'tidypics:upload:error' => 'Error:',
+ 'tidypics:upload:maxfilesize' => 'File is too big',
+ 'tidypics:upload:minfilesize' => 'File is too small',
+ 'tidypics:upload:acceptfiletypes' => 'Filetype not allowed',
+ 'tidypics:upload:maxnumberoffiles' => 'Max number of files exceeded',
+
+ //settings
+ 'tidypics:settings' => 'Settings',
+ 'tidypics:settings:main' => 'Primary settings',
+ 'tidypics:settings:image_lib' => "Image Library",
+ 'tidypics:settings:thumbnail' => "Thumbnail Creation",
+ 'tidypics:settings:help' => "Help",
+ 'tidypics:settings:download_link' => "Show download link",
+ 'tidypics:settings:photo_ratings' => "Enable photo ratings (requires rate plugin of Miguel Montes or compatible)",
+ 'tidypics:settings:exif' => "Display EXIF data",
+ 'tidypics:settings:view_count' => "Display view count",
+ 'tidypics:settings:grp_perm_override' => "Allow group members full access to group albums",
+ 'tidypics:settings:maxfilesize' => "Maximum image size in megabytes (MB):",
+ 'tidypics:settings:quota' => "User Quota (MB) - 0 equals no quota",
+ 'tidypics:settings:im_path' => "Enter the path to your ImageMagick commands",
+ 'tidypics:settings:img_river_view' => "How many entries in activity river for each batch of uploaded images",
+ 'tidypics:settings:album_river_view' => "Show the album cover or a set of photos for new album",
+ 'tidypics:settings:largesize' => "Primary image size",
+ 'tidypics:settings:smallsize' => "Album view image size",
+ 'tidypics:settings:tinysize' => "Thumbnail image size",
+ 'tidypics:settings:sizes:instructs' => 'You may need to change the CSS if you change the default sizes',
+ 'tidypics:settings:im_id' => "Image ID",
+ 'tidypics:settings:heading:img_lib' => "Image Library Settings",
+ 'tidypics:settings:heading:main' => "Major Settings",
+ 'tidypics:settings:heading:river' => "Activity Integration Options",
+ 'tidypics:settings:heading:sizes' => "Thumbnail Size",
+ 'tidypics:settings:heading:groups' => "Group Settings",
+ 'tidypics:option:all' => 'All',
+ 'tidypics:option:none' => 'None',
+ 'tidypics:option:cover' => 'Cover',
+ 'tidypics:option:set' => 'Set',
+
+ // server analysis
+ 'tidypics:server_info' => 'Server Information',
+ 'tidypics:server_info:gd_desc' => 'Elgg requires the GD extension to be loaded',
+ 'tidypics:server_info:exec_desc' => 'Required for ImageMagick command line',
+ 'tidypics:server_info:memory_limit_desc' => 'Change memory_limit to increase',
+ 'tidypics:server_info:peak_usage_desc' => 'This is approximately the minimum per page',
+ 'tidypics:server_info:upload_max_filesize_desc' => 'Max size of an uploaded image',
+ 'tidypics:server_info:post_max_size_desc' => 'Max post size = sum of images + html form',
+ 'tidypics:server_info:max_input_time_desc' => 'Time script waits for upload to finish',
+ 'tidypics:server_info:max_execution_time_desc' => 'Max time a script will run',
+
+ 'tidypics:server_info:php_version' => 'PHP Version',
+ 'tidypics:server_info:memory_limit' => 'Memory Available to PHP',
+ 'tidypics:server_info:peak_usage' => 'Memory Used to Load This Page',
+ 'tidypics:server_info:upload_max_filesize' => 'Max File Upload Size',
+ 'tidypics:server_info:post_max_size' => 'Max Post Size',
+ 'tidypics:server_info:max_input_time' => 'Max Input Time',
+ 'tidypics:server_info:max_execution_time' => 'Max Execution Time',
+ 'tidypics:server_info:use_only_cookies' => 'Cookie only sessions',
+
+ 'tidypics:server_config' => 'Server Configuration',
+ 'tidypics:server_configuration_doc' => 'Server configuration documentation',
+
+ // ImageMagick test
+ 'tidypics:lib_tools:testing' =>
+ 'Tidypics needs to know the location of the ImageMagick executables if you have selected it
+ as the image library. Your hosting service should be able to provide this to you. You can test
+ if the location is correct below. If successful, it should display the version of ImageMagick installed
+ on your server.',
+
+ // thumbnail tool
+ 'tidypics:thumbnail_tool' => 'Thumbnail Creation',
+ 'tidypics:thumbnail_tool_blurb' =>
+ 'This page allows you to create thumbnails for images when the thumbnail creation failed during upload.
+ You may experience problems with thumbnail creation if your image library is not configured properly or
+ if there is not enough memory for the GD library to load and resize an image. If your users have
+ experienced problems with thumbnail creation and you have corrected your configuration, you can try to redo the
+ thumbnails. Find the unique identifier of the photo (it is the number near the end of the url when viewing
+ a photo) and enter it below.',
+ 'tidypics:thumbnail_tool:unknown_image' => 'Unable to get original image',
+ 'tidypics:thumbnail_tool:invalid_image_info' => 'Error retrieving information about the image',
+ 'tidypics:thumbnail_tool:create_failed' => 'Failed to create thumbnails',
+ 'tidypics:thumbnail_tool:created' => 'Created thumbnails.',
+
+ //actions
+ 'album:create' => "Create new album",
+ 'album:add' => "Add Photo Album",
+ 'album:addpix' => "Add photos to album",
+ 'album:edit' => "Edit album",
+ 'album:delete' => "Delete album",
+ 'album:sort' => "Sort",
+ 'image:edit' => "Edit image",
+ 'image:delete' => "Delete image",
+ 'image:download' => "Download image",
+
+ //forms
+ 'album:title' => "Title",
+ 'album:desc' => "Description",
+ 'album:tags' => "Tags",
+ 'album:cover' => "Make this image the album cover?",
+ 'album:cover_link' => 'Make cover',
+ 'tidypics:title:quota' => 'Quota',
+ 'tidypics:quota' => "Quota usage:",
+ 'tidypics:uploader:choose' => "Choose photos",
+ 'tidypics:uploader:upload' => "Upload photos",
+ 'tidypics:uploader:describe' => "Describe photos",
+ 'tidypics:uploader:filedesc' => 'Image files (jpeg, png, gif)',
+ 'tidypics:uploader:help' => 'Tip: use <code>Ctrl</code> or <code>Shift</code> keys to select more than one file. You also can drag&drop photos from desktop.',
+ 'tidypics:sort:instruct' => 'Sort the album photos by dragging and dropping the images. Then click the save button.',
+ 'tidypics:sort:no_images' => 'No images found to sort. Upload images using the link above.',
+
+ // albums
+ 'album:num' => '%s photos',
+
+ //views
+ 'image:total' => "Images in album:",
+ 'image:by' => "Image added by",
+ 'album:by' => "Album created by",
+ 'album:created:on' => "Created",
+ 'image:none' => "No images have been added yet.",
+ 'image:back' => "Previous",
+ 'image:next' => "Next",
+ 'image:index' => "%u of %u",
+
+ //rss
+ 'tidypics:posted' => 'posted a photo:',
+
+ //widgets
+ 'tidypics:widget:albums' => "Photo Albums",
+ 'tidypics:widget:album_descr' => "Showcase your photo albums",
+ 'tidypics:widget:num_albums' => "Number of albums to display",
+ 'tidypics:widget:latest' => "Latest Photos",
+ 'tidypics:widget:latest_descr' => "Display your latest photos",
+ 'tidypics:widget:num_latest' => "Number of images to display",
+ 'album:more' => "View all albums",
+
+ // river
+ 'river:create:object:image' => "%s uploaded the photo %s",
+ 'image:river:created' => "%s added a photo to the album %s",
+ 'image:river:created:multiple' => "%s added %u photos to the album %s",
+ 'image:river:item' => "a photo",
+ 'image:river:annotate' => "a comment on the photo",
+ 'river:create:object:album' => "%s created a new photo album %s",
+ 'album:river:group' => "in the group",
+ 'album:river:item' => "an album",
+ 'album:river:annotate' => "a comment on the photo album",
+ 'river:comment:object:image' => '%s commented on the photo %s',
+ 'river:comment:object:album' => '%s commented on the album %s',
+
+ // notifications
+ 'tidypics:newalbum_subject' => 'New photo album',
+ 'tidypics:newalbum' => '%s created a new photo album',
+ 'tidypics:updatealbum' => "%s uploaded new photos to the album %s",
+
+ // Status messages
+ 'tidypics:upl_success' => "Your images uploaded successfully.",
+ 'image:saved' => "Your image was successfully saved.",
+ 'images:saved' => "All images were successfully saved.",
+ 'image:deleted' => "Your image was successfully deleted.",
+ 'image:delete:confirm' => "Are you sure you want to delete this image?",
+ 'images:edited' => "Your images were successfully updated.",
+ 'album:edited' => "Your album was successfully updated.",
+ 'album:saved' => "Your album was successfully saved.",
+ 'album:deleted' => "Your album was successfully deleted.",
+ 'album:delete:confirm' => "Are you sure you want to delete this album?",
+ 'album:created' => "Your new album has been created.",
+ 'album:save_cover_image' => 'Cover image saved.',
+ 'tidypics:settings:save:ok' => 'Successfully saved the Tidypics plugin settings',
+ 'tidypics:album:sorted' => 'The album %s is sorted',
+ 'tidypics:album:could_not_sort' => 'Could not sort the album %s. Make sure there are images in the album and try again.',
+ 'tidypics:upgrade:success' => 'Upgrade of Tidypics a success',
+
+ //Error messages
+ 'tidypics:baduploadform' => "There was an error with the upload form",
+ 'tidypics:partialuploadfailure' => "There were errors uploading some of the images (%s of %s images).",
+ 'tidypics:completeuploadfailure' => "Upload of images failed.",
+ 'tidypics:exceedpostlimit' => "Too many large images - try to upload fewer or smaller images.",
+ 'tidypics:noimages' => "No images were selected.",
+ 'tidypics:image_mem' => "Image is too large - too many bytes",
+ 'tidypics:image_pixels' => "Image has too many pixels",
+ 'tidypics:unk_error' => "Unknown upload error",
+ 'tidypics:save_error' => "Unknown error saving the image on server",
+ 'tidypics:not_image' => "This is not a recognized image type",
+ 'tidypics:deletefailed' => "Sorry. Deletion failed.",
+ 'tidypics:deleted' => "Successful deletion.",
+ 'tidypics:nosettings' => "Admin of this site has not set photo album settings.",
+ 'tidypics:exceed_quota' => "You have exceeded the quota set by the administrator",
+ 'tidypics:cannot_upload_exceeds_quota' => 'Image not uploaded. File size exceeds available quota.',
+
+ 'album:none' => "No albums have been created yet.",
+ 'album:uploadfailed' => "Sorry; we could not save your album.",
+ 'album:deletefailed' => "Your album could not be deleted.",
+ 'album:blank' => "Please give this album a title.",
+ 'album:invalid_album' => 'Invalid album',
+ 'album:cannot_save_cover_image' => 'Cannot save cover image',
+
+ 'image:downloadfailed' => "Sorry; this image is not available.",
+ 'images:notedited' => "Not all images were successfully updated",
+ 'image:blank' => 'Please give this image a title.',
+ 'image:error' => 'Could not save image.',
+
+ 'tidypics:upgrade:failed' => "The upgrade of Tidypics failed",
+);
+
+add_translation("en", $english);
diff --git a/mod/lightpics/languages/es.php b/mod/lightpics/languages/es.php
new file mode 100644
index 000000000..b3c773584
--- /dev/null
+++ b/mod/lightpics/languages/es.php
@@ -0,0 +1,210 @@
+<?php
+$language = array (
+ 'image' => 'Imagen',
+ 'images' => 'Imágenes',
+ 'caption' => 'Leyenda',
+ 'photos' => 'Fotos',
+ 'album' => 'Álbum de fotos',
+ 'albums' => 'Álbumes de fotos',
+ 'tidypics:disabled' => 'Desactivado',
+ 'tidypics:enabled' => 'Activado',
+ 'admin:settings:photos' => 'Lightpics',
+ 'photos:add' => 'Crear álbum',
+ 'images:upload' => 'Subir fotos',
+ 'album:slideshow' => 'Presentación de diapositivas',
+ 'album:yours' => 'Tus álbumes de fotos',
+ 'album:yours:friends' => 'Los álbumes de fotos de tus amigas',
+ 'album:user' => 'Álbumes de %s',
+ 'album:friends' => 'Álbumes de tus amigas',
+ 'album:all' => 'Todos los álbumes del sitio',
+ 'photos:group' => 'Fotos del grupo',
+ 'item:object:image' => 'Fotos',
+ 'item:object:album' => 'Álbumes',
+ 'tidypics:uploading:images' => 'Por favor espera. Subiendo imágenes',
+ 'tidypics:enablephotos' => 'Activar álbumes de fotos del grupo',
+ 'tidypics:editprops' => 'Editar propiedades de la imagen',
+ 'tidypics:mostcommented' => 'Imágenes más comentadas',
+ 'tidypics:mostcommentedthismonth' => 'Más comentadas este mes',
+ 'tidypics:mostcommentedtoday' => 'Más comentadas hoy',
+ 'tidypics:mostviewed' => 'Imágenes más vistas',
+ 'tidypics:mostvieweddashboard' => 'Escritorio más visto',
+ 'tidypics:mostviewedthisyear' => 'Más vistas este año',
+ 'tidypics:mostviewedthismonth' => 'Más vistas este mes',
+ 'tidypics:mostviewedlastmonth' => 'Más vistas el último més',
+ 'tidypics:mostviewedtoday' => 'Más vistas hoy',
+ 'tidypics:recentlyviewed' => 'Vistas recientemente',
+ 'tidypics:recentlycommented' => 'Imágenes comentadas recientemente',
+ 'tidypics:mostrecent' => 'Imágenes más recientes',
+ 'tidypics:yourmostviewed' => 'Tus imágenes más vistas',
+ 'tidypics:yourmostrecent' => 'Tus imágenes más recientes',
+ 'tidypics:friendmostviewed' => 'Imágenes más vistas de %s',
+ 'tidypics:friendmostrecent' => 'Imágenes más recientes de %s',
+ 'tidypics:highestrated' => 'Imágenes mejor valoradas',
+ 'tidypics:views' => '%s vistas',
+ 'tidypics:viewsbyowner' => 'por %s usuarias (sin incluirte)',
+ 'tidypics:viewsbyothers' => '(%s por ti)',
+ 'tidypics:administration' => 'Administración del Tidypics',
+ 'tidypics:stats' => 'Estadísticas',
+ 'tidypics:nophotosingroup' => 'Estos grupos no tienen ninguna foto',
+ 'tidypics:upgrade' => 'Mejorar',
+ 'tidypics:sort' => 'Ordenando el álbum %s',
+ 'tidypics:none' => 'No hay álbumes de fotos',
+ 'tidypics:upload:error' => 'Error:',
+ 'tidypics:upload:maxfilesize' => 'El archivo es demasiado grande',
+ 'tidypics:upload:minfilesize' => 'El archivo es demasiado pequeño',
+ 'tidypics:upload:acceptfiletypes' => 'Tipo de archivo no permitido',
+ 'tidypics:upload:maxnumberoffiles' => 'Número máximo de archivos excedido',
+ 'tidypics:settings' => 'Opciones',
+ 'tidypics:settings:main' => 'Opciones principales',
+ 'tidypics:settings:image_lib' => 'Librería de imágenes',
+ 'tidypics:settings:thumbnail' => 'Creación de previsualizaciones',
+ 'tidypics:settings:help' => 'Ayuda',
+ 'tidypics:settings:download_link' => 'Muestra enlace de descarga',
+ 'tidypics:settings:photo_ratings' => 'Activa puntuación de fotos (requiere la extensión de Miguel Montes o alguna compatible)',
+ 'tidypics:settings:exif' => 'Muestra los datos EXIF',
+ 'tidypics:settings:view_count' => 'Muestra un contador de vistas',
+ 'tidypics:settings:grp_perm_override' => 'Permitir acceso completo a los habitantes de este grupo al album de fotos',
+ 'tidypics:settings:maxfilesize' => 'Tamaño máximo de imagen, en megabytes (MB)',
+ 'tidypics:settings:quota' => 'Cuota de usuario (MB) - 0 significa sin cuota',
+ 'tidypics:settings:im_path' => 'Introduce la ruta a los comandos de ImageMagick',
+ 'tidypics:settings:img_river_view' => 'Cuantas entradas de actividad para cada paquete de imágenes subidas',
+ 'tidypics:settings:album_river_view' => 'Muestra la cubierta del álbum o un conjunto de fotos para un nuevo álbum',
+ 'tidypics:settings:largesize' => 'Tamaño de la imagen primaria',
+ 'tidypics:settings:smallsize' => 'Tamaño de la imagen en la vista de álbum',
+ 'tidypics:settings:tinysize' => 'Tamaño de la imagen de previsualización',
+ 'tidypics:settings:sizes:instructs' => 'Puede que tengas que cambiar el CSS si cambias los tamaños predefinidos',
+ 'tidypics:settings:im_id' => 'ID de imagen',
+ 'tidypics:settings:heading:img_lib' => 'Opciones de la librería de imágenes',
+ 'tidypics:settings:heading:main' => 'Opciones avanzadas',
+ 'tidypics:settings:heading:river' => 'Opciones de integración de actividad',
+ 'tidypics:settings:heading:sizes' => 'Tamaño de la previsualización',
+ 'tidypics:settings:heading:groups' => 'Opciones del grupo',
+ 'tidypics:option:all' => 'Todas',
+ 'tidypics:option:none' => 'Ninguna',
+ 'tidypics:option:cover' => 'Cubierta',
+ 'tidypics:option:set' => 'Conjunto',
+ 'tidypics:server_info' => 'Información del servidor',
+ 'tidypics:server_info:gd_desc' => 'El Elgg requiere que cargues la extensión GD',
+ 'tidypics:server_info:exec_desc' => 'Requerido para la línea de comandos de ImageMagick',
+ 'tidypics:server_info:memory_limit_desc' => 'Cambia el memory_limit para incrementarlo',
+ 'tidypics:server_info:peak_usage_desc' => 'Esto es aproximadamente el mínimo por página',
+ 'tidypics:server_info:upload_max_filesize_desc' => 'Tamaño máximo de una imagen subida',
+ 'tidypics:server_info:post_max_size_desc' => 'Tamaño máximo del mensaje = suma de las imágenes + formulario HTML',
+ 'tidypics:server_info:max_input_time_desc' => 'Tiempo que espera el script de subida para terminar',
+ 'tidypics:server_info:max_execution_time_desc' => 'Tiempo máximo que un script está funcionando',
+ 'tidypics:server_info:php_version' => 'Versión de PHP',
+ 'tidypics:server_info:memory_limit' => 'Memoria disponible para PHP',
+ 'tidypics:server_info:peak_usage' => 'Memoria utilizada para cargar esta página',
+ 'tidypics:server_info:upload_max_filesize' => 'Tamaño máximo de subida de archivos',
+ 'tidypics:server_info:post_max_size' => 'Tamaño máximo de las entradas',
+ 'tidypics:server_info:max_input_time' => 'Tiempo máximo para enviar',
+ 'tidypics:server_info:max_execution_time' => 'Tiempo máximo para la ejecución',
+ 'tidypics:server_info:use_only_cookies' => 'Sesiones sólo con cookies',
+ 'tidypics:server_config' => 'Configuración del servidor',
+ 'tidypics:server_configuration_doc' => 'Documentación de configuración del servidor',
+ 'tidypics:lib_tools:testing' => 'Tidypics necesita conocer la localización de los ejecutables de ImageMagick si lo has seleccionado como librería de imágenes. Tu servicio de hospedaje debería poder proporcionarte esto. Puedes comprobar si la localización es correcta más abajo. Si es así, debería mostrar la versión de ImageMagick que hay instalada en tu servidor.',
+ 'tidypics:thumbnail_tool' => 'Creación de previsualizaciones',
+ 'tidypics:thumbnail_tool_blurb' => 'Esta página te permite crear previsualizaciones para las imágenes cuando la creación de previsualizaciones falla durante la subida. Puedes tener algunos problemas con la creación de vistas previas si tu librería de imágenes no está configurada adecuadamente o si no hay suficiente memoria para que la librería GD cargue y cambie el tamaño de una imange. Si tus usuarias han tenido problemas con la creación de previsualizaciones y tienes configurada correctamente la aplicación, puedes probar de volver a hacer las vistas previas. Encuentra el identificador único de la foto (es el número cerca del final de la URL cuando ves una foto) y escríbelo aquí abajo.',
+ 'tidypics:thumbnail_tool:unknown_image' => 'No se puede conseguir la imagen original',
+ 'tidypics:thumbnail_tool:invalid_image_info' => 'Error al tratar de conseguir información sobre la imagen',
+ 'tidypics:thumbnail_tool:create_failed' => 'No se han podido crear las previsualizaciones',
+ 'tidypics:thumbnail_tool:created' => 'Previsualizaciones creadas.',
+ 'album:create' => 'Crear nuevo álbum',
+ 'album:add' => 'Añadir álbum de fotos',
+ 'album:addpix' => 'Añadir fotos al álbum',
+ 'album:edit' => 'Editar álbum',
+ 'album:delete' => 'Borrar álbum',
+ 'album:sort' => 'Ordenar',
+ 'image:edit' => 'Editar imagen',
+ 'image:delete' => 'Borrar imagen',
+ 'image:download' => 'Descargar imagen',
+ 'album:title' => 'Título',
+ 'album:desc' => 'Descripción',
+ 'album:tags' => 'Etiquetas',
+ 'album:cover' => '¿Hacer de esta imagen la carátula del álbum?',
+ 'album:cover_link' => 'Hacer caráctula',
+ 'tidypics:title:quota' => 'Couta',
+ 'tidypics:quota' => 'Uso de cuota:',
+ 'tidypics:uploader:choose' => 'Elige fotos',
+ 'tidypics:uploader:upload' => 'Sube fotos',
+ 'tidypics:uploader:describe' => 'Descripción de las fotos',
+ 'tidypics:uploader:filedesc' => 'Archivos de imagen (jpeg, png, gif)',
+ 'tidypics:uploader:help' => 'Consejo: utiliza <code>Ctrl</code> o <code>Shift</code> para seleccionar más de un archivo. Además puedes arrastrar fotos desde el escritorio.',
+ 'tidypics:sort:instruct' => 'Ordena las fotos del álbum arrastrándolas. Haz clic después en el botón de guardar.',
+ 'tidypics:sort:no_images' => 'No se han encontrado imágenes para ordenar. Sube imágenes con el enlace de más arriba.',
+ 'album:num' => '%s fotos',
+ 'image:total' => 'Imágenes en el álbum:',
+ 'image:by' => 'Imagen añadida por',
+ 'album:by' => 'Álbum creado por',
+ 'album:created:on' => 'Creado',
+ 'image:none' => 'No se ha añadido ninguna imagen aún.',
+ 'image:back' => 'Previa',
+ 'image:next' => 'Siguiente',
+ 'image:index' => '%u de %u',
+ 'tidypics:posted' => 'ha subido una foto:',
+ 'tidypics:widget:albums' => 'Álbums de fotos',
+ 'tidypics:widget:album_descr' => 'Muestra tus álbumes',
+ 'tidypics:widget:num_albums' => 'Número de álbums a mostrar',
+ 'tidypics:widget:latest' => 'Últimas fotos',
+ 'tidypics:widget:latest_descr' => 'Muestra tus últimas fotos',
+ 'tidypics:widget:num_latest' => 'Número de imágenes a mostrar',
+ 'album:more' => 'Ver todos los álbumes',
+ 'river:create:object:image' => '%s ha subido la foto %s',
+ 'image:river:created' => '%s ha añadido una foto al álbum %s',
+ 'image:river:created:multiple' => '%s ha añadido %u fotos al álbum %s',
+ 'image:river:item' => 'una foto',
+ 'image:river:annotate' => 'un comentario en la foto',
+ 'river:create:object:album' => '%s ha creado un nuevo álbum de fotos %s',
+ 'album:river:group' => 'en el grupo',
+ 'album:river:item' => 'un álbum',
+ 'album:river:annotate' => 'un comentario en el álbum de fotos',
+ 'river:comment:object:image' => '%s ha comentado en la foto %s',
+ 'river:comment:object:album' => '%s ha comentado en el álbum %s',
+ 'tidypics:newalbum_subject' => 'Nuevo álbum de fotos',
+ 'tidypics:newalbum' => '%s ha creado un álbum de fotos nuevo',
+ 'tidypics:updatealbum' => '%s ha subido nuevas fotos al álbum %s',
+ 'tidypics:upl_success' => 'Se han subido correctamente tus imágenes.',
+ 'image:saved' => 'Se ha guardado correctamente tu imagen.',
+ 'images:saved' => 'Todas las imágenes se han guardado correctamente.',
+ 'image:deleted' => 'Se ha eliminado correctamente tu imagen.',
+ 'image:delete:confirm' => '¿Estás segura de querer borrar esta imagen?',
+ 'images:edited' => 'Tus imágenes se han actualizado correctamente.',
+ 'album:edited' => 'Tu álbum se ha actualizado correctamente.',
+ 'album:saved' => 'Tu álbum se ha guardado correctamente.',
+ 'album:deleted' => 'Tu álbum se ha borrado correctamente.',
+ 'album:delete:confirm' => '¿Estás segura de querer borrar este álbum?',
+ 'album:created' => 'Tu nuevo álbum ha sido creado.',
+ 'album:save_cover_image' => 'Imagen de carátula guardada.',
+ 'tidypics:settings:save:ok' => 'Se han guardado correctamente las opciones del plugin Tidypics',
+ 'tidypics:album:sorted' => 'El álbum %s está ordenado',
+ 'tidypics:album:could_not_sort' => 'No se puede ordenar el álbum %s. Asegúrate de que hay imágenes en el álbum y vuélvelo a intentar.',
+ 'tidypics:upgrade:success' => 'La mejora de Tidypics ha sido un éxito',
+ 'tidypics:baduploadform' => 'Ha habido un error con el formulario de subida',
+ 'tidypics:partialuploadfailure' => 'Ha habido algunos errores al subir algunas de las imágenes (%s de %s).',
+ 'tidypics:completeuploadfailure' => 'La subida de imágenes ha fallado.',
+ 'tidypics:exceedpostlimit' => 'Las imágenes son demasiado grandes - inténtalo subiendo menos imágenes o más pequeñas.',
+ 'tidypics:noimages' => 'No has seleccionado ninguna imagen.',
+ 'tidypics:image_mem' => 'La imagen es demasiado grande - demasiados bytes',
+ 'tidypics:image_pixels' => 'La imagen tiene demasiados píxeles',
+ 'tidypics:unk_error' => 'Error desconocido durante la carga',
+ 'tidypics:save_error' => 'Error desconocido al guardar la imagen en el servidor',
+ 'tidypics:not_image' => 'No es un tipo de imagen reconocido',
+ 'tidypics:deletefailed' => 'Lo sentimos, el borrado ha fallado.',
+ 'tidypics:deleted' => 'Borrado correcto.',
+ 'tidypics:nosettings' => 'La administradora del sitio no ha ajustado las opciones para los álbumes.',
+ 'tidypics:exceed_quota' => 'Has excedido la cuota establecida por la administradora',
+ 'tidypics:cannot_upload_exceeds_quota' => 'Imagen no subida. El archivo supera la cuota disponible.',
+ 'album:none' => 'No se ha creado ningún álbum todavía.',
+ 'album:uploadfailed' => 'Lo sentimos, no podemos guardar tu álbum.',
+ 'album:deletefailed' => 'Tu álbum no puede ser borrado.',
+ 'album:blank' => 'Por favor, proporciona un título al álbum.',
+ 'album:invalid_album' => 'Álbum inválido',
+ 'album:cannot_save_cover_image' => 'No se puede guardar la imagen de cubierta',
+ 'image:downloadfailed' => 'Lo sentimos, esta imagen no está disponible.',
+ 'images:notedited' => 'No se han podido actualizar correctamente todas las fotos',
+ 'image:blank' => 'Por favor proporciona un título a la imagen.',
+ 'image:error' => 'No se puede guardar la imagen.',
+ 'tidypics:upgrade:failed' => 'La mejora de Tidypics ha fallado',
+ 'untitled' => 'Sin T&iacute;tulo',
+);
+add_translation("es", $language); \ No newline at end of file
diff --git a/mod/lightpics/languages/fr.php b/mod/lightpics/languages/fr.php
new file mode 100644
index 000000000..0b9ceac49
--- /dev/null
+++ b/mod/lightpics/languages/fr.php
@@ -0,0 +1,140 @@
+<?php
+
+// Generated by TranslationBrowser 20091025-06:26:52 PM
+
+$french = array(
+ 'untitled' => "Sans titre" ,
+ 'image' => "Image" ,
+ 'images' => "Images" ,
+ 'caption' => "Légende" ,
+ 'photos' => "Photos" ,
+ 'images:upload' => "Charger des images" ,
+ 'images:multiupload:todo' => "Sélectionnez un ou plusieurs fichiers à charger." ,
+ 'album' => "Album photo" ,
+ 'albums' => "Albums photo" ,
+ 'album:slideshow' => "Voir diaporama" ,
+ 'album:yours' => "Vos albums photo" ,
+ 'album:yours:friends' => "Les albums photo de vos amis" ,
+ 'album:user' => "Albums photo de %s" ,
+ 'album:friends' => "Albums photo des amis de %s" ,
+ 'album:all' => "Tout les albums photo" ,
+ 'album:group' => "Albums de groupe" ,
+ 'item:object:image' => "Photos" ,
+ 'item:object:album' => "Albums" ,
+ 'tidypics:uploading:images' => "Veuillez patientez, nous chargeons les fichiers." ,
+ 'tidypics:enablephotos' => "Activer les albums photo pour les groupes" ,
+ 'tidypics:editprops' => "Modifier les propriétés des images" ,
+ 'tidypics:mostcommented' => "Images les plus commentées." ,
+ 'tidypics:mostcommentedthismonth' => "Les plus commentés ce mois-ci" ,
+ 'tidypics:mostcommentedtoday' => "Les plus commentés ce jour-ci" ,
+ 'tidypics:mostviewed' => "Images les plus vues" ,
+ 'tidypics:mostvieweddashboard' => "Tableaux de bord les plus vus" ,
+ 'tidypics:mostviewedthisyear' => "Les plus vus cette année." ,
+ 'tidypics:mostviewedthismonth' => "Les plus vus ce mois-ci" ,
+ 'tidypics:mostviewedlastmonth' => "Les plus vus le mois dernier" ,
+ 'tidypics:mostviewedtoday' => "Les plus vus aujourd'hui" ,
+ 'tidypics:recentlyviewed' => "Images récemment vues" ,
+ 'tidypics:recentlycommented' => "Image récemment commentées" ,
+ 'tidypics:mostrecent' => "Images les plus récentes" ,
+ 'tidypics:yourmostviewed' => "Vos images les plus vues" ,
+ 'tidypics:yourmostrecent' => "Vos images les plus récentes" ,
+ 'tidypics:friendmostviewed' => "Les images les plus vues de %s" ,
+ 'tidypics:friendmostrecent' => "Les images les plus récentes de %s" ,
+ 'tidypics:highestrated' => "Les images les mieux notées" ,
+ 'tidypics:views' => "Vues: %s" ,
+ 'tidypics:viewsbyowner' => "par % membres (vous exclus)" ,
+ 'tidypics:viewsbyothers' => "(%s par vous)" ,
+ 'tidypics:administration' => "Administration Tydipics" ,
+ 'tidypics:stats' => "Stats" ,
+ 'tidypics:settings' => "Paramétrages" ,
+ 'tidypics:admin:instructions' => "Réglages principaux de Tydipics. Modifiez les pour votre usage et cliquez sur Sauvegarder" ,
+ 'tidypics:settings:image_lib' => "Librairie graphique" ,
+ 'tidypics:settings:thumbnail' => "Création des vignettes" ,
+ 'tidypics:settings:download_link' => "Voir le lien de download" ,
+ 'tidypics:settings:photo_ratings' => "Activer les notations des photos (nécessite le rate plugin de Miguel Montes ou compatible)" ,
+ 'tidypics:settings:exif' => "Voir les données EXIF" ,
+ 'tidypics:settings:view_count' => "Voir le compteur" ,
+ 'tidypics:settings:grp_perm_override' => "Autoriser l'acés total aux membres du groupe" ,
+ 'tidypics:settings:maxfilesize' => "Taille maximum des images en Mb:" ,
+ 'tidypics:settings:quota' => "Quota Utilisateur/Groupe (Mb) - O égal pas de quota" ,
+ 'tidypics:settings:im_path' => "Chemin de l'exécutable ImageMagick, terminé par un slash" ,
+ 'tidypics:settings:img_river_view' => "Combien d'entrées dans le river pour chaque lot de traitement des fichiers chargés" ,
+ 'tidypics:settings:album_river_view' => "Montrer la couverture de l'album ou un ensemble de photos pour tout nouvel album" ,
+ 'tidypics:settings:largesize' => "Taille initiale de l'image" ,
+ 'tidypics:settings:smallsize' => "Taille de la vue de l'album" ,
+ 'tidypics:settings:thumbsize' => "Taille des vignettes" ,
+ 'tidypics:settings:im_id' => "Identifiant de l'image" ,
+ 'album:create' => "Créer un nouvel album" ,
+ 'album:add' => "Ajouter un Album photo" ,
+ 'album:addpix' => "Ajouter des photos à l'album" ,
+ 'album:edit' => "Modifier l'album" ,
+ 'album:delete' => "Supprimer l'album" ,
+ 'image:edit' => "Modifier l'image" ,
+ 'image:delete' => "Supprimer l'image" ,
+ 'image:download' => "Télécharger l'image" ,
+ 'album:title' => "Titre" ,
+ 'album:desc' => "Description" ,
+ 'album:tags' => "Tags" ,
+ 'album:cover' => "Faire de cette image la couverture de l'album" ,
+ 'tidypics:quota' => "Quota utilisé:" ,
+ 'image:total' => "Images dans l'album:" ,
+ 'image:by' => "Image ajoutée par" ,
+ 'album:by' => "Album créé par" ,
+ 'album:created:on' => "Création" ,
+ 'image:none' => "Aucune image n'a encore été ajoutée" ,
+ 'image:back' => "Précédent" ,
+ 'image:next' => "Suivant" ,
+ 'tidypics:actiondelete' => "Supprimer" ,
+ 'tidypics:actioncancel' => "Annuler" ,
+ 'tidypics:inthisphoto' => "Dans cette photo" ,
+ 'tidypics:posted' => "a posté une photo" ,
+ 'tidypics:widget:albums' => "Albums photo" ,
+ 'tidypics:widget:album_descr' => "Échantillon de vos albums photo" ,
+ 'tidypics:widget:num_albums' => "Nombre de photos à montrer" ,
+ 'tidypics:widget:latest' => "Dernières photos" ,
+ 'tidypics:widget:latest_descr' => "Montrer les dernières photos" ,
+ 'tidypics:widget:num_latest' => "Nombre d'images à montrer" ,
+ 'album:more' => "Voir tout les albums" ,
+ 'image:river:created' => "%s a ajouté la photo %s à l'album %s" ,
+ 'image:river:item' => "une photo" ,
+ 'image:river:annotate' => "commentaire sur la photo" ,
+ 'album:river:created' => "%s a créé un nouvel album photo" ,
+ 'album:river:group' => "dans le groupe" ,
+ 'album:river:item' => "un album" ,
+ 'album:river:annotate' => "un commentaire sur l'album photo" ,
+ 'tidypics:newalbum' => "Nouvel album photo" ,
+ 'tidypics:upl_success' => "Vos images ont été correctement chargées." ,
+ 'image:saved' => "Votre image a été correctement enregistrée" ,
+ 'images:saved' => "Toutes les images ont été correctement enregistrées" ,
+ 'image:deleted' => "Votre image a correctement été supprimée" ,
+ 'image:delete:confirm' => "Confirmez-vous la suppression de cette image" ,
+ 'images:edited' => "Vos images ont été correctement mises à jour" ,
+ 'album:edited' => "Votre album a correctement été mis à jour" ,
+ 'album:saved' => "Votre album a correctement été enregistré" ,
+ 'album:deleted' => "Votre album a correctement été supprimé" ,
+ 'album:delete:confirm' => "Confirmez-vous la suppression de cet album" ,
+ 'album:created' => "Votre nouvel album a été créé" ,
+ 'tidypics:settings:save:ok' => "Réglages du plugin Tydipics enregistrés" ,
+ 'tidypics:upgrade:success' => "Mise à jour de Tydipics effectuée" ,
+ 'tidypics:partialuploadfailure' => "Des erreurs sont survenues durant le chargement des images (%s sur %s images)" ,
+ 'tidypics:completeuploadfailure' => "Echec du chargement des images " ,
+ 'tidypics:exceedpostlimit' => "Trop d'images trop lourdes - essayez de charges des images plus petites" ,
+ 'tidypics:noimages' => "Aucune image sélectionnée" ,
+ 'tidypics:image_mem' => "Image trop large - taille trop grosse" ,
+ 'tidypics:image_pixels' => "L'image a trop de pixels" ,
+ 'tidypics:unk_error' => "Erreur inconnue de chargement" ,
+ 'tidypics:save_error' => "Erreur inconnue lors de l'enregistrement de l'image sur le serveur" ,
+ 'tidypics:not_image' => "Type d'image non reconnu" ,
+ 'image:deletefailed' => "Votre image n'a pu être supprimée" ,
+ 'image:downloadfailed' => "Désolé, image indisponible pour le moment" ,
+ 'tidypics:nosettings' => "L'administrateur n'a pas effectué les reglages minimaux des albums" ,
+ 'tidypics:exceed_quota' => "Quota fixé par l'administrateur dépassé" ,
+ 'images:notedited' => "Toutes les images n'ont pas été correctement mises à jour" ,
+ 'album:none' => "Aucun album encore créé" ,
+ 'album:uploadfailed' => "Désolé, nous ne pouvons pas enregistrer l'album" ,
+ 'album:deletefailed' => "Votre album ne peut pas être supprimé pour le moment" ,
+ 'album:blank' => "Donnez un titre et une description à cet album" ,
+ 'tidypics:upgrade:failed' => "Mise à jour de Tydipics infructueuse"
+);
+
+add_translation('fr', $french);
diff --git a/mod/lightpics/languages/he.php b/mod/lightpics/languages/he.php
new file mode 100644
index 000000000..d2a2bb9ab
--- /dev/null
+++ b/mod/lightpics/languages/he.php
@@ -0,0 +1,135 @@
+<?php
+
+// Generate By translationbrowser.
+
+$hebrew = array(
+ 'image' => "תמונה" ,
+ 'images' => "תמונות" ,
+ 'caption' => "כיתוב" ,
+ 'photos' => "תמונות" ,
+ 'images:upload' => "העלאת תמונות" ,
+ 'images:multiupload' => "כלי פלאש להעלאת תמונות רבות" ,
+ 'images:multiupload:todo' => "בחרו קובץ אחד או יותר" ,
+ 'album' => "אלבום תמונות" ,
+ 'albums' => "אלבומי תמונות" ,
+ 'album:slideshow' => "צפיה כמצגת" ,
+ 'album:yours' => "אלבומי התמונות שלך" ,
+ 'album:yours:friends' => "אלבומי תמונות של חברים" ,
+ 'album:user' => "אלבומי תמונות של %s" ,
+ 'album:friends' => "אלבומי התמונות של החברים של %s " ,
+ 'album:all' => "כל אלבומי התמונות באתר" ,
+ 'album:group' => "אלבומים קבוצתיים" ,
+ 'item:object:image' => "תמונות" ,
+ 'item:object:album' => "אלבומים" ,
+ 'tidypics:enablephotos' => "אפשר אלבומי תמונות קבוצתיים" ,
+ 'tidypics:editprops' => "ערוך מאפייני תמונה" ,
+ 'tidypics:mostcommented' => "תמונות עם הכי הרבה תגובות" ,
+ 'tidypics:mostcommentedthismonth' => "הכי הרבה תגובות בחודש זה" ,
+ 'tidypics:mostcommentedtoday' => "הכי הרבה תגובות היום" ,
+ 'tidypics:mostviewed' => "תמונות אשר נצפו הכי הרבה" ,
+ 'tidypics:mostvieweddashboard' => "הדף אשר נצפה הכי הרבה" ,
+ 'tidypics:mostviewedthisyear' => "נצפה הכי הרבה השנה" ,
+ 'tidypics:mostviewedthismonth' => "נצפה הכי הרבה החודש" ,
+ 'tidypics:mostviewedlastmonth' => "נצפה הכי הרבה בחודש שעבר" ,
+ 'tidypics:mostviewedtoday' => "נצפה הכי הרבה היום" ,
+ 'tidypics:recentlyviewed' => "תמונות אשר נצפו לאחרונה" ,
+ 'tidypics:mostrecent' => "תמונות חדשות" ,
+ 'tidypics:yourmostviewed' => "תמונות שלך אשר נצפו הכי הרבה" ,
+ 'tidypics:yourmostrecent' => "התמונות הכי חדשות שלך" ,
+ 'tidypics:friendmostviewed' => "תמונות של %s אשר נצפו הכי הרבה" ,
+ 'tidypics:friendmostrecent' => "תמונות החדשות ביותר של %s" ,
+ 'tidypics:highestrated' => "תמונות בעלי הדירוג הגבוה ביותר" ,
+ 'tidypics:viewsbyowner' => "צפיות: %s על ידי %s משתמשים (לא כולל אותך)" ,
+ 'tidypics:viewsbyothers' => "צפיות: %s (%s על ידיך)" ,
+ 'tidypics:administration' => "ניהול Tidypics" ,
+ 'tidypics:stats' => "סטטיסטיקה" ,
+ 'tidypics:settings' => "הגדרות" ,
+ 'tidypics:admin:instructions' => "אלה הגדרות הליבה של Tidypics. שנה אותן לפי הצורך ולחץ על שמור" ,
+ 'tidypics:settings:image_lib' => "ספריית תמונות:" ,
+ 'tidypics:settings:download_link' => "הצג לינק להורדה" ,
+ 'tidypics:settings:photo_ratings' => "אפשר דירוג תמונות, דורש תוסף דירוג של Miguel Montes או דומה" ,
+ 'tidypics:settings:exif' => "הצג נתוני EXIF" ,
+ 'tidypics:settings:view_count' => "מונה צפיה" ,
+ 'tidypics:settings:grp_perm_override' => "אפשר לחברי קבוצה גישה מלאה לאלבומים קבוצתיים" ,
+ 'tidypics:settings:maxfilesize' => "גודל תמונה מירבי בMB" ,
+ 'tidypics:settings:quota' => "מכסה בMB למשתמש/קבוצה - 0 שווה ללא מכסה" ,
+ 'tidypics:settings:im_path' => "הזן את הנטיב לפקודות ImageMagick ללא לוכסן בסוף" ,
+ 'tidypics:settings:img_river_view' => "כמה רשומות במה חדש עבור כל מקבץ תמונות אשר נוסף" ,
+ 'tidypics:settings:album_river_view' => "הצג תמונה אחת או מקבץ תמונות עבור אלבום חדש" ,
+ 'tidypics:settings:largesize' => "גודל תמונה בסיסי" ,
+ 'tidypics:settings:smallsize' => "גודל תמונה בתצוגת אלבום" ,
+ 'tidypics:settings:thumbsize' => "גודל תמונה ממוזערת" ,
+ 'album:create' => "יצירת אלבום חדש" ,
+ 'album:add' => "הוספת אלבום" ,
+ 'album:addpix' => "הוספת תמונות לאלבום" ,
+ 'album:edit' => "עריכת אלבום" ,
+ 'album:delete' => "מחיקת אלבום" ,
+ 'image:edit' => "עריכת תמונה" ,
+ 'image:delete' => "מחיקת תמונה" ,
+ 'image:download' => "הורדת תמונה" ,
+ 'album:title' => "כותרת" ,
+ 'album:desc' => "תאור" ,
+ 'album:tags' => "תגים" ,
+ 'album:cover' => "קבע תמונה זו כעטיפה " ,
+ 'tidypics:quota' => "שימוש מכסה" ,
+ 'image:total' => "תמונות באלבום:" ,
+ 'image:by' => "תמונה נוספה על ידי" ,
+ 'album:by' => "אלבום נוצר על ידי" ,
+ 'album:created:on' => "יצר/ה" ,
+ 'image:none' => "טרם נוספו תמונות" ,
+ 'image:back' => "הקודם" ,
+ 'image:next' => "הבא" ,
+ 'tidypics:actiondelete' => "מחיקה" ,
+ 'tidypics:actioncancel' => "ביטול" ,
+ 'tidypics:inthisphoto' => "בתמונה זו" ,
+ 'tidypics:posted' => "הוסיף/ה תמונה" ,
+ 'tidypics:widget:albums' => "אלבומי תמונות" ,
+ 'tidypics:widget:album_descr' => "הצג את האלבומים החדשים שלך" ,
+ 'tidypics:widget:num_albums' => "מספר האלבומים לתצוגה" ,
+ 'tidypics:widget:latest' => "Latest Photos",
+ 'tidypics:widget:latest_descr' => "Display your latest photos",
+ 'tidypics:widget:num_latest' => "Number of images to display",
+ 'album:more' => "צפיה בכל האלבומים" ,
+ 'image:river:created' => "%s הוסיף/ה את התמונה %s לאלבום %s" ,
+ 'image:river:item' => "תמונה" ,
+ 'image:river:annotate' => "תגובה לתמונה" ,
+ 'album:river:created' => "%s יצר/ה אלבום תמונות חדש" ,
+ 'album:river:group' => "בקבוצה" ,
+ 'album:river:item' => "אלבום" ,
+ 'album:river:annotate' => "תגובה באלבום התמונות" ,
+ 'tidypics:newalbum' => "אלבום תמונות חדש" ,
+ 'tidypics:upl_success' => "התמונות הועלו בהצלחה" ,
+ 'image:saved' => "התמונה נשמרה בהצלחה" ,
+ 'images:saved' => "כל התמונות נשמרו בהצלחה" ,
+ 'image:deleted' => "התמונה נמחקה בהצלחה" ,
+ 'image:delete:confirm' => "בטוח? למחוק תמונה זו?" ,
+ 'images:edited' => "התמונות עודכנו בהצלחה" ,
+ 'album:edited' => "האלבום עודכן בהצלחה" ,
+ 'album:saved' => "האלבום נשמר בהצלחה" ,
+ 'album:deleted' => "האלבום נמחק בהצלחה" ,
+ 'album:delete:confirm' => "בטוח? למחוק אלבום זה?" ,
+ 'album:created' => "האלבום החדש נוצר" ,
+ 'tidypics:settings:save:ok' => "הגדרות התוסף Tidypics נשמרו בהצלחה" ,
+ 'tidypics:upgrade:success' => "Tidypics שודרג בהצלחה" ,
+ 'tidypics:partialuploadfailure' => "אירעו שגיאות בהעלאת חלק מהתמונות (%s מתוך %s תמונות)." ,
+ 'tidypics:completeuploadfailure' => "העלאת התמונות נכשל" ,
+ 'tidypics:exceedpostlimit' => "יותר מדי תמונות גדולות - נסה פחות תמונות או תמונות קטנות יותר" ,
+ 'tidypics:noimages' => "לא נבחרו תמונות" ,
+ 'tidypics:image_mem' => "התמונה גדולה מדי - יותר מדי ביטים" ,
+ 'tidypics:image_pixels' => "לתמונה יותר מדי פיקסלים" ,
+ 'tidypics:unk_error' => "שגיאה לא מוכרת בהעלאת תמונות" ,
+ 'tidypics:save_error' => "שגיאה לא מוכרת בשמירת תמונות" ,
+ 'tidypics:not_image' => "סוק הקובץ לא מזוהה" ,
+ 'image:deletefailed' => "לא ניתן היה למחוק תמונה זו " ,
+ 'image:downloadfailed' => "מצטערים התמונה אינה זמינה כרגע" ,
+ 'tidypics:nosettings' => "מנהל הרשת טרם קבע הגדרות עבור אלבומי תמונות" ,
+ 'tidypics:exceed_quota' => "עברת את מגבלת המכסה הנקבע על ידי מנהל הרשת" ,
+ 'images:notedited' => "לא עודכנו כל התמונות בהצלחה" ,
+ 'album:none' => "טרם נוצרו אלבומים" ,
+ 'album:uploadfailed' => "מצטערים לא הצלחנו לשמור את האלבום שלך" ,
+ 'album:deletefailed' => "לא ניתן היה למחוק את האלבום" ,
+ 'album:blank' => "אנא קבע שם ותאור עבור אלבום זה" ,
+ 'tidypics:upgrade:failed' => "שדרוג Tidypics נכשל"
+);
+
+add_translation('he', $hebrew);
diff --git a/mod/lightpics/languages/it.php b/mod/lightpics/languages/it.php
new file mode 100644
index 000000000..6b42fe655
--- /dev/null
+++ b/mod/lightpics/languages/it.php
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * Italian language pack
+ */
+
+$italian = array(
+
+ // Menu items and titles
+ 'image' => "Immagine",
+ 'images' => "Immagini",
+ 'caption' => "Caption",
+ 'photos' => "Foto",
+ 'album' => "Album fotografico",
+ 'albums' => "Album fotografici",
+ 'album:yours' => "Il tuo Album fotografico",
+ 'album:yours:friends' => "Album Foto degli amici",
+ 'album:user' => "Album foto di %s",
+ 'album:friends' => "Album foto degli amici di %s",
+ 'album:all' => "Tutti gli album di foto",
+ 'album:group' => "Gruppi di album",
+ 'item:object:image' => "Foto",
+ 'item:object:album' => "Album",
+ 'tidypics:settings:maxfilesize' => "Dimensione massima dei file in kilo bytes (KB):",
+ 'tidypics:enablephotos' => 'Attiva Group Photo Albums',
+ 'tidypics:editprops' => 'Modifica le proprietà della immagine',
+ 'tidypics:upgrade' => 'Aggiorna',
+ 'tidypics:sort' => 'Ordina il %s album',
+
+ //Settings
+ 'tidypics:settings:server:analysis' => 'Esegui Analisi del Server',
+
+ //actions
+ 'album:create' => "Crea un nuovo album",
+ 'album:add' => "Aggiungi un foto Album",
+ 'album:addpix' => "Aggiungi foto all'album",
+ 'album:edit' => "Modifica album",
+ 'album:delete' => "Cancella album",
+ 'album:sort' => "Ordina album",
+ 'image:edit' => "Modifica immagine",
+ 'image:delete' => "Cancella immagine",
+ 'image:download' => "Download immagine",
+
+ //forms
+ 'album:title' => "Titolo",
+ 'album:desc' => "Descrizione",
+ 'album:tags' => "Tags",
+ 'album:cover' => "Copertina dell'album?",
+ 'album:cover:yes' => "Si",
+ 'image:access:note' => "(view access is inherited from the album)",
+ 'tidypics:uploader:choose' => "Scegli le Foto",
+ 'tidypics:uploader:upload' => "Carica le Foto",
+ 'tidypics:uploader:describe' => "Descrivi le Foto",
+ 'tidypics:uploader:basic' => 'Puoi caricare fino a 10 Foto alla volta (%s MB massimo per foto)',
+ 'tidypics:sort:instruct' => 'Ordina i Foto Album trascinando le immagini. Poi clicca il bottone salva.',
+
+ //views
+ 'image:total' => "immagini nell'album:",
+ 'image:by' => "immagini aggiunte da",
+ 'album:by' => "Album creato da",
+ 'album:created:on' => "Creato",
+ 'image:none' => "Non sono ancora state aggiunte immagini.",
+ 'image:back' => "Indietro",
+ 'image:next' => "Avanti",
+
+ //widgets
+ 'album:widget' => "Album Fotografici",
+ 'album:more' => "Vedi tutti gli albums",
+ 'album:widget:description' => "Visualizza il tuo ultimo album fotografico",
+ 'album:display:number' => "Numero di album da mostrare",
+ 'album:num_albums' => "Numero di album da mostrare",
+
+ // river
+ 'image:river:created' => "%s uploaded %s %s",
+ 'image:river:created:multiple' => "%s added %u photos to album %s",
+ 'image:river:item' => "una immagine",
+ 'image:river:annotate' => "a comment on the image",
+ 'album:river:created' => "%s created a new photo album: ",
+ 'album:river:item' => "an album",
+ 'album:river:annotate' => "un commento nel foto album",
+
+ // notifications
+ 'tidypics:newalbum' => 'Nuovo album di foto',
+
+
+ // Status messages
+ 'image:saved' => "Immagine salvata.",
+ 'images:saved' => "Tutte le immagini sono state salvate.",
+ 'image:deleted' => "Immagine cancellata.",
+ 'image:delete:confirm' => "Sei sicuro di volerla cancellare?",
+ 'images:edited' => "Immagini modificate.",
+ 'album:edited' => "Album fotografico aggiornato.",
+ 'album:saved' => "Album fotografico salvato.",
+ 'album:deleted' => "L'album � stato cancellato.",
+ 'album:delete:confirm' => "Sei sicuro di voler cancellare questo album?",
+ 'album:created' => "Nuovo Album creato.",
+ 'tidypics:status:processing' => "Attendere ....",
+ 'tidypics:album:sorted' => "L'album %s è stato ordinato",
+
+ //Error messages
+ 'image:none' => "Non ci sono immagini.",
+ 'image:uploadfailed' => "il file non � stato caricato:",
+ 'image:deletefailed' => "L'immagine non puo essere cancellata in questo momento.",
+ 'image:downloadfailed' => "Spiacente; questa immagine non � attualmente disponibile.",
+ 'image:notimage' => "Sono accettate immagini jpg, gif o png delle dimensioni entro i limiti.",
+ 'images:notedited' => "Non tutte le immagini sono state caricate",
+ 'album:none' => "Nessun album � stato ancora creato.",
+ 'album:uploadfailed' => "Spiacente; non � possibile salvare l'album.",
+ 'album:deletefailed' => "L'album non può essere cancellato.",
+ 'album:blank' => "Dai a quest'album un titolo e una descrizione."
+);
+
+add_translation("it", $italian);
diff --git a/mod/lightpics/languages/pl.php b/mod/lightpics/languages/pl.php
new file mode 100644
index 000000000..cdc4d3711
--- /dev/null
+++ b/mod/lightpics/languages/pl.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * Elgg tidypics plugin Polish language pack
+ *
+ */
+
+$polish = array(
+
+ // Menu items and titles
+ 'image' => "Obrazek",
+ 'images' => "Obrazki",
+ 'caption' => "Opis",
+ 'photos' => "Zdjęcia",
+ 'images:upload' => "Dodaj obrazki",
+ 'album' => "Album zdjęciowy",
+ 'albums' => "Albumy zdjęciowe",
+ 'album:yours' => "Twoje albumy",
+ 'album:yours:friends' => "Albumy twoich znajomych",
+ 'album:user' => "Albumy użytkownika %s",
+ 'album:friends' => "Albumy przyjaciół użytkownika %s",
+ 'album:all' => "Wszystkie publiczne albumy",
+ 'album:group' => "Albumy rejsu",
+ 'item:object:image' => "Zdjęcia",
+ 'item:object:album' => "Albumy",
+ 'tidypics:settings:maxfilesize' => "Maximum file size in kilo bytes (KB):",
+ 'tidypics:editprops' => 'Edycja obrazu Właściwości',
+
+ //actions
+ 'album:create' => "Nowy album",
+ 'album:add' => "Dodaj album zdjęciowy",
+ 'album:addpix' => "Dodaj zdjęcia",
+ 'album:edit' => "Edytuj album",
+ 'album:delete' => "Skasuj album",
+ 'image:edit' => "Edytuj obrazek",
+ 'image:delete' => "Skasuj obrazek",
+ 'image:download' => "Pobierz obrazek",
+
+ //forms
+ 'album:title' => "Tytuł albumu",
+ 'album:desc' => "Opis (widoczny tylko dla twórcy)",
+ 'album:tags' => "Tagi",
+ 'album:cover' => "Ustaw jako okładkę albumu?",
+ 'album:cover:yes' => "Tak",
+ 'image:access:note' => "(prawa dostępu pobierane są z ustawień albumu)",
+
+ //views
+ 'image:total' => "Obrazki w albumie:",
+ 'image:by' => "Obrazek dodany przez",
+ 'album:by' => "Album stworzony przez",
+ 'album:created:on' => "Stworzono",
+ 'image:none' => "Nie dodano jeszcze żadnych obrazków.",
+ 'image:back' => "Poprzednia",
+ 'image:next' => "Kolejna",
+
+ //widgets
+ 'album:widget' => "Albumy zdjęciowe",
+ 'album:more' => "Pokaż wszystkie albumy",
+ 'album:widget:description' => "Pokazuje twoje ostatnie albumy zdjęciowe",
+ 'album:display:number' => "Liczba wyświetlanych albumów",
+ 'album:num_albums' => "Liczba wyświetlanych albumów",
+
+ // river
+ 'image:river:created' => "wgrano %s %s %s",
+ 'image:river:item' => "obrazek",
+ 'image:river:annotate' => "%s skomentował",
+ 'album:river:created' => "Stworzono %s",
+ 'album:river:item' => "album",
+ 'album:river:annotate' => "%s skomentował",
+
+ // Status messages
+ 'image:saved' => "Twój obrazek został pomyślnie zapisany.",
+ 'images:saved' => "Wszystkie obrazki zostały pomyślnie zapisane.",
+ 'image:deleted' => "Twój obrazek został pomyślnie skasowany.",
+ 'image:delete:confirm' => "Czy jesteś pewien że chcesz skasować ten obrazek?",
+ 'images:edited' => "Twoje obrazki zostały pomyślnie zapisane.",
+ 'album:edited' => "Twój album został pomyślnie zapisany.",
+ 'album:saved' => "Twój album został pomyślnie zapisany.",
+ 'album:deleted' => "Twój album został skasowany.",
+ 'album:delete:confirm' => "Na pewno chcesz skasować ten album?",
+ 'album:created' => "Stworzono nowy album.",
+ 'tidypics:status:processing' => "Please wait while we process your picture....",
+
+ //Error messages
+ 'image:none' => "Jeszcze nie dodano żadnych obrazków.",
+ 'image:uploadfailed' => "Pliki nie zapisane:",
+ 'image:deletefailed' => "Nie udało się skasować obrazka.",
+ 'image:downloadfailed' => "Nie udało się ściągnąć albumu.",
+ 'image:notimage' => "Akceptowane formaty to tylko: jpeg, gif i png, and the allowed file size.",
+ 'images:notedited' => "Nie wszystkie obrazki zostały zapisane",
+ 'album:none' => "Jeszcze nie dodano żadnych albumów.",
+ 'album:uploadfailed' => "Nie udało się zapisać twojego albumu.",
+ 'album:deletefailed' => "Nie udało się usunąć twojego albumu.",
+ 'album:blank' => "Please give this albumu a tytuł and opis."
+);
+
+add_translation("pl", $polish); \ No newline at end of file
diff --git a/mod/lightpics/languages/ru.php b/mod/lightpics/languages/ru.php
new file mode 100644
index 000000000..c1a4e7e86
--- /dev/null
+++ b/mod/lightpics/languages/ru.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * Russian language pack
+ */
+
+$russian = array(
+
+ // Menu items and titles
+ 'image' => "Фотография",
+ 'images' => "Фотографии",
+ 'caption' => "Описание",
+ 'photos' => "Фотки",
+ 'images:upload' => "Загрузить Фотки",
+ 'album' => "Фотоальбом",
+ 'albums' => "Фотоальбомы",
+ 'album:yours' => "Ваш Альбом",
+ 'album:yours:friends' => "Альбомы Ваших Друзей",
+ 'album:user' => "%s - Фотоальбом",
+ 'album:friends' => "Альбомы друзей пользователя %s",
+ 'album:all' => "Все Альбомы Сайта",
+ 'album:group' => "Альбомы Группы",
+ 'item:object:image' => "Фотки",
+ 'item:object:album' => "Альбомы",
+ 'tidypics:settings:maxfilesize' => "Максимальный размер файла (KB):",
+ 'tidypics:enablephotos' => 'Включить Альбомы Группы',
+ 'tidypics:editprops' => 'Редактировать данные фотогографии',
+
+ //actions
+ 'album:create' => "Создать новый Альбом",
+ 'album:add' => "Добавить Альбом",
+ 'album:addpix' => "Добавить Фотки в Альбом",
+ 'album:edit' => "Редактировать Альбом",
+ 'album:delete' => "Стереть Альбом",
+ 'image:edit' => "Редактировать Фотку",
+ 'image:delete' => "Стереть Фотку",
+ 'image:download' => "Скачать Фотку",
+
+ //forms
+ 'album:title' => "Заголовок",
+ 'album:desc' => "Описание",
+ 'album:tags' => "Тэги",
+ 'album:cover' => "Сдеалем Обложку Альбома?",
+ 'album:cover:yes' => "Да",
+ 'image:access:note' => "(ыровень доступа как и у основного Альбома)",
+
+ //views
+ 'image:total' => "Всего Фоток:",
+ 'image:by' => "Фотка добавлена:",
+ 'album:by' => "Альбом принадлежит:",
+ 'album:created:on' => "Создан:",
+ 'image:none' => "Пока Фоток не добавлено",
+ 'image:back' => "Назад",
+ 'image:next' => "Вперед",
+
+ //widgets
+ 'album:widget' => "Фотоальбомы",
+ 'album:more' => "Посмотреть все Альбомы",
+ 'album:widget:description' => "Продемонстрировать свой новый Альбом",
+ 'album:display:number' => "Число Альбомов для показа",
+ 'album:num_albums' => "Число Альбомов для показа",
+
+ // river
+ 'image:river:created' => "%s загрузил",
+ 'image:river:item' => "фотку",
+ 'image:river:annotate' => "комментариы к Фотке",
+ 'album:river:created' => "%s создал новый Альбом: ",
+ 'album:river:item' => "Альбом",
+ 'album:river:annotate' => "Комментарий к Альбому",
+
+ // notifications
+ 'tidypics:newalbum' => 'Новый Фото Альбом',
+
+
+ // Status messages
+ 'image:saved' => "Ваша фотография загружена успешно.",
+ 'images:saved' => "Все Фотки успешно сохранены.",
+ 'image:deleted' => "Ваша Фотография стерта.",
+ 'image:delete:confirm' => "Вы уверены, что хотите стереть эту Фотку?",
+ 'images:edited' => "Ваши Фотки успешно обновленны.",
+ 'album:edited' => "Ваш Альбом обновлен.",
+ 'album:saved' => "Ваш Альбом сохранен.",
+ 'album:deleted' => "Ваш Альбом стерт.",
+ 'album:delete:confirm' => "Вы уверены, что хотите стереть этот Альбом?",
+ 'album:created' => "Ваш новый Альбом готов.",
+ 'tidypics:status:processing' => "Подозчдите пока мы загружаем фотку....",
+
+ //Error messages
+ 'image:none' => "На данный момент никаких фоток не обнаружено.",
+ 'image:uploadfailed' => "Файлы не загружены:",
+ 'image:deletefailed' => "В данный момент эту Фотку стереть невозможно.",
+ 'image:downloadfailed' => "На данный момент эту фотку просмотреть невозможно.",
+ 'image:notimage' => "Допускаются только фотки формата jpeg, gif, или png разрешенного размера.",
+ 'images:notedited' => "Не все фотографии были обновленны",
+ 'album:none' => "Пока никаких Альбомов не имеется.",
+ 'album:uploadfailed' => "Ваш Альбом сохранить не удалось.",
+ 'album:deletefailed' => "В данный момент ваш Альбом стереть невозможно.",
+ 'album:blank' => "Пожалуйста создайте заголовок и описание вашего Альбома.",
+);
+
+add_translation("ru", $russian);
diff --git a/mod/lightpics/languages/tr.php b/mod/lightpics/languages/tr.php
new file mode 100644
index 000000000..7d650572a
--- /dev/null
+++ b/mod/lightpics/languages/tr.php
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * Elgg tidypics plugin language pack
+ *
+ * Türkçe Çeviri Orhan Can Ceylan | elggturkiye.com
+ */
+
+
+$turkish = array(
+ 'image' => "Resim" ,
+ 'image:access:note' => "Görüntüleme izni albümden tevarüs edildi." ,
+ 'images' => "Resimler" ,
+ 'caption' => "Açıklama" ,
+ 'photos' => "Resimler" ,
+ 'images:upload' => "Resimleri Yükle" ,
+ 'album' => "Fotoğraf Albümü" ,
+ 'albums' => "Fotoğraf Albümleri" ,
+ 'album:yours' => "Senin Fotoğraf Albümlerin" ,
+ 'album:yours:friends' => "Arkadaşlarının Fotoğraf Albümleri" ,
+ 'album:user' => "%s adlı üyenin fotoğraf albümleri" ,
+ 'album:friends' => "%s adlı üyenin arkadaşlarının fotoğraf albümleri" ,
+ 'album:all' => "Tüm sitenin fotoğraf albümleri" ,
+ 'album:group' => "Grubun Albümleri" ,
+ 'item:object:image' => "Fotoğraflar" ,
+ 'item:object:album' => "Albümler" ,
+ 'tidypics:settings:maxfilesize' => "Bir fotoğrafın maksimum büyüklüğü (kilobyte):" ,
+ 'tidypics:enablephotos' => 'Grup Albümlerini Aktif Et',
+ 'tidypics:editprops' => 'Fotoğrafın özelliklerini Düzenle',
+ 'tidypics:newalbum' => 'Yeni Fotoğraf Albümü',
+ 'album:create' => "Yeni Albüm" ,
+ 'album:add' => "Yeni Fotoğraf Albümü Ekle" ,
+ 'album:addpix' => "Fotoğraf ekle" ,
+ 'album:edit' => "Albümü Düzenle" ,
+ 'album:delete' => "Albümü Sil" ,
+ 'image:edit' => "Resmi Düzenle" ,
+ 'image:delete' => "Resmi Sil" ,
+ 'image:download' => "Resmi İndir" ,
+ 'album:title' => "Başlık" ,
+ 'album:desc' => "Tanım" ,
+ 'album:tags' => "Tagler" ,
+ 'album:cover' => "Bu resim albüm fotoğrafı olsun mu ?" ,
+ 'album:cover:yes' => "Evet" ,
+ 'image:total' => "Albümdeki fotoğraflar" ,
+ 'image:by' => "Fotoğrafı ekleyen " ,
+ 'album:by' => "Albümü oluşturan" ,
+ 'album:created:on' => "Oluşturuldu" ,
+ 'image:none' => "Şuan görünülenecek fotoğraf bulunamadı" ,
+ 'image:back' => "Geri" ,
+ 'image:next' => "İleri" ,
+ 'album:widget' => "Fotoğraf Albümleri" ,
+ 'album:more' => "Tüm albümleri gör" ,
+ 'album:widget:description' => "Son albümleri göster" ,
+ 'album:display:number' => "Gösterilecek albüm sayısı" ,
+ 'album:num_albums' => "Gösterilecek albüm sayısı" ,
+ 'image:river:created' => "%s upload edildi" ,
+ 'image:river:item' => "resim" ,
+ 'image:river:annotate' => "%s yorum yapıldı" ,
+ 'album:river:created' => "%s oluşturuldu" ,
+ 'album:river:item' => "albüm" ,
+ 'album:river:annotate' => "%s yorum yapıldı" ,
+ 'image:saved' => "Fotoğrafınız başarıyla kaydedildi." ,
+ 'images:saved' => "Tüm fotoğraflarınız başarıyla kaydedildi." ,
+ 'image:deleted' => "Fotoğrafınız başarıyla silindi." ,
+ 'image:delete:confirm' => "Bu fotoğrafı silmek istediğinizden emin misiniz ?" ,
+ 'images:edited' => "Fotoğraflarınız başarıyla yüklendi" ,
+ 'album:edited' => "Albümünüz başarıyla güncellendi." ,
+ 'album:saved' => "Albümünüz başarıyla kaydedildi." ,
+ 'album:deleted' => "Albümünüz başarıyla silindi." ,
+ 'album:delete:confirm' => "Albümü silmek istediğinizden emin misiniz ?" ,
+ 'album:created' => "Yeni albümünüz oluşturuldu." ,
+ 'image:uploadfailed' => "Fotoğraflar yüklenemedi :" ,
+ 'image:deletefailed' => "Fotoğrafınız şuan silinemiyor." ,
+ 'image:downloadfailed' => "Üzgünüz, bu fotoğraf şuan uygun değil." ,
+ 'image:notimage' => "Sadece jpeg, gif veya png uzantılı fotoğraflar kabul edilir." ,
+ 'images:notedited' => "Tüm fotoğraflar başarıyla güncellenemedi." ,
+ 'album:none' => "Şuan herhangi bir albüm bulunamadı." ,
+ 'album:uploadfailed' => "Üzgünüm, albümünüz kaydolmadı." ,
+ 'album:deletefailed' => "Albümünüz silinemedi." ,
+ 'tidypics:status:processing' => "Fotoğrafınız işlenirken lütfen bekleyiniz." ,
+ 'album:blank' => "Lütfen albümünüze başlık ve tanım giriniz."
+
+
+);
+
+add_translation('tr', $turkish);
diff --git a/mod/lightpics/lib/exif.php b/mod/lightpics/lib/exif.php
new file mode 100644
index 000000000..46a73e920
--- /dev/null
+++ b/mod/lightpics/lib/exif.php
@@ -0,0 +1,110 @@
+<?php
+/**
+ * Exif Processing Library
+ *
+ * @package TidypicsExif
+ */
+
+/**
+ * Pull EXIF data from image file
+ *
+ * @param TidypicsImage $image
+ */
+function td_get_exif($image) {
+
+ // catch for those who don't have exif module loaded
+ if (!is_callable('exif_read_data')) {
+ return;
+ }
+
+ $mime = $image->mimetype;
+ if ($mime != 'image/jpeg' && $mime != 'image/pjpeg') {
+ return;
+ }
+
+ $filename = $image->getFilenameOnFilestore();
+ $exif = exif_read_data($filename, 'IFD0,EXIF', true);
+ if (is_array($exif) && isset($exif['EXIF'])) {
+ $data = array_merge($exif['IFD0'], $exif['EXIF']);
+ foreach ($data as $key => $value) {
+ if (is_string($value)) {
+ // there are sometimes unicode characters that cause problems with serialize
+ $data[$key] = preg_replace( '/[^[:print:]]/', '', $value);
+ }
+ }
+ $image->tp_exif = serialize($data);
+ }
+}
+
+/**
+ * Grab array of EXIF data for display
+ *
+ * @param TidypicsImage $image
+ * @return array|false
+ */
+function tp_exif_formatted($image) {
+
+ $exif = $image->tp_exif;
+ if (!$exif) {
+ return false;
+ }
+
+ $exif = unserialize($exif);
+
+ $model = $exif['Model'];
+ if (!$model) {
+ $model = "N/A";
+ }
+ $exif_data['Model'] = $model;
+
+ $exposure = $exif['ExposureTime'];
+ if (!$exposure) {
+ $exposure = "N/A";
+ }
+ $exif_data['Shutter'] = $exposure;
+
+ //got the code snippet below from http://www.zenphoto.org/support/topic.php?id=17
+ //convert the raw values to understandible values
+ $Fnumber = explode("/", $exif['FNumber']);
+ if ($Fnumber[1] != 0) {
+ $Fnumber = $Fnumber[0] / $Fnumber[1];
+ } else {
+ $Fnumber = 0;
+ }
+ if (!$Fnumber) {
+ $Fnumber = "N/A";
+ } else {
+ $Fnumber = "f/$Fnumber";
+ }
+ $exif_data['Aperture'] = $Fnumber;
+
+ $iso = $exif['ISOSpeedRatings'];
+ if (!$iso) {
+ $iso = "N/A";
+ }
+ $exif_data['ISO Speed'] = $iso;
+
+ $Focal = explode("/", $exif['FocalLength']);
+ if ($Focal[1] != 0) {
+ $Focal = $Focal[0] / $Focal[1];
+ } else {
+ $Focal = 0;
+ }
+ if (!$Focal || round($Focal) == "0") {
+ $Focal = 0;
+ }
+ if (round($Focal) == 0) {
+ $Focal = "N/A";
+ } else {
+ $Focal = round($Focal) . "mm";
+ }
+ $exif_data['Focal Length'] = $Focal;
+
+ $captured = $exif['DateTime'];
+ if (!$captured) {
+ $captured = "N/A";
+ }
+ $exif_data['Captured'] = $captured;
+
+ return $exif_data;
+}
diff --git a/mod/lightpics/lib/migrate.php b/mod/lightpics/lib/migrate.php
new file mode 100644
index 000000000..8c62126f0
--- /dev/null
+++ b/mod/lightpics/lib/migrate.php
@@ -0,0 +1,301 @@
+<?php
+/**
+ * Tidypics file plugin migration
+ *
+ * Supports moving photos from the files plugin to Tidypics. All of a users
+ * photos end up in a single album.
+ *
+ * Not supported
+ */
+
+// need access to ElggDiskFilestore::make_file_matrix(), which is protected.
+// this is a PITA.
+class tempFilestore extends ElggDiskFilestore {
+ public function make_file_matrix($filename) {
+ return parent::make_file_matrix($filename);
+ }
+
+}
+$filestore = new tempFilestore();
+
+
+
+/**
+ * Migrates all pics from files to tidypics.
+ *
+ */
+function tidypics_migrate_pics() {
+ $limit = 100;
+ $r = true;
+
+ // migrate
+ // @todo: should this return false since there was no error?
+ if (!$users = tidypics_get_user_guids_with_pics_in_files(0, $limit)) {
+ return $r;
+ }
+
+ //echo "Grabbed " . count($users) . " users\n";
+ while (is_array($users) AND count($users) > 0) {
+ foreach ($users as $user_guid) {
+ // reset the query cache.
+ $DB_QUERY_CACHE = array();
+ if (!$user = get_entity($user_guid)) {
+ continue;
+ }
+
+ $r = tidypics_migrate_user_pics($user);
+ }
+
+ //echo "Trying to grab $limit more users...\n";
+ $offset = $offset + $limit;
+ $users = tidypics_get_user_guids_with_pics_in_files($offset, $limit);
+ }
+
+ return $r;
+}
+
+
+/**
+ * Migrates all pictures owned by a user regardless of
+ * if they're group or user files.
+ *
+ * @param ElggUser $user User to migrate.
+ * @return bool on success
+ */
+function tidypics_migrate_user_pics(ElggUser $user) {
+ global $CONFIG, $filestore;
+
+ $user_guid = $user->getGUID();
+
+ // update all entity subtypes in a single go at the end.
+ $updated_guids = array();
+
+ if (!$pics = tidypics_get_user_pics_from_files($user_guid) OR count($pics) < 1) {
+ return false;
+ }
+
+ //echo "{$user->name} ({$user->getGUID()}) has " . count($pics) . " pics.\n";
+
+ // get an album to migrate into if it already exists.
+ // will create later on if it doesn't.
+ $user_album_entities = get_entities_from_metadata('migrated_from_files', true, 'object', 'album', $user->getGUID(), 1);
+ $user_album_guid = isset($album_entities[0]) ? $album_entities[0]->getGUID() : false;
+
+ // a list of albums to randomly select a cover for on newly created albums.
+ $new_album_guids = array();
+
+ foreach ($pics as $pic) {
+ // check that it's not already in tidy pics
+ if (false !== strpos($pic->filename, 'image/')) {
+ //echo "{$pic->filename} ({$pic->getGUID()}) looks like it's already in tidy pics. Ignoring.\n";
+ continue;
+ }
+
+ // blank some vars
+ $group_pic = $group_album_guid = $group_guid = false;
+
+ // see if we're doing a group file migration.
+ if ($pic->container_guid != $user->getGUID()
+ AND $group = get_entity($pic->container_guid)
+ AND $group instanceof ElggGroup
+ ) {
+ //echo "{$pic->getGUID()} is in a group!\n";
+ $group_pic = true;
+ $group_guid = $group->getGUID();
+
+ // yes, this is how you get entities by container_guid.
+ // yes, it's wrong, wrong, wrong for this function to work this way.
+ $group_album_entities = get_entities('object', 'album', $group_guid);
+
+ // get_entities_from_metadata doesn't support container_guid (or owner_guid meaning container_guid)
+ // do it the hard way.
+ if (is_array($group_album_entities)) {
+ foreach ($group_album_entities as $group_album) {
+ if ($group_album->migrated_from_files == true) {
+ $group_album_guid = $group_album->getGUID();
+ break;
+ }
+ }
+ }
+ $album_guid = $group_album_guid;
+ $group_album_guids[] = $group_album_guid;
+ } else {
+ $album_guid = $user_album_guid;
+ }
+
+ //echo "album_guid is $album_guid and group_pic is: $group_pic\n";
+
+ // create an album if we need to.
+ if (!$album_guid) {
+ //echo "Creating new album...\n";
+ $album = new ElggObject();
+ $album->subtype = 'album';
+ $album->new_album = TP_NEW_ALBUM;
+
+ if ($group_pic) {
+ $album->container_guid = $group_guid;
+ $album->owner_guid = $group->owner_guid;
+ $album->access_id = $group->group_acl;
+ $album->title = $group->name;
+ } else {
+ $album->container_guid = $user_guid;
+ $album->owner_guid = $user->getGUID();
+ $album->access_id = ACCESS_DEFAULT;
+ $album->title = $user->name;
+ }
+
+ if (!$album->save()) {
+ //echo "Couldn't migrate pics for {$user->name} ($user_guid)!\n";
+ return false;
+ }
+ $album->migrated_from_files = true;
+ $album_guid = $album->getGUID();
+ $new_album_guids[] = $album_guid;
+
+ // save the album guid as the users
+ if (!$group_pic) {
+ $user_album_guid = $album_guid;
+ }
+ }
+
+ if (!tidypics_migrate_pic_from_files($pic, $album_guid)) {
+ //echo "{$pic->filename} ({$pic->getGUID()}) Couldn't be migrated. Ignoring.\n";
+ continue;
+ }
+ }
+
+ // randomly pic an image to be the cover for the user gallery
+ //$album->cover = $pic_guids[array_rand($pic_guids)];
+ foreach ($new_album_guids as $guid) {
+ tidypics_set_random_cover_pic($guid);
+ }
+
+ return true;
+}
+
+
+/**
+ * Randomly pics an image from an album to be the cover.
+ * @return bool on success
+ */
+function tidypics_set_random_cover_pic($album_guid) {
+ global $CONFIG;
+
+ if ($album = get_entity($album_guid) AND $album instanceof TidypicsAlbum) {
+ $q = "SELECT guid FROM {$CONFIG->dbprefix}entities WHERE container_guid = $album_guid ORDER BY RAND() limit 1";
+ $pic = get_data($q);
+
+ return $album->cover = $pic[0]->guid;
+ }
+
+ return false;
+}
+
+/**
+ * Migrates a single pic from the file repo.
+ * @return bool on succes.
+ */
+function tidypics_migrate_pic_from_files($pic, $album_guid) {
+ global $CONFIG, $filestore;
+
+ // get the subtype id.
+ $image_subtype_id = get_subtype_id('object', 'image');
+
+ // hold which metadata on the files need to be changes
+ // also holds the images we need to move
+ $file_md_fields = array('filename', 'thumbnail', 'smallthumb', 'largethumb');
+
+ if (!$user = get_entity($pic->owner_guid)) {
+ return false;
+ }
+
+ // figure out where to move the files.
+ $matrix = $filestore->make_file_matrix($user->username);
+ $user_fs_path = $CONFIG->dataroot . $matrix;
+ $album_fs_path = $CONFIG->dataroot . $matrix . "image/$album_guid/";
+ if (!is_dir($album_fs_path)) {
+ if (!mkdir($album_fs_path, 0700, true)) {
+ return false;
+ }
+ }
+
+ // change all the 'file/'s to 'image/'s in certain metadata
+ // these are also the files we need to move.
+ foreach ($file_md_fields as $md_name) {
+ // $pic->$md_name = str_replace('file/', 'image/', $pic->$md_name);
+ $old_file = $pic->$md_name;
+ $new_file = str_replace('file/', "image/$album_guid", $old_file);
+
+ if (!($old_fp = fopen($user_fs_path . $old_file, 'r')
+ AND $new_fp = fopen($user_fs_path . $new_file, 'w'))) {
+ //echo "Could not move {$user_fs_path}{$old_file} to {$user_fs_path}{$new_file}\n";
+ continue;
+ }
+
+ while (!feof($old_fp)) {
+ if (!fputs($new_fp, fread($old_fp, 8192))) {
+ //echo "Could not move {$user_fs_path}{$old_file} to {$user_fs_path}{$new_file} (Error writing.)\n";
+ break;
+ }
+ }
+
+ $pic->$md_name = $new_file;
+ }
+ // update container.
+ // this doesn't work...?
+ //$pic->container_guid = $album_guid;
+
+ // delete old one.
+ unlink($user_fs_path . $old_file);
+
+ $q = "UPDATE {$CONFIG->dbprefix}entities SET subtype = $image_subtype_id, container_guid = $album_guid WHERE guid = {$pic->getGUID()}";
+ //echo "Finished moving {$user_fs_path}{$old_file} to {$user_fs_path}{$new_file}\n";
+
+ return update_data($q);
+}
+
+
+/**
+ * Grabs all user IDs with images in the files repo.
+ * return mixed. False on fail, array of GUIDs on success.
+ */
+function tidypics_get_user_guids_with_pics_in_files($offset, $limit) {
+ global $CONFIG;
+
+ //$simpletype_ms_id = add_metastring('simple_type');
+ //$image_ms_id = add_metastring('image');
+
+ $q = "SELECT DISTINCT e.owner_guid
+ FROM
+ {$CONFIG->dbprefix}entities as e,
+ {$CONFIG->dbprefix}entity_subtypes as st
+
+ WHERE st.subtype = 'file'
+ AND e.subtype = st.id
+ LIMIT $offset, $limit";
+
+ if (!$data = get_data($q)) {
+ return false;
+ }
+
+ // return an array of IDs
+ $r = array();
+ foreach ($data as $row) {
+ $r[] = $row->owner_guid;
+ }
+
+ return $r;
+}
+
+/**
+ * Gets a list of images for a single user.
+ * @return array of GUIDs, false on fail.
+ */
+function tidypics_get_user_pics_from_files($user_guid) {
+ if (!$user = get_entity($user_guid) AND $user instanceof ElggUser) {
+ return false;
+ }
+
+ // @todo Might have to cycle this through with standard while + foreach.
+ return get_entities_from_metadata('simpletype', 'image', 'object', 'file', $user_guid, 5000);
+}
diff --git a/mod/lightpics/lib/resize.php b/mod/lightpics/lib/resize.php
new file mode 100644
index 000000000..aa2eabea1
--- /dev/null
+++ b/mod/lightpics/lib/resize.php
@@ -0,0 +1,522 @@
+<?php
+/**
+ * Elgg tidypics library of resizing functions
+ *
+ * @package TidypicsImageResize
+ */
+
+
+/**
+ * Create thumbnails using PHP GD Library
+ *
+ * @param ElggFile holds the image that was uploaded
+ * @param string folder to store thumbnail in
+ * @param string name of the thumbnail
+ * @return bool TRUE on success
+ */
+function tp_create_gd_thumbnails($file, $prefix, $filestorename) {
+ global $CONFIG;
+
+ $image_sizes = elgg_get_plugin_setting('image_sizes', 'lightpics');
+ if (!$image_sizes) {
+ // move this out of library
+ register_error(elgg_echo('tidypics:nosettings'));
+ forward(REFERER);
+ return FALSE;
+ }
+ $image_sizes = unserialize($image_sizes);
+
+ $thumb = new ElggFile();
+ $thumb->owner_guid = $file->owner_guid;
+ $thumb->container_guid = $file->container_guid;
+
+ // tiny thumbail
+ $thumb->setFilename($prefix."thumb".$filestorename);
+ $thumbname = $thumb->getFilenameOnFilestore();
+ $rtn_code = tp_gd_resize( $file->getFilenameOnFilestore(),
+ $thumbname,
+ $image_sizes['tiny_image_width'],
+ $image_sizes['tiny_image_height'],
+ TRUE);
+ if (!$rtn_code) {
+ return FALSE;
+ }
+ $file->thumbnail = $prefix."thumb".$filestorename;
+
+ // album thumbnail
+ global $CONFIG;
+ $CONFIG->debug = 'WARNING';
+ $thumb->setFilename($prefix."smallthumb".$filestorename);
+ $thumbname = $thumb->getFilenameOnFilestore();
+ $rtn_code = tp_gd_resize( $file->getFilenameOnFilestore(),
+ $thumbname,
+ $image_sizes['small_image_width'],
+ $image_sizes['small_image_height'],
+ TRUE);
+ if (!$rtn_code) {
+ return FALSE;
+ }
+ $file->smallthumb = $prefix."smallthumb".$filestorename;
+ unset($CONFIG->debug);
+
+ // main image
+ $thumb->setFilename($prefix."largethumb".$filestorename);
+ $thumbname = $thumb->getFilenameOnFilestore();
+ $rtn_code = tp_gd_resize( $file->getFilenameOnFilestore(),
+ $thumbname,
+ $image_sizes['large_image_width'],
+ $image_sizes['large_image_height'],
+ FALSE);
+ if (!$rtn_code) {
+ return FALSE;
+ }
+ $file->largethumb = $prefix."largethumb".$filestorename;
+
+
+ unset($thumb);
+
+ return TRUE;
+}
+
+/**
+ * Writes resized version of an already uploaded image - original from Elgg filestore.php
+ * Saves it in the same format as uploaded
+ *
+ * @param string $input_name The name of the file on the disk
+ * @param string $output_name The name of the file to be written
+ * @param int $maxwidth The maximum width of the resized image
+ * @param int $maxheight The maximum height of the resized image
+ * @param TRUE|FALSE $square If set to TRUE, will take the smallest of maxwidth and maxheight and use it to set the dimensions on all size; the image will be cropped.
+ * @return bool TRUE on success or FALSE on failure
+ */
+function tp_gd_resize($input_name, $output_name, $maxwidth, $maxheight, $square = FALSE, $x1 = 0, $y1 = 0, $x2 = 0, $y2 = 0) {
+
+ // Get the size information from the image
+ $imgsizearray = getimagesize($input_name);
+ if (!$imgsizearray) {
+ return FALSE;
+ }
+
+ // Get width and height of image
+ $width = $imgsizearray[0];
+ $height = $imgsizearray[1];
+
+ $params = tp_im_calc_resize_params($width, $height, $maxwidth, $maxheight, $square, $x1, $y1, $x2, $y2);
+ if (!$params) {
+ return FALSE;
+ }
+
+ $new_width = $params['new_width'];
+ $new_height = $params['new_height'];
+ $region_width = $params['region_width'];
+ $region_height = $params['region_height'];
+ $widthoffset = $params['width_offset'];
+ $heightoffset = $params['height_offset'];
+
+ $accepted_formats = array(
+ 'image/jpeg' => 'jpeg',
+ 'image/pjpeg' => 'jpeg',
+ 'image/png' => 'png',
+ 'image/x-png' => 'png',
+ 'image/gif' => 'gif'
+ );
+
+ // make sure the function is available
+ $function = "imagecreatefrom" . $accepted_formats[$imgsizearray['mime']];
+ if (!is_callable($function)) {
+ return FALSE;
+ }
+
+ // load old image
+ $oldimage = $function($input_name);
+ if (!$oldimage) {
+ return FALSE;
+ }
+
+ // allocate the new image
+ $newimage = imagecreatetruecolor($new_width, $new_height);
+ if (!$newimage) {
+ return FALSE;
+ }
+
+ $rtn_code = imagecopyresampled( $newimage,
+ $oldimage,
+ 0,
+ 0,
+ $widthoffset,
+ $heightoffset,
+ $new_width,
+ $new_height,
+ $region_width,
+ $region_height);
+ if (!$rtn_code) {
+ return $rtn_code;
+ }
+
+ switch ($imgsizearray['mime']) {
+ case 'image/jpeg':
+ case 'image/pjpeg':
+ $rtn_code = imagejpeg($newimage, $output_name, 85);
+ break;
+ case 'image/png':
+ case 'image/x-png':
+ $rtn_code = imagepng($newimage, $output_name);
+ break;
+ case 'image/gif':
+ $rtn_code = imagegif($newimage, $output_name);
+ break;
+ }
+
+ imagedestroy($newimage);
+ imagedestroy($oldimage);
+
+ return $rtn_code;
+}
+
+
+/**
+ * Create thumbnails using PHP imagick extension
+ *
+ * @param ElggFile holds the image that was uploaded
+ * @param string folder to store thumbnail in
+ * @param string name of the thumbnail
+ * @return bool TRUE on success
+ */
+function tp_create_imagick_thumbnails($file, $prefix, $filestorename) {
+ $image_sizes = elgg_get_plugin_setting('image_sizes', 'tidypics');
+ if (!$image_sizes) {
+ register_error(elgg_echo('tidypics:nosettings'));
+ return FALSE;
+ }
+ $image_sizes = unserialize($image_sizes);
+
+ $thumb = new ElggFile();
+ $thumb->owner_guid = $file->owner_guid;
+ $thumb->container_guid = $file->container_guid;
+
+ // tiny thumbnail
+ $thumb->setFilename($prefix."thumb".$filestorename);
+ $thumbname = $thumb->getFilenameOnFilestore();
+ $rtn_code = tp_imagick_resize( $file->getFilenameOnFilestore(),
+ $thumbname,
+ $image_sizes['tiny_image_width'],
+ $image_sizes['tiny_image_height'],
+ TRUE);
+ if (!$rtn_code) {
+ return FALSE;
+ }
+ $file->thumbnail = $prefix."thumb".$filestorename;
+
+ // album thumbnail
+ $thumb->setFilename($prefix."smallthumb".$filestorename);
+ $thumbname = $thumb->getFilenameOnFilestore();
+ $rtn_code = tp_imagick_resize( $file->getFilenameOnFilestore(),
+ $thumbname,
+ $image_sizes['small_image_width'],
+ $image_sizes['small_image_height'],
+ TRUE);
+ if (!$rtn_code) {
+ return FALSE;
+ }
+ $file->smallthumb = $prefix."smallthumb".$filestorename;
+
+ // main image
+ $thumb->setFilename($prefix."largethumb".$filestorename);
+ $thumbname = $thumb->getFilenameOnFilestore();
+ $rtn_code = tp_imagick_resize( $file->getFilenameOnFilestore(),
+ $thumbname,
+ $image_sizes['large_image_width'],
+ $image_sizes['large_image_height'],
+ FALSE);
+ if (!$rtn_code) {
+ return FALSE;
+ }
+ $file->largethumb = $prefix."largethumb".$filestorename;
+
+ unset($thumb);
+
+ return TRUE;
+}
+
+
+/**
+ * Resize using PHP imagick extension
+ *
+ * Writes resized version of an already uploaded image
+ *
+ *
+ * @param string $input_name The name of the file input field on the submission form
+ * @param string $output_name The name of the file to be written
+ * @param int $maxwidth The maximum width of the resized image
+ * @param int $maxheight The maximum height of the resized image
+ * @param TRUE|FALSE $square If set to TRUE, will take the smallest of maxwidth and maxheight and use it to set the dimensions on all size; the image will be cropped.
+ * @return bool TRUE on success
+ */
+function tp_imagick_resize($input_name, $output_name, $maxwidth, $maxheight, $square = FALSE, $x1 = 0, $y1 = 0, $x2 = 0, $y2 = 0) {
+
+ // Get the size information from the image
+ $imgsizearray = getimagesize($input_name);
+ if (!$imgsizearray) {
+ return FALSE;
+ }
+
+ // Get width and height
+ $width = $imgsizearray[0];
+ $height = $imgsizearray[1];
+
+ $params = tp_im_calc_resize_params($width, $height, $maxwidth, $maxheight, $square, $x1, $y1, $x2, $y2);
+ if (!$params) {
+ return FALSE;
+ }
+
+ $new_width = $params['new_width'];
+ $new_height = $params['new_height'];
+ $region_width = $params['region_width'];
+ $region_height = $params['region_height'];
+ $widthoffset = $params['width_offset'];
+ $heightoffset = $params['height_offset'];
+
+ try {
+ $img = new Imagick($input_name);
+ } catch (ImagickException $e) {
+ return FALSE;
+ }
+
+ $img->cropImage($region_width, $region_height, $widthoffset, $heightoffset);
+
+ // use the default IM filter (windowing filter), I think 1 means default blurring or number of lobes
+ $img->resizeImage($new_width, $new_height, imagick::FILTER_LANCZOS, 1);
+ $img->setImagePage($new_width, $new_height, 0, 0);
+
+ if ($img->writeImage($output_name) != TRUE) {
+ $img->destroy();
+ return FALSE;
+ }
+
+ $img->destroy();
+
+ return TRUE;
+}
+
+/**
+ * Create thumbnails using ImageMagick executables
+ *
+ * @param ElggFile holds the image that was uploaded
+ * @param string folder to store thumbnail in
+ * @param string name of the thumbnail
+ * @return bool TRUE on success
+ */
+function tp_create_im_cmdline_thumbnails($file, $prefix, $filestorename) {
+ $image_sizes = elgg_get_plugin_setting('image_sizes', 'tidypics');
+ if (!$image_sizes) {
+ register_error(elgg_echo('tidypics:nosettings'));
+ return FALSE;
+ }
+ $image_sizes = unserialize($image_sizes);
+
+ $thumb = new ElggFile();
+ $thumb->owner_guid = $file->owner_guid;
+ $thumb->container_guid = $file->container_guid;
+
+ // tiny thumbnail
+ $thumb->setFilename($prefix."thumb".$filestorename);
+ $thumbname = $thumb->getFilenameOnFilestore();
+ $rtn_code = tp_im_cmdline_resize( $file->getFilenameOnFilestore(),
+ $thumbname,
+ $image_sizes['tiny_image_width'],
+ $image_sizes['tiny_image_height'],
+ TRUE);
+ if (!$rtn_code) {
+ return FALSE;
+ }
+ $file->thumbnail = $prefix."thumb".$filestorename;
+
+
+ // album thumbnail
+ $thumb->setFilename($prefix."smallthumb".$filestorename);
+ $thumbname = $thumb->getFilenameOnFilestore();
+ $rtn_code = tp_im_cmdline_resize( $file->getFilenameOnFilestore(),
+ $thumbname,
+ $image_sizes['small_image_width'],
+ $image_sizes['small_image_height'],
+ TRUE);
+ if (!$rtn_code) {
+ return FALSE;
+ }
+ $file->smallthumb = $prefix."smallthumb".$filestorename;
+
+ // main image
+ $thumb->setFilename($prefix."largethumb".$filestorename);
+ $thumbname = $thumb->getFilenameOnFilestore();
+ $rtn_code = tp_im_cmdline_resize( $file->getFilenameOnFilestore(),
+ $thumbname,
+ $image_sizes['large_image_width'],
+ $image_sizes['large_image_height'],
+ FALSE);
+ if (!$rtn_code) {
+ return FALSE;
+ }
+ $file->largethumb = $prefix."largethumb".$filestorename;
+
+ unset($thumb);
+
+ return TRUE;
+}
+
+/**
+ * Gets the jpeg contents of the resized version of an already uploaded image
+ * (Returns FALSE if the uploaded file was not an image)
+ *
+ * @param string $input_name The name of the file input field on the submission form
+ * @param string $output_name The name of the file to be written
+ * @param int $maxwidth The maximum width of the resized image
+ * @param int $maxheight The maximum height of the resized image
+ * @param TRUE|FALSE $square If set to TRUE, will take the smallest of maxwidth and maxheight and use it to set the dimensions on all size; the image will be cropped.
+ * @return bool
+ */
+function tp_im_cmdline_resize($input_name, $output_name, $maxwidth, $maxheight, $square = FALSE, $x1 = 0, $y1 = 0, $x2 = 0, $y2 = 0) {
+
+
+ // Get the size information from the image
+ $imgsizearray = getimagesize($input_name);
+ if (!$imgsizearray) {
+ return FALSE;
+ }
+
+ // Get width and height
+ $orig_width = $imgsizearray[0];
+ $orig_height = $imgsizearray[1];
+
+ $params = tp_im_calc_resize_params($orig_width, $orig_height, $maxwidth, $maxheight, $square, $x1, $y1, $x2, $y2);
+ if (!$params) {
+ return FALSE;
+ }
+
+ $newwidth = $params['new_width'];
+ $newheight = $params['new_height'];
+
+ $accepted_formats = array(
+ 'image/jpeg' => 'jpeg',
+ 'image/pjpeg' => 'jpeg',
+ 'image/png' => 'png',
+ 'image/x-png' => 'png',
+ 'image/gif' => 'gif'
+ );
+
+ // If it's a file we can manipulate ...
+ if (!array_key_exists($imgsizearray['mime'],$accepted_formats)) {
+ return FALSE;
+ }
+
+ $im_path = elgg_get_plugin_setting('im_path', 'tidypics');
+ if (!$im_path) {
+ $im_path = "/usr/bin/";
+ }
+ if (substr($im_path, strlen($im_path)-1, 1) != "/") {
+ $im_path .= "/";
+ }
+
+ // see imagemagick web site for explanation of these parameters
+ // the ^ in the resize means those are minimum width and height values
+ $command = $im_path . "convert \"$input_name\" -resize ".$newwidth."x".$newheight."^ -gravity center -extent ".$newwidth."x".$newheight." \"$output_name\"";
+ $output = array();
+ $ret = 0;
+ exec($command, $output, $ret);
+ if ($ret == 127) {
+ trigger_error('Tidypics warning: Image Magick convert is not found', E_USER_WARNING);
+ return FALSE;
+ } else if ($ret > 0) {
+ trigger_error('Tidypics warning: Image Magick convert failed', E_USER_WARNING);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Calculate the resizing/cropping parameters
+ *
+ * @param int $orig_width
+ * @param int $orig_height
+ * @param int $new_width
+ * @param int $new_height
+ * @param bool $square
+ * @param int $x1
+ * @param int $y1
+ * @param int $x2
+ * @param int $y2
+ * @return array|FALSE
+ */
+function tp_im_calc_resize_params($orig_width, $orig_height, $new_width, $new_height, $square = FALSE, $x1 = 0, $y1 = 0, $x2 = 0, $y2 = 0) {
+ // crop image first?
+ $crop = TRUE;
+ if ($x1 == 0 && $y1 == 0 && $x2 == 0 && $y2 == 0) {
+ $crop = FALSE;
+ }
+
+ // how large a section of the image has been selected
+ if ($crop) {
+ $region_width = $x2 - $x1;
+ $region_height = $y2 - $y1;
+ } else {
+ // everything selected if no crop parameters
+ $region_width = $orig_width;
+ $region_height = $orig_height;
+ }
+
+ // determine cropping offsets
+ if ($square) {
+ // asking for a square image back
+
+ // detect case where someone is passing crop parameters that are not for a square
+ if ($crop == TRUE && $region_width != $region_height) {
+ return FALSE;
+ }
+
+ // size of the new square image
+ $new_width = $new_height = min($new_width, $new_height);
+
+ // find largest square that fits within the selected region
+ $region_width = $region_height = min($region_width, $region_height);
+
+ // set offsets for crop
+ if ($crop) {
+ $widthoffset = $x1;
+ $heightoffset = $y1;
+ $orig_width = $x2 - $x1;
+ $orig_height = $orig_width;
+ } else {
+ // place square region in the center
+ $widthoffset = floor(($orig_width - $region_width) / 2);
+ $heightoffset = floor(($orig_height - $region_height) / 2);
+ }
+ } else {
+ // non-square new image
+
+ // maintain aspect ratio of original image/crop
+ if (($region_height / (float)$new_height) > ($region_width / (float)$new_width)) {
+ $new_width = floor($new_height * $region_width / (float)$region_height);
+ } else {
+ $new_height = floor($new_width * $region_height / (float)$region_width);
+ }
+
+ // by default, use entire image
+ $widthoffset = 0;
+ $heightoffset = 0;
+
+ if ($crop) {
+ $widthoffset = $x1;
+ $heightoffset = $y1;
+ }
+ }
+
+ $resize_params = array();
+ $resize_params['new_width'] = $new_width;
+ $resize_params['new_height'] = $new_height;
+ $resize_params['region_width'] = $region_width;
+ $resize_params['region_height'] = $region_height;
+ $resize_params['width_offset'] = $widthoffset;
+ $resize_params['height_offset'] = $heightoffset;
+
+ return $resize_params;
+} \ No newline at end of file
diff --git a/mod/lightpics/lib/tidypics.php b/mod/lightpics/lib/tidypics.php
new file mode 100644
index 000000000..1419a260a
--- /dev/null
+++ b/mod/lightpics/lib/tidypics.php
@@ -0,0 +1,365 @@
+<?php
+/**
+ * Elgg tidypics library of common functions
+ *
+ * @package TidypicsCommon
+ */
+
+/**
+ * Get images for display on front page
+ *
+ * @param int number of images
+ * @param int (optional) guid of owner
+ * @return string of html for display
+ *
+ * To use with the custom index plugin, use something like this:
+
+ if (is_plugin_enabled('tidypics')) {
+ ?>
+ <!-- display latest photos -->
+ <div class="index_box">
+ <h2><a href="<?php echo $vars['url']; ?>pg/photos/world/"><?php echo elgg_echo("tidypics:mostrecent"); ?></a></h2>
+ <div class="contentWrapper">
+ <?php
+ echo tp_get_latest_photos(5);
+ ?>
+ </div>
+ </div>
+ <?php
+ }
+ ?>
+
+ * Good luck
+ */
+function tp_get_latest_photos($num_images, $owner_guid = 0, $context = 'front') {
+ $prev_context = get_context();
+ set_context($context);
+ $image_html = elgg_list_entities(array(
+ 'type' => 'object',
+ 'subtype' => 'image',
+ 'owner_guid' => $owner_guid,
+ 'limit' => $num_images,
+ 'full_view' => false,
+ 'pagination' => false,
+ ));
+ set_context($prev_context);
+ return $image_html;
+}
+
+
+/**
+ * Get image directory path
+ *
+ * Each album gets a subdirectory based on its container id
+ *
+ * @return string path to image directory
+ */
+function tp_get_img_dir() {
+ $file = new ElggFile();
+ $file->setFilename('image/');
+ return $file->getFilenameOnFilestore();
+}
+
+/**
+ * Prepare vars for a form, pulling from an entity or sticky forms.
+ *
+ * @param type $entity
+ * @return type
+ */
+function tidypics_prepare_form_vars($entity = null) {
+ // input names => defaults
+ $values = array(
+ 'title' => '',
+ 'description' => '',
+ 'access_id' => ACCESS_DEFAULT,
+ 'tags' => '',
+ 'container_guid' => elgg_get_page_owner_guid(),
+ 'guid' => null,
+ 'entity' => $entity,
+ );
+
+ if ($entity) {
+ foreach (array_keys($values) as $field) {
+ if (isset($entity->$field)) {
+ $values[$field] = $entity->$field;
+ }
+ }
+ }
+
+ if (elgg_is_sticky_form('tidypics')) {
+ $sticky_values = elgg_get_sticky_values('tidypics');
+ foreach ($sticky_values as $key => $value) {
+ $values[$key] = $value;
+ }
+ }
+
+ elgg_clear_sticky_form('tidypics');
+
+ return $values;
+}
+
+/**
+ * Returns available image libraries.
+ *
+ * @return string
+ */
+function tidypics_get_image_libraries() {
+ $options = array();
+ if (extension_loaded('gd')) {
+ $options['GD'] = 'GD';
+ }
+
+ if (extension_loaded('imagick')) {
+ $options['ImageMagickPHP'] = 'imagick PHP extension';
+ }
+
+ $disablefunc = explode(',', ini_get('disable_functions'));
+ if (is_callable('exec') && !in_array('exec', $disablefunc)) {
+ $options['ImageMagick'] = 'ImageMagick executable';
+ }
+
+ return $options;
+}
+
+/**
+ * Are there upgrade scripts to be run?
+ *
+ * @return bool
+ */
+function tidypics_is_upgrade_available() {
+ // sets $version based on code
+ require_once elgg_get_plugins_path() . "lightpics/version.php";
+ return true;
+
+ $local_version = elgg_get_plugin_setting('version', 'tidypics');
+ if ($local_version === false) {
+ // no version set so either new install or really old one
+ if (!get_subtype_class('object', 'image') || !get_subtype_class('object', 'album')) {
+ $local_version = 0;
+ } else {
+ // set initial version for new install
+ elgg_set_plugin_setting('version', $version, 'tidypics');
+ $local_version = $version;
+ }
+ } elseif ($local_version === '1.62') {
+ // special work around to handle old upgrade system
+ $local_version = 2010010101;
+ elgg_set_plugin_setting('version', $local_version, 'tidypics');
+ }
+
+ if ($local_version == $version) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+/**
+ * This lists the photos in an album as sorted by metadata
+ *
+ * @todo this only supports a single album. The only case for use a
+ * procedural function like this instead of TidypicsAlbum::viewImgaes() is to
+ * fetch images across albums as a helper to elgg_get_entities().
+ * This should function be deprecated or fixed to work across albums.
+ *
+ * @param array $options
+ * @return string
+ */
+function tidypics_list_photos(array $options = array()) {
+ global $autofeed;
+ $autofeed = true;
+
+ $defaults = array(
+ 'offset' => (int) max(get_input('offset', 0), 0),
+ 'limit' => (int) max(get_input('limit', 10), 0),
+ 'full_view' => true,
+ 'list_type_toggle' => false,
+ 'pagination' => true,
+ );
+
+ $options = array_merge($defaults, $options);
+
+ $options['count'] = true;
+ $count = elgg_get_entities($options);
+
+ $album = get_entity($options['container_guid']);
+ if ($album) {
+ $guids = $album->getImageList();
+ // need to pass all the guids and handle the limit / offset in sql
+ // to avoid problems with the navigation
+ //$guids = array_slice($guids, $options['offset'], $options['limit']);
+ $options['guids'] = $guids;
+ unset($options['container_guid']);
+ }
+ $options['count'] = false;
+ $entities = elgg_get_entities($options);
+
+ $keys = array();
+ foreach ($entities as $entity) {
+ $keys[] = $entity->guid;
+ }
+
+ $entities = array_combine($keys, $entities);
+
+ $sorted_entities = array();
+ foreach ($guids as $guid) {
+ if (isset($entities[$guid])) {
+ $sorted_entities[] = $entities[$guid];
+ }
+ }
+
+ // for this function count means the total number of entities
+ // and is required for pagination
+ $options['count'] = $count;
+
+ return elgg_view_entity_list($sorted_entities, $options);
+}
+
+/**
+ * Returns just a guid from a database $row. Used in elgg_get_entities()'s callback.
+ *
+ * @param stdClass $row
+ * @return type
+ */
+function tp_guid_callback($row) {
+ return ($row->guid) ? $row->guid : false;
+}
+
+
+/*********************************************************************
+ * the functions below replace broken core functions or add functions
+ * that could/should exist in the core
+ */
+
+function tp_view_entity_list($entities, $count, $offset, $limit, $fullview = true, $viewtypetoggle = false, $pagination = true) {
+ $context = get_context();
+
+ $html = elgg_view('tidypics/gallery',array(
+ 'entities' => $entities,
+ 'count' => $count,
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'baseurl' => $_SERVER['REQUEST_URI'],
+ 'fullview' => $fullview,
+ 'context' => $context,
+ 'viewtypetoggle' => $viewtypetoggle,
+ 'viewtype' => get_input('search_viewtype','list'),
+ 'pagination' => $pagination
+ ));
+
+ return $html;
+}
+
+function tp_get_entities_from_annotations_calculate_x($sum = "sum", $entity_type = "", $entity_subtype = "", $name = "", $mdname = '', $mdvalue = '', $owner_guid = 0, $limit = 10, $offset = 0, $orderdir = 'desc', $count = false) {
+ global $CONFIG;
+
+ $sum = sanitise_string($sum);
+ $entity_type = sanitise_string($entity_type);
+ $entity_subtype = get_subtype_id($entity_type, $entity_subtype);
+ $name = get_metastring_id($name);
+ $limit = (int) $limit;
+ $offset = (int) $offset;
+ $owner_guid = (int) $owner_guid;
+ if (!empty($mdname) && !empty($mdvalue)) {
+ $meta_n = get_metastring_id($mdname);
+ $meta_v = get_metastring_id($mdvalue);
+ }
+
+ if (empty($name)) return 0;
+
+ $where = array();
+
+ if ($entity_type!="")
+ $where[] = "e.type='$entity_type'";
+ if ($owner_guid > 0)
+ $where[] = "e.owner_guid = $owner_guid";
+ if ($entity_subtype)
+ $where[] = "e.subtype=$entity_subtype";
+ if ($name!="")
+ $where[] = "a.name_id='$name'";
+
+ if (!empty($mdname) && !empty($mdvalue)) {
+ if ($mdname!="")
+ $where[] = "m.name_id='$meta_n'";
+ if ($mdvalue!="")
+ $where[] = "m.value_id='$meta_v'";
+ }
+
+ if ($sum != "count")
+ $where[] = "a.value_type='integer'"; // Limit on integer types
+
+ if (!$count) {
+ $query = "SELECT distinct e.*, $sum(ms.string) as sum ";
+ } else {
+ $query = "SELECT count(distinct e.guid) as num, $sum(ms.string) as sum ";
+ }
+ $query .= " from {$CONFIG->dbprefix}entities e JOIN {$CONFIG->dbprefix}annotations a on a.entity_guid = e.guid JOIN {$CONFIG->dbprefix}metastrings ms on a.value_id=ms.id ";
+
+ if (!empty($mdname) && !empty($mdvalue)) {
+ $query .= " JOIN {$CONFIG->dbprefix}metadata m on m.entity_guid = e.guid ";
+ }
+
+ $query .= " WHERE ";
+ foreach ($where as $w)
+ $query .= " $w and ";
+ $query .= get_access_sql_suffix("a"); // now add access
+ $query .= ' and ' . get_access_sql_suffix("e"); // now add access
+ if (!$count) $query .= ' group by e.guid';
+
+ if (!$count) {
+ $query .= ' order by sum ' . $orderdir;
+ $query .= ' limit ' . $offset . ' , ' . $limit;
+ return get_data($query, "entity_row_to_elggstar");
+ } else {
+ if ($row = get_data_row($query)) {
+ return $row->num;
+ }
+ }
+ return false;
+}
+
+/**
+ * Is page owner a group - convenience function
+ *
+ * @return true/false
+ */
+function tp_is_group_page() {
+
+ if ($group = page_owner_entity()) {
+ if ($group instanceof ElggGroup)
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Is the request from a known browser
+ *
+ * @return true/false
+ */
+function tp_is_person() {
+ $known = array('msie', 'mozilla', 'firefox', 'safari', 'webkit', 'opera', 'netscape', 'konqueror', 'gecko');
+
+ $agent = strtolower($_SERVER['HTTP_USER_AGENT']);
+
+ foreach ($known as $browser) {
+ if (strpos($agent, $browser) !== false) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Convenience function for listing recent images
+ *
+ * @param int $max
+ * @param bool $pagination
+ * @return string
+ */
+function tp_mostrecentimages($max = 8, $pagination = true) {
+ return list_entities("object", "image", 0, $max, false, false, $pagination);
+}
diff --git a/mod/lightpics/lib/upload.php b/mod/lightpics/lib/upload.php
new file mode 100644
index 000000000..a83323f16
--- /dev/null
+++ b/mod/lightpics/lib/upload.php
@@ -0,0 +1,119 @@
+<?php
+/**
+ * Helper library for working with uploads
+ */
+
+/**
+ * Guess on the mimetype based on file extension
+ *
+ * @param string $originalName
+ * @return string
+ */
+function tp_upload_get_mimetype($originalName) {
+ $extension = substr(strrchr($originalName, '.'), 1);
+ switch (strtolower($extension)) {
+ case 'png':
+ return 'image/png';
+ break;
+ case 'gif':
+ return 'image/gif';
+ break;
+ case 'jpg':
+ case 'jpeg':
+ return 'image/jpeg';
+ break;
+ default:
+ return 'unknown';
+ break;
+ }
+}
+
+/**
+ * Check if this is an image
+ *
+ * @param string $mime
+ * @return bool false = not image
+ */
+function tp_upload_check_format($mime) {
+ $accepted_formats = array(
+ 'image/jpeg',
+ 'image/png',
+ 'image/gif',
+ 'image/pjpeg',
+ 'image/x-png',
+ );
+
+ if (!in_array($mime, $accepted_formats)) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Check if there is enough memory to process this image
+ *
+ * @param string $image_lib
+ * @param int $num_pixels
+ * @return bool false = not enough memory
+ */
+function tp_upload_memory_check($image_lib, $num_pixels) {
+ if ($image_lib !== 'GD') {
+ return true;
+ }
+
+ $mem_avail = ini_get('memory_limit');
+ $mem_avail = rtrim($mem_avail, 'M');
+ $mem_avail = $mem_avail * 1024 * 1024;
+ $mem_used = memory_get_usage();
+ $mem_required = ceil(5.35 * $num_pixels);
+
+ $mem_avail = $mem_avail - $mem_used - 2097152; // 2 MB buffer
+ if ($mem_required > $mem_avail) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Check if image is within limits
+ *
+ * @param int $image_size
+ * @return bool false = too large
+ */
+function tp_upload_check_max_size($image_size) {
+ $max_file_size = (float) elgg_get_plugin_setting('maxfilesize', 'tidypics');
+ if (!$max_file_size) {
+ // default to 5 MB if not set
+ $max_file_size = 5;
+ }
+ // convert to bytes from MBs
+ $max_file_size = 1024 * 1024 * $max_file_size;
+ return $image_size <= $max_file_size;
+}
+
+/**
+ * Check if this image pushes user over quota
+ *
+ * @param int $image_size
+ * @param int $owner_guid
+ * @return bool false = exceed quota
+ */
+function tp_upload_check_quota($image_size, $owner_guid) {
+ static $quota;
+
+ if (!isset($quota)) {
+ $quota = elgg_get_plugin_setting('quota', 'tidypics');
+ $quota = 1024 * 1024 * $quota;
+ }
+
+ if ($quota == 0) {
+ // no quota
+ return true;
+ }
+
+ $owner = get_entity($owner_guid);
+ $image_repo_size_md = (int)$owner->image_repo_size;
+
+ return ($image_repo_size + $image_size) < $quota;
+} \ No newline at end of file
diff --git a/mod/lightpics/manifest.xml b/mod/lightpics/manifest.xml
new file mode 100644
index 000000000..f892a0e0a
--- /dev/null
+++ b/mod/lightpics/manifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">
+ <name>Lightpics Photo Gallery</name>
+ <author>Lorea developers</author>
+ <version>1.8</version>
+ <description>A simple photo gallery with albums and a slideshow.</description>
+ <category>content</category>
+ <category>multimedia</category>
+ <website>https://lorea.org</website>
+ <copyright>(C) 2011-2012 Cash Costello, 2012-2013 Lorea</copyright>
+ <license>GNU General Public License Version 2</license>
+ <requires>
+ <type>elgg_release</type>
+ <version>1.8</version>
+ </requires>
+ <conflicts>
+ <type>plugin</type>
+ <name>tidypics</name>
+ </conflicts>
+ <suggests>
+ <type>plugin</type>
+ <name>colorbox</name>
+ <version>1.8</version>
+ </suggests>
+ <requires>
+ <type>priority</type>
+ <priority>after</priority>
+ <plugin>colorbox</plugin>
+ </requires>
+</plugin_manifest>
diff --git a/mod/lightpics/pages/lists/highestrated.php b/mod/lightpics/pages/lists/highestrated.php
new file mode 100644
index 000000000..e05e7a9ba
--- /dev/null
+++ b/mod/lightpics/pages/lists/highestrated.php
@@ -0,0 +1,65 @@
+<?php
+ /**
+ * Tidypics Friends Albums Listing
+ *
+ */
+
+ include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+ //if no friends were requested, see world pictures instead, or redirect to user's friends
+/* if (is_null(get_input('username')) || get_input('username')=='') {
+ if (!isloggedin()) {
+ forward('pg/photos/world');
+ } else {
+ forward('pg/photos/friends/' . $_SESSION['user']->username);
+ }
+ }*/
+
+// if (is_null(page_owner_entity()->name) || page_owner_entity()->name == '') {
+// $groupname = get_input('username');
+// } else {
+// $groupname = page_owner_entity()->name;
+// };
+//
+ //there has to be a better way to do this
+ if(!$groupname) {
+ $page = get_input("page");
+ list($pagename, $groupname) = split("/", $page);
+ }
+
+ list($group_holder, $album_id) = split(":", $groupname);
+// echo "<pre>page: $page\ngroup: $groupname\nalbum: $album_id"; die;
+
+ $user = get_user_by_username($friendname);
+ global $CONFIG;
+ $prefix = $CONFIG->dbprefix;
+ $max = 24;
+
+ $sql = "SELECT ent.guid, count(1) as mycount, avg(ms2.string) as average
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image' AND ent.container_guid = $album_id
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'generic_rate'
+ INNER JOIN " . $prefix . "metastrings ms2 ON ms2.id = ann1.value_id
+ INNER JOIN " . $prefix . "users_entity u ON ann1.owner_guid = u.guid
+ GROUP BY ent.guid HAVING mycount > 1
+ ORDER BY average DESC
+ LIMIT $max";
+
+ $result = get_data($sql);
+
+ $entities = array();
+ foreach($result as $entity) {
+ $entities[] = get_entity($entity->guid);
+ }
+
+ $album = get_entity($album_id);
+ $title = $album["title"] . ": " . elgg_echo("tidypics:highestrated");
+ $area2 = elgg_view_title($title);
+ $area2 .= elgg_view_entity_list($entities, $max, 0, $max, false);
+ $body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+ page_draw($title, $body);
+
+?> \ No newline at end of file
diff --git a/mod/lightpics/pages/lists/highestvotecount.php b/mod/lightpics/pages/lists/highestvotecount.php
new file mode 100644
index 000000000..26b907144
--- /dev/null
+++ b/mod/lightpics/pages/lists/highestvotecount.php
@@ -0,0 +1,50 @@
+<?php
+
+ /**
+ * Tidypics full view of an image
+ * Given a GUID, this page will try and display any entity
+ *
+ */
+
+ // Load Elgg engine
+ include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+ global $CONFIG;
+ $prefix = $CONFIG->dbprefix;
+ $max = 24;
+
+ $sql = "SELECT ent.guid, u.name as owner, count( 1 ) AS mycount, avg( ms2.string ) AS average
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image'
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'generic_rate'
+ INNER JOIN " . $prefix . "metastrings ms2 ON ms2.id = ann1.value_id
+ INNER JOIN " . $prefix . "users_entity u ON ent.owner_guid = u.guid
+ GROUP BY ent.guid
+ ORDER BY mycount DESC
+ LIMIT $max";
+
+ $result = get_data($sql);
+
+ $title = "Most voted images";
+ $area2 = elgg_view_title($title);
+
+ $entities = array();
+ foreach($result as $entity) {
+ $entities[] = get_entity($entity->guid);
+ $full_entity = get_entity($entity->guid);
+ $area2 .= " <div class='tidypics_album_images'>
+ Owner: $entity->owner<br />
+ Votes: $entity->mycount<br />
+ Average: $entity->average
+ </div>
+ ";
+ $area2 .= elgg_view_entity($full_entity);
+
+ }
+
+ $body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+ page_draw($title, $body);
+?> \ No newline at end of file
diff --git a/mod/lightpics/pages/lists/mostcommentedimages.php b/mod/lightpics/pages/lists/mostcommentedimages.php
new file mode 100644
index 000000000..0a4eb9622
--- /dev/null
+++ b/mod/lightpics/pages/lists/mostcommentedimages.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * Tidypics full view of an image
+ * Given a GUID, this page will try and display any entity
+ *
+ */
+
+// Load Elgg engine
+include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+global $CONFIG;
+$prefix = $CONFIG->dbprefix;
+$max = 24;
+
+//this works but is wildly inefficient
+//$annotations = get_annotations(0, "object", "image", "tp_view", "", "", 5000);
+
+$sql = "SELECT ent.guid, count( * ) AS views
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image'
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'generic_comment'
+ GROUP BY ent.guid
+ ORDER BY views DESC
+ LIMIT $max";
+
+$result = get_data($sql);
+
+$entities = array();
+foreach ($result as $entity) {
+ $entities[] = get_entity($entity->guid);
+}
+
+tidypics_mostviewed_submenus();
+$title = elgg_echo("tidypics:mostcommented");
+$area2 = elgg_view_title($title);
+$area2 .= elgg_view_entity_list($entities, $max, 0, $max, false);
+$body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+page_draw($title, $body); \ No newline at end of file
diff --git a/mod/lightpics/pages/lists/mostcommentedimagesthismonth.php b/mod/lightpics/pages/lists/mostcommentedimagesthismonth.php
new file mode 100644
index 000000000..d95e2aff5
--- /dev/null
+++ b/mod/lightpics/pages/lists/mostcommentedimagesthismonth.php
@@ -0,0 +1,50 @@
+<?php
+
+ /**
+ * Tidypics full view of an image
+ * Given a GUID, this page will try and display any entity
+ *
+ */
+
+ // Load Elgg engine
+ include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+ global $CONFIG;
+ $prefix = $CONFIG->dbprefix;
+ $max = 24;
+
+
+ //find timestamps for first and last days of this month
+ $time_info = new stdClass();
+ $time_info->start = mktime(0,0,0, date("m"), 1, date("Y"));
+ $time_info->end = mktime();
+
+ //this works but is wildly inefficient
+ //$annotations = get_annotations(0, "object", "image", "tp_view", "", "", 5000);
+
+ $sql = "SELECT ent.guid, count( * ) AS views
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image'
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'generic_comment'
+ WHERE ann1.time_created BETWEEN $time_info->start AND $time_info->end
+ GROUP BY ent.guid
+ ORDER BY views DESC
+ LIMIT $max";
+
+ $result = get_data($sql);
+
+ $entities = array();
+ foreach($result as $entity) {
+ $entities[] = get_entity($entity->guid);
+ }
+
+ tidypics_mostviewed_submenus();
+ $title = elgg_echo("tidypics:mostcommentedthismonth");
+ $area2 = elgg_view_title($title);
+ $area2 .= elgg_view_entity_list($entities, $max, 0, $max, false);
+ $body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+ page_draw($title, $body);
+?> \ No newline at end of file
diff --git a/mod/lightpics/pages/lists/mostcommentedimagestoday.php b/mod/lightpics/pages/lists/mostcommentedimagestoday.php
new file mode 100644
index 000000000..bd1a0cbec
--- /dev/null
+++ b/mod/lightpics/pages/lists/mostcommentedimagestoday.php
@@ -0,0 +1,50 @@
+<?php
+
+ /**
+ * Tidypics full view of an image
+ * Given a GUID, this page will try and display any entity
+ *
+ */
+
+ // Load Elgg engine
+ include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+ global $CONFIG;
+ $prefix = $CONFIG->dbprefix;
+ $max = 24;
+
+
+ //find timestamps for today
+ $time_info = new stdClass();
+ $time_info->start = mktime(0,0,0, date("m"), date("d"), date("Y"));
+ $time_info->end = mktime();
+
+ //this works but is wildly inefficient
+ //$annotations = get_annotations(0, "object", "image", "tp_view", "", "", 5000);
+
+ $sql = "SELECT ent.guid, count( * ) AS views
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image'
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'generic_comment'
+ WHERE ann1.time_created BETWEEN $time_info->start AND $time_info->end
+ GROUP BY ent.guid
+ ORDER BY views DESC
+ LIMIT $max";
+
+ $result = get_data($sql);
+
+ $entities = array();
+ foreach($result as $entity) {
+ $entities[] = get_entity($entity->guid);
+ }
+
+ tidypics_mostviewed_submenus();
+ $title = elgg_echo("tidypics:mostcommentedtoday");
+ $area2 = elgg_view_title($title);
+ $area2 .= elgg_view_entity_list($entities, $max, 0, $max, false);
+ $body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+ page_draw($title, $body);
+?> \ No newline at end of file
diff --git a/mod/lightpics/pages/lists/mostrecentimages.php b/mod/lightpics/pages/lists/mostrecentimages.php
new file mode 100644
index 000000000..83ec3e988
--- /dev/null
+++ b/mod/lightpics/pages/lists/mostrecentimages.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * Most recently uploaded images - individual or world
+ *
+ */
+
+// Load Elgg engine
+include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+// start with assumption this is for all site photos
+$title = elgg_echo('tidypics:mostrecent');
+$user_id = 0;
+
+// is this all site or an individuals images
+$username = get_input('username');
+if ($username) {
+ $user = get_user_by_username($username);
+ if ($user) {
+ $user_id = $user->guid;
+
+ if ($user_id == get_loggedin_userid()) {
+ $title = elgg_echo('tidypics:yourmostrecent');
+ } else {
+ $title = sprintf(elgg_echo("tidypics:friendmostrecent"), $user->name);
+ }
+ }
+} else {
+ // world view - set page owner to logged in user
+ if (isloggedin()) {
+ set_page_owner(get_loggedin_userid());
+ }
+}
+
+// how many do we display
+$max = 12;
+
+// grab the html to display the images
+$images = elgg_list_entities(array(
+ "type" => "object",
+ "subtype" => "image",
+ "owner_guid" => $user_id,
+ "limit" => $max,
+ "full_view" => false,
+));
+
+
+// this view takes care of the title on the main column and the content wrapper
+$area2 = elgg_view('tidypics/content_wrapper', array('title' => $title, 'content' => $images,));
+if (empty($area2)) {
+ $area2 = $images;
+}
+
+$body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+
+page_draw($title, $body);
diff --git a/mod/lightpics/pages/lists/mostviewedimages.php b/mod/lightpics/pages/lists/mostviewedimages.php
new file mode 100644
index 000000000..c113c39e2
--- /dev/null
+++ b/mod/lightpics/pages/lists/mostviewedimages.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * Most viewed images - either for a user or all site
+ *
+ */
+
+// Load Elgg engine
+include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+global $CONFIG;
+$prefix = $CONFIG->dbprefix;
+$max = 24;
+
+$owner_guid = page_owner();
+
+//$start = microtime(true);
+$photos = tp_get_entities_from_annotations_calculate_x(
+ 'count',
+ 'object',
+ 'image',
+ 'tp_view',
+ '',
+ '',
+ $owner_guid,
+ $max);
+//error_log("elgg query is " . (float)(microtime(true) - $start));
+
+//this works but is wildly inefficient
+//$annotations = get_annotations(0, "object", "image", "tp_view", "", "", 5000);
+/*
+ $start = microtime(true);
+ $sql = "SELECT ent.guid, count( * ) AS views
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image'
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid AND ann1.owner_guid != ent.owner_guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'tp_view'
+ GROUP BY ent.guid
+ ORDER BY views DESC
+ LIMIT $max";
+
+ $result = get_data($sql);
+
+ $entities = array();
+ foreach($result as $entity) {
+ $entities[] = get_entity($entity->guid);
+ }
+*/
+//error_log("custom query is " . (float)(microtime(true) - $start));
+
+if ($owner_guid) {
+ if ($owner_guid == get_loggedin_userid()) {
+ $title = elgg_echo("tidypics:yourmostviewed");
+ } else {
+ $title = sprintf(elgg_echo("tidypics:friendmostviewed"), page_owner_entity()->name);
+ }
+} else {
+ // world view - set page owner to logged in user
+ if (isloggedin()) {
+ set_page_owner(get_loggedin_userid());
+ }
+
+ $title = elgg_echo("tidypics:mostviewed");
+}
+$area2 = elgg_view_title($title);
+
+// grab the html to display the images
+$content = tp_view_entity_list($photos, $max, 0, $max, false);
+
+// this view takes care of the title on the main column and the content wrapper
+$area2 = elgg_view('tidypics/content_wrapper', array('title' => $title, 'content' => $content,));
+if (empty($area2)) {
+ $area2 = $content;
+}
+
+$body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+page_draw($title, $body);
diff --git a/mod/lightpics/pages/lists/mostviewedimageslastmonth.php b/mod/lightpics/pages/lists/mostviewedimageslastmonth.php
new file mode 100644
index 000000000..1ed9161f7
--- /dev/null
+++ b/mod/lightpics/pages/lists/mostviewedimageslastmonth.php
@@ -0,0 +1,50 @@
+<?php
+
+ /**
+ * Tidypics full view of an image
+ * Given a GUID, this page will try and display any entity
+ *
+ */
+
+ // Load Elgg engine
+ include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+ global $CONFIG;
+ $prefix = $CONFIG->dbprefix;
+ $max = 24;
+
+
+ //find timestamps for first and last days of last month
+ $time_info = new stdClass();
+ $time_info->start = strtotime("-1 months", mktime(0,0,0, date("m"), 1, date("Y")));
+ $time_info->end = mktime(0,0,0,date("m"), 0, date("Y"));
+
+ //this works but is wildly inefficient
+ //$annotations = get_annotations(0, "object", "image", "tp_view", "", "", 5000);
+
+ $sql = "SELECT ent.guid, count( * ) AS views
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image'
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid AND ann1.owner_guid != ent.owner_guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'tp_view'
+ WHERE ann1.time_created BETWEEN $time_info->start AND $time_info->end
+ GROUP BY ent.guid
+ ORDER BY views DESC
+ LIMIT $max";
+
+ $result = get_data($sql);
+
+ $entities = array();
+ foreach($result as $entity) {
+ $entities[] = get_entity($entity->guid);
+ }
+
+ tidypics_mostviewed_submenus();
+ $title = elgg_echo("tidypics:mostviewedlastmonth");
+ $area2 = elgg_view_title($title);
+ $area2 .= elgg_view_entity_list($entities, $max, 0, $max, false);
+ $body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+ page_draw($title, $body);
+?> \ No newline at end of file
diff --git a/mod/lightpics/pages/lists/mostviewedimagesthismonth.php b/mod/lightpics/pages/lists/mostviewedimagesthismonth.php
new file mode 100644
index 000000000..bfe08e1da
--- /dev/null
+++ b/mod/lightpics/pages/lists/mostviewedimagesthismonth.php
@@ -0,0 +1,50 @@
+<?php
+
+ /**
+ * Tidypics full view of an image
+ * Given a GUID, this page will try and display any entity
+ *
+ */
+
+ // Load Elgg engine
+ include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+ global $CONFIG;
+ $prefix = $CONFIG->dbprefix;
+ $max = 24;
+
+
+ //find timestamps for first and last days of this month
+ $time_info = new stdClass();
+ $time_info->start = mktime(0,0,0, date("m"), 1, date("Y"));
+ $time_info->end = mktime();
+
+ //this works but is wildly inefficient
+ //$annotations = get_annotations(0, "object", "image", "tp_view", "", "", 5000);
+
+ $sql = "SELECT ent.guid, count( * ) AS views
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image'
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid AND ann1.owner_guid != ent.owner_guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'tp_view'
+ WHERE ann1.time_created BETWEEN $time_info->start AND $time_info->end
+ GROUP BY ent.guid
+ ORDER BY views DESC
+ LIMIT $max";
+
+ $result = get_data($sql);
+
+ $entities = array();
+ foreach($result as $entity) {
+ $entities[] = get_entity($entity->guid);
+ }
+
+ tidypics_mostviewed_submenus();
+ $title = elgg_echo("tidypics:mostviewedthismonth");
+ $area2 = elgg_view_title($title);
+ $area2 .= elgg_view_entity_list($entities, $max, 0, $max, false);
+ $body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+ page_draw($title, $body);
+?> \ No newline at end of file
diff --git a/mod/lightpics/pages/lists/mostviewedimagesthisyear.php b/mod/lightpics/pages/lists/mostviewedimagesthisyear.php
new file mode 100644
index 000000000..fe1a63d38
--- /dev/null
+++ b/mod/lightpics/pages/lists/mostviewedimagesthisyear.php
@@ -0,0 +1,50 @@
+<?php
+
+ /**
+ * Tidypics full view of an image
+ * Given a GUID, this page will try and display any entity
+ *
+ */
+
+ // Load Elgg engine
+ include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+ global $CONFIG;
+ $prefix = $CONFIG->dbprefix;
+ $max = 24;
+
+
+ //find timestamps for first day of the year and current date
+ $time_info = new stdClass();
+ $time_info->start = mktime(0,0,0, 1, 1, date("Y"));
+ $time_info->end = mktime();
+
+ //this works but is wildly inefficient
+ //$annotations = get_annotations(0, "object", "image", "tp_view", "", "", 5000);
+
+ $sql = "SELECT ent.guid, count( * ) AS views
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image'
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid AND ann1.owner_guid != ent.owner_guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'tp_view'
+ WHERE ann1.time_created BETWEEN $time_info->start AND $time_info->end
+ GROUP BY ent.guid
+ ORDER BY views DESC
+ LIMIT $max";
+
+ $result = get_data($sql);
+
+ $entities = array();
+ foreach($result as $entity) {
+ $entities[] = get_entity($entity->guid);
+ }
+
+ tidypics_mostviewed_submenus();
+ $title = elgg_echo("tidypics:mostviewedthisyear");
+ $area2 = elgg_view_title($title);
+ $area2 .= elgg_view_entity_list($entities, $max, 0, $max, false);
+ $body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+ page_draw($title, $body);
+?> \ No newline at end of file
diff --git a/mod/lightpics/pages/lists/mostviewedimagestoday.php b/mod/lightpics/pages/lists/mostviewedimagestoday.php
new file mode 100644
index 000000000..f8e844753
--- /dev/null
+++ b/mod/lightpics/pages/lists/mostviewedimagestoday.php
@@ -0,0 +1,50 @@
+<?php
+
+ /**
+ * Tidypics full view of an image
+ * Given a GUID, this page will try and display any entity
+ *
+ */
+
+ // Load Elgg engine
+ include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+ global $CONFIG;
+ $prefix = $CONFIG->dbprefix;
+ $max = 24;
+
+
+ //find timestamps for today
+ $time_info = new stdClass();
+ $time_info->start = mktime(0,0,0, date("m"), date("d"), date("Y"));
+ $time_info->end = mktime();
+
+ //this works but is wildly inefficient
+ //$annotations = get_annotations(0, "object", "image", "tp_view", "", "", 5000);
+
+ $sql = "SELECT ent.guid, count( * ) AS views
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image'
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid AND ann1.owner_guid != ent.owner_guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'tp_view'
+ WHERE ann1.time_created BETWEEN $time_info->start AND $time_info->end
+ GROUP BY ent.guid
+ ORDER BY views DESC
+ LIMIT $max";
+
+ $result = get_data($sql);
+
+ $entities = array();
+ foreach($result as $entity) {
+ $entities[] = get_entity($entity->guid);
+ }
+
+ tidypics_mostviewed_submenus();
+ $title = elgg_echo("tidypics:mostviewedtoday");
+ $area2 = elgg_view_title($title);
+ $area2 .= elgg_view_entity_list($entities, $max, 0, $max, false);
+ $body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+ page_draw($title, $body);
+?> \ No newline at end of file
diff --git a/mod/lightpics/pages/lists/recentlycommented.php b/mod/lightpics/pages/lists/recentlycommented.php
new file mode 100644
index 000000000..08f69603a
--- /dev/null
+++ b/mod/lightpics/pages/lists/recentlycommented.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * Images recently commented on - world view only
+ *
+ */
+
+// Load Elgg engine
+include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+// world view - set page owner to logged in user
+if (isloggedin()) {
+ set_page_owner(get_loggedin_userid());
+}
+
+
+global $CONFIG;
+$prefix = $CONFIG->dbprefix;
+$max_limit = 200; //get extra because you'll have multiple views per image in the result set
+$max = 16; //controls how many actually show on screen
+
+//this works but is wildly inefficient
+//$annotations = get_annotations(0, "object", "image", "tp_view", "", "", 5000);
+
+$sql = "SELECT distinct (ent.guid), ann1.time_created
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image'
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'generic_comment'
+ ORDER BY ann1.time_created DESC
+ LIMIT $max_limit";
+
+$result = get_data($sql);
+
+$entities = array();
+foreach ($result as $entity) {
+ if (!$entities[$entity->guid]) {
+ $entities[$entity->guid] = get_entity($entity->guid);
+ }
+ if (count($entities) >= $max) {
+ break;
+ }
+}
+
+$user = get_loggedin_user();
+$title = elgg_echo("tidypics:recentlycommented");
+$area2 = elgg_view_title($title);
+
+// grab the html to display the images
+$images = tp_view_entity_list($entities, $max, 0, $max, false);
+
+// this view takes care of the title on the main column and the content wrapper
+$area2 = elgg_view('tidypics/content_wrapper', array('title' => $title, 'content' => $images,));
+if (empty($area2)) {
+ $area2 = $images;
+}
+
+$body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+page_draw($title, $body);
diff --git a/mod/lightpics/pages/lists/recentlyviewed.php b/mod/lightpics/pages/lists/recentlyviewed.php
new file mode 100644
index 000000000..851804e99
--- /dev/null
+++ b/mod/lightpics/pages/lists/recentlyviewed.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * Most recently viewed images - world view only right now
+ *
+ */
+
+// Load Elgg engine
+include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+// world view - set page owner to logged in user
+if (isloggedin()) {
+ set_page_owner(get_loggedin_userid());
+}
+
+
+global $CONFIG;
+$prefix = $CONFIG->dbprefix;
+$max_limit = 200; //get extra because you'll have multiple views per image in the result set
+$max = 16; //controls how many actually show on screen
+
+//this works but is wildly inefficient
+//$annotations = get_annotations(0, "object", "image", "tp_view", "", "", 5000);
+
+$sql = "SELECT distinct ent.guid, ann1.time_created
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image'
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'tp_view'
+ ORDER BY ann1.id DESC
+ LIMIT $max_limit";
+
+$result = get_data($sql);
+
+$entities = array();
+foreach ($result as $entity) {
+ if (!$entities[$entity->guid]) {
+ $entities[$entity->guid] = get_entity($entity->guid);
+ }
+ if (count($entities) >= $max) {
+ break;
+ }
+}
+
+$title = elgg_echo("tidypics:recentlyviewed");
+$area2 = elgg_view_title($title);
+
+// grab the html to display the images
+$images = tp_view_entity_list($entities, $max, 0, $max, false);
+
+// this view takes care of the title on the main column and the content wrapper
+$area2 = elgg_view('tidypics/content_wrapper', array('title' => $title, 'content' => $images,));
+if (empty($area2)) {
+ $area2 = $images;
+}
+
+$body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+page_draw($title, $body);
diff --git a/mod/lightpics/pages/lists/recentvotes.php b/mod/lightpics/pages/lists/recentvotes.php
new file mode 100644
index 000000000..3d8eac97e
--- /dev/null
+++ b/mod/lightpics/pages/lists/recentvotes.php
@@ -0,0 +1,52 @@
+<?php
+
+ /**
+ * Tidypics full view of an image
+ * Given a GUID, this page will try and display any entity
+ *
+ */
+
+ // Load Elgg engine
+ include_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/engine/start.php";
+
+ global $CONFIG;
+ $prefix = $CONFIG->dbprefix;
+ $max = 24;
+
+ $sql = "SELECT ent.guid, u2.name AS owner, u.name AS voter, ms2.string as vote
+ FROM " . $prefix . "entities ent
+ INNER JOIN " . $prefix . "entity_subtypes sub ON ent.subtype = sub.id
+ AND sub.subtype = 'image'
+ INNER JOIN " . $prefix . "annotations ann1 ON ann1.entity_guid = ent.guid
+ INNER JOIN " . $prefix . "metastrings ms ON ms.id = ann1.name_id
+ AND ms.string = 'generic_rate'
+ INNER JOIN " . $prefix . "metastrings ms2 ON ms2.id = ann1.value_id
+ INNER JOIN " . $prefix . "users_entity u ON ann1.owner_guid = u.guid
+ INNER JOIN " . $prefix . "users_entity u2 ON ent.owner_guid = u2.guid
+ ORDER BY ann1.time_created DESC
+ LIMIT $max";
+
+ $result = get_data($sql);
+
+ $title = "Recently rated images";
+ $area2 = elgg_view_title($title);
+
+ $entities = array();
+ foreach($result as $entity) {
+ $entities[] = get_entity($entity->guid);
+ $full_entity = get_entity($entity->guid);
+ $area2 .= " <div class='tidypics_album_images'>
+ Owner: $entity->owner<br />
+ Voter: $entity->voter<br />
+ Rating: $entity->vote
+ </div>
+ ";
+ $area2 .= elgg_view_entity($full_entity);
+
+ }
+
+
+// $area2 .= elgg_view_entity_list($entities, $max, 0, $max);
+ $body = elgg_view_layout('two_column_left_sidebar', '', $area2);
+ page_draw($title, $body);
+?> \ No newline at end of file
diff --git a/mod/lightpics/pages/photos/album/add.php b/mod/lightpics/pages/photos/album/add.php
new file mode 100644
index 000000000..d34d39177
--- /dev/null
+++ b/mod/lightpics/pages/photos/album/add.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Create new album page
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$owner = elgg_get_page_owner_entity();
+
+gatekeeper();
+group_gatekeeper();
+
+$title = elgg_echo('photos:add');
+
+// set up breadcrumbs
+elgg_push_breadcrumb(elgg_echo('photos'), "photos/all");
+if (elgg_instanceof($owner, 'user')) {
+ elgg_push_breadcrumb($owner->name, "photos/owner/$owner->username");
+} else {
+ elgg_push_breadcrumb($owner->name, "photos/group/$owner->guid/all");
+}
+elgg_push_breadcrumb($title);
+
+$vars = tidypics_prepare_form_vars();
+$content = elgg_view_form('photos/album/save', array('method' => 'post'), $vars);
+
+$body = elgg_view_layout('content', array(
+ 'content' => $content,
+ 'title' => $title,
+ 'filter' => '',
+));
+
+echo elgg_view_page($title, $body);
diff --git a/mod/lightpics/pages/photos/album/edit.php b/mod/lightpics/pages/photos/album/edit.php
new file mode 100644
index 000000000..7efb05ce1
--- /dev/null
+++ b/mod/lightpics/pages/photos/album/edit.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Edit an album
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$guid = (int) get_input('guid');
+
+if (!$entity = get_entity($guid)) {
+ // @todo either deleted or do not have access
+ forward('photos/all');
+}
+
+if (!$entity->canEdit()) {
+ // @todo cannot change it
+ forward('photos/all');
+}
+
+elgg_set_page_owner_guid($entity->getContainerGUID());
+$owner = elgg_get_page_owner_entity();
+
+gatekeeper();
+group_gatekeeper();
+
+$title = elgg_echo('album:edit');
+
+// set up breadcrumbs
+elgg_push_breadcrumb(elgg_echo('photos'), "photos/all");
+if (elgg_instanceof($owner, 'user')) {
+ elgg_push_breadcrumb($owner->name, "photos/owner/$owner->username");
+} else {
+ elgg_push_breadcrumb($owner->name, "photos/group/$owner->guid/all");
+}
+elgg_push_breadcrumb($entity->getTitle(), $entity->getURL());
+elgg_push_breadcrumb($title);
+
+$vars = tidypics_prepare_form_vars($entity);
+$content = elgg_view_form('photos/album/save', array('method' => 'post'), $vars);
+
+$body = elgg_view_layout('content', array(
+ 'content' => $content,
+ 'title' => $title,
+ 'filter' => '',
+));
+
+echo elgg_view_page($title, $body);
diff --git a/mod/lightpics/pages/photos/album/sort.php b/mod/lightpics/pages/photos/album/sort.php
new file mode 100644
index 000000000..005205dd5
--- /dev/null
+++ b/mod/lightpics/pages/photos/album/sort.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Album sort page
+ *
+ * This displays a listing of all the photos so that they can be sorted
+ */
+
+gatekeeper();
+group_gatekeeper();
+
+// get the album entity
+$album_guid = (int) get_input('guid');
+$album = get_entity($album_guid);
+
+// panic if we can't get it
+if (!$album) {
+ forward();
+}
+
+// container should always be set, but just in case
+$owner = $album->getContainerEntity();
+elgg_set_page_owner_guid($owner->getGUID());
+
+$title = elgg_echo('tidypics:sort', array($album->getTitle()));
+
+// set up breadcrumbs
+elgg_push_breadcrumb(elgg_echo('photos'), 'photos/all');
+if (elgg_instanceof($owner, 'group')) {
+ elgg_push_breadcrumb($owner->name, "photos/group/$owner->guid/all");
+} else {
+ elgg_push_breadcrumb($owner->name, "photos/owner/$owner->username");
+}
+elgg_push_breadcrumb($album->getTitle(), $album->getURL());
+elgg_push_breadcrumb(elgg_echo('album:sort'));
+
+elgg_register_menu_item('title', array(
+ 'name' => 'upload',
+ 'href' => 'photos/upload/' . $album->getGUID(),
+ 'text' => elgg_echo('images:upload'),
+ 'link_class' => 'elgg-button elgg-button-action',
+));
+
+if ($album->getSize()) {
+ $content = elgg_view_form('photos/album/sort', array(), array('album' => $album));
+} else {
+ $content = elgg_echo('tidypics:sort:no_images');
+}
+
+$body = elgg_view_layout('content', array(
+ 'filter' => false,
+ 'content' => $content,
+ 'title' => $title,
+ 'sidebar' => elgg_view('tidypics/sidebar', array('page' => 'album')),
+));
+
+echo elgg_view_page($title, $body);
diff --git a/mod/lightpics/pages/photos/album/view.php b/mod/lightpics/pages/photos/album/view.php
new file mode 100644
index 000000000..6e111ab98
--- /dev/null
+++ b/mod/lightpics/pages/photos/album/view.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * This displays the photos that belong to an album
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+group_gatekeeper();
+
+// get the album entity
+$album_guid = (int) get_input('guid');
+$album = get_entity($album_guid);
+if (!$album) {
+ register_error(elgg_echo('noaccess'));
+ $_SESSION['last_forward_from'] = current_page_url();
+ forward('');
+}
+
+elgg_set_page_owner_guid($album->getContainerGUID());
+$owner = elgg_get_page_owner_entity();
+
+$title = elgg_echo($album->getTitle());
+
+// set up breadcrumbs
+elgg_push_breadcrumb(elgg_echo('photos'), 'photos/all');
+if (elgg_instanceof($owner, 'group')) {
+ elgg_push_breadcrumb($owner->name, "photos/group/$owner->guid/all");
+} else {
+ elgg_push_breadcrumb($owner->name, "photos/owner/$owner->username");
+}
+elgg_push_breadcrumb($album->getTitle());
+
+$content = elgg_view_entity($album, array('full_view' => true));
+
+if ($album->getContainerEntity()->canWriteToContainer()) {
+ elgg_register_menu_item('title', array(
+ 'name' => 'upload',
+ 'href' => 'photos/upload/' . $album->getGUID(),
+ 'text' => elgg_echo('images:upload'),
+ 'link_class' => 'elgg-button elgg-button-action',
+ ));
+}
+
+// only show sort button if there are images
+if ($album->canEdit() && $album->getSize() > 0) {
+ elgg_register_menu_item('title', array(
+ 'name' => 'sort',
+ 'href' => "photos/sort/" . $album->getGUID(),
+ 'text' => elgg_echo('album:sort'),
+ 'link_class' => 'elgg-button elgg-button-action',
+ 'priority' => 200,
+ ));
+}
+
+$body = elgg_view_layout('content', array(
+ 'filter' => false,
+ 'content' => $content,
+ 'title' => $album->getTitle(),
+ 'sidebar' => elgg_view('tidypics/sidebar', array('page' => 'album')),
+));
+
+echo elgg_view_page($title, $body);
diff --git a/mod/lightpics/pages/photos/all.php b/mod/lightpics/pages/photos/all.php
new file mode 100644
index 000000000..aef7f11c6
--- /dev/null
+++ b/mod/lightpics/pages/photos/all.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * View all albums on the site
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+elgg_push_breadcrumb(elgg_echo('photos'));
+
+$num_albums = 16;
+
+$offset = (int)get_input('offset', 0);
+$content = elgg_list_entities(array(
+ 'type' => 'object',
+ 'subtype' => 'album',
+ 'limit' => $num_albums,
+ 'full_view' => false,
+ 'list_type' => 'gallery',
+ 'list_type_toggle' => false,
+ 'gallery_class' => 'tidypics-gallery',
+));
+if (!$content) {
+ $content = elgg_echo('tidypics:none');
+}
+
+$title = elgg_echo('album:all');
+
+elgg_register_title_button('photos');
+
+$body = elgg_view_layout('content', array(
+ 'filter_context' => 'all',
+ 'content' => $content,
+ 'title' => $title,
+ 'sidebar' => elgg_view('tidypics/sidebar', array('page' => 'all')),
+));
+
+echo elgg_view_page($title, $body);
diff --git a/mod/lightpics/pages/photos/batch/edit.php b/mod/lightpics/pages/photos/batch/edit.php
new file mode 100644
index 000000000..ca9a55b18
--- /dev/null
+++ b/mod/lightpics/pages/photos/batch/edit.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Edit the image information for a batch of images
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+gatekeeper();
+
+$guid = (int) get_input('guid');
+
+if (!$batch = get_entity($guid)) {
+ // @todo either deleted or do not have access
+ forward('photos/all');
+}
+
+if (!$batch->canEdit()) {
+ // @todo cannot change it
+ forward('photos/all');
+}
+
+$album = $batch->getContainerEntity();
+
+elgg_set_page_owner_guid($batch->getContainerEntity()->getContainerGUID());
+$owner = elgg_get_page_owner_entity();
+
+$title = elgg_echo('tidypics:editprops');
+
+elgg_push_breadcrumb(elgg_echo('photos'), "photos/all");
+elgg_push_breadcrumb($owner->name, "photos/owner/$owner->username");
+elgg_push_breadcrumb($album->getTitle(), $album->getURL());
+elgg_push_breadcrumb($title);
+
+$content = elgg_view_form('photos/batch/edit', array(), array('batch' => $batch));
+
+$body = elgg_view_layout('content', array(
+ 'filter' => false,
+ 'content' => $content,
+ 'title' => elgg_echo('tidypics:editprops'),
+ 'sidebar' => elgg_view('tidypics/sidebar', array('page' => 'album')),
+));
+
+echo elgg_view_page($title, $body);
diff --git a/mod/lightpics/pages/photos/friends.php b/mod/lightpics/pages/photos/friends.php
new file mode 100644
index 000000000..e6ac49cc6
--- /dev/null
+++ b/mod/lightpics/pages/photos/friends.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * List all the albums of someone's friends
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$owner = elgg_get_page_owner_entity();
+
+elgg_push_breadcrumb(elgg_echo('photos'), "photos/all");
+elgg_push_breadcrumb($owner->name, "photos/friends/$owner->username");
+elgg_push_breadcrumb(elgg_echo('friends'));
+
+$title = elgg_echo('album:friends');
+
+
+$num_albums = 16;
+
+set_input('list_type', 'gallery');
+$content = list_user_friends_objects($owner->guid, 'album', $num_albums, false);
+if (!$content) {
+ $content = elgg_echo('tidypics:none');
+}
+
+elgg_register_title_button();
+
+$body = elgg_view_layout('content', array(
+ 'filter_context' => 'friends',
+ 'content' => $content,
+ 'title' => $title,
+));
+
+echo elgg_view_page($title, $body);
diff --git a/mod/lightpics/pages/photos/image/download.php b/mod/lightpics/pages/photos/image/download.php
new file mode 100644
index 000000000..ef47b7638
--- /dev/null
+++ b/mod/lightpics/pages/photos/image/download.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Download a photo
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$guid = (int) get_input('guid');
+$image = get_entity($guid);
+
+$disposition = get_input('disposition', 'attachment');
+
+if ($image) {
+ $filename = $image->originalfilename;
+ $mime = $image->mimetype;
+
+ header("Content-Type: $mime");
+ header("Content-Disposition: $disposition; filename=\"$filename\"");
+
+ $contents = $image->grabFile();
+
+ if (empty($contents)) {
+ echo file_get_contents(dirname(dirname(__FILE__)) . "/graphics/image_error_large.png" );
+ } else {
+
+ // expires every 60 days
+ $expires = 60 * 60*60*24;
+
+ header("Content-Length: " . strlen($contents));
+ header("Cache-Control: public", true);
+ header("Pragma: public", true);
+ header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $expires) . ' GMT', true);
+
+ echo $contents;
+ }
+
+ exit;
+} else {
+ register_error(elgg_echo("image:downloadfailed"));
+}
diff --git a/mod/lightpics/pages/photos/image/edit.php b/mod/lightpics/pages/photos/image/edit.php
new file mode 100644
index 000000000..76c1381c9
--- /dev/null
+++ b/mod/lightpics/pages/photos/image/edit.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Edit an image
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$guid = (int) get_input('guid');
+
+if (!$entity = get_entity($guid)) {
+ // @todo either deleted or do not have access
+ forward('photos/all');
+}
+
+if (!$entity->canEdit()) {
+ // @todo cannot change it
+ forward($entity->getContainerEntity()->getURL());
+}
+
+$album = $entity->getContainerEntity();
+if (!$album) {
+
+}
+
+elgg_set_page_owner_guid($album->getContainerGUID());
+$owner = elgg_get_page_owner_entity();
+
+gatekeeper();
+group_gatekeeper();
+
+$title = elgg_echo('image:edit');
+
+// set up breadcrumbs
+elgg_push_breadcrumb(elgg_echo('photos'), "photos/all");
+if (elgg_instanceof($owner, 'user')) {
+ elgg_push_breadcrumb($owner->name, "photos/owner/$owner->username");
+} else {
+ elgg_push_breadcrumb($owner->name, "photos/group/$owner->guid/all");
+}
+elgg_push_breadcrumb($album->getTitle(), $album->getURL());
+elgg_push_breadcrumb($entity->getTitle(), $entity->getURL());
+elgg_push_breadcrumb($title);
+
+$vars = tidypics_prepare_form_vars($entity);
+$content = elgg_view_form('photos/image/save', array('method' => 'post'), $vars);
+
+$body = elgg_view_layout('content', array(
+ 'content' => $content,
+ 'title' => $title,
+ 'filter' => '',
+));
+
+echo elgg_view_page($title, $body);
diff --git a/mod/lightpics/pages/photos/image/thumbnail.php b/mod/lightpics/pages/photos/image/thumbnail.php
new file mode 100644
index 000000000..28fabf7aa
--- /dev/null
+++ b/mod/lightpics/pages/photos/image/thumbnail.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Image thumbnail view
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$guid = (int) get_input('guid');
+$size = get_input('size');
+$image = get_entity($guid);
+if (!$image) {
+ // @todo
+}
+
+if ($size == 'master') {
+ $contents = $image->getImage();
+} else {
+ $contents = $image->getThumbnail($size);
+}
+if (!$contents) {
+ forward("mod/lightpics/graphics/image_error_$size");
+}
+
+// expires every 14 days
+$expires = 14 * 60*60*24;
+
+// overwrite header caused by php session code so images can be cached
+$mime = $image->getMimeType();
+header("Content-Type: $mime");
+header("Content-Length: " . strlen($contents));
+header("Cache-Control: public", true);
+header("Pragma: public", true);
+header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $expires) . ' GMT', true);
+
+// Return the thumbnail and exit
+echo $contents;
+exit;
diff --git a/mod/lightpics/pages/photos/image/upload.php b/mod/lightpics/pages/photos/image/upload.php
new file mode 100644
index 000000000..c8e57038c
--- /dev/null
+++ b/mod/lightpics/pages/photos/image/upload.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Upload images
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+gatekeeper();
+
+$album_guid = (int) get_input('guid');
+if (!$album_guid) {
+ // @todo
+ forward();
+}
+
+$album = get_entity($album_guid);
+if (!$album) {
+ // @todo
+ // throw warning and forward to previous page
+ forward(REFERER);
+}
+
+if (!$album->getContainerEntity()->canWriteToContainer()) {
+ // @todo have to be able to edit album to upload photos
+ forward(REFERER);
+}
+
+// set page owner based on container (user or group)
+elgg_set_page_owner_guid($album->getContainerGUID());
+$owner = elgg_get_page_owner_entity();
+
+$title = elgg_echo('album:addpix');
+
+// set up breadcrumbs
+elgg_push_breadcrumb(elgg_echo('photos'), "photos/all");
+elgg_push_breadcrumb($owner->name, "photos/owner/$owner->username");
+elgg_push_breadcrumb($album->getTitle(), $album->getURL());
+elgg_push_breadcrumb(elgg_echo('album:addpix'));
+
+// load javascript dependences
+elgg_load_js('jquery-tmpl');
+elgg_load_js('jquery-load-image');
+elgg_load_js('jquery-canvas-to-blob');
+elgg_load_js('jquery-fileupload');
+elgg_load_js('jquery-fileupload-ui');
+elgg_load_js('tidypics:upload');
+
+$form_vars = array(
+ 'id' => 'fileupload',
+ 'action' => 'action/photos/image/upload',
+ 'enctype' => 'multipart/form-data',
+);
+
+$content = elgg_view_form('photos/basic_upload', $form_vars, array('entity' => $album));
+
+$body = elgg_view_layout('content', array(
+ 'content' => $content,
+ 'title' => $title,
+ 'filter' => '',
+ 'sidebar' => elgg_view('photos/sidebar', array('page' => 'upload')),
+));
+
+echo elgg_view_page($title, $body);
diff --git a/mod/lightpics/pages/photos/image/view.php b/mod/lightpics/pages/photos/image/view.php
new file mode 100644
index 000000000..e30bed70a
--- /dev/null
+++ b/mod/lightpics/pages/photos/image/view.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * View an image
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+group_gatekeeper();
+
+// get the photo entity
+$photo_guid = (int) get_input('guid');
+$photo = get_entity($photo_guid);
+if (!$photo) {
+ register_error(elgg_echo('noaccess'));
+ $_SESSION['last_forward_from'] = current_page_url();
+ forward('');
+}
+
+$photo->addView();
+
+// set page owner based on owner of photo album
+$album = $photo->getContainerEntity();
+if ($album) {
+ elgg_set_page_owner_guid($album->getContainerGUID());
+}
+$owner = elgg_get_page_owner_entity();
+
+// set up breadcrumbs
+elgg_push_breadcrumb(elgg_echo('photos'), 'photos/all');
+if (elgg_instanceof($owner, 'group')) {
+ elgg_push_breadcrumb($owner->name, "photos/group/$owner->guid/all");
+} else {
+ elgg_push_breadcrumb($owner->name, "photos/owner/$owner->username");
+}
+elgg_push_breadcrumb($album->getTitle(), $album->getURL());
+elgg_push_breadcrumb($photo->getTitle());
+
+if (elgg_get_plugin_setting('download_link', 'tidypics')) {
+ // add download button to title menu
+ elgg_register_menu_item('title', array(
+ 'name' => 'download',
+ 'href' => "photos/download/$photo_guid",
+ 'text' => elgg_echo('image:download'),
+ 'link_class' => 'elgg-button elgg-button-action',
+ ));
+}
+
+$content = elgg_view_entity($photo, array('full_view' => true));
+
+$body = elgg_view_layout('content', array(
+ 'filter' => false,
+ 'content' => $content,
+ 'title' => $photo->getTitle(),
+ 'sidebar' => elgg_view('photos/sidebar', array(
+ 'page' => 'view',
+ 'image' => $photo,
+ )),
+));
+
+echo elgg_view_page($photo->getTitle(), $body);
diff --git a/mod/lightpics/pages/photos/owner.php b/mod/lightpics/pages/photos/owner.php
new file mode 100644
index 000000000..506cb569b
--- /dev/null
+++ b/mod/lightpics/pages/photos/owner.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Show all the albums that belong to a user or group
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+group_gatekeeper();
+
+$owner = elgg_get_page_owner_entity();
+
+$title = elgg_echo('album:user', array($owner->name));
+
+// set up breadcrumbs
+elgg_push_breadcrumb(elgg_echo('photos'), 'photos/all');
+elgg_push_breadcrumb($owner->name);
+
+
+$num_albums = 16;
+
+$content = elgg_list_entities(array(
+ 'type' => 'object',
+ 'subtype' => 'album',
+ 'container_guid' => $owner->getGUID(),
+ 'limit' => $num_albums,
+ 'full_view' => false,
+ 'list_type' => 'gallery',
+ 'list_type_toggle' => false,
+ 'gallery_class' => 'tidypics-gallery',
+));
+if (!$content) {
+ $content = elgg_echo('tidypics:none');
+}
+
+elgg_register_title_button();
+
+$params = array(
+ 'filter_context' => 'mine',
+ 'content' => $content,
+ 'title' => $title,
+ 'sidebar' => elgg_view('tidypics/sidebar', array('page' => 'owner')),
+);
+
+// don't show filter if out of filter context
+if ($owner instanceof ElggGroup) {
+ $params['filter'] = false;
+}
+
+if ($owner->getGUID() != elgg_get_logged_in_user_guid()) {
+ $params['filter_context'] = '';
+}
+
+$body = elgg_view_layout('content', $params);
+
+echo elgg_view_page($title, $body);
diff --git a/mod/lightpics/start.php b/mod/lightpics/start.php
new file mode 100644
index 000000000..a2520e499
--- /dev/null
+++ b/mod/lightpics/start.php
@@ -0,0 +1,421 @@
+<?php
+/**
+ * Photo Gallery plugin
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+elgg_register_event_handler('init', 'system', 'tidypics_init');
+
+/**
+ * Tidypics plugin initialization
+ */
+function tidypics_init() {
+ // Register libraries
+ $base_dir = elgg_get_plugins_path() . 'lightpics/lib';
+ elgg_register_library('tidypics:core', "$base_dir/tidypics.php");
+ elgg_register_library('tidypics:upload', "$base_dir/upload.php");
+ elgg_register_library('tidypics:resize', "$base_dir/resize.php");
+ elgg_register_library('tidypics:exif', "$base_dir/exif.php");
+ elgg_load_library('tidypics:core');
+
+ // Set up site menu
+ elgg_register_menu_item('site', array(
+ 'name' => 'photos',
+ 'href' => 'photos/all',
+ 'text' => elgg_echo('photos'),
+ ));
+
+ // Register a page handler so we can have nice URLs
+ elgg_register_page_handler('photos', 'tidypics_page_handler');
+
+ // Extend CSS
+ elgg_extend_view('css/elgg', 'photos/css');
+ elgg_extend_view('css/admin', 'photos/css');
+
+ // Register the JavaScript lib
+ $js = elgg_get_simplecache_url('js', 'photos/tidypics');
+ elgg_register_simplecache_view('js/photos/tidypics');
+ elgg_register_js('tidypics', $js, 'footer');
+ $js = elgg_get_simplecache_url('js', 'photos/upload');
+ elgg_register_simplecache_view('js/photos/upload');
+ elgg_register_js('tidypics:upload', $js, 'footer');
+
+ $js_base = 'mod/lightpics/vendors/jquery-file-upload/js';
+
+ elgg_register_js('jquery-tmpl', "$js_base/vendor/tmpl.min.js", 'footer');
+ elgg_register_js('jquery-load-image', "$js_base/vendor/load-image.min.js", 'footer');
+ elgg_register_js('jquery-canvas-to-blob', "$js_base/vendor/canvas-to-blob.min.js", 'footer');
+
+ elgg_register_js('jquery-fileupload', "$js_base/jquery.fileupload.js", 'footer');
+ elgg_register_js('jquery-fileupload-ui', "$js_base/jquery.fileupload-ui.js", 'footer');
+
+ // Add photos link to owner block/hover menus
+ elgg_register_plugin_hook_handler('register', 'menu:owner_block', 'tidypics_owner_block_menu');
+
+ // Add admin menu item
+ elgg_register_admin_menu_item('configure', 'photos', 'settings');
+
+ // Register for search
+ elgg_register_entity_type('object', 'image');
+ elgg_register_entity_type('object', 'album');
+
+ // Register for the entity menu
+ elgg_register_plugin_hook_handler('register', 'menu:entity', 'tidypics_entity_menu_setup');
+
+ // Register group option
+ add_group_tool_option('photos', elgg_echo('tidypics:enablephotos'), true);
+ elgg_extend_view('groups/tool_latest', 'photos/group_module');
+
+ // Register widgets
+ elgg_register_widget_type('album_view', elgg_echo("tidypics:widget:albums"), elgg_echo("tidypics:widget:album_descr"), 'profile');
+ elgg_register_widget_type('latest_photos', elgg_echo("tidypics:widget:latest"), elgg_echo("tidypics:widget:latest_descr"), 'profile');
+
+ // RSS extensions for embedded media
+ elgg_extend_view('extensions/xmlns', 'extensions/photos/xmlns');
+
+ // allow group members add photos to group albums
+ elgg_register_plugin_hook_handler('container_permissions_check', 'object', 'tidypics_group_permission_override');
+ elgg_register_plugin_hook_handler('permissions_check:metadata', 'object', 'tidypics_group_permission_override');
+
+ // notifications
+ register_notification_object('object', 'album', elgg_echo('tidypics:newalbum_subject'));
+ elgg_register_plugin_hook_handler('notify:entity:message', 'object', 'tidypics_notify_message');
+
+ // Register actions
+ $base_dir = elgg_get_plugins_path() . 'lightpics/actions/photos';
+ elgg_register_action("photos/delete", "$base_dir/delete.php");
+
+ elgg_register_action("photos/album/save", "$base_dir/album/save.php");
+ elgg_register_action("photos/album/sort", "$base_dir/album/sort.php");
+ elgg_register_action("photos/album/set_cover", "$base_dir/album/set_cover.php");
+
+ elgg_register_action("photos/image/upload", "$base_dir/image/upload.php");
+ elgg_register_action("photos/image/save", "$base_dir/image/save.php");
+ elgg_register_action("photos/image/ajax_upload", "$base_dir/image/ajax_upload.php", 'logged_in');
+ elgg_register_action("photos/image/ajax_upload_complete", "$base_dir/image/ajax_upload_complete.php", 'logged_in');
+
+ elgg_register_action("photos/batch/edit", "$base_dir/batch/edit.php");
+
+ elgg_register_action("photos/admin/settings", "$base_dir/admin/settings.php", 'admin');
+ elgg_register_action("photos/admin/create_thumbnails", "$base_dir/admin/create_thumbnails.php", 'admin');
+ elgg_register_action("photos/admin/upgrade", "$base_dir/admin/upgrade.php", 'admin');
+}
+
+/**
+ * Tidypics page handler
+ *
+ * @param array $page Array of url segments
+ * @return bool
+ */
+function tidypics_page_handler($page) {
+
+ if (!isset($page[0])) {
+ return false;
+ }
+
+ elgg_load_js('lightbox');
+ elgg_load_css('lightbox');
+ elgg_load_js('tidypics');
+
+ $base = elgg_get_plugins_path() . 'lightpics/pages/photos';
+
+ // sometimes owner comes as group:181872 which is not automatically handled
+ if (isset($page[1]) && strpos($page[1], 'group:') === 0) {
+ $parts = explode(':', $page[1]);
+ $owner_guid = $parts[1];
+ if ($owner_guid) {
+ elgg_set_page_owner_guid($owner_guid);
+ }
+ }
+ switch ($page[0]) {
+ case "all": // all site albums
+ case "world":
+ require "$base/all.php";
+ break;
+
+ case "owned": // albums owned by container entity
+ case "owner":
+ if (!$owner) {
+ forward();
+ }
+ require "$base/owner.php";
+ break;
+
+ case "friends": // albums of friends
+ require "$base/friends.php";
+ break;
+
+ case "group": // albums of a group
+ require "$base/owner.php";
+ break;
+
+ case "album": // view an album individually
+ set_input('guid', $page[1]);
+ require "$base/album/view.php";
+ break;
+
+ case "new": // create new album
+ case "add":
+ set_input('guid', $page[1]);
+ require "$base/album/add.php";
+ break;
+
+ case "edit": //edit image or album
+ set_input('guid', $page[1]);
+ $entity = get_entity($page[1]);
+ switch ($entity->getSubtype()) {
+ case 'album':
+ require "$base/album/edit.php";
+ break;
+ case 'image':
+ require "$base/image/edit.php";
+ break;
+ case 'tidypics_batch':
+ require "$base/batch/edit.php";
+ break;
+ default:
+ return false;
+ }
+ break;
+
+ case "sort": // sort a photo album
+ set_input('guid', $page[1]);
+ require "$base/album/sort.php";
+ break;
+
+ case "image": //view an image
+ case "view":
+ set_input('guid', $page[1]);
+ require "$base/image/view.php";
+ break;
+
+ case "thumbnail": // tidypics thumbnail
+ set_input('guid', $page[1]);
+ set_input('size', elgg_extract(2, $page, 'small'));
+ require "$base/image/thumbnail.php";
+ break;
+
+ case "upload": // upload images to album
+ set_input('guid', $page[1]);
+
+ if (elgg_get_plugin_setting('uploader', 'tidypics')) {
+ $default_uploader = 'ajax';
+ } else {
+ $default_uploader = 'basic';
+ }
+
+ set_input('uploader', elgg_extract(2, $page, $default_uploader));
+ require "$base/image/upload.php";
+ break;
+
+ case "batch": //update titles and descriptions
+ if (isset($page[1])) {
+ set_input('batch', $page[1]);
+ }
+ include($CONFIG->pluginspath . "lightpics/pages/edit_multiple.php");
+ break;
+
+ case "download": // download an image
+ set_input('guid', $page[1]);
+ set_input('disposition', elgg_extract(2, $page, 'attachment'));
+ include "$base/image/download.php";
+ break;
+
+ case "mostviewed": // images with the most views
+ if (isset($page[1])) {
+ set_input('username', $page[1]);
+ }
+ include($CONFIG->pluginspath . "lightpics/pages/lists/mostviewedimages.php");
+ break;
+
+ case "mostrecent": // images uploaded most recently
+ if (isset($page[1])) {
+ set_input('username', $page[1]);
+ }
+ include($CONFIG->pluginspath . "lightpics/pages/lists/mostrecentimages.php");
+ break;
+
+ case "recentlyviewed": // images most recently viewed
+ include($CONFIG->pluginspath . "lightpics/pages/lists/recentlyviewed.php");
+ break;
+
+ case "recentlycommented": // images with the most recent comments
+ include($CONFIG->pluginspath . "lightpics/pages/lists/recentlycommented.php");
+ break;
+
+ case "highestrated": // images with the highest average rating
+ include($CONFIG->pluginspath . "lightpics/pages/lists/highestrated.php");
+ break;
+
+ case "admin":
+ include ($CONFIG->pluginspath . "lightpics/pages/admin.php");
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Add a menu item to an ownerblock
+ */
+function tidypics_owner_block_menu($hook, $type, $return, $params) {
+ if (elgg_instanceof($params['entity'], 'user')) {
+ $url = "photos/owner/{$params['entity']->username}";
+ $item = new ElggMenuItem('photos', elgg_echo('photos'), $url);
+ $return[] = $item;
+ } else {
+ if ($params['entity']->photos_enable != "no") {
+ $url = "photos/group/{$params['entity']->guid}/all";
+ $item = new ElggMenuItem('photos', elgg_echo('photos:group'), $url);
+ $return[] = $item;
+ }
+ }
+
+ return $return;
+}
+
+/**
+ * Add Tidypics links/info to entity menu
+ */
+function tidypics_entity_menu_setup($hook, $type, $return, $params) {
+ if (elgg_in_context('widgets')) {
+ return $return;
+ }
+
+ $entity = $params['entity'];
+ $handler = elgg_extract('handler', $params, false);
+ if ($handler != 'photos') {
+ return $return;
+ }
+
+ if (elgg_instanceof($entity, 'object', 'image')) {
+ $album = $entity->getContainerEntity();
+ $cover_guid = $album->getCoverImageGuid();
+ if ($cover_guid != $entity->getGUID() && $album->canEdit()) {
+ $url = 'action/photos/album/set_cover'
+ . '?image_guid=' . $entity->getGUID()
+ . '&album_guid=' . $album->getGUID();
+
+ $params = array(
+ 'href' => $url,
+ 'text' => elgg_echo('album:cover_link'),
+ 'is_action' => true,
+ 'is_trusted' => true,
+ 'confirm' => elgg_echo('album:cover')
+ );
+ $text = elgg_view('output/confirmlink', $params);
+
+ $options = array(
+ 'name' => 'set_cover',
+ 'text' => $text,
+ 'priority' => 80,
+ );
+ $return[] = ElggMenuItem::factory($options);
+ }
+
+ if (elgg_get_plugin_setting('view_count', 'tidypics')) {
+ $view_info = $entity->getViewInfo();
+ $text = elgg_echo('tidypics:views', array((int)$view_info['total']));
+ $options = array(
+ 'name' => 'views',
+ 'text' => "<span>$text</span>",
+ 'href' => false,
+ 'priority' => 90,
+ );
+ $return[] = ElggMenuItem::factory($options);
+ }
+ }
+ return $return;
+}
+
+/**
+ * Override permissions for group albums
+ *
+ * 1. To write to a container (album) you must be able to write to the owner of the container (odd)
+ * 2. We also need to change metadata on the album
+ *
+ * @param string $hook
+ * @param string $type
+ * @param bool $result
+ * @param array $params
+ * @return mixed
+ */
+function tidypics_group_permission_override($hook, $type, $result, $params) {
+ if (get_input('action') == 'photos/image/upload') {
+ if (isset($params['container'])) {
+ $album = $params['container'];
+ } else {
+ $album = $params['entity'];
+ }
+
+ if (elgg_instanceof($album, 'object', 'album')) {
+ return $album->getContainerEntity()->canWriteToContainer();
+ }
+ }
+}
+
+
+/**
+ * Create the body of the notification message
+ *
+ * Does not run if a new album without photos
+ *
+ * @param string $hook
+ * @param string $type
+ * @param bool $result
+ * @param array $params
+ * @return mixed
+ */
+function tidypics_notify_message($hook, $type, $result, $params) {
+ $entity = $params['entity'];
+ $to_entity = $params['to_entity'];
+ $method = $params['method'];
+
+ if (elgg_instanceof($entity, 'object', 'album')) {
+ if ($entity->new_album) {
+ // stops notification from being sent
+ return false;
+ }
+
+ if ($entity->first_upload) {
+ $descr = $entity->description;
+ $title = $entity->getTitle();
+ $owner = $entity->getOwnerEntity();
+ return elgg_echo('tidypics:newalbum', array($owner->name))
+ . ': ' . $title . "\n\n" . $descr . "\n\n" . $entity->getURL();
+ } else {
+ if ($entity->shouldNotify()) {
+ $descr = $entity->description;
+ $title = $entity->getTitle();
+ $owner = $entity->getOwnerEntity();
+
+ return elgg_echo('tidypics:updatealbum', array($owner->name, $title)) . ': ' . $entity->getURL();
+ }
+ }
+ }
+
+ return null;
+}
+
+/**
+ * Sets up submenus for tidypics most viewed pages
+ */
+function tidypics_mostviewed_submenus() {
+
+ global $CONFIG;
+
+ add_submenu_item(elgg_echo('tidypics:mostvieweddashboard'), $CONFIG->url . "mod/tidypics/mostvieweddashboard.php");
+ add_submenu_item(elgg_echo('tidypics:mostviewedthisyear'), $CONFIG->url . "mod/tidypics/pages/lists/mostviewedimagesthisyear.php");
+ add_submenu_item(elgg_echo('tidypics:mostviewedthismonth'), $CONFIG->url . "mod/tidypics/pages/lists/mostviewedimagesthismonth.php");
+ add_submenu_item(elgg_echo('tidypics:mostviewedlastmonth'), $CONFIG->url . "mod/tidypics/pages/lists/mostviewedimageslastmonth.php");
+ add_submenu_item(elgg_echo('tidypics:mostviewedtoday'), $CONFIG->url . "mod/tidypics/pages/lists/mostviewedimagestoday.php");
+ add_submenu_item(elgg_echo('tidypics:mostcommented'), $CONFIG->url . "mod/tidypics/pages/lists/mostcommentedimages.php");
+ add_submenu_item(elgg_echo('tidypics:mostcommentedthismonth'), $CONFIG->url . "mod/tidypics/pages/lists/mostcommentedimagesthismonth.php");
+ add_submenu_item(elgg_echo('tidypics:mostcommentedtoday'), $CONFIG->url . "mod/tidypics/pages/lists/mostcommentedimagestoday.php");
+ add_submenu_item(elgg_echo('tidypics:recentlycommented'), $CONFIG->wwwroot . 'pg/photos/recentlycommented/');
+}
diff --git a/mod/lightpics/upgrades/2009082901.php b/mod/lightpics/upgrades/2009082901.php
new file mode 100644
index 000000000..dfbdf79f4
--- /dev/null
+++ b/mod/lightpics/upgrades/2009082901.php
@@ -0,0 +1,29 @@
+<?php
+
+/********************************************
+ *
+ * Upgrade from Tidypics 1.5 to 1.6
+ *
+ *********************************************/
+
+global $CONFIG;
+
+// add image class
+$id = get_subtype_id("object", "image");
+if ($id != 0) {
+ $table = $CONFIG->dbprefix . 'entity_subtypes';
+ $result = update_data("UPDATE {$table} set class='TidypicsImage' where id={$id}");
+ if (!$result) {
+ register_error('unable to update tidypics image class');
+ }
+}
+
+// add album class
+$id = get_subtype_id("object", "album");
+if ($id != 0) {
+ $table = $CONFIG->dbprefix . 'entity_subtypes';
+ $result = update_data("UPDATE {$table} set class='TidypicsAlbum' where id={$id}");
+ if (!$result) {
+ register_error('unable to update tidypics album class');
+ }
+}
diff --git a/mod/lightpics/upgrades/2010073101.php b/mod/lightpics/upgrades/2010073101.php
new file mode 100644
index 000000000..587490500
--- /dev/null
+++ b/mod/lightpics/upgrades/2010073101.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Populate image lists for current photo albums
+ */
+
+$album_subtype_id = get_subtype_id('object', 'album');
+
+global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE, $CONFIG;
+$album_guids = mysql_query("SELECT guid FROM {$CONFIG->dbprefix}entities WHERE subtype = $album_subtype_id");
+while ($guid_obj = mysql_fetch_object($album_guids)) {
+ $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
+
+ $album = get_entity($guid_obj->guid);
+ $images = elgg_get_entities(array(
+ "type" => "object",
+ "subtype" => "image",
+ "container_guid" => $album->guid,
+ "limit" => ELGG_ENTITIES_NO_VALUE,
+ ));
+ $image_list = array();
+ foreach ($images as $image) {
+ $image_list[] = $image->guid;
+ }
+
+ $album->prependImageList($image_list);
+}
+
diff --git a/mod/lightpics/upgrades/2010102801.php b/mod/lightpics/upgrades/2010102801.php
new file mode 100644
index 000000000..5bd5f78b4
--- /dev/null
+++ b/mod/lightpics/upgrades/2010102801.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Convert river entries for tags to be tagger-tagee-annotation from
+ * image-tagee
+ */
+
+$album_subtype_id = get_subtype_id('object', 'album');
+
+global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE, $CONFIG;
+$query = "SELECT * FROM {$CONFIG->dbprefix}river WHERE view = 'river/object/image/tag'";
+$river_items = mysql_query($query);
+while ($item = mysql_fetch_object($river_items)) {
+ $DB_QUERY_CACHE = $DB_PROFILE = array();
+
+ // find the annotation for this river item
+ $annotations = get_annotations($item->subject_guid, '', '', 'phototag', '', 0, 999);
+ foreach ($annotations as $annotation) {
+ $tag = unserialize($annotation->value);
+ if ($tag->type === 'user') {
+ if ($tag->value == $item->object_guid) {
+ $update = "UPDATE {$CONFIG->dbprefix}river SET subject_guid = $annotation->owner_guid, annotation_id = $annotation->id where id = $item->id";
+ mysql_query($update);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/mod/lightpics/upgrades/2012020901.php b/mod/lightpics/upgrades/2012020901.php
new file mode 100644
index 000000000..793279b38
--- /dev/null
+++ b/mod/lightpics/upgrades/2012020901.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Adds last notified metadata and sets the notify interval
+ */
+
+elgg_set_plugin_setting('notify_interval', 60 * 60 * 24, 'tidypics');
+
+$options = array(
+ 'type' => 'object',
+ 'subtype' => 'album',
+ 'limit' => 0
+);
+
+$prefix = elgg_get_config('dbprefix');
+$batch = new ElggBatch('elgg_get_entities', $options);
+
+foreach ($batch as $album) {
+ // grab earliest picture and use that as the notification time
+ // in old version of tidypics notifications went out only when a new album was populated.
+ $q = "SELECT MIN(time_created) as ts FROM {$prefix}entities WHERE container_guid = $album->guid";
+ $row = get_data_row($q);
+
+ if ($row) {
+ $album->last_notified = $row->ts;
+ }
+}
diff --git a/mod/lightpics/vendors/jquery-file-upload/README.md b/mod/lightpics/vendors/jquery-file-upload/README.md
new file mode 100644
index 000000000..4bdca2d5c
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/README.md
@@ -0,0 +1,73 @@
+# jQuery File Upload Plugin
+
+## Demo
+[Demo File Upload](http://blueimp.github.com/jQuery-File-Upload/)
+
+## Setup instructions
+* [How to setup the plugin on your website](https://github.com/blueimp/jQuery-File-Upload/wiki/Setup)
+* [How to use only the basic plugin (minimal setup guide).](https://github.com/blueimp/jQuery-File-Upload/wiki/Basic-plugin)
+
+## Features
+* **Multiple file upload:**
+ Allows to select multiple files at once and upload them simultaneously.
+* **Drag & Drop support:**
+ Allows to upload files by dragging them from your desktop or filemanager and dropping them on your browser window.
+* **Upload progress bar:**
+ Shows a progress bar indicating the upload progress for individual files and for all uploads combined.
+* **Cancelable uploads:**
+ Individual file uploads can be canceled to stop the upload progress.
+* **Resumable uploads:**
+ Aborted uploads can be resumed with browsers supporting the Blob API.
+* **Chunked uploads:**
+ Large files can be uploaded in smaller chunks with browsers supporting the Blob API.
+* **Client-side image resizing:**
+ Images can be automatically resized on client-side with browsers supporting the required JS APIs.
+* **Preview images:**
+ A preview of image files can be displayed before uploading with browsers supporting the required JS APIs.
+* **No browser plugins (e.g. Adobe Flash) required:**
+ The implementation is based on open standards like HTML5 and JavaScript and requires no additional browser plugins.
+* **Graceful fallback for legacy browsers:**
+ Uploads files via XMLHttpRequests if supported and uses iframes as fallback for legacy browsers.
+* **HTML file upload form fallback:**
+ Shows a standard HTML file upload form if JavaScript is disabled.
+* **Cross-site file uploads:**
+ Supports uploading files to a different domain with Cross-site XMLHttpRequests.
+* **Multiple plugin instances:**
+ Allows to use multiple plugin instances on the same webpage.
+* **Customizable and extensible:**
+ Provides an API to set individual options and define callBack methods for various upload events.
+* **Multipart and file contents stream uploads:**
+ Files can be uploaded as standard "multipart/form-data" or file contents stream (HTTP PUT file upload).
+* **Compatible with any server-side application platform:**
+ Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.
+
+## Requirements
+* [jQuery](http://jquery.com/) v. 1.6+
+* [jQuery UI](http://jqueryui.com/) v. 1.8+
+* [jQuery Iframe Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/jquery.iframe-transport.js) (included)
+* [JavaScript Templates engine](https://github.com/blueimp/JavaScript-Templates) v. 2.1.0+ (optional)
+* [JavaScript Load Image function](https://github.com/blueimp/JavaScript-Load-Image) v. 1.1.6+ (optional)
+* [JavaScript Canvas to Blob function](https://github.com/blueimp/JavaScript-Canvas-to-Blob) v. 2.0.0+ (optional)
+
+The jQuery Iframe Transport is required for [browsers without XHR file upload support](https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support).
+The UI version of the File Upload plugin also requires the JavaScript Templates engine as well as the JavaScript Load Image and JavaScript Canvas to Blob functions (for the image previews and resizing functionality). These dependencies are marked as optional, as the basic File Upload plugin can be used without them and the UI version of the plugin can be extended to override these dependencies with alternative solutions.
+
+The User Interface is built with [jQuery UI](http://jqueryui.com/). The demo also includes the [jQuery Image Gallery Plugin](https://github.com/blueimp/jQuery-Image-Gallery).
+
+The repository also includes the [jQuery XDomainRequest Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/cors/jquery.xdr-transport.js), which enables Cross-domain AJAX requests (GET and POST only) in Microsoft Internet Explorer >= 8. However, the XDomainRequest object doesn't support file uploads and the plugin is only used by the [Demo](http://blueimp.github.com/jQuery-File-Upload/) for Cross-domain requests to delete uploaded files from the demo file upload service.
+
+[Cross-domain File Uploads](https://github.com/blueimp/jQuery-File-Upload/wiki/Cross-domain-uploads) using the [Iframe Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/jquery.iframe-transport.js) require a redirect back to the origin server to retrieve the upload results. The [example implementation](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/main.js) makes use of [result.html](https://github.com/blueimp/jQuery-File-Upload/blob/master/cors/result.html) as a static redirect page for the origin server.
+
+## Browser Support (tested versions)
+* Google Chrome - 7.0+
+* Apple Safari - 4.0+
+* Mozilla Firefox - 3.0+
+* Opera - 10.0+
+* Microsoft Internet Explorer 6.0+
+
+Drag & Drop is only supported on Google Chrome, Firefox 4.0+, Safari 5.0+ and Opera 12.0+.
+Microsoft Internet Explorer has no support for multiple file selection or upload progress.
+[Extended browser support information](https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support).
+
+## License
+Released under the [MIT license](http://www.opensource.org/licenses/MIT).
diff --git a/mod/lightpics/vendors/jquery-file-upload/cors/postmessage.html b/mod/lightpics/vendors/jquery-file-upload/cors/postmessage.html
new file mode 100644
index 000000000..4a93bb867
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/cors/postmessage.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<!--
+/*
+ * jQuery File Upload Plugin postMessage API 1.1.1
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+-->
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>jQuery File Upload Plugin postMessage API</title>
+<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
+</head>
+<body>
+<script>
+/*jslint unparam: true, regexp: true */
+/*global $, Blob, FormData, location */
+'use strict';
+var origin = /^http:\/\/example.org/,
+ target = new RegExp('^(http(s)?:)?\\/\\/' + location.host + '\\/');
+$(window).on('message', function (e) {
+ e = e.originalEvent;
+ var s = e.data,
+ xhr = $.ajaxSettings.xhr(),
+ f;
+ if (!origin.test(e.origin)) {
+ throw new Error('Origin "' + e.origin + '" does not match ' + origin);
+ }
+ if (!target.test(e.data.url)) {
+ throw new Error('Target "' + e.data.url + '" does not match ' + target);
+ }
+ $(xhr.upload).on('progress', function (ev) {
+ ev = ev.originalEvent;
+ e.source.postMessage({
+ id: s.id,
+ type: ev.type,
+ timeStamp: ev.timeStamp,
+ lengthComputable: ev.lengthComputable,
+ loaded: ev.loaded,
+ total: ev.total
+ }, e.origin);
+ });
+ s.xhr = function () {
+ return xhr;
+ };
+ if (!(s.data instanceof Blob)) {
+ f = new FormData();
+ $.each(s.data, function (i, v) {
+ f.append(v.name, v.value);
+ });
+ s.data = f;
+ }
+ $.ajax(s).always(function (result, statusText, jqXHR) {
+ if (!jqXHR.done) {
+ jqXHR = result;
+ result = null;
+ }
+ e.source.postMessage({
+ id: s.id,
+ status: jqXHR.status,
+ statusText: statusText,
+ result: result,
+ headers: jqXHR.getAllResponseHeaders()
+ }, e.origin);
+ });
+});
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/mod/lightpics/vendors/jquery-file-upload/cors/result.html b/mod/lightpics/vendors/jquery-file-upload/cors/result.html
new file mode 100644
index 000000000..7c9802684
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/cors/result.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<!--
+/*
+ * jQuery Iframe Transport Plugin Redirect Page 2.0
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+-->
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>jQuery Iframe Transport Plugin Redirect Page</title>
+</head>
+<body><script>document.body.innerHTML=decodeURIComponent(window.location.search.slice(1));</script></body>
+</html> \ No newline at end of file
diff --git a/mod/lightpics/vendors/jquery-file-upload/css/jquery.fileupload-ui.css b/mod/lightpics/vendors/jquery-file-upload/css/jquery.fileupload-ui.css
new file mode 100644
index 000000000..bb706ff06
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/css/jquery.fileupload-ui.css
@@ -0,0 +1,107 @@
+@charset 'UTF-8';
+/*
+ * jQuery File Upload UI Plugin CSS 6.3
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+.fileinput-button {
+ position: relative;
+ overflow: hidden;
+ float: left;
+ margin-right: 5px;
+}
+.fileinput-button input {
+ position: absolute;
+ top: 0;
+ right: 0;
+ margin: 0;
+ border: solid transparent;
+ border-width: 0 0 100px 200px;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ -moz-transform: translate(-300px, 0) scale(4);
+ direction: ltr;
+ cursor: pointer;
+}
+.fileupload-buttonbar .ui-button,
+.fileupload-buttonbar .toggle {
+ margin-bottom: 5px;
+}
+.files .progress {
+ width: 200px;
+}
+.files .progress,
+.fileupload-buttonbar .progress {
+ height: 20px;
+}
+.files .ui-progressbar-value,
+.fileupload-buttonbar .ui-progressbar-value {
+ background: url(../img/progressbar.gif);
+}
+.fileupload-buttonbar .fade,
+.files .fade {
+ display: none;
+}
+.fileupload-loading {
+ position: absolute;
+ left: 50%;
+ width: 128px;
+ height: 128px;
+ background: url(../img/loading.gif) center no-repeat;
+ display: none;
+}
+.fileupload-processing .fileupload-loading {
+ display: block;
+}
+
+/* Fix for IE 6: */
+*html .fileinput-button {
+ margin-right: -2px;
+}
+*html .fileinput-button .ui-button-text {
+ line-height: 24px;
+}
+*html .fileupload-buttonbar .ui-button {
+ margin-left: 3px;
+}
+
+/* Fix for IE 7: */
+*+html .fileinput-button {
+ margin-right: 1px;
+}
+*+html .fileinput-button .ui-button-text {
+ line-height: 24px;
+}
+*+html .fileupload-buttonbar .ui-button {
+ margin-left: 3px;
+}
+
+@media (max-width: 480px) {
+ .files .preview * {
+ width: 40px;
+ }
+ .files .name * {
+ width: 80px;
+ display: inline-block;
+ word-wrap: break-word;
+ }
+ .files .progress {
+ width: 20px;
+ }
+ .files .delete {
+ width: 60px;
+ }
+}
+
+/* Fix for Webkit (Safari, Chrome) */
+@media screen and (-webkit-min-device-pixel-ratio:0) {
+ .fileinput-button {
+ margin-top: 2px;
+ }
+}
diff --git a/mod/lightpics/vendors/jquery-file-upload/css/style.css b/mod/lightpics/vendors/jquery-file-upload/css/style.css
new file mode 100644
index 000000000..6ed82e679
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/css/style.css
@@ -0,0 +1,92 @@
+@charset 'UTF-8';
+/*
+ * jQuery File Upload Plugin CSS Example 1.1
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+body {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 13px;
+ line-height: 18px;
+ width: 960px;
+ margin: 10px auto;
+}
+a {
+ color: #08C;
+ text-decoration: none;
+}
+a:hover,
+.active a {
+ color: #005580;
+ text-decoration: underline;
+}
+td {
+ padding: 5px;
+}
+img {
+ border: 0;
+}
+.nav-collapse,
+ul.nav,
+ul.nav li {
+ display: inline-block;
+ margin-right: 10px;
+}
+.navbar .brand {
+ font-size: 16px;
+}
+.row {
+ zoom: 1;
+}
+.row:before, .row:after {
+ display: table;
+ content: "";
+}
+.row:after {
+ clear: both;
+}
+.span5 {
+ width: 400px;
+ float: left;
+}
+.span7 {
+ width: 560px ;
+ float: left;
+}
+
+/* Fix for IE 6: */
+*html .nav-collapse,
+*html ul.nav,
+*html ul.nav li {
+ display: inline;
+}
+*html .navbar-fixed-top {
+ margin-bottom: 35px;
+}
+
+/* Fix for IE 7: */
+*+html .nav-collapse,
+*+html ul.nav,
+*+html ul.nav li {
+ display: inline;
+}
+*+html .navbar-fixed-top {
+ margin-bottom: 35px;
+}
+
+@media (max-width: 980px) {
+ body {
+ width: auto;
+ padding: 10px;
+ }
+ .span5,
+ .span7 {
+ width: auto;
+ }
+}
diff --git a/mod/lightpics/vendors/jquery-file-upload/img/loading.gif b/mod/lightpics/vendors/jquery-file-upload/img/loading.gif
new file mode 100644
index 000000000..90f28cbdb
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/img/loading.gif
Binary files differ
diff --git a/mod/lightpics/vendors/jquery-file-upload/img/progressbar.gif b/mod/lightpics/vendors/jquery-file-upload/img/progressbar.gif
new file mode 100644
index 000000000..fbcce6bc9
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/img/progressbar.gif
Binary files differ
diff --git a/mod/lightpics/vendors/jquery-file-upload/index.html b/mod/lightpics/vendors/jquery-file-upload/index.html
new file mode 100644
index 000000000..a57d6fa55
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/index.html
@@ -0,0 +1,234 @@
+<!DOCTYPE HTML>
+<!--
+/*
+ * jQuery File Upload Plugin Demo 6.9.1
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+-->
+<html lang="en">
+<head>
+<!-- Force latest IE rendering engine or ChromeFrame if installed -->
+<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><![endif]-->
+<meta charset="utf-8">
+<title>jQuery File Upload Demo</title>
+<meta name="description" content="File Upload widget with multiple file selection, drag&amp;drop support, progress bar and preview images for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.">
+<meta name="viewport" content="width=device-width">
+<!-- jQuery UI styles -->
+<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/base/jquery-ui.css" id="theme">
+<!-- jQuery Image Gallery styles -->
+<link rel="stylesheet" href="http://blueimp.github.com/jQuery-Image-Gallery/css/jquery.image-gallery.min.css">
+<!-- CSS to style the file input field as button and adjust the jQuery UI progress bars -->
+<link rel="stylesheet" href="css/jquery.fileupload-ui.css">
+<!-- Generic page styles -->
+<link rel="stylesheet" href="css/style.css">
+<!-- Shim to make HTML5 elements usable in older Internet Explorer versions -->
+<!--[if lt IE 9]><script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
+</head>
+<body>
+<div class="navbar navbar-fixed-top">
+ <div class="navbar-inner">
+ <div class="container">
+ <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </a>
+ <a class="brand" href="https://github.com/blueimp/jQuery-File-Upload">jQuery File Upload</a>
+ <div class="nav-collapse">
+ <ul class="nav">
+ <li class="active"><a href="#">Demo</a></li>
+ <li><a href="https://github.com/blueimp/jQuery-File-Upload/downloads">Downloads</a></li>
+ <li><a href="https://github.com/blueimp/jQuery-File-Upload">Source Code</a></li>
+ <li><a href="https://github.com/blueimp/jQuery-File-Upload/wiki">Documentation</a></li>
+ <li><a href="https://github.com/blueimp/jQuery-File-Upload/issues">Issues</a></li>
+ <li><a href="test/">Test</a></li>
+ <li><a href="https://blueimp.net">&copy; Sebastian Tschan</a></li>
+ </ul>
+ <select id="theme-switcher" class="pull-right">
+ <option value="base" selected>Base</option>
+ <option value="black-tie">Black Tie</option>
+ <option value="blitzer">Blitzer</option>
+ <option value="cupertino">Cupertino</option>
+ <option value="dark-hive">Dark Hive</option>
+ <option value="dot-luv">Dot Luv</option>
+ <option value="eggplant">Eggplant</option>
+ <option value="excite-bike">Excite Bike</option>
+ <option value="flick">Flick</option>
+ <option value="hot-sneaks">Hot sneaks</option>
+ <option value="humanity">Humanity</option>
+ <option value="le-frog">Le Frog</option>
+ <option value="mint-choc">Mint Choc</option>
+ <option value="overcast">Overcast</option>
+ <option value="pepper-grinder">Pepper Grinder</option>
+ <option value="redmond">Redmond</option>
+ <option value="smoothness">Smoothness</option>
+ <option value="south-street">South Street</option>
+ <option value="start">Start</option>
+ <option value="sunny">Sunny</option>
+ <option value="swanky-purse">Swanky Purse</option>
+ <option value="trontastic">Trontastic</option>
+ <option value="ui-darkness">UI Darkness</option>
+ <option value="ui-lightness">UI Lightness</option>
+ <option value="vader">Vader</option>
+ </select>
+ </div>
+ </div>
+ </div>
+</div>
+<div class="container">
+ <div class="page-header">
+ <h1>jQuery File Upload Demo</h1>
+ </div>
+ <blockquote>
+ <p>File Upload widget with multiple file selection, drag&amp;drop support, progress bars and preview images for jQuery.<br>
+ Supports cross-domain, chunked and resumable file uploads and client-side image resizing.<br>
+ Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.</p>
+ </blockquote>
+ <br>
+ <!-- The file upload form used as target for the file upload widget -->
+ <form id="fileupload" action="server/php/" method="POST" enctype="multipart/form-data">
+ <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
+ <div class="row fileupload-buttonbar">
+ <div class="span7">
+ <!-- The fileinput-button span is used to style the file input field as button -->
+ <span class="btn btn-success fileinput-button">
+ <i class="icon-plus icon-white"></i>
+ <span>Add files...</span>
+ <input type="file" name="files[]" multiple>
+ </span>
+ <button type="submit" class="btn btn-primary start">
+ <i class="icon-upload icon-white"></i>
+ <span>Start upload</span>
+ </button>
+ <button type="reset" class="btn btn-warning cancel">
+ <i class="icon-ban-circle icon-white"></i>
+ <span>Cancel upload</span>
+ </button>
+ <button type="button" class="btn btn-danger delete">
+ <i class="icon-trash icon-white"></i>
+ <span>Delete</span>
+ </button>
+ <input type="checkbox" class="toggle">
+ </div>
+ <!-- The global progress information -->
+ <div class="span5 fileupload-progress fade">
+ <!-- The global progress bar -->
+ <div class="progress progress-success progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100">
+ <div class="bar" style="width:0%;"></div>
+ </div>
+ <!-- The extended global progress information -->
+ <div class="progress-extended">&nbsp;</div>
+ </div>
+ </div>
+ <!-- The loading indicator is shown during file processing -->
+ <div class="fileupload-loading"></div>
+ <br>
+ <!-- The table listing the files available for upload/download -->
+ <table role="presentation" class="table table-striped"><tbody class="files" data-toggle="modal-gallery" data-target="#modal-gallery"></tbody></table>
+ </form>
+ <br>
+ <div class="well">
+ <h3>Demo Notes</h3>
+ <ul>
+ <li>The maximum file size for uploads in this demo is <strong>5 MB</strong> (default file size is unlimited).</li>
+ <li>Only image files (<strong>JPG, GIF, PNG</strong>) are allowed in this demo (by default there is no file type restriction).</li>
+ <li>Uploaded files will be deleted automatically after <strong>5 minutes</strong> (demo setting).</li>
+ <li>You can <strong>drag &amp; drop</strong> files from your desktop on this webpage with Google Chrome, Mozilla Firefox and Apple Safari.</li>
+ <li>Please refer to the <a href="https://github.com/blueimp/jQuery-File-Upload">project website</a> and <a href="https://github.com/blueimp/jQuery-File-Upload/wiki">documentation</a> for more information.</li>
+ </ul>
+ </div>
+</div>
+<!-- The template to display files available for upload -->
+<script id="template-upload" type="text/x-tmpl">
+{% for (var i=0, file; file=o.files[i]; i++) { %}
+ <tr class="template-upload fade">
+ <td class="preview"><span class="fade"></span></td>
+ <td class="name"><span>{%=file.name%}</span></td>
+ <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
+ {% if (file.error) { %}
+ <td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
+ {% } else if (o.files.valid && !i) { %}
+ <td>
+ <div class="progress progress-success progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><div class="bar" style="width:0%;"></div></div>
+ </td>
+ <td class="start">{% if (!o.options.autoUpload) { %}
+ <button class="btn btn-primary">
+ <i class="icon-upload icon-white"></i>
+ <span>{%=locale.fileupload.start%}</span>
+ </button>
+ {% } %}</td>
+ {% } else { %}
+ <td colspan="2"></td>
+ {% } %}
+ <td class="cancel">{% if (!i) { %}
+ <button class="btn btn-warning">
+ <i class="icon-ban-circle icon-white"></i>
+ <span>{%=locale.fileupload.cancel%}</span>
+ </button>
+ {% } %}</td>
+ </tr>
+{% } %}
+</script>
+<!-- The template to display files available for download -->
+<script id="template-download" type="text/x-tmpl">
+{% for (var i=0, file; file=o.files[i]; i++) { %}
+ <tr class="template-download fade">
+ {% if (file.error) { %}
+ <td></td>
+ <td class="name"><span>{%=file.name%}</span></td>
+ <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
+ <td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
+ {% } else { %}
+ <td class="preview">{% if (file.thumbnail_url) { %}
+ <a href="{%=file.url%}" title="{%=file.name%}" rel="gallery" download="{%=file.name%}"><img src="{%=file.thumbnail_url%}"></a>
+ {% } %}</td>
+ <td class="name">
+ <a href="{%=file.url%}" title="{%=file.name%}" rel="{%=file.thumbnail_url&&'gallery'%}" download="{%=file.name%}">{%=file.name%}</a>
+ </td>
+ <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
+ <td colspan="2"></td>
+ {% } %}
+ <td class="delete">
+ <button class="btn btn-danger" data-type="{%=file.delete_type%}" data-url="{%=file.delete_url%}">
+ <i class="icon-trash icon-white"></i>
+ <span>{%=locale.fileupload.destroy%}</span>
+ </button>
+ <input type="checkbox" name="delete" value="1">
+ </td>
+ </tr>
+{% } %}
+</script>
+<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
+<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>
+<!-- The Templates plugin is included to render the upload/download listings -->
+<script src="http://blueimp.github.com/JavaScript-Templates/tmpl.min.js"></script>
+<!-- The Load Image plugin is included for the preview images and image resizing functionality -->
+<script src="http://blueimp.github.com/JavaScript-Load-Image/load-image.min.js"></script>
+<!-- The Canvas to Blob plugin is included for image resizing functionality -->
+<script src="http://blueimp.github.com/JavaScript-Canvas-to-Blob/canvas-to-blob.min.js"></script>
+<!-- jQuery Image Gallery -->
+<script src="http://blueimp.github.com/jQuery-Image-Gallery/js/jquery.image-gallery.min.js"></script>
+<!-- The Iframe Transport is required for browsers without support for XHR file uploads -->
+<script src="js/jquery.iframe-transport.js"></script>
+<!-- The basic File Upload plugin -->
+<script src="js/jquery.fileupload.js"></script>
+<!-- The File Upload file processing plugin -->
+<script src="js/jquery.fileupload-fp.js"></script>
+<!-- The File Upload user interface plugin -->
+<script src="js/jquery.fileupload-ui.js"></script>
+<!-- The File Upload jQuery UI plugin -->
+<script src="js/jquery.fileupload-jui.js"></script>
+<!-- The localization script -->
+<script src="js/locale.js"></script>
+<!-- The main application script -->
+<script src="js/main.js"></script>
+<!-- The XDomainRequest Transport is included for cross-domain file deletion for IE8+ -->
+<!--[if gte IE 8]><script src="js/cors/jquery.xdr-transport.js"></script><![endif]-->
+</body>
+</html>
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/cors/jquery.postmessage-transport.js b/mod/lightpics/vendors/jquery-file-upload/js/cors/jquery.postmessage-transport.js
new file mode 100644
index 000000000..931b6352b
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/cors/jquery.postmessage-transport.js
@@ -0,0 +1,117 @@
+/*
+ * jQuery postMessage Transport Plugin 1.1
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint unparam: true, nomen: true */
+/*global define, window, document */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define(['jquery'], factory);
+ } else {
+ // Browser globals:
+ factory(window.jQuery);
+ }
+}(function ($) {
+ 'use strict';
+
+ var counter = 0,
+ names = [
+ 'accepts',
+ 'cache',
+ 'contents',
+ 'contentType',
+ 'crossDomain',
+ 'data',
+ 'dataType',
+ 'headers',
+ 'ifModified',
+ 'mimeType',
+ 'password',
+ 'processData',
+ 'timeout',
+ 'traditional',
+ 'type',
+ 'url',
+ 'username'
+ ],
+ convert = function (p) {
+ return p;
+ };
+
+ $.ajaxSetup({
+ converters: {
+ 'postmessage text': convert,
+ 'postmessage json': convert,
+ 'postmessage html': convert
+ }
+ });
+
+ $.ajaxTransport('postmessage', function (options) {
+ if (options.postMessage && window.postMessage) {
+ var iframe,
+ loc = $('<a>').prop('href', options.postMessage)[0],
+ target = loc.protocol + '//' + loc.host,
+ xhrUpload = options.xhr().upload;
+ return {
+ send: function (_, completeCallback) {
+ var message = {
+ id: 'postmessage-transport-' + (counter += 1)
+ },
+ eventName = 'message.' + message.id;
+ iframe = $(
+ '<iframe style="display:none;" src="' +
+ options.postMessage + '" name="' +
+ message.id + '"></iframe>'
+ ).bind('load', function () {
+ $.each(names, function (i, name) {
+ message[name] = options[name];
+ });
+ message.dataType = message.dataType.replace('postmessage ', '');
+ $(window).bind(eventName, function (e) {
+ e = e.originalEvent;
+ var data = e.data,
+ ev;
+ if (e.origin === target && data.id === message.id) {
+ if (data.type === 'progress') {
+ ev = document.createEvent('Event');
+ ev.initEvent(data.type, false, true);
+ $.extend(ev, data);
+ xhrUpload.dispatchEvent(ev);
+ } else {
+ completeCallback(
+ data.status,
+ data.statusText,
+ {postmessage: data.result},
+ data.headers
+ );
+ iframe.remove();
+ $(window).unbind(eventName);
+ }
+ }
+ });
+ iframe[0].contentWindow.postMessage(
+ message,
+ target
+ );
+ }).appendTo(document.body);
+ },
+ abort: function () {
+ if (iframe) {
+ iframe.remove();
+ }
+ }
+ };
+ }
+ });
+
+}));
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/cors/jquery.xdr-transport.js b/mod/lightpics/vendors/jquery-file-upload/js/cors/jquery.xdr-transport.js
new file mode 100644
index 000000000..c42c54828
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/cors/jquery.xdr-transport.js
@@ -0,0 +1,85 @@
+/*
+ * jQuery XDomainRequest Transport Plugin 1.1.2
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ *
+ * Based on Julian Aubourg's ajaxHooks xdr.js:
+ * https://github.com/jaubourg/ajaxHooks/
+ */
+
+/*jslint unparam: true */
+/*global define, window, XDomainRequest */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define(['jquery'], factory);
+ } else {
+ // Browser globals:
+ factory(window.jQuery);
+ }
+}(function ($) {
+ 'use strict';
+ if (window.XDomainRequest && !$.support.cors) {
+ $.ajaxTransport(function (s) {
+ if (s.crossDomain && s.async) {
+ if (s.timeout) {
+ s.xdrTimeout = s.timeout;
+ delete s.timeout;
+ }
+ var xdr;
+ return {
+ send: function (headers, completeCallback) {
+ function callback(status, statusText, responses, responseHeaders) {
+ xdr.onload = xdr.onerror = xdr.ontimeout = $.noop;
+ xdr = null;
+ completeCallback(status, statusText, responses, responseHeaders);
+ }
+ xdr = new XDomainRequest();
+ // XDomainRequest only supports GET and POST:
+ if (s.type === 'DELETE') {
+ s.url = s.url + (/\?/.test(s.url) ? '&' : '?') +
+ '_method=DELETE';
+ s.type = 'POST';
+ } else if (s.type === 'PUT') {
+ s.url = s.url + (/\?/.test(s.url) ? '&' : '?') +
+ '_method=PUT';
+ s.type = 'POST';
+ }
+ xdr.open(s.type, s.url);
+ xdr.onload = function () {
+ callback(
+ 200,
+ 'OK',
+ {text: xdr.responseText},
+ 'Content-Type: ' + xdr.contentType
+ );
+ };
+ xdr.onerror = function () {
+ callback(404, 'Not Found');
+ };
+ if (s.xdrTimeout) {
+ xdr.ontimeout = function () {
+ callback(0, 'timeout');
+ };
+ xdr.timeout = s.xdrTimeout;
+ }
+ xdr.send((s.hasContent && s.data) || null);
+ },
+ abort: function () {
+ if (xdr) {
+ xdr.onerror = $.noop();
+ xdr.abort();
+ }
+ }
+ };
+ }
+ });
+ }
+}));
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload-fp.js b/mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload-fp.js
new file mode 100644
index 000000000..634fb5e4e
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload-fp.js
@@ -0,0 +1,219 @@
+/*
+ * jQuery File Upload File Processing Plugin 1.0
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint nomen: true, unparam: true, regexp: true */
+/*global define, window, document */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define([
+ 'jquery',
+ 'load-image',
+ 'canvas-to-blob',
+ './jquery.fileupload'
+ ], factory);
+ } else {
+ // Browser globals:
+ factory(
+ window.jQuery,
+ window.loadImage
+ );
+ }
+}(function ($, loadImage) {
+ 'use strict';
+
+ // The File Upload IP version extends the basic fileupload widget
+ // with file processing functionality:
+ $.widget('blueimpFP.fileupload', $.blueimp.fileupload, {
+
+ options: {
+ // The list of file processing actions:
+ process: [
+ /*
+ {
+ action: 'load',
+ fileTypes: /^image\/(gif|jpeg|png)$/,
+ maxFileSize: 20000000 // 20MB
+ },
+ {
+ action: 'resize',
+ maxWidth: 1920,
+ maxHeight: 1200,
+ minWidth: 800,
+ minHeight: 600
+ },
+ {
+ action: 'save'
+ }
+ */
+ ],
+
+ // The add callback is invoked as soon as files are added to the
+ // fileupload widget (via file input selection, drag & drop or add
+ // API call). See the basic file upload widget for more information:
+ add: function (e, data) {
+ $(this).fileupload('process', data).done(function () {
+ data.submit();
+ });
+ }
+ },
+
+ processActions: {
+ // Loads the image given via data.files and data.index
+ // as canvas element.
+ // Accepts the options fileTypes (regular expression)
+ // and maxFileSize (integer) to limit the files to load:
+ load: function (data, options) {
+ var that = this,
+ file = data.files[data.index],
+ dfd = $.Deferred();
+ if (window.HTMLCanvasElement &&
+ window.HTMLCanvasElement.prototype.toBlob &&
+ ($.type(options.maxFileSize) !== 'number' ||
+ file.size < options.maxFileSize) &&
+ (!options.fileTypes ||
+ options.fileTypes.test(file.type))) {
+ loadImage(
+ file,
+ function (canvas) {
+ data.canvas = canvas;
+ dfd.resolveWith(that, [data]);
+ },
+ {canvas: true}
+ );
+ } else {
+ dfd.rejectWith(that, [data]);
+ }
+ return dfd.promise();
+ },
+ // Resizes the image given as data.canvas and updates
+ // data.canvas with the resized image.
+ // Accepts the options maxWidth, maxHeight, minWidth and
+ // minHeight to scale the given image:
+ resize: function (data, options) {
+ if (data.canvas) {
+ var canvas = loadImage.scale(data.canvas, options);
+ if (canvas.width !== data.canvas.width ||
+ canvas.height !== data.canvas.height) {
+ data.canvas = canvas;
+ data.processed = true;
+ }
+ }
+ return data;
+ },
+ // Saves the processed image given as data.canvas
+ // inplace at data.index of data.files:
+ save: function (data, options) {
+ // Do nothing if no processing has happened:
+ if (!data.canvas || !data.processed) {
+ return data;
+ }
+ var that = this,
+ file = data.files[data.index],
+ name = file.name,
+ dfd = $.Deferred(),
+ callback = function (blob) {
+ if (!blob.name) {
+ if (file.type === blob.type) {
+ blob.name = file.name;
+ } else if (file.name) {
+ blob.name = file.name.replace(
+ /\..+$/,
+ '.' + blob.type.substr(6)
+ );
+ }
+ }
+ // Store the created blob at the position
+ // of the original file in the files list:
+ data.files[data.index] = blob;
+ dfd.resolveWith(that, [data]);
+ };
+ // Use canvas.mozGetAsFile directly, to retain the filename, as
+ // Gecko doesn't support the filename option for FormData.append:
+ if (data.canvas.mozGetAsFile) {
+ callback(data.canvas.mozGetAsFile(
+ (/^image\/(jpeg|png)$/.test(file.type) && name) ||
+ ((name && name.replace(/\..+$/, '')) ||
+ 'blob') + '.png',
+ file.type
+ ));
+ } else {
+ data.canvas.toBlob(callback, file.type);
+ }
+ return dfd.promise();
+ }
+ },
+
+ // Resizes the file at the given index and stores the created blob at
+ // the original position of the files list, returns a Promise object:
+ _processFile: function (files, index, options) {
+ var that = this,
+ dfd = $.Deferred().resolveWith(that, [{
+ files: files,
+ index: index
+ }]),
+ chain = dfd.promise();
+ that._processing += 1;
+ $.each(options.process, function (i, settings) {
+ chain = chain.pipe(function (data) {
+ return that.processActions[settings.action]
+ .call(this, data, settings);
+ });
+ });
+ chain.always(function () {
+ that._processing -= 1;
+ if (that._processing === 0) {
+ that.element
+ .removeClass('fileupload-processing');
+ }
+ });
+ if (that._processing === 1) {
+ that.element.addClass('fileupload-processing');
+ }
+ return chain;
+ },
+
+ // Processes the files given as files property of the data parameter,
+ // returns a Promise object that allows to bind a done handler, which
+ // will be invoked after processing all files (inplace) is done:
+ process: function (data) {
+ var that = this,
+ options = $.extend({}, this.options, data);
+ if (options.process && options.process.length &&
+ this._isXHRUpload(options)) {
+ $.each(data.files, function (index, file) {
+ that._processingQueue = that._processingQueue.pipe(
+ function () {
+ var dfd = $.Deferred();
+ that._processFile(data.files, index, options)
+ .always(function () {
+ dfd.resolveWith(that);
+ });
+ return dfd.promise();
+ }
+ );
+ });
+ }
+ return this._processingQueue;
+ },
+
+ _create: function () {
+ $.blueimp.fileupload.prototype._create.call(this);
+ this._processing = 0;
+ this._processingQueue = $.Deferred().resolveWith(this)
+ .promise();
+ }
+
+ });
+
+}));
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload-jui.js b/mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload-jui.js
new file mode 100644
index 000000000..69736a4a4
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload-jui.js
@@ -0,0 +1,141 @@
+/*
+ * jQuery File Upload jQuery UI Plugin 1.2
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint nomen: true */
+/*global define, window */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define(['jquery', './jquery.fileupload-ui.js'], factory);
+ } else {
+ // Browser globals:
+ factory(window.jQuery);
+ }
+}(function ($) {
+ 'use strict';
+ $.widget('blueimpJUI.fileupload', $.blueimpUI.fileupload, {
+ options: {
+ sent: function (e, data) {
+ if (data.context && data.dataType &&
+ data.dataType.substr(0, 6) === 'iframe') {
+ // Iframe Transport does not support progress events.
+ // In lack of an indeterminate progress bar, we set
+ // the progress to 100%, showing the full animated bar:
+ data.context
+ .find('.progress').progressbar(
+ 'option',
+ 'value',
+ 100
+ );
+ }
+ },
+ progress: function (e, data) {
+ if (data.context) {
+ data.context.find('.progress').progressbar(
+ 'option',
+ 'value',
+ parseInt(data.loaded / data.total * 100, 10)
+ );
+ }
+ },
+ progressall: function (e, data) {
+ var $this = $(this);
+ $this.find('.fileupload-progress')
+ .find('.progress').progressbar(
+ 'option',
+ 'value',
+ parseInt(data.loaded / data.total * 100, 10)
+ ).end()
+ .find('.progress-extended').each(function () {
+ $(this).html(
+ $this.data('fileupload')
+ ._renderExtendedProgress(data)
+ );
+ });
+ }
+ },
+ _renderUpload: function (func, files) {
+ var node = $.blueimpUI.fileupload.prototype
+ ._renderUpload.call(this, func, files),
+ showIconText = $(window).width() > 480;
+ node.find('.progress').empty().progressbar();
+ node.find('.start button').button({
+ icons: {primary: 'ui-icon-circle-arrow-e'},
+ text: showIconText
+ });
+ node.find('.cancel button').button({
+ icons: {primary: 'ui-icon-cancel'},
+ text: showIconText
+ });
+ return node;
+ },
+ _renderDownload: function (func, files) {
+ var node = $.blueimpUI.fileupload.prototype
+ ._renderDownload.call(this, func, files),
+ showIconText = $(window).width() > 480;
+ node.find('.delete button').button({
+ icons: {primary: 'ui-icon-trash'},
+ text: showIconText
+ });
+ return node;
+ },
+ _transition: function (node) {
+ var that = this,
+ deferred = $.Deferred();
+ if (node.hasClass('fade')) {
+ node.fadeToggle(function () {
+ deferred.resolveWith(node);
+ });
+ } else {
+ deferred.resolveWith(node);
+ }
+ return deferred;
+ },
+ _create: function () {
+ $.blueimpUI.fileupload.prototype._create.call(this);
+ this.element
+ .find('.fileupload-buttonbar')
+ .find('.fileinput-button').each(function () {
+ var input = $(this).find('input:file').detach();
+ $(this)
+ .button({icons: {primary: 'ui-icon-plusthick'}})
+ .append(input);
+ })
+ .end().find('.start')
+ .button({icons: {primary: 'ui-icon-circle-arrow-e'}})
+ .end().find('.cancel')
+ .button({icons: {primary: 'ui-icon-cancel'}})
+ .end().find('.delete')
+ .button({icons: {primary: 'ui-icon-trash'}})
+ .end().find('.progress').empty().progressbar();
+ },
+ destroy: function () {
+ this.element
+ .find('.fileupload-buttonbar')
+ .find('.fileinput-button').each(function () {
+ var input = $(this).find('input:file').detach();
+ $(this)
+ .button('destroy')
+ .append(input);
+ })
+ .end().find('.start')
+ .button('destroy')
+ .end().find('.cancel')
+ .button('destroy')
+ .end().find('.delete')
+ .button('destroy')
+ .end().find('.progress').progressbar('destroy');
+ $.blueimpUI.fileupload.prototype.destroy.call(this);
+ }
+ });
+}));
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload-ui.js b/mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload-ui.js
new file mode 100644
index 000000000..4c36f0086
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload-ui.js
@@ -0,0 +1,736 @@
+/*
+ * jQuery File Upload User Interface Plugin 6.9.4
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint nomen: true, unparam: true, regexp: true */
+/*global define, window, document, URL, webkitURL, FileReader */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define([
+ 'jquery',
+ 'tmpl',
+ 'load-image',
+ './jquery.fileupload-fp'
+ ], factory);
+ } else {
+ // Browser globals:
+ factory(
+ window.jQuery,
+ window.tmpl,
+ window.loadImage
+ );
+ }
+}(function ($, tmpl, loadImage) {
+ 'use strict';
+
+ // The UI version extends the FP (file processing) version or the basic
+ // file upload widget and adds complete user interface interaction:
+ var parentWidget = ($.blueimpFP || $.blueimp).fileupload;
+ $.widget('blueimpUI.fileupload', parentWidget, {
+
+ options: {
+ // By default, files added to the widget are uploaded as soon
+ // as the user clicks on the start buttons. To enable automatic
+ // uploads, set the following option to true:
+ autoUpload: false,
+ // The following option limits the number of files that are
+ // allowed to be uploaded using this widget:
+ maxNumberOfFiles: undefined,
+ // The maximum allowed file size:
+ maxFileSize: undefined,
+ // The minimum allowed file size:
+ minFileSize: undefined,
+ // The regular expression for allowed file types, matches
+ // against either file type or file name:
+ acceptFileTypes: /.+$/i,
+ // The regular expression to define for which files a preview
+ // image is shown, matched against the file type:
+ previewSourceFileTypes: /^image\/(gif|jpeg|png)$/,
+ // The maximum file size of images that are to be displayed as preview:
+ previewSourceMaxFileSize: 5000000, // 5MB
+ // The maximum width of the preview images:
+ previewMaxWidth: 80,
+ // The maximum height of the preview images:
+ previewMaxHeight: 80,
+ // By default, preview images are displayed as canvas elements
+ // if supported by the browser. Set the following option to false
+ // to always display preview images as img elements:
+ previewAsCanvas: true,
+ // The ID of the upload template:
+ uploadTemplateId: 'template-upload',
+ // The ID of the download template:
+ downloadTemplateId: 'template-download',
+ // The container for the list of files. If undefined, it is set to
+ // an element with class "files" inside of the widget element:
+ filesContainer: undefined,
+ // By default, files are appended to the files container.
+ // Set the following option to true, to prepend files instead:
+ prependFiles: false,
+ // The expected data type of the upload response, sets the dataType
+ // option of the $.ajax upload requests:
+ dataType: 'json',
+
+ // The add callback is invoked as soon as files are added to the fileupload
+ // widget (via file input selection, drag & drop or add API call).
+ // See the basic file upload widget for more information:
+ add: function (e, data) {
+ var that = $(this).data('fileupload'),
+ options = that.options,
+ files = data.files;
+ $(this).fileupload('process', data).done(function () {
+ that._adjustMaxNumberOfFiles(-files.length);
+ data.maxNumberOfFilesAdjusted = true;
+ data.files.valid = data.isValidated = that._validate(files);
+ data.context = that._renderUpload(files).data('data', data);
+ options.filesContainer[
+ options.prependFiles ? 'prepend' : 'append'
+ ](data.context);
+ that._renderPreviews(files, data.context);
+ that._forceReflow(data.context);
+ that._transition(data.context).done(
+ function () {
+ if ((that._trigger('added', e, data) !== false) &&
+ (options.autoUpload || data.autoUpload) &&
+ data.autoUpload !== false && data.isValidated) {
+ data.submit();
+ }
+ }
+ );
+ });
+ },
+ // Callback for the start of each file upload request:
+ send: function (e, data) {
+ var that = $(this).data('fileupload');
+ if (!data.isValidated) {
+ if (!data.maxNumberOfFilesAdjusted) {
+ that._adjustMaxNumberOfFiles(-data.files.length);
+ data.maxNumberOfFilesAdjusted = true;
+ }
+ if (!that._validate(data.files)) {
+ return false;
+ }
+ }
+ if (data.context && data.dataType &&
+ data.dataType.substr(0, 6) === 'iframe') {
+ // Iframe Transport does not support progress events.
+ // In lack of an indeterminate progress bar, we set
+ // the progress to 100%, showing the full animated bar:
+ data.context
+ .find('.progress').addClass(
+ !$.support.transition && 'progress-animated'
+ )
+ .attr('aria-valuenow', 100)
+ .find('.bar').css(
+ 'width',
+ '100%'
+ );
+ }
+ return that._trigger('sent', e, data);
+ },
+ // Callback for successful uploads:
+ done: function (e, data) {
+ var that = $(this).data('fileupload'),
+ template;
+ if (data.context) {
+ data.context.each(function (index) {
+ var file = ($.isArray(data.result) &&
+ data.result[index]) || {error: 'emptyResult'};
+ if (file.error) {
+ that._adjustMaxNumberOfFiles(1);
+ }
+ that._transition($(this)).done(
+ function () {
+ var node = $(this);
+ template = that._renderDownload([file])
+ .replaceAll(node);
+ that._forceReflow(template);
+ that._transition(template).done(
+ function () {
+ data.context = $(this);
+ that._trigger('completed', e, data);
+ }
+ );
+ }
+ );
+ });
+ } else {
+ if ($.isArray(data.result)) {
+ $.each(data.result, function (index, file) {
+ if (data.maxNumberOfFilesAdjusted && file.error) {
+ that._adjustMaxNumberOfFiles(1);
+ } else if (!data.maxNumberOfFilesAdjusted &&
+ !file.error) {
+ that._adjustMaxNumberOfFiles(-1);
+ }
+ });
+ data.maxNumberOfFilesAdjusted = true;
+ }
+ template = that._renderDownload(data.result)
+ .appendTo(that.options.filesContainer);
+ that._forceReflow(template);
+ that._transition(template).done(
+ function () {
+ data.context = $(this);
+ that._trigger('completed', e, data);
+ }
+ );
+ }
+ },
+ // Callback for failed (abort or error) uploads:
+ fail: function (e, data) {
+ var that = $(this).data('fileupload'),
+ template;
+ if (data.maxNumberOfFilesAdjusted) {
+ that._adjustMaxNumberOfFiles(data.files.length);
+ }
+ if (data.context) {
+ data.context.each(function (index) {
+ if (data.errorThrown !== 'abort') {
+ var file = data.files[index];
+ file.error = file.error || data.errorThrown ||
+ true;
+ that._transition($(this)).done(
+ function () {
+ var node = $(this);
+ template = that._renderDownload([file])
+ .replaceAll(node);
+ that._forceReflow(template);
+ that._transition(template).done(
+ function () {
+ data.context = $(this);
+ that._trigger('failed', e, data);
+ }
+ );
+ }
+ );
+ } else {
+ that._transition($(this)).done(
+ function () {
+ $(this).remove();
+ that._trigger('failed', e, data);
+ }
+ );
+ }
+ });
+ } else if (data.errorThrown !== 'abort') {
+ data.context = that._renderUpload(data.files)
+ .appendTo(that.options.filesContainer)
+ .data('data', data);
+ that._forceReflow(data.context);
+ that._transition(data.context).done(
+ function () {
+ data.context = $(this);
+ that._trigger('failed', e, data);
+ }
+ );
+ } else {
+ that._trigger('failed', e, data);
+ }
+ },
+ // Callback for upload progress events:
+ progress: function (e, data) {
+ if (data.context) {
+ var progress = parseInt(data.loaded / data.total * 100, 10);
+ data.context.find('.progress')
+ .attr('aria-valuenow', progress)
+ .find('.bar').css(
+ 'width',
+ progress + '%'
+ );
+ }
+ },
+ // Callback for global upload progress events:
+ progressall: function (e, data) {
+ var $this = $(this),
+ progress = parseInt(data.loaded / data.total * 100, 10),
+ globalProgressNode = $this.find('.fileupload-progress'),
+ extendedProgressNode = globalProgressNode
+ .find('.progress-extended');
+ if (extendedProgressNode.length) {
+ extendedProgressNode.html(
+ $this.data('fileupload')._renderExtendedProgress(data)
+ );
+ }
+ globalProgressNode
+ .find('.progress')
+ .attr('aria-valuenow', progress)
+ .find('.bar').css(
+ 'width',
+ progress + '%'
+ );
+ },
+ // Callback for uploads start, equivalent to the global ajaxStart event:
+ start: function (e) {
+ var that = $(this).data('fileupload');
+ that._transition($(this).find('.fileupload-progress')).done(
+ function () {
+ that._trigger('started', e);
+ }
+ );
+ },
+ // Callback for uploads stop, equivalent to the global ajaxStop event:
+ stop: function (e) {
+ var that = $(this).data('fileupload');
+ that._transition($(this).find('.fileupload-progress')).done(
+ function () {
+ $(this).find('.progress')
+ .attr('aria-valuenow', '0')
+ .find('.bar').css('width', '0%');
+ $(this).find('.progress-extended').html('&nbsp;');
+ that._trigger('stopped', e);
+ }
+ );
+ },
+ // Callback for file deletion:
+ destroy: function (e, data) {
+ var that = $(this).data('fileupload');
+ if (data.url) {
+ $.ajax(data);
+ that._adjustMaxNumberOfFiles(1);
+ }
+ that._transition(data.context).done(
+ function () {
+ $(this).remove();
+ that._trigger('destroyed', e, data);
+ }
+ );
+ }
+ },
+
+ // Link handler, that allows to download files
+ // by drag & drop of the links to the desktop:
+ _enableDragToDesktop: function () {
+ var link = $(this),
+ url = link.prop('href'),
+ name = link.prop('download'),
+ type = 'application/octet-stream';
+ link.bind('dragstart', function (e) {
+ try {
+ e.originalEvent.dataTransfer.setData(
+ 'DownloadURL',
+ [type, name, url].join(':')
+ );
+ } catch (err) {}
+ });
+ },
+
+ _adjustMaxNumberOfFiles: function (operand) {
+ if (typeof this.options.maxNumberOfFiles === 'number') {
+ this.options.maxNumberOfFiles += operand;
+ if (this.options.maxNumberOfFiles < 1) {
+ this._disableFileInputButton();
+ } else {
+ this._enableFileInputButton();
+ }
+ }
+ },
+
+ _formatFileSize: function (bytes) {
+ if (typeof bytes !== 'number') {
+ return '';
+ }
+ if (bytes >= 1000000000) {
+ return (bytes / 1000000000).toFixed(2) + ' GB';
+ }
+ if (bytes >= 1000000) {
+ return (bytes / 1000000).toFixed(2) + ' MB';
+ }
+ return (bytes / 1000).toFixed(2) + ' KB';
+ },
+
+ _formatBitrate: function (bits) {
+ if (typeof bits !== 'number') {
+ return '';
+ }
+ if (bits >= 1000000000) {
+ return (bits / 1000000000).toFixed(2) + ' Gbit/s';
+ }
+ if (bits >= 1000000) {
+ return (bits / 1000000).toFixed(2) + ' Mbit/s';
+ }
+ if (bits >= 1000) {
+ return (bits / 1000).toFixed(2) + ' kbit/s';
+ }
+ return bits + ' bit/s';
+ },
+
+ _formatTime: function (seconds) {
+ var date = new Date(seconds * 1000),
+ days = parseInt(seconds / 86400, 10);
+ days = days ? days + 'd ' : '';
+ return days +
+ ('0' + date.getUTCHours()).slice(-2) + ':' +
+ ('0' + date.getUTCMinutes()).slice(-2) + ':' +
+ ('0' + date.getUTCSeconds()).slice(-2);
+ },
+
+ _formatPercentage: function (floatValue) {
+ return (floatValue * 100).toFixed(2) + ' %';
+ },
+
+ _renderExtendedProgress: function (data) {
+ return this._formatBitrate(data.bitrate) + ' | ' +
+ this._formatTime(
+ (data.total - data.loaded) * 8 / data.bitrate
+ ) + ' | ' +
+ this._formatPercentage(
+ data.loaded / data.total
+ ) + ' | ' +
+ this._formatFileSize(data.loaded) + ' / ' +
+ this._formatFileSize(data.total);
+ },
+
+ _hasError: function (file) {
+ if (file.error) {
+ return file.error;
+ }
+ // The number of added files is subtracted from
+ // maxNumberOfFiles before validation, so we check if
+ // maxNumberOfFiles is below 0 (instead of below 1):
+ if (this.options.maxNumberOfFiles < 0) {
+ return 'maxNumberOfFiles';
+ }
+ // Files are accepted if either the file type or the file name
+ // matches against the acceptFileTypes regular expression, as
+ // only browsers with support for the File API report the type:
+ if (!(this.options.acceptFileTypes.test(file.type) ||
+ this.options.acceptFileTypes.test(file.name))) {
+ return 'acceptFileTypes';
+ }
+ if (this.options.maxFileSize &&
+ file.size > this.options.maxFileSize) {
+ return 'maxFileSize';
+ }
+ if (typeof file.size === 'number' &&
+ file.size < this.options.minFileSize) {
+ return 'minFileSize';
+ }
+ return null;
+ },
+
+ _validate: function (files) {
+ var that = this,
+ valid = !!files.length;
+ $.each(files, function (index, file) {
+ file.error = that._hasError(file);
+ if (file.error) {
+ valid = false;
+ }
+ });
+ return valid;
+ },
+
+ _renderTemplate: function (func, files) {
+ if (!func) {
+ return $();
+ }
+ var result = func({
+ files: files,
+ formatFileSize: this._formatFileSize,
+ options: this.options
+ });
+ if (result instanceof $) {
+ return result;
+ }
+ return $(this.options.templatesContainer).html(result).children();
+ },
+
+ _renderPreview: function (file, node) {
+ var that = this,
+ options = this.options,
+ dfd = $.Deferred();
+ return ((loadImage && loadImage(
+ file,
+ function (img) {
+ node.append(img);
+ that._forceReflow(node);
+ that._transition(node).done(function () {
+ dfd.resolveWith(node);
+ });
+ if (!$.contains(document.body, node[0])) {
+ // If the element is not part of the DOM,
+ // transition events are not triggered,
+ // so we have to resolve manually:
+ dfd.resolveWith(node);
+ }
+ },
+ {
+ maxWidth: options.previewMaxWidth,
+ maxHeight: options.previewMaxHeight,
+ canvas: options.previewAsCanvas
+ }
+ )) || dfd.resolveWith(node)) && dfd;
+ },
+
+ _renderPreviews: function (files, nodes) {
+ var that = this,
+ options = this.options;
+ nodes.find('.preview span').each(function (index, element) {
+ var file = files[index];
+ if (options.previewSourceFileTypes.test(file.type) &&
+ ($.type(options.previewSourceMaxFileSize) !== 'number' ||
+ file.size < options.previewSourceMaxFileSize)) {
+ that._processingQueue = that._processingQueue.pipe(function () {
+ var dfd = $.Deferred();
+ that._renderPreview(file, $(element)).done(
+ function () {
+ dfd.resolveWith(that);
+ }
+ );
+ return dfd.promise();
+ });
+ }
+ });
+ return this._processingQueue;
+ },
+
+ _renderUpload: function (files) {
+ return this._renderTemplate(
+ this.options.uploadTemplate,
+ files
+ );
+ },
+
+ _renderDownload: function (files) {
+ return this._renderTemplate(
+ this.options.downloadTemplate,
+ files
+ ).find('a[download]').each(this._enableDragToDesktop).end();
+ },
+
+ _startHandler: function (e) {
+ e.preventDefault();
+ var button = $(this),
+ template = button.closest('.template-upload'),
+ data = template.data('data');
+ if (data && data.submit && !data.jqXHR && data.submit()) {
+ button.prop('disabled', true);
+ }
+ },
+
+ _cancelHandler: function (e) {
+ e.preventDefault();
+ var template = $(this).closest('.template-upload'),
+ data = template.data('data') || {};
+ if (!data.jqXHR) {
+ data.errorThrown = 'abort';
+ e.data.fileupload._trigger('fail', e, data);
+ } else {
+ data.jqXHR.abort();
+ }
+ },
+
+ _deleteHandler: function (e) {
+ e.preventDefault();
+ var button = $(this);
+ e.data.fileupload._trigger('destroy', e, {
+ context: button.closest('.template-download'),
+ url: button.attr('data-url'),
+ type: button.attr('data-type') || 'DELETE',
+ dataType: e.data.fileupload.options.dataType
+ });
+ },
+
+ _forceReflow: function (node) {
+ return $.support.transition && node.length &&
+ node[0].offsetWidth;
+ },
+
+ _transition: function (node) {
+ var dfd = $.Deferred();
+ if ($.support.transition && node.hasClass('fade')) {
+ node.bind(
+ $.support.transition.end,
+ function (e) {
+ // Make sure we don't respond to other transitions events
+ // in the container element, e.g. from button elements:
+ if (e.target === node[0]) {
+ node.unbind($.support.transition.end);
+ dfd.resolveWith(node);
+ }
+ }
+ ).toggleClass('in');
+ } else {
+ node.toggleClass('in');
+ dfd.resolveWith(node);
+ }
+ return dfd;
+ },
+
+ _initButtonBarEventHandlers: function () {
+ var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
+ filesList = this.options.filesContainer,
+ ns = this.options.namespace;
+ fileUploadButtonBar.find('.start')
+ .bind('click.' + ns, function (e) {
+ e.preventDefault();
+ filesList.find('.start button').click();
+ });
+ fileUploadButtonBar.find('.cancel')
+ .bind('click.' + ns, function (e) {
+ e.preventDefault();
+ filesList.find('.cancel button').click();
+ });
+ fileUploadButtonBar.find('.delete')
+ .bind('click.' + ns, function (e) {
+ e.preventDefault();
+ filesList.find('.delete input:checked')
+ .siblings('button').click();
+ fileUploadButtonBar.find('.toggle')
+ .prop('checked', false);
+ });
+ fileUploadButtonBar.find('.toggle')
+ .bind('change.' + ns, function (e) {
+ filesList.find('.delete input').prop(
+ 'checked',
+ $(this).is(':checked')
+ );
+ });
+ },
+
+ _destroyButtonBarEventHandlers: function () {
+ this.element.find('.fileupload-buttonbar button')
+ .unbind('click.' + this.options.namespace);
+ this.element.find('.fileupload-buttonbar .toggle')
+ .unbind('change.' + this.options.namespace);
+ },
+
+ _initEventHandlers: function () {
+ parentWidget.prototype._initEventHandlers.call(this);
+ var eventData = {fileupload: this};
+ this.options.filesContainer
+ .delegate(
+ '.start button',
+ 'click.' + this.options.namespace,
+ eventData,
+ this._startHandler
+ )
+ .delegate(
+ '.cancel button',
+ 'click.' + this.options.namespace,
+ eventData,
+ this._cancelHandler
+ )
+ .delegate(
+ '.delete button',
+ 'click.' + this.options.namespace,
+ eventData,
+ this._deleteHandler
+ );
+ this._initButtonBarEventHandlers();
+ },
+
+ _destroyEventHandlers: function () {
+ var options = this.options;
+ this._destroyButtonBarEventHandlers();
+ options.filesContainer
+ .undelegate('.start button', 'click.' + options.namespace)
+ .undelegate('.cancel button', 'click.' + options.namespace)
+ .undelegate('.delete button', 'click.' + options.namespace);
+ parentWidget.prototype._destroyEventHandlers.call(this);
+ },
+
+ _enableFileInputButton: function () {
+ this.element.find('.fileinput-button input')
+ .prop('disabled', false)
+ .parent().removeClass('disabled');
+ },
+
+ _disableFileInputButton: function () {
+ this.element.find('.fileinput-button input')
+ .prop('disabled', true)
+ .parent().addClass('disabled');
+ },
+
+ _initTemplates: function () {
+ var options = this.options;
+ options.templatesContainer = document.createElement(
+ options.filesContainer.prop('nodeName')
+ );
+ if (tmpl) {
+ if (options.uploadTemplateId) {
+ options.uploadTemplate = tmpl(options.uploadTemplateId);
+ }
+ if (options.downloadTemplateId) {
+ options.downloadTemplate = tmpl(options.downloadTemplateId);
+ }
+ }
+ },
+
+ _initFilesContainer: function () {
+ var options = this.options;
+ if (options.filesContainer === undefined) {
+ options.filesContainer = this.element.find('.files');
+ } else if (!(options.filesContainer instanceof $)) {
+ options.filesContainer = $(options.filesContainer);
+ }
+ },
+
+ _stringToRegExp: function (str) {
+ var parts = str.split('/'),
+ modifiers = parts.pop();
+ parts.shift();
+ return new RegExp(parts.join('/'), modifiers);
+ },
+
+ _initRegExpOptions: function () {
+ var options = this.options;
+ if ($.type(options.acceptFileTypes) === 'string') {
+ options.acceptFileTypes = this._stringToRegExp(
+ options.acceptFileTypes
+ );
+ }
+ if ($.type(options.previewSourceFileTypes) === 'string') {
+ options.previewSourceFileTypes = this._stringToRegExp(
+ options.previewSourceFileTypes
+ );
+ }
+ },
+
+ _initSpecialOptions: function () {
+ parentWidget.prototype._initSpecialOptions.call(this);
+ this._initFilesContainer();
+ this._initTemplates();
+ this._initRegExpOptions();
+ },
+
+ _create: function () {
+ parentWidget.prototype._create.call(this);
+ this._refreshOptionsList.push(
+ 'filesContainer',
+ 'uploadTemplateId',
+ 'downloadTemplateId'
+ );
+ if (!$.blueimpFP) {
+ this._processingQueue = $.Deferred().resolveWith(this).promise();
+ this.process = function () {
+ return this._processingQueue;
+ };
+ }
+ },
+
+ enable: function () {
+ parentWidget.prototype.enable.call(this);
+ this.element.find('input, button').prop('disabled', false);
+ this._enableFileInputButton();
+ },
+
+ disable: function () {
+ this.element.find('input, button').prop('disabled', true);
+ this._disableFileInputButton();
+ parentWidget.prototype.disable.call(this);
+ }
+
+ });
+
+}));
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload.js b/mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload.js
new file mode 100644
index 000000000..4bbd28729
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/jquery.fileupload.js
@@ -0,0 +1,972 @@
+/*
+ * jQuery File Upload Plugin 5.13
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint nomen: true, unparam: true, regexp: true */
+/*global define, window, document, Blob, FormData, location */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define([
+ 'jquery',
+ 'jquery.ui.widget'
+ ], factory);
+ } else {
+ // Browser globals:
+ factory(window.jQuery);
+ }
+}(function ($) {
+ 'use strict';
+
+ // The FileReader API is not actually used, but works as feature detection,
+ // as e.g. Safari supports XHR file uploads via the FormData API,
+ // but not non-multipart XHR file uploads:
+ $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader);
+ $.support.xhrFormDataFileUpload = !!window.FormData;
+
+ // The fileupload widget listens for change events on file input fields defined
+ // via fileInput setting and paste or drop events of the given dropZone.
+ // In addition to the default jQuery Widget methods, the fileupload widget
+ // exposes the "add" and "send" methods, to add or directly send files using
+ // the fileupload API.
+ // By default, files added via file input selection, paste, drag & drop or
+ // "add" method are uploaded immediately, but it is possible to override
+ // the "add" callback option to queue file uploads.
+ $.widget('blueimp.fileupload', {
+
+ options: {
+ // The namespace used for event handler binding on the dropZone and
+ // fileInput collections.
+ // If not set, the name of the widget ("fileupload") is used.
+ namespace: undefined,
+ // The drop target collection, by the default the complete document.
+ // Set to null or an empty collection to disable drag & drop support:
+ dropZone: $(document),
+ // The file input field collection, that is listened for change events.
+ // If undefined, it is set to the file input fields inside
+ // of the widget element on plugin initialization.
+ // Set to null or an empty collection to disable the change listener.
+ fileInput: undefined,
+ // By default, the file input field is replaced with a clone after
+ // each input field change event. This is required for iframe transport
+ // queues and allows change events to be fired for the same file
+ // selection, but can be disabled by setting the following option to false:
+ replaceFileInput: true,
+ // The parameter name for the file form data (the request argument name).
+ // If undefined or empty, the name property of the file input field is
+ // used, or "files[]" if the file input name property is also empty,
+ // can be a string or an array of strings:
+ paramName: undefined,
+ // By default, each file of a selection is uploaded using an individual
+ // request for XHR type uploads. Set to false to upload file
+ // selections in one request each:
+ singleFileUploads: true,
+ // To limit the number of files uploaded with one XHR request,
+ // set the following option to an integer greater than 0:
+ limitMultiFileUploads: undefined,
+ // Set the following option to true to issue all file upload requests
+ // in a sequential order:
+ sequentialUploads: false,
+ // To limit the number of concurrent uploads,
+ // set the following option to an integer greater than 0:
+ limitConcurrentUploads: undefined,
+ // Set the following option to true to force iframe transport uploads:
+ forceIframeTransport: false,
+ // Set the following option to the location of a redirect url on the
+ // origin server, for cross-domain iframe transport uploads:
+ redirect: undefined,
+ // The parameter name for the redirect url, sent as part of the form
+ // data and set to 'redirect' if this option is empty:
+ redirectParamName: undefined,
+ // Set the following option to the location of a postMessage window,
+ // to enable postMessage transport uploads:
+ postMessage: undefined,
+ // By default, XHR file uploads are sent as multipart/form-data.
+ // The iframe transport is always using multipart/form-data.
+ // Set to false to enable non-multipart XHR uploads:
+ multipart: true,
+ // To upload large files in smaller chunks, set the following option
+ // to a preferred maximum chunk size. If set to 0, null or undefined,
+ // or the browser does not support the required Blob API, files will
+ // be uploaded as a whole.
+ maxChunkSize: undefined,
+ // When a non-multipart upload or a chunked multipart upload has been
+ // aborted, this option can be used to resume the upload by setting
+ // it to the size of the already uploaded bytes. This option is most
+ // useful when modifying the options object inside of the "add" or
+ // "send" callbacks, as the options are cloned for each file upload.
+ uploadedBytes: undefined,
+ // By default, failed (abort or error) file uploads are removed from the
+ // global progress calculation. Set the following option to false to
+ // prevent recalculating the global progress data:
+ recalculateProgress: true,
+ // Interval in milliseconds to calculate and trigger progress events:
+ progressInterval: 100,
+ // Interval in milliseconds to calculate progress bitrate:
+ bitrateInterval: 500,
+
+ // Additional form data to be sent along with the file uploads can be set
+ // using this option, which accepts an array of objects with name and
+ // value properties, a function returning such an array, a FormData
+ // object (for XHR file uploads), or a simple object.
+ // The form of the first fileInput is given as parameter to the function:
+ formData: function (form) {
+ return form.serializeArray();
+ },
+
+ // The add callback is invoked as soon as files are added to the fileupload
+ // widget (via file input selection, drag & drop, paste or add API call).
+ // If the singleFileUploads option is enabled, this callback will be
+ // called once for each file in the selection for XHR file uplaods, else
+ // once for each file selection.
+ // The upload starts when the submit method is invoked on the data parameter.
+ // The data object contains a files property holding the added files
+ // and allows to override plugin options as well as define ajax settings.
+ // Listeners for this callback can also be bound the following way:
+ // .bind('fileuploadadd', func);
+ // data.submit() returns a Promise object and allows to attach additional
+ // handlers using jQuery's Deferred callbacks:
+ // data.submit().done(func).fail(func).always(func);
+ add: function (e, data) {
+ data.submit();
+ },
+
+ // Other callbacks:
+ // Callback for the submit event of each file upload:
+ // submit: function (e, data) {}, // .bind('fileuploadsubmit', func);
+ // Callback for the start of each file upload request:
+ // send: function (e, data) {}, // .bind('fileuploadsend', func);
+ // Callback for successful uploads:
+ // done: function (e, data) {}, // .bind('fileuploaddone', func);
+ // Callback for failed (abort or error) uploads:
+ // fail: function (e, data) {}, // .bind('fileuploadfail', func);
+ // Callback for completed (success, abort or error) requests:
+ // always: function (e, data) {}, // .bind('fileuploadalways', func);
+ // Callback for upload progress events:
+ // progress: function (e, data) {}, // .bind('fileuploadprogress', func);
+ // Callback for global upload progress events:
+ // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);
+ // Callback for uploads start, equivalent to the global ajaxStart event:
+ // start: function (e) {}, // .bind('fileuploadstart', func);
+ // Callback for uploads stop, equivalent to the global ajaxStop event:
+ // stop: function (e) {}, // .bind('fileuploadstop', func);
+ // Callback for change events of the fileInput collection:
+ // change: function (e, data) {}, // .bind('fileuploadchange', func);
+ // Callback for paste events to the dropZone collection:
+ // paste: function (e, data) {}, // .bind('fileuploadpaste', func);
+ // Callback for drop events of the dropZone collection:
+ // drop: function (e, data) {}, // .bind('fileuploaddrop', func);
+ // Callback for dragover events of the dropZone collection:
+ // dragover: function (e) {}, // .bind('fileuploaddragover', func);
+
+ // The plugin options are used as settings object for the ajax calls.
+ // The following are jQuery ajax settings required for the file uploads:
+ processData: false,
+ contentType: false,
+ cache: false
+ },
+
+ // A list of options that require a refresh after assigning a new value:
+ _refreshOptionsList: [
+ 'namespace',
+ 'dropZone',
+ 'fileInput',
+ 'multipart',
+ 'forceIframeTransport'
+ ],
+
+ _BitrateTimer: function () {
+ this.timestamp = +(new Date());
+ this.loaded = 0;
+ this.bitrate = 0;
+ this.getBitrate = function (now, loaded, interval) {
+ var timeDiff = now - this.timestamp;
+ if (!this.bitrate || !interval || timeDiff > interval) {
+ this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
+ this.loaded = loaded;
+ this.timestamp = now;
+ }
+ return this.bitrate;
+ };
+ },
+
+ _isXHRUpload: function (options) {
+ return !options.forceIframeTransport &&
+ ((!options.multipart && $.support.xhrFileUpload) ||
+ $.support.xhrFormDataFileUpload);
+ },
+
+ _getFormData: function (options) {
+ var formData;
+ if (typeof options.formData === 'function') {
+ return options.formData(options.form);
+ }
+ if ($.isArray(options.formData)) {
+ return options.formData;
+ }
+ if (options.formData) {
+ formData = [];
+ $.each(options.formData, function (name, value) {
+ formData.push({name: name, value: value});
+ });
+ return formData;
+ }
+ return [];
+ },
+
+ _getTotal: function (files) {
+ var total = 0;
+ $.each(files, function (index, file) {
+ total += file.size || 1;
+ });
+ return total;
+ },
+
+ _onProgress: function (e, data) {
+ if (e.lengthComputable) {
+ var now = +(new Date()),
+ total,
+ loaded;
+ if (data._time && data.progressInterval &&
+ (now - data._time < data.progressInterval) &&
+ e.loaded !== e.total) {
+ return;
+ }
+ data._time = now;
+ total = data.total || this._getTotal(data.files);
+ loaded = parseInt(
+ e.loaded / e.total * (data.chunkSize || total),
+ 10
+ ) + (data.uploadedBytes || 0);
+ this._loaded += loaded - (data.loaded || data.uploadedBytes || 0);
+ data.lengthComputable = true;
+ data.loaded = loaded;
+ data.total = total;
+ data.bitrate = data._bitrateTimer.getBitrate(
+ now,
+ loaded,
+ data.bitrateInterval
+ );
+ // Trigger a custom progress event with a total data property set
+ // to the file size(s) of the current upload and a loaded data
+ // property calculated accordingly:
+ this._trigger('progress', e, data);
+ // Trigger a global progress event for all current file uploads,
+ // including ajax calls queued for sequential file uploads:
+ this._trigger('progressall', e, {
+ lengthComputable: true,
+ loaded: this._loaded,
+ total: this._total,
+ bitrate: this._bitrateTimer.getBitrate(
+ now,
+ this._loaded,
+ data.bitrateInterval
+ )
+ });
+ }
+ },
+
+ _initProgressListener: function (options) {
+ var that = this,
+ xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
+ // Accesss to the native XHR object is required to add event listeners
+ // for the upload progress event:
+ if (xhr.upload) {
+ $(xhr.upload).bind('progress', function (e) {
+ var oe = e.originalEvent;
+ // Make sure the progress event properties get copied over:
+ e.lengthComputable = oe.lengthComputable;
+ e.loaded = oe.loaded;
+ e.total = oe.total;
+ that._onProgress(e, options);
+ });
+ options.xhr = function () {
+ return xhr;
+ };
+ }
+ },
+
+ _initXHRData: function (options) {
+ var formData,
+ file = options.files[0],
+ // Ignore non-multipart setting if not supported:
+ multipart = options.multipart || !$.support.xhrFileUpload,
+ paramName = options.paramName[0];
+ if (!multipart || options.blob) {
+ // For non-multipart uploads and chunked uploads,
+ // file meta data is not part of the request body,
+ // so we transmit this data as part of the HTTP headers.
+ // For cross domain requests, these headers must be allowed
+ // via Access-Control-Allow-Headers or removed using
+ // the beforeSend callback:
+ options.headers = $.extend(options.headers, {
+ 'X-File-Name': file.name,
+ 'X-File-Type': file.type,
+ 'X-File-Size': file.size
+ });
+ if (!options.blob) {
+ // Non-chunked non-multipart upload:
+ options.contentType = file.type;
+ options.data = file;
+ } else if (!multipart) {
+ // Chunked non-multipart upload:
+ options.contentType = 'application/octet-stream';
+ options.data = options.blob;
+ }
+ }
+ if (multipart && $.support.xhrFormDataFileUpload) {
+ if (options.postMessage) {
+ // window.postMessage does not allow sending FormData
+ // objects, so we just add the File/Blob objects to
+ // the formData array and let the postMessage window
+ // create the FormData object out of this array:
+ formData = this._getFormData(options);
+ if (options.blob) {
+ formData.push({
+ name: paramName,
+ value: options.blob
+ });
+ } else {
+ $.each(options.files, function (index, file) {
+ formData.push({
+ name: options.paramName[index] || paramName,
+ value: file
+ });
+ });
+ }
+ } else {
+ if (options.formData instanceof FormData) {
+ formData = options.formData;
+ } else {
+ formData = new FormData();
+ $.each(this._getFormData(options), function (index, field) {
+ formData.append(field.name, field.value);
+ });
+ }
+ if (options.blob) {
+ formData.append(paramName, options.blob, file.name);
+ } else {
+ $.each(options.files, function (index, file) {
+ // File objects are also Blob instances.
+ // This check allows the tests to run with
+ // dummy objects:
+ if (file instanceof Blob) {
+ formData.append(
+ options.paramName[index] || paramName,
+ file,
+ file.name
+ );
+ }
+ });
+ }
+ }
+ options.data = formData;
+ }
+ // Blob reference is not needed anymore, free memory:
+ options.blob = null;
+ },
+
+ _initIframeSettings: function (options) {
+ // Setting the dataType to iframe enables the iframe transport:
+ options.dataType = 'iframe ' + (options.dataType || '');
+ // The iframe transport accepts a serialized array as form data:
+ options.formData = this._getFormData(options);
+ // Add redirect url to form data on cross-domain uploads:
+ if (options.redirect && $('<a></a>').prop('href', options.url)
+ .prop('host') !== location.host) {
+ options.formData.push({
+ name: options.redirectParamName || 'redirect',
+ value: options.redirect
+ });
+ }
+ },
+
+ _initDataSettings: function (options) {
+ if (this._isXHRUpload(options)) {
+ if (!this._chunkedUpload(options, true)) {
+ if (!options.data) {
+ this._initXHRData(options);
+ }
+ this._initProgressListener(options);
+ }
+ if (options.postMessage) {
+ // Setting the dataType to postmessage enables the
+ // postMessage transport:
+ options.dataType = 'postmessage ' + (options.dataType || '');
+ }
+ } else {
+ this._initIframeSettings(options, 'iframe');
+ }
+ },
+
+ _getParamName: function (options) {
+ var fileInput = $(options.fileInput),
+ paramName = options.paramName;
+ if (!paramName) {
+ paramName = [];
+ fileInput.each(function () {
+ var input = $(this),
+ name = input.prop('name') || 'files[]',
+ i = (input.prop('files') || [1]).length;
+ while (i) {
+ paramName.push(name);
+ i -= 1;
+ }
+ });
+ if (!paramName.length) {
+ paramName = [fileInput.prop('name') || 'files[]'];
+ }
+ } else if (!$.isArray(paramName)) {
+ paramName = [paramName];
+ }
+ return paramName;
+ },
+
+ _initFormSettings: function (options) {
+ // Retrieve missing options from the input field and the
+ // associated form, if available:
+ if (!options.form || !options.form.length) {
+ options.form = $(options.fileInput.prop('form'));
+ }
+ options.paramName = this._getParamName(options);
+ if (!options.url) {
+ options.url = options.form.prop('action') || location.href;
+ }
+ // The HTTP request method must be "POST" or "PUT":
+ options.type = (options.type || options.form.prop('method') || '')
+ .toUpperCase();
+ if (options.type !== 'POST' && options.type !== 'PUT') {
+ options.type = 'POST';
+ }
+ },
+
+ _getAJAXSettings: function (data) {
+ var options = $.extend({}, this.options, data);
+ this._initFormSettings(options);
+ this._initDataSettings(options);
+ return options;
+ },
+
+ // Maps jqXHR callbacks to the equivalent
+ // methods of the given Promise object:
+ _enhancePromise: function (promise) {
+ promise.success = promise.done;
+ promise.error = promise.fail;
+ promise.complete = promise.always;
+ return promise;
+ },
+
+ // Creates and returns a Promise object enhanced with
+ // the jqXHR methods abort, success, error and complete:
+ _getXHRPromise: function (resolveOrReject, context, args) {
+ var dfd = $.Deferred(),
+ promise = dfd.promise();
+ context = context || this.options.context || promise;
+ if (resolveOrReject === true) {
+ dfd.resolveWith(context, args);
+ } else if (resolveOrReject === false) {
+ dfd.rejectWith(context, args);
+ }
+ promise.abort = dfd.promise;
+ return this._enhancePromise(promise);
+ },
+
+ // Uploads a file in multiple, sequential requests
+ // by splitting the file up in multiple blob chunks.
+ // If the second parameter is true, only tests if the file
+ // should be uploaded in chunks, but does not invoke any
+ // upload requests:
+ _chunkedUpload: function (options, testOnly) {
+ var that = this,
+ file = options.files[0],
+ fs = file.size,
+ ub = options.uploadedBytes = options.uploadedBytes || 0,
+ mcs = options.maxChunkSize || fs,
+ // Use the Blob methods with the slice implementation
+ // according to the W3C Blob API specification:
+ slice = file.webkitSlice || file.mozSlice || file.slice,
+ upload,
+ n,
+ jqXHR,
+ pipe;
+ if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
+ options.data) {
+ return false;
+ }
+ if (testOnly) {
+ return true;
+ }
+ if (ub >= fs) {
+ file.error = 'uploadedBytes';
+ return this._getXHRPromise(
+ false,
+ options.context,
+ [null, 'error', file.error]
+ );
+ }
+ // n is the number of blobs to upload,
+ // calculated via filesize, uploaded bytes and max chunk size:
+ n = Math.ceil((fs - ub) / mcs);
+ // The chunk upload method accepting the chunk number as parameter:
+ upload = function (i) {
+ if (!i) {
+ return that._getXHRPromise(true, options.context);
+ }
+ // Upload the blobs in sequential order:
+ return upload(i -= 1).pipe(function () {
+ // Clone the options object for each chunk upload:
+ var o = $.extend({}, options);
+ o.blob = slice.call(
+ file,
+ ub + i * mcs,
+ ub + (i + 1) * mcs
+ );
+ // Expose the chunk index:
+ o.chunkIndex = i;
+ // Expose the number of chunks:
+ o.chunksNumber = n;
+ // Store the current chunk size, as the blob itself
+ // will be dereferenced after data processing:
+ o.chunkSize = o.blob.size;
+ // Process the upload data (the blob and potential form data):
+ that._initXHRData(o);
+ // Add progress listeners for this chunk upload:
+ that._initProgressListener(o);
+ jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context))
+ .done(function () {
+ // Create a progress event if upload is done and
+ // no progress event has been invoked for this chunk:
+ if (!o.loaded) {
+ that._onProgress($.Event('progress', {
+ lengthComputable: true,
+ loaded: o.chunkSize,
+ total: o.chunkSize
+ }), o);
+ }
+ options.uploadedBytes = o.uploadedBytes +=
+ o.chunkSize;
+ });
+ return jqXHR;
+ });
+ };
+ // Return the piped Promise object, enhanced with an abort method,
+ // which is delegated to the jqXHR object of the current upload,
+ // and jqXHR callbacks mapped to the equivalent Promise methods:
+ pipe = upload(n);
+ pipe.abort = function () {
+ return jqXHR.abort();
+ };
+ return this._enhancePromise(pipe);
+ },
+
+ _beforeSend: function (e, data) {
+ if (this._active === 0) {
+ // the start callback is triggered when an upload starts
+ // and no other uploads are currently running,
+ // equivalent to the global ajaxStart event:
+ this._trigger('start');
+ // Set timer for global bitrate progress calculation:
+ this._bitrateTimer = new this._BitrateTimer();
+ }
+ this._active += 1;
+ // Initialize the global progress values:
+ this._loaded += data.uploadedBytes || 0;
+ this._total += this._getTotal(data.files);
+ },
+
+ _onDone: function (result, textStatus, jqXHR, options) {
+ if (!this._isXHRUpload(options)) {
+ // Create a progress event for each iframe load:
+ this._onProgress($.Event('progress', {
+ lengthComputable: true,
+ loaded: 1,
+ total: 1
+ }), options);
+ }
+ options.result = result;
+ options.textStatus = textStatus;
+ options.jqXHR = jqXHR;
+ this._trigger('done', null, options);
+ },
+
+ _onFail: function (jqXHR, textStatus, errorThrown, options) {
+ options.jqXHR = jqXHR;
+ options.textStatus = textStatus;
+ options.errorThrown = errorThrown;
+ this._trigger('fail', null, options);
+ if (options.recalculateProgress) {
+ // Remove the failed (error or abort) file upload from
+ // the global progress calculation:
+ this._loaded -= options.loaded || options.uploadedBytes || 0;
+ this._total -= options.total || this._getTotal(options.files);
+ }
+ },
+
+ _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
+ this._active -= 1;
+ options.textStatus = textStatus;
+ if (jqXHRorError && jqXHRorError.always) {
+ options.jqXHR = jqXHRorError;
+ options.result = jqXHRorResult;
+ } else {
+ options.jqXHR = jqXHRorResult;
+ options.errorThrown = jqXHRorError;
+ }
+ this._trigger('always', null, options);
+ if (this._active === 0) {
+ // The stop callback is triggered when all uploads have
+ // been completed, equivalent to the global ajaxStop event:
+ this._trigger('stop');
+ // Reset the global progress values:
+ this._loaded = this._total = 0;
+ this._bitrateTimer = null;
+ }
+ },
+
+ _onSend: function (e, data) {
+ var that = this,
+ jqXHR,
+ slot,
+ pipe,
+ options = that._getAJAXSettings(data),
+ send = function (resolve, args) {
+ that._sending += 1;
+ // Set timer for bitrate progress calculation:
+ options._bitrateTimer = new that._BitrateTimer();
+ jqXHR = jqXHR || (
+ (resolve !== false &&
+ that._trigger('send', e, options) !== false &&
+ (that._chunkedUpload(options) || $.ajax(options))) ||
+ that._getXHRPromise(false, options.context, args)
+ ).done(function (result, textStatus, jqXHR) {
+ that._onDone(result, textStatus, jqXHR, options);
+ }).fail(function (jqXHR, textStatus, errorThrown) {
+ that._onFail(jqXHR, textStatus, errorThrown, options);
+ }).always(function (jqXHRorResult, textStatus, jqXHRorError) {
+ that._sending -= 1;
+ that._onAlways(
+ jqXHRorResult,
+ textStatus,
+ jqXHRorError,
+ options
+ );
+ if (options.limitConcurrentUploads &&
+ options.limitConcurrentUploads > that._sending) {
+ // Start the next queued upload,
+ // that has not been aborted:
+ var nextSlot = that._slots.shift();
+ while (nextSlot) {
+ if (!nextSlot.isRejected()) {
+ nextSlot.resolve();
+ break;
+ }
+ nextSlot = that._slots.shift();
+ }
+ }
+ });
+ return jqXHR;
+ };
+ this._beforeSend(e, options);
+ if (this.options.sequentialUploads ||
+ (this.options.limitConcurrentUploads &&
+ this.options.limitConcurrentUploads <= this._sending)) {
+ if (this.options.limitConcurrentUploads > 1) {
+ slot = $.Deferred();
+ this._slots.push(slot);
+ pipe = slot.pipe(send);
+ } else {
+ pipe = (this._sequence = this._sequence.pipe(send, send));
+ }
+ // Return the piped Promise object, enhanced with an abort method,
+ // which is delegated to the jqXHR object of the current upload,
+ // and jqXHR callbacks mapped to the equivalent Promise methods:
+ pipe.abort = function () {
+ var args = [undefined, 'abort', 'abort'];
+ if (!jqXHR) {
+ if (slot) {
+ slot.rejectWith(args);
+ }
+ return send(false, args);
+ }
+ return jqXHR.abort();
+ };
+ return this._enhancePromise(pipe);
+ }
+ return send();
+ },
+
+ _onAdd: function (e, data) {
+ var that = this,
+ result = true,
+ options = $.extend({}, this.options, data),
+ limit = options.limitMultiFileUploads,
+ paramName = this._getParamName(options),
+ paramNameSet,
+ paramNameSlice,
+ fileSet,
+ i;
+ if (!(options.singleFileUploads || limit) ||
+ !this._isXHRUpload(options)) {
+ fileSet = [data.files];
+ paramNameSet = [paramName];
+ } else if (!options.singleFileUploads && limit) {
+ fileSet = [];
+ paramNameSet = [];
+ for (i = 0; i < data.files.length; i += limit) {
+ fileSet.push(data.files.slice(i, i + limit));
+ paramNameSlice = paramName.slice(i, i + limit);
+ if (!paramNameSlice.length) {
+ paramNameSlice = paramName;
+ }
+ paramNameSet.push(paramNameSlice);
+ }
+ } else {
+ paramNameSet = paramName;
+ }
+ data.originalFiles = data.files;
+ $.each(fileSet || data.files, function (index, element) {
+ var newData = $.extend({}, data);
+ newData.files = fileSet ? element : [element];
+ newData.paramName = paramNameSet[index];
+ newData.submit = function () {
+ newData.jqXHR = this.jqXHR =
+ (that._trigger('submit', e, this) !== false) &&
+ that._onSend(e, this);
+ return this.jqXHR;
+ };
+ return (result = that._trigger('add', e, newData));
+ });
+ return result;
+ },
+
+ // File Normalization for Gecko 1.9.1 (Firefox 3.5) support:
+ _normalizeFile: function (index, file) {
+ if (file.name === undefined && file.size === undefined) {
+ file.name = file.fileName;
+ file.size = file.fileSize;
+ }
+ },
+
+ _replaceFileInput: function (input) {
+ var inputClone = input.clone(true);
+ $('<form></form>').append(inputClone)[0].reset();
+ // Detaching allows to insert the fileInput on another form
+ // without loosing the file input value:
+ input.after(inputClone).detach();
+ // Avoid memory leaks with the detached file input:
+ $.cleanData(input.unbind('remove'));
+ // Replace the original file input element in the fileInput
+ // collection with the clone, which has been copied including
+ // event handlers:
+ this.options.fileInput = this.options.fileInput.map(function (i, el) {
+ if (el === input[0]) {
+ return inputClone[0];
+ }
+ return el;
+ });
+ // If the widget has been initialized on the file input itself,
+ // override this.element with the file input clone:
+ if (input[0] === this.element[0]) {
+ this.element = inputClone;
+ }
+ },
+
+ _getFileInputFiles: function (fileInput) {
+ fileInput = $(fileInput);
+ var files = $.each($.makeArray(fileInput.prop('files')), this._normalizeFile),
+ value;
+ if (!files.length) {
+ value = fileInput.prop('value');
+ if (!value) {
+ return [];
+ }
+ // If the files property is not available, the browser does not
+ // support the File API and we add a pseudo File object with
+ // the input value as name with path information removed:
+ files = [{name: value.replace(/^.*\\/, '')}];
+ }
+ return files;
+ },
+
+ _onChange: function (e) {
+ var that = e.data.fileupload,
+ data = {
+ fileInput: $(e.target),
+ form: $(e.target.form)
+ };
+ data.files = that._getFileInputFiles(data.fileInput);
+ if (that.options.replaceFileInput) {
+ that._replaceFileInput(data.fileInput);
+ }
+ if (that._trigger('change', e, data) === false ||
+ that._onAdd(e, data) === false) {
+ return false;
+ }
+ },
+
+ _onPaste: function (e) {
+ var that = e.data.fileupload,
+ cbd = e.originalEvent.clipboardData,
+ items = (cbd && cbd.items) || [],
+ data = {files: []};
+ $.each(items, function (index, item) {
+ var file = item.getAsFile && item.getAsFile();
+ if (file) {
+ data.files.push(file);
+ }
+ });
+ if (that._trigger('paste', e, data) === false ||
+ that._onAdd(e, data) === false) {
+ return false;
+ }
+ },
+
+ _onDrop: function (e) {
+ var that = e.data.fileupload,
+ dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer,
+ data = {
+ files: $.each(
+ $.makeArray(dataTransfer && dataTransfer.files),
+ that._normalizeFile
+ )
+ };
+ if (that._trigger('drop', e, data) === false ||
+ that._onAdd(e, data) === false) {
+ return false;
+ }
+ e.preventDefault();
+ },
+
+ _onDragOver: function (e) {
+ var that = e.data.fileupload,
+ dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer;
+ if (that._trigger('dragover', e) === false) {
+ return false;
+ }
+ if (dataTransfer) {
+ dataTransfer.dropEffect = 'copy';
+ }
+ e.preventDefault();
+ },
+
+ _initEventHandlers: function () {
+ var ns = this.options.namespace;
+ if (this._isXHRUpload(this.options)) {
+ this.options.dropZone
+ .bind('dragover.' + ns, {fileupload: this}, this._onDragOver)
+ .bind('drop.' + ns, {fileupload: this}, this._onDrop)
+ .bind('paste.' + ns, {fileupload: this}, this._onPaste);
+ }
+ this.options.fileInput
+ .bind('change.' + ns, {fileupload: this}, this._onChange);
+ },
+
+ _destroyEventHandlers: function () {
+ var ns = this.options.namespace;
+ this.options.dropZone
+ .unbind('dragover.' + ns, this._onDragOver)
+ .unbind('drop.' + ns, this._onDrop)
+ .unbind('paste.' + ns, this._onPaste);
+ this.options.fileInput
+ .unbind('change.' + ns, this._onChange);
+ },
+
+ _setOption: function (key, value) {
+ var refresh = $.inArray(key, this._refreshOptionsList) !== -1;
+ if (refresh) {
+ this._destroyEventHandlers();
+ }
+ $.Widget.prototype._setOption.call(this, key, value);
+ if (refresh) {
+ this._initSpecialOptions();
+ this._initEventHandlers();
+ }
+ },
+
+ _initSpecialOptions: function () {
+ var options = this.options;
+ if (options.fileInput === undefined) {
+ options.fileInput = this.element.is('input:file') ?
+ this.element : this.element.find('input:file');
+ } else if (!(options.fileInput instanceof $)) {
+ options.fileInput = $(options.fileInput);
+ }
+ if (!(options.dropZone instanceof $)) {
+ options.dropZone = $(options.dropZone);
+ }
+ },
+
+ _create: function () {
+ var options = this.options;
+ // Initialize options set via HTML5 data-attributes:
+ $.extend(options, $(this.element[0].cloneNode(false)).data());
+ options.namespace = options.namespace || this.widgetName;
+ this._initSpecialOptions();
+ this._slots = [];
+ this._sequence = this._getXHRPromise(true);
+ this._sending = this._active = this._loaded = this._total = 0;
+ this._initEventHandlers();
+ },
+
+ destroy: function () {
+ this._destroyEventHandlers();
+ $.Widget.prototype.destroy.call(this);
+ },
+
+ enable: function () {
+ $.Widget.prototype.enable.call(this);
+ this._initEventHandlers();
+ },
+
+ disable: function () {
+ this._destroyEventHandlers();
+ $.Widget.prototype.disable.call(this);
+ },
+
+ // This method is exposed to the widget API and allows adding files
+ // using the fileupload API. The data parameter accepts an object which
+ // must have a files property and can contain additional options:
+ // .fileupload('add', {files: filesList});
+ add: function (data) {
+ if (!data || this.options.disabled) {
+ return;
+ }
+ if (data.fileInput && !data.files) {
+ data.files = this._getFileInputFiles(data.fileInput);
+ } else {
+ data.files = $.each($.makeArray(data.files), this._normalizeFile);
+ }
+ this._onAdd(null, data);
+ },
+
+ // This method is exposed to the widget API and allows sending files
+ // using the fileupload API. The data parameter accepts an object which
+ // must have a files property and can contain additional options:
+ // .fileupload('send', {files: filesList});
+ // The method returns a Promise object for the file upload call.
+ send: function (data) {
+ if (data && !this.options.disabled) {
+ if (data.fileInput && !data.files) {
+ data.files = this._getFileInputFiles(data.fileInput);
+ } else {
+ data.files = $.each($.makeArray(data.files), this._normalizeFile);
+ }
+ if (data.files.length) {
+ return this._onSend(null, data);
+ }
+ }
+ return this._getXHRPromise(false, data && data.context);
+ }
+
+ });
+
+}));
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/jquery.iframe-transport.js b/mod/lightpics/vendors/jquery-file-upload/js/jquery.iframe-transport.js
new file mode 100644
index 000000000..04a566230
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/jquery.iframe-transport.js
@@ -0,0 +1,171 @@
+/*
+ * jQuery Iframe Transport Plugin 1.4
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint unparam: true, nomen: true */
+/*global define, window, document */
+
+(function (factory) {
+ 'use strict';
+ if (typeof define === 'function' && define.amd) {
+ // Register as an anonymous AMD module:
+ define(['jquery'], factory);
+ } else {
+ // Browser globals:
+ factory(window.jQuery);
+ }
+}(function ($) {
+ 'use strict';
+
+ // Helper variable to create unique names for the transport iframes:
+ var counter = 0;
+
+ // The iframe transport accepts three additional options:
+ // options.fileInput: a jQuery collection of file input fields
+ // options.paramName: the parameter name for the file form data,
+ // overrides the name property of the file input field(s),
+ // can be a string or an array of strings.
+ // options.formData: an array of objects with name and value properties,
+ // equivalent to the return data of .serializeArray(), e.g.:
+ // [{name: 'a', value: 1}, {name: 'b', value: 2}]
+ $.ajaxTransport('iframe', function (options) {
+ if (options.async && (options.type === 'POST' || options.type === 'GET')) {
+ var form,
+ iframe;
+ return {
+ send: function (_, completeCallback) {
+ form = $('<form style="display:none;"></form>');
+ // javascript:false as initial iframe src
+ // prevents warning popups on HTTPS in IE6.
+ // IE versions below IE8 cannot set the name property of
+ // elements that have already been added to the DOM,
+ // so we set the name along with the iframe HTML markup:
+ iframe = $(
+ '<iframe src="javascript:false;" name="iframe-transport-' +
+ (counter += 1) + '"></iframe>'
+ ).bind('load', function () {
+ var fileInputClones,
+ paramNames = $.isArray(options.paramName) ?
+ options.paramName : [options.paramName];
+ iframe
+ .unbind('load')
+ .bind('load', function () {
+ var response;
+ // Wrap in a try/catch block to catch exceptions thrown
+ // when trying to access cross-domain iframe contents:
+ try {
+ response = iframe.contents();
+ // Google Chrome and Firefox do not throw an
+ // exception when calling iframe.contents() on
+ // cross-domain requests, so we unify the response:
+ if (!response.length || !response[0].firstChild) {
+ throw new Error();
+ }
+ } catch (e) {
+ response = undefined;
+ }
+ // The complete callback returns the
+ // iframe content document as response object:
+ completeCallback(
+ 200,
+ 'success',
+ {'iframe': response}
+ );
+ // Fix for IE endless progress bar activity bug
+ // (happens on form submits to iframe targets):
+ $('<iframe src="javascript:false;"></iframe>')
+ .appendTo(form);
+ form.remove();
+ });
+ form
+ .prop('target', iframe.prop('name'))
+ .prop('action', options.url)
+ .prop('method', options.type);
+ if (options.formData) {
+ $.each(options.formData, function (index, field) {
+ $('<input type="hidden"/>')
+ .prop('name', field.name)
+ .val(field.value)
+ .appendTo(form);
+ });
+ }
+ if (options.fileInput && options.fileInput.length &&
+ options.type === 'POST') {
+ fileInputClones = options.fileInput.clone();
+ // Insert a clone for each file input field:
+ options.fileInput.after(function (index) {
+ return fileInputClones[index];
+ });
+ if (options.paramName) {
+ options.fileInput.each(function (index) {
+ $(this).prop(
+ 'name',
+ paramNames[index] || options.paramName
+ );
+ });
+ }
+ // Appending the file input fields to the hidden form
+ // removes them from their original location:
+ form
+ .append(options.fileInput)
+ .prop('enctype', 'multipart/form-data')
+ // enctype must be set as encoding for IE:
+ .prop('encoding', 'multipart/form-data');
+ }
+ form.submit();
+ // Insert the file input fields at their original location
+ // by replacing the clones with the originals:
+ if (fileInputClones && fileInputClones.length) {
+ options.fileInput.each(function (index, input) {
+ var clone = $(fileInputClones[index]);
+ $(input).prop('name', clone.prop('name'));
+ clone.replaceWith(input);
+ });
+ }
+ });
+ form.append(iframe).appendTo(document.body);
+ },
+ abort: function () {
+ if (iframe) {
+ // javascript:false as iframe src aborts the request
+ // and prevents warning popups on HTTPS in IE6.
+ // concat is used to avoid the "Script URL" JSLint error:
+ iframe
+ .unbind('load')
+ .prop('src', 'javascript'.concat(':false;'));
+ }
+ if (form) {
+ form.remove();
+ }
+ }
+ };
+ }
+ });
+
+ // The iframe transport returns the iframe content document as response.
+ // The following adds converters from iframe to text, json, html, and script:
+ $.ajaxSetup({
+ converters: {
+ 'iframe text': function (iframe) {
+ return $(iframe[0].body).text();
+ },
+ 'iframe json': function (iframe) {
+ return $.parseJSON($(iframe[0].body).text());
+ },
+ 'iframe html': function (iframe) {
+ return $(iframe[0].body).html();
+ },
+ 'iframe script': function (iframe) {
+ return $.globalEval($(iframe[0].body).text());
+ }
+ }
+ });
+
+}));
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/locale.js b/mod/lightpics/vendors/jquery-file-upload/js/locale.js
new file mode 100644
index 000000000..ea64b0a87
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/locale.js
@@ -0,0 +1,29 @@
+/*
+ * jQuery File Upload Plugin Localization Example 6.5.1
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global window */
+
+window.locale = {
+ "fileupload": {
+ "errors": {
+ "maxFileSize": "File is too big",
+ "minFileSize": "File is too small",
+ "acceptFileTypes": "Filetype not allowed",
+ "maxNumberOfFiles": "Max number of files exceeded",
+ "uploadedBytes": "Uploaded bytes exceed file size",
+ "emptyResult": "Empty file upload result"
+ },
+ "error": "Error",
+ "start": "Start",
+ "cancel": "Cancel",
+ "destroy": "Delete"
+ }
+};
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/main.js b/mod/lightpics/vendors/jquery-file-upload/js/main.js
new file mode 100644
index 000000000..67109588d
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/main.js
@@ -0,0 +1,93 @@
+/*
+ * jQuery File Upload Plugin JS Example 6.7
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint nomen: true, unparam: true, regexp: true */
+/*global $, window, document */
+
+$(function () {
+ 'use strict';
+
+ // Initialize the jQuery File Upload widget:
+ $('#fileupload').fileupload();
+
+ // Enable iframe cross-domain access via redirect option:
+ $('#fileupload').fileupload(
+ 'option',
+ 'redirect',
+ window.location.href.replace(
+ /\/[^\/]*$/,
+ '/cors/result.html?%s'
+ )
+ );
+
+ if (window.location.hostname === 'blueimp.github.com') {
+ // Demo settings:
+ $('#fileupload').fileupload('option', {
+ url: '//jquery-file-upload.appspot.com/',
+ maxFileSize: 5000000,
+ acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
+ process: [
+ {
+ action: 'load',
+ fileTypes: /^image\/(gif|jpeg|png)$/,
+ maxFileSize: 20000000 // 20MB
+ },
+ {
+ action: 'resize',
+ maxWidth: 1440,
+ maxHeight: 900
+ },
+ {
+ action: 'save'
+ }
+ ]
+ });
+ // Upload server status check for browsers with CORS support:
+ if ($.support.cors) {
+ $.ajax({
+ url: '//jquery-file-upload.appspot.com/',
+ type: 'HEAD'
+ }).fail(function () {
+ $('<span class="alert alert-error"/>')
+ .text('Upload server currently unavailable - ' +
+ new Date())
+ .appendTo('#fileupload');
+ });
+ }
+ } else {
+ // Load existing files:
+ $('#fileupload').each(function () {
+ var that = this;
+ $.getJSON(this.action, function (result) {
+ if (result && result.length) {
+ $(that).fileupload('option', 'done')
+ .call(that, null, {result: result});
+ }
+ });
+ });
+ }
+
+ // Initialize the Image Gallery widget:
+ $('#fileupload .files').imagegallery();
+
+ // Initialize the theme switcher:
+ $('#theme-switcher').change(function () {
+ var theme = $('#theme');
+ theme.prop(
+ 'href',
+ theme.prop('href').replace(
+ /[\w\-]+\/jquery-ui.css/,
+ $(this).val() + '/jquery-ui.css'
+ )
+ );
+ });
+
+});
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/vendor/canvas-to-blob.min.js b/mod/lightpics/vendors/jquery-file-upload/js/vendor/canvas-to-blob.min.js
new file mode 100644
index 000000000..9328aae30
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/vendor/canvas-to-blob.min.js
@@ -0,0 +1 @@
+(function(a){"use strict";var b=a.HTMLCanvasElement&&a.HTMLCanvasElement.prototype,c=a.BlobBuilder||a.WebKitBlobBuilder||a.MozBlobBuilder||a.MSBlobBuilder,d=c&&a.atob&&a.ArrayBuffer&&a.Uint8Array&&function(a){var b,d,e,f,g,h;a.split(",")[0].indexOf("base64")>=0?b=atob(a.split(",")[1]):b=decodeURIComponent(a.split(",")[1]),d=new ArrayBuffer(b.length),e=new Uint8Array(d);for(f=0;f<b.length;f+=1)e[f]=b.charCodeAt(f);return g=new c,g.append(d),h=a.split(",")[0].split(":")[1].split(";")[0],g.getBlob(h)};a.HTMLCanvasElement&&!b.toBlob&&(b.mozGetAsFile?b.toBlob=function(a,b){a(this.mozGetAsFile("blob",b))}:b.toDataURL&&d&&(b.toBlob=function(a,b){a(d(this.toDataURL(b)))})),typeof define!="undefined"&&define.amd?define(function(){return d}):a.dataURLtoBlob=d})(this); \ No newline at end of file
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/vendor/jquery.ui.widget.js b/mod/lightpics/vendors/jquery-file-upload/js/vendor/jquery.ui.widget.js
new file mode 100644
index 000000000..b980122a3
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/vendor/jquery.ui.widget.js
@@ -0,0 +1,282 @@
+/*
+ * jQuery UI Widget 1.8.22+amd
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+
+(function (factory) {
+ if (typeof define === "function" && define.amd) {
+ // Register as an anonymous AMD module:
+ define(["jquery"], factory);
+ } else {
+ // Browser globals:
+ factory(jQuery);
+ }
+}(function( $, undefined ) {
+
+// jQuery 1.4+
+if ( $.cleanData ) {
+ var _cleanData = $.cleanData;
+ $.cleanData = function( elems ) {
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ try {
+ $( elem ).triggerHandler( "remove" );
+ // http://bugs.jquery.com/ticket/8235
+ } catch( e ) {}
+ }
+ _cleanData( elems );
+ };
+} else {
+ var _remove = $.fn.remove;
+ $.fn.remove = function( selector, keepData ) {
+ return this.each(function() {
+ if ( !keepData ) {
+ if ( !selector || $.filter( selector, [ this ] ).length ) {
+ $( "*", this ).add( [ this ] ).each(function() {
+ try {
+ $( this ).triggerHandler( "remove" );
+ // http://bugs.jquery.com/ticket/8235
+ } catch( e ) {}
+ });
+ }
+ }
+ return _remove.call( $(this), selector, keepData );
+ });
+ };
+}
+
+$.widget = function( name, base, prototype ) {
+ var namespace = name.split( "." )[ 0 ],
+ fullName;
+ name = name.split( "." )[ 1 ];
+ fullName = namespace + "-" + name;
+
+ if ( !prototype ) {
+ prototype = base;
+ base = $.Widget;
+ }
+
+ // create selector for plugin
+ $.expr[ ":" ][ fullName ] = function( elem ) {
+ return !!$.data( elem, name );
+ };
+
+ $[ namespace ] = $[ namespace ] || {};
+ $[ namespace ][ name ] = function( options, element ) {
+ // allow instantiation without initializing for simple inheritance
+ if ( arguments.length ) {
+ this._createWidget( options, element );
+ }
+ };
+
+ var basePrototype = new base();
+ // we need to make the options hash a property directly on the new instance
+ // otherwise we'll modify the options hash on the prototype that we're
+ // inheriting from
+// $.each( basePrototype, function( key, val ) {
+// if ( $.isPlainObject(val) ) {
+// basePrototype[ key ] = $.extend( {}, val );
+// }
+// });
+ basePrototype.options = $.extend( true, {}, basePrototype.options );
+ $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
+ namespace: namespace,
+ widgetName: name,
+ widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
+ widgetBaseClass: fullName
+ }, prototype );
+
+ $.widget.bridge( name, $[ namespace ][ name ] );
+};
+
+$.widget.bridge = function( name, object ) {
+ $.fn[ name ] = function( options ) {
+ var isMethodCall = typeof options === "string",
+ args = Array.prototype.slice.call( arguments, 1 ),
+ returnValue = this;
+
+ // allow multiple hashes to be passed on init
+ options = !isMethodCall && args.length ?
+ $.extend.apply( null, [ true, options ].concat(args) ) :
+ options;
+
+ // prevent calls to internal methods
+ if ( isMethodCall && options.charAt( 0 ) === "_" ) {
+ return returnValue;
+ }
+
+ if ( isMethodCall ) {
+ this.each(function() {
+ var instance = $.data( this, name ),
+ methodValue = instance && $.isFunction( instance[options] ) ?
+ instance[ options ].apply( instance, args ) :
+ instance;
+ // TODO: add this back in 1.9 and use $.error() (see #5972)
+// if ( !instance ) {
+// throw "cannot call methods on " + name + " prior to initialization; " +
+// "attempted to call method '" + options + "'";
+// }
+// if ( !$.isFunction( instance[options] ) ) {
+// throw "no such method '" + options + "' for " + name + " widget instance";
+// }
+// var methodValue = instance[ options ].apply( instance, args );
+ if ( methodValue !== instance && methodValue !== undefined ) {
+ returnValue = methodValue;
+ return false;
+ }
+ });
+ } else {
+ this.each(function() {
+ var instance = $.data( this, name );
+ if ( instance ) {
+ instance.option( options || {} )._init();
+ } else {
+ $.data( this, name, new object( options, this ) );
+ }
+ });
+ }
+
+ return returnValue;
+ };
+};
+
+$.Widget = function( options, element ) {
+ // allow instantiation without initializing for simple inheritance
+ if ( arguments.length ) {
+ this._createWidget( options, element );
+ }
+};
+
+$.Widget.prototype = {
+ widgetName: "widget",
+ widgetEventPrefix: "",
+ options: {
+ disabled: false
+ },
+ _createWidget: function( options, element ) {
+ // $.widget.bridge stores the plugin instance, but we do it anyway
+ // so that it's stored even before the _create function runs
+ $.data( element, this.widgetName, this );
+ this.element = $( element );
+ this.options = $.extend( true, {},
+ this.options,
+ this._getCreateOptions(),
+ options );
+
+ var self = this;
+ this.element.bind( "remove." + this.widgetName, function() {
+ self.destroy();
+ });
+
+ this._create();
+ this._trigger( "create" );
+ this._init();
+ },
+ _getCreateOptions: function() {
+ return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
+ },
+ _create: function() {},
+ _init: function() {},
+
+ destroy: function() {
+ this.element
+ .unbind( "." + this.widgetName )
+ .removeData( this.widgetName );
+ this.widget()
+ .unbind( "." + this.widgetName )
+ .removeAttr( "aria-disabled" )
+ .removeClass(
+ this.widgetBaseClass + "-disabled " +
+ "ui-state-disabled" );
+ },
+
+ widget: function() {
+ return this.element;
+ },
+
+ option: function( key, value ) {
+ var options = key;
+
+ if ( arguments.length === 0 ) {
+ // don't return a reference to the internal hash
+ return $.extend( {}, this.options );
+ }
+
+ if (typeof key === "string" ) {
+ if ( value === undefined ) {
+ return this.options[ key ];
+ }
+ options = {};
+ options[ key ] = value;
+ }
+
+ this._setOptions( options );
+
+ return this;
+ },
+ _setOptions: function( options ) {
+ var self = this;
+ $.each( options, function( key, value ) {
+ self._setOption( key, value );
+ });
+
+ return this;
+ },
+ _setOption: function( key, value ) {
+ this.options[ key ] = value;
+
+ if ( key === "disabled" ) {
+ this.widget()
+ [ value ? "addClass" : "removeClass"](
+ this.widgetBaseClass + "-disabled" + " " +
+ "ui-state-disabled" )
+ .attr( "aria-disabled", value );
+ }
+
+ return this;
+ },
+
+ enable: function() {
+ return this._setOption( "disabled", false );
+ },
+ disable: function() {
+ return this._setOption( "disabled", true );
+ },
+
+ _trigger: function( type, event, data ) {
+ var prop, orig,
+ callback = this.options[ type ];
+
+ data = data || {};
+ event = $.Event( event );
+ event.type = ( type === this.widgetEventPrefix ?
+ type :
+ this.widgetEventPrefix + type ).toLowerCase();
+ // the original event may come from any element
+ // so we need to reset the target on the new event
+ event.target = this.element[ 0 ];
+
+ // copy original event properties over to the new event
+ orig = event.originalEvent;
+ if ( orig ) {
+ for ( prop in orig ) {
+ if ( !( prop in event ) ) {
+ event[ prop ] = orig[ prop ];
+ }
+ }
+ }
+
+ this.element.trigger( event, data );
+
+ return !( $.isFunction(callback) &&
+ callback.call( this.element[0], event, data ) === false ||
+ event.isDefaultPrevented() );
+ }
+};
+
+}));
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/vendor/load-image.min.js b/mod/lightpics/vendors/jquery-file-upload/js/vendor/load-image.min.js
new file mode 100644
index 000000000..0f7ab3ffe
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/vendor/load-image.min.js
@@ -0,0 +1 @@
+(function(a){"use strict";var b=function(a,c,d){var e=document.createElement("img"),f,g;return e.onerror=c,e.onload=function(){g&&(!d||!d.noRevoke)&&b.revokeObjectURL(g),c(b.scale(e,d))},window.Blob&&a instanceof Blob||window.File&&a instanceof File?f=g=b.createObjectURL(a):f=a,f?(e.src=f,e):b.readFile(a,function(a){e.src=a})},c=window.createObjectURL&&window||window.URL&&URL.revokeObjectURL&&URL||window.webkitURL&&webkitURL;b.scale=function(a,b){b=b||{};var c=document.createElement("canvas"),d=a.width,e=a.height,f=Math.max((b.minWidth||d)/d,(b.minHeight||e)/e);return f>1&&(d=parseInt(d*f,10),e=parseInt(e*f,10)),f=Math.min((b.maxWidth||d)/d,(b.maxHeight||e)/e),f<1&&(d=parseInt(d*f,10),e=parseInt(e*f,10)),a.getContext||b.canvas&&c.getContext?(c.width=d,c.height=e,c.getContext("2d").drawImage(a,0,0,d,e),c):(a.width=d,a.height=e,a)},b.createObjectURL=function(a){return c?c.createObjectURL(a):!1},b.revokeObjectURL=function(a){return c?c.revokeObjectURL(a):!1},b.readFile=function(a,b){if(window.FileReader&&FileReader.prototype.readAsDataURL){var c=new FileReader;return c.onload=function(a){b(a.target.result)},c.readAsDataURL(a),c}return!1},typeof define!="undefined"&&define.amd?define(function(){return b}):a.loadImage=b})(this); \ No newline at end of file
diff --git a/mod/lightpics/vendors/jquery-file-upload/js/vendor/tmpl.min.js b/mod/lightpics/vendors/jquery-file-upload/js/vendor/tmpl.min.js
new file mode 100644
index 000000000..065532e7c
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/js/vendor/tmpl.min.js
@@ -0,0 +1 @@
+(function(a){"use strict";var b=function(a,c){var d=/[^\w\-\.:]/.test(a)?new Function(b.arg+",tmpl","var _e=tmpl.encode"+b.helper+",_s='"+a.replace(b.regexp,b.func)+"';return _s;"):b.cache[a]=b.cache[a]||b(b.load(a));return c?d(c,b):function(a){return d(a,b)}};b.cache={},b.load=function(a){return document.getElementById(a).innerHTML},b.regexp=/([\s'\\])(?![^%]*%\})|(?:\{%(=|#)([\s\S]+?)%\})|(\{%)|(%\})/g,b.func=function(a,b,c,d,e,f){if(b)return{"\n":"\\n","\r":"\\r","\t":"\\t"," ":" "}[a]||"\\"+a;if(c)return c==="="?"'+_e("+d+")+'":"'+("+d+"||'')+'";if(e)return"';";if(f)return"_s+='"},b.encReg=/[<>&"'\x00]/g,b.encMap={"<":"&lt;",">":"&gt;","&":"&amp;",'"':"&quot;","'":"&#39;"},b.encode=function(a){return String(a||"").replace(b.encReg,function(a){return b.encMap[a]||""})},b.arg="o",b.helper=",print=function(s,e){_s+=e&&(s||'')||_e(s);},include=function(s,d){_s+=tmpl(s,d);}",typeof define=="function"&&define.amd?define(function(){return b}):a.tmpl=b})(this); \ No newline at end of file
diff --git a/mod/lightpics/vendors/jquery-file-upload/package.json b/mod/lightpics/vendors/jquery-file-upload/package.json
new file mode 100644
index 000000000..56f9d5f25
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/package.json
@@ -0,0 +1,56 @@
+{
+ "name": "blueimp-file-upload-jquery-ui",
+ "version": "6.9.2",
+ "title": "jQuery File Upload - jQuery UI version",
+ "description": "File Upload widget with multiple file selection, drag&drop support, progress bars and preview images for jQuery. Supports cross-domain, chunked and resumable file uploads and client-side image resizing. Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.",
+ "keywords": [
+ "jquery",
+ "file",
+ "upload",
+ "widget",
+ "multiple",
+ "selection",
+ "drag",
+ "drop",
+ "progress",
+ "preview",
+ "cross-domain",
+ "cross-site",
+ "chunk",
+ "resume",
+ "gae",
+ "go",
+ "python",
+ "php",
+ "ui"
+ ],
+ "homepage": "https://github.com/blueimp/jQuery-File-Upload",
+ "author": {
+ "name": "Sebastian Tschan",
+ "url": "https://blueimp.net"
+ },
+ "maintainers": [
+ {
+ "name": "Sebastian Tschan",
+ "url": "https://blueimp.net"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/blueimp/jQuery-File-Upload.git"
+ },
+ "bugs": "https://github.com/blueimp/jQuery-File-Upload/issues",
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://www.opensource.org/licenses/MIT"
+ }
+ ],
+ "dependencies": {
+ "jquery": ">=1.6",
+ "jquery.ui": ">=1.8",
+ "blueimp-tmpl": ">=2.1.0",
+ "blueimp-load-image": ">=1.1.6",
+ "blueimp-canvas-to-blob": ">=2.0.0"
+ }
+}
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-go/app.yaml b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/app.yaml
new file mode 100644
index 000000000..2d09daa56
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/app.yaml
@@ -0,0 +1,12 @@
+application: jquery-file-upload
+version: 2
+runtime: go
+api_version: go1
+
+handlers:
+- url: /(favicon\.ico|robots\.txt)
+ static_files: static/\1
+ upload: static/(.*)
+ expiration: '1d'
+- url: /.*
+ script: _go_app
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-go/app/main.go b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/app/main.go
new file mode 100644
index 000000000..01dc2f204
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/app/main.go
@@ -0,0 +1,361 @@
+/*
+ * jQuery File Upload Plugin GAE Go Example 2.0
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+package app
+
+import (
+ "appengine"
+ "appengine/blobstore"
+ "appengine/memcache"
+ "appengine/taskqueue"
+ "bytes"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "image"
+ "image/png"
+ "io"
+ "log"
+ "mime/multipart"
+ "net/http"
+ "net/url"
+ "regexp"
+ "resize"
+ "strings"
+ "time"
+)
+
+import _ "image/gif"
+import _ "image/jpeg"
+
+const (
+ WEBSITE = "http://blueimp.github.com/jQuery-File-Upload/"
+ MIN_FILE_SIZE = 1 // bytes
+ MAX_FILE_SIZE = 5000000 // bytes
+ IMAGE_TYPES = "image/(gif|p?jpeg|(x-)?png)"
+ ACCEPT_FILE_TYPES = IMAGE_TYPES
+ EXPIRATION_TIME = 300 // seconds
+ THUMBNAIL_MAX_WIDTH = 80
+ THUMBNAIL_MAX_HEIGHT = THUMBNAIL_MAX_WIDTH
+)
+
+var (
+ imageTypes = regexp.MustCompile(IMAGE_TYPES)
+ acceptFileTypes = regexp.MustCompile(ACCEPT_FILE_TYPES)
+)
+
+type FileInfo struct {
+ Key appengine.BlobKey `json:"-"`
+ Url string `json:"url,omitempty"`
+ ThumbnailUrl string `json:"thumbnail_url,omitempty"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+ Size int64 `json:"size"`
+ Error string `json:"error,omitempty"`
+ DeleteUrl string `json:"delete_url,omitempty"`
+ DeleteType string `json:"delete_type,omitempty"`
+}
+
+func (fi *FileInfo) ValidateType() (valid bool) {
+ if acceptFileTypes.MatchString(fi.Type) {
+ return true
+ }
+ fi.Error = "acceptFileTypes"
+ return false
+}
+
+func (fi *FileInfo) ValidateSize() (valid bool) {
+ if fi.Size < MIN_FILE_SIZE {
+ fi.Error = "minFileSize"
+ } else if fi.Size > MAX_FILE_SIZE {
+ fi.Error = "maxFileSize"
+ } else {
+ return true
+ }
+ return false
+}
+
+func (fi *FileInfo) CreateUrls(r *http.Request, c appengine.Context) {
+ u := &url.URL{
+ Scheme: r.URL.Scheme,
+ Host: appengine.DefaultVersionHostname(c),
+ Path: "/",
+ }
+ uString := u.String()
+ fi.Url = uString + escape(string(fi.Key)) + "/" +
+ escape(string(fi.Name))
+ fi.DeleteUrl = fi.Url
+ fi.DeleteType = "DELETE"
+ if fi.ThumbnailUrl != "" && -1 == strings.Index(
+ r.Header.Get("Accept"),
+ "application/json",
+ ) {
+ fi.ThumbnailUrl = uString + "thumbnails/" +
+ escape(string(fi.Key))
+ }
+}
+
+func (fi *FileInfo) CreateThumbnail(r io.Reader, c appengine.Context) (data []byte, err error) {
+ defer func() {
+ if rec := recover(); rec != nil {
+ log.Println(rec)
+ // 1x1 pixel transparent GIf, bas64 encoded:
+ s := "R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
+ data, _ = base64.StdEncoding.DecodeString(s)
+ fi.ThumbnailUrl = "data:image/gif;base64," + s
+ }
+ memcache.Add(c, &memcache.Item{
+ Key: string(fi.Key),
+ Value: data,
+ Expiration: EXPIRATION_TIME,
+ })
+ }()
+ img, _, err := image.Decode(r)
+ check(err)
+ if bounds := img.Bounds(); bounds.Dx() > THUMBNAIL_MAX_WIDTH ||
+ bounds.Dy() > THUMBNAIL_MAX_HEIGHT {
+ w, h := THUMBNAIL_MAX_WIDTH, THUMBNAIL_MAX_HEIGHT
+ if bounds.Dx() > bounds.Dy() {
+ h = bounds.Dy() * h / bounds.Dx()
+ } else {
+ w = bounds.Dx() * w / bounds.Dy()
+ }
+ img = resize.Resize(img, img.Bounds(), w, h)
+ }
+ var b bytes.Buffer
+ err = png.Encode(&b, img)
+ check(err)
+ data = b.Bytes()
+ fi.ThumbnailUrl = "data:image/png;base64," +
+ base64.StdEncoding.EncodeToString(data)
+ return
+}
+
+func check(err error) {
+ if err != nil {
+ panic(err)
+ }
+}
+
+func escape(s string) string {
+ return strings.Replace(url.QueryEscape(s), "+", "%20", -1)
+}
+
+func delayedDelete(c appengine.Context, fi *FileInfo) {
+ if key := string(fi.Key); key != "" {
+ task := &taskqueue.Task{
+ Path: "/" + escape(key) + "/-",
+ Method: "DELETE",
+ Delay: time.Duration(EXPIRATION_TIME) * time.Second,
+ }
+ taskqueue.Add(c, task, "")
+ }
+}
+
+func handleUpload(r *http.Request, p *multipart.Part) (fi *FileInfo) {
+ fi = &FileInfo{
+ Name: p.FileName(),
+ Type: p.Header.Get("Content-Type"),
+ }
+ if !fi.ValidateType() {
+ return
+ }
+ defer func() {
+ if rec := recover(); rec != nil {
+ log.Println(rec)
+ fi.Error = rec.(error).Error()
+ }
+ }()
+ var b bytes.Buffer
+ lr := &io.LimitedReader{R: p, N: MAX_FILE_SIZE + 1}
+ context := appengine.NewContext(r)
+ w, err := blobstore.Create(context, fi.Type)
+ defer func() {
+ w.Close()
+ fi.Size = MAX_FILE_SIZE + 1 - lr.N
+ fi.Key, err = w.Key()
+ check(err)
+ if !fi.ValidateSize() {
+ err := blobstore.Delete(context, fi.Key)
+ check(err)
+ return
+ }
+ delayedDelete(context, fi)
+ if b.Len() > 0 {
+ fi.CreateThumbnail(&b, context)
+ }
+ fi.CreateUrls(r, context)
+ }()
+ check(err)
+ var wr io.Writer = w
+ if imageTypes.MatchString(fi.Type) {
+ wr = io.MultiWriter(&b, w)
+ }
+ _, err = io.Copy(wr, lr)
+ return
+}
+
+func getFormValue(p *multipart.Part) string {
+ var b bytes.Buffer
+ io.CopyN(&b, p, int64(1<<20)) // Copy max: 1 MiB
+ return b.String()
+}
+
+func handleUploads(r *http.Request) (fileInfos []*FileInfo) {
+ fileInfos = make([]*FileInfo, 0)
+ mr, err := r.MultipartReader()
+ check(err)
+ r.Form, err = url.ParseQuery(r.URL.RawQuery)
+ check(err)
+ part, err := mr.NextPart()
+ for err == nil {
+ if name := part.FormName(); name != "" {
+ if part.FileName() != "" {
+ fileInfos = append(fileInfos, handleUpload(r, part))
+ } else {
+ r.Form[name] = append(r.Form[name], getFormValue(part))
+ }
+ }
+ part, err = mr.NextPart()
+ }
+ return
+}
+
+func get(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path == "/" {
+ http.Redirect(w, r, WEBSITE, http.StatusFound)
+ return
+ }
+ parts := strings.Split(r.URL.Path, "/")
+ if len(parts) == 3 {
+ if key := parts[1]; key != "" {
+ blobKey := appengine.BlobKey(key)
+ bi, err := blobstore.Stat(appengine.NewContext(r), blobKey)
+ if err == nil {
+ w.Header().Add(
+ "Cache-Control",
+ fmt.Sprintf("public,max-age=%d", EXPIRATION_TIME),
+ )
+ if imageTypes.MatchString(bi.ContentType) {
+ w.Header().Add("X-Content-Type-Options", "nosniff")
+ } else {
+ w.Header().Add("Content-Type", "application/octet-stream")
+ w.Header().Add(
+ "Content-Disposition:",
+ fmt.Sprintf("attachment; filename=%s;", parts[2]),
+ )
+ }
+ blobstore.Send(w, appengine.BlobKey(key))
+ return
+ }
+ }
+ }
+ http.Error(w, "404 Not Found", http.StatusNotFound)
+}
+
+func post(w http.ResponseWriter, r *http.Request) {
+ b, err := json.Marshal(handleUploads(r))
+ check(err)
+ if redirect := r.FormValue("redirect"); redirect != "" {
+ http.Redirect(w, r, fmt.Sprintf(
+ redirect,
+ escape(string(b)),
+ ), http.StatusFound)
+ return
+ }
+ jsonType := "application/json"
+ if strings.Index(r.Header.Get("Accept"), jsonType) != -1 {
+ w.Header().Set("Content-Type", jsonType)
+ }
+ fmt.Fprintln(w, string(b))
+}
+
+func delete(w http.ResponseWriter, r *http.Request) {
+ parts := strings.Split(r.URL.Path, "/")
+ if len(parts) != 3 {
+ return
+ }
+ if key := parts[1]; key != "" {
+ c := appengine.NewContext(r)
+ blobstore.Delete(c, appengine.BlobKey(key))
+ memcache.Delete(c, key)
+ }
+}
+
+func serveThumbnail(w http.ResponseWriter, r *http.Request) {
+ parts := strings.Split(r.URL.Path, "/")
+ if len(parts) == 3 {
+ if key := parts[2]; key != "" {
+ var data []byte
+ c := appengine.NewContext(r)
+ item, err := memcache.Get(c, key)
+ if err == nil {
+ data = item.Value
+ } else {
+ blobKey := appengine.BlobKey(key)
+ if _, err = blobstore.Stat(c, blobKey); err == nil {
+ fi := FileInfo{Key: blobKey}
+ data, _ = fi.CreateThumbnail(
+ blobstore.NewReader(c, blobKey),
+ c,
+ )
+ }
+ }
+ if err == nil && len(data) > 3 {
+ w.Header().Add(
+ "Cache-Control",
+ fmt.Sprintf("public,max-age=%d", EXPIRATION_TIME),
+ )
+ contentType := "image/png"
+ if string(data[:3]) == "GIF" {
+ contentType = "image/gif"
+ } else if string(data[1:4]) != "PNG" {
+ contentType = "image/jpeg"
+ }
+ w.Header().Set("Content-Type", contentType)
+ fmt.Fprintln(w, string(data))
+ return
+ }
+ }
+ }
+ http.Error(w, "404 Not Found", http.StatusNotFound)
+}
+
+func handle(w http.ResponseWriter, r *http.Request) {
+ params, err := url.ParseQuery(r.URL.RawQuery)
+ check(err)
+ w.Header().Add("Access-Control-Allow-Origin", "*")
+ w.Header().Add(
+ "Access-Control-Allow-Methods",
+ "OPTIONS, HEAD, GET, POST, PUT, DELETE",
+ )
+ switch r.Method {
+ case "OPTIONS":
+ case "HEAD":
+ case "GET":
+ get(w, r)
+ case "POST":
+ if len(params["_method"]) > 0 && params["_method"][0] == "DELETE" {
+ delete(w, r)
+ } else {
+ post(w, r)
+ }
+ case "DELETE":
+ delete(w, r)
+ default:
+ http.Error(w, "501 Not Implemented", http.StatusNotImplemented)
+ }
+}
+
+func init() {
+ http.HandleFunc("/", handle)
+ http.HandleFunc("/thumbnails/", serveThumbnail)
+}
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-go/resize/resize.go b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/resize/resize.go
new file mode 100644
index 000000000..dcb627870
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/resize/resize.go
@@ -0,0 +1,247 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package resize
+
+import (
+ "image"
+ "image/color"
+)
+
+// Resize returns a scaled copy of the image slice r of m.
+// The returned image has width w and height h.
+func Resize(m image.Image, r image.Rectangle, w, h int) image.Image {
+ if w < 0 || h < 0 {
+ return nil
+ }
+ if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 {
+ return image.NewRGBA64(image.Rect(0, 0, w, h))
+ }
+ switch m := m.(type) {
+ case *image.RGBA:
+ return resizeRGBA(m, r, w, h)
+ case *image.YCbCr:
+ if m, ok := resizeYCbCr(m, r, w, h); ok {
+ return m
+ }
+ }
+ ww, hh := uint64(w), uint64(h)
+ dx, dy := uint64(r.Dx()), uint64(r.Dy())
+ // The scaling algorithm is to nearest-neighbor magnify the dx * dy source
+ // to a (ww*dx) * (hh*dy) intermediate image and then minify the intermediate
+ // image back down to a ww * hh destination with a simple box filter.
+ // The intermediate image is implied, we do not physically allocate a slice
+ // of length ww*dx*hh*dy.
+ // For example, consider a 4*3 source image. Label its pixels from a-l:
+ // abcd
+ // efgh
+ // ijkl
+ // To resize this to a 3*2 destination image, the intermediate is 12*6.
+ // Whitespace has been added to delineate the destination pixels:
+ // aaab bbcc cddd
+ // aaab bbcc cddd
+ // eeef ffgg ghhh
+ //
+ // eeef ffgg ghhh
+ // iiij jjkk klll
+ // iiij jjkk klll
+ // Thus, the 'b' source pixel contributes one third of its value to the
+ // (0, 0) destination pixel and two thirds to (1, 0).
+ // The implementation is a two-step process. First, the source pixels are
+ // iterated over and each source pixel's contribution to 1 or more
+ // destination pixels are summed. Second, the sums are divided by a scaling
+ // factor to yield the destination pixels.
+ // TODO: By interleaving the two steps, instead of doing all of
+ // step 1 first and all of step 2 second, we could allocate a smaller sum
+ // slice of length 4*w*2 instead of 4*w*h, although the resultant code
+ // would become more complicated.
+ n, sum := dx*dy, make([]uint64, 4*w*h)
+ for y := r.Min.Y; y < r.Max.Y; y++ {
+ for x := r.Min.X; x < r.Max.X; x++ {
+ // Get the source pixel.
+ r32, g32, b32, a32 := m.At(x, y).RGBA()
+ r64 := uint64(r32)
+ g64 := uint64(g32)
+ b64 := uint64(b32)
+ a64 := uint64(a32)
+ // Spread the source pixel over 1 or more destination rows.
+ py := uint64(y) * hh
+ for remy := hh; remy > 0; {
+ qy := dy - (py % dy)
+ if qy > remy {
+ qy = remy
+ }
+ // Spread the source pixel over 1 or more destination columns.
+ px := uint64(x) * ww
+ index := 4 * ((py/dy)*ww + (px / dx))
+ for remx := ww; remx > 0; {
+ qx := dx - (px % dx)
+ if qx > remx {
+ qx = remx
+ }
+ sum[index+0] += r64 * qx * qy
+ sum[index+1] += g64 * qx * qy
+ sum[index+2] += b64 * qx * qy
+ sum[index+3] += a64 * qx * qy
+ index += 4
+ px += qx
+ remx -= qx
+ }
+ py += qy
+ remy -= qy
+ }
+ }
+ }
+ return average(sum, w, h, n*0x0101)
+}
+
+// average convert the sums to averages and returns the result.
+func average(sum []uint64, w, h int, n uint64) image.Image {
+ ret := image.NewRGBA(image.Rect(0, 0, w, h))
+ for y := 0; y < h; y++ {
+ for x := 0; x < w; x++ {
+ index := 4 * (y*w + x)
+ ret.SetRGBA(x, y, color.RGBA{
+ uint8(sum[index+0] / n),
+ uint8(sum[index+1] / n),
+ uint8(sum[index+2] / n),
+ uint8(sum[index+3] / n),
+ })
+ }
+ }
+ return ret
+}
+
+// resizeYCbCr returns a scaled copy of the YCbCr image slice r of m.
+// The returned image has width w and height h.
+func resizeYCbCr(m *image.YCbCr, r image.Rectangle, w, h int) (image.Image, bool) {
+ var verticalRes int
+ switch m.SubsampleRatio {
+ case image.YCbCrSubsampleRatio420:
+ verticalRes = 2
+ case image.YCbCrSubsampleRatio422:
+ verticalRes = 1
+ default:
+ return nil, false
+ }
+ ww, hh := uint64(w), uint64(h)
+ dx, dy := uint64(r.Dx()), uint64(r.Dy())
+ // See comment in Resize.
+ n, sum := dx*dy, make([]uint64, 4*w*h)
+ for y := r.Min.Y; y < r.Max.Y; y++ {
+ Y := m.Y[y*m.YStride:]
+ Cb := m.Cb[y/verticalRes*m.CStride:]
+ Cr := m.Cr[y/verticalRes*m.CStride:]
+ for x := r.Min.X; x < r.Max.X; x++ {
+ // Get the source pixel.
+ r8, g8, b8 := color.YCbCrToRGB(Y[x], Cb[x/2], Cr[x/2])
+ r64 := uint64(r8)
+ g64 := uint64(g8)
+ b64 := uint64(b8)
+ // Spread the source pixel over 1 or more destination rows.
+ py := uint64(y) * hh
+ for remy := hh; remy > 0; {
+ qy := dy - (py % dy)
+ if qy > remy {
+ qy = remy
+ }
+ // Spread the source pixel over 1 or more destination columns.
+ px := uint64(x) * ww
+ index := 4 * ((py/dy)*ww + (px / dx))
+ for remx := ww; remx > 0; {
+ qx := dx - (px % dx)
+ if qx > remx {
+ qx = remx
+ }
+ qxy := qx * qy
+ sum[index+0] += r64 * qxy
+ sum[index+1] += g64 * qxy
+ sum[index+2] += b64 * qxy
+ sum[index+3] += 0xFFFF * qxy
+ index += 4
+ px += qx
+ remx -= qx
+ }
+ py += qy
+ remy -= qy
+ }
+ }
+ }
+ return average(sum, w, h, n), true
+}
+
+// resizeRGBA returns a scaled copy of the RGBA image slice r of m.
+// The returned image has width w and height h.
+func resizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) image.Image {
+ ww, hh := uint64(w), uint64(h)
+ dx, dy := uint64(r.Dx()), uint64(r.Dy())
+ // See comment in Resize.
+ n, sum := dx*dy, make([]uint64, 4*w*h)
+ for y := r.Min.Y; y < r.Max.Y; y++ {
+ pixOffset := m.PixOffset(r.Min.X, y)
+ for x := r.Min.X; x < r.Max.X; x++ {
+ // Get the source pixel.
+ r64 := uint64(m.Pix[pixOffset+0])
+ g64 := uint64(m.Pix[pixOffset+1])
+ b64 := uint64(m.Pix[pixOffset+2])
+ a64 := uint64(m.Pix[pixOffset+3])
+ pixOffset += 4
+ // Spread the source pixel over 1 or more destination rows.
+ py := uint64(y) * hh
+ for remy := hh; remy > 0; {
+ qy := dy - (py % dy)
+ if qy > remy {
+ qy = remy
+ }
+ // Spread the source pixel over 1 or more destination columns.
+ px := uint64(x) * ww
+ index := 4 * ((py/dy)*ww + (px / dx))
+ for remx := ww; remx > 0; {
+ qx := dx - (px % dx)
+ if qx > remx {
+ qx = remx
+ }
+ qxy := qx * qy
+ sum[index+0] += r64 * qxy
+ sum[index+1] += g64 * qxy
+ sum[index+2] += b64 * qxy
+ sum[index+3] += a64 * qxy
+ index += 4
+ px += qx
+ remx -= qx
+ }
+ py += qy
+ remy -= qy
+ }
+ }
+ }
+ return average(sum, w, h, n)
+}
+
+// Resample returns a resampled copy of the image slice r of m.
+// The returned image has width w and height h.
+func Resample(m image.Image, r image.Rectangle, w, h int) image.Image {
+ if w < 0 || h < 0 {
+ return nil
+ }
+ if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 {
+ return image.NewRGBA64(image.Rect(0, 0, w, h))
+ }
+ curw, curh := r.Dx(), r.Dy()
+ img := image.NewRGBA(image.Rect(0, 0, w, h))
+ for y := 0; y < h; y++ {
+ for x := 0; x < w; x++ {
+ // Get a source pixel.
+ subx := x * curw / w
+ suby := y * curh / h
+ r32, g32, b32, a32 := m.At(subx, suby).RGBA()
+ r := uint8(r32 >> 8)
+ g := uint8(g32 >> 8)
+ b := uint8(b32 >> 8)
+ a := uint8(a32 >> 8)
+ img.SetRGBA(x, y, color.RGBA{r, g, b, a})
+ }
+ }
+ return img
+}
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/favicon.ico b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/favicon.ico
new file mode 100644
index 000000000..1a71ea772
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/favicon.ico
Binary files differ
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/robots.txt b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/robots.txt
new file mode 100644
index 000000000..eb0536286
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-go/static/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow:
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-python/app.yaml b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/app.yaml
new file mode 100644
index 000000000..5fe123f59
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/app.yaml
@@ -0,0 +1,16 @@
+application: jquery-file-upload
+version: 1
+runtime: python27
+api_version: 1
+threadsafe: true
+
+builtins:
+- deferred: on
+
+handlers:
+- url: /(favicon\.ico|robots\.txt)
+ static_files: static/\1
+ upload: static/(.*)
+ expiration: '1d'
+- url: /.*
+ script: main.app
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-python/main.py b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/main.py
new file mode 100644
index 000000000..37218c827
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/main.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+#
+# jQuery File Upload Plugin GAE Python Example 1.1.4
+# https://github.com/blueimp/jQuery-File-Upload
+#
+# Copyright 2011, Sebastian Tschan
+# https://blueimp.net
+#
+# Licensed under the MIT license:
+# http://www.opensource.org/licenses/MIT
+#
+
+from __future__ import with_statement
+from google.appengine.api import files, images
+from google.appengine.ext import blobstore, deferred
+from google.appengine.ext.webapp import blobstore_handlers
+import json, re, urllib, webapp2
+
+WEBSITE = 'http://blueimp.github.com/jQuery-File-Upload/'
+MIN_FILE_SIZE = 1 # bytes
+MAX_FILE_SIZE = 5000000 # bytes
+IMAGE_TYPES = re.compile('image/(gif|p?jpeg|(x-)?png)')
+ACCEPT_FILE_TYPES = IMAGE_TYPES
+THUMBNAIL_MODIFICATOR = '=s80' # max width / height
+EXPIRATION_TIME = 300 # seconds
+
+def cleanup(blob_keys):
+ blobstore.delete(blob_keys)
+
+class UploadHandler(webapp2.RequestHandler):
+
+ def initialize(self, request, response):
+ super(UploadHandler, self).initialize(request, response)
+ self.response.headers['Access-Control-Allow-Origin'] = '*'
+ self.response.headers[
+ 'Access-Control-Allow-Methods'
+ ] = 'OPTIONS, HEAD, GET, POST, PUT, DELETE'
+
+ def validate(self, file):
+ if file['size'] < MIN_FILE_SIZE:
+ file['error'] = 'minFileSize'
+ elif file['size'] > MAX_FILE_SIZE:
+ file['error'] = 'maxFileSize'
+ elif not ACCEPT_FILE_TYPES.match(file['type']):
+ file['error'] = 'acceptFileTypes'
+ else:
+ return True
+ return False
+
+ def get_file_size(self, file):
+ file.seek(0, 2) # Seek to the end of the file
+ size = file.tell() # Get the position of EOF
+ file.seek(0) # Reset the file position to the beginning
+ return size
+
+ def write_blob(self, data, info):
+ blob = files.blobstore.create(
+ mime_type=info['type'],
+ _blobinfo_uploaded_filename=info['name']
+ )
+ with files.open(blob, 'a') as f:
+ f.write(data)
+ files.finalize(blob)
+ return files.blobstore.get_blob_key(blob)
+
+ def handle_upload(self):
+ results = []
+ blob_keys = []
+ for name, fieldStorage in self.request.POST.items():
+ if type(fieldStorage) is unicode:
+ continue
+ result = {}
+ result['name'] = re.sub(r'^.*\\', '',
+ fieldStorage.filename)
+ result['type'] = fieldStorage.type
+ result['size'] = self.get_file_size(fieldStorage.file)
+ if self.validate(result):
+ blob_key = str(
+ self.write_blob(fieldStorage.value, result)
+ )
+ blob_keys.append(blob_key)
+ result['delete_type'] = 'DELETE'
+ result['delete_url'] = self.request.host_url +\
+ '/?key=' + urllib.quote(blob_key, '')
+ if (IMAGE_TYPES.match(result['type'])):
+ try:
+ result['url'] = images.get_serving_url(
+ blob_key,
+ secure_url=self.request.host_url\
+ .startswith('https')
+ )
+ result['thumbnail_url'] = result['url'] +\
+ THUMBNAIL_MODIFICATOR
+ except: # Could not get an image serving url
+ pass
+ if not 'url' in result:
+ result['url'] = self.request.host_url +\
+ '/' + blob_key + '/' + urllib.quote(
+ result['name'].encode('utf-8'), '')
+ results.append(result)
+ deferred.defer(
+ cleanup,
+ blob_keys,
+ _countdown=EXPIRATION_TIME
+ )
+ return results
+
+ def options(self):
+ pass
+
+ def head(self):
+ pass
+
+ def get(self):
+ self.redirect(WEBSITE)
+
+ def post(self):
+ if (self.request.get('_method') == 'DELETE'):
+ return self.delete()
+ s = json.dumps(self.handle_upload(), separators=(',',':'))
+ redirect = self.request.get('redirect')
+ if redirect:
+ return self.redirect(str(
+ redirect.replace('%s', urllib.quote(s, ''), 1)
+ ))
+ if 'application/json' in self.request.headers.get('Accept'):
+ self.response.headers['Content-Type'] = 'application/json'
+ self.response.write(s)
+
+ def delete(self):
+ blobstore.delete(self.request.get('key') or '')
+
+class DownloadHandler(blobstore_handlers.BlobstoreDownloadHandler):
+ def get(self, key, filename):
+ if not blobstore.get(key):
+ self.error(404)
+ else:
+ # Cache for the expiration time:
+ self.response.headers['Cache-Control'] =\
+ 'public,max-age=%d' % EXPIRATION_TIME
+ self.send_blob(key, save_as=filename)
+
+app = webapp2.WSGIApplication(
+ [
+ ('/', UploadHandler),
+ ('/([^/]+)/([^/]+)', DownloadHandler)
+ ],
+ debug=True
+) \ No newline at end of file
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/favicon.ico b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/favicon.ico
new file mode 100644
index 000000000..1a71ea772
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/favicon.ico
Binary files differ
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/robots.txt b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/robots.txt
new file mode 100644
index 000000000..eb0536286
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/gae-python/static/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow:
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/node/.gitignore b/mod/lightpics/vendors/jquery-file-upload/server/node/.gitignore
new file mode 100644
index 000000000..9daa8247d
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/node/.gitignore
@@ -0,0 +1,2 @@
+.DS_Store
+node_modules
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/node/package.json b/mod/lightpics/vendors/jquery-file-upload/server/node/package.json
new file mode 100644
index 000000000..0e0c1aaae
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/node/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "blueimp-file-upload-node",
+ "version": "1.0.2",
+ "title": "jQuery File Upload Node.js example",
+ "description": "Node.js implementation example of a file upload handler for jQuery File Upload.",
+ "keywords": [
+ "file",
+ "upload",
+ "cross-domain",
+ "cross-site",
+ "node"
+ ],
+ "homepage": "https://github.com/blueimp/jQuery-File-Upload",
+ "author": {
+ "name": "Sebastian Tschan",
+ "url": "https://blueimp.net"
+ },
+ "maintainers": [
+ {
+ "name": "Sebastian Tschan",
+ "url": "https://blueimp.net"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/blueimp/jQuery-File-Upload.git"
+ },
+ "bugs": "https://github.com/blueimp/jQuery-File-Upload/issues",
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://www.opensource.org/licenses/MIT"
+ }
+ ],
+ "dependencies": {
+ "formidable": ">=1.0.8",
+ "node-static": ">=0.5.9",
+ "imagemagick": ">=0.1.2"
+ },
+ "main": "server.js"
+}
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/node/public/files/thumbnail/.gitignore b/mod/lightpics/vendors/jquery-file-upload/server/node/public/files/thumbnail/.gitignore
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/node/public/files/thumbnail/.gitignore
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/node/server.js b/mod/lightpics/vendors/jquery-file-upload/server/node/server.js
new file mode 100755
index 000000000..f1bec542b
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/node/server.js
@@ -0,0 +1,285 @@
+#!/usr/bin/env node
+/*
+ * jQuery File Upload Plugin Node.js Example 1.0.2
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint nomen: true, regexp: true, unparam: true */
+/*global require, __dirname, unescape */
+
+(function (port) {
+ 'use strict';
+ var path = require('path'),
+ fs = require('fs'),
+ // Since Node 0.8, .existsSync() moved from path to fs:
+ _existsSync = fs.existsSync || path.existsSync,
+ formidable = require('formidable'),
+ nodeStatic = require('node-static'),
+ imageMagick = require('imagemagick'),
+ options = {
+ tmpDir: __dirname + '/tmp',
+ publicDir: __dirname + '/public',
+ uploadDir: __dirname + '/public/files',
+ uploadUrl: '/files/',
+ maxPostSize: 500000000, // 500 MB
+ minFileSize: 1,
+ maxFileSize: 100000000, // 100 MB
+ acceptFileTypes: /.+/i,
+ // Files not matched by this regular expression force a download dialog,
+ // to prevent executing any scripts in the context of the service domain:
+ safeFileTypes: /\.(gif|jpe?g|png)$/i,
+ imageTypes: /\.(gif|jpe?g|png)$/i,
+ imageVersions: {
+ 'thumbnail': {
+ width: 80,
+ height: 80
+ }
+ },
+ accessControl: {
+ allowOrigin: '*',
+ allowMethods: 'OPTIONS, HEAD, GET, POST, PUT, DELETE'
+ },
+ /* Uncomment and edit this section to provide the service via HTTPS:
+ ssl: {
+ key: fs.readFileSync('/Applications/XAMPP/etc/ssl.key/server.key'),
+ cert: fs.readFileSync('/Applications/XAMPP/etc/ssl.crt/server.crt')
+ },
+ */
+ nodeStatic: {
+ cache: 3600 // seconds to cache served files
+ }
+ },
+ utf8encode = function (str) {
+ return unescape(encodeURIComponent(str));
+ },
+ fileServer = new nodeStatic.Server(options.publicDir, options.nodeStatic),
+ nameCountRegexp = /(?:(?: \(([\d]+)\))?(\.[^.]+))?$/,
+ nameCountFunc = function (s, index, ext) {
+ return ' (' + ((parseInt(index, 10) || 0) + 1) + ')' + (ext || '');
+ },
+ FileInfo = function (file) {
+ this.name = file.name;
+ this.size = file.size;
+ this.type = file.type;
+ this.delete_type = 'DELETE';
+ },
+ UploadHandler = function (req, res, callback) {
+ this.req = req;
+ this.res = res;
+ this.callback = callback;
+ },
+ serve = function (req, res) {
+ res.setHeader(
+ 'Access-Control-Allow-Origin',
+ options.accessControl.allowOrigin
+ );
+ res.setHeader(
+ 'Access-Control-Allow-Methods',
+ options.accessControl.allowMethods
+ );
+ var handleResult = function (result, redirect) {
+ if (redirect) {
+ res.writeHead(302, {
+ 'Location': redirect.replace(
+ /%s/,
+ encodeURIComponent(JSON.stringify(result))
+ )
+ });
+ res.end();
+ } else {
+ res.writeHead(200, {
+ 'Content-Type': req.headers.accept
+ .indexOf('application/json') !== -1 ?
+ 'application/json' : 'text/plain'
+ });
+ res.end(JSON.stringify(result));
+ }
+ },
+ setNoCacheHeaders = function () {
+ res.setHeader('Pragma', 'no-cache');
+ res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
+ res.setHeader('Content-Disposition', 'inline; filename="files.json"');
+ },
+ handler = new UploadHandler(req, res, handleResult);
+ switch (req.method) {
+ case 'OPTIONS':
+ res.end();
+ break;
+ case 'HEAD':
+ case 'GET':
+ if (req.url === '/') {
+ setNoCacheHeaders();
+ if (req.method === 'GET') {
+ handler.get();
+ } else {
+ res.end();
+ }
+ } else {
+ fileServer.serve(req, res);
+ }
+ break;
+ case 'POST':
+ setNoCacheHeaders();
+ handler.post();
+ break;
+ case 'DELETE':
+ handler.destroy();
+ break;
+ default:
+ res.statusCode = 405;
+ res.end();
+ }
+ };
+ fileServer.respond = function (pathname, status, _headers, files, stat, req, res, finish) {
+ if (!options.safeFileTypes.test(files[0])) {
+ // Force a download dialog for unsafe file extensions:
+ res.setHeader(
+ 'Content-Disposition',
+ 'attachment; filename="' + utf8encode(path.basename(files[0])) + '"'
+ );
+ } else {
+ // Prevent Internet Explorer from MIME-sniffing the content-type:
+ res.setHeader('X-Content-Type-Options', 'nosniff');
+ }
+ nodeStatic.Server.prototype.respond
+ .call(this, pathname, status, _headers, files, stat, req, res, finish);
+ };
+ FileInfo.prototype.validate = function () {
+ if (options.minFileSize && options.minFileSize > this.size) {
+ this.error = 'minFileSize';
+ } else if (options.maxFileSize && options.maxFileSize < this.size) {
+ this.error = 'maxFileSize';
+ } else if (!options.acceptFileTypes.test(this.name)) {
+ this.error = 'acceptFileTypes';
+ }
+ return !this.error;
+ };
+ FileInfo.prototype.safeName = function () {
+ // Prevent directory traversal and creating hidden system files:
+ this.name = path.basename(this.name).replace(/^\.+/, '');
+ // Prevent overwriting existing files:
+ while (_existsSync(options.uploadDir + '/' + this.name)) {
+ this.name = this.name.replace(nameCountRegexp, nameCountFunc);
+ }
+ };
+ FileInfo.prototype.initUrls = function (req) {
+ if (!this.error) {
+ var that = this,
+ baseUrl = (options.ssl ? 'https:' : 'http:') +
+ '//' + req.headers.host + options.uploadUrl;
+ this.url = this.delete_url = baseUrl + encodeURIComponent(this.name);
+ Object.keys(options.imageVersions).forEach(function (version) {
+ if (_existsSync(
+ options.uploadDir + '/' + version + '/' + that.name
+ )) {
+ that[version + '_url'] = baseUrl + version + '/' +
+ encodeURIComponent(that.name);
+ }
+ });
+ }
+ };
+ UploadHandler.prototype.get = function () {
+ var handler = this,
+ files = [];
+ fs.readdir(options.uploadDir, function (err, list) {
+ list.forEach(function (name) {
+ var stats = fs.statSync(options.uploadDir + '/' + name),
+ fileInfo;
+ if (stats.isFile()) {
+ fileInfo = new FileInfo({
+ name: name,
+ size: stats.size
+ });
+ fileInfo.initUrls(handler.req);
+ files.push(fileInfo);
+ }
+ });
+ handler.callback(files);
+ });
+ };
+ UploadHandler.prototype.post = function () {
+ var handler = this,
+ form = new formidable.IncomingForm(),
+ tmpFiles = [],
+ files = [],
+ map = {},
+ counter = 1,
+ redirect,
+ finish = function () {
+ counter -= 1;
+ if (!counter) {
+ files.forEach(function (fileInfo) {
+ fileInfo.initUrls(handler.req);
+ });
+ handler.callback(files, redirect);
+ }
+ };
+ form.uploadDir = options.tmpDir;
+ form.on('fileBegin', function (name, file) {
+ tmpFiles.push(file.path);
+ var fileInfo = new FileInfo(file, handler.req, true);
+ fileInfo.safeName();
+ map[path.basename(file.path)] = fileInfo;
+ files.push(fileInfo);
+ }).on('field', function (name, value) {
+ if (name === 'redirect') {
+ redirect = value;
+ }
+ }).on('file', function (name, file) {
+ var fileInfo = map[path.basename(file.path)];
+ fileInfo.size = file.size;
+ if (!fileInfo.validate()) {
+ fs.unlink(file.path);
+ return;
+ }
+ fs.renameSync(file.path, options.uploadDir + '/' + fileInfo.name);
+ if (options.imageTypes.test(fileInfo.name)) {
+ Object.keys(options.imageVersions).forEach(function (version) {
+ counter += 1;
+ var opts = options.imageVersions[version];
+ imageMagick.resize({
+ width: opts.width,
+ height: opts.height,
+ srcPath: options.uploadDir + '/' + fileInfo.name,
+ dstPath: options.uploadDir + '/' + version + '/' +
+ fileInfo.name
+ }, finish);
+ });
+ }
+ }).on('aborted', function () {
+ tmpFiles.forEach(function (file) {
+ fs.unlink(file);
+ });
+ }).on('progress', function (bytesReceived, bytesExpected) {
+ if (bytesReceived > options.maxPostSize) {
+ handler.req.connection.destroy();
+ }
+ }).on('end', finish).parse(handler.req);
+ };
+ UploadHandler.prototype.destroy = function () {
+ var handler = this,
+ fileName;
+ if (handler.req.url.slice(0, options.uploadUrl.length) === options.uploadUrl) {
+ fileName = path.basename(decodeURIComponent(handler.req.url));
+ fs.unlink(options.uploadDir + '/' + fileName, function (ex) {
+ Object.keys(options.imageVersions).forEach(function (version) {
+ fs.unlink(options.uploadDir + '/' + version + '/' + fileName);
+ });
+ handler.callback(!ex);
+ });
+ } else {
+ handler.callback(false);
+ }
+ };
+ if (options.ssl) {
+ require('https').createServer(options.ssl, serve).listen(port);
+ } else {
+ require('http').createServer(serve).listen(port);
+ }
+}(8888));
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/node/tmp/.gitignore b/mod/lightpics/vendors/jquery-file-upload/server/node/tmp/.gitignore
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/node/tmp/.gitignore
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/php/files/.htaccess b/mod/lightpics/vendors/jquery-file-upload/server/php/files/.htaccess
new file mode 100644
index 000000000..a6a9f6a75
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/php/files/.htaccess
@@ -0,0 +1,4 @@
+ForceType application/octet-stream
+<FilesMatch "(?i)\.(gif|jpe?g|png)$">
+ ForceType none
+</FilesMatch> \ No newline at end of file
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/php/index.php b/mod/lightpics/vendors/jquery-file-upload/server/php/index.php
new file mode 100644
index 000000000..1601c76f3
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/php/index.php
@@ -0,0 +1,46 @@
+<?php
+/*
+ * jQuery File Upload Plugin PHP Example 5.7
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+error_reporting(E_ALL | E_STRICT);
+
+require('upload.class.php');
+
+$upload_handler = new UploadHandler();
+
+header('Pragma: no-cache');
+header('Cache-Control: no-store, no-cache, must-revalidate');
+header('Content-Disposition: inline; filename="files.json"');
+header('X-Content-Type-Options: nosniff');
+header('Access-Control-Allow-Origin: *');
+header('Access-Control-Allow-Methods: OPTIONS, HEAD, GET, POST, PUT, DELETE');
+header('Access-Control-Allow-Headers: X-File-Name, X-File-Type, X-File-Size');
+
+switch ($_SERVER['REQUEST_METHOD']) {
+ case 'OPTIONS':
+ break;
+ case 'HEAD':
+ case 'GET':
+ $upload_handler->get();
+ break;
+ case 'POST':
+ if (isset($_REQUEST['_method']) && $_REQUEST['_method'] === 'DELETE') {
+ $upload_handler->delete();
+ } else {
+ $upload_handler->post();
+ }
+ break;
+ case 'DELETE':
+ $upload_handler->delete();
+ break;
+ default:
+ header('HTTP/1.1 405 Method Not Allowed');
+}
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/php/thumbnails/.htaccess b/mod/lightpics/vendors/jquery-file-upload/server/php/thumbnails/.htaccess
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/php/thumbnails/.htaccess
diff --git a/mod/lightpics/vendors/jquery-file-upload/server/php/upload.class.php b/mod/lightpics/vendors/jquery-file-upload/server/php/upload.class.php
new file mode 100644
index 000000000..c4efacbdb
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/server/php/upload.class.php
@@ -0,0 +1,436 @@
+<?php
+/*
+ * jQuery File Upload Plugin PHP Class 5.11.2
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+class UploadHandler
+{
+ protected $options;
+
+ function __construct($options=null) {
+ $this->options = array(
+ 'script_url' => $this->getFullUrl().'/',
+ 'upload_dir' => dirname($_SERVER['SCRIPT_FILENAME']).'/files/',
+ 'upload_url' => $this->getFullUrl().'/files/',
+ 'param_name' => 'files',
+ // Set the following option to 'POST', if your server does not support
+ // DELETE requests. This is a parameter sent to the client:
+ 'delete_type' => 'DELETE',
+ // The php.ini settings upload_max_filesize and post_max_size
+ // take precedence over the following max_file_size setting:
+ 'max_file_size' => null,
+ 'min_file_size' => 1,
+ 'accept_file_types' => '/.+$/i',
+ // The maximum number of files for the upload directory:
+ 'max_number_of_files' => null,
+ // Image resolution restrictions:
+ 'max_width' => null,
+ 'max_height' => null,
+ 'min_width' => 1,
+ 'min_height' => 1,
+ // Set the following option to false to enable resumable uploads:
+ 'discard_aborted_uploads' => true,
+ // Set to true to rotate images based on EXIF meta data, if available:
+ 'orient_image' => false,
+ 'image_versions' => array(
+ // Uncomment the following version to restrict the size of
+ // uploaded images. You can also add additional versions with
+ // their own upload directories:
+ /*
+ 'large' => array(
+ 'upload_dir' => dirname($_SERVER['SCRIPT_FILENAME']).'/files/',
+ 'upload_url' => $this->getFullUrl().'/files/',
+ 'max_width' => 1920,
+ 'max_height' => 1200,
+ 'jpeg_quality' => 95
+ ),
+ */
+ 'thumbnail' => array(
+ 'upload_dir' => dirname($_SERVER['SCRIPT_FILENAME']).'/thumbnails/',
+ 'upload_url' => $this->getFullUrl().'/thumbnails/',
+ 'max_width' => 80,
+ 'max_height' => 80
+ )
+ )
+ );
+ if ($options) {
+ $this->options = array_replace_recursive($this->options, $options);
+ }
+ }
+
+ protected function getFullUrl() {
+ $https = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
+ return
+ ($https ? 'https://' : 'http://').
+ (!empty($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'].'@' : '').
+ (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME'].
+ ($https && $_SERVER['SERVER_PORT'] === 443 ||
+ $_SERVER['SERVER_PORT'] === 80 ? '' : ':'.$_SERVER['SERVER_PORT']))).
+ substr($_SERVER['SCRIPT_NAME'],0, strrpos($_SERVER['SCRIPT_NAME'], '/'));
+ }
+
+ protected function set_file_delete_url($file) {
+ $file->delete_url = $this->options['script_url']
+ .'?file='.rawurlencode($file->name);
+ $file->delete_type = $this->options['delete_type'];
+ if ($file->delete_type !== 'DELETE') {
+ $file->delete_url .= '&_method=DELETE';
+ }
+ }
+
+ protected function get_file_object($file_name) {
+ $file_path = $this->options['upload_dir'].$file_name;
+ if (is_file($file_path) && $file_name[0] !== '.') {
+ $file = new stdClass();
+ $file->name = $file_name;
+ $file->size = filesize($file_path);
+ $file->url = $this->options['upload_url'].rawurlencode($file->name);
+ foreach($this->options['image_versions'] as $version => $options) {
+ if (is_file($options['upload_dir'].$file_name)) {
+ $file->{$version.'_url'} = $options['upload_url']
+ .rawurlencode($file->name);
+ }
+ }
+ $this->set_file_delete_url($file);
+ return $file;
+ }
+ return null;
+ }
+
+ protected function get_file_objects() {
+ return array_values(array_filter(array_map(
+ array($this, 'get_file_object'),
+ scandir($this->options['upload_dir'])
+ )));
+ }
+
+ protected function create_scaled_image($file_name, $options) {
+ $file_path = $this->options['upload_dir'].$file_name;
+ $new_file_path = $options['upload_dir'].$file_name;
+ list($img_width, $img_height) = @getimagesize($file_path);
+ if (!$img_width || !$img_height) {
+ return false;
+ }
+ $scale = min(
+ $options['max_width'] / $img_width,
+ $options['max_height'] / $img_height
+ );
+ if ($scale >= 1) {
+ if ($file_path !== $new_file_path) {
+ return copy($file_path, $new_file_path);
+ }
+ return true;
+ }
+ $new_width = $img_width * $scale;
+ $new_height = $img_height * $scale;
+ $new_img = @imagecreatetruecolor($new_width, $new_height);
+ switch (strtolower(substr(strrchr($file_name, '.'), 1))) {
+ case 'jpg':
+ case 'jpeg':
+ $src_img = @imagecreatefromjpeg($file_path);
+ $write_image = 'imagejpeg';
+ $image_quality = isset($options['jpeg_quality']) ?
+ $options['jpeg_quality'] : 75;
+ break;
+ case 'gif':
+ @imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0));
+ $src_img = @imagecreatefromgif($file_path);
+ $write_image = 'imagegif';
+ $image_quality = null;
+ break;
+ case 'png':
+ @imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0));
+ @imagealphablending($new_img, false);
+ @imagesavealpha($new_img, true);
+ $src_img = @imagecreatefrompng($file_path);
+ $write_image = 'imagepng';
+ $image_quality = isset($options['png_quality']) ?
+ $options['png_quality'] : 9;
+ break;
+ default:
+ $src_img = null;
+ }
+ $success = $src_img && @imagecopyresampled(
+ $new_img,
+ $src_img,
+ 0, 0, 0, 0,
+ $new_width,
+ $new_height,
+ $img_width,
+ $img_height
+ ) && $write_image($new_img, $new_file_path, $image_quality);
+ // Free up memory (imagedestroy does not delete files):
+ @imagedestroy($src_img);
+ @imagedestroy($new_img);
+ return $success;
+ }
+
+ protected function validate($uploaded_file, $file, $error, $index) {
+ if ($error) {
+ $file->error = $error;
+ return false;
+ }
+ if (!$file->name) {
+ $file->error = 'missingFileName';
+ return false;
+ }
+ if (!preg_match($this->options['accept_file_types'], $file->name)) {
+ $file->error = 'acceptFileTypes';
+ return false;
+ }
+ if ($uploaded_file && is_uploaded_file($uploaded_file)) {
+ $file_size = filesize($uploaded_file);
+ } else {
+ $file_size = $_SERVER['CONTENT_LENGTH'];
+ }
+ if ($this->options['max_file_size'] && (
+ $file_size > $this->options['max_file_size'] ||
+ $file->size > $this->options['max_file_size'])
+ ) {
+ $file->error = 'maxFileSize';
+ return false;
+ }
+ if ($this->options['min_file_size'] &&
+ $file_size < $this->options['min_file_size']) {
+ $file->error = 'minFileSize';
+ return false;
+ }
+ if (is_int($this->options['max_number_of_files']) && (
+ count($this->get_file_objects()) >= $this->options['max_number_of_files'])
+ ) {
+ $file->error = 'maxNumberOfFiles';
+ return false;
+ }
+ list($img_width, $img_height) = @getimagesize($uploaded_file);
+ if (is_int($img_width)) {
+ if ($this->options['max_width'] && $img_width > $this->options['max_width'] ||
+ $this->options['max_height'] && $img_height > $this->options['max_height']) {
+ $file->error = 'maxResolution';
+ return false;
+ }
+ if ($this->options['min_width'] && $img_width < $this->options['min_width'] ||
+ $this->options['min_height'] && $img_height < $this->options['min_height']) {
+ $file->error = 'minResolution';
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected function upcount_name_callback($matches) {
+ $index = isset($matches[1]) ? intval($matches[1]) + 1 : 1;
+ $ext = isset($matches[2]) ? $matches[2] : '';
+ return ' ('.$index.')'.$ext;
+ }
+
+ protected function upcount_name($name) {
+ return preg_replace_callback(
+ '/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/',
+ array($this, 'upcount_name_callback'),
+ $name,
+ 1
+ );
+ }
+
+ protected function trim_file_name($name, $type, $index) {
+ // Remove path information and dots around the filename, to prevent uploading
+ // into different directories or replacing hidden system files.
+ // Also remove control characters and spaces (\x00..\x20) around the filename:
+ $file_name = trim(basename(stripslashes($name)), ".\x00..\x20");
+ // Add missing file extension for known image types:
+ if (strpos($file_name, '.') === false &&
+ preg_match('/^image\/(gif|jpe?g|png)/', $type, $matches)) {
+ $file_name .= '.'.$matches[1];
+ }
+ if ($this->options['discard_aborted_uploads']) {
+ while(is_file($this->options['upload_dir'].$file_name)) {
+ $file_name = $this->upcount_name($file_name);
+ }
+ }
+ return $file_name;
+ }
+
+ protected function handle_form_data($file, $index) {
+ // Handle form data, e.g. $_REQUEST['description'][$index]
+ }
+
+ protected function orient_image($file_path) {
+ $exif = @exif_read_data($file_path);
+ if ($exif === false) {
+ return false;
+ }
+ $orientation = intval(@$exif['Orientation']);
+ if (!in_array($orientation, array(3, 6, 8))) {
+ return false;
+ }
+ $image = @imagecreatefromjpeg($file_path);
+ switch ($orientation) {
+ case 3:
+ $image = @imagerotate($image, 180, 0);
+ break;
+ case 6:
+ $image = @imagerotate($image, 270, 0);
+ break;
+ case 8:
+ $image = @imagerotate($image, 90, 0);
+ break;
+ default:
+ return false;
+ }
+ $success = imagejpeg($image, $file_path);
+ // Free up memory (imagedestroy does not delete files):
+ @imagedestroy($image);
+ return $success;
+ }
+
+ protected function handle_file_upload($uploaded_file, $name, $size, $type, $error, $index = null) {
+ $file = new stdClass();
+ $file->name = $this->trim_file_name($name, $type, $index);
+ $file->size = intval($size);
+ $file->type = $type;
+ if ($this->validate($uploaded_file, $file, $error, $index)) {
+ $this->handle_form_data($file, $index);
+ $file_path = $this->options['upload_dir'].$file->name;
+ $append_file = !$this->options['discard_aborted_uploads'] &&
+ is_file($file_path) && $file->size > filesize($file_path);
+ clearstatcache();
+ if ($uploaded_file && is_uploaded_file($uploaded_file)) {
+ // multipart/formdata uploads (POST method uploads)
+ if ($append_file) {
+ file_put_contents(
+ $file_path,
+ fopen($uploaded_file, 'r'),
+ FILE_APPEND
+ );
+ } else {
+ move_uploaded_file($uploaded_file, $file_path);
+ }
+ } else {
+ // Non-multipart uploads (PUT method support)
+ file_put_contents(
+ $file_path,
+ fopen('php://input', 'r'),
+ $append_file ? FILE_APPEND : 0
+ );
+ }
+ $file_size = filesize($file_path);
+ if ($file_size === $file->size) {
+ if ($this->options['orient_image']) {
+ $this->orient_image($file_path);
+ }
+ $file->url = $this->options['upload_url'].rawurlencode($file->name);
+ foreach($this->options['image_versions'] as $version => $options) {
+ if ($this->create_scaled_image($file->name, $options)) {
+ if ($this->options['upload_dir'] !== $options['upload_dir']) {
+ $file->{$version.'_url'} = $options['upload_url']
+ .rawurlencode($file->name);
+ } else {
+ clearstatcache();
+ $file_size = filesize($file_path);
+ }
+ }
+ }
+ } else if ($this->options['discard_aborted_uploads']) {
+ unlink($file_path);
+ $file->error = 'abort';
+ }
+ $file->size = $file_size;
+ $this->set_file_delete_url($file);
+ }
+ return $file;
+ }
+
+ public function get() {
+ $file_name = isset($_REQUEST['file']) ?
+ basename(stripslashes($_REQUEST['file'])) : null;
+ if ($file_name) {
+ $info = $this->get_file_object($file_name);
+ } else {
+ $info = $this->get_file_objects();
+ }
+ header('Content-type: application/json');
+ echo json_encode($info);
+ }
+
+ public function post() {
+ if (isset($_REQUEST['_method']) && $_REQUEST['_method'] === 'DELETE') {
+ return $this->delete();
+ }
+ $upload = isset($_FILES[$this->options['param_name']]) ?
+ $_FILES[$this->options['param_name']] : null;
+ $info = array();
+ if ($upload && is_array($upload['tmp_name'])) {
+ // param_name is an array identifier like "files[]",
+ // $_FILES is a multi-dimensional array:
+ foreach ($upload['tmp_name'] as $index => $value) {
+ $info[] = $this->handle_file_upload(
+ $upload['tmp_name'][$index],
+ isset($_SERVER['HTTP_X_FILE_NAME']) ?
+ $_SERVER['HTTP_X_FILE_NAME'] : $upload['name'][$index],
+ isset($_SERVER['HTTP_X_FILE_SIZE']) ?
+ $_SERVER['HTTP_X_FILE_SIZE'] : $upload['size'][$index],
+ isset($_SERVER['HTTP_X_FILE_TYPE']) ?
+ $_SERVER['HTTP_X_FILE_TYPE'] : $upload['type'][$index],
+ $upload['error'][$index],
+ $index
+ );
+ }
+ } elseif ($upload || isset($_SERVER['HTTP_X_FILE_NAME'])) {
+ // param_name is a single object identifier like "file",
+ // $_FILES is a one-dimensional array:
+ $info[] = $this->handle_file_upload(
+ isset($upload['tmp_name']) ? $upload['tmp_name'] : null,
+ isset($_SERVER['HTTP_X_FILE_NAME']) ?
+ $_SERVER['HTTP_X_FILE_NAME'] : (isset($upload['name']) ?
+ $upload['name'] : null),
+ isset($_SERVER['HTTP_X_FILE_SIZE']) ?
+ $_SERVER['HTTP_X_FILE_SIZE'] : (isset($upload['size']) ?
+ $upload['size'] : null),
+ isset($_SERVER['HTTP_X_FILE_TYPE']) ?
+ $_SERVER['HTTP_X_FILE_TYPE'] : (isset($upload['type']) ?
+ $upload['type'] : null),
+ isset($upload['error']) ? $upload['error'] : null
+ );
+ }
+ header('Vary: Accept');
+ $json = json_encode($info);
+ $redirect = isset($_REQUEST['redirect']) ?
+ stripslashes($_REQUEST['redirect']) : null;
+ if ($redirect) {
+ header('Location: '.sprintf($redirect, rawurlencode($json)));
+ return;
+ }
+ if (isset($_SERVER['HTTP_ACCEPT']) &&
+ (strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false)) {
+ header('Content-type: application/json');
+ } else {
+ header('Content-type: text/plain');
+ }
+ echo $json;
+ }
+
+ public function delete() {
+ $file_name = isset($_REQUEST['file']) ?
+ basename(stripslashes($_REQUEST['file'])) : null;
+ $file_path = $this->options['upload_dir'].$file_name;
+ $success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path);
+ if ($success) {
+ foreach($this->options['image_versions'] as $version => $options) {
+ $file = $options['upload_dir'].$file_name;
+ if (is_file($file)) {
+ unlink($file);
+ }
+ }
+ }
+ header('Content-type: application/json');
+ echo json_encode($success);
+ }
+
+}
diff --git a/mod/lightpics/vendors/jquery-file-upload/test/index.html b/mod/lightpics/vendors/jquery-file-upload/test/index.html
new file mode 100644
index 000000000..8a8011d21
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/test/index.html
@@ -0,0 +1,146 @@
+<!DOCTYPE HTML>
+<!--
+/*
+ * jQuery File Upload Plugin Test 6.9.1
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+-->
+<html lang="en">
+<head>
+<!-- Force latest IE rendering engine or ChromeFrame if installed -->
+<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><![endif]-->
+<meta charset="utf-8">
+<title>jQuery File Upload Plugin Test</title>
+<meta name="viewport" content="width=device-width">
+<link rel="stylesheet" href="http://code.jquery.com/qunit/git/qunit.css">
+</head>
+<body>
+<h1 id="qunit-header">jQuery File Upload Plugin Test</h1>
+<h2 id="qunit-banner"></h2>
+<div id="qunit-testrunner-toolbar"></div>
+<h2 id="qunit-userAgent"></h2>
+<ol id="qunit-tests"></ol>
+<div id="qunit-fixture">
+ <!-- The file upload form used as target for the file upload widget -->
+ <form id="fileupload" action="../server/php/" method="POST" enctype="multipart/form-data">
+ <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
+ <div class="row fileupload-buttonbar">
+ <div class="span7">
+ <!-- The fileinput-button span is used to style the file input field as button -->
+ <span class="btn btn-success fileinput-button">
+ <i class="icon-plus icon-white"></i>
+ <span>Add files...</span>
+ <input type="file" name="files[]" multiple>
+ </span>
+ <button type="submit" class="btn btn-primary start">
+ <i class="icon-upload icon-white"></i>
+ <span>Start upload</span>
+ </button>
+ <button type="reset" class="btn btn-warning cancel">
+ <i class="icon-ban-circle icon-white"></i>
+ <span>Cancel upload</span>
+ </button>
+ <button type="button" class="btn btn-danger delete">
+ <i class="icon-trash icon-white"></i>
+ <span>Delete</span>
+ </button>
+ <input type="checkbox" class="toggle">
+ </div>
+ <!-- The global progress information -->
+ <div class="span5 fileupload-progress fade">
+ <!-- The global progress bar -->
+ <div class="progress progress-success progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100">
+ <div class="bar" style="width:0%;"></div>
+ </div>
+ <!-- The extended global progress information -->
+ <div class="progress-extended">&nbsp;</div>
+ </div>
+ </div>
+ <!-- The loading indicator is shown during file processing -->
+ <div class="fileupload-loading"></div>
+ <br>
+ <!-- The table listing the files available for upload/download -->
+ <table role="presentation" class="table table-striped"><tbody class="files" data-toggle="modal-gallery" data-target="#modal-gallery"></tbody></table>
+ </form>
+</div>
+<!-- The template to display files available for upload -->
+<script id="template-upload" type="text/x-tmpl">
+{% for (var i=0, file; file=o.files[i]; i++) { %}
+ <tr class="template-upload">
+ <td class="preview"><span class=""></span></td>
+ <td class="name"><span>{%=file.name%}</span></td>
+ <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
+ {% if (file.error) { %}
+ <td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
+ {% } else if (o.files.valid && !i) { %}
+ <td>
+ <div class="progress progress-success progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><div class="bar" style="width:0%;"></div></div>
+ </td>
+ <td class="start">{% if (!o.options.autoUpload) { %}
+ <button class="btn btn-primary">
+ <i class="icon-upload icon-white"></i>
+ <span>{%=locale.fileupload.start%}</span>
+ </button>
+ {% } %}</td>
+ {% } else { %}
+ <td colspan="2"></td>
+ {% } %}
+ <td class="cancel">{% if (!i) { %}
+ <button class="btn btn-warning">
+ <i class="icon-ban-circle icon-white"></i>
+ <span>{%=locale.fileupload.cancel%}</span>
+ </button>
+ {% } %}</td>
+ </tr>
+{% } %}
+</script>
+<!-- The template to display files available for download -->
+<script id="template-download" type="text/x-tmpl">
+{% for (var i=0, file; file=o.files[i]; i++) { %}
+ <tr class="template-download">
+ {% if (file.error) { %}
+ <td></td>
+ <td class="name"><span>{%=file.name%}</span></td>
+ <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
+ <td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
+ {% } else { %}
+ <td class="preview">{% if (file.thumbnail_url) { %}
+ <a href="{%=file.url%}" title="{%=file.name%}" rel="gallery" download="{%=file.name%}"><img src="{%=file.thumbnail_url%}"></a>
+ {% } %}</td>
+ <td class="name">
+ <a href="{%=file.url%}" title="{%=file.name%}" rel="{%=file.thumbnail_url&&'gallery'%}" download="{%=file.name%}">{%=file.name%}</a>
+ </td>
+ <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
+ <td colspan="2"></td>
+ {% } %}
+ <td class="delete">
+ <button class="btn btn-danger" data-type="{%=file.delete_type%}" data-url="{%=file.delete_url%}">
+ <i class="icon-trash icon-white"></i>
+ <span>{%=locale.fileupload.destroy%}</span>
+ </button>
+ <input type="checkbox" name="delete" value="1">
+ </td>
+ </tr>
+{% } %}
+</script>
+<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
+<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>
+<script src="http://blueimp.github.com/JavaScript-Templates/tmpl.min.js"></script>
+<script src="http://blueimp.github.com/JavaScript-Load-Image/load-image.min.js"></script>
+<script src="http://blueimp.github.com/JavaScript-Canvas-to-Blob/canvas-to-blob.min.js"></script>
+<script src="../js/jquery.iframe-transport.js"></script>
+<script src="../js/jquery.fileupload.js"></script>
+<script src="../js/jquery.fileupload-fp.js"></script>
+<script src="../js/jquery.fileupload-ui.js"></script>
+<script src="../js/jquery.fileupload-jui.js"></script>
+<script src="../js/locale.js"></script>
+<script src="http://code.jquery.com/qunit/git/qunit.js"></script>
+<script src="test.js"></script>
+</body>
+</html>
diff --git a/mod/lightpics/vendors/jquery-file-upload/test/test.js b/mod/lightpics/vendors/jquery-file-upload/test/test.js
new file mode 100644
index 000000000..0f6d90afb
--- /dev/null
+++ b/mod/lightpics/vendors/jquery-file-upload/test/test.js
@@ -0,0 +1,1279 @@
+/*
+ * jQuery File Upload Plugin Test 6.9.2
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*jslint nomen: true, unparam: true */
+/*global $, QUnit, document, expect, module, test, asyncTest, start, ok, strictEqual, notStrictEqual */
+
+$(function () {
+ 'use strict';
+
+ QUnit.done = function () {
+ // Delete all uploaded files:
+ var url = $('#fileupload').find('form').prop('action');
+ $.getJSON(url, function (files) {
+ $.each(files, function (index, file) {
+ $.ajax({
+ url: url + '?file=' + encodeURIComponent(file.name),
+ type: 'DELETE'
+ });
+ });
+ });
+ };
+
+ var lifecycle = {
+ setup: function () {
+ // Set the .fileupload method to the basic widget method:
+ $.widget('blueimp.fileupload', $.blueimp.fileupload, {});
+ },
+ teardown: function () {
+ // De-initialize the file input plugin:
+ $('#fileupload:blueimp-fileupload').fileupload('destroy');
+ // Remove all remaining event listeners:
+ $('#fileupload input').unbind();
+ $(document).unbind();
+ }
+ },
+ lifecycleUI = {
+ setup: function () {
+ // Set the .fileupload method to the JUI widget method:
+ $.widget('blueimpJUI.fileupload', $.blueimpJUI.fileupload, {});
+ },
+ teardown: function () {
+ // De-initialize the file input plugin:
+ $('#fileupload:blueimpJUI-fileupload').fileupload('destroy');
+ // Remove all remaining event listeners:
+ $('#fileupload input, #fileupload button').unbind();
+ $(document).unbind();
+ }
+ };
+
+ module('Initialization', lifecycle);
+
+ test('Widget initialization', function () {
+ ok($('#fileupload').fileupload().data('fileupload'));
+ });
+
+ test('Data attribute options', function () {
+ $('#fileupload').attr('data-url', 'http://example.org');
+ $('#fileupload').fileupload();
+ strictEqual(
+ $('#fileupload').fileupload('option', 'url'),
+ 'http://example.org'
+ );
+ });
+
+ test('File input initialization', function () {
+ var fu = $('#fileupload').fileupload();
+ ok(
+ fu.fileupload('option', 'fileInput').length,
+ 'File input field inside of the widget'
+ );
+ ok(
+ fu.fileupload('option', 'fileInput').length,
+ 'Widget element as file input field'
+ );
+ });
+
+ test('Drop zone initialization', function () {
+ ok($('#fileupload').fileupload()
+ .fileupload('option', 'dropZone').length);
+ });
+
+ test('Event listeners initialization', function () {
+ var fu = $('#fileupload').fileupload();
+ ok(
+ fu.fileupload('option', 'fileInput')
+ .data('events').change.length,
+ 'Listens to file input change events'
+ );
+ if ($.support.xhrFormDataFileUpload) {
+ ok(
+ fu.fileupload('option', 'dropZone')
+ .data('events').drop.length,
+ 'Listens to drop zone drop events'
+ );
+ ok(
+ fu.fileupload('option', 'dropZone')
+ .data('events').dragover.length,
+ 'Listens to drop zone dragover events'
+ );
+ }
+ });
+
+ module('API', lifecycle);
+
+ test('destroy', function () {
+ var fu = $('#fileupload').fileupload(),
+ fileInput = fu.fileupload('option', 'fileInput'),
+ dropZone = fu.fileupload('option', 'dropZone');
+ fileInput.change($.noop);
+ dropZone.bind('drop', $.noop);
+ dropZone.bind('dragover', $.noop);
+ fu.fileupload('destroy');
+ strictEqual(
+ fileInput.data('events').change.length,
+ 1,
+ 'Removes own file input change event listener'
+ );
+ if ($.support.xhrFormDataFileUpload) {
+ strictEqual(
+ dropZone.data('events').drop.length,
+ 1,
+ 'Removes own drop zone drop event listener'
+ );
+ strictEqual(
+ dropZone.data('events').dragover.length,
+ 1,
+ 'Removes own drop zone dragover event listener'
+ );
+ }
+ });
+
+ test('disable', function () {
+ var fu = $('#fileupload').fileupload(),
+ fileInput = fu.fileupload('option', 'fileInput'),
+ dropZone = fu.fileupload('option', 'dropZone'),
+ param = {files: [{name: 'test'}]};
+ fileInput.change($.noop);
+ dropZone.bind('drop', $.noop);
+ dropZone.bind('dragover', $.noop);
+ fu.fileupload('disable');
+ strictEqual(
+ fileInput.data('events').change.length,
+ 1,
+ 'Removes own file input change event listener'
+ );
+ if ($.support.xhrFormDataFileUpload) {
+ strictEqual(
+ dropZone.data('events').drop.length,
+ 1,
+ 'Removes own drop zone drop event listener'
+ );
+ strictEqual(
+ dropZone.data('events').dragover.length,
+ 1,
+ 'Removes own drop zone dragover event listener'
+ );
+ }
+ fu.fileupload({
+ add: function (e, data) {
+ ok(false);
+ }
+ }).fileupload('add', param);
+ });
+
+ test('enable', function () {
+ var fu = $('#fileupload').fileupload(),
+ param = {files: [{name: 'test'}]};
+ fu.fileupload('disable');
+ fu.fileupload('enable');
+ ok(
+ fu.fileupload('option', 'fileInput')
+ .data('events').change.length,
+ 'Listens to file input change events'
+ );
+ if ($.support.xhrFormDataFileUpload) {
+ ok(
+ fu.fileupload('option', 'dropZone')
+ .data('events').drop.length,
+ 'Listens to drop zone drop events'
+ );
+ ok(
+ fu.fileupload('option', 'dropZone')
+ .data('events').dragover.length,
+ 'Listens to drop zone dragover events'
+ );
+ }
+ $('#fileupload').fileupload({
+ send: function (e, data) {
+ strictEqual(
+ data.files[0].name,
+ 'test',
+ 'Triggers send callback'
+ );
+ return false;
+ }
+ }).fileupload('send', param);
+ });
+
+ test('option', function () {
+ var fu = $('#fileupload').fileupload(),
+ fileInput = fu.fileupload('option', 'fileInput'),
+ dropZone = fu.fileupload('option', 'dropZone');
+ fu.fileupload('option', 'fileInput', null);
+ fu.fileupload('option', 'dropZone', null);
+ ok(
+ !fileInput.data('events'),
+ 'Removes event listener after changing fileInput option'
+ );
+ if ($.support.xhrFormDataFileUpload) {
+ ok(
+ !dropZone.data('events'),
+ 'Removes event listeners after changing dropZone option'
+ );
+ }
+ fu.fileupload('option', 'fileInput', fileInput);
+ fu.fileupload('option', 'dropZone', dropZone);
+ ok(
+ fileInput.data('events').change.length,
+ 'Adds change event listener after setting fileInput option'
+ );
+ if ($.support.xhrFormDataFileUpload) {
+ ok(
+ dropZone.data('events').drop.length,
+ 'Adds drop event listener after setting dropZone option'
+ );
+ ok(
+ dropZone.data('events').dragover.length,
+ 'Adds dragover event listener after setting dropZone option'
+ );
+ }
+ fu.fileupload('option', 'dropZone', 'body');
+ strictEqual(
+ fu.fileupload('option', 'dropZone')[0],
+ document.body,
+ 'Allow a query string as parameter for the dropZone option'
+ );
+ fu.fileupload('option', 'dropZone', document);
+ strictEqual(
+ fu.fileupload('option', 'dropZone')[0],
+ document,
+ 'Allow a document element as parameter for the dropZone option'
+ );
+ fu.fileupload('option', 'fileInput', ':file');
+ strictEqual(
+ fu.fileupload('option', 'fileInput')[0],
+ $(':file')[0],
+ 'Allow a query string as parameter for the fileInput option'
+ );
+ fu.fileupload('option', 'fileInput', $(':file')[0]);
+ strictEqual(
+ fu.fileupload('option', 'fileInput')[0],
+ $(':file')[0],
+ 'Allow a document element as parameter for the fileInput option'
+ );
+ });
+
+ asyncTest('add', function () {
+ expect(4);
+ var param = {files: [{name: 'test'}]},
+ param2 = {files: [{fileName: 'test', fileSize: 123}]};
+ $('#fileupload').fileupload({
+ add: function (e, data) {
+ strictEqual(
+ data.files[0].name,
+ param.files[0].name,
+ 'Triggers add callback'
+ );
+ }
+ }).fileupload('add', param).fileupload(
+ 'option',
+ 'add',
+ function (e, data) {
+ strictEqual(
+ data.files[0].name,
+ param2.files[0].fileName,
+ 'Normalizes fileName'
+ );
+ strictEqual(
+ data.files[0].size,
+ param2.files[0].fileSize,
+ 'Normalizes fileSize'
+ );
+ data.submit().complete(function () {
+ ok(true, 'data.submit() Returns a jqXHR object');
+ start();
+ });
+ }
+ ).fileupload('add', param2);
+ });
+
+ asyncTest('send', function () {
+ expect(3);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ send: function (e, data) {
+ strictEqual(
+ data.files[0].name,
+ 'test',
+ 'Triggers send callback'
+ );
+ }
+ }).fileupload('send', param).fail(function () {
+ ok(true, 'Allows to abort the request');
+ }).complete(function () {
+ ok(true, 'Returns a jqXHR object');
+ start();
+ }).abort();
+ });
+
+ module('Callbacks', lifecycle);
+
+ asyncTest('add', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ add: function (e, data) {
+ ok(true, 'Triggers add callback');
+ start();
+ }
+ }).fileupload('add', param);
+ });
+
+ asyncTest('submit', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ submit: function (e, data) {
+ ok(true, 'Triggers submit callback');
+ start();
+ return false;
+ }
+ }).fileupload('add', param);
+ });
+
+ asyncTest('send', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ send: function (e, data) {
+ ok(true, 'Triggers send callback');
+ start();
+ return false;
+ }
+ }).fileupload('send', param);
+ });
+
+ asyncTest('done', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ done: function (e, data) {
+ ok(true, 'Triggers done callback');
+ start();
+ }
+ }).fileupload('send', param);
+ });
+
+ asyncTest('fail', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]},
+ fu = $('#fileupload').fileupload({
+ url: '404',
+ fail: function (e, data) {
+ ok(true, 'Triggers fail callback');
+ start();
+ }
+ });
+ fu.data('fileupload')._isXHRUpload = function () {
+ return true;
+ };
+ fu.fileupload('send', param);
+ });
+
+ asyncTest('always', function () {
+ expect(2);
+ var param = {files: [{name: 'test'}]},
+ counter = 0,
+ fu = $('#fileupload').fileupload({
+ always: function (e, data) {
+ ok(true, 'Triggers always callback');
+ if (counter === 1) {
+ start();
+ } else {
+ counter += 1;
+ }
+ }
+ });
+ fu.data('fileupload')._isXHRUpload = function () {
+ return true;
+ };
+ fu.fileupload('add', param).fileupload(
+ 'option',
+ 'url',
+ '404'
+ ).fileupload('add', param);
+ });
+
+ asyncTest('progress', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]},
+ counter = 0;
+ $('#fileupload').fileupload({
+ forceIframeTransport: true,
+ progress: function (e, data) {
+ ok(true, 'Triggers progress callback');
+ if (counter === 0) {
+ start();
+ } else {
+ counter += 1;
+ }
+ }
+ }).fileupload('send', param);
+ });
+
+ asyncTest('progressall', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]},
+ counter = 0;
+ $('#fileupload').fileupload({
+ forceIframeTransport: true,
+ progressall: function (e, data) {
+ ok(true, 'Triggers progressall callback');
+ if (counter === 0) {
+ start();
+ } else {
+ counter += 1;
+ }
+ }
+ }).fileupload('send', param);
+ });
+
+ asyncTest('start', function () {
+ expect(1);
+ var param = {files: [{name: '1'}, {name: '2'}]},
+ active = 0;
+ $('#fileupload').fileupload({
+ send: function (e, data) {
+ active += 1;
+ },
+ start: function (e, data) {
+ ok(!active, 'Triggers start callback before uploads');
+ start();
+ }
+ }).fileupload('send', param);
+ });
+
+ asyncTest('stop', function () {
+ expect(1);
+ var param = {files: [{name: '1'}, {name: '2'}]},
+ active = 0;
+ $('#fileupload').fileupload({
+ send: function (e, data) {
+ active += 1;
+ },
+ always: function (e, data) {
+ active -= 1;
+ },
+ stop: function (e, data) {
+ ok(!active, 'Triggers stop callback after uploads');
+ start();
+ }
+ }).fileupload('send', param);
+ });
+
+ test('change', function () {
+ var fu = $('#fileupload').fileupload(),
+ fuo = fu.data('fileupload'),
+ fileInput = fu.fileupload('option', 'fileInput');
+ expect(2);
+ fu.fileupload({
+ change: function (e, data) {
+ ok(true, 'Triggers change callback');
+ strictEqual(
+ data.files.length,
+ 0,
+ 'Returns empty files list'
+ );
+ },
+ add: $.noop
+ });
+ fuo._onChange({
+ data: {fileupload: fuo},
+ target: fileInput[0]
+ });
+ });
+
+ test('paste', function () {
+ var fu = $('#fileupload').fileupload(),
+ fuo = fu.data('fileupload');
+ expect(1);
+ fu.fileupload({
+ paste: function (e, data) {
+ ok(true, 'Triggers paste callback');
+ },
+ add: $.noop
+ });
+ fuo._onPaste({
+ data: {fileupload: fuo},
+ originalEvent: {clipboardData: {}},
+ preventDefault: $.noop
+ });
+ });
+
+ test('drop', function () {
+ var fu = $('#fileupload').fileupload(),
+ fuo = fu.data('fileupload');
+ expect(1);
+ fu.fileupload({
+ drop: function (e, data) {
+ ok(true, 'Triggers drop callback');
+ },
+ add: $.noop
+ });
+ fuo._onDrop({
+ data: {fileupload: fuo},
+ originalEvent: {dataTransfer: {}},
+ preventDefault: $.noop
+ });
+ });
+
+ test('dragover', function () {
+ var fu = $('#fileupload').fileupload(),
+ fuo = fu.data('fileupload');
+ expect(1);
+ fu.fileupload({
+ dragover: function (e, data) {
+ ok(true, 'Triggers dragover callback');
+ },
+ add: $.noop
+ });
+ fuo._onDragOver({
+ data: {fileupload: fuo},
+ originalEvent: {dataTransfer: {}},
+ preventDefault: $.noop
+ });
+ });
+
+ module('Options', lifecycle);
+
+ test('paramName', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ paramName: null,
+ send: function (e, data) {
+ strictEqual(
+ data.paramName[0],
+ data.fileInput.prop('name'),
+ 'Takes paramName from file input field if not set'
+ );
+ return false;
+ }
+ }).fileupload('send', param);
+ });
+
+ test('url', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ url: null,
+ send: function (e, data) {
+ strictEqual(
+ data.url,
+ $(data.fileInput.prop('form')).prop('action'),
+ 'Takes url from form action if not set'
+ );
+ return false;
+ }
+ }).fileupload('send', param);
+ });
+
+ test('type', function () {
+ expect(2);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ type: null,
+ send: function (e, data) {
+ strictEqual(
+ data.type,
+ 'POST',
+ 'Request type is "POST" if not set to "PUT"'
+ );
+ return false;
+ }
+ }).fileupload('send', param);
+ $('#fileupload').fileupload({
+ type: 'PUT',
+ send: function (e, data) {
+ strictEqual(
+ data.type,
+ 'PUT',
+ 'Request type is "PUT" if set to "PUT"'
+ );
+ return false;
+ }
+ }).fileupload('send', param);
+ });
+
+ test('replaceFileInput', function () {
+ var fu = $('#fileupload').fileupload(),
+ fuo = fu.data('fileupload'),
+ fileInput = fu.fileupload('option', 'fileInput'),
+ fileInputElement = fileInput[0];
+ expect(2);
+ fu.fileupload({
+ replaceFileInput: false,
+ change: function (e, data) {
+ strictEqual(
+ fu.fileupload('option', 'fileInput')[0],
+ fileInputElement,
+ 'Keeps file input with replaceFileInput: false'
+ );
+ },
+ add: $.noop
+ });
+ fuo._onChange({
+ data: {fileupload: fuo},
+ target: fileInput[0]
+ });
+ fu.fileupload({
+ replaceFileInput: true,
+ change: function (e, data) {
+ notStrictEqual(
+ fu.fileupload('option', 'fileInput')[0],
+ fileInputElement,
+ 'Replaces file input with replaceFileInput: true'
+ );
+ },
+ add: $.noop
+ });
+ fuo._onChange({
+ data: {fileupload: fuo},
+ target: fileInput[0]
+ });
+ });
+
+ asyncTest('forceIframeTransport', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ forceIframeTransport: true,
+ done: function (e, data) {
+ strictEqual(
+ data.dataType.substr(0, 6),
+ 'iframe',
+ 'Iframe Transport is used'
+ );
+ start();
+ }
+ }).fileupload('send', param);
+ });
+
+ test('singleFileUploads', function () {
+ expect(3);
+ var fu = $('#fileupload').fileupload(),
+ param = {files: [{name: '1'}, {name: '2'}]},
+ index = 1;
+ fu.data('fileupload')._isXHRUpload = function () {
+ return true;
+ };
+ $('#fileupload').fileupload({
+ singleFileUploads: true,
+ add: function (e, data) {
+ ok(true, 'Triggers callback number ' + index.toString());
+ index += 1;
+ }
+ }).fileupload('add', param).fileupload(
+ 'option',
+ 'singleFileUploads',
+ false
+ ).fileupload('add', param);
+ });
+
+ test('limitMultiFileUploads', function () {
+ expect(3);
+ var fu = $('#fileupload').fileupload(),
+ param = {files: [
+ {name: '1'},
+ {name: '2'},
+ {name: '3'},
+ {name: '4'},
+ {name: '5'}
+ ]},
+ index = 1;
+ fu.data('fileupload')._isXHRUpload = function () {
+ return true;
+ };
+ $('#fileupload').fileupload({
+ singleFileUploads: false,
+ limitMultiFileUploads: 2,
+ add: function (e, data) {
+ ok(true, 'Triggers callback number ' + index.toString());
+ index += 1;
+ }
+ }).fileupload('add', param);
+ });
+
+ asyncTest('sequentialUploads', function () {
+ expect(6);
+ var param = {files: [
+ {name: '1'},
+ {name: '2'},
+ {name: '3'},
+ {name: '4'},
+ {name: '5'},
+ {name: '6'}
+ ]},
+ addIndex = 0,
+ sendIndex = 0,
+ loadIndex = 0,
+ fu = $('#fileupload').fileupload({
+ sequentialUploads: true,
+ add: function (e, data) {
+ addIndex += 1;
+ if (addIndex === 4) {
+ data.submit().abort();
+ } else {
+ data.submit();
+ }
+ },
+ send: function (e, data) {
+ sendIndex += 1;
+ },
+ done: function (e, data) {
+ loadIndex += 1;
+ strictEqual(sendIndex, loadIndex, 'upload in order');
+ },
+ fail: function (e, data) {
+ strictEqual(data.errorThrown, 'abort', 'upload aborted');
+ },
+ stop: function (e) {
+ start();
+ }
+ });
+ fu.data('fileupload')._isXHRUpload = function () {
+ return true;
+ };
+ fu.fileupload('add', param);
+ });
+
+ asyncTest('limitConcurrentUploads', function () {
+ expect(12);
+ var param = {files: [
+ {name: '1'},
+ {name: '2'},
+ {name: '3'},
+ {name: '4'},
+ {name: '5'},
+ {name: '6'},
+ {name: '7'},
+ {name: '8'},
+ {name: '9'},
+ {name: '10'},
+ {name: '11'},
+ {name: '12'}
+ ]},
+ addIndex = 0,
+ sendIndex = 0,
+ loadIndex = 0,
+ fu = $('#fileupload').fileupload({
+ limitConcurrentUploads: 3,
+ add: function (e, data) {
+ addIndex += 1;
+ if (addIndex === 4) {
+ data.submit().abort();
+ } else {
+ data.submit();
+ }
+ },
+ send: function (e, data) {
+ sendIndex += 1;
+ },
+ done: function (e, data) {
+ loadIndex += 1;
+ ok(sendIndex - loadIndex < 3);
+ },
+ fail: function (e, data) {
+ strictEqual(data.errorThrown, 'abort', 'upload aborted');
+ },
+ stop: function (e) {
+ start();
+ }
+ });
+ fu.data('fileupload')._isXHRUpload = function () {
+ return true;
+ };
+ fu.fileupload('add', param);
+ });
+
+ if ($.support.xhrFileUpload) {
+ asyncTest('multipart', function () {
+ expect(4);
+ var param = {files: [{
+ name: 'test.png',
+ size: 123,
+ type: 'image/png'
+ }]},
+ fu = $('#fileupload').fileupload({
+ multipart: false,
+ always: function (e, data) {
+ strictEqual(
+ data.contentType,
+ param.files[0].type,
+ 'non-multipart upload sets file type as contentType'
+ );
+ strictEqual(
+ data.headers['X-File-Name'],
+ param.files[0].name,
+ 'non-multipart upload sets X-File-Name header'
+ );
+ strictEqual(
+ data.headers['X-File-Type'],
+ param.files[0].type,
+ 'non-multipart upload sets X-File-Type header'
+ );
+ strictEqual(
+ data.headers['X-File-Size'],
+ param.files[0].size,
+ 'non-multipart upload sets X-File-Size header'
+ );
+ start();
+ }
+ });
+ fu.fileupload('send', param);
+ });
+ }
+
+ module('UI Initialization', lifecycleUI);
+
+ test('Widget initialization', function () {
+ ok($('#fileupload').fileupload().data('fileupload'));
+ ok(
+ $('#fileupload').fileupload('option', 'uploadTemplate').length,
+ 'Initialized upload template'
+ );
+ ok(
+ $('#fileupload').fileupload('option', 'downloadTemplate').length,
+ 'Initialized download template'
+ );
+ });
+
+ test('Buttonbar event listeners', function () {
+ var buttonbar = $('#fileupload .fileupload-buttonbar'),
+ files = [{name: 'test'}];
+ expect(7);
+ $('#fileupload').fileupload({
+ send: function (e, data) {
+ ok(true, 'Started file upload via global start button');
+ },
+ fail: function (e, data) {
+ ok(true, 'Canceled file upload via global cancel button');
+ data.context.remove();
+ },
+ destroy: function (e, data) {
+ ok(true, 'Delete action called via global delete button');
+ }
+ });
+ ok(
+ buttonbar.find('.start')
+ .data('events').click.length,
+ 'Listens to start button click events'
+ );
+ ok(
+ buttonbar.find('.cancel')
+ .data('events').click.length,
+ 'Listens to cancel button click events'
+ );
+ ok(
+ buttonbar.find('.delete')
+ .data('events').click.length,
+ 'Listens to delete button click events'
+ );
+ $('#fileupload').fileupload('add', {files: files});
+ buttonbar.find('.cancel').click();
+ $('#fileupload').fileupload('add', {files: files});
+ buttonbar.find('.start').click();
+ buttonbar.find('.cancel').click();
+ $('#fileupload').data('fileupload')._renderDownload(files)
+ .appendTo($('#fileupload .files')).show()
+ .find('.delete input').click();
+ buttonbar.find('.delete').click();
+ });
+
+ module('UI API', lifecycleUI);
+
+ test('destroy', function () {
+ var buttonbar = $('#fileupload .fileupload-buttonbar');
+ $('#fileupload').fileupload();
+ buttonbar.find('button').click($.noop);
+ $('#fileupload').fileupload('destroy');
+ strictEqual(
+ buttonbar.find('.start').data('events').click.length,
+ 1,
+ 'Removes own start button click event listener'
+ );
+ strictEqual(
+ buttonbar.find('.cancel').data('events').click.length,
+ 1,
+ 'Removes own cancel button click event listener'
+ );
+ strictEqual(
+ buttonbar.find('.delete').data('events').click.length,
+ 1,
+ 'Removes own delete button click event listener'
+ );
+ });
+
+ test('disable', function () {
+ var buttonbar = $('#fileupload .fileupload-buttonbar');
+ $('#fileupload').fileupload();
+ $('#fileupload').fileupload('disable');
+ strictEqual(
+ buttonbar.find('input[type=file], button').not(':disabled').length,
+ 0,
+ 'Disables the buttonbar buttons'
+ );
+ });
+
+ test('enable', function () {
+ var buttonbar = $('#fileupload .fileupload-buttonbar');
+ $('#fileupload')
+ .fileupload()
+ .fileupload('disable')
+ .fileupload('enable');
+ strictEqual(
+ buttonbar.find('input[type=file], button').not(':disabled').length,
+ 4,
+ 'Enables the buttonbar buttons'
+ );
+ });
+
+ module('UI Callbacks', lifecycleUI);
+
+ test('destroy', function () {
+ expect(3);
+ $('#fileupload').fileupload({
+ destroy: function (e, data) {
+ ok(true, 'Triggers destroy callback');
+ strictEqual(
+ data.url,
+ 'test',
+ 'Passes over deletion url parameter'
+ );
+ strictEqual(
+ data.type,
+ 'DELETE',
+ 'Passes over deletion request type parameter'
+ );
+ }
+ });
+ $('#fileupload').data('fileupload')._renderDownload([{
+ name: 'test',
+ delete_url: 'test',
+ delete_type: 'DELETE'
+ }]).appendTo($('#fileupload .files')).show()
+ .find('.delete input').click();
+ $('#fileupload .fileupload-buttonbar .delete').click();
+ });
+
+ asyncTest('added', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ added: function (e, data) {
+ start();
+ strictEqual(
+ data.files[0].name,
+ param.files[0].name,
+ 'Triggers added callback'
+ );
+ },
+ send: function () {
+ return false;
+ }
+ }).fileupload('add', param);
+ });
+
+ asyncTest('started', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ started: function (e) {
+ start();
+ ok('Triggers started callback');
+ return false;
+ },
+ sent: function (e, data) {
+ return false;
+ }
+ }).fileupload('send', param);
+ });
+
+ asyncTest('sent', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ sent: function (e, data) {
+ start();
+ strictEqual(
+ data.files[0].name,
+ param.files[0].name,
+ 'Triggers sent callback'
+ );
+ return false;
+ }
+ }).fileupload('send', param);
+ });
+
+ asyncTest('completed', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ completed: function (e, data) {
+ start();
+ ok('Triggers completed callback');
+ return false;
+ }
+ }).fileupload('send', param);
+ });
+
+ asyncTest('failed', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ failed: function (e, data) {
+ start();
+ ok('Triggers failed callback');
+ return false;
+ }
+ }).fileupload('send', param).abort();
+ });
+
+ asyncTest('stopped', function () {
+ expect(1);
+ var param = {files: [{name: 'test'}]};
+ $('#fileupload').fileupload({
+ stopped: function (e, data) {
+ start();
+ ok('Triggers stopped callback');
+ return false;
+ }
+ }).fileupload('send', param);
+ });
+
+ asyncTest('destroyed', function () {
+ expect(1);
+ $('#fileupload').fileupload({
+ destroyed: function (e, data) {
+ start();
+ ok(true, 'Triggers destroyed callback');
+ }
+ });
+ $('#fileupload').data('fileupload')._renderDownload([{
+ name: 'test',
+ delete_url: 'test',
+ delete_type: 'DELETE'
+ }]).appendTo($('#fileupload .files')).show()
+ .find('.delete input').click();
+ $('#fileupload .fileupload-buttonbar .delete').click();
+ });
+
+ module('UI Options', lifecycleUI);
+
+ test('autoUpload', function () {
+ expect(1);
+ $('#fileupload')
+ .fileupload({
+ autoUpload: true,
+ send: function (e, data) {
+ ok(true, 'Started file upload automatically');
+ return false;
+ }
+ })
+ .fileupload('add', {files: [{name: 'test'}]})
+ .fileupload('option', 'autoUpload', false)
+ .fileupload('add', {files: [{name: 'test'}]});
+ });
+
+ test('maxNumberOfFiles', function () {
+ expect(4);
+ var addIndex = 0,
+ sendIndex = 0;
+ $('#fileupload')
+ .fileupload({
+ autoUpload: true,
+ maxNumberOfFiles: 1,
+ singleFileUploads: false,
+ send: function (e, data) {
+ strictEqual(
+ sendIndex += 1,
+ addIndex
+ );
+ },
+ progress: $.noop,
+ progressall: $.noop,
+ done: $.noop,
+ stop: $.noop
+ })
+ .fileupload('add', {files: [{name: (addIndex += 1)}]})
+ .fileupload('add', {files: [{name: 'test'}]})
+ .fileupload('option', 'maxNumberOfFiles', 1)
+ .fileupload('add', {files: [{name: 1}, {name: 2}]})
+ .fileupload({
+ maxNumberOfFiles: 1,
+ send: function (e, data) {
+ strictEqual(
+ sendIndex += 1,
+ addIndex
+ );
+ return false;
+ }
+ })
+ .fileupload('add', {files: [{name: (addIndex += 1)}]})
+ .fileupload('add', {files: [{name: (addIndex += 1)}]})
+ .fileupload({
+ maxNumberOfFiles: 0,
+ send: function (e, data) {
+ ok(
+ !$.blueimpUI.fileupload.prototype.options
+ .send.call(this, e, data)
+ );
+ return false;
+ }
+ })
+ .fileupload('send', {files: [{name: 'test'}]});
+ });
+
+ test('maxFileSize', function () {
+ expect(3);
+ var addIndex = 0,
+ sendIndex = 0;
+ $('#fileupload')
+ .fileupload({
+ autoUpload: true,
+ maxFileSize: 1000,
+ send: function (e, data) {
+ strictEqual(
+ sendIndex += 1,
+ addIndex
+ );
+ return false;
+ }
+ })
+ .fileupload('add', {files: [{
+ name: (addIndex += 1)
+ }]})
+ .fileupload('add', {files: [{
+ name: (addIndex += 1),
+ size: 999
+ }]})
+ .fileupload('add', {files: [{
+ name: 'test',
+ size: 1001
+ }]})
+ .fileupload({
+ send: function (e, data) {
+ ok(
+ !$.blueimpUI.fileupload.prototype.options
+ .send.call(this, e, data)
+ );
+ return false;
+ }
+ })
+ .fileupload('send', {files: [{
+ name: 'test',
+ size: 1001
+ }]});
+ });
+
+ test('minFileSize', function () {
+ expect(3);
+ var addIndex = 0,
+ sendIndex = 0;
+ $('#fileupload')
+ .fileupload({
+ autoUpload: true,
+ minFileSize: 1000,
+ send: function (e, data) {
+ strictEqual(
+ sendIndex += 1,
+ addIndex
+ );
+ return false;
+ }
+ })
+ .fileupload('add', {files: [{
+ name: (addIndex += 1)
+ }]})
+ .fileupload('add', {files: [{
+ name: (addIndex += 1),
+ size: 1001
+ }]})
+ .fileupload('add', {files: [{
+ name: 'test',
+ size: 999
+ }]})
+ .fileupload({
+ send: function (e, data) {
+ ok(
+ !$.blueimpUI.fileupload.prototype.options
+ .send.call(this, e, data)
+ );
+ return false;
+ }
+ })
+ .fileupload('send', {files: [{
+ name: 'test',
+ size: 999
+ }]});
+ });
+
+ test('acceptFileTypes', function () {
+ expect(3);
+ var addIndex = 0,
+ sendIndex = 0;
+ $('#fileupload')
+ .fileupload({
+ autoUpload: true,
+ acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
+ previewFileTypes: /none/,
+ send: function (e, data) {
+ strictEqual(
+ sendIndex += 1,
+ addIndex
+ );
+ return false;
+ }
+ })
+ .fileupload('add', {files: [{
+ name: (addIndex += 1) + '.jpg'
+ }]})
+ .fileupload('add', {files: [{
+ name: (addIndex += 1),
+ type: 'image/jpeg'
+ }]})
+ .fileupload('add', {files: [{
+ name: 'test.txt',
+ type: 'text/plain'
+ }]})
+ .fileupload({
+ send: function (e, data) {
+ ok(
+ !$.blueimpUI.fileupload.prototype.options
+ .send.call(this, e, data)
+ );
+ return false;
+ }
+ })
+ .fileupload('send', {files: [{
+ name: 'test.txt',
+ type: 'text/plain'
+ }]});
+ });
+
+ test('acceptFileTypes as HTML5 data attribute', function () {
+ expect(2);
+ var regExp = /(\.|\/)(gif|jpe?g|png)$/i;
+ $('#fileupload')
+ .attr('data-accept-file-types', regExp.toString())
+ .fileupload();
+ strictEqual(
+ $.type($('#fileupload').fileupload('option', 'acceptFileTypes')),
+ $.type(regExp)
+ );
+ strictEqual(
+ $('#fileupload').fileupload('option', 'acceptFileTypes').toString(),
+ regExp.toString()
+ );
+ });
+
+});
diff --git a/mod/lightpics/version.php b/mod/lightpics/version.php
new file mode 100644
index 000000000..68817c053
--- /dev/null
+++ b/mod/lightpics/version.php
@@ -0,0 +1,7 @@
+<?php
+/**
+ * Version of this plugin.
+ * Used for the upgrade system.
+ */
+
+$version = 2012020901;
diff --git a/mod/lightpics/views/default/admin/settings/photos.php b/mod/lightpics/views/default/admin/settings/photos.php
new file mode 100644
index 000000000..cc3592f37
--- /dev/null
+++ b/mod/lightpics/views/default/admin/settings/photos.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Admin page
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$tab = get_input('tab', 'settings');
+
+echo elgg_view('navigation/tabs', array(
+ 'tabs' => array(
+ array(
+ 'text' => elgg_echo('settings'),
+ 'href' => '/admin/settings/photos',
+ 'selected' => ($tab == 'settings'),
+ ),
+ array(
+ 'text' => elgg_echo('tidypics:server_info'),
+ 'href' => '/admin/settings/photos?tab=server_info',
+ 'selected' => ($tab == 'server_info'),
+ ),
+ array(
+ 'text' => elgg_echo('tidypics:server_config'),
+ 'href' => '/admin/settings/photos?tab=server_config',
+ 'selected' => ($tab == 'server_config'),
+ ),
+ array(
+ 'text' => 'ImageMagick',
+ 'href' => '/admin/settings/photos?tab=image_lib',
+ 'selected' => ($tab == 'image_lib'),
+ ),
+ array(
+ 'text' => elgg_echo('tidypics:settings:thumbnail'),
+ 'href' => '/admin/settings/photos?tab=thumbnail',
+ 'selected' => ($tab == 'thumbnail'),
+ ),
+ array(
+ 'text' => elgg_echo('tidypics:settings:help'),
+ 'href' => '/admin/settings/photos?tab=help',
+ 'selected' => ($tab == 'help'),
+ ),
+ )
+));
+
+switch ($tab) {
+ case 'server_info':
+ echo elgg_view('admin/settings/photos/server_info');
+ break;
+
+ case 'server_config':
+ echo elgg_view('admin/settings/photos/server_config');
+ break;
+
+ case 'image_lib':
+ echo elgg_view('admin/settings/photos/image_lib');
+ break;
+
+ case 'thumbnail':
+ echo elgg_view('admin/settings/photos/thumbnail');
+ break;
+
+ case 'help':
+ echo elgg_view('admin/settings/photos/help');
+ break;
+
+ default:
+ case 'settings':
+ echo elgg_view('admin/settings/photos/settings');
+ break;
+}
diff --git a/mod/lightpics/views/default/admin/settings/photos/help.php b/mod/lightpics/views/default/admin/settings/photos/help.php
new file mode 100644
index 000000000..1e5bdd254
--- /dev/null
+++ b/mod/lightpics/views/default/admin/settings/photos/help.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * Tidypics Help
+ *
+ */
+
+elgg_load_library('elgg:markdown');
+
+$faq = elgg_get_plugins_path() . 'lightpics/FAQ.txt';
+$text = Markdown(file_get_contents($faq));
+
+$content = "<div class=\"elgg-markdown\">$text</div>";
+
+echo elgg_view_module('inline', elgg_echo('tidypics:settings:help'), $content);
diff --git a/mod/lightpics/views/default/admin/settings/photos/image_lib.php b/mod/lightpics/views/default/admin/settings/photos/image_lib.php
new file mode 100644
index 000000000..6e519b607
--- /dev/null
+++ b/mod/lightpics/views/default/admin/settings/photos/image_lib.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Test the location of ImageMagick
+ */
+
+$content .= '<p>' . elgg_echo('tidypics:lib_tools:testing') . '</p>';
+$content .= '<p><label>' . elgg_echo('tidypics:settings:im_path');
+$content .= elgg_view('input/text', array(
+ 'name' => 'im_location'
+));
+$content .= '</p><p>';
+$content .= elgg_view('input/submit', array(
+ 'value' => elgg_echo('submit'),
+ 'id' => 'tidypics-im-test'
+));
+$content .= '</p>';
+$content .= '<p id="tidypics-im-results"></p>';
+
+echo elgg_view_module('inline', 'ImageMagick', $content);
+
+?>
+<script type="text/javascript">
+ $(function() {
+ $('#tidypics-im-test').click(function() {
+ var loc = $('input[name=im_location]').val();
+ $("#tidypics-im-results").html("");
+ $.ajax({
+ type: "GET",
+ url: elgg.normalize_url('mod/tidypics/actions/photos/admin/imtest.php'),
+ data: {location: loc},
+ cache: false,
+ success: function(html){
+ $("#tidypics-im-results").html(html);
+ }
+ });
+ });
+ });
+</script>
diff --git a/mod/lightpics/views/default/admin/settings/photos/server_config.php b/mod/lightpics/views/default/admin/settings/photos/server_config.php
new file mode 100644
index 000000000..b4fe552ec
--- /dev/null
+++ b/mod/lightpics/views/default/admin/settings/photos/server_config.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * Tidypics server configuration
+ *
+ */
+
+elgg_load_library('elgg:markdown');
+
+$faq = elgg_get_plugins_path() . 'lightpics/CONFIG.txt';
+$text = Markdown(file_get_contents($faq));
+
+$content = "<div class=\"elgg-markdown\">$text</div>";
+
+echo elgg_view_module('inline', elgg_echo('tidypics:server_config'), $content);
diff --git a/mod/lightpics/views/default/admin/settings/photos/server_info.php b/mod/lightpics/views/default/admin/settings/photos/server_info.php
new file mode 100644
index 000000000..c100b0de2
--- /dev/null
+++ b/mod/lightpics/views/default/admin/settings/photos/server_info.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * Tidypics server analysis
+ */
+
+function tp_readable_size($bytes) {
+ if (strpos($bytes, 'M')) {
+ return $bytes . 'B';
+ }
+
+ $size = $bytes / 1024;
+ if ($size < 1024) {
+ $size = number_format($size, 2);
+ $size .= ' KB';
+ } else {
+ $size = $size / 1024;
+ if ($size < 1024) {
+ $size = number_format($size, 2);
+ $size .= ' MB';
+ } else {
+ $size = $size / 1024;
+ $size = number_format($size, 2);
+ $size .= ' GB';
+ }
+ }
+ return $size;
+}
+
+$disablefunc = explode(',', ini_get('disable_functions'));
+$exec_avail = elgg_echo('tidypics:disabled');
+if (is_callable('exec') && !in_array('exec',$disablefunc)) {
+ $exec_avail = elgg_echo('tidypics:enabled');
+}
+
+ob_start();
+
+?>
+<table class="elgg-table-alt">
+ <tr>
+ <td><?php echo elgg_echo('tidypics:server_info:php_version'); ?></td>
+ <td><?php echo phpversion(); ?></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>GD</td>
+ <td><?php echo (extension_loaded('gd')) ? elgg_echo('tidypics:enabled') : elgg_echo('tidypics:disabled'); ?></td>
+ <td><?php echo elgg_echo('tidypics:server_info:gd_desc'); ?></td>
+ </tr>
+ <tr>
+ <td>imagick</td>
+ <td><?php echo (extension_loaded('imagick')) ? elgg_echo('tidypics:enabled') : elgg_echo('tidypics:disabled'); ?></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>exec()</td>
+ <td><?php echo $exec_avail; ?></td>
+ <td><?php echo elgg_echo('tidypics:server_info:exec_desc'); ?></td>
+ </tr>
+ <tr>
+ <td><?php echo elgg_echo('tidypics:server_info:memory_limit'); ?></td>
+ <td><?php echo tp_readable_size(ini_get('memory_limit')); ?></td>
+ <td><?php echo elgg_echo('tidypics:server_info:memory_limit_desc'); ?></td>
+ </tr>
+ <tr>
+ <td><?php echo elgg_echo('tidypics:server_info:peak_usage'); ?></td>
+ <td><?php if (function_exists('memory_get_peak_usage')) echo tp_readable_size(memory_get_peak_usage()); ?></td>
+ <td><?php echo elgg_echo('tidypics:server_info:peak_usage_desc'); ?></td>
+ </tr>
+ <tr>
+ <td><?php echo elgg_echo('tidypics:server_info:upload_max_filesize'); ?></td>
+ <td><?php echo tp_readable_size(ini_get('upload_max_filesize')); ?></td>
+ <td><?php echo elgg_echo('tidypics:server_info:upload_max_filesize_desc'); ?></td>
+ </tr>
+ <tr>
+ <td><?php echo elgg_echo('tidypics:server_info:post_max_size'); ?></td>
+ <td><?php echo tp_readable_size(ini_get('post_max_size')); ?></td>
+ <td><?php echo elgg_echo('tidypics:server_info:post_max_size_desc'); ?></td>
+ </tr>
+ <tr>
+ <td><?php echo elgg_echo('tidypics:server_info:max_input_time'); ?></td>
+ <td><?php echo ini_get('max_input_time'); ?>s</td>
+ <td><?php echo elgg_echo('tidypics:server_info:max_input_time_desc'); ?></td>
+ </tr>
+ <tr>
+ <td><?php echo elgg_echo('tidypics:server_info:max_execution_time'); ?></td>
+ <td><?php echo ini_get('max_execution_time'); ?> s</td>
+ <td><?php echo elgg_echo('tidypics:server_info:max_execution_time_desc'); ?></td>
+ </tr>
+ <tr>
+ <td>GD imagejpeg</td>
+ <td><?php echo (is_callable('imagejpeg')) ? elgg_echo('tidypics:enabled') : elgg_echo('tidypics:disabled'); ?></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>GD imagepng</td>
+ <td><?php echo (is_callable('imagepng')) ? elgg_echo('tidypics:enabled') : elgg_echo('tidypics:disabled'); ?></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>GD imagegif</td>
+ <td><?php echo (is_callable('imagegif')) ? elgg_echo('tidypics:enabled') : elgg_echo('tidypics:disabled'); ?></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>EXIF</td>
+ <td><?php echo (is_callable('exif_read_data')) ? elgg_echo('tidypics:enabled') : elgg_echo('tidypics:disabled'); ?></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td><?php echo elgg_echo('tidypics:server_info:use_only_cookies'); ?></td>
+ <td><?php echo (ini_get('session.use_only_cookies')) ? elgg_echo('tidypics:enabled') : elgg_echo('tidypics:disabled'); ?></td>
+ <td><?php echo elgg_echo('tidypics:server_info:use_only_cookies_desc'); ?></td>
+ </tr>
+</table>
+
+<?php
+
+$content = ob_get_clean();
+
+echo elgg_view_module('inline', elgg_echo('tidypics:server_info'), $content); \ No newline at end of file
diff --git a/mod/lightpics/views/default/admin/settings/photos/settings.php b/mod/lightpics/views/default/admin/settings/photos/settings.php
new file mode 100644
index 000000000..6ea444392
--- /dev/null
+++ b/mod/lightpics/views/default/admin/settings/photos/settings.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Tidypics settings
+ */
+
+if (tidypics_is_upgrade_available()) {
+ echo '<div class="elgg-admin-notices">';
+ echo '<p>';
+ echo elgg_view('output/url', array(
+ 'text' => elgg_echo('tidypics:upgrade'),
+ 'href' => 'action/photos/admin/upgrade',
+ 'is_action' => true,
+ ));
+ echo '</p>';
+ echo '</div>';
+}
+
+echo elgg_view_form('photos/admin/settings');
diff --git a/mod/lightpics/views/default/admin/settings/photos/thumbnail.php b/mod/lightpics/views/default/admin/settings/photos/thumbnail.php
new file mode 100644
index 000000000..5f9769e91
--- /dev/null
+++ b/mod/lightpics/views/default/admin/settings/photos/thumbnail.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Tidypics thumbnail creation tool
+ */
+
+$title = elgg_echo('tidypics:settings:thumbnail');
+$body = '<p>' . elgg_echo('tidypics:thumbnail_tool_blurb') . '</p>';
+$im_id = elgg_echo('tidypics:settings:im_id');
+$input = elgg_view('input/text', array(
+ 'name' => 'image_id'
+));
+$submit = elgg_view('input/submit', array(
+ 'value' => elgg_echo('submit'),
+ 'id' => 'elgg-tidypics-im-test'
+));
+
+$body .=<<<HTML
+ <p>
+ <label>$im_id $input</label>
+ </p>
+ <p>
+ $submit
+ <div id="elgg-tidypics-im-results"></div>
+ </p>
+HTML;
+
+echo elgg_view_module('inline', $title, $body);
+
+?>
+
+<script type="text/javascript">
+ $(function() {
+ $('#elgg-tidypics-im-test').click(function() {
+ var image_id = $('input[name=image_id]').val();
+ $("#elgg-tidypics-im-results").html('<div class="elgg-ajax-loader"></div>');
+ elgg.action('photos/admin/create_thumbnails', {
+ format: 'JSON',
+ data: {guid: image_id},
+ cache: false,
+ success: function(result) {
+ // error
+ if (result.status < 0) {
+ var html = '';
+ } else {
+ var html = '<img class="elgg-photo tidypics-photo" src="'
+ + result.output.thumbnail_src + '" alt="' + result.output.title
+ + '" />';
+ }
+ $("#elgg-tidypics-im-results").html(html);
+ }
+ });
+ });
+ });
+</script> \ No newline at end of file
diff --git a/mod/lightpics/views/default/forms/photos/admin/settings.php b/mod/lightpics/views/default/forms/photos/admin/settings.php
new file mode 100644
index 000000000..e0b8a9880
--- /dev/null
+++ b/mod/lightpics/views/default/forms/photos/admin/settings.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Tidypics admin settings form body
+ *
+ * @todo remove original image, group only upload not delete
+ */
+
+$plugin = elgg_get_plugin_from_id('lightpics');
+
+$title = elgg_echo('tidypics:settings:main');
+$content = elgg_view('forms/photos/admin/settings/main', array('plugin' => $plugin));
+echo elgg_view_module('inline', $title, $content);
+
+$title = elgg_echo('tidypics:settings:heading:img_lib');
+$content = elgg_view('forms/photos/admin/settings/image_lib', array('plugin' => $plugin));
+echo elgg_view_module('inline', $title, $content);
+
+$title = elgg_echo('tidypics:settings:heading:river');
+$content = elgg_view('forms/photos/admin/settings/activity', array('plugin' => $plugin));
+echo elgg_view_module('inline', $title, $content);
+
+$title = elgg_echo('tidypics:settings:heading:sizes');
+$content = elgg_view('forms/photos/admin/settings/thumbnails', array('plugin' => $plugin));
+echo elgg_view_module('inline', $title, $content);
+
+echo elgg_view('input/submit', array('value' => elgg_echo("save")));
diff --git a/mod/lightpics/views/default/forms/photos/admin/settings/activity.php b/mod/lightpics/views/default/forms/photos/admin/settings/activity.php
new file mode 100644
index 000000000..d62901af6
--- /dev/null
+++ b/mod/lightpics/views/default/forms/photos/admin/settings/activity.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * River integration
+ */
+
+$plugin = $vars['plugin'];
+
+echo '<div>';
+echo elgg_echo('tidypics:settings:img_river_view') . ': ';
+echo elgg_view('input/dropdown', array(
+ 'name' => 'params[img_river_view]',
+ 'options_values' => array(
+ 'all' => elgg_echo('tidypics:option:all'),
+ 'batch' => '1',
+ 'none' => elgg_echo('tidypics:option:none'),
+ ),
+ 'value' => $plugin->img_river_view,
+));
+echo '</div>';
+echo '<div>';
+echo elgg_echo('tidypics:settings:album_river_view') . ': ';
+echo elgg_view('input/dropdown', array(
+ 'name' => 'params[album_river_view]',
+ 'options_values' => array(
+ 'cover' => elgg_echo('tidypics:option:cover'),
+ 'set' => elgg_echo('tidypics:option:set'),
+ ),
+ 'value' => $plugin->album_river_view,
+));
+echo '</div>';
+
diff --git a/mod/lightpics/views/default/forms/photos/admin/settings/image_lib.php b/mod/lightpics/views/default/forms/photos/admin/settings/image_lib.php
new file mode 100644
index 000000000..4e3f7f2a0
--- /dev/null
+++ b/mod/lightpics/views/default/forms/photos/admin/settings/image_lib.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Image library settings
+ */
+
+$plugin = $vars['plugin'];
+
+echo'<div>';
+echo elgg_echo('tidypics:settings:image_lib') . ': ';
+echo elgg_view('input/dropdown', array(
+ 'name' => 'params[image_lib]',
+ 'options_values' => tidypics_get_image_libraries(),
+ 'value' => $plugin->image_lib,
+));
+echo '</div>';
+echo '<div>';
+echo elgg_echo('tidypics:settings:im_path') . ' ';
+echo elgg_view("input/text", array('name' => 'params[im_path]', 'value' => $plugin->im_path));
+echo '</div>';
+
diff --git a/mod/lightpics/views/default/forms/photos/admin/settings/main.php b/mod/lightpics/views/default/forms/photos/admin/settings/main.php
new file mode 100644
index 000000000..a35c9ab59
--- /dev/null
+++ b/mod/lightpics/views/default/forms/photos/admin/settings/main.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Primary settings for Elgg
+ */
+
+$plugin = $vars['plugin'];
+
+$checkboxes = array('view_count', 'exif', 'download_link');
+foreach ($checkboxes as $checkbox) {
+ echo '<div>';
+ echo elgg_view('input/checkbox', array(
+ 'name' => "params[$checkbox]",
+ 'value' => true,
+ 'checked' => (bool)$plugin->$checkbox,
+ ));
+ echo ' ' . elgg_echo("tidypics:settings:$checkbox");
+ echo '</div>';
+}
+
+// max image size
+echo '<div>';
+echo elgg_echo('tidypics:settings:maxfilesize');
+echo elgg_view('input/text', array(
+ 'name' => 'params[maxfilesize]',
+ 'value' => $plugin->maxfilesize,
+));
+echo '</div>';
+
+// Quota Size
+$quota = $plugin->quota;
+if (!$quota) {
+ $quota = 0;
+}
+echo '<div>' . elgg_echo('tidypics:settings:quota');
+echo elgg_view('input/text', array(
+ 'name' => 'params[quota]',
+ 'value' => $quota,
+));
+echo '</div>';
diff --git a/mod/lightpics/views/default/forms/photos/admin/settings/thumbnails.php b/mod/lightpics/views/default/forms/photos/admin/settings/thumbnails.php
new file mode 100644
index 000000000..ec7a6d1b2
--- /dev/null
+++ b/mod/lightpics/views/default/forms/photos/admin/settings/thumbnails.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Thumbnail sizes
+ */
+
+$plugin = $vars['plugin'];
+
+echo '<span class="elgg-text-help">' . elgg_echo('tidypics:settings:sizes:instructs') . '</span>';
+$image_sizes = unserialize($plugin->image_sizes);
+echo '<table>';
+$sizes = array('large', 'small', 'tiny');
+foreach ($sizes as $size) {
+ echo '<tr>';
+ echo '<td class="pas">';
+ echo elgg_echo("tidypics:settings:{$size}size");
+ echo '</td><td class="pas">';
+ echo 'width: ';
+ echo elgg_view('input/text', array(
+ 'name' => "{$size}_image_width",
+ 'value' => $image_sizes["{$size}_image_width"],
+ 'class' => 'tidypics-input-thin',
+ ));
+ echo '</td><td class="pas">';
+ echo 'height: ';
+ echo elgg_view('input/text', array(
+ 'name' => "{$size}_image_height",
+ 'value' => $image_sizes["{$size}_image_height"],
+ 'class' => 'tidypics-input-thin',
+ ));
+ echo '</td>';
+ echo '</tr>';
+}
+echo '</table>';
diff --git a/mod/lightpics/views/default/forms/photos/album/save.php b/mod/lightpics/views/default/forms/photos/album/save.php
new file mode 100644
index 000000000..8a1202c07
--- /dev/null
+++ b/mod/lightpics/views/default/forms/photos/album/save.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Save album form body
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$title = elgg_extract('title', $vars, '');
+$description = elgg_extract('description', $vars, '');
+$tags = elgg_extract('tags', $vars, '');
+$access_id = elgg_extract('access_id', $vars, get_default_access());
+$container_guid = elgg_extract('container_guid', $vars, elgg_get_page_owner_guid());
+$guid = elgg_extract('guid', $vars, 0);
+
+?>
+
+<div>
+ <label><?php echo elgg_echo('album:title'); ?></label>
+ <?php echo elgg_view('input/text', array('name' => 'title', 'value' => $title)); ?>
+</div>
+<div>
+ <label><?php echo elgg_echo('album:desc'); ?></label>
+ <?php echo elgg_view('input/longtext', array('name' => 'description', 'value' => $description)); ?>
+</div>
+<div>
+ <label><?php echo elgg_echo('tags'); ?></label>
+ <?php echo elgg_view('input/tags', array('name' => 'tags', 'value' => $tags)); ?>
+</div>
+<?php
+
+$categories = elgg_view('input/categories', $vars);
+if ($categories) {
+ echo $categories;
+}
+
+?>
+<div>
+ <label><?php echo elgg_echo('access'); ?></label>
+ <?php echo elgg_view('input/access', array('name' => 'access_id', 'value' => $access_id)); ?>
+</div>
+<div class="elgg-foot">
+<?php
+echo elgg_view('input/hidden', array('name' => 'guid', 'value' => $guid));
+echo elgg_view('input/hidden', array('name' => 'container_guid', 'value' => $container_guid));
+echo elgg_view('input/submit', array('value' => elgg_echo('save')));
+?>
+</div>
diff --git a/mod/lightpics/views/default/forms/photos/album/sort.php b/mod/lightpics/views/default/forms/photos/album/sort.php
new file mode 100644
index 000000000..49bd016aa
--- /dev/null
+++ b/mod/lightpics/views/default/forms/photos/album/sort.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Album sorting view
+ */
+
+$album = $vars['album'];
+$image_guids = $album->getImageList();
+
+echo '<div>';
+echo elgg_echo('tidypics:sort:instruct');
+echo '</div>';
+
+echo '<div>';
+echo elgg_view('input/hidden', array('name' => 'guids'));
+echo elgg_view('input/hidden', array('name' => 'album_guid', 'value' => $album->guid));
+echo elgg_view('input/submit', array('value' => elgg_echo('save')));
+echo '</div>';
+
+echo '<div class="elgg-foot">';
+echo '<ul id="tidypics-sort" class="elgg-gallery">';
+foreach ($image_guids as $image_guid) {
+ $image = get_entity($image_guid);
+ $img = elgg_view('output/img', array(
+ 'src' => $image->getIconURL(),
+ ));
+ echo "<li class=\"mam\" id=\"$image_guid\">$img</li>";
+}
+echo '</ul>';
diff --git a/mod/lightpics/views/default/forms/photos/basic_upload.php b/mod/lightpics/views/default/forms/photos/basic_upload.php
new file mode 100644
index 000000000..9e71cf59e
--- /dev/null
+++ b/mod/lightpics/views/default/forms/photos/basic_upload.php
@@ -0,0 +1,63 @@
+<?php
+$album = $vars['entity'];
+$help = elgg_echo('tidypics:uploader:help');
+
+$input = elgg_view('input/file', array(
+ 'name' => 'images[]',
+ 'multiple' => 'multiple',
+ 'class' => 'hidden-js',
+));
+
+$button = elgg_view('output/url', array(
+ 'text' => elgg_echo('tidypics:uploader:upload') . $input,
+ 'class' => 'elgg-button elgg-button-action fileinput-button',
+));
+
+$reset = elgg_view('input/reset', array(
+ 'value' => elgg_echo('cancel'),
+ 'class' => 'hidden',
+));
+
+$foot = elgg_view('input/hidden', array('name' => 'guid', 'value' => $album->getGUID()));
+$foot .= elgg_view('input/submit', array('value' => elgg_echo("save")));
+
+echo <<<HTML
+<div>
+ $max
+</div>
+<div class="fileinput-container">
+ $button
+ $reset
+ <p class="elgg-text-help">$help</p>
+</div>
+<div class="mtm"><!-- The table listing the files available for upload/download -->
+ <table role="presentation" class="elgg-table-alt clearfloat mtm">
+ <tbody class="files"></tbody>
+ </table>
+</div>
+<div class='elgg-foot'>
+ $foot
+</div>
+HTML;
+
+?>
+
+<noscript><style type="text/css">hidden-nojs {display: hidden}</style></noscript>
+
+<!-- The template to display files available for upload -->
+<script id="template-upload" type="text/x-tmpl">
+{% for (var i=0, file; file=o.files[i]; i++) { %}
+ <tr class="template-upload fade">
+ {% if (file.error) { %}
+ <td class="error"><span class="elgg-message elgg-state-error">{%=locale.fileupload.error%} {%=locale.fileupload.errors[file.error] || file.error%}</span></td>
+ {% } else { %}
+ <td class="preview"><span class="fade"></span></td>
+ {% } %}
+ <td class="name"><span>{%=file.name%}</span></td>
+ <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
+
+ </tr>
+{% } %}
+</script>
+<!-- The template to display files available for download -->
+<script id="template-download" type="text/x-tmpl" />
diff --git a/mod/lightpics/views/default/forms/photos/batch/edit.php b/mod/lightpics/views/default/forms/photos/batch/edit.php
new file mode 100644
index 000000000..d843d8349
--- /dev/null
+++ b/mod/lightpics/views/default/forms/photos/batch/edit.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Edit properties on a batch of images
+ *
+ * @uses $vars['batch'] ElggObject
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$batch = $vars['batch'];
+$album = $batch->getContainerEntity();
+
+$images = elgg_get_entities_from_relationship(array(
+ 'type' => 'object',
+ 'subtype' => 'image',
+ 'relationship' => 'belongs_to_batch',
+ 'relationship_guid' => $batch->getGUID(),
+ 'inverse_relationship' => true,
+ 'limit' => 0
+));
+
+echo '<ul>';
+foreach ($images as $image) {
+ echo '<li>';
+ echo elgg_view('forms/photos/batch/edit/image', array('entity' => $image));
+ echo '</li>';
+}
+echo '</ul>';
+
+echo '<div class="elgg-foot">';
+echo elgg_view('input/submit', array('value' => elgg_echo('save')));
+echo '</div>';
diff --git a/mod/lightpics/views/default/forms/photos/batch/edit/image.php b/mod/lightpics/views/default/forms/photos/batch/edit/image.php
new file mode 100644
index 000000000..eed804e90
--- /dev/null
+++ b/mod/lightpics/views/default/forms/photos/batch/edit/image.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Form component for editing a single image
+ *
+ * @uses $vars['entity']
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$image = $vars['entity'];
+
+echo '<div class="elgg-image-block">';
+
+echo '<div class="elgg-image">';
+echo elgg_view_entity_icon($image, 'small', array('href' => false));
+echo '</div>';
+
+echo '<div class="elgg-body"><fieldset class="mlm">';
+echo '<div><label>' . elgg_echo('album:title') . '</label>';
+echo elgg_view('input/text', array('name' => 'title[]', 'value' => $title,));
+echo '</div>';
+
+echo '<div><label>' . elgg_echo('caption') . '</label>';
+echo elgg_view('input/longtext', array('name' => 'caption[]'));
+echo '</div>';
+
+echo '<div><label>' . elgg_echo("tags") . '</label>';
+echo elgg_view('input/tags', array('name' => 'tags[]'));
+echo '</div>';
+
+echo elgg_view('input/hidden', array('name' => 'guid[]', 'value' => $image->getGUID()));
+echo '<fieldset></div>';
+
+echo '</div>';
diff --git a/mod/lightpics/views/default/forms/photos/image/save.php b/mod/lightpics/views/default/forms/photos/image/save.php
new file mode 100644
index 000000000..47a08640a
--- /dev/null
+++ b/mod/lightpics/views/default/forms/photos/image/save.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Save image form body
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$title = elgg_extract('title', $vars, '');
+$description = elgg_extract('description', $vars, '');
+$tags = elgg_extract('tags', $vars, '');
+$access_id = elgg_extract('access_id', $vars, get_default_access());
+$container_guid = elgg_extract('container_guid', $vars, elgg_get_page_owner_guid());
+$guid = elgg_extract('guid', $vars, 0);
+
+?>
+
+<div>
+ <label><?php echo elgg_echo('album:title'); ?></label>
+ <?php echo elgg_view('input/text', array('name' => 'title', 'value' => $title)); ?>
+</div>
+<div>
+ <label><?php echo elgg_echo('album:desc'); ?></label>
+ <?php echo elgg_view('input/longtext', array('name' => 'description', 'value' => $description)); ?>
+</div>
+<div>
+ <label><?php echo elgg_echo('tags'); ?></label>
+ <?php echo elgg_view('input/tags', array('name' => 'tags', 'value' => $tags)); ?>
+</div>
+<?php
+
+$categories = elgg_view('input/categories', $vars);
+if ($categories) {
+ echo $categories;
+}
+
+?>
+<div>
+ <label><?php echo elgg_echo('access'); ?></label>
+ <?php echo elgg_view('input/access', array('name' => 'access_id', 'value' => $access_id)); ?>
+</div>
+<div class="elgg-foot">
+<?php
+echo elgg_view('input/hidden', array('name' => 'guid', 'value' => $guid));
+echo elgg_view('input/hidden', array('name' => 'container_guid', 'value' => $container_guid));
+echo elgg_view('input/submit', array('value' => elgg_echo('save')));
+?>
+</div>
diff --git a/mod/lightpics/views/default/icon/object/album.php b/mod/lightpics/views/default/icon/object/album.php
new file mode 100644
index 000000000..ddc6a8a56
--- /dev/null
+++ b/mod/lightpics/views/default/icon/object/album.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Image icon view
+ *
+ * @uses $vars['entity'] The entity the icon represents - uses getIconURL() method
+ * @uses $vars['size'] tiny, small (default), large, master
+ * @uses $vars['href'] Optional override for link
+ * @uses $vars['img_class'] Optional CSS class added to img
+ * @uses $vars['link_class'] Optional CSS class added to link
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$album = $vars['entity'];
+
+$cover_guid = $album->getCoverImageGuid();
+if ($cover_guid) {
+ $vars['title'] = $album->getTitle();
+ $vars['href'] = $album->getURL();
+ echo elgg_view_entity_icon(get_entity($cover_guid), $vars['size'], $vars);
+} else {
+ $url = "mod/lightpics/graphics/empty_album.png";
+ $url = elgg_normalize_url($url);
+ $img = elgg_view('output/img', array(
+ 'src' => $url,
+ 'class' => 'elgg-photo',
+ 'title' => $album->getTitle(),
+ 'alt' => $album->getTitle(),
+ ));
+
+ $params = array(
+ 'href' => $url,
+ 'text' => $img,
+ 'is_trusted' => true,
+ );
+ if (isset($vars['link_class'])) {
+ $params['class'] = $vars['link_class'];
+ }
+ echo elgg_view('output/url', $params);
+}
diff --git a/mod/lightpics/views/default/icon/object/image.php b/mod/lightpics/views/default/icon/object/image.php
new file mode 100644
index 000000000..9087fc3d8
--- /dev/null
+++ b/mod/lightpics/views/default/icon/object/image.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Image icon view
+ *
+ * @uses $vars['entity'] The entity the icon represents - uses getIconURL() method
+ * @uses $vars['size'] tiny, small (default), large, master
+ * @uses $vars['href'] Optional override for link
+ * @uses $vars['img_class'] Optional CSS class added to img
+ * @uses $vars['link_class'] Optional CSS class added to link
+ * @uses $vars['title'] Optional title override
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$entity = $vars['entity'];
+
+$sizes = array('master', 'large', 'small', 'tiny');
+// Get size
+if (!in_array($vars['size'], $sizes)) {
+ $vars['size'] = 'small';
+}
+
+if (!isset($vars['title'])) {
+ $title = $entity->getTitle();
+} else {
+ $title = $vars['title'];
+}
+
+$url = isset($vars['href']) ? $vars['href'] : $entity->getURL();
+if (isset($vars['href'])) {
+ $url = $vars['href'];
+}
+
+$class = '';
+if (isset($vars['img_class'])) {
+ $class = $vars['img_class'];
+}
+$class = "elgg-photo $class";
+
+$img_src = $entity->getIconURL($vars['size']);
+$img_src = elgg_format_url($img_src);
+$img = elgg_view('output/img', array(
+ 'src' => $img_src,
+ 'class' => $class,
+ 'title' => $title,
+ 'alt' => $title,
+));
+
+if ($url) {
+ $params = array(
+ 'href' => $url,
+ 'text' => $img,
+ 'is_trusted' => true,
+ );
+ if (isset($vars['link_class'])) {
+ $params['class'] = $vars['link_class'];
+ }
+ echo elgg_view('output/url', $params);
+} else {
+ echo $img;
+}
diff --git a/mod/lightpics/views/default/js/photos/tidypics.php b/mod/lightpics/views/default/js/photos/tidypics.php
new file mode 100644
index 000000000..1ff7b2c40
--- /dev/null
+++ b/mod/lightpics/views/default/js/photos/tidypics.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ *
+ */
+
+?>
+
+elgg.provide('elgg.tidypics');
+
+elgg.tidypics.init = function() {
+
+ if (elgg.ui.lightbox) {
+ $('.elgg-lightbox, .elgg-lightbox-photo').colorbox({
+ href: function() {
+ if ((new RegExp("photos/image/[0-9]+", 'i')).test($(this).attr('href'))) {
+ var guid = (new RegExp("photos/image/[0-9]+", 'i')).exec($(this).attr('href')).toString().substr("photos/image/".length);
+ return elgg.config.wwwroot + "photos/thumbnail/" + guid + "/large";
+ } else {
+ return $(this).attr('href');
+ }
+ },
+ title: function() {
+ return '<h3 style="display: inline">'+ $(this).find('img').attr('title') +'</h3> - <a href="'+ $(this).attr('href') +'">'+ elgg.echo('comments') +'</a>';
+ }
+ });
+ }
+
+ $("#tidypics-sort").sortable({
+ opacity: 0.7,
+ revert: true,
+ scroll: true
+ });
+
+ $('.elgg-form-photos-album-sort').submit(function() {
+ var tidypics_guids = [];
+ $("#tidypics-sort li").each(function(index) {
+ tidypics_guids.push($(this).attr('id'));
+ });
+ $('input[name="guids"]').val(tidypics_guids.toString());
+ });
+};
+
+elgg.register_hook_handler('init', 'system', elgg.tidypics.init);
diff --git a/mod/lightpics/views/default/js/photos/upload.php b/mod/lightpics/views/default/js/photos/upload.php
new file mode 100644
index 000000000..e3922c8c8
--- /dev/null
+++ b/mod/lightpics/views/default/js/photos/upload.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ *
+ */
+
+if ($maxfilesize = (int) elgg_get_plugin_setting('maxfilesize', 'lightpics')) {
+ $maxfilesize *= 1024 * 1024;
+} else {
+ $maxfilesize = 5 * 1024 * 1024;
+}
+?>
+
+elgg.provide('elgg.tidypics.upload');
+
+elgg.tidypics.upload.init = function() {
+
+ window.locale = {
+ "fileupload": {
+ "error": elgg.echo('tidypics:upload:error'),
+ "errors": {
+ "maxFileSize": elgg.echo('tidypics:upload:maxfilesize'),
+ "minFileSize": elgg.echo('tidypics:upload:minfilesize'),
+ "acceptFileTypes": elgg.echo('tidypics:upload:acceptfiletypes'),
+ "maxNumberOfFiles": elgg.echo('tidypics:upload:maxnumberoffiles'),
+ },
+ }
+ };
+
+ $.widget('blueimpJUI.fileupload', $.blueimpUI.fileupload, {
+ _transition: function (node) {
+ var that = this,
+ deferred = $.Deferred();
+ if (node.hasClass('fade')) {
+ node.fadeToggle(function () {
+ deferred.resolveWith(node);
+ });
+ } else {
+ deferred.resolveWith(node);
+ }
+ return deferred;
+ },
+ });
+
+ // Initialize the jQuery File Upload widget:
+ $('#fileupload').fileupload();
+
+ // Settings
+ $('#fileupload').fileupload('option', {
+ maxFileSize: <?php echo $maxfilesize; ?>,
+ acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
+ change: function() {
+ elgg.tidypics.upload.fileinput.hide().appendTo($('#fileupload'));
+ elgg.tidypics.upload.fileinput = $('#fileupload .elgg-input-file');
+ },
+ drop: function () {
+ return false;
+ }
+ });
+
+ elgg.tidypics.upload.fileinput = $('#fileupload .elgg-input-file');
+};
+
+elgg.register_hook_handler('init', 'system', elgg.tidypics.upload.init);
diff --git a/mod/lightpics/views/default/object/album.php b/mod/lightpics/views/default/object/album.php
new file mode 100644
index 000000000..bfcfd96d2
--- /dev/null
+++ b/mod/lightpics/views/default/object/album.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Album view
+ *
+ * @uses $vars['entity'] TidypicsAlbum
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$album = elgg_extract('entity', $vars);
+$full_view = elgg_extract('full_view', $vars, false);
+
+if ($full_view) {
+ echo elgg_view('object/album/full', $vars);
+} else {
+ if (elgg_in_context('widgets')) {
+ echo elgg_view('object/album/list', $vars);
+ } else {
+ echo elgg_view('object/album/gallery', $vars);
+ }
+}
diff --git a/mod/lightpics/views/default/object/album/full.php b/mod/lightpics/views/default/object/album/full.php
new file mode 100644
index 000000000..7b273c66e
--- /dev/null
+++ b/mod/lightpics/views/default/object/album/full.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Full view of an album
+ *
+ * @uses $vars['entity'] TidypicsAlbum
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$album = elgg_extract('entity', $vars);
+$owner = $album->getOwnerEntity();
+
+$owner_icon = elgg_view_entity_icon($owner, 'tiny');
+
+$metadata = elgg_view_menu('entity', array(
+ 'entity' => $album,
+ 'handler' => 'photos',
+ 'sort_by' => 'priority',
+ 'class' => 'elgg-menu-hz',
+));
+
+$owner_link = elgg_view('output/url', array(
+ 'href' => "photos/owner/$owner->username",
+ 'text' => $owner->name,
+ 'is_trusted' => true,
+));
+$author_text = elgg_echo('byline', array($owner_link));
+$date = elgg_view_friendly_time($album->time_created);
+$categories = elgg_view('output/categories', $vars);
+
+$subtitle = "$author_text $date $categories";
+
+$params = array(
+ 'entity' => $album,
+ 'title' => false,
+ 'metadata' => $metadata,
+ 'subtitle' => $subtitle,
+ 'tags' => elgg_view('output/tags', array('tags' => $album->tags)),
+);
+$params = $params + $vars;
+$summary = elgg_view('object/elements/summary', $params);
+
+$body = '';
+if ($album->description) {
+ $body = elgg_view('output/longtext', array(
+ 'value' => $album->description,
+ 'class' => 'mbm',
+ ));
+}
+
+$body .= $album->viewImages();
+
+echo elgg_view('object/elements/full', array(
+ 'entity' => $album,
+ 'icon' => $owner_icon,
+ 'summary' => $summary,
+ 'body' => $body,
+));
diff --git a/mod/lightpics/views/default/object/album/gallery.php b/mod/lightpics/views/default/object/album/gallery.php
new file mode 100644
index 000000000..d6fb598e4
--- /dev/null
+++ b/mod/lightpics/views/default/object/album/gallery.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Display an album in a gallery
+ *
+ * @uses $vars['entity'] TidypicsAlbum
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$album = elgg_extract('entity', $vars);
+
+
+$album_cover = elgg_view_entity_icon($album, 'small');
+
+$header = elgg_view('output/url', array(
+ 'text' => $album->getTitle(),
+ 'href' => $album->getURL(),
+ 'is_trusted' => true,
+ 'class' => 'tidypics-heading',
+));
+
+$footer = "";
+if ($album->getContainerEntity()) {
+ $footer .= elgg_view('output/url', array(
+ 'text' => $album->getContainerEntity()->name,
+ 'href' => $album->getContainerEntity()->getURL(),
+ 'is_trusted' => true,
+ ));
+}
+$footer .= '<div class="elgg-subtext">' . elgg_echo('album:num', array($album->getSize())) . '</div>';
+
+$params = array(
+ 'footer' => $footer,
+);
+echo elgg_view_module('tidypics-album', $header, $album_cover, $params);
diff --git a/mod/lightpics/views/default/object/album/list.php b/mod/lightpics/views/default/object/album/list.php
new file mode 100644
index 000000000..e576c92f1
--- /dev/null
+++ b/mod/lightpics/views/default/object/album/list.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Display an album as an item in a list
+ *
+ * @uses $vars['entity'] TidypicsAlbum
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$album = elgg_extract('entity', $vars);
+$owner = $album->getOwnerEntity();
+
+$owner_link = elgg_view('output/url', array(
+ 'href' => "photos/owner/$owner->username",
+ 'text' => $owner->name,
+ 'is_trusted' => true,
+));
+$author_text = elgg_echo('byline', array($owner_link));
+$date = elgg_view_friendly_time($album->time_created);
+$categories = elgg_view('output/categories', $vars);
+
+$subtitle = "$author_text $date $categories";
+
+$title = elgg_view('output/url', array(
+ 'text' => $album->getTitle(),
+ 'href' => $album->getURL(),
+));
+
+$params = array(
+ 'entity' => $album,
+ 'title' => $title,
+ 'metadata' => null,
+ 'subtitle' => $subtitle,
+ 'tags' => elgg_view('output/tags', array('tags' => $album->tags)),
+);
+$params = $params + $vars;
+$summary = elgg_view('object/elements/summary', $params);
+
+$icon = elgg_view_entity_icon($album, 'tiny');
+
+echo $header = elgg_view_image_block($icon, $summary);
diff --git a/mod/lightpics/views/default/object/image.php b/mod/lightpics/views/default/object/image.php
new file mode 100644
index 000000000..fc8ea827e
--- /dev/null
+++ b/mod/lightpics/views/default/object/image.php
@@ -0,0 +1,211 @@
+<?php
+/**
+ * Image view
+ *
+ * @uses $vars['entity'] TidypicsImage
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+
+$full_view = elgg_extract('full_view', $vars, false);
+
+if ($full_view) {
+ echo elgg_view('object/image/full', $vars);
+} else {
+ echo elgg_view('object/image/summary', $vars);
+}
+
+return true;
+
+global $CONFIG;
+include_once dirname(dirname(dirname(dirname(__FILE__)))) . "/lib/exif.php";
+
+$image = $vars['entity'];
+$image_guid = $image->getGUID();
+$tags = $image->tags;
+$title = $image->getTitle();
+$desc = $image->description;
+$owner = $image->getOwnerEntity();
+$friendlytime = friendly_time($image->time_created);
+
+
+/********************************************************************
+ *
+ * search view of an image
+ *
+ ********************************************************************/
+if (get_context() == "search") {
+
+ // gallery view is a matrix view showing just the image - size: small
+ if (get_input('search_viewtype') == "gallery") {
+?>
+<div class="tidypics_album_images">
+ <a href="<?php echo $image->getURL();?>"><img src="<?php echo $vars['url'];?>mod/tidypics/thumbnail.php?file_guid=<?php echo $image_guid;?>&size=small" alt="thumbnail"/></a>
+</div>
+<?php
+ } else {
+ // list view displays a thumbnail icon of the image, its title, and the number of comments
+ $info = '<p><a href="' .$image->getURL(). '">'.$title.'</a></p>';
+ $info .= "<p class=\"owner_timestamp\"><a href=\"{$vars['url']}pg/profile/{$owner->username}\">{$owner->name}</a> {$friendlytime}";
+ $numcomments = elgg_count_comments($image);
+ if ($numcomments) {
+ $info .= ", <a href=\"{$image->getURL()}\">" . sprintf(elgg_echo("comments")) . " (" . $numcomments . ")</a>";
+ }
+ $info .= "</p>";
+ $icon = "<a href=\"{$image->getURL()}\">" . '<img src="' . $vars['url'] . 'mod/tidypics/thumbnail.php?file_guid=' . $image_guid . '&size=thumb" alt="' . $title . '" /></a>';
+
+ echo elgg_view_listing($icon, $info);
+ }
+
+/***************************************************************
+ *
+ * front page view
+ *
+ ****************************************************************/
+} else if (get_context() == "front" || get_context() == "widget") {
+ // the front page view is a clickable thumbnail of the image
+?>
+<a href="<?php echo $image->getURL(); ?>">
+ <img src="<?php echo $vars['url'];?>mod/tidypics/thumbnail.php?file_guid=<?php echo $image_guid;?>&amp;size=thumb" class="tidypics_album_cover" alt="<?php echo $title; ?>" title="<?php echo $title; ?>" />
+</a>
+ <?php
+} else {
+
+/********************************************************************
+ *
+ * listing of photos in an album
+ *
+ *********************************************************************/
+ if (!$vars['full']) {
+
+ // plugins can override the image link to add lightbox code here
+ $image_html = false;
+ $image_html = trigger_plugin_hook('tp_thumbnail_link', 'album', array('image' => $image), $image_html);
+
+ if ($image_html) {
+ echo $image_html;
+ } else {
+ // default link to image if no one overrides
+?>
+<div class="tidypics_album_images">
+ <a href="<?php echo $image->getURL();?>"><img src="<?php echo $vars['url'];?>pg/photos/thumbnail/<?php echo $image_guid;?>/small/" alt="<?php echo $image->title; ?>"/></a>
+</div>
+<?php
+ }
+ } else {
+
+/********************************************************************
+ *
+ * tidypics individual image display
+ *
+ *********************************************************************/
+
+
+ $viewer = get_loggedin_user();
+
+
+ // Build back and next links
+ $back = '';
+ $next = '';
+ $album = get_entity($image->container_guid);
+ $back_guid = $album->getPreviousImageGuid($image->guid);
+ $next_guid = $album->getNextImageGuid($image->guid);
+
+ if ($back_guid != 0) {
+ $text = elgg_echo('image:back');
+ $back = "<a href=\"{$vars['url']}pg/photos/view/$back_guid\">&laquo; $text</a>";
+ }
+
+ if ($next_guid != 0) {
+ $text = elgg_echo('image:next');
+ $next = "<a href=\"{$vars['url']}pg/photos/view/$next_guid\">$text &raquo;</a>";
+ }
+
+?>
+<div class="contentWrapper">
+ <div id="tidypics_wrapper">
+
+ <div id="tidypics_breadcrumbs">
+ <?php echo elgg_view('tidypics/breadcrumbs', array('album' => $album,) ); ?> <br />
+<?php
+ if (get_plugin_setting('view_count', 'tidypics') != "disabled") {
+
+ $image->addView($viewer->guid);
+ $views = $image->getViews($viewer->guid);
+ if (is_array($views)) {
+ echo sprintf(elgg_echo("tidypics:views"), $views['total']);
+ if ($owner->guid == $viewer->guid) {
+ echo ' ' . sprintf(elgg_echo("tidypics:viewsbyowner"), $views['unique']);
+ } else {
+ if ($views['mine']) {
+ echo ' ' . sprintf(elgg_echo("tidypics:viewsbyothers"), $views['mine']);
+ }
+ }
+ }
+ }
+?>
+ </div>
+
+ <div id="tidypics_desc">
+ <?php echo autop($desc); ?>
+ </div>
+ <div id="tidypics_image_nav">
+ <ul>
+ <li><?php echo $back; ?></li>
+ <li><?php echo $next; ?></li>
+ </ul>
+ </div>
+ <div id="tidypics_image_wrapper">
+ <?php
+ // this code controls whether the photo is a hyperlink or not and what it links to
+ if (get_plugin_setting('download_link', 'tidypics') != "disabled") {
+ // admin allows downloads so default to inline download link
+ $image_html = "<a href=\"{$vars['url']}pg/photos/download/{$image_guid}/inline/\" title=\"{$title}\" >";
+ $image_html .= "<img id=\"tidypics_image\" src=\"{$vars['url']}pg/photos/thumbnail/{$image_guid}/large/\" alt=\"{$title}\" />";
+ $image_html .= "</a>";
+ } else {
+ $image_html = "<img id=\"tidypics_image\" src=\"{$vars['url']}pg/photos/thumbnail/{$image_guid}/large/\" alt=\"{$title}\" />";
+ }
+ // does any plugin want to override the link
+ $image_html = trigger_plugin_hook('tp_thumbnail_link', 'image', array('image' => $image), $image_html);
+ echo $image_html;
+ ?>
+ <div class="clearfloat"></div>
+ </div>
+ <?php
+ if (get_plugin_setting('exif', 'tidypics') == "enabled") {
+ echo elgg_view('tidypics/exif', array('guid'=> $image_guid));
+ }
+?>
+ <div class="tidypics_info">
+<?php
+ if (!is_null($tags)) {
+?>
+ <div class="object_tag_string"><?php echo elgg_view('output/tags',array('value' => $tags));?></div>
+<?php
+ }
+ if (get_plugin_setting('photo_ratings', 'tidypics') == "enabled") {
+?>
+ <div id="rate_container">
+ <?php echo elgg_view('rate/rate', array('entity'=> $vars['entity'])); ?>
+ </div>
+<?php
+ }
+
+ echo elgg_echo('image:by');?> <b><a href="<?php echo $vars['url']; ?>pg/profile/<?php echo $owner->username; ?>"><?php echo $owner->name; ?></a></b> <?php echo $friendlytime;
+?>
+ </div>
+ </div> <!-- tidypics wrapper-->
+<?php
+
+ echo elgg_view_comments($image);
+
+ echo '<div class="clearfloat"></div>';
+
+ echo '</div>'; // content wrapper
+
+ } // end of individual image display
+
+}
diff --git a/mod/lightpics/views/default/object/image/full.php b/mod/lightpics/views/default/object/image/full.php
new file mode 100644
index 000000000..74436f500
--- /dev/null
+++ b/mod/lightpics/views/default/object/image/full.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Full view of an image
+ *
+ * @uses $vars['entity'] TidypicsImage
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$image = $photo = $vars['entity'];
+
+$img = elgg_view_entity_icon($image, 'large', array(
+ 'href' => $image->getIconURL('master'),
+ 'img_class' => 'tidypics-photo',
+ 'link_class' => 'tidypics-lightbox elgg-lightbox-photo',
+));
+
+$owner_link = elgg_view('output/url', array(
+ 'href' => "photos/owner/" . $photo->getOwnerEntity()->username,
+ 'text' => $photo->getOwnerEntity()->name,
+));
+$author_text = elgg_echo('byline', array($owner_link));
+
+$owner_icon = elgg_view_entity_icon($photo->getOwnerEntity(), 'tiny');
+
+$metadata = elgg_view_menu('entity', array(
+ 'entity' => $vars['entity'],
+ 'handler' => 'photos',
+ 'sort_by' => 'priority',
+ 'class' => 'elgg-menu-hz',
+));
+
+$subtitle = "$author_text $date $categories $comments_link";
+
+$params = array(
+ 'entity' => $photo,
+ 'title' => false,
+ 'metadata' => $metadata,
+ 'subtitle' => $subtitle,
+ 'tags' => $tags,
+);
+$list_body = elgg_view('object/elements/summary', $params);
+
+$params = array('class' => 'mbl');
+$summary = elgg_view_image_block($owner_icon, $list_body, $params);
+
+echo $summary;
+
+echo '<div class="tidypics-photo-wrapper center">';
+echo elgg_view('object/image/navigation', $vars);
+echo $img;
+echo '</div>';
+
+if ($photo->description) {
+ echo elgg_view('output/longtext', array(
+ 'value' => $photo->description,
+ 'class' => 'mbl',
+ ));
+}
+
+echo elgg_view_comments($photo);
diff --git a/mod/lightpics/views/default/object/image/navigation.php b/mod/lightpics/views/default/object/image/navigation.php
new file mode 100644
index 000000000..155b76364
--- /dev/null
+++ b/mod/lightpics/views/default/object/image/navigation.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Photo navigation
+ *
+ * @uses $vars['entity']
+ */
+
+$photo = $vars['entity'];
+
+$album = $photo->getContainerEntity();
+$previous_photo = $album->getPreviousImage($photo->getGUID());
+$next_photo = $album->getNextImage($photo->getGUID());
+$size = $album->getSize();
+$index = $album->getIndex($photo->getGUID());
+
+echo '<ul class="elgg-menu elgg-menu-hz tidypics-album-nav">';
+echo '<li>';
+echo elgg_view('output/url', array(
+ 'text' => elgg_view_icon('arrow-left'),
+ 'href' => $previous_photo->getURL(),
+));
+echo '</li>';
+
+echo '<li>';
+echo '<span>' . elgg_echo('image:index', array($index, $size)) . '</span>';
+echo '</li>';
+
+echo '<li>';
+echo elgg_view('output/url', array(
+ 'text' => elgg_view_icon('arrow-right'),
+ 'href' => $next_photo->getURL(),
+));
+echo '</li>';
+echo '</ul>';
diff --git a/mod/lightpics/views/default/object/image/summary.php b/mod/lightpics/views/default/object/image/summary.php
new file mode 100644
index 000000000..0fa03cbe8
--- /dev/null
+++ b/mod/lightpics/views/default/object/image/summary.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Summary of an image for lists/galleries
+ *
+ * @uses $vars['entity'] TidypicsImage
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$image = elgg_extract('entity', $vars);
+
+
+$header = elgg_view('output/url', array(
+ 'text' => $image->getTitle(),
+ 'href' => $image->getURL(),
+ 'is_trusted' => true,
+ 'class' => 'tidypics-heading',
+));
+
+$body = elgg_view_entity_icon($image, 'small', array(
+ 'href' => $image->getURL(),
+ 'img_class' => 'tidypics-photo',
+ 'encode_text' => false,
+ 'is_trusted' => true,
+ 'link_class' => 'tidypics-lightbox elgg-lightbox-photo',
+));
+
+/*
+$footer = elgg_view('output/url', array(
+ 'text' => $image->getContainerEntity()->name,
+ 'href' => $image->getContainerEntity()->getURL(),
+ 'is_trusted' => true,
+));
+$footer .= '<div class="elgg-subtext">' . elgg_echo('album:num', array($album->getSize())) . '</div>';
+*/
+
+$params = array(
+ 'footer' => $footer,
+);
+echo elgg_view_module('tidypics-image', $header, $body, $params);
diff --git a/mod/lightpics/views/default/photos/css.php b/mod/lightpics/views/default/photos/css.php
new file mode 100644
index 000000000..9a2b1768f
--- /dev/null
+++ b/mod/lightpics/views/default/photos/css.php
@@ -0,0 +1,470 @@
+<?php
+/**
+ * Tidypics CSS
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+?>
+
+/* ***************************************
+ TIDYPICS
+*************************************** */
+.elgg-module-tidypics-album,
+.elgg-module-tidypics-image {
+ width: 161px;
+ text-align: center;
+ margin: 5px 0;
+}
+.elgg-module-tidypics-image {
+ margin: 5px auto;
+ height: 80%;
+}
+
+.tidypics-gallery-widget > li {
+ width: 100%;
+}
+.tidypics-photo-wrapper {
+ position: relative;
+}
+
+.tidypics-heading {
+ color: #0054A7;
+}
+.tidypics-heading:hover {
+ color: #0054A7;
+ text-decoration: none;
+}
+
+.tidypics-input-thin {
+ width: 120px;
+}
+
+#tidypics-sort li {
+ width:153px;
+ height:153px;
+ cursor: move;
+}
+
+.tidypics-river-list > li {
+ display: inline-block;
+}
+
+.tidypics-photo-item + .tidypics-photo-item {
+ margin-left: 7px;
+}
+
+.tidypics-gallery > li {
+ padding: 0 10px;
+}
+
+.tidypics-album-nav {
+ margin: 3px 0;
+ text-align: center;
+ color: #aaa;
+}
+
+.tidypics-album-nav > li {
+ padding: 0 3px;
+}
+
+.tidypics-album-nav > li {
+ vertical-align: top;
+}
+
+/* ***************************************
+ UPLOADER
+*************************************** */
+
+.fileinput-container {
+ text-align: center;
+}
+.fileinput-button {
+ cursor: pointer;
+}
+.fileinput-button input {
+ position: absolute;
+ top: 0;
+ right: 0;
+ margin: 0;
+ opacity: 0;
+ filter: alpha(opacity=0);
+
+}
+.files .fade {
+ display: none;
+}
+/* Fix for IE 6: */
+*html .fileinput-button {
+ margin-right: -2px;
+}
+*html .fileinput-button .elgg-button {
+ line-height: 24px;
+}
+*html .fileupload-buttonbar .elgg-button {
+ margin-left: 3px;
+}
+
+/* Fix for IE 7: */
+*+html .fileinput-button {
+ margin-right: 1px;
+}
+*+html .fileinput-button .elgg-button {
+ line-height: 24px;
+}
+*+html .fileupload-buttonbar .elgg-button {
+ margin-left: 3px;
+}
+
+@media (max-width: 480px) {
+ .files .preview * {
+ width: 40px;
+ }
+ .files .name * {
+ width: 80px;
+ display: inline-block;
+ word-wrap: break-word;
+ }
+}
+
+/* Fix for Webkit (Safari, Chrome) */
+@media screen and (-webkit-min-device-pixel-ratio:0) {
+ .fileinput-button {
+ margin-top: 2px;
+ }
+}
+
+<?php
+return true;
+?>
+
+/* ---- tidypics object views ---- */
+
+.tidypics_wrapper > table.entity_gallery {
+ border-spacing: 0;
+}
+
+.tidypics_wrapper .entity_gallery td {
+ padding: 0;
+}
+
+.tidypics_wrapper .entity_gallery_item,
+.tidypics_wrapper .entity_gallery_item:hover {
+ background-color: transparent;
+ color: inherit;
+}
+
+#tidypics_breadcrumbs {
+margin:5px 0 15px 0;
+font-size:80%;
+}
+
+#tidypics_desc {
+padding:0 20px;
+font-style:italic;
+}
+
+#tidypics_image_nav {
+text-align:center;
+}
+
+#tidypics_image_wrapper {
+margin:10px 0 10px 0;
+text-align:center;
+}
+
+#tidypics_image {
+border:1px solid #dedede;
+padding:5px;
+}
+
+#tidypics_image_nav ul li {
+display:inline;
+margin-right:15px;
+}
+
+#tidypics_controls {
+text-align:center;
+margin-bottom:10px;
+}
+
+#tidypics_controls a {
+margin:10px;
+}
+
+#tidypics_controls ul {
+list-style:none;
+margin:0px;
+padding:8px;
+}
+
+#tidypics_controls ul li {
+padding:2px 10px 2px 22px;
+margin:2px 0px;
+display:inline;
+}
+
+.tidypics_info {
+padding:20px;
+}
+
+#tidypics_exif {
+padding-left:20px;
+font-size:80%;
+}
+
+.tidypics_album_images {
+float:left;
+width:153px;
+height:153px;
+margin:3px;
+padding:4px;
+border:1px solid #dedede;
+text-align:center;
+}
+
+.tidypics_album_cover {
+padding:2px;
+border:1px solid #dedede;
+margin:5px 0;
+}
+
+.tidypics_album_widget_single_item {
+margin-bottom:8px;
+}
+
+.tidypics_album_gallery_item {
+float:left;
+margin-bottom:20px;
+padding: 4px;
+text-align:center;
+width: 160px;
+}
+
+.tidypics_line_break {
+width: 100%;
+clear: both;
+}
+
+.tidypics_gallery_title {
+font-weight:bold;
+}
+
+.tidypics_popup {
+border:1px solid #3B5999;
+width:200px;
+position:absolute;
+z-index:10000;
+display:none;
+background:#ffffff;
+padding:10px;
+font-size:12px;
+text-align:left;
+}
+
+/* ------ tidypics widget view ------ */
+
+#tidypics_album_widget_container {
+text-align:center;
+}
+
+.tidypics_album_widget_timestamp {
+color:#333333;
+}
+
+.tidypics_widget_latest {
+margin: 0 auto;
+width: 208px;
+}
+
+/* --------- image upload/edit forms ------------ */
+
+#tidpics_image_upload_list li {
+margin:3px 0;
+}
+
+.tidypics_edit_image_container {
+padding:5px;
+margin:5px 0;
+overflow:auto;
+}
+
+.tidypics_edit_images {
+float:right;
+width:160px;
+height:160px;
+margin:4px;
+padding:5px;
+border:1px solid #dedede;
+text-align:center;
+}
+
+.tidypics_image_info {
+float:left;
+width:60%;
+}
+
+.tidypics_image_info label {
+font-size:1em;
+}
+
+.tidypics_caption_input {
+ width:98%;
+ height:100px;
+}
+
+/* ---- tidypics group css ----- */
+
+#tidypics_group_profile {
+-webkit-border-radius: 8px;
+-moz-border-radius: 8px;
+background:white none repeat scroll 0 0;
+margin:0 0 20px;
+padding:0 0 5px;
+}
+
+
+/* --------- tidypics river items ------------ */
+
+.river_object_image_create {
+ background: url(<?php echo $vars['url']; ?>mod/lightpics/graphics/icons/river_icon_image.gif) no-repeat left -1px;
+}
+.river_object_album_create {
+ background: url(<?php echo $vars['url']; ?>mod/lightpics/graphics/icons/river_icon_album.gif) no-repeat left -1px;
+}
+.river_object_image_comment {
+ background: url(<?php echo $vars['url']; ?>_graphics/river_icons/river_icon_comment.gif) no-repeat left -1px;
+}
+.river_object_album_comment {
+ background: url(<?php echo $vars['url']; ?>_graphics/river_icons/river_icon_comment.gif) no-repeat left -1px;
+}
+.river_user_tag {
+ background: url(<?php echo $vars['url']; ?>mod/lightpics/graphics/icons/river_icon_tag.gif) no-repeat left -1px;
+}
+
+/* ----------- tagging ---------------- */
+#tidypics_tag_instructions {
+background:#BBDAF7;
+border:1px solid #4690D6;
+padding:10px;
+height:25px;
+min-width:360px;
+display:none;
+overflow:hidden;
+position:absolute;
+z-index:10000;
+-webkit-border-radius: 8px;
+-moz-border-radius: 8px;
+}
+
+#tidypics_tag_instruct_text {
+padding-top: 3px;
+float: left;
+}
+
+#tidypics_tag_instruct_button_div {
+float: left;
+margin-left: 15px;
+}
+
+#tidypics_tag_instruct_button {
+margin:0;
+}
+
+#tidypics_tag_menu {
+width:240px;
+max-height:400px;
+overflow:hidden;
+-webkit-border-radius: 8px;
+-moz-border-radius: 8px;
+}
+
+.tidypics_popup_header {
+width:100%;
+margin-bottom:10px;
+}
+
+
+#tidypics_tagmenu_left {
+width:175px;
+float:left;
+}
+
+#tidypics_tagmenu_right {
+float:left;
+}
+
+#tidypics_tagmenu_left .input-filter {
+width:150px;
+}
+
+#tidypics_tagmenu_right .submit_button {
+margin-top:2px;
+}
+
+#tidypics_delete_tag_menu {
+-webkit-border-radius: 8px;
+-moz-border-radius: 8px;
+overflow:hidden;
+}
+
+.tidypics_tag {
+display:none;
+background:url(<?php echo $vars['url']; ?>mod/lightpics/graphics/spacer.gif);
+border:2px solid #ffffff;
+overflow:hidden;
+position:absolute;
+z-index:0;
+}
+
+.tidypics_tag_text {
+display:none;
+overflow:hidden;
+position:absolute;
+z-index:0;
+text-align:center;
+background:#BBDAF7;
+border:1px solid #3B5999;
+-webkit-border-radius:3px;
+-moz-border-radius:3px;
+padding:1px;
+}
+
+#tidypics_phototags_list {
+padding:0 20px 0 20px;
+}
+
+#tidypics_phototags_list ul {
+list-style:none;
+margin:0px;
+padding:8px;
+}
+
+#tidypics_phototags_list ul li {
+padding-right:10px;
+margin:2px 0px;
+display:inline;
+}
+
+#tidypics_image_upload_list {
+list-style: none;
+}
+
+#tidypics_album_sort {
+padding:0;
+margin:0;
+}
+
+#tidypics_album_sort li {
+float:left;
+margin:3px;
+width:161px;
+height:161px;
+list-style:none;
+}
+
+#tidypics_album_sort img {
+border:1px solid #dedede;
+padding:4px;
+}
+
diff --git a/mod/lightpics/views/default/photos/group_module.php b/mod/lightpics/views/default/photos/group_module.php
new file mode 100644
index 000000000..de7a451c4
--- /dev/null
+++ b/mod/lightpics/views/default/photos/group_module.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Group blog module
+ */
+
+$group = $vars['entity'];
+
+if ($group->photos_enable == "no") {
+ return true;
+}
+
+$all_link = elgg_view('output/url', array(
+ 'href' => "photos/group/$group->guid/all",
+ 'text' => elgg_echo('link:view:all'),
+ 'is_trusted' => true,
+));
+
+elgg_push_context('widgets');
+$options = array(
+ 'type' => 'object',
+ 'subtype' => 'album',
+ 'container_guid' => elgg_get_page_owner_guid(),
+ 'limit' => 6,
+ 'full_view' => false,
+ 'pagination' => false,
+);
+$content = elgg_list_entities($options);
+elgg_pop_context();
+
+if (!$content) {
+ $content = '<p>' . elgg_echo('tidypics:none') . '</p>';
+}
+
+$new_link = elgg_view('output/url', array(
+ 'href' => "photos/add/$group->guid",
+ 'text' => elgg_echo('photos:add'),
+ 'is_trusted' => true,
+));
+
+echo elgg_view('groups/profile/module', array(
+ 'title' => elgg_echo('photos:group'),
+ 'content' => $content,
+ 'all_link' => $all_link,
+ 'add_link' => $new_link,
+));
diff --git a/mod/lightpics/views/default/photos/sidebar.php b/mod/lightpics/views/default/photos/sidebar.php
new file mode 100644
index 000000000..fd5202691
--- /dev/null
+++ b/mod/lightpics/views/default/photos/sidebar.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Sidebar view
+ */
+
+$page = elgg_extract('page', $vars);
+$image = elgg_extract('image', $vars);
+if ($image && $page == 'view') {
+ if (elgg_get_plugin_setting('exif', 'tidypics')) {
+ echo elgg_view('photos/sidebar/exif', $vars);
+ }
+}
+
+if ($page == 'upload') {
+ if (elgg_get_plugin_setting('quota', 'tidypics')) {
+ echo elgg_view('photos/sidebar/quota', $vars);
+ }
+} \ No newline at end of file
diff --git a/mod/lightpics/views/default/photos/sidebar/exif.php b/mod/lightpics/views/default/photos/sidebar/exif.php
new file mode 100644
index 000000000..1d4444b53
--- /dev/null
+++ b/mod/lightpics/views/default/photos/sidebar/exif.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * EXIF sidebar module
+ */
+
+$image = $vars['image'];
+
+elgg_load_library('tidypics:exif');
+
+$exif = tp_exif_formatted($image);
+if ($exif) {
+ $title = "EXIF";
+ $body = '<table class="elgg-table elgg-table-alt">';
+ foreach ($exif as $key => $value) {
+ $body .= "<tr><td>$key</td><td>$value</td></tr>";
+ }
+ $body .= '</table>';
+
+ echo elgg_view_module('aside', $title, $body);
+}
diff --git a/mod/lightpics/views/default/photos/sidebar/quota.php b/mod/lightpics/views/default/photos/sidebar/quota.php
new file mode 100644
index 000000000..16b7885c4
--- /dev/null
+++ b/mod/lightpics/views/default/photos/sidebar/quota.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * User quota
+ */
+
+$user = elgg_get_logged_in_user_entity();
+
+
+$quota = elgg_get_plugin_setting('quota', 'tidypics');
+if ($quota) {
+ $image_repo_size = (int)$user->image_repo_size;
+ $image_repo_size = $image_repo_size / 1024 / 1024;
+ $quote_percentage = round(100 * ($image_repo_size / $quota));
+ // for small quotas, so one decimal place
+ if ($quota < 10) {
+ $image_repo_size = sprintf('%.1f', $image_repo_size);
+ } else {
+ $image_repo_size = round($image_repo_size);
+ }
+ if ($image_repo_size > $quota) {
+ $image_repo_size = $quota;
+ }
+
+ $title = elgg_echo('tidypics:title:quota');
+ $body = elgg_echo("tidypics:quota") . ' ' . $image_repo_size . '/' . $quota . " MB ({$quote_percentage}%)";
+ echo elgg_view_module('aside', $title, $body);
+}
diff --git a/mod/lightpics/views/default/river/object/album/create.php b/mod/lightpics/views/default/river/object/album/create.php
new file mode 100644
index 000000000..29620041f
--- /dev/null
+++ b/mod/lightpics/views/default/river/object/album/create.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Album river view
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+elgg_load_css('lightbox');
+elgg_load_js('lightbox');
+elgg_load_js('tidypics');
+
+$album = $vars['item']->getObjectEntity();
+
+$album_river_view = elgg_get_plugin_setting('album_river_view', 'tidypics');
+if ($album_river_view == "cover") {
+ $image = $album->getCoverImage();
+ if ($image) {
+ $attachments = elgg_view_entity_icon($image, 'tiny');
+ }
+} else {
+ $images = $album->getImages(7);
+
+ if (count($images)) {
+ $attachments = '<ul class="tidypics-river-list elgg-lightbox-gallery">';
+ foreach($images as $image) {
+ $attachments .= '<li class="tidypics-photo-item">';
+ $attachments .= elgg_view_entity_icon($image, 'tiny', array(
+ 'link_class' => 'tidypics-lightbox elgg-lightbox-photo',
+ ));
+ $attachments .= '</li>';
+ }
+ $attachments .= '</ul>';
+ }
+}
+
+echo elgg_view('river/elements/layout', array(
+ 'item' => $vars['item'],
+ 'attachments' => $attachments,
+));
diff --git a/mod/lightpics/views/default/river/object/image/create.php b/mod/lightpics/views/default/river/object/image/create.php
new file mode 100644
index 000000000..6b68b4d68
--- /dev/null
+++ b/mod/lightpics/views/default/river/object/image/create.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Image album view
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+elgg_load_css('lightbox');
+elgg_load_js('lightbox');
+elgg_load_js('tidypics');
+
+$subject = $vars['item']->getSubjectEntity();
+$subject_link = elgg_view('output/url', array(
+ 'href' => $subject->getURL(),
+ 'text' => $subject->name,
+ 'class' => 'elgg-river-subject',
+ 'is_trusted' => true,
+));
+
+$image = $vars['item']->getObjectEntity();
+$attachments = elgg_view_entity_icon($image, 'tiny');
+
+$image_link = elgg_view('output/url', array(
+ 'href' => $image->getURL(),
+ 'text' => $image->getTitle(),
+ 'is_trusted' => true,
+ 'class' => 'elgg-lightbox-photo',
+));
+
+$album_link = elgg_view('output/url', array(
+ 'href' => $image->getContainerEntity()->getURL(),
+ 'text' => $image->getContainerEntity()->getTitle(),
+ 'is_trusted' => true,
+));
+
+echo elgg_view('river/elements/layout', array(
+ 'item' => $vars['item'],
+ 'attachments' => $attachments,
+ 'summary' => elgg_echo('image:river:created', array($subject_link, $image_link, $album_link)),
+));
diff --git a/mod/lightpics/views/default/river/object/tidypics_batch/create.php b/mod/lightpics/views/default/river/object/tidypics_batch/create.php
new file mode 100644
index 000000000..b97c853a3
--- /dev/null
+++ b/mod/lightpics/views/default/river/object/tidypics_batch/create.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Batch river view
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+elgg_load_css('lightbox');
+elgg_load_js('lightbox');
+elgg_load_js('tidypics');
+
+$batch = $vars['item']->getObjectEntity();
+
+// Get images related to this batch
+$images = elgg_get_entities_from_relationship(array(
+ 'relationship' => 'belongs_to_batch',
+ 'relationship_guid' => $batch->getGUID(),
+ 'inverse_relationship' => true,
+ 'type' => 'object',
+ 'subtype' => 'image',
+ 'offset' => 0,
+));
+
+$album = $batch->getContainerEntity();
+if (!$album) {
+ // something went quite wrong - this batch has no associated album
+ return true;
+}
+$album_link = elgg_view('output/url', array(
+ 'href' => $album->getURL(),
+ 'text' => $album->getTitle(),
+ 'is_trusted' => true,
+));
+
+$subject = $vars['item']->getSubjectEntity();
+$subject_link = elgg_view('output/url', array(
+ 'href' => $subject->getURL(),
+ 'text' => $subject->name,
+ 'class' => 'elgg-river-subject',
+ 'is_trusted' => true,
+));
+
+if (count($images)) {
+ $attachments = '<ul class="tidypics-river-list elgg-lightbox-gallery">';
+ foreach($images as $image) {
+ $attachments .= '<li class="tidypics-photo-item">';
+ $attachments .= elgg_view_entity_icon($image, 'tiny', array(
+ 'link_class' => 'tidypics-lightbox elgg-lightbox-photo',
+ ));
+ $attachments .= '</li>';
+ }
+ $attachments .= '</ul>';
+}
+
+if (count($images) == 1) {
+ $summary = elgg_echo('image:river:created', array($subject_link, $album_link));
+} else {
+ $summary = elgg_echo('image:river:created:multiple', array($subject_link, count($images), $album_link));
+}
+
+echo elgg_view('river/elements/layout', array(
+ 'item' => $vars['item'],
+ 'attachments' => $attachments,
+ 'summary' => $summary
+));
diff --git a/mod/lightpics/views/default/widgets/album_view/content.php b/mod/lightpics/views/default/widgets/album_view/content.php
new file mode 100644
index 000000000..88c8837b3
--- /dev/null
+++ b/mod/lightpics/views/default/widgets/album_view/content.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * List albums in a widget
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$options = array(
+ 'type' => 'object',
+ 'subtype' => 'album',
+ 'container_guid' => elgg_get_page_owner_guid(),
+ 'limit' => $vars['entity']->num_display,
+ 'full_view' => false,
+ 'pagination' => false,
+);
+echo elgg_list_entities($options);
diff --git a/mod/lightpics/views/default/widgets/album_view/edit.php b/mod/lightpics/views/default/widgets/album_view/edit.php
new file mode 100644
index 000000000..873fcc3eb
--- /dev/null
+++ b/mod/lightpics/views/default/widgets/album_view/edit.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Widget settings for newest albums
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+// set default value
+if (!isset($vars['entity']->num_display)) {
+ $vars['entity']->num_display = 5;
+}
+
+$params = array(
+ 'name' => 'params[num_display]',
+ 'value' => $vars['entity']->num_display,
+ 'options' => array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20),
+);
+$dropdown = elgg_view('input/dropdown', $params);
+
+?>
+<div>
+ <?php echo elgg_echo('tidypics:widget:num_albums'); ?>:
+ <?php echo $dropdown; ?>
+</div>
diff --git a/mod/lightpics/views/default/widgets/latest_photos/content.php b/mod/lightpics/views/default/widgets/latest_photos/content.php
new file mode 100644
index 000000000..8a3bb4891
--- /dev/null
+++ b/mod/lightpics/views/default/widgets/latest_photos/content.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Display the latest photos uploaded by an individual
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+echo elgg_list_entities(array(
+ 'type' => 'object',
+ 'subtype' => 'image',
+ 'limit' => $vars['entity']->num_display,
+ 'owner_guid' => elgg_get_page_owner_guid(),
+ 'full_view' => false,
+ 'list_type' => 'gallery',
+ 'list_type_toggle' => false,
+ 'gallery_class' => 'tidypics-gallery-widget',
+));
diff --git a/mod/lightpics/views/default/widgets/latest_photos/edit.php b/mod/lightpics/views/default/widgets/latest_photos/edit.php
new file mode 100644
index 000000000..dd32e2b6d
--- /dev/null
+++ b/mod/lightpics/views/default/widgets/latest_photos/edit.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Widget settings for latest photos
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+// set default value
+if (!isset($vars['entity']->num_display)) {
+ $vars['entity']->num_display = 6;
+}
+
+$params = array(
+ 'name' => 'params[num_display]',
+ 'value' => $vars['entity']->num_display,
+ 'options' => array(3, 6, 9, 12),
+);
+$dropdown = elgg_view('input/dropdown', $params);
+
+?>
+<div>
+ <?php echo elgg_echo('tidypics:widget:num_latest'); ?>:
+ <?php echo $dropdown; ?>
+</div>
diff --git a/mod/lightpics/views/rss/extensions/photos/xmlns.php b/mod/lightpics/views/rss/extensions/photos/xmlns.php
new file mode 100644
index 000000000..67cf45133
--- /dev/null
+++ b/mod/lightpics/views/rss/extensions/photos/xmlns.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * Support media files by adding a namespace
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+?> xmlns:media="http://search.yahoo.com/mrss/" \ No newline at end of file
diff --git a/mod/lightpics/views/rss/object/album.php b/mod/lightpics/views/rss/object/album.php
new file mode 100644
index 000000000..f880b56d6
--- /dev/null
+++ b/mod/lightpics/views/rss/object/album.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Album RSS view
+ *
+ * @uses $vars['entity'] TidypicsAlbum
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$full_view = elgg_extract('full_view', $vars, false);
+
+if ($full_view) {
+ echo elgg_view('object/album/full', $vars);
+} else {
+ echo elgg_view('object/album/summary', $vars);
+}
diff --git a/mod/lightpics/views/rss/object/album/full.php b/mod/lightpics/views/rss/object/album/full.php
new file mode 100644
index 000000000..b1f5a567b
--- /dev/null
+++ b/mod/lightpics/views/rss/object/album/full.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * List photos in an album for RSS
+ *
+ * @uses $vars['entity'] TidypicsAlbum
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$limit = (int)get_input('limit', 20);
+
+echo elgg_list_entities(array(
+ 'type' => 'object',
+ 'subtype' => 'image',
+ 'container_guid' => $vars['entity']->getGUID(),
+ 'limit' => $limit,
+ 'full_view' => false,
+));
diff --git a/mod/lightpics/views/rss/object/album/summary.php b/mod/lightpics/views/rss/object/album/summary.php
new file mode 100644
index 000000000..b6dc8f003
--- /dev/null
+++ b/mod/lightpics/views/rss/object/album/summary.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Individual album summary view for RSS
+ *
+ * @uses $vars['entity'] TidypicsAlbum
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$permalink = htmlspecialchars($vars['entity']->getURL(), ENT_NOQUOTES, 'UTF-8');
+$pubdate = date('r', $vars['entity']->getTimeCreated());
+
+$title = $vars['entity']->getTitle();
+$description = autop($vars['entity']->description);
+
+$creator = elgg_view('page/components/creator', $vars);
+$georss = elgg_view('page/components/georss', $vars);
+$extension = elgg_view('extensions/item', $vars);
+
+$item = <<<__HTML
+<item>
+ <guid isPermaLink="true">$permalink</guid>
+ <pubDate>$pubdate</pubDate>
+ <link>$permalink</link>
+ <title><![CDATA[$title]]></title>
+ <description><![CDATA[$description]]></description>
+ $creator$georss$extension
+</item>
+
+__HTML;
+
+echo $item;
diff --git a/mod/lightpics/views/rss/object/image.php b/mod/lightpics/views/rss/object/image.php
new file mode 100644
index 000000000..e3b67ee74
--- /dev/null
+++ b/mod/lightpics/views/rss/object/image.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Individual image RSS view
+ *
+ * @uses $vars['entity'] TidypicsImage
+ *
+ * @author Cash Costello
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2
+ */
+
+$permalink = htmlspecialchars($vars['entity']->getURL(), ENT_NOQUOTES, 'UTF-8');
+$pubdate = date('r', $vars['entity']->getTimeCreated());
+
+$title = $vars['entity']->getTitle();
+$description = autop($vars['entity']->description);
+
+$creator = elgg_view('page/components/creator', $vars);
+$georss = elgg_view('page/components/georss', $vars);
+$extension = elgg_view('extensions/item', $vars);
+
+$thumbnail_url = $vars['entity']->getIconURL('tiny');
+$download_url = $vars['entity']->getIconURL('large');
+
+$mime_type = $vars['entity']->getMimeType();
+
+$item = <<<__HTML
+<item>
+ <guid isPermaLink="true">$permalink</guid>
+ <pubDate>$pubdate</pubDate>
+ <link>$permalink</link>
+ <title><![CDATA[$title]]></title>
+ <description><![CDATA[$description]]></description>
+ $creator$georss$extension
+ <media:content url="$download_url" medium="image" type="$mime_type" />
+ <media:title><![CDATA[$title]]></media:title>
+ <media:description><![CDATA[$description]]></media:description>
+ <media:thumbnail url="$thumbnail_url"></media:thumbnail>
+</item>
+
+__HTML;
+
+echo $item;