diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index d801d3bb8..a9c6defbd 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -1,4 +1,8 @@ 7.7.1 + - rfe #9353: Welcome message template (SDH Consulting Group) + - rfe #10007: New Month and Year question type. If required, Month must be selected and a 4 digit year must be entered. + - fixed #10011: Wrong spelling of "Descendant" in Navigation Asset Help function (Rob Schipper/NFIA India) + - rfe #9098: Thingy Thing-copy function (SDH Consulting Group) 7.7.0 - fixed #9913: New Content Side Bar missing in Asset window diff --git a/docs/create.sql b/docs/create.sql index 188efc80b..524ec766e 100644 --- a/docs/create.sql +++ b/docs/create.sql @@ -971,6 +971,7 @@ CREATE TABLE `RSSCapable` ( `rssCapableRssEnabled` int(11) NOT NULL default '1', `rssCapableRssTemplateId` char(22) NOT NULL default 'PBtmpl0000000000000142', `rssCapableRssFromParentId` char(22) default NULL, + `rssCapableRssLimit` integer default '10', PRIMARY KEY (`assetId`,`revisionDate`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; SET character_set_client = @saved_cs_client; 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/packages-7.7.1/root_import_auth_webgui_create_default-webgui-welcome-message-template.wgpkg b/docs/upgrades/packages-7.7.1/root_import_auth_webgui_create_default-webgui-welcome-message-template.wgpkg new file mode 100644 index 000000000..868b4da2b Binary files /dev/null and b/docs/upgrades/packages-7.7.1/root_import_auth_webgui_create_default-webgui-welcome-message-template.wgpkg differ diff --git a/docs/upgrades/packages-7.7.1/root_import_survey_default-questions.wgpkg b/docs/upgrades/packages-7.7.1/root_import_survey_default-questions.wgpkg new file mode 100644 index 000000000..8b7602ba2 Binary files /dev/null and b/docs/upgrades/packages-7.7.1/root_import_survey_default-questions.wgpkg differ diff --git a/docs/upgrades/packages-7.7.1/templates_thingy-default.wgpkg b/docs/upgrades/packages-7.7.1/templates_thingy-default.wgpkg new file mode 100644 index 000000000..025ed65b3 Binary files /dev/null and b/docs/upgrades/packages-7.7.1/templates_thingy-default.wgpkg differ diff --git a/docs/upgrades/upgrade_7.6.14-7.7.0.pl b/docs/upgrades/upgrade_7.6.14-7.7.0.pl index a614dffc7..573053173 100644 --- a/docs/upgrades/upgrade_7.6.14-7.7.0.pl +++ b/docs/upgrades/upgrade_7.6.14-7.7.0.pl @@ -123,7 +123,7 @@ sub installAssetHistory { print "\tAdding Asset History content handler... \n" unless $quiet; ##Content Handler my $contentHandlers = $session->config->get('contentHandlers'); - if (! isIn('WebGUI::Content::Handler', @{ $contentHandlers }) ) { + if (! isIn('WebGUI::Content::AssetHistory', @{ $contentHandlers }) ) { my @newHandlers = (); foreach my $handler (@{ $contentHandlers }) { push @newHandlers, $handler; 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 4d03f414d..4aebadfd5 100644 --- a/docs/upgrades/upgrade_7.7.0-7.7.1.pl +++ b/docs/upgrades/upgrade_7.7.0-7.7.1.pl @@ -32,9 +32,54 @@ my $session = start(); # this line required # upgrade functions go here adSkuInstall($session); +addWelcomeMessageTemplateToSettings( $session ); +addStatisticsCacheTimeoutToMatrix( $session ); + +# image mods +addImageAnnotation($session); + +# rss mods +addRssLimit($session); finish($session); # this line required +sub addWelcomeMessageTemplateToSettings { + my $session = shift; + print "\tAdding welcome message template to settings \n" unless $quiet; + + $session->db->write("insert into settings values ('webguiWelcomeMessageTemplate', 'PBtmpl0000000000000015');"); + 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; + print "\tAdding statisticsCacheTimeout setting to Matrix table... \n" unless $quiet; + $session->db->write("alter table Matrix add statisticsCacheTimeout int(11) not null default 3600"); + print "Done.\n" unless $quiet; +} #---------------------------------------------------------------------------- # Describe what our function does 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 = '
'.$self->get(
'; + + 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 = '
'.$self->get(
'; + + 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 = '
'.$self->get(
'; + my $image = '
'.$self->get(
'.$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 = '
'.$filename.'
'.$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/Post.pm b/lib/WebGUI/Asset/Post.pm index fa68b8624..5debd6ba5 100644 --- a/lib/WebGUI/Asset/Post.pm +++ b/lib/WebGUI/Asset/Post.pm @@ -1424,8 +1424,6 @@ sub www_edit { richEditId=>$self->getThread->getParent->get("richEditor") }); ##Edit variables just for Threads - $session->log->warn("className: $className"); - $session->log->warn("canEdit parent: ". $self->getThread->getParent->canEdit); if ($className eq 'WebGUI::Asset::Post::Thread' && $self->getThread->getParent->canEdit) { $var{'sticky.form'} = WebGUI::Form::yesNo($session, { name=>'isSticky', diff --git a/lib/WebGUI/Asset/RSSCapable.pm b/lib/WebGUI/Asset/RSSCapable.pm index d75af0d9c..f6c86652d 100644 --- a/lib/WebGUI/Asset/RSSCapable.pm +++ b/lib/WebGUI/Asset/RSSCapable.pm @@ -35,11 +35,11 @@ using the RSSFromParent asset. =cut sub definition { - my $class = shift; - my $session = shift; - my $definition = shift; - my %properties; - tie %properties, 'Tie::IxHash'; +my $class = shift; +my $session = shift; +my $definition = shift; +my %properties; +tie %properties, 'Tie::IxHash'; my $i18n = WebGUI::International->new($session, 'Asset_RSSCapable'); # We do this prefixing to avoid name collisions because properties aren't namespaced. @@ -58,6 +58,13 @@ sub definition { label => $i18n->get('rssTemplateId label'), hoverHelp => $i18n->get('rssTemplateId hoverHelp') }, + rssCapableRssLimit => { tab => 'display', + fieldType => 'integer', + defaultValue => 10, + namespace => 'RSSCapable/RSS', + label => $i18n->get('rssLimit label'), + hoverHelp => $i18n->get('rssLimit hoverHelp') + }, rssCapableRssFromParentId => { fieldType => 'hidden', noFormPost => 1, defaultValue => undef, @@ -65,12 +72,12 @@ sub definition { ); push @$definition, { assetName => $i18n->get('assetName'), - tableName => 'RSSCapable', - autoGenerateForms => 1, - className => 'WebGUI::Asset::RSSCapable', - icon => 'rssCapable.gif', - properties => \%properties - }; + tableName => 'RSSCapable', + autoGenerateForms => 1, + className => 'WebGUI::Asset::RSSCapable', + icon => 'rssCapable.gif', + properties => \%properties + }; return $class->NEXT::definition($session, $definition); } diff --git a/lib/WebGUI/Asset/RSSFromParent.pm b/lib/WebGUI/Asset/RSSFromParent.pm index edb052db4..27127e341 100644 --- a/lib/WebGUI/Asset/RSSFromParent.pm +++ b/lib/WebGUI/Asset/RSSFromParent.pm @@ -131,7 +131,7 @@ sub www_view { my $self = shift; return '' unless $self->session->asset->getId eq $self->getId; return '' unless $self->getParent->isa('WebGUI::Asset::RSSCapable'); - return '' unless $self->getParent->canView; # Go to parent for auth + return '' unless $self->getParent->canView; # Go to parent for auth my $parent = $self->getParent; my $template = WebGUI::Asset::Template->new($self->session, $parent->get('rssCapableRssTemplateId')); $template->prepare($self->getMetaDataAsTemplateVariables); 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/Asset/Wobject/Matrix.pm b/lib/WebGUI/Asset/Wobject/Matrix.pm index 4e4075dfe..b23baac70 100644 --- a/lib/WebGUI/Asset/Wobject/Matrix.pm +++ b/lib/WebGUI/Asset/Wobject/Matrix.pm @@ -238,6 +238,14 @@ sub definition { hoverHelp =>$i18n->get('ratings duration description'), label =>$i18n->get('ratings duration label'), }, + statisticsCacheTimeout => { + tab => "display", + fieldType => "interval", + defaultValue => 3600, + uiLevel => 8, + label => $i18n->get("statistics cache timeout label"), + hoverHelp => $i18n->get("statistics cache timeout description") + }, ); push(@{$definition}, { assetName=>$i18n->get('assetName'), @@ -520,6 +528,7 @@ sub view { $self->session->style->setScript($self->session->url->extras('wobject/Matrix/matrix.js'), {type => 'text/javascript'}); + my ($varStatistics,$varStatisticsEncoded); my $var = $self->get; $var->{isLoggedIn} = ($self->session->user->userId ne "1"); $var->{addMatrixListing_url} = $self->getUrl('func=add;class=WebGUI::Asset::MatrixListing'); @@ -527,90 +536,104 @@ sub view { $var->{exportAttributes_url} = $self->getUrl('func=exportAttributes'); $var->{listAttributes_url} = $self->getUrl('func=listAttributes'); $var->{search_url} = $self->getUrl('func=search'); - - # Get the MatrixListing with the most views as an object using getLineage. - my ($bestViews_listing) = @{ $self->getLineage(['descendants'], { + + if ($self->canEdit){ + # Get all the MatrixListings that are still pending. + my @pendingListings = @{ $self->getLineage(['descendants'], { + includeOnlyClasses => ['WebGUI::Asset::MatrixListing'], + orderByClause => "revisionDate asc", + returnObjects => 1, + statusToInclude => ['pending'], + }) }; + foreach my $pendingListing (@pendingListings){ + push (@{ $var->{pending_loop} }, { + url => $pendingListing->getUrl + ."?func=view;revision=".$pendingListing->get('revisionDate'), + name => $pendingListing->get('title'), + }); + } + } + + my $versionTag = WebGUI::VersionTag->getWorking($session, 1); + my $noCache = + $session->var->isAdminOn + || $self->get("statisticsCacheTimeout") <= 10 + || ($versionTag && $versionTag->getId eq $self->get("tagId")); + unless ($noCache) { + $varStatisticsEncoded = WebGUI::Cache->new($session,"matrixStatistics_".$self->getId)->get; + } + + if ($varStatisticsEncoded){ + $varStatistics = JSON->new->decode($varStatisticsEncoded); + } + else{ + # Get the MatrixListing with the most views as an object using getLineage. + my ($bestViews_listing) = @{ $self->getLineage(['descendants'], { includeOnlyClasses => ['WebGUI::Asset::MatrixListing'], joinClass => "WebGUI::Asset::MatrixListing", orderByClause => "views desc", limit => 1, returnObjects => 1, }) }; - if($bestViews_listing){ - $var->{bestViews_url} = $bestViews_listing->getUrl; - $var->{bestViews_count} = $bestViews_listing->get('views'); - $var->{bestViews_name} = $bestViews_listing->get('title'); - $var->{bestViews_sortButton} = "
"; - } + if($bestViews_listing){ + $varStatistics->{bestViews_url} = $bestViews_listing->getUrl; + $varStatistics->{bestViews_count} = $bestViews_listing->get('views'); + $varStatistics->{bestViews_name} = $bestViews_listing->get('title'); + $varStatistics->{bestViews_sortButton} = "
"; + } - # Get the MatrixListing with the most compares as an object using getLineage. + # Get the MatrixListing with the most compares as an object using getLineage. - my ($bestCompares_listing) = @{ $self->getLineage(['descendants'], { + my ($bestCompares_listing) = @{ $self->getLineage(['descendants'], { includeOnlyClasses => ['WebGUI::Asset::MatrixListing'], joinClass => "WebGUI::Asset::MatrixListing", orderByClause => "compares desc", limit => 1, returnObjects => 1, }) }; - if($bestCompares_listing){ - $var->{bestCompares_url} = $bestCompares_listing->getUrl; - $var->{bestCompares_count} = $bestCompares_listing->get('compares'); - $var->{bestCompares_name} = $bestCompares_listing->get('title'); - $var->{bestCompares_sortButton} = "
"; - } + if($bestCompares_listing){ + $varStatistics->{bestCompares_url} = $bestCompares_listing->getUrl; + $varStatistics->{bestCompares_count} = $bestCompares_listing->get('compares'); + $varStatistics->{bestCompares_name} = $bestCompares_listing->get('title'); + $varStatistics->{bestCompares_sortButton} = "
"; + } - # Get the MatrixListing with the most clicks as an object using getLineage. - my ($bestClicks_listing) = @{ $self->getLineage(['descendants'], { + # Get the MatrixListing with the most clicks as an object using getLineage. + my ($bestClicks_listing) = @{ $self->getLineage(['descendants'], { includeOnlyClasses => ['WebGUI::Asset::MatrixListing'], joinClass => "WebGUI::Asset::MatrixListing", orderByClause => "clicks desc", limit => 1, returnObjects => 1, }) }; - if($bestClicks_listing){ - $var->{bestClicks_url} = $bestClicks_listing->getUrl; - $var->{bestClicks_count} = $bestClicks_listing->get('clicks'); - $var->{bestClicks_name} = $bestClicks_listing->get('title'); - $var->{bestClicks_sortButton} = "
"; - } - # Get the 5 MatrixListings that were last updated as objects using getLineage. + if($bestClicks_listing){ + $varStatistics->{bestClicks_url} = $bestClicks_listing->getUrl; + $varStatistics->{bestClicks_count} = $bestClicks_listing->get('clicks'); + $varStatistics->{bestClicks_name} = $bestClicks_listing->get('title'); + $varStatistics->{bestClicks_sortButton} = "
"; + } - my @lastUpdatedListings = @{ $self->getLineage(['descendants'], { + # Get the 5 MatrixListings that were last updated as objects using getLineage. + + my @lastUpdatedListings = @{ $self->getLineage(['descendants'], { includeOnlyClasses => ['WebGUI::Asset::MatrixListing'], joinClass => "WebGUI::Asset::MatrixListing", orderByClause => "lastUpdated desc", limit => 5, returnObjects => 1, }) }; - foreach my $lastUpdatedListing (@lastUpdatedListings){ - push (@{ $var->{last_updated_loop} }, { + foreach my $lastUpdatedListing (@lastUpdatedListings){ + push (@{ $varStatistics->{last_updated_loop} }, { url => $lastUpdatedListing->getUrl, name => $lastUpdatedListing->get('title'), lastUpdated => $self->session->datetime->epochToHuman($lastUpdatedListing->get('lastUpdated'),"%z") }); - } - $var->{lastUpdated_sortButton} = "
"; + } + $var->{lastUpdated_sortButton} = "
"; + # For each category, get the MatrixListings with the best ratings. - # Get all the MatrixListings that are still pending. - - my @pendingListings = @{ $self->getLineage(['descendants'], { - includeOnlyClasses => ['WebGUI::Asset::MatrixListing'], - orderByClause => "revisionDate asc", - returnObjects => 1, - statusToInclude => ['pending'], - }) }; - foreach my $pendingListing (@pendingListings){ - push (@{ $var->{pending_loop} }, { - url => $pendingListing->getUrl - ."?func=view;revision=".$pendingListing->get('revisionDate'), - name => $pendingListing->get('title'), - }); - } - - # For each category, get the MatrixListings with the best ratings. - - foreach my $category (keys %{$self->getCategories}) { + foreach my $category (keys %{$self->getCategories}) { my $data; my $sql = " select @@ -641,7 +664,7 @@ sub view { order by rating.meanValue "; $data = $db->quickHashRef($sql." desc limit 1",[$category,$self->getId]); - push(@{ $var->{best_rating_loop} },{ + push(@{ $varStatistics->{best_rating_loop} },{ url=>'/'.$data->{url}, category=>$category, name=>$data->{productName}, @@ -650,7 +673,7 @@ sub view { count=>$data->{countValue} }); $data = $db->quickHashRef($sql." asc limit 1",[$category,$self->getId]); - push(@{ $var->{worst_rating_loop} },{ + push(@{ $varStatistics->{worst_rating_loop} },{ url=>'/'.$data->{url}, category=>$category, name=>$data->{productName}, @@ -658,9 +681,9 @@ sub view { median=>$data->{medianValue}, count=>$data->{countValue} }); - } + } - $var->{listingCount} = scalar $db->buildArray(" + $varStatistics->{listingCount} = scalar $db->buildArray(" select * from asset, assetData where asset.assetId=assetData.assetId @@ -670,7 +693,17 @@ sub view { and assetData.status='approved' group by asset.assetId", [$self->getId]); - + + $varStatisticsEncoded = JSON->new->encode($varStatistics); + WebGUI::Cache->new($session,"matrixStatistics_".$self->getId)->set( + $varStatisticsEncoded,$self->get("statisticsCacheTimeout") + ); + } + + foreach my $statistic (keys %{$varStatistics}) { + $var->{$statistic} = $varStatistics->{$statistic}; + } + return $self->processTemplate($var, undef, $self->{_viewTemplate}); } @@ -977,14 +1010,31 @@ sub www_getCompareFormData { my $form = $session->form; my $sort = shift || $session->scratch->get('matrixSort') || $self->get('defaultSort'); my $sortDirection = ' desc'; -# if ( WebGUI::Utility::isIn($sort, qw(revisionDate score)) ) { -# $sortDirection = " desc"; -# } + my @results; my @listingIds = $self->session->form->checkList("listingId"); $self->session->http->setMimeType("application/json"); + my (@searchParams,@searchParams_sorted,@searchParamList,$searchParamList); + if($form->process("search")){ + foreach my $param ($form->param) { + if($param =~ m/^search_/){ + my $parameter; + $parameter->{name} = $param; + $parameter->{value} = $form->process($param); + my $attributeId = $param; + $attributeId =~ s/^search_//; + $attributeId =~ s/_____/-/g; + $parameter->{attributeId} = $attributeId; + push(@searchParamList,'"'.$parameter->{attributeId}.'"'); + push(@searchParams,$parameter); + } + } + $searchParamList = join(',',@searchParamList); + @searchParams_sorted = sort { $b->{value} <=> $a->{value} } @searchParams; + } + my $sql = " select assetData.title, @@ -1005,31 +1055,18 @@ assetData.revisionDate and assetData.revisionDate = (SELECT max(revisionDate) from assetData where assetId=asset.assetId and status='approved') and status='approved' order by ".$sort.$sortDirection; + + my $sth = $self->session->db->read($sql,[$self->getId]); + my @results; - @results = @{ $session->db->buildArrayRefOfHashRefs($sql,[$self->getId]) }; - - my (@searchParams,@searchParams_sorted); + my $gateway = $self->session->config->get("gateway"); if($form->process("search")){ - foreach my $param ($form->param) { - if($param =~ m/^search_/){ - my $parameter; - $parameter->{name} = $param; - $parameter->{value} = $form->process($param); - my $attributeId = $param; - $attributeId =~ s/^search_//; - $attributeId =~ s/_____/-/g; - $parameter->{attributeId} = $attributeId; - push(@searchParams,$parameter); - } - } - } - @searchParams_sorted = sort { $b->{value} <=> $a->{value} } @searchParams; - foreach my $result (@results){ - if($form->process("search")){ + while (my $result = $sth->hashRef) { my $matrixListing_attributes = $session->db->buildHashRefOfHashRefs(" select value, fieldType, attributeId from Matrix_attribute left join MatrixListing_attribute as listing using(attributeId) - where listing.matrixListingId = ? order by value asc", + where listing.matrixListingId = ? + and attributeId IN(".$searchParamList.")", [$result->{assetId}],'attributeId'); PARAM: foreach my $param (@searchParams_sorted) { my $fieldType = $matrixListing_attributes->{$param->{attributeId}}->{fieldType}; @@ -1046,20 +1083,27 @@ assetData.revisionDate $result->{checked} = 'checked'; } } - } - else{ - $result->{assetId} =~ s/-/_____/g; - if(WebGUI::Utility::isIn($result->{assetId},@listingIds)){ - $result->{checked} = 'checked'; - } - } $result->{assetId} =~ s/-/_____/g; - $result->{url} = $session->url->gateway($result->{url}); + $result->{url} = $gateway."/".$result->{url}; + push @results, $result; + } + }else{ + while (my $result = $sth->hashRef) { + $result->{assetId} =~ s/-/_____/g; + if(WebGUI::Utility::isIn($result->{assetId},@listingIds)){ + $result->{checked} = 'checked'; + } + $result->{url} = $gateway."/".$result->{url}; + push @results, $result; + } } + $sth->finish; + my $jsonOutput; $jsonOutput->{ResultSet} = {Result=>\@results}; my $encodedOutput = JSON->new->encode($jsonOutput); + return $encodedOutput; } diff --git a/lib/WebGUI/Asset/Wobject/Survey.pm b/lib/WebGUI/Asset/Wobject/Survey.pm index 97ab5a70c..0afea44a3 100644 --- a/lib/WebGUI/Asset/Wobject/Survey.pm +++ b/lib/WebGUI/Asset/Wobject/Survey.pm @@ -1219,8 +1219,10 @@ sub prepareShowSurveyTemplate { my %text = ( 'Text', 1, 'Email', 1, 'Phone Number', 1, 'Text Date', 1, 'Currency', 1 ); my %slider = ( 'Slider', 1, 'Dual Slider - Range', 1, 'Multi Slider - Allocate', 1 ); my %dateType = ( 'Date', 1, 'Date Range', 1 ); + my %dateShort = ( 'Year Month', 1 ); my %fileUpload = ( 'File Upload', 1 ); my %hidden = ( 'Hidden', 1 ); +use Data::Dumper; foreach my $q (@$questions) { if ( $fileUpload{ $q->{questionType} } ) { $q->{fileLoader} = 1; } @@ -1236,6 +1238,26 @@ sub prepareShowSurveyTemplate { elsif ( $dateType{ $q->{questionType} } ) { $q->{dateType} = 1; } + elsif ( $dateShort{ $q->{questionType} } ) { + $q->{dateShort} = 1; + foreach my $a(@{$q->{answers}}){ + $a->{months} = [ + {'month' => ''}, + {'month' => 'January'}, + {'month' => 'February'}, + {'month' => 'March'}, + {'month' => 'April'}, + {'month' => 'May'}, + {'month' => 'June'}, + {'month' => 'July'}, + {'month' => 'August'}, + {'month' => 'September'}, + {'month' => 'October'}, + {'month' => 'November'}, + {'month' => 'December'} + ]; + } + } elsif ( $slider{ $q->{questionType} } ) { $q->{slider} = 1; if ( $q->{questionType} eq 'Dual Slider - Range' ) { diff --git a/lib/WebGUI/Asset/Wobject/Survey/SurveyJSON.pm b/lib/WebGUI/Asset/Wobject/Survey/SurveyJSON.pm index 701327de6..40a354874 100644 --- a/lib/WebGUI/Asset/Wobject/Survey/SurveyJSON.pm +++ b/lib/WebGUI/Asset/Wobject/Survey/SurveyJSON.pm @@ -115,6 +115,7 @@ my @SPECIAL_QUESTION_TYPES = ( 'File Upload', 'Date', 'Date Range', + 'Year Month', 'Hidden', ); diff --git a/lib/WebGUI/Asset/Wobject/Thingy.pm b/lib/WebGUI/Asset/Wobject/Thingy.pm index af0dcaa6d..ad996c24a 100644 --- a/lib/WebGUI/Asset/Wobject/Thingy.pm +++ b/lib/WebGUI/Asset/Wobject/Thingy.pm @@ -308,6 +308,42 @@ sub duplicate { return $newAsset; } +#------------------------------------------------------------------- + +=head2 duplicateThing ( thingId ) + +Duplicates a thing. + +=head3 thingId + +The id of the Thing that will be duplicated. + +=cut + +sub duplicateThing { + + my $self = shift; + my $oldThingId = shift; + my $db = $self->session->db; + + my $thingProperties = $self->getThing($oldThingId); + $thingProperties->{thingId} = 'new'; + $thingProperties->{label} = $thingProperties->{label}.' (copy)'; + + my $newThingId = $self->addThing($thingProperties); + my $fields = $db->buildArrayRefOfHashRefs('select * from Thingy_fields where assetId=? and thingId=?' + ,[$self->getId,$oldThingId]); + foreach my $field (@$fields) { + # set thingId to newly created thing's id. + $field->{thingId} = $newThingId; + $self->addField($field,0); + } + + return $newThingId; + +} + + #------------------------------------------------------------------- =head2 deleteField ( fieldId , thingId ) @@ -1436,6 +1472,26 @@ sub www_deleteFieldConfirm { return 1; } + +#------------------------------------------------------------------- + +=head2 www_duplicateThing ( ) + +Duplicates a Thing. + +=cut + +sub www_duplicateThing { + my $self = shift; + my $session = $self->session; + my $thingId = $session->form->process("thingId"); + return $session->privilege->insufficient() unless $self->canEdit; + + $self->duplicateThing($thingId); + + return $self->www_manage; +} + #------------------------------------------------------------------- =head2 www_copyThingData( ) @@ -2868,6 +2924,8 @@ sub www_manage { "",$i18n->get('delete thing warning')), 'thing_editUrl' => $session->url->append($url, 'func=editThing;thingId='.$thing->{thingId}), 'thing_editIcon' => $session->icon->edit('func=editThing;thingId='.$thing->{thingId}), + 'thing_copyUrl' => $session->url->append($url, 'func=duplicateThing;thingId='.$thing->{thingId}), + 'thing_copyIcon' => $session->icon->copy('func=duplicateThing;thingId='.$thing->{thingId}), 'thing_addUrl' => $session->url->append($url, 'func=editThingData;thingId='.$thing->{thingId}.';thingDataId=new'), 'thing_searchUrl' => $session->url->append($url, 'func=search;thingId='.$thing->{thingId}), diff --git a/lib/WebGUI/Auth.pm b/lib/WebGUI/Auth.pm index b39f72804..dbfec3184 100644 --- a/lib/WebGUI/Auth.pm +++ b/lib/WebGUI/Auth.pm @@ -298,11 +298,14 @@ sub createAccountSave { $self->saveParams($userId,$self->authMethod,$properties); if ($self->getSetting("sendWelcomeMessage")){ - my $authInfo = "\n\n".$i18n->get(50).": ".$username; - $authInfo .= "\n".$i18n->get(51).": ".$password if($password); - $authInfo .= "\n\n"; - WebGUI::Inbox->new($self->session)->addMessage({ - message => $self->getSetting("welcomeMessage").$authInfo, + my $var; + $var->{welcomeMessage} = $self->getSetting("welcomeMessage"); + $var->{newUser_username} = $username; + $var->{newUser_password} = $password; + my $message = WebGUI::Asset::Template->new($self->session,$self->getSetting('welcomeMessageTemplate'))->process($var); + WebGUI::Macro::process($self->session,\$message); + WebGUI::Inbox->new($self->session)->addMessage({ + message => $message, subject => $i18n->get(870), userId => $self->userId, status => 'completed', diff --git a/lib/WebGUI/Auth/WebGUI.pm b/lib/WebGUI/Auth/WebGUI.pm index a0bc70e45..f8f0043c0 100644 --- a/lib/WebGUI/Auth/WebGUI.pm +++ b/lib/WebGUI/Auth/WebGUI.pm @@ -497,7 +497,7 @@ sub editUserSettingsForm { -label => $i18n->get(868,'WebGUI'), -hoverHelp => $i18n->get('868 help','WebGUI'), ); - $f->textarea( + $f->HTMLArea( -name => "webguiWelcomeMessage", -value => $self->session->setting->get("webguiWelcomeMessage"), -label => $i18n->get(869,'WebGUI'), @@ -577,6 +577,13 @@ sub editUserSettingsForm { -label => $i18n->get("password recovery template"), -hoverHelp => $i18n->get("password recovery template help") ); + $f->template( + -name => "webguiWelcomeMessageTemplate", + -value => $self->session->setting->get("webguiWelcomeMessageTemplate"), + -namespace => "Auth/WebGUI/Welcome", + -label => $i18n->get("welcome message template"), + -hoverHelp => $i18n->get("welcome message template help") + ); $f->template( -name => "webguiAccountActivationTemplate", -value => $self->session->setting->get("webguiAccountActivationTemplate"), @@ -635,6 +642,7 @@ sub editUserSettingsFormSave { $s->set("webguiExpiredPasswordTemplate", $f->process("webguiExpiredPasswordTemplate","template")); $s->set("webguiLoginTemplate", $f->process("webguiLoginTemplate","template")); $s->set("webguiPasswordRecoveryTemplate", $f->process("webguiPasswordRecoveryTemplate","template")); + $s->set("webguiWelcomeMessageTemplate", $f->process("webguiWelcomeMessageTemplate","template")); $s->set("webguiAccountActivationTemplate", $f->process("webguiAccountActivationTemplate","template")); if (@errors) { diff --git a/lib/WebGUI/Help/Asset_Navigation.pm b/lib/WebGUI/Help/Asset_Navigation.pm index 39c764faa..3dac4e2bf 100644 --- a/lib/WebGUI/Help/Asset_Navigation.pm +++ b/lib/WebGUI/Help/Asset_Navigation.pm @@ -53,7 +53,7 @@ our $HELP = { { 'name' => 'page.isChild' }, { 'name' => 'page.isParent' }, { 'name' => 'page.isCurrent' }, - { 'name' => 'page.isDescendent' }, + { 'name' => 'page.isDescendant' }, { 'name' => 'page.isAncestor' }, { 'name' => 'page.inBranchRoot' }, { 'name' => 'page.isSibling' }, diff --git a/lib/WebGUI/Help/Asset_Thingy.pm b/lib/WebGUI/Help/Asset_Thingy.pm index 366d22969..d740ed4f5 100644 --- a/lib/WebGUI/Help/Asset_Thingy.pm +++ b/lib/WebGUI/Help/Asset_Thingy.pm @@ -26,6 +26,8 @@ our $HELP = { { 'name' => 'thing_deleteUrl' }, { 'name' => 'thing_searchUrl' }, { 'name' => 'thing_addUrl' }, + { 'name' => 'thing_copyUrl' }, + { 'name' => 'thing_copyIcon' }, ] }, ], diff --git a/lib/WebGUI/Help/AuthWebGUI.pm b/lib/WebGUI/Help/AuthWebGUI.pm index 6103b6cea..9eb272195 100644 --- a/lib/WebGUI/Help/AuthWebGUI.pm +++ b/lib/WebGUI/Help/AuthWebGUI.pm @@ -163,6 +163,18 @@ our $HELP = { related => [], }, + 'webgui welcome message template' => { + title => 'welcome message template title', + body => '', + variables => [ + { 'name' => 'welcomeMessage' }, + { 'name' => 'newUser_username' }, + { 'name' => 'newUser_password' }, + ], + fields => [], + related => [] + }, + 'account activation template' => { title => 'account activation template title', body => '', diff --git a/lib/WebGUI/Inbox/Message.pm b/lib/WebGUI/Inbox/Message.pm index 9cbcfbd19..fb6bf0b96 100644 --- a/lib/WebGUI/Inbox/Message.pm +++ b/lib/WebGUI/Inbox/Message.pm @@ -163,14 +163,9 @@ sub create { $preface = sprintf($i18n->get('from user preface'), $fromUser->username); } my $msg = (defined $properties->{emailMessage}) ? $properties->{emailMessage} : $self->{_properties}{message}; - if ($msg =~ m/\<.*\>/) { - $msg = '

' . $preface . '


'.$msg if($preface ne ""); - $mail->addHtml($msg); - } else { - $msg = $preface."\n\n".$msg if($preface ne ""); - $mail->addText($msg); - } - $mail->addFooter; + $msg = '

' . $preface . '


'.$msg if($preface ne ""); + $mail->addHtml($msg); + $mail->addFooter; $mail->queue; } $self->{_session} = $session; 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/lib/WebGUI/i18n/English/Asset_Matrix.pm b/lib/WebGUI/i18n/English/Asset_Matrix.pm index f7ace67ae..a8b5b5bf9 100644 --- a/lib/WebGUI/i18n/English/Asset_Matrix.pm +++ b/lib/WebGUI/i18n/English/Asset_Matrix.pm @@ -427,6 +427,17 @@ listing,|, lastUpdated => 1235681965, }, + 'statistics cache timeout label' => { + message => q|Statistics Cache Timeout|, + lastUpdated => 0, + }, + + 'statistics cache timeout description' => { + message => q|Since all users will see the matrix statistics the same way, we can cache them for some time +to increase performance. How long should we cache them?|, + lastUpdated => 0, + }, + 'rating timeout description' => { message => q|Set a timeout so that users are prevented from rating a given listing too often.|, lastUpdated => 1135271460, diff --git a/lib/WebGUI/i18n/English/Asset_Navigation.pm b/lib/WebGUI/i18n/English/Asset_Navigation.pm index 2dae4d4f6..00866ad42 100644 --- a/lib/WebGUI/i18n/English/Asset_Navigation.pm +++ b/lib/WebGUI/i18n/English/Asset_Navigation.pm @@ -136,10 +136,10 @@ all of the classes of pages that should be included:
The current page.
Siblings
Pages at the same level as the current URL.
-
Descendents
+
Descendants
Pages lower than the current page in the tree.
Pedigree
-
When using a different start page, this option selects the Ancestors, Siblings and Descendents of that page.
+
When using a different start page, this option selects the Ancestors, Siblings and Descendants of that page.
|, lastUpdated => 1146456217, }, @@ -372,7 +372,7 @@ the Navigation Template to determine who can see them in the menu.

lastUpdated => 1163720148, }, - 'page.isDescendent' => { + 'page.isDescendant' => { message => q|A conditional indicating whether this page is a descendant of the current page.|, lastUpdated => 1163720154, }, @@ -393,7 +393,7 @@ the Navigation Template to determine who can see them in the menu.

}, 'page.inBranch' => { - message => q|A conditional that is the logical OR of isAncestor, isSibling, isBasepage and isDescendent.|, + message => q|A conditional that is the logical OR of isAncestor, isSibling, isBasepage and isDescendant.|, lastUpdated => 1157647394, }, diff --git a/lib/WebGUI/i18n/English/Asset_RSSCapable.pm b/lib/WebGUI/i18n/English/Asset_RSSCapable.pm index 36e4fcd23..58c803c82 100644 --- a/lib/WebGUI/i18n/English/Asset_RSSCapable.pm +++ b/lib/WebGUI/i18n/English/Asset_RSSCapable.pm @@ -5,6 +5,8 @@ our $I18N = { 'rssEnabled label' => { message => 'Enable RSS', lastUpdate => 1162487361 }, 'rssEnabled hoverHelp' => { message => q|Whether or not to enable the RSS feed for this asset. If enabled, an RSS From Parent asset will be created and managed as an extra child for this purpose. If not enabled, no such child will be created and the existing one will be deleted.|, lastUpdate => 1162487361 }, + 'rssLimit label' => { message => 'RSS Display limit', lastUpdate => 1162487361 }, + 'rssLimit hoverHelp' => { message => q|How many RSS items to display|, lastUpdate => 1162487361 }, 'rssTemplateId label' => { message => 'RSS Template', lastUpdate => 1162487361 }, 'rssTemplateId hoverHelp' => { message => q|The template to use for the RSS feed of this asset.|, lastUpdate => 1162487361 }, diff --git a/lib/WebGUI/i18n/English/Asset_Thingy.pm b/lib/WebGUI/i18n/English/Asset_Thingy.pm index 3c7a987eb..96cce408a 100644 --- a/lib/WebGUI/i18n/English/Asset_Thingy.pm +++ b/lib/WebGUI/i18n/English/Asset_Thingy.pm @@ -652,47 +652,68 @@ Braces, quotes and all. You simply replace "key1"/"value1" with your own name/v 'canEditThings' => { message => q|Is true if the current user can view the Things that are defined for this Thingy Asset|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'things_loop' => { message => q|A loop containing the Things that are defined for this Thingy Asset, with links to management functions for those Things|, lastUpdated => 1104630516, + context => q|Description of a tmpl_loop for the template help.|, }, 'thing_editIcon' => { message => q|An icon with a link to the edit screen for this Thing|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'thing_editUrl' => { message => q|A link to the edit screen for this Thing|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, + }, + + 'thing_copyIcon' => { + message => q|An icon with a link to copy this Thing|, + lastUpdated => 0, + context => q|Description of a tmpl_var for the template help.|, + }, + + 'thing_copyUrl' => { + message => q|A link to copy this Thing|, + lastUpdated => 0, + context => q|Description of a tmpl_var for the template help.|, }, 'thing_viewIcon' => { message => q|An icon with a link to default screen for this Thing|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'thing_searchUrl' => { message => q|A link to the search screen for this Thing|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'thing_addUrl' => { message => q|A link to the add screen for this Thing|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'thing_deleteIcon' => { message => q|An icon with a link to delete this Thing|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'thing_deleteUrl' => { message => q|A link to delete this Thing|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'thing_label' => { @@ -708,31 +729,37 @@ functions for those Things|, 'manage_url' => { message => q|This Url will lead to a screen to manage the Things in this Thingy Asset.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'addThing_url' => { message => q|This Url leads to the screen to add a new Thing to this Thingy Asset.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'delete_url' => { message => q|The Url to delete the current thing|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'delete_confirm' => { message => q|This contains an onclick property for the delete link that adds a javascript confirmation|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'search_url' => { message => q|Url that leads to the search screen|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'add_url' => { message => q|Url to the add screen for this thing|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'editScreenTitle' => { @@ -749,16 +776,19 @@ functions for those Things|, message => q|A loop containing error information, for instance if someone doesn't fill out a required field.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_loop for the template help.|, }, 'error_message' => { message => q|An error message indicating what the user might have done wrong.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'field_loop' => { message => q|A loop containing the visible fields for this thing|, lastUpdated => 1104630516, + context => q|Description of a tmpl_loop for the template help.|, }, 'listOfThings' => { @@ -788,73 +818,87 @@ field.|, 'field_isHidden' => { message => q|A conditional indicating whether this field is supposed to be hidden.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'field_isRequired' => { message => q|A conditional indicating whether this field is required.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'field_isVisible' => { message => q|A conditional indicating whether this field is supposed to be visible but not editable.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'field_form' => { message => q|The form element for this field.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'field_name' => { message => q|The name of the form element for this field|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'field_label' => { message => q|The label of this field|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'field_url' => { message => q|An optional url that this field should link to, used i.e. for fields in other things.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'field_value' => { message => q|The value of this field|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'field_pretext' => { message => q|An extra description of the field so that users know what to put in the field. To be displayed above/before the form element.|, lastUpdated => 1224518799, + context => q|Description of a tmpl_var for the template help.|, }, 'field_subtext' => { message => q|A description of the field so that users know what to put in the field. To be displayed below/after the form element.|, lastUpdated => 1224518799, + context => q|Description of a tmpl_var for the template help.|, }, 'form_start' => { message => q|The start tag of the form|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'form_submit' => { message => q|A form button to submit the form data.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'form_end' => { message => q|The end of the form|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'edit_url' => { message => q|Url to the current things edit screen.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'viewScreenTitle' => { @@ -865,12 +909,14 @@ below/after the form element.|, 'import_url' => { message => q|Url to import data into this Thing from a csv file|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'export_url' => { message => q|Url to export the data in a search result into a csv file. This is only available after a search has been done.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'searchScreenTitle' => { @@ -886,86 +932,103 @@ search has been done.|, 'searchFields_loop' => { message => q|A loop that contains the fields that are searchable|, lastUpdated => 1104630516, + context => q|Description of a tmpl_loop for the template help.|, }, 'searchFields_label' => { message => q|The label of a searchable field.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'searchFields_fieldId' => { message => q|The fieldId of a searchable field.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'searchFields_form' => { message => q|A form element corresponding to this fields fieldType to search this field.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'searchFields_textForm' => { message => q|A text input to search this field.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'searchFields_is__fieldType__' => { message => q|A boolean indicating wether this field is of type __fieldType__. The first letter of __fieldType__ is always uppercase. Example: for a select box the value of <tmpl_var searchFields_isSelectBox> is true.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'displayInSearchFields_loop' => { message => q|A loop containing the fields that are displayed in the search results.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_loop for the template help.|, }, 'displayInSearchFields_orderByUrl' => { message => q|Url to order the search results by this field.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'displayInSearchFields_label' => { message => q|The label of this field.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'displayInSearchFields_fieldId' => { message => q|The fieldId of this field.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'searchResult_loop' => { message => q|A loop containing the search results.|, lastUpdated => 1206472126, + context => q|Description of a tmpl_loop for the template help.|, }, 'searchResult_id' => { message => q|The id of a search result.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'searchResult_view_url' => { message => q|Url to the view screen of this search result|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'searchResult_edit_icon' => { message => q|Edit icon that links to the edit screen of this search result.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'searchResult_delete_icon' => { message => q|Delete icon to delete this search result.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'searchResult_field_loop' => { message => q|A loop containing the fields that are to be displayed for this search result.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_loop for the template help.|, }, 'field_id' => { message => q|The id of this field.|, lastUpdated => 1104630516, + context => q|Description of a tmpl_var for the template help.|, }, 'no field values message' => { @@ -1011,6 +1074,7 @@ search has been done.|, 'view_url' => { message => q|The URL that will take the user back to the Thingy default view.|, lastUpdated => 1227737813, + context => q|Description of a tmpl_var for the template help.|, }, 'other thing field missing message' => { diff --git a/lib/WebGUI/i18n/English/AuthWebGUI.pm b/lib/WebGUI/i18n/English/AuthWebGUI.pm index 39ec97a98..e15756d17 100644 --- a/lib/WebGUI/i18n/English/AuthWebGUI.pm +++ b/lib/WebGUI/i18n/English/AuthWebGUI.pm @@ -65,7 +65,7 @@ our $I18N = { 'account activation template title' => { message => q|WebGUI Authentication Account Activation Mail Template|, lastUpdated => 1230600500, - context => q|The title of the help page for the webgui auth welcome message template.|, + context => q|The title of the help page for the webgui auth account activition mail template.|, }, 'account activation template' => { @@ -84,7 +84,33 @@ our $I18N = { message => q|The url to activate the newly created account.|, lastUpdated => 1230600500, }, - + + 'welcome message template title' => { + message => q|WebGUI Authentication Welcome Message Template|, + lastUpdated => 0, + context => q|The title of the help page for the webgui auth welcome message template.|, + }, + + 'welcome message template' => { + message => q|Welcome Message Template|, + lastUpdated => 0, + context => q|The label for the 'welcome message template' field on the Authentication tab of the Settings +screen.|, + }, + + 'welcome message template help' => { + message => q|Select a template for the welcome message that is sent to new users.|, + lastUpdated => 0, + context => q|The description of the 'welcome message template' field on the Authentication tab of the +Settings screen, displayed as hoverhelp.|, + }, + + 'welcomeMessage' => { + message => q|The welcome message as defined in the authentication settings.|, + lastUpdated => 0, + context => q|Description of the welcomeMessage tmpl_var for the template help.|, + }, + 'use captcha' => { message => q|Use captcha image?|, lastUpdated => 1078852836 @@ -764,6 +790,12 @@ our $I18N = { message => 'Username of registering user', }, + 'newUser_password' => { + message => q|The password for the newly created account.|, + lastUpdated => 0, + context => q|Description of the newUser_password tmpl_var for the template help.|, + }, + }; 1; 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/t/Asset/Wobject/Collaboration.t b/t/Asset/Wobject/Collaboration.t index ef7039417..cbd8c91a9 100644 --- a/t/Asset/Wobject/Collaboration.t +++ b/t/Asset/Wobject/Collaboration.t @@ -32,7 +32,7 @@ use WebGUI::Asset::Wobject::Collaboration; use WebGUI::Asset::Post; use WebGUI::Asset::Wobject::Layout; use Data::Dumper; -use Test::More tests => 4; # increment this value for each test you create +use Test::More tests => 8; # increment this value for each test you create my $session = WebGUI::Test->session; @@ -60,6 +60,38 @@ ok(defined $collab->get('groupToEditPost'), 'groupToEditPost field is defined'); # Verify sane defaults cmp_ok($collab->get('groupToEditPost'), 'eq', $collab->get('groupIdEdit'), 'groupToEditPost defaults to groupIdEdit correctly'); +is($collab->get('rssCapableRssLimit'), 10, 'rssCapableRssLimit is set to the default'); + +# finally, add the post to the collaboration system +my $props = { + className => 'WebGUI::Asset::Post::Thread', + content => 'hello, world!', +}; +my $post = $collab->addChild($props, + undef, + undef, + { + skipAutoCommitWorkflows => 1, + }); + +# Test for a sane object type +isa_ok($post, 'WebGUI::Asset::Post::Thread'); + +$props = { + className => 'WebGUI::Asset::Post::Thread', + content => 'jello, world!', +}; +$post = $collab->addChild($props, + undef, + undef, + { + skipAutoCommitWorkflows => 1, + }); + +my $rssitems = $collab->getRssItems(); +is($rssitems, 2, 'rssitems set to number of posts added'); + +is($collab->get('rssCapableRssLimit'), 10, 'rssCapableRssLimit is set to the default'); TODO: { local $TODO = "Tests to make later"; diff --git a/t/Asset/Wobject/GalleryAlbum/rss.t b/t/Asset/Wobject/GalleryAlbum/rss.t index c5433ea23..383d81711 100644 --- a/t/Asset/Wobject/GalleryAlbum/rss.t +++ b/t/Asset/Wobject/GalleryAlbum/rss.t @@ -74,7 +74,11 @@ $oldSettings{ specialState } = $session->setting->get( 'specialState' ); $session->setting->set( 'specialState', '' ); my ( $mech ); -my $baseUrl = 'http://' . $session->config->get('sitename')->[0]; +my $config_port; +if ($session->config->get('webServerPort')) { + $config_port = $session->config->get('webServerPort') || 80; +} +my $baseUrl = 'http://' . $session->config->get('sitename')->[0] . ":$config_port"; #---------------------------------------------------------------------------- # Tests diff --git a/t/Asset/Wobject/Thingy.t b/t/Asset/Wobject/Thingy.t index 9902a1c5f..ed5ad0a40 100644 --- a/t/Asset/Wobject/Thingy.t +++ b/t/Asset/Wobject/Thingy.t @@ -17,7 +17,7 @@ use lib "$FindBin::Bin/../../lib"; use WebGUI::Test; use WebGUI::Session; use WebGUI::PseudoRequest; -use Test::More tests => 16; # increment this value for each test you create +use Test::More tests => 17; # increment this value for each test you create use Test::Deep; use JSON; use WebGUI::Asset::Wobject::Thingy; @@ -192,6 +192,14 @@ my ($fieldLabel, $columnType, $Null, $Key, $Default, $Extra) = $session->db->qui is($fieldLabel,"field_".$fieldId,"A column for the new field Field_$fieldId exists."); is($columnType,"longtext","The columns is the right type"); +# Test duplicating a Thing + +my $copyThingId = $thingy->duplicateThing($thingId); + +$isValidId = $session->id->valid($copyThingId); + +is($isValidId,1,"duplicating a Thing: duplicateThing returned a valid id: ".$copyThingId); + # Test adding, editing, getting and deleting thing data my ($newThingDataId,$errors) = $thingy->editThingDataSave($thingId,'new',{"field_".$fieldId => 'test value'}); diff --git a/www/extras/wobject/Matrix/matrixSearch.js b/www/extras/wobject/Matrix/matrixSearch.js index 077beec5d..e4d36c871 100644 --- a/www/extras/wobject/Matrix/matrixSearch.js +++ b/www/extras/wobject/Matrix/matrixSearch.js @@ -55,7 +55,8 @@ YAHOO.util.Event.addListener(window, "load", function() { var attributeSelects = YAHOO.util.Dom.getElementsByClassName('attributeSelect','select'); var newUri = "func=getCompareFormData;search=1"; for (var i = attributeSelects.length; i--; ) { - if(attributeSelects[i].value != 'blank'){ + var selectedIndex = attributeSelects[i].selectedIndex; + if(attributeSelects[i].value != 'blank' && !(attributeSelects[i].value == '0' && attributeSelects[i][selectedIndex].text == 'No')){ newUri = newUri + ';search_' + attributeSelects[i].id + '=' + attributeSelects[i].value; } } diff --git a/www/extras/wobject/Survey/administersurvey.js b/www/extras/wobject/Survey/administersurvey.js index 61d707f3e..86486347a 100644 --- a/www/extras/wobject/Survey/administersurvey.js +++ b/www/extras/wobject/Survey/administersurvey.js @@ -23,6 +23,9 @@ if (typeof Survey === "undefined") { 'Dual Slider - Range': 1, 'Multi Slider - Allocate': 1 }; + var DATE_SHORT = { + 'Year Month': 1 + }; var DATE_TYPES = { 'Date': 1, 'Date Range': 1 @@ -62,6 +65,16 @@ if (typeof Survey === "undefined") { alert("Please allocate the remaining " + amountLeft + "."); } } + else if (toValidate[i].type === 'Year Month') { + answered = 1;//set to true, then let a single failure set it back to false. + for (var z1 in toValidate[i].answers) { + var m = document.getElementById(z1+'-month').value; + var y = document.getElementById(z1+'-year').value; + if(m == ''){ answered = 0; } + if(y.length != 4) { answered = 0; } + if(answered == 1){ document.getElementById(z1).value = m + "-" + y; } + } + } else { for (var z1 in toValidate[i].answers) { if (YAHOO.lang.hasOwnProperty(toValidate[i].answers, z1)) { @@ -395,7 +408,17 @@ if (typeof Survey === "undefined") { toValidate[q.id].type = q.questionType; toValidate[q.id].answers = []; } - + + if (DATE_SHORT[q.questionType]) { + for (var k = 0; k < q.answers.length; k++) { + var ans = q.answers[k]; + if (toValidate[q.id]) { + toValidate[q.id].type = q.questionType; + toValidate[q.id].answers[ans.id] = 1; + } + } + continue; + } if (DATE_TYPES[q.questionType]) { for (var k = 0; k < q.answers.length; k++) { 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;}