Additional features in preparation for final checkin of Calendar rewrite.

This commit is contained in:
Doug Bell 2006-12-01 22:26:09 +00:00
parent 3b4943177f
commit d805a6a310
8 changed files with 515 additions and 29 deletions

View file

@ -27,6 +27,18 @@
the user is redirected to http. (Martin Kamerbeek / Procolix)
- fix: RSS From Parent assets should always be hidden from navigation
- fix: profile field i18ned possibleValues with apostrophes failing
- Added a new DateTime subclass, WebGUI::DateTime, with convenience methods to
convert to and from MySQL Date/Time strings. Moving forward, this method
should be used in place of the existing WebGUI::Session::DateTime, which can
create problems when handling time zones.
- Form elements Date, DateTime, and TimeField now return MySQL Date/Time
strings when given a MySQL Date/Time string as a default value. This is now
the recommended method of storing date/time in the database.
- WebGUI::Search now accepts more rules, "where" for specifying an additional
where clause, "join" for making join clauses, and "columns" for adding more
columns to return.
- WebGUI::TabForm->addTab now returns the WebGUI::HTMLForm created.
- WebGUI::AssetLineage::getLineage can now limit the number of records returned
7.2.3
- fix: minor bug with new template vars in Auth::createAccount

View file

@ -299,6 +299,10 @@ A string containing extra where clause information for the query.
A string containing an order by clause (without the "order by").
=head4 limit
The maximum amount of entries to return
=cut
sub getLineage {
@ -408,6 +412,12 @@ sub getLineage {
$sortOrder = $rules->{orderByClause};
}
my $sql = "select $columns from $tables where $where group by assetData.assetId order by $sortOrder";
# Add limit if necessary
if ($rules->{limit}) {
$sql .= " limit ".$rules->{limit};
}
my @lineage;
my %relativeCache;
my $sth = $self->session->db->read($sql);

254
lib/WebGUI/DateTime.pm Executable file
View file

@ -0,0 +1,254 @@
package WebGUI::DateTime;
=head1 LEGAL
-------------------------------------------------------------------
WebGUI is Copyright 2001-2006 Plain Black Corporation.
-------------------------------------------------------------------
Please read the legal notices (docs/legal.txt) and the license
(docs/license.txt) that came with this distribution before using
this software.
-------------------------------------------------------------------
http://www.plainblack.com info@plainblack.com
-------------------------------------------------------------------
=cut
use strict;
use base 'DateTime';
=head1 NAME
WebGUI::Session::Date - DateTime subclass with additional WebGUI methods
=head1 SYNOPSIS
my $dt = $session->date->new("2006-11-06 21:12:45");
my $dt = $session->date->new(time);
my $dt = $session->date->new({ year => 2006, month => 11, day => 6 });
my $mysql = $dt->toMysql; # Make a MySQL date/time string
my $mysqlDate = $dt->toMysqlDate; # Make a MySQL date string
my $mysqlTime = $dt->toMysqlTime; # Make a MySQL time string
### See perldoc DateTime for additional methods ###
=head1 DESCRIPTION
This module is intended as a drop-in replacement for Perl's DateTime module,
with additional methods for translating to and from MySQL Date/Time field
strings.
NOTE: This module replaces WebGUI::Session::DateTime, which has a problem
dealing with time zones.
=head1 METHODS
=cut
#######################################################################
=head2 new ( string )
Creates a new object from a MySQL Date/Time string with the UTC time zone.
=head2 new ( integer )
Creates a new object from an epoch time.
=head2 new ( "mysql" => string, "time_zone" => string)
Creates a new object from a MySQL Date/Time string with the specified time zone
=head2 new ( hash )
Creates a new object from a hash of data passed directly to DateTime.
=cut
sub new
{
# Drop-in replacement for Perl's DateTime.pm
my $class = shift;
my $self;
#use Data::Dumper;
#warn "Args to DateTime->new: ".Dumper \@_;
if (@_ > 1 && grep /^mysql$/, @_)
{
my %hash = @_;
$hash{time_zone} ||= "UTC";
my $string = delete $hash{mysql};
my %mysql = _splitMysql($string);
$hash{$_} = $mysql{$_}
for keys %mysql;
$self = $class->SUPER::new(%hash);
}
elsif (@_ > 1)
{
$self = $class->SUPER::new(@_);
}
elsif ($_[0] =~ /^\d+$/)
{
$self = DateTime->from_epoch(epoch=>$_[0], time_zone=>"UTC");
}
else
{
$self = $class->SUPER::new(
(_splitMysql($_[0])),
time_zone => "UTC",
);
}
# If no DateTime object created yet, I don't know how
unless ($self)
{
return;
}
return bless $self, $class;
}
#######################################################################
=head2 toIcal
Returns a Date/Time string in the UTC time zone in the iCalendar format.
20061124T120000Z
=cut
sub toIcal
{
my $self = shift;
if ($self->time_zone->is_utc)
{
return $self->strftime('%Y%m%dT%H%M%SZ');
}
else
{
return $self->clone->set_time_zone("UTC")->strftime('%Y%m%dT%H%M%SZ');
}
}
#######################################################################
=head2 toIcalDate
Returns only the date portion in the format suitable for iCal. Does not adjust
time zone.
=cut
sub toIcalDate
{
return $_[0]->strftime('%Y%m%d');
}
#######################################################################
=head2 toMysql
Returns a MySQL Date/Time string.
=cut
sub toMysql
{
return $_[0]->strftime("%Y-%m-%d %H:%M:%S");
}
#######################################################################
=head2 toMysqlDate
Returns a MySQL Date string. Any time data stored by this object will be
ignored.
=cut
sub toMysqlDate
{
return $_[0]->strftime("%Y-%m-%d");
}
#######################################################################
=head2 toMysqlTime
Returns a MySQL Time string. Any date data stored by this object will be
ignored.
=cut
sub toMysqlTime
{
return $_[0]->strftime("%H:%M:%S");
}
#######################################################################
=head2 _splitMysql ( string )
Class method that splits a MySQL Date/Time string into a hash to be passed into
DateTime
=cut
sub _splitMysql
{
my $string = shift;
my ($y,$m,$d,$h,$n,$s) = split /\D+/,$string;
my %hash = (
year => $y,
month => $m,
day => $d,
hour => $h,
minute => $n,
second => $s,
);
return %hash;
}
=head1 SEE ALSO
=over 8
=item *
perldoc DateTime
=back
=cut
1;

View file

@ -17,7 +17,6 @@ package WebGUI::Form::Date;
use strict;
use base 'WebGUI::Form::Text';
use WebGUI::Form::Hidden;
use WebGUI::Form::Text;
use WebGUI::International;
=head1 NAME
@ -28,6 +27,10 @@ Package WebGUI::Form::Date
Accepts and returns and epoch date and creates a date picker control.
If the current or default value is a MySQL date string, accepts and returns
a MySQL date string. Note: Cannot do time zone conversion since it is not a
complete date/time string.
=head1 SEE ALSO
This is a subclass of WebGUI::Form::Text.
@ -64,6 +67,9 @@ A default date is placed in the value field. Set this to "1" to leave it empty.
If no value is specified, this will be used. Defaults to today and now.
If the defaultValue is a MySQL date string, this form control will return MySQL
date strings instead of epoch times.
=head4 profileEnabled
Flag that tells the User Profile system that this is a valid form element in a User Profile
@ -108,20 +114,38 @@ Return the date in a human readable format for the Profile system.
sub displayValue {
my ($self) = @_;
return $self->session->datetime->epochToHuman($self->get("value"),"%z");
if (!$self->get("defaultValue") || $self->get("defaultValue") =~ m/^\d+$/) {
return $self->session->datetime->epochToHuman($self->get("value"),"%z");
} else {
# MySQL format
my $value = $self->get("value");
}
}
#-------------------------------------------------------------------
=head2 getValueFromPost ( )
Returns a validated form post result. If the result does not pass validation, it returns undef instead.
Returns a validated form post result. If the result does not pass validation,
it returns undef instead.
=cut
sub getValueFromPost {
my $self = shift;
return $self->session->datetime->setToEpoch($self->session->form->param($self->get("name")));
if (!$self->get("defaultValue") || $self->get("defaultValue") =~ m/^\d+$/) {
# Epoch format
return $self->session->datetime->setToEpoch($self->session->form->param($self->get("name")));
} else {
# MySQL format
# YY(YY)?-MM-DD
my $value = $self->session->form->param($self->get("name"));
# NOTE: Cannot fix time zone since we don't have a complete date/time
return $1
if ($value =~ m/((?:\d{2}|\d{4})\D\d{2}\D\d{2})/);
}
}
#-------------------------------------------------------------------
@ -134,11 +158,17 @@ Renders a date picker control.
sub toHtml {
my $self = shift;
my $value;
if ($self->get("_defaulted") && $self->get("noDate") ) {
$self->set("value",'');
}
else {
$self->set("value",$self->session->datetime->epochToSet($self->get("value")));
# No default date
$value = $self->set("value",'');
} elsif (!$self->get("defaultValue") || $self->get("defaultValue") =~ m/^\d+$/) {
# Epoch format
$value = $self->set("value",$self->session->datetime->epochToSet($self->get("value")));
} else {
# MySQL format
$value = $self->get("value");
# NOTE: Cannot fix time zone since we don't have a complete date/time
}
my $language = WebGUI::International->new($self->session)->getLanguage($self->session->user->profileField("language"),"languageAbbreviation");
@ -150,7 +180,14 @@ sub toHtml {
$self->session->style->setScript($self->session->url->extras('calendar/calendar-setup.js'),{ type=>'text/javascript' });
$self->session->style->setLink($self->session->url->extras('calendar/calendar-win2k-1.css'), { rel=>"stylesheet", type=>"text/css", media=>"all" });
my $mondayFirst = $self->session->user->profileField("firstDayOfWeek") ? "1" : "0";
return $self->SUPER::toHtml. '<script type="text/javascript">
return WebGUI::Form::Text->new($self->session,
name=>$self->get("name"),
value=>$value,
size=>$self->get("size"),
extras=>$self->get("extras"),
id=>$self->get('id'),
maxlength=>$self->get("maxlength")
)->toHtml . '<script type="text/javascript">
Calendar.setup({
inputField : "'.$self->get('id').'",
ifFormat : "%Y-%m-%d",
@ -172,9 +209,19 @@ Renders the form field to HTML as a hidden field rather than whatever field type
sub toHtmlAsHidden {
my $self = shift;
my $value;
if (!$self->get("defaultValue") || $self->get("defaultValue") =~ m/^\d+$/) {
$value = $self->session->datetime->epochToSet($self->get("value"),"%z");
} else {
# MySQL format
$value = $self->get("value");
# NOTE: Cannot fix time zone since we don't have a complete date/time
}
return WebGUI::Form::Hidden->new($self->session,
name=>$self->get("name"),
value=>$self->session->datetime->epochToSet($self->get("value"))
name => $self->get("name"),
value => $value,
)->toHtmlAsHidden;
}

View file

@ -18,6 +18,7 @@ use strict;
use base 'WebGUI::Form::Text';
use WebGUI::Form::Hidden;
use WebGUI::International;
use WebGUI::DateTime;
=head1 NAME
@ -25,7 +26,13 @@ Package WebGUI::Form::DateTime
=head1 DESCRIPTION
Accepts and returns and epoch date and creates a date picker control.
Accepts and returns an epoch date and creates a date picker control.
If the default value is a MySQL date/time string, accepts and returns MySQL
date/time strings.
NOTE: Does not adjust for the user's time zone unless using MySQL date/time
strings.
=head1 SEE ALSO
@ -63,6 +70,9 @@ Flag that tells the User Profile system that this is a valid form element in a U
If no value is specified, this will be used. Defaults to today and now.
If the defaultValue is a MySQL date/time string, this form control will return
MySQL date/time strings adjusted for the user's time zone.
=cut
sub definition {
@ -100,7 +110,25 @@ Returns a validated form post result. If the result does not pass validation, it
sub getValueFromPost {
my $self = shift;
return $self->session->datetime->setToEpoch($self->session->form->param($self->get("name")));
if (!$self->get("defaultValue") || $self->get("defaultValue") =~ m/^\d+$/) {
# Epoch format
return $self->session->datetime->setToEpoch($self->session->form->param($self->get("name")),1);
} else {
# MySQL format
# YY(YY)?-MM-DD HH:MM:SS
my $value = $self->session->form->param($self->get("name"));
$self->session->errorHandler->warn("Date value: $value");
# Verify format
return undef
unless ($value =~ m/(?:\d{2}|\d{4})\D\d{2}\D\d{2}\D\d{2}\D\d{2}\D\d{2}/);
# Fix time zone
$value = WebGUI::DateTime->new(mysql => $value, time_zone => $self->session->user->profileField("timeZone"))
->set_time_zone("UTC")->toMysql;
return $value;
}
}
#-------------------------------------------------------------------
@ -113,7 +141,18 @@ Renders a date picker control.
sub toHtml {
my $self = shift;
my $value = $self->session->datetime->epochToSet($self->get("value"),1);
my $value;
if (!$self->get("defaultValue") || $self->get("defaultValue") =~ m/^\d+$/) {
# Epoch format
$value = $self->session->datetime->epochToSet($self->get("value"),1);
} else {
# MySQL format
$value = $self->get("value");
# Fix time zone
$value = WebGUI::DateTime->new($value)
->set_time_zone($self->session->user->profileField("timeZone"))
->toMysql;
}
my $i18n = WebGUI::International->new($self->session);
my $language = $i18n->getLanguage($self->session->user->profileField("language"),"languageAbbreviation");
unless ($language) {
@ -153,9 +192,22 @@ Renders the form field to HTML as a hidden field rather than whatever field type
sub toHtmlAsHidden {
my $self = shift;
my $value;
if (!$self->get("defaultValue") || $self->get("defaultValue") =~ m/^\d+$/) {
$value = $self->session->datetime->epochToSet($self->get("value"),1);
} else {
# MySQL format
$value = $self->get("value");
# Fix Time zone
$value = WebGUI::DateTime->new($value)
->set_time_zone($self->session->user->profileField("timeZone"))
->toMysql;
}
return WebGUI::Form::Hidden->new(
name=>$self->get("name"),
value=>$self->session->datetime->epochToSet($self->get("value"),1)
name => $self->get("name"),
value => $value,
)->toHtmlAsHidden;
}

View file

@ -26,7 +26,13 @@ Package WebGUI::Form::TimeField
=head1 DESCRIPTION
Creates a time form field.
Creates a time form field.
If the default value is a MySQL time, the value returned by this form element
will be a MySQL time. Note: Will not be adjusted for the user's time zone.
Otherwise, the value returned by this form element will be a number of seconds,
adjusted for the user's time zone..
=head1 SEE ALSO
@ -48,6 +54,10 @@ See the superclass for additional details.
The following additional parameters have been added via this sub class.
=head4 defaultValue
Either a number of seconds or a MySQL time.
=head4 maxlength
Defaults to 8. Determines the maximum number of characters allowed in this field.
@ -88,13 +98,25 @@ sub definition {
=head2 getValueFromPost ( )
Returns the number of seconds since 00:00:00 on a 24 hour clock. Note, this will adjust for the user's time offset in the reverse manner that the form field adjusts for it in order to make the times come out appropriately.
If the defaultValue is a MySQL time, the value returned by this form element
will be a MySQL time. Note: Will not be adjusted for the user's time zone.
Otherwise, the value returned by this form element will be a number of seconds,
adjusted for the user's time zone..
=cut
sub getValueFromPost {
my $self = shift;
return $self->session->datetime->timeToSeconds($self->session->form->param($self->get("name")))-($self->session->user->profileField("timeOffset")*3600);
if (!$self->get("defaultValue") || $self->get("defaultValue") =~ /^\d+$/) {
# epoch format
return $self->session->datetime->timeToSeconds($self->session->form->param($self->get("name")))-($self->session->user->profileField("timeOffset")*3600);
} else {
# Mysql format
my $value = $self->session->form->param($self->get("name"));
return undef unless $value =~ /^\d{2}\D\d{2}(\D\d{2})?$/;
return $value;
}
}
#-------------------------------------------------------------------
@ -107,7 +129,14 @@ Renders a time field.
sub toHtml {
my $self = shift;
my $value = $self->session->datetime->secondsToTime($self->get("value"));
my $value;
if ($self->get("value") =~ /^\d+$/) {
# Epoch format
$value = $self->session->datetime->secondsToTime($self->get("value"));
} else {
# MySQL format
$value = $self->get("value");
}
my $i18n = WebGUI::International->new($self->session);
$self->session->style->setScript($self->session->url->extras('inputCheck.js'),{ type=>'text/javascript' });
$self->set("extras", $self->get('extras') . ' onkeyup="doInputCheck(document.getElementById(\''.$self->get("id").'\'),\'0123456789:\')"');
@ -129,9 +158,17 @@ Renders the field as a hidden field.
sub toHtmlAsHidden {
my $self = shift;
my $value;
if ($self->get("value") =~ /^\d+$/) {
# Epoch format
$value = $self->session->datetime->secondsToTime($self->get("value"));
} else {
# MySQL format
$value = $self->get("value");
}
return WebGUI::Form::Hidden->new($self->session,
name=>$self->get("name"),
value=>$self->session->datetime->secondsToTime($self->get("value"))
value=>$value
)->toHtmlAsHidden;
}

View file

@ -46,7 +46,11 @@ This is a private method and should never be used outside of this class.
sub _getQuery {
my $self = shift;
my $selectsRef = shift;
return ('select ' . join(', ', @$selectsRef, ($self->{_score} ? $self->{_score} : ())) . ' from assetIndex where '
return ('select '
. join(', ', @$selectsRef, ($self->{_score} ? $self->{_score} : ()))
. ' from assetIndex '
. ($self->{_join} ? join(" ",@{$self->{_join}}) : '') # JOIN
. ' where '
. ($self->{_isPublic}? 'isPublic = 1 and ' : '')
. '('.$self->{_where}.')'
. ($self->{_score} ? ' order by score desc' : '')
@ -64,7 +68,7 @@ Returns an array reference containing all the asset ids of the assets that match
sub getAssetIds {
my $self = shift;
my $query = $self->_getQuery(['assetId']);
my $query = $self->_getQuery(['assetIndex.assetId']);
my $rs = $self->session->db->prepare($query);
$rs->execute($self->{_params});
my @ids = ();
@ -85,7 +89,7 @@ Returns an array reference containing asset objects for those that matched.
sub getAssets {
my $self = shift;
my $query = $self->_getQuery([qw(assetId className revisionDate)]);
my $query = $self->_getQuery([qw(assetIndex.assetId assetIndex.className assetIndex.revisionDate)]);
my $rs = $self->session->db->prepare($query);
$rs->execute($self->{_params});
my @assets = ();
@ -131,7 +135,22 @@ sub getPaginatorResultSet {
my $paginate = shift;
my $pageNumber = shift;
my $formVar = shift;
my $query = $self->_getQuery([qw(assetId title url synopsis ownerUserId groupIdView groupIdEdit creationDate revisionDate className)]);
my @columns = qw( assetIndex.assetId
assetIndex.title
assetIndex.url
assetIndex.synopsis
assetIndex.ownerUserId
assetIndex.groupIdView
assetIndex.groupIdEdit
assetIndex.creationDate
assetIndex.revisionDate
assetIndex.className
);
push @columns, (@{$self->{_columns}})
if $self->{_columns};
my $query = $self->_getQuery(\@columns);
my $paginator = WebGUI::Paginator->new($self->session, $url, $paginate, $pageNumber, $formVar);
$paginator->setDataByQuery($query, undef, undef, $self->{_params});
return $paginator;
@ -141,13 +160,31 @@ sub getPaginatorResultSet {
=head2 getResultSet ( )
Returns a WebGUI::SQL::ResultSet object containing the search results with columns labeled "assetId", "title", "url", "synopsis", "ownerUserId", "groupIdView", "groupIdEdit", "creationDate", "revisionDate", and "className".
Returns a WebGUI::SQL::ResultSet object containing the search results with
columns labeled "assetId", "title", "url", "synopsis", "ownerUserId",
"groupIdView", "groupIdEdit", "creationDate", "revisionDate", and "className",
in addition to any columns passed as rules.
=cut
sub getResultSet {
my $self = shift;
my $query = $self->_getQuery([qw(assetId title url synopsis ownerUserId groupIdView groupIdEdit creationDate revisionDate className)]);
my $self = shift;
my @columns = qw( assetIndex.assetId
assetIndex.title
assetIndex.url
assetIndex.synopsis
assetIndex.ownerUserId
assetIndex.groupIdView
assetIndex.groupIdEdit
assetIndex.creationDate
assetIndex.revisionDate
assetIndex.className
);
push @columns, (@{$self->{_columns}})
if $self->{_columns};
my $query = $self->_getQuery(\@columns);
my $rs = $self->session->db->prepare($query);
$rs->execute($self->{_params});
return $rs;
@ -255,6 +292,28 @@ This rule limits the search to a revision date range. It has two parameters: "st
end=>30300003
}
=head4 where
This rule adds an additional where clause to the search.
where => 'className NOT LIKE "WebGUI::Asset::Wobject%"'
=head4 join
This rule allows for an array reference of table join clauses.
join => 'join assetData on assetId = assetData.assetId'
=head4 columns
This rule allows for additional columns to be returned by getResultSet().
columns => ['assetData.title','assetData.description']
TODO: 'where' and 'join' were added hackishly. It'd be nicer to see a data
structure for 'join', and the ability to have multiple 'where' clauses with
placeholders and parameters.
=cut
sub search {
@ -321,6 +380,20 @@ sub search {
push(@clauses, "revisionDate between ? and ?");
push(@params, $start, $end);
}
if ($rules->{where}) {
push(@clauses, $rules->{where});
}
if ($rules->{join}) { # This join happens in _getQuery
$rules->{join} = [$rules->{join}]
unless (ref $rules->{join} eq "ARRAY");
$self->{_join} = $rules->{join};
}
if ($rules->{columns}) {
$rules->{columns} = [$rules->{columns}]
unless (ref $rules->{columns} eq "ARRAY");
$self->{_columns} = $rules->{columns};
}
$self->{_params} = \@params;
$self->{_where} = "(".join(") and (", @clauses).")";
return $self;

View file

@ -75,7 +75,7 @@ These methods are available from this class:
=head2 addTab ( name, label, uiLevel )
Adds a new tab to the tab form.
Adds a new tab to the tab form. Returns the WebGUI::HTMLForm object created.
=head3 name
@ -99,6 +99,7 @@ sub addTab {
$self->{_tab}{$name}{form} = WebGUI::HTMLForm->new($self->session,uiLevelOverride=>$self->{_uiLevelOverride});
$self->{_tab}{$name}{label} = $label;
$self->{_tab}{$name}{uiLevel} = $uiLevel;
return $self->{_tab}{$name}{form};
}
#-------------------------------------------------------------------