diff --git a/docs/upgrades/packages-7.7.1/image3.wgpkg b/docs/upgrades/packages-7.7.1/image3.wgpkg
new file mode 100644
index 000000000..0b72d1e2a
Binary files /dev/null and b/docs/upgrades/packages-7.7.1/image3.wgpkg differ
diff --git a/docs/upgrades/upgrade_7.7.0-7.7.1.pl b/docs/upgrades/upgrade_7.7.0-7.7.1.pl
index 6ddcce291..ade60ff83 100644
--- a/docs/upgrades/upgrade_7.7.0-7.7.1.pl
+++ b/docs/upgrades/upgrade_7.7.0-7.7.1.pl
@@ -31,10 +31,15 @@ my $quiet; # this line required
my $session = start(); # this line required
# upgrade functions go here
-
addWelcomeMessageTemplateToSettings( $session );
addStatisticsCacheTimeoutToMatrix( $session );
+# image mods
+addImageAnnotation($session);
+
+# rss mods
+addRssLimit($session);
+
finish($session); # this line required
sub addWelcomeMessageTemplateToSettings {
@@ -45,6 +50,28 @@ sub addWelcomeMessageTemplateToSettings {
print "Done.\n" unless $quiet;
}
+#----------------------------------------------------------------------------
+sub addRssLimit {
+ my $session = shift;
+ print "\tAdding rssLimit to RSSCapable table, if needed... \n" unless $quiet;
+ my $sth = $session->db->read('describe RSSCapable rssCableRssLimit');
+ if (! defined $sth->hashRef) {
+ $session->db->write("alter table RSSCapable add column rssCableRssLimit integer");
+ }
+ print "Done.\n" unless $quiet;
+}
+
+#----------------------------------------------------------------------------
+sub addImageAnnotation {
+ my $session = shift;
+ print "\tAdding annotations to imageAsset table, if needed... \n" unless $quiet;
+ my $sth = $session->db->read('describe imageAsset annotations');
+ if (! defined $sth->hashRef) {
+ $session->db->write("alter table imageAsset add column annotations mediumtext");
+ }
+ print "Done.\n" unless $quiet;
+}
+
#----------------------------------------------------------------------------
sub addStatisticsCacheTimeoutToMatrix{
my $session = shift;
diff --git a/lib/WebGUI/Asset.pm b/lib/WebGUI/Asset.pm
index 585a84ccf..76729aec2 100644
--- a/lib/WebGUI/Asset.pm
+++ b/lib/WebGUI/Asset.pm
@@ -2606,6 +2606,11 @@ NOTE: Don't try to override or overload this method. It won't work. What you are
sub www_editSave {
my $self = shift;
+
+ my $annotations = "";
+ if ($self->isa("WebGUI::Asset::File::Image")) {
+ $annotations = $self->get("annotations");
+ }
##If this is a new asset (www_add), the parent may be locked. We should still be able to add a new asset.
my $isNewAsset = $self->session->form->process("assetId") eq "new" ? 1 : 0;
return $self->session->privilege->locked() if (!$self->canEditIfLocked and !$isNewAsset);
@@ -2644,6 +2649,12 @@ sub www_editSave {
}
}
+ if ($self->isa("WebGUI::Asset::File::Image")) {
+ $object->update({ annotations => $annotations });
+ }
+
+ ###
+
$object->updateHistory("edited");
# we handle auto commit assets here in case they didn't handle it themselves
diff --git a/lib/WebGUI/Asset/File.pm b/lib/WebGUI/Asset/File.pm
index 214f264c5..884ebadeb 100644
--- a/lib/WebGUI/Asset/File.pm
+++ b/lib/WebGUI/Asset/File.pm
@@ -245,7 +245,6 @@ sub getEditFormUploadControl {
return $html;
}
-
#-------------------------------------------------------------------
sub getFileUrl {
my $self = shift;
@@ -559,10 +558,11 @@ sub www_edit {
#-------------------------------------------------------------------
+
sub www_view {
my $self = shift;
return $self->session->privilege->noAccess() unless $self->canView;
-
+
# Check to make sure it's not in the trash or some other weird place
if ($self->get("state") ne "published") {
my $i18n = WebGUI::International->new($self->session,'Asset_File');
diff --git a/lib/WebGUI/Asset/File/Image.pm b/lib/WebGUI/Asset/File/Image.pm
index 9e639d746..22f471ee9 100644
--- a/lib/WebGUI/Asset/File/Image.pm
+++ b/lib/WebGUI/Asset/File/Image.pm
@@ -112,6 +112,10 @@ sub definition {
fieldType => 'textarea',
defaultValue => 'style="border-style:none;"',
},
+ annotations => {
+ fieldType => 'textarea',
+ defaultValue => '',
+ },
},
};
return $class->SUPER::definition($session,$definition);
@@ -236,17 +240,32 @@ sub view {
return $out if $out;
}
my %var = %{$self->get};
+ my ($crop_js, $domMe) = $self->annotate_js({ just_image => 1 });
+
+ if ($crop_js) {
+ my ($style, $url) = $self->session->quick(qw(style url));
+
+ $style->setLink($url->extras('yui/build/fonts/fonts-min.css'), {rel=>'stylesheet', type=>'text/css'});
+ $style->setLink($url->extras('yui/container/assets/container.css'), {rel=>'stylesheet', type=>'text/css'});
+ $style->setScript($url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/container/container-min.js'), {type=>'text/javascript'});
+ }
+
$var{controls} = $self->getToolbar;
$var{fileUrl} = $self->getFileUrl;
$var{fileIcon} = $self->getFileIconUrl;
$var{thumbnail} = $self->getThumbnailUrl;
- my $out = $self->processTemplate(\%var,undef,$self->{_viewTemplate});
+ $var{annotateJs} = "$crop_js$domMe";
+ $var{parameters} = sprintf("id=%s", $self->getId());
+ my $form = $self->session->form;
+ my $out = $self->processTemplate(\%var,undef,$self->{_viewTemplate});
if (!$self->session->var->isAdminOn && $self->get("cacheTimeout") > 10) {
WebGUI::Cache->new($self->session,"view_".$self->getId)->set($out,$self->get("cacheTimeout"));
}
return $out;
}
+
#----------------------------------------------------------------------------
=head2 setFile ( filename )
@@ -268,6 +287,10 @@ sub www_edit {
return $self->session->privilege->locked() unless $self->canEditIfLocked;
my $i18n = WebGUI::International->new($self->session, 'Asset_Image');
$self->getAdminConsole->addSubmenuItem($self->getUrl('func=resize'),$i18n->get("resize image")) if ($self->get("filename"));
+ $self->getAdminConsole->addSubmenuItem($self->getUrl('func=rotate'),$i18n->get("rotate image")) if ($self->get("filename"));
+ $self->getAdminConsole->addSubmenuItem($self->getUrl('func=crop'),$i18n->get("crop image")) if ($self->get("filename"));
+ $self->getAdminConsole->addSubmenuItem($self->getUrl('func=annotate'),$i18n->get("annotate image")) if ($self->get("filename"));
+ $self->getAdminConsole->addSubmenuItem($self->getUrl('func=undo'),$i18n->get("undo image")) if ($self->get("filename"));
my $tabform = $self->getEditForm;
$tabform->getTab("display")->template(
-value=>$self->get("templateId"),
@@ -278,6 +301,283 @@ sub www_edit {
return $self->getAdminConsole->render($tabform->print,$i18n->get("edit image"));
}
+#-------------------------------------------------------------------
+sub www_undo {
+ my $self = shift;
+ my $previous = (@{$self->getRevisions()})[1];
+ # instantiate assetId
+ if ($previous) {
+ # my $session = $self->session;
+
+ # my $cache = WebGUI::Cache->new($self->session, ["asset",$self->getId,$self->getRevisionDate]);
+ # $cache->flush;
+ # my $cache = WebGUI::Cache->new($previous->session, ["asset",$previous->getId,$previous->getRevisionDate]);
+ # $cache->flush;
+
+ $self = $self->purgeRevision();
+ # $self = undef;
+ # $self = WebGUI::Asset->new($previous->session, $previous->getId, ref $previous, $previous->getRevisionDate);
+ $self = $previous;
+ $self->generateThumbnail;
+ }
+ return $self->www_edit();
+}
+
+#-------------------------------------------------------------------
+
+#
+# All of the images will have to change to support annotate.
+# The revision system doesn't support the blobs, it seems.
+# All of the image operations will have to be updated to support annotations.
+#
+
+sub www_annotate {
+ my $self = shift;
+ return $self->session->privilege->insufficient() unless $self->canEdit;
+ return $self->session->privilege->locked() unless $self->canEditIfLocked;
+ if (1) {
+ my $newSelf = $self->addRevision();
+ delete $newSelf->{_storageLocation};
+ $newSelf->getStorageLocation->annotate($newSelf->get("filename"),$newSelf,$newSelf->session->form);
+ $newSelf->setSize($newSelf->getStorageLocation->getFileSize($newSelf->get("filename")));
+ $self = $newSelf;
+ $self->generateThumbnail;
+ }
+
+ my ($style, $url) = $self->session->quick(qw(style url));
+ # $style->setLink($url->extras('annotate/imageMap.css'), {rel=>'stylesheet', type=>'text/css'});
+
+ $style->setLink($url->extras('yui/build/resize/assets/skins/sam/resize.css'), {rel=>'stylesheet', type=>'text/css'});
+ $style->setLink($url->extras('yui/build/fonts/fonts-min.css'), {rel=>'stylesheet', type=>'text/css'});
+ $style->setLink($url->extras('yui/build/imagecropper/assets/skins/sam/imagecropper.css'), {rel=>'stylesheet', type=>'text/css'});
+
+ $style->setScript($url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/element/element-beta-min.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/dragdrop/dragdrop-min.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/resize/resize-min.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/imagecropper/imagecropper-beta-min.js'), {type=>'text/javascript'});
+
+ # my $imageAsset = $self->session->db->getRow("ImageAsset","assetId",$self->getId);
+
+ my @pieces = split(/\n/, $self->get('annotations'));
+ # my ($top_left, $width_height, $note) = split(/\n/, $imageAsset->{annotations});
+
+ my ($img_null, $tooltip_block, $tooltip_none) = ('', '', '');
+ for (my $i = 0; $i < $#pieces; $i += 3) {
+ $img_null .= "YAHOO.img.container.tt$i = null;\n";
+ $tooltip_block .= "YAHOO.util.Dom.setStyle('tooltip$i', 'display', 'block');\n";
+ $tooltip_none .= "YAHOO.util.Dom.setStyle('tooltip$i', 'display', 'none');\n";
+ my $j = $i + 2;
+ # warn("i: $i: ", $self->session->form->process("delAnnotate$i"));
+ }
+
+ my $image = '

';
+
+ my ($width, $height) = $self->getStorageLocation->getSize($self->get("filename"));
+
+ my @checkboxes = ();
+ my $i18n = WebGUI::International->new($self->session,"Asset_Image");
+ my $f = WebGUI::HTMLForm->new($self->session,-action=>$self->getUrl);
+
+ $self->getAdminConsole->addSubmenuItem($self->getUrl('func=edit'),$i18n->get("edit image"));
+ $f->hidden(
+ -name=>"func",
+ -value=>"annotate"
+ );
+ $f->text(
+ -label=>$i18n->get('annotate image'),
+ -value=>'',
+ -hoverHelp=>$i18n->get('annotate image description'),
+ -name=>'annotate_text'
+ );
+ $f->integer(
+ -label=>$i18n->get('top'),
+ -name=>"annotate_top",
+ -value=>,
+ );
+ $f->integer(
+ -label=>$i18n->get('left'),
+ -name=>"annotate_left",
+ -value=>,
+ );
+ $f->integer(
+ -label=>$i18n->get('width'),
+ -name=>"annotate_width",
+ -value=>,
+ );
+ $f->integer(
+ -label=>$i18n->get('height'),
+ -name=>"annotate_height",
+ -value=>,
+ );
+ $f->button(
+ -value=>$i18n->get('annotate'),
+ -extras=>'onclick="switchState();"',
+ );
+ $f->submit;
+ my ($crop_js, $domMe) = $self->annotate_js();
+ return $self->getAdminConsole->render($f->print."$image$crop_js$domMe",$i18n->get("annotate image"));
+}
+
+#-------------------------------------------------------------------
+sub annotate_js {
+ my $self = shift;
+ my $opts = shift;
+
+ my @pieces = split(/\n/, $self->get('annotations'));
+
+ # warn("pieces: $#pieces: ". $self->getId());
+ return "" if !@pieces && $opts->{just_image};
+
+ my ($img_null, $tooltip_block, $tooltip_none) = ('', '', '');
+ for (my $i = 0; $i < $#pieces; $i += 3) {
+ $img_null .= "YAHOO.img.container.tt$i = null;\n";
+ $tooltip_block .= "YAHOO.util.Dom.setStyle('tooltip$i', 'display', 'block');\n";
+ $tooltip_none .= "YAHOO.util.Dom.setStyle('tooltip$i', 'display', 'none');\n";
+ my $j = $i + 2;
+ # warn("i: $i: ", $self->session->form->process("delAnnotate$i"));
+ }
+
+ my $id = $$opts{just_image} ? $self->getId : "yui_img";
+
+ my $crop_js = qq(
+
+ );
+
+ my $hotspots = '';
+ my $domMe = '';
+
+ for (my $i = 0; $i < $#pieces; $i += 3) {
+ my $top_left = $pieces[$i];
+ my $width_height = $pieces[$i + 1];
+ my $note = $pieces[$i + 2];
+
+ if ($top_left =~ /top: (\d+)px; left: (\d+)px;/) {
+ $top_left = "xy[0]+$1, xy[1]+$2";
+ }
+
+ my ($width, $height) = ("", "");
+ if ($width_height =~ /width: (\d+)px; height: (\d+)px;/) {
+ ($width, $height) = ("$1px", "$2px");
+ }
+
+ # next if 3 == $i;
+
+ warn('next');
+ $domMe .= qq(
+
+
+
+
+
+
+ );
+ }
+
+ return($crop_js, $domMe);
+}
+
+#-------------------------------------------------------------------
+sub www_rotate {
+ my $self = shift;
+ return $self->session->privilege->insufficient() unless $self->canEdit;
+ return $self->session->privilege->locked() unless $self->canEditIfLocked;
+ # warn(sprintf("Rotate_formId: %s", $self->session->form->process("Rotate")));
+ if (defined $self->session->form->process("Rotate")) {
+ my $newSelf = $self->addRevision();
+ delete $newSelf->{_storageLocation};
+ $newSelf->getStorageLocation->rotate($newSelf->get("filename"),$newSelf->session->form->process("Rotate"));
+ $newSelf->setSize($newSelf->getStorageLocation->getFileSize($newSelf->get("filename")));
+ $self = $newSelf;
+ $self->generateThumbnail;
+ }
+
+ my ($x, $y) = $self->getStorageLocation->getSizeInPixels($self->get("filename"));
+
+ ##YUI specific datatable CSS
+ my ($style, $url) = $self->session->quick(qw(style url));
+
+ my $img_name = $self->getStorageLocation->getUrl($self->get("filename"));
+ my $img_file = $self->get("filename");
+ my $image = '';
+
+ my $i18n = WebGUI::International->new($self->session,"Asset_Image");
+ $self->getAdminConsole->addSubmenuItem($self->getUrl('func=edit'),$i18n->get("edit image"));
+ my $f = WebGUI::HTMLForm->new($self->session,-action=>$self->getUrl);
+ $f->hidden(
+ -name=>"func",
+ -value=>"rotate"
+ );
+ $f->button(
+ -value=>"Left",
+ -extras=>qq(onclick="var deg = document.getElementById('Rotate_formId').value; deg = parseInt(deg) + 90; document.getElementById('Rotate_formId').value = deg;"),
+ );
+ $f->button(
+ -value=>"Right",
+ -extras=>qq(onclick="var deg = document.getElementById('Rotate_formId').value; deg = parseInt(deg) - 90; document.getElementById('Rotate_formId').value = deg;"),
+ );
+ $f->integer(
+ -label=>$i18n->get('degree'),
+ -name=>"Rotate",
+ -value=>0,
+ );
+ $f->submit;
+ return $self->getAdminConsole->render($f->print.$image,$i18n->get("rotate image"));
+}
+
#-------------------------------------------------------------------
sub www_resize {
my $self = shift;
@@ -289,7 +589,59 @@ sub www_resize {
$newSelf->getStorageLocation->resize($newSelf->get("filename"),$newSelf->session->form->process("newWidth"),$newSelf->session->form->process("newHeight"));
$newSelf->setSize($newSelf->getStorageLocation->getFileSize($newSelf->get("filename")));
$self = $newSelf;
+ $self->generateThumbnail;
}
+
+ my ($x, $y) = $self->getStorageLocation->getSizeInPixels($self->get("filename"));
+
+ ##YUI specific datatable CSS
+ my ($style, $url) = $self->session->quick(qw(style url));
+
+ $style->setLink($url->extras('yui/build/fonts/fonts-min.css'), {rel=>'stylesheet', type=>'text/css'});
+ $style->setLink($url->extras('yui/build/resize/assets/skins/sam/resize.css'), {rel=>'stylesheet', type=>'text/css'});
+ $style->setScript($url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/element/element-beta-min.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/dragdrop/dragdrop-min.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/resize/resize-min.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/animation/animation-min.js'), {type=>'text/javascript'});
+
+ my $resize_js = qq(
+
+ );
+
my $i18n = WebGUI::International->new($self->session,"Asset_Image");
$self->getAdminConsole->addSubmenuItem($self->getUrl('func=edit'),$i18n->get("edit image"));
my $f = WebGUI::HTMLForm->new($self->session,-action=>$self->getUrl);
@@ -297,7 +649,6 @@ sub www_resize {
-name=>"func",
-value=>"resize"
);
- my ($x, $y) = $self->getStorageLocation->getSizeInPixels($self->get("filename"));
$f->readOnly(
-label=>$i18n->get('image size'),
-hoverHelp=>$i18n->get('image size description'),
@@ -316,15 +667,121 @@ sub www_resize {
-value=>$y,
);
$f->submit;
- my $image = '';
+ my $image = ''.$resize_js;
return $self->getAdminConsole->render($f->print.$image,$i18n->get("resize image"));
}
+#-------------------------------------------------------------------
+sub www_crop {
+ my $self = shift;
+ return $self->session->privilege->insufficient() unless $self->canEdit;
+ return $self->session->privilege->locked() unless $self->canEditIfLocked;
+
+ if ($self->session->form->process("Width") || $self->session->form->process("Height")
+ || $self->session->form->process("Top") || $self->session->form->process("Left")) {
+ my $newSelf = $self->addRevision();
+ delete $newSelf->{_storageLocation};
+ $newSelf->getStorageLocation->crop(
+ $newSelf->get("filename"),
+ $newSelf->session->form->process("Width"),
+ $newSelf->session->form->process("Height"),
+ $newSelf->session->form->process("Top"),
+ $newSelf->session->form->process("Left")
+ );
+ $self = $newSelf;
+ $self->generateThumbnail;
+ }
+
+ my $filename = $self->get("filename");
+
+ ##YUI specific datatable CSS
+ my ($style, $url) = $self->session->quick(qw(style url));
+
+ my $crop_js = qq(
+
+ );
+
+ $style->setLink($url->extras('yui/build/resize/assets/skins/sam/resize.css'), {rel=>'stylesheet', type=>'text/css'});
+ $style->setLink($url->extras('yui/build/fonts/fonts-min.css'), {rel=>'stylesheet', type=>'text/css'});
+ $style->setLink($url->extras('yui/build/imagecropper/assets/skins/sam/imagecropper.css'), {rel=>'stylesheet', type=>'text/css'});
+ $style->setScript($url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/element/element-beta-min.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/dragdrop/dragdrop-min.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/resize/resize-min.js'), {type=>'text/javascript'});
+ $style->setScript($url->extras('yui/build/imagecropper/imagecropper-beta-min.js'), {type=>'text/javascript'});
+
+ my $i18n = WebGUI::International->new($self->session,"Asset_Image");
+
+ $self->getAdminConsole->addSubmenuItem($self->getUrl('func=edit'),$i18n->get("edit image"));
+ my $f = WebGUI::HTMLForm->new($self->session,-action=>$self->getUrl);
+ $f->hidden(
+ -name=>"degree",
+ -value=>"0"
+ );
+ $f->hidden(
+ -name=>"func",
+ -value=>"crop"
+ );
+ my ($x, $y) = $self->getStorageLocation->getSizeInPixels($filename);
+ $f->integer(
+ -label=>$i18n->get('width'),
+ -hoverHelp=>$i18n->get('new width description'),
+ -name=>"Width",
+ -value=>$x,
+ );
+ $f->integer(
+ -label=>$i18n->get('height'),
+ -hoverHelp=>$i18n->get('new height description'),
+ -name=>"Height",
+ -value=>$y,
+ );
+ $f->integer(
+ -label=>$i18n->get('top'),
+ -hoverHelp=>$i18n->get('new width description'),
+ -name=>"Top",
+ -value=>$x,
+ );
+ $f->integer(
+ -label=>$i18n->get('left'),
+ -hoverHelp=>$i18n->get('new height description'),
+ -name=>"Left",
+ -value=>$y,
+ );
+ $f->submit;
+
+ my $image = ''.$crop_js;
+
+ return $self->getAdminConsole->render($f->print.$image,$i18n->get("crop image"));
+}
+
#-------------------------------------------------------------------
# Use superclass method for now.
sub www_view {
my $self = shift;
- $self->SUPER::www_view;
+ return($self->SUPER::www_view);
}
#sub www_view {
diff --git a/lib/WebGUI/Asset/Wobject/Collaboration.pm b/lib/WebGUI/Asset/Wobject/Collaboration.pm
index db0b0a6cd..da641d788 100644
--- a/lib/WebGUI/Asset/Wobject/Collaboration.pm
+++ b/lib/WebGUI/Asset/Wobject/Collaboration.pm
@@ -861,6 +861,7 @@ SQL
my $datetime = $self->session->datetime;
my @posts;
+ my $rssLimit = $self->get('rssCapableRssLimit') || 10;
for my $postId (@postIds) {
my $post = WebGUI::Asset->new($self->session, $postId, 'WebGUI::Asset::Post::Thread');
my $postUrl = $siteUrl . $post->getUrl;
@@ -896,6 +897,8 @@ SQL
userDefined4 => $post->get("userDefined4"),
userDefined5 => $post->get("userDefined5"),
};
+
+ last if $rssLimit <= scalar(@posts);
}
return @posts;
diff --git a/lib/WebGUI/Storage.pm b/lib/WebGUI/Storage.pm
index 6432e33ac..af39508a0 100644
--- a/lib/WebGUI/Storage.pm
+++ b/lib/WebGUI/Storage.pm
@@ -678,6 +678,32 @@ sub generateThumbnail {
#-------------------------------------------------------------------
+=head2 getSize ( filename )
+
+Returns width and height of image.
+
+=head3 filename
+
+The file to generate a thumbnail for.
+
+=cut
+
+sub getSize {
+ my $self = shift;
+ my $filename = shift;
+ my $image = Image::Magick->new;
+ my $error = $image->Read($self->getPath($filename));
+ if ($error) {
+ $self->session->errorHandler->error("Couldn't read image for size reading: ".$error);
+ return 0;
+ }
+ my ($x, $y) = $image->Get('width','height');
+
+ return($x, $y);
+}
+
+#-------------------------------------------------------------------
+
=head2 getErrorCount ( )
Returns the number of errors that have been generated on this object instance.
@@ -1054,6 +1080,203 @@ sub renameFile {
#-------------------------------------------------------------------
+=head2 crop ( filename [, width, height ] )
+
+Resizes the specified image by the specified height and width. If either is omitted the iamge will be scaleed proportionately to the non-omitted one.
+
+=head3 filename
+
+The name of the file to resize.
+
+=head3 width
+
+The new width of the image in pixels.
+
+=head3 height
+
+The new height of the image in pixels.
+
+=head3 x
+
+The top of the image in pixels.
+
+=head3 y
+
+The top of the image in pixels.
+
+=cut
+
+# TODO: Make this take a hash reference with width, height, and density keys.
+
+sub crop {
+ my $self = shift;
+ my $filename = shift;
+ my $width = shift;
+ my $height = shift;
+ my $x = shift;
+ my $y = shift;
+ unless (defined $filename) {
+ $self->session->errorHandler->error("Can't resize when you haven't specified a file.");
+ return 0;
+ }
+ unless ($self->isImage($filename)) {
+ $self->session->errorHandler->error("Can't resize something that's not an image.");
+ return 0;
+ }
+ unless ($width || $height || $x || $y) {
+ $self->session->errorHandler->error("Can't resize with no resizing parameters.");
+ return 0;
+ }
+ my $image = Image::Magick->new;
+ my $error = $image->Read($self->getPath($filename));
+ if ($error) {
+ $self->session->errorHandler->error("Couldn't read image for resizing: ".$error);
+ return 0;
+ }
+
+ # Next, resize dimensions
+ if ( $width || $height || $x || $y ) {
+ $self->session->errorHandler->info( "Resizing $filename to w:$width h:$height x:$x y:$y" );
+ $image->Crop( height => $height, width => $width, x => $x, y => $y );
+ }
+
+ # Write our changes to disk
+ $error = $image->Write($self->getPath($filename));
+ if ($error) {
+ $self->session->errorHandler->error("Couldn't resize image: ".$error);
+ return 0;
+ }
+
+ return 1;
+}
+
+#-------------------------------------------------------------------
+
+=head2 annotate ( filename [ text ] )
+
+Adds annotation text to the image.
+
+=head3 filename
+
+The name of the file to annotate.
+
+=head3 text
+
+Text to add.
+
+=cut
+
+sub annotate {
+ my $self = shift;
+ my $filename = shift;
+ my $asset = shift;
+ my $form = shift;
+ unless (defined $filename) {
+ $self->session->errorHandler->error("Can't rotate when you haven't specified a file.");
+ return 0;
+ }
+ unless ($self->isImage($filename)) {
+ $self->session->errorHandler->error("Can't rotate something that's not an image.");
+ return 0;
+ }
+ # unless ($annotate_text) {
+ # $self->session->errorHandler->error("Can't annotate with no text.");
+ # return 0;
+ # }
+ # unless ($annotate_top && $annotate_left && $annotate_width && $annotate_height) {
+ # $self->session->errorHandler->error("Can't annotate with no dimensions.");
+ # return 0;
+ # }
+
+ my $annotate = $asset->get('annotations');
+ my $save_annotate = "";
+ my @pieces = split(/\n/, $annotate);
+ for (my $i = 0; $i < $#pieces; $i += 3) {
+ my $top_left = $pieces[$i];
+ my $width_height = $pieces[$i + 1];
+ my $note = $pieces[$i + 2];
+
+ # warn("i: $i: ", $form->process("delAnnotate$i"));
+ next if $form->process("delAnnotate$i");
+
+ if ($save_annotate) {
+ $save_annotate .= "\n";
+ }
+ $save_annotate .= "$top_left\n$width_height\n$note";
+ }
+
+ my $annotate_text = $form->process("annotate_text");
+ my $annotate_top = $form->process("annotate_top");
+ my $annotate_left = $form->process("annotate_left");
+ my $annotate_width = $form->process("annotate_width");
+ my $annotate_height = $form->process("annotate_height");
+ # warn(qq(unless ($annotate_top && $annotate_left && $annotate_width && $annotate_height && $annotate_text !~ /^\s*$/)));
+ if (defined $annotate_top && defined $annotate_left && defined $annotate_width && defined $annotate_height && $annotate_text !~ /^\s*$/) {
+ if ($save_annotate) {
+ $save_annotate .= "\n";
+ }
+ # warn(qq($save_annotate .= "top: ${annotate_top}px; left: ${annotate_left}px;\nwidth: ${annotate_width}px; height: ${annotate_height}px;\n'$annotate_text'"));
+ $save_annotate .= "top: ${annotate_top}px; left: ${annotate_left}px;\nwidth: ${annotate_width}px; height: ${annotate_height}px;\n$annotate_text";
+ }
+ # warn($save_annotate);
+
+ $asset->update({ annotations => $save_annotate });
+ $save_annotate = $asset->get('annotations');
+ # warn($save_annotate);
+
+ return 1;
+}
+
+#-------------------------------------------------------------------
+
+=head2 rotate ( filename [ degrees ] )
+
+Rotates the image by the specified degrees.
+
+=head3 filename
+
+The name of the file to resize.
+
+=head3 width
+
+Number of degrees to rotate.
+
+=cut
+
+sub rotate {
+ my $self = shift;
+ my $filename = shift;
+ my $degree = shift || 0;
+ unless (defined $filename) {
+ $self->session->errorHandler->error("Can't rotate when you haven't specified a file.");
+ return 0;
+ }
+ unless ($self->isImage($filename)) {
+ $self->session->errorHandler->error("Can't rotate something that's not an image.");
+ return 0;
+ }
+ my $image = Image::Magick->new;
+ my $error = $image->Read($self->getPath($filename));
+ if ($error) {
+ $self->session->errorHandler->error("Couldn't read image for resizing: ".$error);
+ return 0;
+ }
+
+ $self->session->errorHandler->info( "Rotating $filename by $degree degrees" );
+ $image->Rotate( $degree );
+
+ # Write our changes to disk
+ $error = $image->Write($self->getPath($filename));
+ if ($error) {
+ $self->session->errorHandler->error("Couldn't rotate image: ".$error);
+ return 0;
+ }
+
+ return 1;
+}
+
+#-------------------------------------------------------------------
+
=head2 resize ( filename [, width, height ] )
Resizes the specified image by the specified height and width. If either is omitted the iamge will be scaleed proportionately to the non-omitted one.
diff --git a/lib/WebGUI/i18n/English/Asset_Image.pm b/lib/WebGUI/i18n/English/Asset_Image.pm
index 026b1529d..6ed0c5b2f 100644
--- a/lib/WebGUI/i18n/English/Asset_Image.pm
+++ b/lib/WebGUI/i18n/English/Asset_Image.pm
@@ -77,6 +77,54 @@ shown here.|,
lastUpdated => 1106765841
},
+ 'annotate' => {
+ message => q|Annotate|,
+ context => q|label to annotate the image|,
+ lastUpdated => 1106765841
+ },
+
+ 'annotate image' => {
+ message => q|Annotate Image|,
+ context => q|label to annotate the image|,
+ lastUpdated => 1106765841
+ },
+
+ 'annotate image description' => {
+ message => q|Text Around the Image|,
+ context => q|label to annotate the image|,
+ lastUpdated => 1106765841
+ },
+
+ 'degree' => {
+ message => q|Degrees to Rotate|,
+ context => q|label to rotate the image|,
+ lastUpdated => 1106765841
+ },
+
+ 'rotate image label' => {
+ message => q|Please click to rotate image|,
+ context => q|label to rotate the image|,
+ lastUpdated => 1106765841
+ },
+
+ 'rotate image' => {
+ message => q|Rotate Image|,
+ context => q|label to rotate the image|,
+ lastUpdated => 1106765841
+ },
+
+ 'rotate image label' => {
+ message => q|Please click to rotate image|,
+ context => q|label to rotate the image|,
+ lastUpdated => 1106765841
+ },
+
+ 'crop image' => {
+ message => q|Crop Image|,
+ context => q|label to crop the image|,
+ lastUpdated => 1106765841
+ },
+
'new width' => {
message => q|New Width|,
context => q|label to resize the image|,
@@ -101,6 +149,42 @@ shown here.|,
lastUpdated => 1130538987
},
+ 'undo image' => {
+ message => q|Undo Image|,
+ context => q|undo editing operations|,
+ lastUpdated => 1106765841
+ },
+
+ 'delete' => {
+ message => q|Delete|,
+ context => q|label to delete annotation|,
+ lastUpdated => 1106765841
+ },
+
+ 'height' => {
+ message => q|Height|,
+ context => q|label to resize the image|,
+ lastUpdated => 1106765841
+ },
+
+ 'width' => {
+ message => q|Width|,
+ context => q|label to resize the image|,
+ lastUpdated => 1106765841
+ },
+
+ 'top' => {
+ message => q|Top|,
+ context => q|label to resize the image|,
+ lastUpdated => 1106765841
+ },
+
+ 'left' => {
+ message => q|Left|,
+ context => q|label to resize the image|,
+ lastUpdated => 1106765841
+ },
+
'image template title' => {
message => q|Image Template Variables|,
lastUpdated => 1184820779,
diff --git a/t/Asset/File/Image.t b/t/Asset/File/Image.t
index bad2c80d4..90307548d 100644
--- a/t/Asset/File/Image.t
+++ b/t/Asset/File/Image.t
@@ -20,6 +20,7 @@ BEGIN {
$mocker->fake_new('WebGUI::Form::Image');
}
+use File::Copy;
use WebGUI::Test;
use WebGUI::Session;
use WebGUI::Image;
@@ -29,23 +30,23 @@ use WebGUI::Form::File;
use Test::More; # increment this value for each test you create
use Test::Deep;
-plan tests => 7;
+plan tests => 11;
my $session = WebGUI::Test->session;
-my $square = WebGUI::Image->new($session, 100, 100);
-$square->setBackgroundColor('#0000FF');
+my $rectangle = WebGUI::Image->new($session, 100, 200);
+$rectangle->setBackgroundColor('#0000FF');
##Create a storage location
my $storage = WebGUI::Storage->create($session);
##Save the image to the location
-$square->saveToStorageLocation($storage, 'square.png');
+$rectangle->saveToStorageLocation($storage, 'blue.png');
##Do a file existance check.
ok((-e $storage->getPath and -d $storage->getPath), 'Storage location created and is a directory');
-cmp_bag($storage->getFiles, ['square.png'], 'Only 1 file in storage with correct name');
+cmp_bag($storage->getFiles, ['blue.png'], 'Only 1 file in storage with correct name');
##Initialize an Image Asset with that filename and storage location
@@ -68,9 +69,29 @@ is($asset->get('storageId'), $asset->getStorageLocation->getId, 'Image Asset sto
$asset->update({
storageId => $storage->getId,
- filename => 'square.png',
+ filename => 'blue.png',
});
+my $filename = $asset->getStorageLocation->getPath . "/" . $asset->get("filename");
+
+my @stat_before = stat($filename);
+$asset->getStorageLocation->rotate($asset->get("filename"), 90);
+my @stat_after = stat($filename);
+is(isnt_array(\@stat_before, \@stat_after), 1, 'Image is different after rotation');
+
+@stat_before = stat($filename);
+$asset->getStorageLocation->resize($asset->get("filename"), 200, 300);
+my @stat_after = stat($filename);
+is(isnt_array(\@stat_before, \@stat_after), 1, 'Image is different after resize');
+
+@stat_before = stat($filename);
+$asset->getStorageLocation->crop($asset->get("filename"), 100, 125, 10, 25);
+my @stat_after = stat($filename);
+is(isnt_array(\@stat_before, \@stat_after), 1, 'Image is different after crop');
+
+my $sth = $session->db->read('describe imageAsset annotations');
+isnt($sth->hashRef, undef, 'Annotations column is defined');
+
is($storage->getId, $asset->get('storageId'), 'Asset updated with correct new storageId');
is($storage->getId, $asset->getStorageLocation->getId, 'Cached Asset storage location updated with correct new storageId');
@@ -81,3 +102,13 @@ END {
$versionTag->rollback;
}
}
+
+sub isnt_array {
+ my ($a, $b) = @_;
+
+ for (my $i = 0; $i < @{ $a }; ++$i) {
+ return 1 if @{ $a }[$i] ne @{ $b }[$i];
+ }
+
+ return 0;
+}
diff --git a/www/extras/yui/build/fonts/imagecropper/assets/sam/imagecropper.css b/www/extras/yui/build/fonts/imagecropper/assets/sam/imagecropper.css
new file mode 100644
index 000000000..73d3216f9
--- /dev/null
+++ b/www/extras/yui/build/fonts/imagecropper/assets/sam/imagecropper.css
@@ -0,0 +1,7 @@
+/*
+Copyright (c) 2008, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.6.0
+*/
+.yui-crop{position:relative;}.yui-crop .yui-crop-mask{position:absolute;top:0;left:0;height:100%;width:100%;}.yui-crop .yui-resize{position:absolute;top:10px;left:10px;border:0;}.yui-crop .yui-crop-resize-mask{position:absolute;top:0;left:0;height:100%;width:100%;background-position:-10px -10px;overflow:hidden;}.yui-skin-sam .yui-crop .yui-crop-mask{background-color:#000;opacity:.5;filter:alpha(opacity=50);}.yui-skin-sam .yui-crop .yui-resize{border:1px dashed #fff;}