From b9d021d4af1018ac2c60640678c1b790251190e9 Mon Sep 17 00:00:00 2001 From: Simon Cozens Date: Tue, 6 Feb 2007 18:23:49 +0000 Subject: [PATCH] Memories as at 1.3 git-svn-id: http://svn.simon-cozens.org/memories/trunk@2 041978f6-d955-411f-a9d7-1d8545c9c3c7 --- COPYING | 6 + Changes | 18 +++ MANIFEST | 70 ++++++++ META.yml | 24 +++ Makefile.PL | 21 +++ Memories/Album.pm | 77 +++++++++ Memories/Comment.pm | 7 + Memories/Config.pm | 58 +++++++ Memories/DBI.pm | 8 + Memories/Photo.pm | 290 ++++++++++++++++++++++++++++++++++ Memories/SystemTag.pm | 63 ++++++++ Memories/Tag.pm | 110 +++++++++++++ Memories/User.pm | 77 +++++++++ README | 64 ++++++++ Tagtools.pm | 76 +++++++++ debian/apache.conf | 18 +++ debian/changelog | 5 + debian/compat | 1 + debian/control | 31 ++++ debian/copyright | 8 + debian/memories.docs | 2 + debian/rules | 36 +++++ memories.sql | 58 +++++++ templates/album/denied | 7 + templates/album/list | 25 +++ templates/album/view | 9 ++ templates/album_adder | 9 ++ templates/calendar | 43 +++++ templates/custom/list | 20 +++ templates/footer | 9 ++ templates/frontpage | 34 ++++ templates/header | 28 ++++ templates/header.orig | 27 ++++ templates/login | 34 ++++ templates/login_box | 34 ++++ templates/macros | 67 ++++++++ templates/memories.css | 8 + templates/memories.png | Bin 0 -> 40186 bytes templates/nav | 11 ++ templates/pager | 32 ++++ templates/photo/comment | 18 +++ templates/photo/exif | 16 ++ templates/photo/recent | 18 +++ templates/photo/similar | 9 ++ templates/photo/tagedit | 21 +++ templates/photo/upload | 63 ++++++++ templates/photo/view | 104 ++++++++++++ templates/picture/view | 7 + templates/recent.rss | 22 +++ templates/rhs | 49 ++++++ templates/robots.txt | 5 + templates/static/1-s.gif | Bin 0 -> 646 bytes templates/static/1.gif | Bin 0 -> 649 bytes templates/static/2-s.gif | Bin 0 -> 718 bytes templates/static/2.gif | Bin 0 -> 694 bytes templates/static/3-s.gif | Bin 0 -> 718 bytes templates/static/3.gif | Bin 0 -> 695 bytes templates/static/4-s.gif | Bin 0 -> 680 bytes templates/static/4.gif | Bin 0 -> 668 bytes templates/static/5-s.gif | Bin 0 -> 710 bytes templates/static/5.gif | Bin 0 -> 692 bytes templates/static/6-s.gif | Bin 0 -> 719 bytes templates/static/6.gif | Bin 0 -> 708 bytes templates/static/7-s.gif | Bin 0 -> 673 bytes templates/static/7.gif | Bin 0 -> 672 bytes templates/static/8-s.gif | Bin 0 -> 731 bytes templates/static/8.gif | Bin 0 -> 713 bytes templates/static/9-s.gif | Bin 0 -> 731 bytes templates/static/9.gif | Bin 0 -> 704 bytes templates/static/memories.css | 141 +++++++++++++++++ templates/static/memories.png | Bin 0 -> 33929 bytes templates/static/sitemap.gz | Bin 0 -> 8203 bytes templates/static/upload.js | 82 ++++++++++ templates/systemtag/list | 36 +++++ templates/systemtag/view | 14 ++ templates/tag/list | 36 +++++ templates/tag/list_js | 1 + templates/tag/view | 28 ++++ templates/tagcloud | 4 + templates/test | 2 + templates/user/album_list | 41 +++++ templates/user/list | 25 +++ templates/user/view | 7 + 83 files changed, 2274 insertions(+) create mode 100644 COPYING create mode 100644 Changes create mode 100644 MANIFEST create mode 100644 META.yml create mode 100644 Makefile.PL create mode 100644 Memories/Album.pm create mode 100644 Memories/Comment.pm create mode 100644 Memories/Config.pm create mode 100644 Memories/DBI.pm create mode 100644 Memories/Photo.pm create mode 100644 Memories/SystemTag.pm create mode 100644 Memories/Tag.pm create mode 100644 Memories/User.pm create mode 100644 README create mode 100644 Tagtools.pm create mode 100644 debian/apache.conf create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/memories.docs create mode 100644 debian/rules create mode 100644 memories.sql create mode 100644 templates/album/denied create mode 100644 templates/album/list create mode 100644 templates/album/view create mode 100644 templates/album_adder create mode 100644 templates/calendar create mode 100644 templates/custom/list create mode 100644 templates/footer create mode 100644 templates/frontpage create mode 100644 templates/header create mode 100644 templates/header.orig create mode 100644 templates/login create mode 100644 templates/login_box create mode 100644 templates/macros create mode 100644 templates/memories.css create mode 100644 templates/memories.png create mode 100644 templates/nav create mode 100644 templates/pager create mode 100644 templates/photo/comment create mode 100644 templates/photo/exif create mode 100644 templates/photo/recent create mode 100644 templates/photo/similar create mode 100644 templates/photo/tagedit create mode 100644 templates/photo/upload create mode 100644 templates/photo/view create mode 100644 templates/picture/view create mode 100644 templates/recent.rss create mode 100644 templates/rhs create mode 100644 templates/robots.txt create mode 100644 templates/static/1-s.gif create mode 100644 templates/static/1.gif create mode 100644 templates/static/2-s.gif create mode 100644 templates/static/2.gif create mode 100644 templates/static/3-s.gif create mode 100644 templates/static/3.gif create mode 100644 templates/static/4-s.gif create mode 100644 templates/static/4.gif create mode 100644 templates/static/5-s.gif create mode 100644 templates/static/5.gif create mode 100644 templates/static/6-s.gif create mode 100644 templates/static/6.gif create mode 100644 templates/static/7-s.gif create mode 100644 templates/static/7.gif create mode 100644 templates/static/8-s.gif create mode 100644 templates/static/8.gif create mode 100644 templates/static/9-s.gif create mode 100644 templates/static/9.gif create mode 100644 templates/static/memories.css create mode 100644 templates/static/memories.png create mode 100644 templates/static/sitemap.gz create mode 100644 templates/static/upload.js create mode 100644 templates/systemtag/list create mode 100644 templates/systemtag/view create mode 100644 templates/tag/list create mode 100644 templates/tag/list_js create mode 100644 templates/tag/view create mode 100644 templates/tagcloud create mode 100644 templates/test create mode 100644 templates/user/album_list create mode 100644 templates/user/list create mode 100644 templates/user/view diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..86bd182 --- /dev/null +++ b/COPYING @@ -0,0 +1,6 @@ +This software is licensed under the GPL version 2 or higher, or the Perl +Artistic License, at your choice. The text of these two licenses can be +found in /usr/share/common-licenses/ on a Debian system, or at: + GPL: http://www.gnu.org/licenses/gpl.txt + Areistic: http://www.perl.com/pub/a/language/misc/Artistic.html + diff --git a/Changes b/Changes new file mode 100644 index 0000000..7d4c62b --- /dev/null +++ b/Changes @@ -0,0 +1,18 @@ +1.2 - Tue Jan 3 12:50:39 GMT 2006 + + - Slight calendar fix + - Tidy up display of large datasets. + - Rejig tag searching. + - Edit/delete photos + - Tag hierarchy computed over whole dataset, not just current page. + - RSS feeds available on most pages + +1.1 - Mon Oct 31 23:59:52 GMT 2005 + + - RSS feeds available from [%base%]/recent.rss + - Javascript suggestion of tags + - Calendaring and system tags + - Visual cleanups + - Concept of "enclosing tag" as well as "inside this tag" + - Minor fixes (zap cache on upload, URI escaping, etc.) + - Pager uses gifs diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..04f7441 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,70 @@ +Memories/Photo.pm +Memories/Tag.pm +Memories/DBI.pm +Memories/User.pm +Memories/Album.pm +Memories/Comment.pm +Memories/Config.pm +Memories/SystemTag.pm +Memories.pm +README +COPYING +templates/login_box +templates/nav +templates/macros +templates/frontpage +templates/static/memories.png +templates/static/memories.css +templates/static/1-s.gif +templates/static/1.gif +templates/static/2.gif +templates/static/3.gif +templates/static/4.gif +templates/static/5.gif +templates/static/6.gif +templates/static/7.gif +templates/static/8.gif +templates/static/9.gif +templates/static/2-s.gif +templates/static/3-s.gif +templates/static/4-s.gif +templates/static/5-s.gif +templates/static/6-s.gif +templates/static/7-s.gif +templates/static/8-s.gif +templates/static/9-s.gif +templates/static/upload.js +templates/header +templates/photo/recent +templates/photo/tagedit +templates/photo/comment +templates/photo/upload +templates/photo/view +templates/photo/exif +templates/rhs +templates/footer +templates/pager +templates/album_adder +templates/album/denied +templates/album/view +templates/album/list +templates/tagcloud +templates/custom/list +templates/tag/view +templates/tag/list +templates/tag/list_js +templates/user/view +templates/user/list +templates/user/album_list +templates/calendar +templates/memories.css +templates/memories.png +templates/picture/view +templates/systemtag/view +templates/systemtag/list +templates/recent.rss +memories.sql +MANIFEST +tag_dates +Changes +META.yml Module meta-data (added by MakeMaker) diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..44f500b --- /dev/null +++ b/META.yml @@ -0,0 +1,24 @@ +# http://module-build.sourceforge.net/META-spec.html +#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# +name: Memories +version: 1.2 +version_from: Memories.pm +installdirs: site +requires: + Cache::MemoryCache: 0 + Calendar::Simple: 0 + Class::DBI::Plugin::AbstractCount: 0 + Class::DBI::Plugin::Pager: 0 + File::Path: 0 + HTML::TagCloud: 0 + Image::ExifTool: 0 + Image::Imlib2: 0 + Image::Info: 0 + Maypole: 1.1 + Text::Balanced: 0 + Time::Piece: 0 + URI::Escape: 0 + XML::RSS: 0 + +distribution_type: module +generated_by: ExtUtils::MakeMaker version 6.17 diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..5737856 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,21 @@ +use ExtUtils::MakeMaker; +WriteMakefile( + 'NAME' => 'Memories', + 'VERSION_FROM' => 'Memories.pm', # finds $VERSION + 'PREREQ_PM' => { + Maypole => 1.1, + HTML::TagCloud => 0, + URI::Escape => 0, + Calendar::Simple => 0, + XML::RSS => 0, + Time::Piece => 0, + Class::DBI::Plugin::Pager => 0, + Class::DBI::Plugin::AbstractCount => 0, + Cache::MemoryCache => 0, + Image::Info => 0, + Image::ExifTool => 0, + File::Path => 0, + Image::Imlib2 => 0, + Text::Balanced => 0, + } +); diff --git a/Memories/Album.pm b/Memories/Album.pm new file mode 100644 index 0000000..b4f869d --- /dev/null +++ b/Memories/Album.pm @@ -0,0 +1,77 @@ +package Memories::Album; +use base qw(Memories::DBI Maypole::Model::CDBI::Plain); +__PACKAGE__->columns(Essential => qw/id name user privacy/); +Memories::Album->has_a(user => "Memories::User"); +Memories::User->has_many(albums => "Memories::Album"); + +sub view :Exported { + my ($self, $r) = @_; + my $album = $r->objects->[0]; + if ($album->privacy && $album->user != $r->user) { + $r->template("denied"); return; + } + $r->template_args->{photos} = [ $album->photos ]; +} + +sub list :Exported { + my ($self, $r) = @_; + my $page = $r->{params}{page} || 1; + my $pager = Memories::Album->pager( + Memories->config->{photos_per_page}, $page); + $r->{objects} = [$pager->search(privacy => 0)]; + $r->{template_args}{pager} = $pager; +} + +sub edit :Exported { + my ($self, $r) = @_; + use Data::Dumper; warn Dumper($r->{params}); + my $album; + if ($r->{params}{album}) { # We're adding to an album + $album = $self->retrieve($r->{params}{album}); + if (!$album or $album->user != $r->user) { + $r->template("denied"); return; + } + for (map /(\d+)/,grep /add\d+/, keys %{$r->{params}}) { + Memories::AlbumEntry->create({ + album => $album->id, + photo => $_ + }); + } + } else { + $album = $r->{objects}[0]; + if (!$album or $album->user != $r->user) { + $r->template("denied"); return; + } + for (map /(\d+)/,grep /delete\d+/, keys %{$r->{params}}) { + my ($ae) = Memories::AlbumEntry->search({ + album => $album->id, + photo => $_ + }); + $ae->delete if $ae; + } + } + $r->objects([ $album ]); + $r->template("view"); $self->view($r); +} + +package Memories::AlbumEntry; +use base qw(Memories::DBI); +__PACKAGE__->table("album_entry"); +__PACKAGE__->columns(TEMP => qw/count/); +__PACKAGE__->columns(Essential => qw/id album photo/); +__PACKAGE__->set_sql(summary => qq/ +SELECT id, album, count(*) AS count +FROM album_entry; +GROUP BY album +ORDER BY count DESC +LIMIT 50 + /); +Memories::AlbumEntry->has_a("photo" => "Memories::Photo"); +Memories::AlbumEntry->has_a("album" => "Memories::Album"); + +Memories::Photo->has_many(albums => ["Memories::AlbumEntry" => "album"]); +Memories::Photo->has_many(albumentries => "Memories::AlbumEntry"); +Memories::Album->has_many(photos => ["Memories::AlbumEntry" => "photo"] ); +Memories::Album->has_many(albumentries => "Memories::AlbumEntry"); + +1; diff --git a/Memories/Comment.pm b/Memories/Comment.pm new file mode 100644 index 0000000..2b7f0ca --- /dev/null +++ b/Memories/Comment.pm @@ -0,0 +1,7 @@ +package Memories::Comment; +use base qw(Memories::DBI); +__PACKAGE__->columns(Essential => qw/id name photo/); +__PACKAGE__->columns(Others => qw/content/); +Memories::Comment->has_a("photo" => "Memories::Photo"); + +1; diff --git a/Memories/Config.pm b/Memories/Config.pm new file mode 100644 index 0000000..9796ab0 --- /dev/null +++ b/Memories/Config.pm @@ -0,0 +1,58 @@ +package Memories::Config; +# PATHS AND URLS + +# This parameter should be the external address of your Memories +# installation +Memories->config->uri_base("http://memories.simon-cozens.org/"); + +# This is where your templates will live. +Memories->config->{template_root} = "/home/simon/maypole-sites/memories/templates"; + +# Here is where uploaded photos will be stored. Your web server user +# should own this directory. +Memories->config->{data_store} = "/web/photostore/"; + +# You also need to configure your web server so that it serves files out +# of the data store; this URL should be where that data store directory +# is exposed on the web. +Memories->config->{data_store_external} = "http://memories.simon-cozens.org/store/"; + +# Your database server: the first part should always be "dbi"; the +# second, the name of the DBD driver you're using (usually mysql unless +# you want to do your own thing); the final part, the name of the +# database. +# +# Initialize this database from memories.sql and give www-data (or +# equivalent) read/write access to all tables. +Memories->config->{dsn} = "dbi:mysql:memories"; + +# SESSION MANAGEMENT +# +# The name of any cookies this application should give out +Memories->config->{auth}{cookie_name} = "memories"; + +# Session file storage. Create these directories and have them owned by +# www-data +Memories->config->{auth}{session_args} = { + Directory => "/var/lib/memories/sessions", + LockDirectory => "/var/lib/memories/sessionlock", + }; + +# This is where your Image::Seek library will be stored. +Memories->config->{image_seek} = "/var/lib/memories/imageseek.db"; + +# DISPLAY PARAMETERS +# +# It's OK to leave these as they are. +# +# The size of thumbnails to generate +Memories->config->{thumb_size} = "90x90"; + +# Sizes you want to scale to. "full" is special. +Memories->config->{sizes} = + [ qw/ 150x100 300x200 640x480 800x600 1024x768 1280x1024 full /]; + +# Number of photos on a page. It's best if this is a multiple of three +Memories->config->{photos_per_page} = 21; + +1; diff --git a/Memories/DBI.pm b/Memories/DBI.pm new file mode 100644 index 0000000..594d91e --- /dev/null +++ b/Memories/DBI.pm @@ -0,0 +1,8 @@ +package Memories::DBI; +#use base qw(Class::DBI::Sweet Class::DBI::mysql); +use base qw(Class::DBI::mysql); +Memories::DBI->connection(Memories->config->{dsn}); +Memories::DBI->autoupdate(1); +use Carp qw(carp); + +1; diff --git a/Memories/Photo.pm b/Memories/Photo.pm new file mode 100644 index 0000000..4e2a59d --- /dev/null +++ b/Memories/Photo.pm @@ -0,0 +1,290 @@ +package Memories::Photo; +use strict; +use Carp qw(cluck confess); +use base qw(Memories::DBI Maypole::Model::CDBI::Plain); +use Time::Piece; +use Image::Seek; +use constant PAGER_SYNTAX => "LimitXY"; +__PACKAGE__->columns(Essential => qw(id title uploader uploaded x y)); +__PACKAGE__->untaint_columns(printable => [qw/title/]); +__PACKAGE__->columns(TEMP => qw/exif_object/); +__PACKAGE__->set_sql(recent => q{ +SELECT __ESSENTIAL__ +FROM __TABLE__ +ORDER BY uploaded DESC +LIMIT 4 +}); + +__PACKAGE__->has_many(comments => "Memories::Comment"); +__PACKAGE__->has_a(uploader => "Memories::User"); +__PACKAGE__->has_a(uploaded => "Time::Piece", + inflate => sub { Time::Piece->strptime(shift, "%Y-%m-%d %H:%M:%S") }, + deflate => 'datetime', +); + +sub do_upload :Exported { + my ($self, $r) = @_; + my %upload = $r->upload("photo"); + + # XXX Stop anonymous uploads! + my $photo = $self->create({ + uploader => $r->user, + uploaded => Time::Piece->new(), + title => ($r->params->{title} || $upload{filename}) + }); + + # Dump content + if (!open OUT, ">". $photo->path("file")) { + die "Can't write ".$photo->path("file")." because $!"; + } + # XXX Check it's a JPEG, etc. + # XXX Unzip ZIP file + print OUT $upload{content}; + close OUT; + my ($x, $y) = dim(image_info($photo->path)); + $photo->x($x); $photo->y($y); + + # Rotate? + $photo->unrotate(); + + $photo->make_thumb; + $photo->add_tags($r->{params}{tags}); + $photo->add_to_imageseek_library; + Memories->zap_cache(); + + # Add system tags here + my $tag = "date:".$photo->shot->ymd; + $photo->add_to_system_tags({tag => Memories::SystemTag->find_or_create({name =>$tag}) }); + + # Set it up to go again + $r->objects([$photo]); + $r->template("view"); + $r->message("Thanks for the upload! ". + ($r->{params}{tags} ? "" + : "Don't forget to config->uri_base."photo/view/".$photo->id."?active=tagedit\">tag your photo" + ) + ); +} + +sub view :Exported { + my ($self, $r) = @_; + if ($r->{session}{last_search}) { + my $photo = $r->{objects}[0]; + # This is slightly inefficient + my @search = split/,/, $r->{session}{last_search}; + my $found = -1; + for my $i (0..$#search) { + next unless $photo->id == $search[$i]; + $found = $i; + } + return unless $found > -1; + $r->{template_args}{next} = $self->retrieve($search[$found+1]) + if $found+1 <= $#search; + $r->{template_args}{prev} = $self->retrieve($search[$found-1]) + if $found-1 >= 0; + } +} +sub upload :Exported {} + +use Class::DBI::Plugin::Pager; +use Class::DBI::Plugin::AbstractCount; + +sub recent :Exported { + my ($self, $r) = @_; + my $page = $r->params->{page} || 1; + my $pager = $self->pager( + per_page => Memories->config->{photos_per_page}, + page => $page, + syntax => PAGER_SYNTAX, + order_by => "uploaded desc" + ); + $r->objects([$pager->retrieve_all ]); + $r->{template_args}{pager} = $pager; + $r->last_search; +} + +sub add_comment :Exported { + my ($self, $r, $photo) = @_; + $r->template("view"); + $r->objects->[0]->add_to_comments({ + name => $r->params->{name}, + content => $r->params->{content} + }); +} + +sub format { + "jpg" # For now +} + +use Cache::MemoryCache; +use Image::Info qw(dim image_info); +use Image::ExifTool; +my $cache = new Cache::MemoryCache( { 'namespace' => 'MemoriesInfo' }); + +sub add_to_imageseek_library { + my $self = shift; + Image::Seek::cleardb(); + my $img = Image::Imlib2->load($self->path("file")); + + Image::Seek::add_image($img, $self->id); + # Merge this new one into the main database; there is a bit of a + # race condition here. XXX + Image::Seek::loaddb(Memories->config->{image_seek}); + Image::Seek::savedb(Memories->config->{image_seek}); +} + +sub recommended_tags { + my $self = shift; + my %tags = map { $_->name => $_ } + map { $_->tags } + $self->find_similar(3); + values %tags; +} + +sub find_similar { + my ($self, $count) = @_; + Image::Seek::cleardb(); + Image::Seek::loaddb(Memories->config->{image_seek}); + my @res = map {$_->[0] } Image::Seek::query_id($self->id, $count); + shift @res; # $self + map { $self->retrieve($_) } @res; +} + +sub unrotate { + my $self = shift; + my $orient = $self->exif_info->{EXIF}->{Orientation}; + return if $orient !~/Rotate (\d+)/i; + my $steps = $1/90; + my $img = Image::Imlib2->load($self->path("file")); + $img->image_orientate($steps); + $img->save($self->path("file")); +} + +sub exif_info { + my $self = shift; + my $info = $self->exif_object; + return $info if $info; + # Get it from the Cache + if (!($info = $cache->get($self->id))) { + # Create it + $info = $self->_exif_info; + $cache->set($self->id, $info); + } + $self->exif_object($info); + $info; +} + +sub _exif_info { + my $exifTool = new Image::ExifTool; + $exifTool->Options(Group0 => ['EXIF', 'MakerNotes', 'Composite']); + my $info = $exifTool->ImageInfo(shift->path); + my $hash = {}; + foreach my $tag ($exifTool->GetFoundTags('Group0')) { + my $group = $exifTool->GetGroup($tag); + my $val = $info->{$tag}; + next if ref $val eq 'SCALAR'; + next if $val =~ /^[0\s]*$/; + $hash->{$group}->{$exifTool->GetDescription($tag)} = $val; + } + return $hash; +} + +# XXX Cache this +sub dimensions { join "x", $_[0]->x, $_[0]->y } + +sub is_bigger { + my ($self, $size) = @_; + return 1 if $size eq "full"; + my ($w, $h) = ($self->x, $self->y); + my ($w2, $h2) = split /x/, $size; + return 1 if $w > $w2 or $h > $h2; + return 0; +} + +sub sized_url { # Use this rather than ->path from TT + my ($self, $size) = @_; + my $url = Memories->config->{data_store_external}; + my $resized = Memories->config->{sizes}->[$size]; + if (!$resized) { cluck "Asked for crazy size $size"; return; } + if ($resized eq "full") { return $self->path("url") } + $self->scale($resized) + unless -e $self->path( file => $resized ); + return $self->path(url => $resized); +} + +sub path { + my ($self, $is_url, $scale) = @_; + my $path = + Memories->config->{$is_url eq "url" ? "data_store_external" : "data_store" }; + if ($scale) { $path .= "$scale/" } + # Make dir if it doesn't exist, save trouble later + use File::Path; + if ($is_url ne "url") {mkpath($path);} + $path .= $self->id.".".$self->format; + return $path; +} + +sub thumb_url { shift->path(url => Memories->config->{thumb_size}); } +sub make_thumb { shift->scale(Memories->config->{thumb_size}, 1); } + +use Image::Imlib2; +sub scale { + my ($self, $scale, $swap) = @_; + my ($x, $y) = split /x/, $scale; + # Find out smaller axis + my ($cur_x, $cur_y) = ($self->x, $self->y); + if (!$swap) { + if ($cur_x < $cur_y) { $y = 0 } else { $x = 0 } + } else { + if ($cur_x > $cur_y) { $y = 0 } else { $x = 0 } + } + my $img = Image::Imlib2->load($self->path("file")); + unless ($img) { + cluck "Couldn't open image file ".$self->path("file"); + return; + } + $img = $img->create_scaled_image($x, $y); + $img->image_set_format("jpeg"); + my $file = $self->path( file => $scale ); + $img->save($file); + if ($!) { + cluck "Couldn't write image file $file ($!)"; + return; + } +} + +use Text::Balanced qw(extract_multiple extract_quotelike); +sub edit_tags :Exported { + my ($self, $r) = @_; + my $photo = $r->objects->[0]; + my %params = %{$r->params}; + for (keys %params) { + next unless /delete_(\d+)/; + my $tagging = Memories::Tagging->retrieve($1) or next; + next unless $tagging->photo->id == $photo->id; + $tagging->delete; + } + $photo->add_tags($params{newtags}); + $r->template("view"); +} + +sub add_tags { + my ($photo, $tagstring) = @_; + + for my $tag (map { s/^"|"$//g; $_} extract_multiple(lc $tagstring, [ \&extract_quotelike, qr/([^\s]+)/ ], undef,1)) { + $photo->add_to_tags({tag => Memories::Tag->find_or_create({name =>$tag}) }) + } +} + +sub shot { + my $self = shift; + my $exif = $self->exif_info->{EXIF}; + my ($dt) = + grep {$_ and /[^ 0:]/} + ($exif->{ 'Shooting Date/Time' }, + $exif->{ 'Date/Time Of Digitization' }, + $exif->{ 'Date/Time Of Last Modification' }); + if (!$dt) { return $self->uploaded } + return Time::Piece->strptime($dt, "%Y:%m:%d %T") || $self->uploaded; +} +1; diff --git a/Memories/SystemTag.pm b/Memories/SystemTag.pm new file mode 100644 index 0000000..cdb8e14 --- /dev/null +++ b/Memories/SystemTag.pm @@ -0,0 +1,63 @@ +package Memories::SystemTag; +use strict; +use base qw(Memories::DBI Maypole::Model::CDBI::Plain); +__PACKAGE__->columns(Essential => qw/id name/); +__PACKAGE__->table("system_tag"); + +Memories::Photo->set_sql(sorted_by_system_tag => q/ +SELECT photo.id as id, title, uploader, uploaded, x, y +FROM photo, system_tag, system_tagging +WHERE system_tagging.photo = photo.id + AND system_tagging.tag = system_tag.id + AND system_tag.id = ? +ORDER BY photo.uploaded DESC +/ +); +sub view :Exported { + my ($self, $r) = @_; + my $tag; + my $page = $r->params->{page} || 1; + my $pager = Class::DBI::Pager::_pager("Memories::Photo", + Memories->config->{photos_per_page}, $page); + $r->{template_args}{pager} = $pager; + + if (!$r->objects) { + $tag = $self->search(name => $r->{args}->[0])->first; + } else { + $tag = $r->objects->[0]; # Should hardly happen + } + $r->{template_args}{tag} = $tag; + $r->{template_args}{tags} = [$tag]; # For selector + $r->{template_args}{photos} = + [$pager->search_sorted_by_system_tag($tag->id)]; + $r->last_search; +} + +package Memories::SystemTagging; +use base qw(Memories::DBI); +use Class::DBI::Pager; +__PACKAGE__->table("system_tagging"); +__PACKAGE__->columns(TEMP => qw/count/); +__PACKAGE__->columns(Essential => qw/id tag photo/); +__PACKAGE__->set_sql(summary => qq/ +SELECT id, system_tag, count(*) AS count +FROM system_tagging +GROUP BY system_tag +ORDER BY count DESC +LIMIT 50 + /); +__PACKAGE__->set_sql(all => qq/ +SELECT id, tag, count(*) AS count +FROM system_tagging +GROUP BY system_tag +ORDER BY count DESC + /); +Memories::SystemTagging->has_a("photo" => "Memories::Photo"); +Memories::SystemTagging->has_a("tag" => "Memories::SystemTag"); + +Memories::Photo->has_many(system_tags => ["Memories::SystemTagging" => "system_tag"]); +Memories::Photo->has_many(system_taggings => "Memories::SystemTagging"); +Memories::SystemTag->has_many(photos => ["Memories::SystemTagging" => "photo"] ); +Memories::SystemTag->has_many(system_taggings => "Memories::SystemTagging"); + +1; diff --git a/Memories/Tag.pm b/Memories/Tag.pm new file mode 100644 index 0000000..1140185 --- /dev/null +++ b/Memories/Tag.pm @@ -0,0 +1,110 @@ +package Memories::Tag; +use strict; +use base qw(Memories::DBI Maypole::Model::CDBI::Plain); +__PACKAGE__->columns(Essential => qw/id name/); + +Memories::Photo->set_sql(sorted_by_tag => q/ +SELECT photo.id as id, title, uploader, uploaded, x, y +FROM photo, tag, tagging +WHERE tagging.photo = photo.id + AND tagging.tag = tag.id + AND tag.id = ? +ORDER BY photo.uploaded DESC +/ +); + +sub view :Exported { + my ($self, $r) = @_; + my $tag; + my $page = $r->params->{page} || 1; + my $pager = Class::DBI::Pager::_pager("Memories::Photo", + Memories->config->{photos_per_page}, $page); + $r->{template_args}{pager} = $pager; + my @tags = map { $self->search(name => $_)->first } @{$r->args}; + + if (@{$r->args} > 1) { # This is actually an n-level search + my $sth = $self->multi_search(@tags); + $r->{template_args}{photos} = [ $r->{template_args}{pager}->sth_to_objects($sth) ]; + $sth->finish; + $r->{template_args}{tags} = \@tags; + } else { + if (!$r->objects) { + $tag = $tags[0]; + } else { + $tag = $r->objects->[0]; + } + $r->{template_args}{tag} = $tag; + $r->{template_args}{tags} = [$tag]; # For selector + $r->{template_args}{photos} = + [$pager->search_sorted_by_tag($tag->id)]; + } + $r->last_search(); +} + +sub multi_search { + my ($self, @tags) = @_; + my $counter = "tagaaa"; + my @stuff; + for my $tag (@tags) { + if (!$tag) { return } + push @stuff, { tag => $tag, id => $tag->id, counter => $counter++ }; + } + my $sql = "select photo.id as id, photo.title as title, uploader, uploaded, x, y +from photo, ". (join ",", map{ "tagging ".$_->{counter} } @stuff). +" where ". (join " AND ", map { "$_->{counter}.tag=$_->{id} and photo.id += $_->{counter}.photo" } @stuff); + +$sql .= " order by photo.uploaded desc"; + my $sth = $self->db_Main->prepare($sql); +} + +sub list :Exported { + my ($self, $r) = @_; + my $page = $r->params->{page} || 1; + my $pager = Memories::Tagging->pager( + Memories->config->{photos_per_page}, $page + ); + $r->{template_args}{pager} = $pager; + $r->objects([map {$_->tag} $pager->search_all]); +} + +sub list_js :Exported { + my ($self, $r) = @_; + $r->objects([ $self->retrieve_all ]); +} + +package Memories::Tagging; +use base qw(Memories::DBI); +use Class::DBI::Pager; +__PACKAGE__->columns(TEMP => qw/count/); +__PACKAGE__->columns(Essential => qw/id tag photo/); +__PACKAGE__->set_sql(summary => qq/ +SELECT id, tag, count(*) AS count +FROM tagging +GROUP BY tag +ORDER BY count DESC +LIMIT 75 + /); +__PACKAGE__->set_sql(all => qq/ +SELECT id, tag, count(*) AS count +FROM tagging +GROUP BY tag +ORDER BY count DESC + /); +__PACKAGE__->set_sql(user_summary => qq/ +SELECT tagging.id id, tag, count(*) AS count +FROM tagging, photo +WHERE tagging.photo = photo.id AND photo.uploader = ? +GROUP BY tag +ORDER BY count DESC +/); + +Memories::Tagging->has_a("photo" => "Memories::Photo"); +Memories::Tagging->has_a("tag" => "Memories::Tag"); + +Memories::Photo->has_many(tags => ["Memories::Tagging" => "tag"]); +Memories::Photo->has_many(taggings => "Memories::Tagging"); +Memories::Tag->has_many(photos => ["Memories::Tagging" => "photo"] ); +Memories::Tag->has_many(taggings => "Memories::Tagging"); + +1; diff --git a/Memories/User.pm b/Memories/User.pm new file mode 100644 index 0000000..b0020a5 --- /dev/null +++ b/Memories/User.pm @@ -0,0 +1,77 @@ +package Memories::User; +use base qw(Memories::DBI Maypole::Model::CDBI::Plain); +__PACKAGE__->columns(All => qw/id name password/); +__PACKAGE__->has_many(photos => "Memories::Photo"); + +sub register :Exported { + my ($class, $r) = @_; + my $login = $r->params->{name}; + my $pass = $r->params->{password}; + $r->template("frontpage"); + return if $r->user; + + # Do we already exist? + if (!$login) { $r->template("login"); $r->message("You didn't enter a user name"); return; } + my ($user) = $class->search(name => $login); + if ($user) { $r->template("login"); $r->message("User name already exists"); return; } + $user = $class->create({name => $login, password => $pass }); + $r->user($user, 0); + $r->login_user($user->id); + +} + +sub view :Exported { + my ($self, $r) = @_; + my $user = $r->objects->[0]; + my $page = $r->params->{page} || 1; + my $pager = Class::DBI::Pager::_pager("Memories::Photo", + Memories->config->{photos_per_page}, $page); + $r->{template_args}{photos} = [$pager->search(uploader => + $user->id,{order_by => "uploaded desc"}) ]; + $r->{template_args}{pager} = $pager; + $r->{template_args}{albums} = [$user->albums(privacy => 0)]; + $r->last_search; +} + +# Album support! + +sub edit_albums :Exported { + my ($self, $r) = @_; + $r->{template} = "view"; + $self->view($r); + my $user = $r->objects->[0]; + return unless $r->user and $r->user->id == $user->id; + if ($r->{params}{create}) { + $user->add_to_albums({ + privacy => !!($r->{params}{new_privacy}), + name => $r->{params}{new_name} + }); + } elsif ($r->{params}{changes}) { + for my $album ($user->albums) { #XXX + warn "Setting $album privacy to ". + $r->{params}{"privacy_".$album->id}; + $album->privacy(!!$r->{params}{"privacy_".$album->id}); + } + } else { + my @to_delete = map /(\d+)/, + grep /delete_\d+/, keys %{$r->{params}}; + for (@to_delete) { + my $album = Memories::Album->retrieve($_); + if ($album and $album->user->id == $user->id) { + $album->delete + } + } + } + +} + +sub api_taglist :Exported { + my ($self, $r) = @_; + my $user = $r->objects->[0]; + $r->{output} .= $_->{tag}.":".$_->{count}."\n" + for Memories::Tagging->search_user_summary($user->id); + $r->{output}.= "\n"; + $r->{content_type} = "text/plain"; +} + +1; diff --git a/README b/README new file mode 100644 index 0000000..5b1fc00 --- /dev/null +++ b/README @@ -0,0 +1,64 @@ +Updating from an earlier release? Read the bit at the bottom! + +Installing the Memories photo sharing application +------------------------------------------------- + +First, Perl modules. This is the hard part. You will need: + Maypole (good luck) + Class::DBI + DBI + DBD::mysql + Class::DBI::mysql + Image::Info + Image::Imlib2 (requires libimlib2 and libimlib2-dev packages) + Time::Piece + Cache::Cache + Maypole::Plugin::Upload + Maypole::Plugin::Authentication::UserSessionCookie + HTML::TagCloud + XML::RSS + +Apache mod_perl is recommended. Memories can also be run as a CGI +application, but you're on your own. + +You will also need a MySQL database. Again, in theory other databases +can be used, but in practice, you're on your own again. + +Configure /etc/memories/Config.pm to your site, and follow the instructions in +there - it will require you to set other things up as well. + +Test that everything works: + + perl -MMemories -e1 + +If this produces no errors, you're good to configure Apache. I have +Memories living in "/home/simon/maypole-sites/memories", and my photo +storage in "/opt/store/", and so my configuration looks like this: + + + use lib qw( /home/simon/maypole-sites/memories/ ); + + + PerlModule Memories + Alias /memories/static/ /home/simon/maypole-sites/memories/templates/static/ + Alias /memories/store/ /opt/store/ + + + PerlHandler Memories + SetHandler perl-script + + +Restart Apache, register yourself and get uploading! + + +Updating Memories 1.0 to 1.1 +---------------------------- + +1) Create the system_tags and system_taggings table as in memories.sql + +2) Run the tag_dates script. + +Updating Memories 1.1 to 1.2 +---------------------------- + +No changes. diff --git a/Tagtools.pm b/Tagtools.pm new file mode 100644 index 0000000..0fc2529 --- /dev/null +++ b/Tagtools.pm @@ -0,0 +1,76 @@ +package Tagtools; +use HTML::TagCloud; +use Carp; +use Cache::FileCache; +use Storable qw(freeze); use MIME::Base64; +use Calendar::Simple; +sub import { + my $whence = caller; + my ($class) = @_; + my %cache_options = ( 'namespace' => $whence.'TagTools', + 'default_expires_in' => 600 ); + my $cache = + new Cache::FileCache( \%cache_options ) or + croak( "Couldn't instantiate FileCache" ); + *{$whence."::zap_cache"} = sub { $cache->Clear }; + *{$whence."::do_cached"} = sub { + my ($self, $codeblock,$arg) = @_; + my $key = 0+$codeblock; if ($arg) { $key .=":".encode_base64(freeze(\$arg)); } + my $c = $cache->get(0+$codeblock); return @$c if $c; + my @stuff = $codeblock->($arg); + $cache->set(0+$codeblock, [ @stuff ]); + return @stuff; + }; + *{$whence."::_tagcloud"} = sub { + my $cloud = HTML::TagCloud->new(); + my $base = $whence->config->uri_base."tag/view/"; + for my $tagging (($whence."::Tagging")->search_summary) { + my $name = $tagging->tag->name; + $cloud->add($name, $base.uri_escape($name), $tagging->{count}) + } + $cloud + }; + *{$whence."::_calendar"} = sub { + my $self = shift; + my $arg = shift; + my ($y, $m) = split /-/, ($arg || Time::Piece->new->ymd); + my @m = Calendar::Simple::calendar($m, $y); + my @month; + foreach my $week (@m) { + my @weekdays; + foreach my $day (@$week) { + my $d = { day => $day }; + if ($day) { + my $tag = "date:$y-$m-".sprintf("%02d", $day); + my ($x) = ($whence."::SystemTag")->search(name => $tag); + if ($x) { $d->{tag} = "/system_tag/view/$tag" } + } + push(@weekdays, $d); + } + push(@month, \@weekdays); + } + return \@month; + }; + for my $thing (qw(tagcloud calendar)) { + *{$whence."::$thing"} = sub { shift->do_cached($thing, @_) } + } + +} + + +# THIS IS A HACK + +use Time::Seconds; +sub Time::Piece::next_month { + my $tp = shift; + my $month = $tp + ONE_MONTH; + return if $month > Time::Piece->new; + return $month +} +sub Time::Piece::prev_month { + my $tp = shift; + my $month = $tp - ONE_MONTH; + return $month +} + +1; diff --git a/debian/apache.conf b/debian/apache.conf new file mode 100644 index 0000000..36cef61 --- /dev/null +++ b/debian/apache.conf @@ -0,0 +1,18 @@ +PerlModule Memories + +Alias /memories/static/ /etc/memories/templates/static/ +Alias /memories/store/ /var/www/memories/store/ + + + PerlHandler Memories + SetHandler perl-script + + +# +# Alias /static/ /etc/memories/templates/static/ +# Alias /store/ /var/www/memories/store/ +# +# PerlHandler Memories +# SetHandler perl-script +# +# diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..4398e70 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +memories (1.1-1) unstable; urgency=low + + * Initial release - closes: #345277 + + -- Ben Hutchings Sat, 31 Dec 2005 02:59:59 +0000 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +4 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..0aee180 --- /dev/null +++ b/debian/control @@ -0,0 +1,31 @@ +Source: memories +Maintainer: Ben Hutchings +Section: web +Priority: extra +Build-Depends-Indep: debhelper (>= 4), perl (>= 5.6.0-16) +Standards-Version: 3.6.2 + +Package: memories +Architecture: all +Depends: ${perl:Depends}, + libapache-session-perl, + libcache-cache-perl, + libcalendar-simple-perl, + libclass-dbi-perl, + libclass-dbi-plugin-pager-perl, + libhtml-tagcloud-perl, + libimage-exiftool-perl, + libimage-imlib2-perl, + libimage-info-perl, + libmaypole-perl, + libmaypole-plugin-authentication-usersessioncookie-perl, + libmaypole-plugin-upload-perl, + libtime-piece-perl +Recommends: libapache-mod-perl | libapache2-mod-perl2, + libclass-dbi-mysql-perl, + libdbd-mysql-perl +Description: a web-based photo sharing application + Memories allows multiple users to browse and share photos. Each + user's photos can be seen in blog format or as albums. Each photo can + be tagged with names, places, and other descriptive terms, and found + through these tags. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..84b7ac4 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,8 @@ +This is the Debian-packaged version of Memories, put together by Ben +Hutchings using source from: + + http://maypole.perl.org/?Applications + +The author of Memories is Simon Cozens . + +TODO: LICENCE TEXT diff --git a/debian/memories.docs b/debian/memories.docs new file mode 100644 index 0000000..5b4892f --- /dev/null +++ b/debian/memories.docs @@ -0,0 +1,2 @@ +Changes +README diff --git a/debian/rules b/debian/rules new file mode 100644 index 0000000..c34b398 --- /dev/null +++ b/debian/rules @@ -0,0 +1,36 @@ +#!/usr/bin/make -f + +build : + +binary : binary-arch binary-indep + +binary-arch : + +binary-indep : + dh_testroot + install -d debian/memories/usr/share/perl5/Memories + install Memories.pm debian/memories/usr/share/perl5/ + install Memories/*.pm debian/memories/usr/share/perl5/Memories/ + install -d debian/memories/etc/memories + mv debian/memories/usr/share/perl5/Memories/Config.pm debian/memories/etc/memories/ + cp -dR templates debian/memories/etc/memories/ + ln -s /etc/memories/Config.pm debian/memories/usr/share/perl5/Memories/ + install debian/apache.conf debian/memories/etc/memories/ + install -d debian/memories/etc/apache/conf.d + ln -s ../../memories/apache.conf debian/memories/etc/apache/conf.d/memories + install -d debian/memories/usr/share/memories + install memories.sql tag_dates debian/memories/usr/share/memories/ + install -d debian/memories/var/lib/memories + install -d -o www-data -g www-data -m 770 debian/memories/var/lib/memories/sessions debian/memories/var/lib/memories/sessionlock + dh_perl + dh_installchangelogs + dh_installdocs + dh_compress + dh_fixperms -X /var/lib/memories/session + dh_gencontrol + dh_builddeb + +clean : + dh_clean + +.PHONY : build binary binary-arch binary-dep clean diff --git a/memories.sql b/memories.sql new file mode 100644 index 0000000..9ebe256 --- /dev/null +++ b/memories.sql @@ -0,0 +1,58 @@ +CREATE TABLE photo ( + id integer not null auto_increment primary key, + title varchar(255), + uploader integer, + uploaded datetime, + x integer, + y integer +); + +CREATE TABLE comment ( + id integer not null auto_increment primary key, + name varchar(255), + content text, + photo integer +); + +CREATE TABLE user ( + id integer not null auto_increment primary key, + name varchar(255), + password varchar(255) +); + +CREATE TABLE tag ( + id integer not null auto_increment primary key, + name varchar(255) +); + +CREATE TABLE tagging ( + id integer not null auto_increment primary key, + tag integer, + photo integer +); + +CREATE TABLE album ( + id integer not null auto_increment primary key, + name varchar(255), + user integer, + privacy integer +); + +CREATE TABLE album_entry ( + id integer not null auto_increment primary key, + album integer, + photo integer +); + +CREATE TABLE system_tag ( + id integer not null auto_increment primary key, + name varchar(255) +); + +CREATE TABLE system_tagging ( + id integer not null auto_increment primary key, + tag integer, + photo integer +); + + diff --git a/templates/album/denied b/templates/album/denied new file mode 100644 index 0000000..7b641a9 --- /dev/null +++ b/templates/album/denied @@ -0,0 +1,7 @@ +[% INCLUDE header %] +
+This album has been marked private, and you do not have permission to +view it. +
+ +[% INCLUDE footer; %] diff --git a/templates/album/list b/templates/album/list new file mode 100644 index 0000000..0229289 --- /dev/null +++ b/templates/album/list @@ -0,0 +1,25 @@ +[% INCLUDE header %] +[% PROCESS macros %] + + +[% WHILE objects.size > 0 %] + + [% SET minilist = objects.splice(0,3) %] + [% FOR album = minilist %] + + [% END %] + +[% END %] +
+ [% album %]
+

+ + [%album.photos.size %] + photo[%-"s" IF album.photos.size != 1 %] +
+ [%album.user%] +
+
+[% INCLUDE pager %] +[% INCLUDE footer %] diff --git a/templates/album/view b/templates/album/view new file mode 100644 index 0000000..a8ad02d --- /dev/null +++ b/templates/album/view @@ -0,0 +1,9 @@ +[% INCLUDE header %] +[% PROCESS macros %] +

[% album.name %]

+[% IF album.user == request.user %] + [% view_page_of(photos, "delete") %] +[% ELSE %] +[% view_page_of(photos) %] +[% END %] +[% INCLUDE footer %] diff --git a/templates/album_adder b/templates/album_adder new file mode 100644 index 0000000..2c3db73 --- /dev/null +++ b/templates/album_adder @@ -0,0 +1,9 @@ +[% IF request.user.albums.size > 0 %] + Add to: + + +[% END %] diff --git a/templates/calendar b/templates/calendar new file mode 100644 index 0000000..c71bf82 --- /dev/null +++ b/templates/calendar @@ -0,0 +1,43 @@ +
+[% IF request.action == "view" %] + [% SET shot = photo.shot %] +[% END %] +[% SET date = view_cal || shot || now %] +[% SET imp_date = shot || now %] +[% SET next = date.next_month %] +[% SET prev = date.prev_month %] +[% SET calendar = request.calendar(date.ymd) %] + + + + + + + + + +[% FOR week = calendar %] + + +[% FOR day = week %] + [% IF day.tag %] + [% IF shot AND day.day == imp_date.mday %] + + [% ELSE %] + + [% END %] +[% END %] + +[% END %] +
« + [% date.strftime("%Y-%m") %] + [% IF next %] + » + [% END %] +
S M + TWTFS
+ [% ELSE %] + + [% END %] + [% day.day %] [% day.day %]
+
diff --git a/templates/custom/list b/templates/custom/list new file mode 100644 index 0000000..3dd50b3 --- /dev/null +++ b/templates/custom/list @@ -0,0 +1,20 @@ +[% INCLUDE header %] +[% PROCESS macros %] + + +[% WHILE objects.size > 0 %] + + [% SET minilist = objects.splice(0,3) %] + [% FOR object = minilist %] + + [% END %] + +[% END %] +
+ [% object %]
+

+ + [%object.photos.size %] + photo[%-"s" IF object.photos.size != 1 %] +
+[% INCLUDE footer %] diff --git a/templates/footer b/templates/footer new file mode 100644 index 0000000..64186ac --- /dev/null +++ b/templates/footer @@ -0,0 +1,9 @@ + + + + [% INCLUDE rhs %] + + + + + diff --git a/templates/frontpage b/templates/frontpage new file mode 100644 index 0000000..9bd9576 --- /dev/null +++ b/templates/frontpage @@ -0,0 +1,34 @@ +[% INCLUDE header %] +

Welcome to Memories

+ +

+ Memories is a site where you can upload and share your photos. +

+

+ To view other people's photos, look at the user list or the album list. +

+

+ Alternatively, Memories supports tagging photos with names, places, or + other descriptive terms. Browse based on the tags on the right, or look + at the full tag list. +

+
+[% IF request.user %] +

+Now that you're logged in, you can start uploading photos. Create some albums or +check out what you've uploaded at your home page. +

+[% ELSE %] +

+To get started uploading your own photos, you'll need to log in. If you +don't have an account, just go to the log in page anyway, choose a +username and password, and click "Register". So long as nobody else has that name, +we'll create an account for you immediately. +

+[% END %] +[% INCLUDE footer %] diff --git a/templates/header b/templates/header new file mode 100644 index 0000000..3241c67 --- /dev/null +++ b/templates/header @@ -0,0 +1,28 @@ + + + Memories - Photo Sharing [% IF photo %] - [% photo.title; END%] + + + [% IF photos %] + +[% END %] +[% IF request.params.active == "tagedit" %] + + + + +[% ELSE %] + + +[% END %] +[% INCLUDE nav %] + + +
+ [% IF messages %] +
+
    [% FOR m = messages %]
  • [%m%]
  • [% END %] +
+ [% END %] +
diff --git a/templates/header.orig b/templates/header.orig new file mode 100644 index 0000000..2ac00f4 --- /dev/null +++ b/templates/header.orig @@ -0,0 +1,27 @@ + + + Memories - ANCC Photo Sharing + + [% IF photos %] + +[% END %] +[% IF request.params.active == "tagedit" %] + + + + +[% ELSE %] + + +[% END %] +[% INCLUDE nav %] + + +
+ [% IF messages %] +
+
    [% FOR m = messages %]
  • [%m%]
  • [% END %] +
+ [% END %] +
diff --git a/templates/login b/templates/login new file mode 100644 index 0000000..b875129 --- /dev/null +++ b/templates/login @@ -0,0 +1,34 @@ + [% INCLUDE header %] + +
+ [% IF request.user %] + Welcome, [% request.user.name %] + [% ELSE %] + + [% IF login_error %] +
[% login_error %]
+ [% END %] +
+
+ Login or register as a new user + + + + +
+ Username: + + +
+ Password: + + +
+ + +
+
+ [% END %] +
+ + [% INCLUDE footer %] diff --git a/templates/login_box b/templates/login_box new file mode 100644 index 0000000..b875129 --- /dev/null +++ b/templates/login_box @@ -0,0 +1,34 @@ + [% INCLUDE header %] + +
+ [% IF request.user %] + Welcome, [% request.user.name %] + [% ELSE %] + + [% IF login_error %] +
[% login_error %]
+ [% END %] +
+
+ Login or register as a new user + + + + +
+ Username: + + +
+ Password: + + +
+ + +
+
+ [% END %] +
+ + [% INCLUDE footer %] diff --git a/templates/macros b/templates/macros new file mode 100644 index 0000000..05824ef --- /dev/null +++ b/templates/macros @@ -0,0 +1,67 @@ +[% MACRO minithumb(photo) BLOCK %] + + [%photo.title|html%] + +[% END; MACRO thumb(photo, album) BLOCK %] + + + + + [% IF request.user %] + [% IF album == 1 %] + + [% ELSIF album == 2 %] + + [% END %] + [% END %] +
+ [% minithumb(photo) %] +
+ + [% photo.title |html%] + + +
+ [% FOR tag = photo.tags %] [%tag %] [% END %] +
Uploaded by + + [% photo.uploader.name |html%] +
at [% photo.uploaded %]
Add to album:
Delete from album:
+[% END %] + +[% MACRO view_page_of(photos) BLOCK; %] +[% IF request.table == "album" AND request.template == "view" %] +[% SET editing_album = 1%] +[% END %] +[% IF editing_album %] +
+[% ELSE %] + +[% END %] +[% IF !photos OR ! photos.size %] +
+ This [% request.table %] is empty! +
+[% ELSE %] + +[% WHILE photos.size > 0 %] +[% SET triple = photos.splice(0,3) %] + + [% FOR photo = triple %] + + [% END %] + +[% END %] +
+ [% IF editing_album; thumb(photo, 2); ELSE; thumb(photo, 1); END %] +
+[% END %] +[% IF editing_album %] + +[% ELSE %] +[% INCLUDE album_adder %] +[% END %] +[% INCLUDE pager %] +
+[% END %] diff --git a/templates/memories.css b/templates/memories.css new file mode 100644 index 0000000..ac5608e --- /dev/null +++ b/templates/memories.css @@ -0,0 +1,8 @@ +body { background: #afa; } + +.recentuploads { + float:right; + background: #ddd; + padding: 10px; + border: 1px solid black; +} diff --git a/templates/memories.png b/templates/memories.png new file mode 100644 index 0000000000000000000000000000000000000000..c602c08d01c3a33f7b67a03544c0b3ffffb605cd GIT binary patch literal 40186 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�c24G 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 b$DzRps3;JAe8i=N@$jfCYl3POEhGN}Hvcn< literal 0 HcmV?d00001 diff --git a/templates/nav b/templates/nav new file mode 100644 index 0000000..f42bb06 --- /dev/null +++ b/templates/nav @@ -0,0 +1,11 @@ + diff --git a/templates/pager b/templates/pager new file mode 100644 index 0000000..3b6211d --- /dev/null +++ b/templates/pager @@ -0,0 +1,32 @@ +[% MACRO link_page(num) BLOCK; %] + [% IF num == pager.current_page %] + [% IF num < 10 %] + + [% ELSE %] + [[% num %]] + [% END %] + [% ELSE %] + + [% IF num < 10 %] + + [% ELSE %] + [[% num %]] + [% END %] + + [% END %] +[% END %] +[% IF pager.last_page > 1; + SET begin_page = pager.current_page - 5; + IF begin_page < 1; + SET begin_page = pager.first_page; + END; + SET end_page = pager.current_page + 5; + IF pager.last_page < end_page; + SET end_page = pager.last_page; + END; + IF begin_page != 1; link_page(1); " ... "; END; + FOREACH num = [begin_page .. end_page]; + link_page(num); + END; + IF end_page != pager.last_page; "..."; link_page(pager.last_page); END; +END %] diff --git a/templates/photo/comment b/templates/photo/comment new file mode 100644 index 0000000..498e5b6 --- /dev/null +++ b/templates/photo/comment @@ -0,0 +1,18 @@ +
+[% FOR comment = photo.comments %] +
+ [% comment.name | html %] writes...
+ [% comment.content %] +
+[% END %] +
+
+ Name:
+ +

+ +

+
+
+
diff --git a/templates/photo/exif b/templates/photo/exif new file mode 100644 index 0000000..b60ddbc --- /dev/null +++ b/templates/photo/exif @@ -0,0 +1,16 @@ +[% SET exif = photo.exif_info %] +[% FOR group = exif.keys %] + [% SET counter = 0 %] +

[% group %]

+ + [% FOR key = exif.$group.keys %] + + [% IF counter % 2 == 0 %] [% END %] + + + [% IF counter % 2 == 1 %] + [% ELSE %] [% END %] + [% SET counter = counter + 1 %] + [% END %] +
[% key %] [% exif.$group.$key %]
 
+[% END %] diff --git a/templates/photo/recent b/templates/photo/recent new file mode 100644 index 0000000..37d1ae3 --- /dev/null +++ b/templates/photo/recent @@ -0,0 +1,18 @@ +[% INCLUDE header %] +[% PROCESS macros %] +

Recent photos

+ +[% WHILE photos.size > 0 %] +[% SET triple = photos.splice(0,3) %] + + [% FOR photo = triple %] + + [% END %] + +[% END %] +
+ [% thumb(photo, 1) %] +
+ +[% INCLUDE pager %] +[% INCLUDE footer %] diff --git a/templates/photo/similar b/templates/photo/similar new file mode 100644 index 0000000..2743353 --- /dev/null +++ b/templates/photo/similar @@ -0,0 +1,9 @@ +

+[% PROCESS macros; FOR sim = photo.find_similar(4); minithumb(sim); END %] +

+ +

+Suggested tags: [% FOR tag = photo.recommended_tags; %] +[%tag.name%] +[% END %] +

diff --git a/templates/photo/tagedit b/templates/photo/tagedit new file mode 100644 index 0000000..a34d8bd --- /dev/null +++ b/templates/photo/tagedit @@ -0,0 +1,21 @@ +
+Tagging advice: Tags should be words, (portrait, henry) or +phrases surrounded by double quotes. ("tall buildings") You can +put any number of tags in the "add tags" box, like this: +

+ landscape cambridge "tall buildings" +

+
+

Suggested tags

+
+
+ +Delete tags:
+
    +[% FOR tagging = photo.taggings %] +
  • [% tagging.tag %] +[% END %] +
+

Add tags:

+
diff --git a/templates/photo/upload b/templates/photo/upload new file mode 100644 index 0000000..ad88ff8 --- /dev/null +++ b/templates/photo/upload @@ -0,0 +1,63 @@ + + + Memories - Photo Sharing + + + + + +[% INCLUDE nav %] + + +
+ [% IF messages %] +
+
    [% FOR m = messages %]
  • [%m%]
  • [% END %] +
+ [% END %] +
+

Upload a photo

+

+This is where you can upload your photographs. At the moment, you must +upload them one at a time; in the near future, you will be able to +upload a Zip file containing several photos. +

+

+Please note that it may take a while to transfer your photograph to the +server, so don't press stop or reload after pressing the Upload button. +

+ +
+ + + + + + + + + + + + +
Title:
Tags:
  +
+Tagging advice: Tags should be words, (portrait, henry) or +phrases surrounded by double quotes. ("tall buildings") You can +put any number of tags in the "add tags" box, like this: +

+ landscape cambridge "tall buildings" +

+You don't have to do this at this stage, and you can always re-tag +photos later, but if do it now, it saves you forgetting later! Try to +re-use tags that other people have used, then all the photos can be +found under the same tag. Use the tag list to find existing tags. +
+

+Suggested tags +

+
+
File
+
+[% INCLUDE footer %] diff --git a/templates/photo/view b/templates/photo/view new file mode 100644 index 0000000..b8a06bf --- /dev/null +++ b/templates/photo/view @@ -0,0 +1,104 @@ +[% INCLUDE header %] +[% SET tab = request.params.active || "comment" %] +[% SET url = base _ "/photo/view/" _ photo.id; %] + + + + + + + + + +
+ [% IF prev %] + << [% prev %] + [% END %] +

[% photo.title %]

+ [% IF next %] + [% next %] >> + [% END %] +[% IF request.user == photo.uploader %] +

Delete this + photo

+
+ + +
+[% END %] +
+[% FOR tag = photo.tags; + ""; + tag; + " ("; + tag.taggings.size; ")"; + ", " UNLESS tag == photo.tags.last; +END %] +
+[% IF photo.albums %] +In albums: +[% FOR album = photo.albums; %] +[%album.name%] ([%album.photos.size%]) +[% ";" UNLESS album == photo.albums.last %] +[% END %] +[% END %] + + +Photo shot on [% photo.shot.ymd %]
+Uploaded by [% +photo.uploader %] +
+
+ [% SET sizes = request.config.sizes %] + [% IF request.params.exists("scale") %] + [% SET size = request.params.scale %] + [% ELSIF request.session.scale %] + [% SET size = request.session.scale %] + [% ELSE; SET size = 1; END; %] + +

(Original size [%photo.dimensions %])

+

+ Size: + [% SET i = 0; WHILE i < sizes.size %] + [% IF photo.is_bigger(sizes.$i); %] + [% IF i == size %] + [% sizes.$i %] + [% ELSIF sizes.$i == "full" %] + full + [% ELSE %] + [% sizes.$i %] + [% END %] + [% END; %] + [% SET i = i + 1 %] + [% END %] +

+ [%# Now put it back in the session %] + [% SET request.session.scale = size %] +
+ +[% MACRO do_tab(tabname, label) BLOCK; %] +
  • + [% IF tab == tabname %] + [% ELSE %] + [% END %] + [%label%] +
  • +[% END %] + +
      + [%do_tab("comment", "Comments") %] + [%do_tab("exif", "Photo info") %] + [%do_tab("tagedit", "Edit tags") %] + [%do_tab("similar", "Similar photos") %] +
    +
    +[% +IF request.params.active == "tagedit"; INCLUDE tagedit; +ELSIF request.params.active == "exif"; INCLUDE exif; +ELSIF request.params.active == "similar"; INCLUDE similar; +ELSE; INCLUDE comment; END; +%] +
    +[% +INCLUDE footer; +%] diff --git a/templates/picture/view b/templates/picture/view new file mode 100644 index 0000000..976b786 --- /dev/null +++ b/templates/picture/view @@ -0,0 +1,7 @@ +[% INCLUDE header %] + +[%# Scale to 800 across by default, else scale to appropriate size %] + + diff --git a/templates/recent.rss b/templates/recent.rss new file mode 100644 index 0000000..51be582 --- /dev/null +++ b/templates/recent.rss @@ -0,0 +1,22 @@ + + + + Memories photostream + [%base%] + Recent photos from [%base %] +[% FOR photo = request.recent_uploads %] + + [%photo.title|html%] + [%photo.shot.strftime("%a, %d %b %Y %H:%M:%S %z") %] + + +[% FILTER html %] + + [%photo.title|html%] + +[% END %] + + +[% END %] + + diff --git a/templates/rhs b/templates/rhs new file mode 100644 index 0000000..2816a73 --- /dev/null +++ b/templates/rhs @@ -0,0 +1,49 @@ + +[% IF ! request.user %] + + + +[% ELSE %] + + +[% END %] + + + + + + + + + + + + + +[% IF request.user %] + + + + + + +[% END %] +
    Login
    [% request.user.name %]
    List users
    View all tags
    Recently uploaded
    List albums
    Home
    Upload
    +[% INCLUDE calendar %] +[% IF photos %] +

    + +RSS + +

    +[% END %] + [% INCLUDE tagcloud %] diff --git a/templates/robots.txt b/templates/robots.txt new file mode 100644 index 0000000..7e053ec --- /dev/null +++ b/templates/robots.txt @@ -0,0 +1,5 @@ +User-agent: NaverBot +Disallow: / + +User-agent: * +Disallow: /tag/view/ diff --git a/templates/static/1-s.gif b/templates/static/1-s.gif new file mode 100644 index 0000000000000000000000000000000000000000..304f6ce9a39bc7cd65fac774f0902818271a9e8d GIT binary patch literal 646 zcmZ?wbhEHb6k!lyc$Ud<V-RmW{ZcMv_`#ezH)q_vHDleHitAUW+`fJL>7#ic-Y#7*Kj_8N z`KM3z%$gOjZcYAyeWmNy7OYsFaByGinN!V^COTie+;!n>+wr5F2ltoGn;WoWTm9t8 zF4L#`Enk+nV|)Io<4q6l&Ny_iY~S9ZJ-eE&UYfLbcZHXi*OVzf2llp1o#M53ch#o# zRrBUXZr+qSe_qV8r4ch`_+7u+Gi8eVvSm@*w&uKkv0(9{uyt#brcUvB@^HqE?FIL4 zPxAKmet2*8>63LQj&)U6S5KerwRvO4`n6?OE>4;?GwAKB#dGIGpFi8SbZN}X=L;`i z?3_0*bj!w?RV(r~tgF0wxo5@<{~6N*H*d;1dZ>NYOz+R1KOa7Pc)|pSkMEZK|NozX zVxahwg^__FlR*b$5-3g>*dI4!HZ^6mwzZ3;Hg~u8wxxCTw7NK`7(}r!wNGen^)gfs zXJu7l7U-SR#w3?y!ojF2$kLbA?wh`2hm$8WTVK0?u8hSFd!69O{i|4|<%81AZ21{a zwXsR*87m}M`-xv|XAsxW3Q1;u(8j>%oF1kq__FmiUwWeXi%+iucgPCQdii3m<)fud z>l^e>Jb0C~VPdPZO^wJw(UgN6I&S*}4m7Aa&Wy^KkkT+I`(Tr?_O>S(0jiBnCh`|K y5|Wf!`M5+R3?3*wKG>qlb%x_tN23EHC;KEN2ZeIX#cv)ATQC;r9U=0A#aVP5l literal 0 HcmV?d00001 diff --git a/templates/static/1.gif b/templates/static/1.gif new file mode 100644 index 0000000000000000000000000000000000000000..0eb416fb09f3479f13469ed279f6ed1cac254f93 GIT binary patch literal 649 zcmZ?wbhEHb6k!lyc$UEM`o)4XryCwUdi3kpuVY6m7cB~&Gskz@G`FXZW**pIeE06% zo7Z|KOmJ*zahg55Z0qK<> z%!z*g{_Tkqi+AqqPDt=tyg1|joyjdN&VT;=Uc5LhD=VYByY%<(ABPUDoj8#gv3BzU*908MVFuMfCz&!j0_A33_2i7Kykvr{;VOPsYzK&PRHL-!9Y(&ym>-` zxm5hrX;U3MMT92G`b=LC&tt;G-0U$eHdauWWg#EevH<(3Y@Cej@v#eeG?~=Rrm-<@ z=VW1BDC!nDoj>mUg@y6%`U}`&Ss2e>3XQoG%X)jEtKI#Facm2fr0>T+S`e)AmVsgC zf~df^ig95Jy(8}PF?{%>aD`9N#i_CS*(;%bZSequ6`~0Tne;i>*c1#Na<}js9|%|w zw32~=<$@nmgup{?J|3$B9s&;(noYa~rsQl?aJaz0s;hNpjUsDvYeX{}M@CYTM-LnC SoD(ksm5z4>B``5ESOWkt2L+7) literal 0 HcmV?d00001 diff --git a/templates/static/2-s.gif b/templates/static/2-s.gif new file mode 100644 index 0000000000000000000000000000000000000000..e6c405c13ccfa2026687c9728f2d028baf79f783 GIT binary patch literal 718 zcmV;<0x|tZNk%w1VH5xq0Oo%H%*u_(#)-+te%IBE*3*-Ge0-CWM4z5gqM=>b*N>Z; zOwrDTo10O$wSK3jb4p4|xVLNT=%VcDqu<<`ySahr<)85FrQOggVWHF%gKz}+uQEzq?MINmX=B5 z;h0QJOyJ#`%*un#%!R(ZdZwjV)Y6Htu6gO_p`)Q?xVCoO*_E!XWS^a7+Sin(rdYVP zZL+U=qoP*U(~6drO~Jo++}W6zmrBRRe5R#m&C7+=(~8&BkgcnC&&`U#!NIk%e3+M2 zxwvrG)s&Z(RJgW(si3($9+P=%B;EhO@D0x3z4eqG+e4YP`96zPy9i){e%+iKnJp<>H*Hsb;CD zUYeOpl$1=CmQSIeX4TV-qoQTd&5z8=f%Eh8!^6XnkU;V7rvLx{A^8LW004ggEC2ui z02BZe000O7fPaF5euajIDSLy9hmD4Oij0LBK{PZbMhOxYg^+`W18`n3WFAT|F$o-; zo}dX^K5riza%W6A1Dk$)hAk!+bA7_YEMqVl9E}TiXdiubJ9cSWeIYUv5seZ88!UZN zHEtvU27L%8HjNk+I>J_L*logC0TqrIAUIb`C>3Q8eZn{(ku$GUYJxwH3=->f_hZhqr5P6}YZ=|-oK>Be}z*8UqJJ~vH ATL1t6 literal 0 HcmV?d00001 diff --git a/templates/static/2.gif b/templates/static/2.gif new file mode 100644 index 0000000000000000000000000000000000000000..251beccdf4ce570d9458afa78f2acf0be2ef05df GIT binary patch literal 694 zcmZ?wbhEHb6k!lyc$UC$^IFfeX>N-a1<#q|d+ccCqeqW^{rYwJV%yV4Gq-L|yL@_7_i>;MmgQ^!xYkKY#x8^*Q9_<-LFZ{>StX2sOi zInJNowQN~tLV|Zff>(EUNqv35p+jr`{Q13Y+qU|8hb2oIHf(5~H!mhFEp5q?w8loa z_wV0kWH>KgoVIP-mLo?t%$XB?_wKRZzkhUhm+sinRaY0#*y!5d?^sgewqQYbak1;1 zIZV^t8+SYW@~1q%Yz4JGBVP(Z{NOiXLnj!Ku1T%y0rMjSr{1@5*TzqmVn}ff&G6&LQ|8mM7X4ikAk_c4S(~5 zgb>~Msne!vIS30(^p~5yAf8vAm905oDr4;GSZ>bP>0(^Vl+@yxnV6z@qGFkuV;QIN z>aaRnO^uCWVUOX6V-Q(2O;p`!>aM8hxM&83^D*%orW&~3h>f}ZIG!bL?=(Z{X-qM3 zaj)Mp9GdEGF`biP#~bN_1KiEt8ci)y5fhj+`0Si&FESmFo6fkvDud(d(*t69oGN+~ znY!edn11gth)B?M;Z$GAu_SqB&*{cO(^WPTE;=|g<%;diwdB-3)UA`CcH_dL+|!Ne z-}VT6_`0;6MJho-WJ80?qNNLP<(i3jU$^d*B^zXly+wJz)1;` O6AyW58Z*%84-kVHJOp=mB?(3xK=b_`^oP2zIpPy5im`d;LrI(jZl$1rEo>Roac+t;= znV448(uv#K+mez-)zXsV;h5;pe)j%V`PY4@*5JYj4;!qqi z1{CzMQ((#q4J`U_Z~_vxdVixvexdh}@P=CohGejV6feE-hm zY17=!oNl;$vF+~NyAvijwzN1MJ6ieY&!4_NhmH=f_wU~?UApx5@87d$m*wTvV0!m8Uwr$&1U+=JEN7voE$BTeFT zrPb91Y}n9z=FHYflbq}81Mc5HwRmyH@83TT9a=koepf~Y(B$Zr7RSa$x4JsV_wV0! zcb8;jI5#%BzI*q&uFh%3jL80e$Akp$Wy>-bEa=|2v8l7ObLrBC`g*5{6P|VDv zVa1B5kMEXEnBegL|9=Kr0>z&!j0_A33_2i7Kykvr{=XrismanTT1G+B$yVK4ym>-` zT1foVX;ZbVM1>~mMNMB2&mY0Y(X2ZyHfHsz7{+N5e9Ii|r^YY~vd6^6Gsdo8VvbFsuR?3w0cI*mJ)A&xsfmWg5CbO-4LoXo5-=XheC zP4kmq7{keXhUtP{r-jg}N#d+0Ie2z_XZ36kIIvH|W5e5qr8;3jvLDhf&e+5Z5#oeC+9?OiWDc=%V4?o9gGG;@_O`?WN`7 zp6BGB!irWe!jeeq@!8J#EGV+TG!Q-mzPY?&W69eblTUD(9MR?&xxR)T&k&I%gKM( z)sV}{jHab=oSRjYltt3ejiaGx#l(M`n^C8xYOAVi$i|ASs&?MnnV6STy}N$V&XCm7 ziKC)wo}O9O)Qh;bf6B;ym6S-x#(b5PO_h{R%E*hIolwQYc%`ISk)+L+MIkDi`W z+SrzwnM;zAM90N@>F1%=)04r!dXkbz!@+{7r)!mzN}-@;#KL&b&4`zlOPZNlpq^Sw zOH1C{lEA%$l$1xWtz_uspT50zxwnABz=qY*l9G}`ot#pbmrkapSf{3Dyt;I?vUHxE zTfe=7o10GN`4&7jD`#xqV!txhJu1i zg$Uru)5y(|8f}cgSRtVRCISNZ3_Y{}!yGh1J|G#PpaaxAf~FQ4pum6w2L=$-DG){g Osjx%^nSE6h2mm|HBwf`2 literal 0 HcmV?d00001 diff --git a/templates/static/4.gif b/templates/static/4.gif new file mode 100644 index 0000000000000000000000000000000000000000..b42183c4d58516c8969f6c2162c0084e2bf66b92 GIT binary patch literal 668 zcmZ?wbhEHb6k!lyc$UDhVnx)twF$d-=AJp-@aWN_U%!4`zSy>PbK2_{3!XljdH3$! zMT>&(-0c;8o?Pc68O%IW;!AWoEk6 z)j1wIwD!!It!Zfi_4NTA9UXV?9{cm>cYnX*ym>LdfB$G{ao(_@dGTVP8@6oR*fedL z+s$h|AKxvTFu~#f|NjiM1d2ad7#SE67<53EfZ~LK{bxf$Q&X^lvZSn|Lxj4FSo4Gg zOPlzq)23=V2?$TLlAFFDp4XX$wb^!I3=?0>bWyHlif#*;)-K>xWeL+>$j%U}wopVR zXdwe5b6o7gcq5Mmf=rBYr_KaePhZ7wGLG$xMd-9!OjkHgFATSz8pF=W9>*cb#<6X> zeB{)(m*W`XIpesUuHN{>%Fw{dv%>HP`+09B_GuXwKM%3a6laSy`e&Sw;Bdf8sYW0% ztbpN&w^>v~#wCW320e~lu~i!sxgKXOeBhueDjw4?(_sOV=%lG~I-QM;4GWmK m`T`VAJ~_(Dvw=-iVFBw=9#tU~m5U!(+yupz9$;Z&um%8SXA2Mj literal 0 HcmV?d00001 diff --git a/templates/static/5-s.gif b/templates/static/5-s.gif new file mode 100644 index 0000000000000000000000000000000000000000..00f59be3ff2234f20902edefbbb7a92c32e99382 GIT binary patch literal 710 zcmV;%0y+IhNk%w1VH5xq0Oo%Hv#@&E*Otx8j>pD*z`lFg){wrugOQO#wX|xToK=~b zO{b=6*VT=al17=BQI(ZQ?dqe{(uwTpqwwve?(3vXOibqEo_u_K;N6<*%24;GCD1Nyx^EmX=B9 z|IVw{^;%E*h#$bZ(< zi>#_}qM=o$q-LU`T$h$jo0?Llq;R#ee8a(c!oYpR!g$8Sd$qK0(a(vfr(B$yQL(UT zvaoNcrfuNfm)h8pl#@s3<)4+5MX;}Cvax5jv~st#ew33*`61g0HQ3-P)F$n^C{L zgPE95nwm_ot#+@jaG{}9$;XJUtZ&@enDg`V!^6XnkU;V7rvLx{A^8LW004ggEC2ui z02BZe000O7fPaF5euajIAbW$0hmD4Oij0O_J|+>DBR3Eekb{Li5gG#)8y2QLOdAw_ zorV!`ItX__9<^j4C#$T7qG~FAz`$iZDHD!<5m+334PjqqYXC1T$9^MiEHN%NL3U7h zBoNgaV|_4v2M-D~NClh65i`pNF%G~91UC}KjZ!yHIf}f|lE(p+GDy7WIz)lNl@%*O zkkA9>L7y`LBs4;igifC<8U189!lKWD4;>mYKwu6)32`WtaM*D&shxp;n>m2r;lbXS`>U>fAOP7kAD67HD`|R z<%?}+PB(1boObu_-3b#Mckj$?X>sc7bNKV;&-?f9XU{I{=h6B4{LGhOQH95-xezI*rhf(700-@o0lqig&2?GGND zNly>>^XK=wcdzHniMoIPR7;C3s$U%nlQoP|Ns9Cv;>MjSr{1@5*TzqmVn}ff&G6&LQ|8inZHb!y@F|=g?RIX z1Z&y&sne#qdWZ;54Dp*josp4Wn}f4Cd@5U99P^qrY`jxN`Ib9GO^u6ZW1PNbD!&GY zv&*!#ObjunVz~EC6;lb`$9^)FHHL|2ft2Mw?wD9!X0`=WgQTZ33p`-uW@q0uJz9NV zTx@I%!*`yzbMcOHuM(vfENHZ2`nZq7GpKLQQs!%H5m`xx#2Fa4ba)-g4osRTaEv43 z^K(~+=`A)jD^46;(B06pTdYKpLD;*S+fc5C(_!stCMFiHh;H?TT-*jC(?Wt68XFpN zSlE7OHy&i2!O5XzpjqyDh(|(FfWzy-gErQ6@fjx$Ojvkr0Skv<-;xtbN=_^qUUqvV M79JLv;lN-G0JKUKSpWb4 literal 0 HcmV?d00001 diff --git a/templates/static/6-s.gif b/templates/static/6-s.gif new file mode 100644 index 0000000000000000000000000000000000000000..226cc050ef2679a57205179d0beb83c127d2608f GIT binary patch literal 719 zcmV;=0x9!q$9|KONuHfqm6b@myKtJATI}hg?(3xQ?4{@ApOlnE*VT+nOiY)TN$KaI zl9EGwe0=cjrRCzD$HjZw+uNR9KY!+*-ii_Xl6=Hs5|<)7;3q3GtIl95B% z*N@K2j+2u_%gTnPq-v|GX|=L^kVwtvyjirUzf*VUA}x^I}5PQ1Ewrlnfa&yk*< zP@kS$ySagzn@+^TdZnaY(aw&ms$s;!e%#rUz`cUFwsGX*o8#b|uB~R$(2SXwRLRGS zt*ddbt$3oKUbM1tsHSqexO~dUfXvE+&C7?at7qZfo1vgvm6b}Sq-D;_jKjfqu&-*X zscpu@fZyDj!@+r=pI+_iqo1Esot$5vo?gese7?Mcv#@)qsA`#*O~Af)nwd?Mlt-eX zSiHJ&v9ETpu6eAgbd!@ulaofLreD_6lk@ZQ!^6XnkU;V7rvLx{A^8LW004ggEC2ui z02BZe000O7fPaF5euajIA$x<1hmD4Oij0RC4Qc`gIV%)?kb{#ABy(a3FeeKK6`YEb zEe1S6A|ha32nrjie5_;_eQ_gweR($u788vD4NVttF?MVZeH>XfAI6gkMHg*hQ%OvH z4s|^in}=UF7cX}J8fO|1XmmFd9*q%Az%(pz03$3;Rcz2}ltYXL4h1AonX#o$0U0Nb z3^YWdggOaP7~n861mTbz<;SbqD6*eGn!GHh(6M8FJ9E0> z*wM-f6CCf}y}NYj(!M^2*|W=9TAbd$f1j6^*U{ni=g*(tzkkn}6Lsdy*0i*=;$qjj zI>-0#-!56wFma-DW24*R#cA8NZJ9L5dC8Kr6DJnmzkll8yVu>_C3EIP&zcppZQHh; zJG&Pw=-#oTD?L4+ySuc$KA^tdsiefMzTV;F$t4>$G#@##p{2#Sr3L7Q<8^gTbLU1s zcyJ~wD`Woru6grfX3U6Oy0qc())%w{JhNzj)3Z-`6h|Jbg6t z*Cm+*ASHWvWWk7~)u%BU$32qSo`+u{tSl zi(}+ukByCCj%Q$s7YX;D9v8EZE74jWw@ zPB-fQ(yH_xWKfN_})pQ6rw)be*_9SSsstGVy0|14*8!`X@ literal 0 HcmV?d00001 diff --git a/templates/static/7-s.gif b/templates/static/7-s.gif new file mode 100644 index 0000000000000000000000000000000000000000..d60763a1ffdfaf60b68e1b0cd6bf106885571fc4 GIT binary patch literal 673 zcmV;S0$%+`Nk%w1VH5xq0Oo!G@9d@2(uqt=OniKN-`tw)>7&!piRtH|;@_Oi%7fe4 zmz|tj!NI}g;hoLOgp-m+$Hj^0=Ahf#+w16})zgZgpH%ASq3-LXm6b^5pk|YjOPQEYm1!ySab6xq+>#cdM#h$;XSQrfSQ{ zjGUWKnV3qIluWa*d$X}=LE<3^{{+9R@984h{*YUI`j>kPjIyq%73IM@uSaEO2lnp%DcS z7=Ex((5{gHJ;DrOdBW$55QIht_(5u@fPxw?bLY(Qy?giW z{X3HmA;|e_L1QIB}x$;>Bra&TQSVq51y(Q*-7-ZQZ)*$dL{2-o1YC;LQB_UCWkb&YBgI zkl?*^X+uUvT1$)Lf(6|tPAuNBqpPmYY2(JGB}*C(9a_6|X=Y=iTV|%q+_}-)w{M>_ zC%V2q;O^aH+qP|)G|9Q8#rf>n?TZ&@WMyS^cbA+zxumJ)onbBO}8( zA;GJ@-s#Vu-^InQzkmPOwr$(ao!v{8r1keZ-o1N##*E0461TLpfX>d&Wy`W(zgY0` z-Le%cq9#mm`2YVu1C4>=PZmZ7hIj@YkUgL{VPOB;5Z~0~7aA00sqCcaC?noHAzocG zZtAqDTB<_A6Ak31FNovOVPbD~jpJltj9t4fmXRfvk87EdeOxRH$C~xbEIWBLm;!>Q zGO|WT$HYW4FmT7jP8C(LoyKwI)|qp>%+p*9r!&N`vOVYLj^>X>XhGE(hwgnzi z(^m<`G4n>V?u%vGq#UBka!R13=;Ofz@l?hejaf#A6PWebmbF$fDTOqpY~oQfOk@q2 z&cqm_{v*>!5RSVz6Htv literal 0 HcmV?d00001 diff --git a/templates/static/8-s.gif b/templates/static/8-s.gif new file mode 100644 index 0000000000000000000000000000000000000000..b648bdd4bb5f7bff4961a016bf6ddaa44afeae07 GIT binary patch literal 731 zcmV<10wn!MNk%w1VH5xq0Oo%H-P)7T&4!wpP}9+gs;OSVzkKQEp{%NI*w&TN&WxU% zSHHb=%E*G@-kXz>MzgVYot$3R)s4^0hrPRbm6b-Kpk9@fNT{c8nweDZ?4|JSrS0mY z?CGOSOiYrJM0|XF?(3xD-<<2{qLY(E#lnZ^<)72gk+rmI+uPgg=%CowkJi(Qt*de5 z;hnLsdY6|;-P)Ms;h3nWV8p?PudZjIpjX=0kf^6zl#@l((~#EFljGo=pPp2fl}*jd zh34a(tgB(czk#r?b<4?qyt;DI&yk{_WTvHIwX%G}!FR^QfX2g!ySZ=D(1*&$gq)jG z!@!2b!-MDKpUTLK%*uh1kwe|umcP7#tE*tSw{xngX2`~hmX%Ahv1zifZQ9t9udR00 z)QjKTm9DIKxwvq`z<gFehC!JT)~mIX)#IpN26u3N7Y}6x6Uzq(zl|9xFMSv?N)m4`XD&J%o*Hc+AmH2x9DVc{$pa%=qZ9#v zjBwxp!?HdQ)K~%5Xh;Nv5a=8bQm2Q5D|b2_0-%COA30-4ByeIPjfXV|#2s4kklh%(04lQ&{ApvE^k}X9VY|zU`P+vb8M3it7B18)*02xQ( NtJaYr&3^&`06WsuY|H=v literal 0 HcmV?d00001 diff --git a/templates/static/8.gif b/templates/static/8.gif new file mode 100644 index 0000000000000000000000000000000000000000..036dbb682fcde2aa120eafcd122e7bb969cdac8c GIT binary patch literal 713 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

    n+F@6hCX=4Qf^aX~Z?Fr5lpiE3g z09}4+>$g*z4di#0##;Z#s@0MSdahpq*c%isE>$>OCY}1<516Kg#Z`%k9s&N&k>ZZy zbwqoNG>Rx7W1>vwb%n*s%CqgvFd{}TS(tnn0y4#6Y_-mZ13LM8i%?O9p&D}nMjzcs z@g-)Nns*@Ya%@~+FeC#|MKu4XzYVReK~&ZFZTx$99b%!__oqi2hkskoTWD=m+3hqD z0t{rF29iIONOPvMo~IImuOG<lwy%@66unmd(vuEWT)A7*`awP^94$3>`dB0+!>155qwhXCFyb~$Wo0K%zs;}mix|j8 zOe!0RXSFI#_z>t1OVL&XO&1#5FQPi%gTf5=>*3 z!r9`;+0gzsF}wPnH!Hr~dt8>y3}!Zs$?x~DTi}~9X?f%6tz)!XYZKTr;H}%ayeRBb zUo~|R#~hCid%izAk}52z0YaW1$8QYDJ3yhmzikP#w3-)eo0i{WnAAOX^EvJB1H(FQ zj!>P5vMMRFh?BB%G5kS@#`$1w7%X-if8@jgaHCDSovx3oW-6zk%D#*5`gAlE}#AhP?77`(Lw^5n@?Yq#G;g{$_7oT%@pEFOvjpXP3}H z;pZUgCbyW)IZipIv3PUscGq@G1BS+As(UyQSuA}lS-WL91cedO#7{(jFd0AEL|{WA z)YTu?0G{KGJ@4r`H+okG5h$38%hq)(fXh+ujp!4BYP~01ELAQzAIqLMQvgx42K-A) zKQt|EzG)C)=H|K9t(yGr<667h`KkBTel)}G9gnR^44GwfyLS9n1bP&Zi;FEl89V4X z*O99PUV?pTYbu|s^<8-QQDfhDHZ>53`UwK4yPri}xTgDr5#Vf?6MY`#gBsNaUJy>} zHoZO2(UBn1&NnvA$=G#xgnXN3vW%h%JaSQJeG-Gt4;XNyVvt}$P~k@!O zXexFymt{e}Bl2Av!u~&SP)>=O97%{&sNK~A0KzJOlKcLxXw_y_7OFcSa3NYzVqdfQ zI)<4sZc*5~Iyv7h{7Ady^Jg69}RXRDB zQ89Fc@NixTalzM5*lCrcHa4PKPEO`NdAX{?yX)Vq(se5;ZY8u?$~QLDuFMwy75TEW zVySNUxamLj_FF02*d!IIpeX2VR=i%9`$Vko`ztJkB_y!%J5}jL(y>Re?SFy3PhIrF z!0j!eVKW)ur|mszFtm~xIJjDQs^|Og_7FqyswCG~JocxK$!R39i(|zG?0u|!!t~)R zNsPOiOI2;DiwdP-cjiy689+=Ok^!PEL-^v|1&>ETQBIm~GUA_jzEs#&{B5d{$ccuX z=R!0deZ(Tg2hLWH3xR27?AN_xu61__JbPiISW|Sf{Q6PCw;Ml?N2uPWihMk<*5hnq znbge3clr-#WP z<7EF-+cAnQnQ@sG8T3FCCr<8Zzn);*?2=`0`T2l1N!IemqV9#0-(Rl_m7>|ft$Q26 znfBp-`sU^m*a);ABTOksvL~F~)a%<;zJA9`EdvUwQ*HLjGEc^evbaPv|NnL`PES#U z9QH2Tj2pRhjc87KcFZScX3f7B7rI`Rb`@m` z3O)eYAhNrXs|E4AP3xB}p~Fp)Woaif{Saes#H!=;9ctb$G}^!^f6Je#9=^=jQS zUFF$}JAT&_e!|^waG??+`#10I%gcV!kL6|FfGq?)n7ZoRMk}411D*W);I3j@)z^Vg z&m8?`&0Gx?MJ%F>GjlF%IU5$?*GN$XY zuhfXUeeU-PSt+0AsRR=2@VDebiw*aUTFlH)4KJCJjEv0Bt~@ID7B9I;@PRib2%ps{ z#T8^v9k^&1_hS0`er9$yEidQ!HvVrb;Z%ckyzyppvm_aD+Rn=hwK%&}enh5nHq1Fg z&Qvu?ca!>k^(NCen9&4d6;7W55(SuwjIFHEZMQQk_FV@Aq5|n50NIODP0hW`cH{fM zM={Y--C51$U+X$fMCT4BN@-I7P;|2EIYa9V7nhkTa&QGARaaFaeb?1#QyGmRL`n%O zj}Ga@P)LN>Po?y`2CywCP*h0KIC4XX^s?qy(er_RyYq4Hz^!7vtFnIoAzVm7H4E3F z%}LOIc=zbaEJyHVzgP$W9F&(=bzKZisK`YNkYfr2jasB1KRJkg|EjRv8sjn;=ZC>4GumZ-h8^35`Mk9y7v}!IooXRZEZ-*jZ8tp12`^_ zGN_6|#P^CWmY+OxVS&p`Jg}hN*h#QH_IcZ-(cRx969OSe^T~^esiLb#!l_q!teQ_k z*9zaxdjf3f9H_8LYoy%7y+8{48E#uUhVfOmm?ULI?QpqGw-xLZzq&CIsuPFtJ9Pyb zIMp@2P`M3W_`U?lLpkMhK6J%Rml8-Bs?g&llEQQFbU%}NtpYTI?BmDQpMQW z$S(_8&9vx4L>_|j-j`q9zD&0rS5wFTK+i(I%3k}GiEHd6{*hqb50a-|9)^dxy|+gN-@7!ZF9W{y z*$Qr{<@)x~9mg^MujYM?AYL9C=q(?a8U>`G2Q{{1ZxR8bH(ck^{4?XdNx zxjC%g*JC_c-oey!1E6x0Fh&$n8fxQ-12Al!C+{EY05x*R>rh`^*-5#DHbr^LKkfZG zIw%a(!!m9thKE=$=)tvvn}X&~KmN6{5>8S%!Hx&pngGERj`Hz^r}j~(dz03^b6!Yp z_vFRbt8uU|P$*~6#cey@33EF(3`pMpcdwhpiW82G0*064X#fVIQL8pugs4CWl*QB$ zq@OBo2n=1;Q)gZH+s&q*gzfotx<*ISD2-H-qz`6ycO%7BnY|KIe+Q+R zQdk2!AapyMSi2264=?}ymqDo?-r`r7YR*qWyPQDDN<@|!t<%Z>=keH%!Z+Vc=THAU zS2prza<4yY)-10(j6Os4)7c4l;$h5M{DXk|+4okXnwyQSU(Q?LJE%u&|Lh1J#l8># zsscPY!CU0BGu2HI1Y&>Mdboq-+FV??^q%{g-n6W=2H|#8iiq&BQ`#vvZ@R8MGcZW^KPN?` zj{imhNkcnqW`~NZeU6USL*A|k?J~a=Uzbs`VuD(s5T~Qo1Nm%(oxG_ZmUea*sZV3& zJP`o9S_9}m(n_?J3BeH0Q`hAn*j@QOB1q&>g0P?U;dx}F?+73OtyKbn7dv}i>+9_= zCxzDB3-7zu$r$b{F}Bha4j6a-470l+2VWG!46i`Cx- zXrzcdZ@2F-)XE3na^8eL-F3eX4YmR0o$b6e%NJ4or=m$_Pc5q@hKLCxTa%&uX`wcQ89u~b;6THauK-d?kb7xgk-_zst zhmyBvGdF(BQ@iq#(tDJU{B!|7+r2)()0-h*cJC(V%w3sdF)(x>Q9sDp*ITcJ8U}i3342c)69S+uZ>A zt+0*+w4tW@a(lMwlpuI=lCe--xq0a7)fbSf$BO<%8BbMNwLdlUyR1Y(se((RUw<-l z@N6P59D0G_+q3*s}p2v6q?KwnDhp)ac>l=605+-vhiV#-7(ja^jdGaJ6Y{e zR=#oGx6Vl(#p8sMNe(cA^jV>RkLJTAu>EK8#MS-_s+u}t|A!CwaVz|O3dxFX{h*|n z0(?h8=Oq(K@sM&MEPKxJIxIL4BgCi$Qpm{&uo-6n7^G^~+qdCF0MPiUbEqd6uPoFc z^9Qt#E{2RRv8!Hop@X@*P(wqi&F)Wu_65$GXc%QV^ z?#Gv3oIA?OM{`!yn;lQjH|kY-9h@woV!fS$g3MZc0SC{Iq(fU1o`&aBD;etQwd)WBz^4H4hi6 z0pHDJ>M_5ss*>NkYT;u_JN=&&j;ZO6y^%g9wLx?LMf1B0am&;TZHm5za^ zgBv)K_@FdpZeFs|=`-9$2MigOI_&MK^tlAJ6pYk8VFOD&m@K=kXikN&s)5<#H)Q_ z?yFkl1TykL^rOZPV<{vl9Fb(=P&vFpG($1{zv1x zXj-o5WmHFcyz4U1QB=XlHpRMvq}se1Kr^~OP|xU9?2YM0V=2ns}@(5jO(y1TiEmVIHY_r z^oQ5l{aMb-M0RnrjZK*m7!^hnF!~tK`uV`fh}g%*t-YqGTuubxw;{FwX}Wg|Cl5a) ziVC zZZ1;H%=nm6O~1Cz{?nNiF}JykfT27F_C|`Y(~MCDYE+bD3koQ;@Uh}Mw|T#}*(7%@ zRMH$DjD)wg_E!6Z7ZD8efRZ8lThS&HYsHUR?|5!r{??dC-zRfSw?t{Jn3?;n+gjUM z*`_&b!Hms|07v4IHQfcQr|Fuh;X%zOa^(MT8UzP6-_M=Ma3B`ta|zBpA=$eTA0u>* zkcbR5z~aqfM+H~UxX1}RI4V$(H4w{(MTh5)sK|wR=Yd4X(BX3{nVEvco1@ zp~`ENY;hwkcrG17XYm-t%KE@^lKjOJ^X<%7++faV6_6##hl~_jTEb6wsOhBG9RC@O z_U`GVf&3T4M+l->1c@RGnbbXzBQY@MheGe<481b+DoXEbMSe|MbH6jz6lvX4KjK&X zoOg3|#YsULL+He(6Ki6iV@;*S>+(c^@%~%+V@DrW9JLtcZ`gDLf?C9_vPj zq1KKwaLJA{ZZ&)W!(qU9%AVik)}2O;o@mY1Om)W*r$&Lb zL}6|s5kfhD8{xXW6)`V4$c`%O)d;ix z&h@4NdNshKL{wE=;N(HXh#58{?)Z@O2|$Ods4u6j(?+l~uM&j2BSwlNK@q}&@|dX7 zhe$*eQ3!UwE9$4#tfr~Z(_f#S#>dJU{|pqfc4RlvZsV-j1d20Yk0kK`sHRVb0}Qf* z@8Nnl5RecirjACjzuBJ{*K?1!&xGe z5exaEql}El+ntc5kk9ufECoQ%+K89lKkfBbw{JTW$<#NX>OC-$=}=G*!4o$(hl$ef zIjxc4mDewed0Kn_2Kl>NIPFPGu)~9VLh@$>xKw2)YM_(}e5S%r=W-!bU^<+D{%e~t zWA1d8@INJv!PdVr0bS;-aRRk9U7TjXY7l5K z>l4`2!ccrc_DPr9Hz=1#bWd>D%-|jt(9;*G_@;jyK^P0WcaZN@ zI+&b}?d2QN==gc*3Ryx1K%z3 zhZxL1Do@RFHXFJA_W&{o*iwlO@})wt-dfnIc;NB#Lbv7~9mjaugypVp)x_}LQNmMz z{L64GNRA!@P4{lvN-U6ekwpF8U{vGa2X8gg`gee#+gFKAJFvZ$l0ju3x~y< zS=kZ+FG=Dkfj@7tp<Lk|inBE^a^bBDw=oU|Ro29=$kWal&@=%kcg zg8b2;veY|xsSRx(LC*ff|B=J{&*sz9u;>x*zPBCY-r4ENu#hD$k;UjY=W{ zDZ~2V^mKfn=nrS~qo!tsG?XY{e@e6_=}z4bFim(S;H*&==f(Itd^d{ILw&7h(%xMJ8AB7}&O z5X}^;7Y_6dOB4g7f&DuWg;SupL|F=ZtV*X;6A>ACIJ&o&jS~DG(!e@YCezvtN{ww7 z1rb39NMY`y9d@xm?%s)$jj#R5w?n{OQav0AQ~- z`JU@+lA3=A>`D?sXI!-W6xsc^P~adTR|QUd3#yNY`qq6vb;eV5jJn(H@}oNZ3s}lO z5dkb`@jWpV^=DbxoPz3Y-Z7dV&23&xQfx`^TvsEOI&J^mGgt2Z68=k)5^B&L+@w`WZRYl#+4HscjVJ6u zV;{SIwZ6x0;^DtbMm?q{qK5>%j9-^;p=n%`?(sMm-~%2W0m;=fxpo(?DzBRbYvd;K zSO(^;w6rWZ0iz+~WXs#I)9d`@q3oHt8Jc_oom-{I`16FyLL{t*h`hAy5qxqp<<$u6?N!f z3>M=y*(Q%_>9nfJ?(UYmMDOKEL42P6_6mSxElmmQqj-MZH!)|7^dtR}s&e^S99J+qN|cK5 z4`+s>1Uiq{@O?GE#PkL3<083q7m_wtJ&D4&lNV2+v#XDfk+)6dLbTDG(C|_QKPzrr zbkk2_Q|GvZ@H%znDwxiI%rzm91qi4jrt^<1QIfG{j-Vb*Ykmpt7A_a6W&i!E{N>a> zA^R7S6*W9Lsly}NfK9B|+lMPN!QjkQM7&&rj&8!Z0*ikybUMT2dFW*m>#HIG&XW`S zoqn6_^~&b&AL)sM<$t8zmGvMu+E^E$PVHyV2fg4;?@<@bOp#|JZ-~O(A~t1rq2tC> zo)_vThw7~S>u8`SM>ODSH*ic{3xOP1Vp#ywR z{-WJl^-O?YzI!9Jh3Duhxn#NL^0S1r2cDzd*p#r2$!p^x+G)DK0V4E1`=nN>{elXy zBZ__3@oHHPk1|^U>qCh5H&8e8s2dsaEy>GNy`8RO3W`z=x6(*Svr5&apw3e?`@7gY zCDURCc~kKZolTo=D17aU3OKRP>6!UTPj%zI3RM~AoV8Ktj~sZvrZNZ=V=!;Wx1PVn zO1Q+%>WRV`5I78I-@R%WP*S@XO+6DA$(*q@jR_>N*N)_jdn(3fVR#MYWv+c8lMWNa zY?r+C@IHWy8NP+w+GEM3%p#F|=rS@Do@SS)(!!u?(OKok`@G(({EB~RXH3Wwr~BSl zat5z(9kd}LMflYXSZow1N2x`W`AQs>`ls8#O!T5X>hzW(JCidR1cE=5f{Lp2Y{qt- zW!RYr%k#tO6(t*R6T{6hsSA#B4fep038BD|iJYsq_3IzRDzK`} z8!GmZ9B_)l77;hx>M%9?&rU0IQTnK*XJVFuyvG`^)sp%?wvOn!3nz14Uh%l~l(fOV z(s_D!iv4HJ>VIxkH7Pe`{9QAHfvnXHik+_l#@O7|LWfPQY=u6{=4EIb7Y|oqxi}5Dc_pIo!YQv<9ac4 z+~@a2cO*gZ{*hh=(S183eH`zR&qV=e(aQzV3tSZ&C20X~62DY$_Xo$3xPrEE^RSvg z@R$@{!{3viM*Ixm>(KGti?4c;%gVzh_i@dCZ4$q}EUj{%UBh&+4G8m$PQDIPcnGiE z3w1_%OK1Fc54JWYi~d-okoyv9`12|A?{CEjhP;lsBFBIT{wZeQmf}B_uChc9`})M7 znINliJJ)PY`xzGvkC*t6FkcVcXF$~2& zq}zDsrv0#r{bId%-h1B^`(^3JKzk7XF)GQmcVK3p<{noq&7gpBW#Tx{1ou|W6yFR~iiQs~r!YAmzNK^DOWa<$)A(SYdvG}Qzw6L(> zY}ssJ<|&ttq-l$az9G!S5ZmqI4X!zice@c2Ke3Cu+-IruYl@;ty2YIjZ7k=OW6*t= zYMxq1u)y`T<2|z9+x|&SOf;z45g~0i3Hi73$&kNFR8iS}-sGQ-$y$EuxnGCi8C>|AblBKJO7$2;{iFUmj51j_Z&L-EC{YOAEx*cBUy0GdDy9l;$ThZuw#9VB zBlh3Vfx&j}MJ`Lo(cMNt=os`0M2FK08wdv&j&gGkzgb7Y`M%3OFH_4 z#&(w*@5xD5s8FFoh3^MXdkUaJg$flaJVjJaxfoDLmBt6hzw&^nwc0zn#OG!{y#~FYA@%U|MP$T&v}mL>zVg?-{1H4c|Y&x z{pw#@v+DI9sFiy-ZKyjVd7=uMG^kLjeMgoTCBEGmX_4a{=y843y{cpwj&8J_H6gwA_W8aTGH-s)oGblUX3@&le`Gx~?K?j~oPRZoi>|v*LibNiF+;WE zUroy7A)Fqm=zhZ7_^cc{?>s;0Yxk-Y96vp&9y>oJq0SGNFj)o6Ly~rL{menj6mI-% znLe&$2pWf#1@huJ`l9K6XsetJ)g3{`%U@26&*5a_-{7Q{y7Bb+d-dYEwfTrgSH@6||NswQpAEd`B=<lfIE^da#Dad5=_@p4bIV4 zEJ+XtxnXt|F|@n5>qii)(N^c_IS$;`*KW-s^x7`EQYcKZ%q;O3&YtW{=r(W@Z7fC; zgjpJ0+%+QzCA8H!`W6Rn3PJpp8)jP(W6`||ZFQEu#h$x?AYSc;0TnS6y11)G5U<@L z2d7BTd?VCo|j6?D&e`l4H>h_QNvl1L9Dh~G2M(QgyPe=TBsx=mpJRZHUK zERgn&DfP=);nfAdz<9-o>W7gu>gC6dw1DGK||4im(myJ0ko7;C$@N+SqGv{fQK$DX_Be#@P=(2R*Mw3hE;h@J=*LHzrAPDgld zXKVSs_C`HmU^Rd<7{)rVbVT3z_Xg0mkeSpdFe!i2!|z^e;-xvfz4 zwxH#LXrR@;jKnK(OIayOO6c;jN5===>^l@5a;8ire1#|~zLBWkMd<@v0@m#p2>tp_ zj43Qqm0HCCt-KRJqSwU;P)h+_&VTgNz19+3XiQmh_ypPX#%|t{#8WCBMpPB>O94gs zkRVY3x}7|mbkyF5!$rUD6Rl#W0qioeD{`Jdd{A#vsJuee50A+ZoR!~p#JAm%VlYWw zz=CY;-3f7~{Ph&9I z1L8$Sl8z0!mDOGiO#(eqWfg0wxf_EEJRpA~BQGBt{HUx}0|<1fvWQx0uEyY84+w;e z3_mv5POZHfk_75hWff?tIU9pBJRsZ2$V0~luTX0>fIx>T>z0<<0b_8A2jmkn(gfH8 zm=T-=`bCv>Q%lX!7@Xt*Swlvy2lfDF0D*Q@R=$=R)EFG+0huNtN4*Ez@6}#CnFM01 zvU0W5_8NoH9*}1wWQ+IUm3y@sK%h;Pm7}F*WekRUKpvBjB=12tU`9|9s8yAft);fp z7#!#UX&@mldk=mD%m4x{sw|wAnz=C;<^j1+LWTo-l-6DiOae8lvNE*PjE%wHctDCs z$V0#$rL`JBph=Z=T}#c-7<||Rl0`zAcniErTkZyA`!`y$t_7wVG=rTptusxc{*?g2 z%hGdCtB>nMs}WtgY3R_AwoQYFBD0J(I8Rkr_NN;B9|l&tJ%J1gS5X@L;8#KSwJqY^uaev{ka*-H(xPhaVh&tHG_u#?y27HG1HM( zGxOJ5H8XRB5B}c`Nc3-1WL;BEHCP7_1Skzam45Xqt@)imo`0ha>sm~zfduR zhmQAe_rB*3?p}yrr;r}?)i>5Bzs^|rF&w4Fg*v_Hc2_*d&Tzk+lc$$W(`gKD5yg|6r}~lde5$ukXE{LZr4~ZN7C9VcWMMGTyYDAB2b2 z;*FZJzr!14ah#0s3;h&T_?Lx#y5M?n5HaA{6=4hbl-f427ko|G9X6J?`@U;Bmd`XD z7FkY~$QfY|&>MHdq9{Xty***`kHa+M`y{9GPRzz6lC(v!IXp(lcf#;nw`wB%Glwkl zWj%-l(zd4qQMm#%rA{?F>XTVbekwNJcD)vgas0weRF!=_ek68O==am6=X1e25m!tl zkvc}08?&?6z3vE{tQBSFrk34#I%~V~09QZ^u{*^Yrn%`Ik@@{-QjFu;fb)!0lGw7H z0gdrG`Lh-JQ?~qy?t<|iKdhM2X+gPi7>)7mioFv76~=DiA+|Q$;GaH|>wN(l;X-8&U+>c+!fb-JQpJ2tfBot$mP2A%^Tkl(_ZOKReWOKnx zs;$Y09gs?#Di|aw_LzlhS>6ulEy*2fUh7jbk3D;wCEoKg%r)lQ{1N%u9sHYe)Q>hH zMG^!7d@WCy_u^a5VVpRgT7AS)GI_$}b7q|wGr!d%o*jd(7-bA|*jbL4glCfyc#Tc3 ztRt(`w)56mqGlCgE!-c8KQ~j zbHkCnv-BV2ciV&^Fj==AjuuxW9g=&J!r~IrqUmFKFebF>OGM6H{kAJc&Q<-hlUXm$ zx?j?#eB6*Ck$zU4PYg_4&*7zMjYAK|J-ETG;OQHw7yBjP#R;GWyo?}DR5)ypk2|Xs#&UI*7wHHNhwu~D- z+49*-Q5f@NM;4Aw3vK|3S?GS|fbWn=y2F%V^N_tYWPS6xfW|0ZzM3Zg)A)q+lFs9v zEd?AjNfI`NB1ts)Md(Vd?+-^2`9n4)Vdc;Pfn1|Bq;me8CSO$+UoOHSJWE7(j$6R> zqCxcyRj_pv5hA4BOXfTmO;1s2+pobJK#1+e^DQE_^^YHNt?M)9<3diP)=A}l9oc8> zRm{-CYITs^6tBLygF`lzxzatip+9O{pA<&_`+E>&Ivaxe9b9bU6)F+ft!)+0lD{^FLv53Pl=sQ6{^}@vEot3+J zj@)VPR2fg7H7LJrsvns*8E^HouueYAJEy%1&*sLQ4n5&;%O_#FJ`hufe>y)qUzyN6 zP4>-U*A;(fcbDJOCoh8pZ4hib>S8NQxj$sllYy$MbeWPh8w-ch1_mq(Ds>^S&s%bX zhLi8=TSt^L<>&57wUyORV_k>BY^%z~_f;fOJVk1^I!@lKrul)5~LlS0&EJqEENRp!_0 zo)Bm*E(1;c&bEfTmZbg2ae|o6LEZ_K~{W z!Zphti6sg9( zwA0l)O)?~N=-ddRs`b_BnPT}(nmQ!fWvJ7_JrzG?xECb2RnP2-f~wm-U9&?i%WBRJ zNa-zyNtUF`U#UJta-q8q@ZGCvQ=>V zm%d0B?D3Jux)DQ7);&$u<7YW0> z(#-SK#LvuiDd^lBSCEyjdroU_eAC5yfw2_x^qw0$%7%Q(ffm^G*Eu+6I5Y5mzku^i zQ(k%Vs2LiDxMGW&%eLeT``SymPzaw8IW!J~ReXS1UuBsq3elo&lVe8Q(>3+-caNpkGx@ZnTxi_S|a z;_t_(CBEC!?g}f+2+7#E9wobgMsfVJpUIA%&-!WOrw;eThgWWxhPQR+R~_|rK_`)V ztIk$)lqH%vX6ACadP82b1=~?n#Z)0#A3)IYOqzQdi?g^r!ynWJ}4@U^-7kf?G095kOeHAyV+O_&uk O6-vQHy<0PsR{alax*~@F literal 0 HcmV?d00001 diff --git a/templates/static/upload.js b/templates/static/upload.js new file mode 100644 index 0000000..0b033c6 --- /dev/null +++ b/templates/static/upload.js @@ -0,0 +1,82 @@ +String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/g,'') } +String.prototype.escRegExp = function(){ return this.replace(/[\\$*+?()=!|,{}\[\]\.^]/g,'\\$&') } +String.prototype.unescHtml = function(){ var i,t=this; for(i in e) t=t.replace(new RegExp(i,'g'),e[i]); return t } +function Suggestions() { this.length=0; this.picked=0 } +var suggestions = new Suggestions(); +var tagSearch='', lastEdit=''; +var h={}, sections=[{},{},{},{},{},{}], selected={}, currentTag={}, +e={'<':'<','>':'>','&':'&','"':'"'}; + +function init () { + document.onkeydown = document.onkeypress = document.onkeyup = handler + h.suggest = document.getElementById("suggestionlist"); + h.tags = document.getElementById("tags"); +} + +function handler(event) { var e=(event||window.event) //w3||ie + if (e.type == 'keyup') { + switch(e.keyCode) { + //case 8: //backspace + //case 46: //delete + case 35: //end + case 36: //home + case 39: // right + case 37: // left + case 32: // space + hideSuggestions(); break + case 38: case 40: break; + case 9: break; + case 13: break; + default: + updateSuggestions() +}} + else if (e.type == "keypress") { lastEdit = h.tags.value } +} + +function makeTag(parent, tag, js) { + parent.appendChild(document.createTextNode(" "+ tag)) +} + +function updateSuggestions() { + while (h.suggest.hasChildNodes()) h.suggest.removeChild(h.suggest.firstChild) + if(!getCurrentTag() || !currentTag.text) { + hideSuggestions(); return false + } + var tagArray = h.tags.value.toLowerCase().split(' '), + txt=currentTag.text.trim().escRegExp(), tagHash={}, t + for(t in tagArray) tagHash[tagArray[t]] = true; + + var search = tagList.match(new RegExp(("(?:^| )("+txt+"[^ ]+)"), "gi")) + if(search){ + var i; + for (i=0; i 0) { showSuggestions() } + else { hideSuggestions(); } +} + +function getCurrentTag() { + if(h.tags.value == lastEdit) return true // no edit + if(h.tags == '') return false + currentTag = {} + var tagArray=h.tags.value.toLowerCase().split(' '), oldArray=lastEdit.toLowerCase().split(' '), currentTags = [], matched=false, t,o + for (t in tagArray) { + for (o in oldArray) { + if(typeof oldArray[o] == 'undefined') { oldArray.splice(o,1); break } + if(tagArray[t] == oldArray[o]) { matched = true; oldArray.splice(o,1); break; } + } + if(!matched) currentTags[currentTags.length] = t + matched=false + } + // more than one word changed... abort + currentTag = { text:tagArray[currentTags[0]], index:currentTags[0] } + return true +} + + +function hideSuggestions() { h.suggest.style.visibility='hidden' } +function showSuggestions() { h.suggest.style.visibility='visible' } diff --git a/templates/systemtag/list b/templates/systemtag/list new file mode 100644 index 0000000..9a7361a --- /dev/null +++ b/templates/systemtag/list @@ -0,0 +1,36 @@ +[% INCLUDE header %] +[% PROCESS macros %] + + +[% WHILE objects.size > 0 %] + + [% SET minilist = objects.splice(0,7) %] + [% FOR tag = minilist %] + + [% END %] + +[% END %] +
    + + [% SET photos = tag.photos %] + [% IF photos.last %] + + + [% ELSE %] + +

    (No photos)

    + + [% END %] +
    + [% tag %] +
    + + + +
    + [%photos.size %] + photo[%-"s" IF photos.size != 1 %] +
    +
    +[% INCLUDE pager %] +[% INCLUDE footer %] diff --git a/templates/systemtag/view b/templates/systemtag/view new file mode 100644 index 0000000..42e885d --- /dev/null +++ b/templates/systemtag/view @@ -0,0 +1,14 @@ +[% INCLUDE header %] +[% PROCESS macros %] +

    +[% IF tag.name %] +[% tag.name %] +[% ELSE %] +[% FOR tag = tags %] +[%tag%] +[% ":" UNLESS tag == tags.last %] +[% END %] +[% END %] +

    +[% view_page_of(photos) %] +[% INCLUDE footer %] diff --git a/templates/tag/list b/templates/tag/list new file mode 100644 index 0000000..9a7361a --- /dev/null +++ b/templates/tag/list @@ -0,0 +1,36 @@ +[% INCLUDE header %] +[% PROCESS macros %] + + +[% WHILE objects.size > 0 %] + + [% SET minilist = objects.splice(0,7) %] + [% FOR tag = minilist %] + + [% END %] + +[% END %] +
    + + [% SET photos = tag.photos %] + [% IF photos.last %] + + + [% ELSE %] + +

    (No photos)

    + + [% END %] +
    + [% tag %] +
    + + + +
    + [%photos.size %] + photo[%-"s" IF photos.size != 1 %] +
    +
    +[% INCLUDE pager %] +[% INCLUDE footer %] diff --git a/templates/tag/list_js b/templates/tag/list_js new file mode 100644 index 0000000..af9b159 --- /dev/null +++ b/templates/tag/list_js @@ -0,0 +1 @@ +var tagList = "[% FOR tag = tags; tag | html; " "; END %]"; diff --git a/templates/tag/view b/templates/tag/view new file mode 100644 index 0000000..00d53a0 --- /dev/null +++ b/templates/tag/view @@ -0,0 +1,28 @@ +[% INCLUDE header %] +[% PROCESS macros %] +

    +[% IF tag.name %] +[% tag.name %] +[% ELSE %] +[% FOR tag = tags %] +[%tag%] +[% ":" UNLESS tag == tags.last %] +[% END %] +[% END %] +

    + +[% view_page_of(photos) %] +[% INCLUDE footer %] diff --git a/templates/tagcloud b/templates/tagcloud new file mode 100644 index 0000000..46fbb10 --- /dev/null +++ b/templates/tagcloud @@ -0,0 +1,4 @@ +

    Popular tags

    +
    +[% request.tagcloud.html(100) %] +
    diff --git a/templates/test b/templates/test new file mode 100644 index 0000000..b5f5031 --- /dev/null +++ b/templates/test @@ -0,0 +1,2 @@ +[% INCLUDE header %] +[% INCLUDE footer %] diff --git a/templates/user/album_list b/templates/user/album_list new file mode 100644 index 0000000..59bcad0 --- /dev/null +++ b/templates/user/album_list @@ -0,0 +1,41 @@ +
    +[% IF request.user == user %] +

    Albums

    +
    + [% IF user.albums.size > 0 %] + + + + + [% FOR album = user.albums %] + + + + + + [% END %] +
    Name Private? Delete?
    [% album.name %] + +
    + +
    + [% END %] +

    Create a new album

    + Name:
    + Private?
    + +
    +[% ELSE %] + +[% IF albums.size > 0 %] +

    Public albums

    +
      + [% FOR album = albums %] +
    • [% album %] + [% END %] +
    +[% END %] + +[% END %] +
    diff --git a/templates/user/list b/templates/user/list new file mode 100644 index 0000000..66ba34e --- /dev/null +++ b/templates/user/list @@ -0,0 +1,25 @@ +[% INCLUDE header %] +[% PROCESS macros %] + + +[% WHILE objects.size > 0 %] + + [% SET minilist = objects.splice(0,3) %] + [% FOR user = minilist %] + + [% END %] + +[% END %] +
    + [% SET photos = user.photos %] + [% user %]
    + [% IF photos.last %] +

    + + [%photos.size %] + photo[%-"s" IF photos.size != 1 %] + [% ELSE %] +

    (No photos)

    + [% END %] +
    +[% INCLUDE footer %] diff --git a/templates/user/view b/templates/user/view new file mode 100644 index 0000000..b3cadec --- /dev/null +++ b/templates/user/view @@ -0,0 +1,7 @@ +[% INCLUDE header %] +[% PROCESS macros %] +

    [% user.name %]

    +[% INCLUDE album_list %] + +[% view_page_of(photos) %] +[% INCLUDE footer %] -- 2.39.5