Index: openacs-4/packages/acs-templating/tcl/date-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/tcl/date-procs.tcl,v
diff -u -r1.35 -r1.36
--- openacs-4/packages/acs-templating/tcl/date-procs.tcl 12 Mar 2005 21:39:59 -0000 1.35
+++ openacs-4/packages/acs-templating/tcl/date-procs.tcl 4 Jun 2006 00:45:48 -0000 1.36
@@ -13,8 +13,10 @@
namespace eval template {}
namespace eval template::data {}
+namespace eval template::data::validate {}
namespace eval template::util {}
namespace eval template::util::date {}
+namespace eval template::util::textdate {}
namespace eval template::widget {}
namespace eval template::data::transform {}
@@ -289,15 +291,17 @@
}
set pad "00"
}
-
- # DRB: We need to differentiate between date and timestamp, for PG, at least,
- # and since Oracle supports to_timestamp() we'll just do it for both DBs.
- if { [llength $date] <= 3 } {
+ # DRB: We need to differentiate between date and timestamp, for PG, at least,
+ # and since Oracle supports to_timestamp() we'll just do it for both DBs.
+ # DEDS: revert this first as to_timestamp is only for
+ # oracle9i. no clear announcement that openacs has dropped
+ # support for 8i
+ if { [llength $date] <= 3 || ([string equal [db_type] "oracle"] && [string match "8.*" [db_version]]) } {
return "to_date('$value', '$format')"
- } else {
+ } else {
return "to_timestamp('$value', '$format')"
}
- }
+ }
ansi {
# LARS: Empty date results in NULL value
if { [empty_string_p $date] } {
@@ -710,73 +714,72 @@
}
variable fragment_formats
-
upvar $error_ref error_msg
unpack $date
- set error_msg ""
- set return_code 1
+ set error_msg [list]
foreach {field exp} { year "YYYY|YY" month "MM|MON|MONTH" day "DD"
hours "HH24|HH12" minutes "MI" seconds "SS" } {
# If the field is required, but missing, report an error
if { [string equal [set $field] {}] } {
- if { [regexp $exp $format match] } {
- append error_msg "No value supplied for $field "
- set return_code 0
- }
+ if { [regexp $exp $format match] } {
+ set field_pretty [_ acs-templating.${field}]
+ lappend error_msg [_ acs-templating.lt_No_value_supplied_for_-field_pretty-]
+ }
} else {
- # fields should only be integers
- if { ![regexp {^[0-9]+$} [set $field] match] } {
- append error_msg "The $field must be a non-negative integer "
- set return_code 0
- set $field {}
- }
+ # fields should only be integers
+ if { ![regexp {^[0-9]+$} [set $field] match] } {
+ set field_pretty [_ acs-templating.${field}]
+ lappend error_msg [_ acs-templating.lt_The_-field_pretty-_must_be_non_negative]
+ set $field {}
+ }
}
}
if { [template::util::negative $year] } {
- append error_msg "Year must be positive "
- set return_code 0
+ lappend error_msg [_ acs-templating.Year_must_be_positive]
}
if { ![string equal $month {}] } {
if { $month < 1 || $month > 12 } {
- append error_msg "Month must be between 1 and 12 "
- set return_code 0
+ lappend error_msg [_ acs-templating.Month_must_be_between_1_and_12]
} else {
if { $year > 0 } {
if { ![string equal $day {}] } {
set maxdays [get_property days_in_month $date]
if { $day < 1 || $day > $maxdays } {
- append error_msg "The day must be between 1 and $maxdays for "
- append error_msg "the month of
- [get_property long_month_name $date] "
- set return_code 0
+ set month_pretty [template::util::date::get_property long_month_name $date]
+ if { $month == "2" } {
+ # February has a different number of days depending on the year
+ append month_pretty " ${year}"
+ }
+ lappend error_msg [_ acs-templating.lt_day_between_for_month_pretty]
}
}
}
}
}
if { [template::util::negative $hours] || $hours > 23 } {
- append error_msg "Hours must be between 0 and 23 "
- set return_code 0
+ lappend error_msg [_ acs-templating.Hours_must_be_between_0_and_23]
}
if { [template::util::negative $minutes] || $minutes > 59 } {
- append error_msg "Minutes must be between 0 and 59 "
- set return_code 0
+ lappend error_msg [_ acs-templating.Minutes_must_be_between_0_and_59]
}
if { [template::util::negative $seconds] || $seconds > 59 } {
- append error_msg "Seconds must be between 0 and 59 "
- set return_code 0
+ lappend error_msg [_ acs-templating.Seconds_must_be_between_0_and_59]
}
-
- return $return_code
+ if { [llength $error_msg] > 0 } {
+ set error_msg "[join $error_msg { }]"
+ return 0
+ } else {
+ return 1
+ }
}
@@ -1028,6 +1031,10 @@
set tokens [list]
+ if {[info exists attributes(id)]} {
+ set id_attr_name $attributes(id)
+ }
+
while { ![string equal $format_string {}] } {
# Snip off the next token
@@ -1045,6 +1052,10 @@
set fragment_def $template::util::date::fragment_widgets([string toupper $token])
set fragment [lindex $fragment_def 1]
+ if {[exists_and_not_null id_attr_name]} {
+ set attributes(id) "${id_attr_name}.${fragment}"
+ }
+
append output [template::widget::[lindex $fragment_def 0] \
element \
$fragment \
@@ -1125,3 +1136,212 @@
}
}
+ad_proc -public template::util::textdate { command args } {
+ Dispatch procedure for the textdate object
+} {
+ eval template::util::textdate::$command $args
+}
+
+ad_proc -public template::util::textdate_localized_format {} {
+ Gets the localized format for the textdate widget
+} {
+ # we get the date format for the connected locale from acs-lang.localization-d_fmt
+ # as of the time of writing this proc the following were by default available that
+ # would work with this proc, and this should cover most installations, if this
+ # format isn't matched we will use the iso standard YYYY-MM-DD.
+ #
+ # %d-%m-%y %d.%m.%y %d/%m-%y %d/%m/%y %m/%d/%y %y-%m-%d %y.%m.%d "%d-%m-%y"
+
+ set format [lc_get "d_fmt"]
+ regsub -all -nocase {\"} $format {} format
+ regsub -all -nocase {\%} $format {} format
+ set format [string tolower $format]
+ # this format key must now be at max five characters, and contain one y, one m and one d
+ # as well as two punction marks ( - . / )
+ if { [regexp {^([y|m|d])([\-|\.|/])([y|m|d])([\-|\.|/])([y|m|d])} $format match first first_punct second second_punct third] && [string length $format] eq "5" } {
+ if { [lsort [list $first $second $third]] eq "d m y" } {
+ # we have a valid format from acs-lang.localization-d_fmt with all 3 necessary elements
+ # and only two valid punctuation marks
+ regsub {d} $format {dd} format
+ regsub {m} $format {mm} format
+ regsub {y} $format {yyyy} format
+ return $format
+ }
+ }
+
+ # we use the iso standard
+ return "yyyy-mm-dd"
+}
+
+ad_proc -public template::util::textdate::create {
+ {textdate {}}
+} {
+ return $textdate
+}
+
+ad_proc -public template::data::transform::textdate { element_ref } {
+ Collect a textdate from the form, it automatically
+ reformats it from the users locale to the iso standard
+ YYYY-MM-DD this is useful because it doesn't need
+ reformatting in tcl code
+} {
+
+ upvar $element_ref element
+ set element_id $element(id)
+ set value [ns_queryget "$element_id"]
+
+ if { $value eq "" } {
+ # they didn't enter anything
+ return ""
+ }
+
+ # we get the format they need to use
+ set format [template::util::textdate_localized_format]
+ set exp $format
+ regsub -all {(\-|\.|/)} $exp {(\1)} exp
+ regsub -all {dd|mm} $exp {([0-9]{1,2})} exp
+ regsub -all {yyyy} $exp {([0-9]{2,4})} exp
+
+ # results is what comes out in a regexp
+ set results $format
+ regsub {\-|\.|/} $results { format_one} results
+ regsub {\-|\.|/} $results { format_two} results
+ regsub {mm} $results { month} results
+ regsub {dd} $results { day} results
+ regsub {yyyy} $results { year} results
+ set results [string trim $results]
+
+ if { [regexp {([\-|\.|/])yyyy$} $format match year_punctuation] } {
+ # we might be willing to accept this date if it doesn't have a year
+ # at the end, since we can assume that the year is the current one
+ # this is useful for fast keyboard based date entry for formats that
+ # have years at the end (such as in en_US which is mm/dd/yyyy or
+ # de_DE which is dd.mm.yyyy)
+
+ # we check if adding the year and punctuation makes it a valid date
+ set command "regexp {$exp} \"\${value}\${year_punctuation}\[dt_sysdate -format %Y\]\" match $results"
+ if { [eval $command] } {
+ if { ![catch { clock scan "${year}-${month}-${day}" }] } {
+ # we add the missing year and punctuation to the value
+ # we don't return it here because formatting is done
+ # later on (i.e. adding leading zeros if needed)
+ append value "${year_punctuation}[dt_sysdate -format %Y]"
+ }
+ }
+ }
+
+ # now we verify that we have a valid date
+ # and adding leading/trailing zeros if needed
+ set command "regexp {$exp} \"\${value}\" match $results"
+ if { [eval $command] } {
+ # the regexp will have given us: year month day format_one format_two
+ if { [string length $month] eq "1" } {
+ set month "0$month"
+ }
+ if { [string length $day] eq "1" } {
+ set day "0$day"
+ }
+ if { [string length $year] eq "2" } {
+ # we'll copy microsoft excel's default assumptions
+ # about the year it is so if the year is 29 or
+ # lower its in this century otherwise its last century
+ if { $year < 30 } {
+ set year "20$year"
+ } else {
+ set year "19$year"
+ }
+ }
+ return "${year}-${month}-${day}"
+ } else {
+ # they did not provide a correctly formatted date so we send it back to them
+ return $value
+ }
+}
+
+ad_proc -public template::data::validate::textdate { value_ref message_ref } {
+
+ upvar 2 $message_ref message $value_ref textdate
+ set error_msg [list]
+ if { [exists_and_not_null textdate] } {
+ if { [regexp {^[0-9]{4}-[0-9]{2}-[0-9]{2}$} $textdate match] } {
+ if { [catch { clock scan "${textdate}" }] } {
+ # the textdate is formatted properly the template::data::transform::textdate proc
+ # will only return correctly formatted dates in iso format, but the date is not
+ # valid so they have entered some info incorrectly
+ set datelist [split $textdate "-"]
+ set year [lindex $datelist 0]
+ set month [::string trimleft [lindex $datelist 1] 0]
+ set day [::string trimleft [lindex $datelist 2] 0]
+ if { $month < 1 || $month > 12 } {
+ lappend error_msg [_ acs-templating.Month_must_be_between_1_and_12]
+ } else {
+ set maxdays [template::util::date::get_property days_in_month $datelist]
+ if { $day < 1 || $day > $maxdays } {
+ set month_pretty [template::util::date::get_property long_month_name $datelist]
+ if { $month == "2" } {
+ # February has a different number of days depending on the year
+ append month_pretty " ${year}"
+ }
+ lappend error_msg [_ acs-templating.lt_day_between_for_month_pretty]
+ }
+ }
+ }
+ } else {
+ # the textdate is not formatted properly
+ set format [::string toupper [template::util::textdate_localized_format]]
+ lappend error_msg [_ acs-templating.lt_Dates_must_be_formatted_]
+ }
+ }
+ if { [llength $error_msg] > 0 } {
+ set message "[join $error_msg { }]"
+ return 0
+ } else {
+ return 1
+ }
+}
+
+ad_proc -public template::widget::textdate { element_reference tag_attributes } {
+ Implements the textdate widget.
+
+} {
+
+ upvar $element_reference element
+
+ set date_valid_p 0
+ if { [info exists element(value)] } {
+ set textdate $element(value)
+ if { [regexp {^([0-9]{4})-([0-9]{2})-([0-9]{2})$} $textdate match year month day] } {
+ set date_valid_p [string is false [catch { clock scan "${textdate}" }]]
+ # we have a correctly formatted iso date that we
+ # can reformat for display, we don't use lc_time_fmt
+ # because it could fail and cause a server error.
+ # The date may be formatted correctly but it may be
+ # an invalid date (which is caught by
+ # template::data::validate::textdate) so we need to
+ # re-format the input into the format the user specified
+ # by this means
+ set textdate [template::util::textdate_localized_format]
+ regsub {yyyy} $textdate $year textdate
+ regsub {mm} $textdate $month textdate
+ regsub {dd} $textdate $day textdate
+ }
+ } else {
+ set textdate ""
+ }
+
+ if { $date_valid_p } {
+ set javascriptdate $textdate
+ } else {
+ set javascriptdate ""
+ }
+
+ if { [string equal $element(mode) "edit"] } {
+ append output ""
+ append output ""
+ } else {
+ append output $textdate
+ append output ""
+ }
+
+ return $output
+}