Merge remote-tracking branch 'origin/1.0.x' into 1.0.x

This commit is contained in:
Evan Prodromou 2011-09-08 08:59:16 -04:00
commit 5eba1030ae
5 changed files with 168 additions and 95 deletions

View File

@ -161,7 +161,9 @@ abstract class Managed_DataObject extends Memcached_DataObject
foreach ($table['foreign keys'] as $keyname => $keydef) { foreach ($table['foreign keys'] as $keyname => $keydef) {
if (count($keydef) == 2 && is_string($keydef[0]) && is_array($keydef[1]) && count($keydef[1]) == 1) { if (count($keydef) == 2 && is_string($keydef[0]) && is_array($keydef[1]) && count($keydef[1]) == 1) {
$links[$keydef[1][0]] = $keydef[0].':'.$keydef[1][1]; if (isset($keydef[1][0])) {
$links[$keydef[1][0]] = $keydef[0].':'.$keydef[1][1];
}
} }
} }
return $links; return $links;

View File

@ -1,73 +1,144 @@
$(document).ready(function() { $(document).ready(function() {
var today = new Date(); // get current time from server
var today = new Date($('now').val());
$("#event-startdate").datepicker({ $("#event-startdate").datepicker({
// Don't let the user set a crazy start date // Don't let the user set a start date < before today
minDate: today, minDate: today,
onClose: function(dateText, picker) { onClose: onStartDateSelected
// Don't let the user set a crazy end date
var newStartDate = new Date(dateText);
var endDate = new Date($("#event-startdate").val());
if (endDate < newStartDate) {
$("#event-enddate").val(dateText);
}
if (dateText !== null) {
$("#event-enddate").datepicker('option', 'minDate', new Date(dateText));
}
},
onSelect: function() {
var startd = $("#event-startdate").val();
var endd = $("#event-enddate").val();
var sdate = new Date(startd);
var edate = new Date(endd);
if (sdate !== edate) {
updateTimes();
}
}
}); });
$("#event-enddate").datepicker({ $("#event-enddate").datepicker({
minDate: today, minDate: today,
onSelect: function() { onClose: onEndDateSelected
var startd = $("#event-startdate").val(); });
var endd = $("#event-enddate").val();
var sdate = new Date(startd); $("#event-starttime").change(function(e) {
var edate = new Date(endd); var tz = $("#tz").val();
if (sdate !== edate) {
updateTimes(); var startDate = $("#event-startdate").val();
var startTime = $("#event-starttime option:selected").val().replace(/(pm|am)/, ' $1');
var startStr = startDate + ' ' + startTime + ' ' + tz;
var endDate = $("#event-enddate").val();
var endTime = $("#event-endtime option:selected").val();
var endStr = endDate + ' ' + endTime.replace(/(pm|am)/, ' $1') + ' ' + tz;
// just need to compare hours
var start = new Date(startStr);
var end = new Date(endStr);
updateTimes(startStr, (startDate === endDate), function (data) {
var times = [];
$.each(data, function(key, val) {
times.push('<option value="' + key + '">' + val + '</option>');
});
$("#event-endtime").html(times.join(''));
if (start > end) {
$("#event-endtime").val(startTime).attr("selected", "selected");
} else {
$("#event-endtime").val(endTime).attr("selected", "selected");
}
});
});
$("#event-endtime").change(function(e) {
var HOUR = 60 * 60 * 1000;
var tz = $("#tz").val();
var startDate = $("#event-startdate").val();
var endDate = $("#event-enddate").val();
var starttime = $("#event-starttime option:selected").val();
var endtime = $("#event-endtime option:selected").val();
var endtimeText = $("#event-endtime option:selected").text();
// If the end time is in the next day then update the start date
if (startDate === endDate) {
var startstr = startDate + ' ' + starttime.replace(/(pm|am)/, ' $1') + ' ' + tz;
var start = new Date(startstr);
var matches = endtimeText.match(/\(.*\)/);
var hours;
if (matches) {
hours = matches[0].substr(1).split(' ')[0]; // get x from (x hours)
if (hours) {
if (hours == 30) {
hours = .5; // special case: x == 30 from (30 mins)
}
var end = new Date(start.getTime() + (hours * HOUR));
if (end.getDate() > start.getDate()) {
$("#event-enddate").datepicker('setDate', end);
var endstr = endDate + ' 12:00 am ' + tz;
updateTimes(endstr, false, function(data) {
var times = [];
$.each(data, function(key, val) {
times.push('<option value="' + key + '">' + val + '</option>');
});
$("#event-endtime").html(times.join(''));
if (start > end) {
$("#event-endtime").val(starttime).attr("selected", "selected");
} else {
$("#event-endtime").val(endtime).attr("selected", "selected");
}
});
}
}
} }
} }
}); });
function updateTimes() { function onStartDateSelected(dateText, inst) {
var startd = $("#event-startdate").val(); var tz = $("#tz").val();
var endd = $("#event-enddate").val(); var startTime = $("#event-starttime option:selected").val();
var startDateTime = new Date(dateText + ' ' + startTime.replace(/(pm|am)/, ' $1') + ' ' + tz);
var startt = $("#event-starttime option:selected").val(); // When we update the start date and time, we need to update the end date and time
var endt = $("#event-endtime option:selected").val(); // to make sure they are equal or in the future
$("#event-enddate").datepicker('option', 'minDate', startDateTime);
var sdate = new Date(startd + " " + startt); recalculateTimes();
var edate = new Date(endd + " " + endt);
var duration = (startd === endd);
$.getJSON($('#timelist_action_url').val(),
{ start: startt, ajax: true, duration: duration },
function(data) {
var times = [];
$.each(data, function(key, val) {
times.push('<option value="' + key + '">' + val + '</option>');
});
$("#event-endtime").html(times.join(''));
if (startt < endt) {
$("#event-endtime").val(endt).attr("selected", "selected");
}
})
} }
$("#event-starttime").change(function(e) { function onEndDateSelected(dateText, inst) {
updateTimes(); recalculateTimes();
}); }
function recalculateTimes(showDuration) {
var tz = $("#tz").val();
var startDate = $("#event-startdate").val();
var startTime = $("#event-starttime option:selected").val();
var startStr = startDate + ' ' + startTime.replace(/(pm|am)/, ' $1') + ' ' + tz;
var startDateTime = new Date(startStr);
var endDate = $("#event-enddate").val();
var endTime = $("#event-endtime option:selected").val();
var endDateTime = new Date(endDate + ' ' + endTime.replace(/(pm|am)/, ' $1') + ' ' + tz);
var showDuration = true;
if (endDateTime.getDate() !== startDateTime.getDate()) {
starStr = endDate + ' 12:00 am ' + tz;
showDuration = false;
}
updateTimes(startStr, showDuration, function(data) {
var times = [];
$.each(data, function(key, val) {
times.push('<option value="' + key + '">' + val + '</option>');
});
$("#event-endtime").html(times.join(''));
if (startDateTime > endDateTime) {
$("#event-endtime").val(startTime).attr("selected", "selected");
} else {
$("#event-endtime").val(endTime).attr("selected", "selected");
}
});
}
function updateTimes(start, duration, onSuccess) {
$.getJSON($('#timelist_action_url').val(), {start: start, ajax: true, duration: duration}, onSuccess);
}
}); });

View File

@ -123,7 +123,9 @@ class EventForm extends Form
$this->li(); $this->li();
$times = EventTimeList::getTimes(); $times = EventTimeList::getTimes($today->format('m/d/Y 12:00') . ' am ' . $today->format('T'));
$start = EventTimeList::nearestHalfHour('@' . $today->getTimestamp());
$start->setTimezone(new DateTimeZone(common_timezone()));
$this->out->dropdown( $this->out->dropdown(
'event-starttime', 'event-starttime',
@ -131,11 +133,15 @@ class EventForm extends Form
_m('LABEL','Start time'), _m('LABEL','Start time'),
$times, $times,
// TRANS: Field title on event form. %s is the abbreviated timezone // TRANS: Field title on event form. %s is the abbreviated timezone
sprintf(_m("Time the event starts (%s)."), $today->format("T")), sprintf(_m("Time the event starts (%s)."), $today->format('T')),
false, false,
null $start->format('g:ia')
); );
// Need to keep JavaScript TZ in sync with PHP TZ
$this->out->hidden('tz', $today->format('T'));
$this->out->hidden('now', $today->format('F d, Y H:i:s T'));
$this->unli(); $this->unli();
$this->li(); $this->li();
@ -150,18 +156,11 @@ class EventForm extends Form
$this->li(); $this->li();
// XXX: Initial end time should be at least 30 mins out? We could do
// every 15 minute instead -z
$keys = array_keys($times);
$endStr = date('m/d/y', strtotime('now')) . " {$keys[0]}";
$end = new DateTime($endStr);
$end->modify('+30');
$this->out->dropdown( $this->out->dropdown(
'event-endtime', 'event-endtime',
// TRANS: Field label on event form. // TRANS: Field label on event form.
_m('LABEL','End time'), _m('LABEL','End time'),
EventTimeList::getTimes($end->format('c'), true), EventTimeList::getTimes('@' . $start->getTimestamp(), true),
// TRANS: Field title on event form. // TRANS: Field title on event form.
_m('Time the event ends.'), _m('Time the event ends.'),
false, false,

View File

@ -39,10 +39,10 @@ class EventTimeList {
*/ */
public static function nearestHalfHour($time) public static function nearestHalfHour($time)
{ {
$start = strtotime($time); $startd = new DateTime($time);
$minutes = date('i', $start); $minutes = $startd->format('i');
$hour = date('H', $start); $hour = $startd->format('H');
if ($minutes >= 30) { if ($minutes >= 30) {
$minutes = '00'; $minutes = '00';
@ -51,39 +51,34 @@ class EventTimeList {
$minutes = '30'; $minutes = '30';
} }
$newTimeStr = date('m/d/y', $start) . " {$hour}:{$minutes}:00"; $startd->setTime($hour, $minutes, 0);
return new DateTime($newTimeStr);
return $startd;
} }
/** /**
* Output a list of times in half-hour intervals * Output a list of times in half-hour intervals
* *
* @param string $start Time to start with (date/time string) * @param string $start Time to start with (date string, usually a ts)
* @param boolean $duration Whether to include the duration of the event * @param boolean $duration Whether to include the duration of the event
* (from the start) * (from the start)
* @return array $times (UTC time string => localized time string) * @return array $times (localized 24 hour time string => fancy time string)
*/ */
public static function getTimes($start = 'now', $duration = false) public static function getTimes($start = 'now', $duration = false)
{ {
$newTime = self::nearestHalfHour($start); $newTime = new DateTime($start);
$times = array();
$len = 0;
$newTime->setTimezone(new DateTimeZone(common_timezone())); $newTime->setTimezone(new DateTimeZone(common_timezone()));
$times = array();
$len = 0;
for ($i = 0; $i < 48; $i++) { for ($i = 0; $i < 47; $i++) {
// make sure we store the time as UTC
$newTime->setTimezone(new DateTimeZone('UTC'));
$utcTime = $newTime->format('H:i:s');
// localize time for user $localTime = $newTime->format("g:ia");
$newTime->setTimezone(new DateTimeZone(common_timezone()));
$localTime = $newTime->format('g:ia');
// pretty up the end-time option list a bit // pretty up the end-time option list a bit
if ($duration) { if ($duration) {
$len += 30; $hours = $len / 60;
$hours = $len / 60;
switch ($hours) { switch ($hours) {
case 0: case 0:
// TRANS: 0 minutes abbreviated. Used in a list. // TRANS: 0 minutes abbreviated. Used in a list.
@ -98,14 +93,18 @@ class EventTimeList {
$total = ' ' . _m('(1 hour)'); $total = ' ' . _m('(1 hour)');
break; break;
default: default:
// TRANS: Number of hours (%d). Used in a list. // TRANS: Number of hours (%.1f and %d). Used in a list.
$total = ' ' . sprintf(_m('(%d hour)','(%d hours)',$hours), $hours); $format = is_float($hours)
? _m('(%.1f hours)')
: _m('(%d hours)');
$total = ' ' . sprintf($format, $hours);
break; break;
} }
$localTime .= $total; $localTime .= $total;
$len += 30;
} }
$times[$utcTime] = $localTime; $times[$newTime->format('g:ia')] = $localTime;
$newTime->modify('+30min'); // 30 min intervals $newTime->modify('+30min'); // 30 min intervals
} }

View File

@ -101,6 +101,7 @@ class NeweventAction extends Action
$this->location = $this->trimmed('location'); $this->location = $this->trimmed('location');
$this->url = $this->trimmed('url'); $this->url = $this->trimmed('url');
$this->description = $this->trimmed('description'); $this->description = $this->trimmed('description');
$tz = $this->trimmed('tz');
$startDate = $this->trimmed('startdate'); $startDate = $this->trimmed('startdate');
@ -128,13 +129,8 @@ class NeweventAction extends Action
$endTime = '00:00'; $endTime = '00:00';
} }
$start = $startDate . ' ' . $startTime; $start = $startDate . ' ' . $startTime . ' ' . $tz;
$end = $endDate . ' ' . $endTime . ' ' . $tz;
common_debug("Event start: '$start'");
$end = $endDate . ' ' . $endTime;
common_debug("Event start: '$end'");
$this->startTime = strtotime($start); $this->startTime = strtotime($start);
$this->endTime = strtotime($end); $this->endTime = strtotime($end);
@ -194,6 +190,7 @@ class NeweventAction extends Action
function newEvent() function newEvent()
{ {
try { try {
if (empty($this->title)) { if (empty($this->title)) {
// TRANS: Client exception thrown when trying to post an event without providing a title. // TRANS: Client exception thrown when trying to post an event without providing a title.
throw new ClientException(_m('Event must have a title.')); throw new ClientException(_m('Event must have a title.'));
@ -209,6 +206,11 @@ class NeweventAction extends Action
throw new ClientException(_m('Event must have an end time.')); throw new ClientException(_m('Event must have an end time.'));
} }
if (isset($this->url) && Validate::uri($this->url) === false) {
// TRANS: Client exception thrown when trying to post an event with an invalid URL.
throw new ClientException(_m('URL must be valid.'));
}
$options = array(); $options = array();
// Does the heavy-lifting for getting "To:" information // Does the heavy-lifting for getting "To:" information