Improving agenda UI, fixing event edition see BT#7803

Adding date-range-picker element in FormValidator.
Updating datetimepicker js library
Adding sabre-vobject ical library.
1.9.x
Julio Montoya 11 years ago
parent 7b1a107e65
commit 4c6799fe57
  1. 3
      composer.json
  2. 88
      main/admin/calendar.lib.php
  3. 15
      main/announcements/announcements.inc.php
  4. 1023
      main/calendar/agenda.inc.php
  5. 1576
      main/calendar/agenda.lib.php
  6. 454
      main/calendar/agenda.php
  7. 95
      main/calendar/agenda_js.php
  8. 22
      main/calendar/agenda_list.php
  9. 33
      main/inc/ajax/agenda.ajax.php
  10. 62
      main/inc/lib/formvalidator/Element/DatePicker.php
  11. 138
      main/inc/lib/formvalidator/Element/DateRangePicker.php
  12. 5
      main/inc/lib/formvalidator/Element/DateTimePicker.php
  13. 19
      main/inc/lib/formvalidator/FormValidator.class.php
  14. 23
      main/inc/lib/groupmanager.lib.php
  15. 256
      main/inc/lib/javascript/daterange/daterangepicker-bs2.css
  16. 267
      main/inc/lib/javascript/daterange/daterangepicker-bs3.css
  17. 1010
      main/inc/lib/javascript/daterange/daterangepicker.js
  18. 9
      main/inc/lib/javascript/daterange/moment.min.js
  19. 5
      main/inc/lib/javascript/datetimepicker/jquery-ui-timepicker-addon.css
  20. 4330
      main/inc/lib/javascript/datetimepicker/jquery-ui-timepicker-addon.js
  21. 7
      main/inc/lib/main_api.lib.php
  22. 22
      main/inc/lib/pear/HTML/QuickForm.php
  23. 42
      main/inc/lib/pear/HTML/QuickForm/select.php
  24. 6
      main/template/default/agenda/event_list.tpl
  25. 124
      main/template/default/agenda/month.tpl
  26. 22
      main/work/work.lib.php
  27. 9
      plugin/bbb/listing.php
  28. 9
      plugin/openmeetings/listing.php
  29. 14
      tests/main/admin/calendar.lib.test.php
  30. 2
      vendor/autoload.php
  31. 36
      vendor/composer/ClassLoader.php
  32. 2
      vendor/composer/autoload_namespaces.php
  33. 11
      vendor/composer/autoload_real.php
  34. 388
      vendor/composer/installed.json
  35. 17
      vendor/monolog/monolog/CHANGELOG.mdown
  36. 13
      vendor/monolog/monolog/README.mdown
  37. 17
      vendor/monolog/monolog/composer.json
  38. 2
      vendor/monolog/monolog/doc/extending.md
  39. 2
      vendor/monolog/monolog/doc/usage.md
  40. 67
      vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php
  41. 53
      vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php
  42. 1
      vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php
  43. 4
      vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php
  44. 4
      vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php
  45. 18
      vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php
  46. 2
      vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php
  47. 123
      vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php
  48. 86
      vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php
  49. 26
      vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php
  50. 5
      vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php
  51. 2
      vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php
  52. 3
      vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php
  53. 15
      vendor/monolog/monolog/src/Monolog/Logger.php
  54. 16
      vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php
  55. 2
      vendor/symfony/process/Symfony/Component/Process/LICENSE
  56. 219
      vendor/symfony/process/Symfony/Component/Process/Process.php
  57. 23
      vendor/symfony/process/Symfony/Component/Process/ProcessUtils.php
  58. 147
      vendor/symfony/process/Symfony/Component/Process/Tests/AbstractProcessTest.php
  59. 2
      vendor/symfony/process/Symfony/Component/Process/Tests/NonStopableProcess.php
  60. 6
      vendor/symfony/process/Symfony/Component/Process/Tests/ProcessBuilderTest.php
  61. 6
      vendor/symfony/process/Symfony/Component/Process/Tests/ProcessUtilsTest.php
  62. 36
      vendor/symfony/process/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php
  63. 17
      vendor/symfony/process/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php
  64. 74
      vendor/symfony/process/Symfony/Component/Process/Tests/SimpleProcessTest.php

@ -1,5 +1,6 @@
{
"require": {
"php-ffmpeg/php-ffmpeg": "0.3.x-dev@dev"
"php-ffmpeg/php-ffmpeg": "0.3.x-dev@dev",
"sabre/vobject" : "~3.1"
}
}

@ -1108,7 +1108,6 @@ function show_user_filter_form()
/** @todo this select missing to implement */
//$user_list=get_course_users();
echo "<select name=\"select\" onchange=\"MM_jumpMenu('parent',this,0)\">";
echo "<option value=\"agenda.php?user=none\">show all users</option>";
/*foreach($user_list as $this_user)
@ -1141,7 +1140,6 @@ function show_user_group_filter_form()
// Users
echo "<optgroup label=\"".get_lang("Users")."\">";
//$user_list=get_course_users();
/* foreach($user_list as $this_user)
{
// echo "<option value=\"agenda.php?isStudentView=true&amp;user=".$this_user['uid']."\">".$this_user['lastName']." ".$this_user['firstName']."</option>";
@ -1546,92 +1544,6 @@ function show_add_form($id = '') {
<?php
}
function get_agendaitems($month, $year)
{
global $_user;
global $_configuration;
$month=Database::escape_string($month);
$year=Database::escape_string($year);
$items = array ();
//databases of the courses
$TABLEAGENDA = Database :: get_main_table(TABLE_MAIN_SYSTEM_CALENDAR);
//$TABLE_ITEMPROPERTY = Database :: get_course_table(TABLE_ITEM_PROPERTY);
//$group_memberships = GroupManager :: get_group_ids(Database::get_current_course_database(), $_user['user_id']);
// if the user is administrator of that course we show all the agenda items
//if (api_is_allowed_to_edit())
//{
//echo "course admin";
$sqlquery = "SELECT
DISTINCT *
FROM ".$TABLEAGENDA."
WHERE
MONTH(start_date)='".$month."'
AND YEAR(start_date)='".$year."'
GROUP BY id
ORDER BY start_date ";
//}
// if the user is not an administrator of that course
/* else
{
//echo "GEEN course admin";
if (is_array($group_memberships) && count($group_memberships)>0)
{
$sqlquery = "SELECT
agenda.*
FROM ".$TABLEAGENDA."
WHERE
MONTH(agenda.start_date)='".$month."'
AND YEAR(agenda.start_date)='".$year."'
ORDER BY start_date ";
}
else
{
$sqlquery = "SELECT
agenda.*, item_property.*
FROM ".$TABLEAGENDA."
WHERE
MONTH(agenda.start_date)='".$month."'
AND YEAR(agenda.start_date)='".$year."'
ORDER BY start_date ";
}
}*/
//$mycourse = api_get_course_info();
$portal_url = $_configuration['root_web'];
if ($_configuration['multiple_access_urls']) {
$access_url_id = api_get_current_access_url_id();
if ($access_url_id != -1 ){
$url = api_get_access_url($access_url_id);
$portal_url = $url['url'];
}
}
$result = Database::query($sqlquery);
while ($item = Database::fetch_array($result))
{
$agendaday_string = api_convert_and_format_date($item['start_date'], "%d", date_default_timezone_get());
$agendaday = intval($agendaday_string);
$time = api_convert_and_format_date($item['start_date'], TIME_NO_SEC_FORMAT, date_default_timezone_get());
$URL = $portal_url.'main/admin/agenda.php?day='.$agendaday."&amp;month=".$month."&amp;year=".$year; // RH //Patrick Cool: to highlight the relevant agenda item
$items[$agendaday][$item['start_time']] .= '<i>'.$time.'</i> <a href="'.$URL.'" title="'.$item['title'].'<br />';
}
// sorting by hour for every day
$agendaitems = array ();
while (list ($agendaday, $tmpitems) = each($items))
{
sort($tmpitems);
while (list ($key, $val) = each($tmpitems))
{
$agendaitems[$agendaday] .= $val;
}
}
return $agendaitems;
}
function display_upcoming_events() {
echo '<br /><b>'.get_lang('UpcomingEvent').'</b><br />';
$number_of_items_to_show = (int)api_get_setting('number_of_upcoming_events');

@ -294,8 +294,14 @@ class AnnouncementManager
* @param string Comment describing the attachment
* @return int false on failure, ID of the announcement on success
*/
public static function add_announcement($emailTitle, $newContent, $sent_to, $file = array(), $file_comment = null, $end_date = null)
{
public static function add_announcement(
$emailTitle,
$newContent,
$sentTo,
$file = array(),
$file_comment = null,
$end_date = null
) {
$_course = api_get_course_info();
$tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
@ -333,11 +339,12 @@ class AnnouncementManager
}
// store in item_property (first the groups, then the users
if (empty($sent_to) || !empty($sent_to) && isset($sent_to[0]) && $sent_to[0] == 'everyone') {
if (empty($sentTo) || !empty($sentTo) && isset($sentTo[0]) && $sentTo[0] == 'everyone') {
// The message is sent to EVERYONE, so we set the group to 0
api_item_property_update($_course, TOOL_ANNOUNCEMENT, $last_id, "AnnouncementAdded", api_get_user_id(), '0');
} else {
$send_to = self::separate_users_groups($sent_to);
$send_to = self::separate_users_groups($sentTo);
// Storing the selected groups
if (is_array($send_to['groups'])) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -16,25 +16,23 @@ $language_file = array('agenda', 'group');
$use_anonymous = true;
require_once '../inc/global.inc.php';
$current_course_tool = TOOL_CALENDAR_EVENT;
$course_info = api_get_course_info();
if (!empty($course_info)) {
api_protect_course_script(true);
}
//session
if (isset($_GET['id_session'])) {
$_SESSION['id_session'] = intval($_GET['id_session']);
}
$action = isset($_GET['action']) ? $_GET['action'] : null;
$origin = isset($_GET['origin']) ? $_GET['origin'] : null;
$this_section = SECTION_COURSES;
if (empty($action)) {
$url = api_get_path(WEB_CODE_PATH).'calendar/agenda_js.php?type=course';
if (!empty($course_info)) {
$url = api_get_path(WEB_CODE_PATH).'calendar/agenda_js.php?type=course'.'&'.api_get_cidreq();
} else {
$url = api_get_path(WEB_CODE_PATH).'calendar/agenda_js.php?';
}
header("Location: $url");
exit;
}
@ -43,185 +41,50 @@ if (empty($action)) {
$_SESSION['source_type'] = 'Agenda';
require_once '../resourcelinker/resourcelinker.inc.php';
require_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php';
if (!empty($addresources)) {
// When the "Add Resource" button is clicked we store all the form data into a session
$form_elements = array(
'day' => Security::remove_XSS($_POST['fday']),
'month' => Security::remove_XSS($_POST['fmonth']),
'year' => Security::remove_XSS($_POST['fyear']),
'hour' => Security::remove_XSS($_POST['fhour']),
'minutes' => Security::remove_XSS($_POST['fminute']),
'end_day' => Security::remove_XSS($_POST['end_fday']),
'end_month' => Security::remove_XSS($_POST['end_fmonth']),
'end_year' => Security::remove_XSS($_POST['end_fyear']),
'end_hours' => Security::remove_XSS($_POST['end_fhour']),
'end_minutes' => Security::remove_XSS($_POST['end_fminute']),
'title' => Security::remove_XSS(stripslashes($_POST['title'])),
'content' => Security::remove_XSS(stripslashes($_POST['content'])),
'id' => Security::remove_XSS($_POST['id']),
'action' => Security::remove_XSS($_POST['action']),
'add_announcement' => Security::remove_XSS($_POST['add_announcement']),
'to' => Security::remove_XSS($_POST['selectedform'])
);
$_SESSION['formelements'] = $form_elements;
// this is to correctly handle edits
if ($id) {
$action = "edit";
}
//print_r($form_elements);
header('Location: '.api_get_path(WEB_CODE_PATH)."resourcelinker/resourcelinker.php?source_id=1&action=$action&id=$id&originalresource=no");
exit;
}
if (!empty($_GET['view'])) {
$_SESSION['view'] = Security::remove_XSS($_GET['view']);
}
// Functions for the agenda tool
require_once 'agenda.inc.php';
/*
TREATING THE PARAMETERS
1. viewing month only or everything
2. sort ascending or descending
3. showing or hiding the send-to-specific-groups-or-users form
4. filter user or group
*/
// 3. showing or hiding the send-to-specific-groups-or-users form
$setting_allow_individual_calendar = true;
if (empty($_POST['To']) and empty($_SESSION['allow_individual_calendar'])) {
$_SESSION['allow_individual_calendar'] = "hide";
}
$allow_individual_calendar_status = $_SESSION['allow_individual_calendar'];
if (!empty($_POST['To']) and ($allow_individual_calendar_status == "hide")) {
$_SESSION['allow_individual_calendar'] = "show";
}
if (!empty($_GET['sort']) and ($allow_individual_calendar_status == "show")) {
$_SESSION['allow_individual_calendar'] = "hide";
}
// 4. filter user or group
if (!empty($_GET['user']) or !empty($_GET['group'])) {
$_SESSION['user'] = (int) $_GET['user'];
$_SESSION['group'] = (int) $_GET['group'];
}
if ((!empty($_GET['user']) and $_GET['user'] == "none") or (!empty($_GET['group']) and $_GET['group'] == "none")) {
Session::erase("user");
Session::erase("group");
}
$group_id = api_get_group_id();
$eventId = isset($_REQUEST['id']) ? $_REQUEST['id'] : null;
$type = $event_type = isset($_GET['type']) ? $_GET['type'] : null;
//It comes from the group tools. If it's define it overwrites $_SESSION['group']
$htmlHeadXtra[] = to_javascript();
$htmlHeadXtra[] = user_group_filter_javascript();
// this loads the javascript that is needed for the date popup selection
$htmlHeadXtra[] = "<script src=\"tbl_change.js\" type=\"text/javascript\" language=\"javascript\"></script>";
$htmlHeadXtra[] = "<script>
$(function() {
var checked = $('input[name=repeat]').attr('checked');
if (checked) {
$('#options2').show();
}
});
</script>
";
// setting the name of the tool
$nameTools = get_lang('Agenda'); // language variable in trad4all.inc.php
// showing the header if we are not in the learning path, if we are in
// the learning path, we do not include the banner so we have to explicitly
// include the stylesheet, which is normally done in the header
if (!empty($group_id)) {
$group_properties = GroupManager :: get_group_properties($group_id);
$interbreadcrumb[] = array("url" => "../group/group.php", "name" => get_lang('Groups'));
$interbreadcrumb[] = array("url" => "../group/group_space.php?gidReq=".$group_id, "name" => get_lang('GroupSpace').' '.$group_properties['name']);
Display::display_header($nameTools, 'Agenda');
} elseif (empty($origin) or $origin != 'learnpath') {
Display::display_header($nameTools, 'Agenda');
} else {
echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"".api_get_path(WEB_CODE_PATH)."css/default.css\"/>";
}
$nameTools = get_lang('Agenda');
/*
TRACKING
*/
event_access_tool(TOOL_CALENDAR_EVENT);
/* SETTING SOME VARIABLES
*/
// Variable definitions
// Defining the shorts for the days. We use camelcase because these are arrays of language variables
$DaysShort = api_get_week_days_short();
// Defining the days of the week to allow translation of the days. We use camelcase because these are arrays of language variables
$DaysLong = api_get_week_days_long();
// Defining the months of the year to allow translation of the months. We use camelcase because these are arrays of language variables
$MonthsLong = api_get_months_long();
// Database table definitions
$TABLEAGENDA = Database::get_course_table(TABLE_AGENDA);
$TABLE_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
$tbl_user = Database::get_main_table(TABLE_MAIN_USER);
$tbl_courseUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
$tbl_group = Database::get_course_table(TABLE_GROUP);
$tbl_groupUser = Database::get_course_table(TABLE_GROUP_USER);
$tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
/* ACCESS RIGHTS */
// permission stuff - also used by loading from global in agenda.inc.php
$is_allowed_to_edit = api_is_allowed_to_edit(false, true) OR (api_get_course_setting('allow_user_edit_agenda') && !api_is_anonymous());
// Tool introduction
Display::display_introduction_section(TOOL_CALENDAR_EVENT);
/* MAIN SECTION */
//setting the default year and month
$select_year = '';
$select_month = '';
$select_day = '';
if (!empty($_GET['year'])) {
$select_year = (int) $_GET['year'];
}
if (!empty($_GET['month'])) {
$select_month = (int) $_GET['month'];
}
if (!empty($_GET['day'])) {
$select_day = (int) $_GET['day'];
}
$today = getdate();
if (empty($select_year)) {
$select_year = $today['year'];
}
if (empty($select_month)) {
$select_month = $today['mon'];
}
echo '<div class="actions">';
if (api_is_allowed_to_edit(false, true) OR
(api_get_course_setting('allow_user_edit_agenda') && !api_is_anonymous()) && api_is_allowed_to_session_edit(false, true) OR
GroupManager::user_has_access(api_get_user_id(), $group_id, GroupManager::GROUP_TOOL_CALENDAR) && GroupManager::is_tutor_of_group(api_get_user_id(), $group_id)
) {
echo display_courseadmin_links();
}
echo '</div>';
$event_id = isset($_REQUEST['id']) ? $_REQUEST['id'] : null;
$type = $event_type = isset($_GET['type']) ? $_GET['type'] : null;
$agenda = new Agenda();
$agenda->type = $type;
$actions = $agenda->displayActions('calendar');
if ($type == 'fromjs') {
$id_list = explode('_', $event_id);
$event_id = $id_list[1];
$id_list = explode('_', $eventId);
$eventId = $id_list[1];
$event_type = $id_list[0];
}
if (!api_is_allowed_to_edit(null, true) && $event_type == 'course') {
api_not_allowed();
api_not_allowed(true);
}
if ($event_type == 'course') {
$agendaUrl = api_get_path(WEB_CODE_PATH).'calendar/agenda_js.php?'.api_get_cidreq().'&type=course';
} else {
$agendaUrl = api_get_path(WEB_CODE_PATH).'calendar/agenda_js.php?&type='.$event_type;
}
$course_info = api_get_course_info();
$agenda = new Agenda();
$agenda->type = $event_type;
$message = null;
$content = null;
if (api_is_allowed_to_edit(false, true) OR
(api_get_course_setting('allow_user_edit_agenda') &&
!api_is_anonymous() &&
@ -231,107 +94,234 @@ if (api_is_allowed_to_edit(false, true) OR
) {
switch ($action) {
case 'add':
if (isset($_POST['submit_event']) && $_POST['submit_event']) {
$event_start = (int) $_POST['fyear'].'-'.(int) $_POST['fmonth'].'-'.(int) $_POST['fday'].' '.(int) $_POST['fhour'].':'.(int) $_POST['fminute'].':00';
$event_stop = (int) $_POST['end_fyear'].'-'.(int) $_POST['end_fmonth'].'-'.(int) $_POST['end_fday'].' '.(int) $_POST['end_fhour'].':'.(int) $_POST['end_fminute'].':00';
$safe_title = Security::remove_XSS($_POST['title']);
$safe_file_comment = isset($_POST['file_comment']) ? Security::remove_XSS($_POST['file_comment']) : null;
if (isset($_POST['empty_end_date']) && $_POST['empty_end_date'] == 'on') {
$event_stop = '0000-00-00 00:00:00';
}
$agenda->type = 'course';
$sendEmail = isset($_POST['add_announcement']) ? true : false;
$eventId = $agenda->add_event($event_start, $event_stop, false, null, $safe_title, $_POST['content'], $_POST['selected_form'], $sendEmail);
if (!empty($_POST['repeat']) && !empty($eventId)) {
$end_y = intval($_POST['repeat_end_year']);
$end_m = intval($_POST['repeat_end_month']);
$end_d = intval($_POST['repeat_end_day']);
$end = mktime(23, 59, 59, $end_m, $end_d, $end_y);
$agenda->addRepeatedItem($eventId, $_POST['repeat_type'], $end, $_POST['selected_form']);
$form = $agenda->getForm(array('action' => 'add'));
if ($form->validate()) {
$values = $form->getSubmitValues();
$sendEmail = isset($values['add_announcement']) ? true : false;
$allDay = isset($values['all_day']) ? 'true' : 'false';
$sendAttachment = isset($_FILES['user_upload']) ? true : false;
$attachment = $sendAttachment ? $_FILES['user_upload'] : null;
$attachmentComment = isset($values['file_comment']) ? $values['file_comment'] : null;
$startDate = $values['date_range_start'];
$endDate = $values['date_range_end'];
$eventId = $agenda->add_event(
$startDate,
$endDate,
$allDay,
$values['title'],
$values['content'],
$values['users_to_send'],
$sendEmail,
null,
$attachment,
$attachmentComment
);
if (!empty($values['repeat']) && !empty($eventId)) {
// End date is always set as 23:59:59
$endDate = substr($values['repeat_end_day'], 0, 10).' 23:59:59';
$agenda->addRepeatedItem(
$eventId,
$values['repeat_type'],
$endDate,
$values['users_to_send']
);
}
Display::display_confirmation_message(get_lang('AddSuccess'));
$message = Display::return_message(get_lang('AddSuccess'), 'confirmation');
if ($sendEmail) {
Display::display_confirmation_message(get_lang('AdditionalMailWasSentToSelectedUsers'));
$message .= Display::return_message(get_lang('AdditionalMailWasSentToSelectedUsers'), 'confirmation');
}
Session::write('message', $message);
header("Location: $agendaUrl");
exit;
} else {
$content = $form->return_form();
}
break;
case 'edit':
$event = $agenda->get_event($eventId);
if (empty($event)) {
api_not_allowed(true);
}
$event['action'] = 'edit';
$event['id'] = $eventId;
$form = $agenda->getForm($event);
if ($form->validate()) {
$values = $form->getSubmitValues();
$allDay = isset($values['all_day']) ? 'true' : 'false';
$startDate = $values['date_range_start'];
$endDate = $values['date_range_end'];
$repeatType = isset($values['edit_repeat_type']) ? $values['edit_repeat_type'] : null;
$sendAttachment = isset($_FILES['user_upload']) ? true : false;
$attachment = $sendAttachment ? $_FILES['user_upload'] : null;
$attachmentComment = isset($values['file_comment']) ? $values['file_comment'] : null;
// This is a sub event. Delete the current and create another BT#7803
if (!empty($event['parent_event_id'])) {
$agenda->delete_event($eventId);
$eventId = $agenda->add_event(
$startDate,
$endDate,
$allDay,
$values['title'],
$values['content'],
$values['users_to_send'],
false,
null,
$attachment,
$attachmentComment
);
$message = Display::return_message(get_lang('Updated'), 'confirmation');
Session::write('message', $message);
header("Location: $agendaUrl");
exit;
}
// Editing normal event.
$agenda->edit_event(
$eventId,
$startDate,
$endDate,
$allDay,
$values['title'],
$values['content'],
$values['users_to_send'],
$repeatType,
$attachment,
$attachmentComment
);
if (!empty($values['repeat']) && !empty($eventId)) {
// End date is always set as 23:59:59
$endDate = substr($values['repeat_end_day'], 0, 10).' 23:59:59';
$agenda->addRepeatedItem(
$eventId,
$values['repeat_type'],
$endDate,
$values['users_to_send']
);
}
$deleteAttachment = isset($values['delete_attachment']) ? true : false;
if ($deleteAttachment && isset($event['attachment']) && !empty($event['attachment'])) {
$agenda->deleteAttachmentFile(
$event['attachment']['id'],
$agenda->course
);
}
if (!empty($values['repeat']) && !empty($eventId)) {
$agenda->addRepeatedItem(
$eventId,
$values['repeat_type'],
$values['repeat_end_day'],
$values['users_to_send']
);
}
$message = Display::return_message(get_lang('Updated'), 'confirmation');
Session::write('message', $message);
header("Location: $agendaUrl");
exit;
} else {
show_add_form();
$content = $form->return_form();
}
break;
case "announce":
//copying the agenda item into an announcement
if (!(api_is_course_coach() && !api_is_element_in_the_session(TOOL_AGENDA, $event_id))) {
/*if (!(api_is_course_coach() && !api_is_element_in_the_session(TOOL_AGENDA, $eventId))) {
// a coach can only delete an element belonging to his session
$ann_id = store_agenda_item_as_announcement($event_id);
$ann_id = store_agenda_item_as_announcement($eventId);
$tool_group_link = (isset($_SESSION['toolgroup']) ? '&toolgroup='.$_SESSION['toolgroup'] : '');
Display::display_normal_message(get_lang('CopiedAsAnnouncement').'&nbsp;<a href="../announcements/announcements.php?id='.$ann_id.$tool_group_link.'">'.get_lang('NewAnnouncement').'</a>', false);
}
$message = Display::return_message(
get_lang('CopiedAsAnnouncement').'&nbsp;<a href='.api_get_path(WEB_CODE_PATH).'announcements/announcements.php?id='.$ann_id.$tool_group_link.'">'.
get_lang('NewAnnouncement').'</a>',
'normal',
false
);
Session::write('message', $message);
}*/
break;
case 'importical':
if (isset($_POST['ical_submit'])) {
$form = $agenda->getImportCalendarForm();
$content = $form->return_form();
if ($form->validate()) {
$ical_name = $_FILES['ical_import']['name'];
$ical_type = $_FILES['ical_import']['type'];
$ext = substr($ical_name, (strrpos($ical_name, ".") + 1));
//$ical_type === 'text/calendar'
if ($ext === 'ics' || $ext === 'ical' || $ext === 'icalendar' || $ext === 'ifb') {
$agenda_result = agenda_import_ical($course_info, $_FILES['ical_import']);
$result = $agenda->importEventFile($course_info, $_FILES['ical_import']);
$is_ical = true;
} else {
$is_ical = false;
}
if (!$is_ical) {
Display::display_error_message(get_lang('IsNotiCalFormatFile'));
display_ical_import_form();
$message = Display::return_message(get_lang('IsNotiCalFormatFile'), 'error');
$form = $agenda->getImportCalendarForm();
$content = $form->return_form();
break;
} else {
Display::display_confirmation_message(get_lang('AddSuccess'));
echo $agenda_result;
$message = Display::return_message(get_lang('AddSuccess'), 'error');
$content = $result;
}
} else {
display_ical_import_form();
}
break;
case 'edit':
// a coach can only delete an element belonging to his session
if ($_POST['submit_event']) {
store_edited_agenda_item($event_id, $_REQUEST['id_attach'], $_REQUEST['file_comment']);
$action = 'view';
} else {
show_add_form($event_id, $event_type);
Session::write('message', $message);
}
break;
case "delete":
if (!(api_is_course_coach() && !api_is_element_in_the_session(TOOL_AGENDA, $event_id) )) {
if (!(api_is_course_coach() && !api_is_element_in_the_session(TOOL_AGENDA, $eventId) )) {
// a coach can only delete an element belonging to his session
delete_agenda_item($event_id);
$action = 'view';
$content = $agenda->delete_event($eventId);
}
break;
case "showhide":
if (!(api_is_course_coach() && !api_is_element_in_the_session(TOOL_AGENDA, $event_id))) {
/*if (!(api_is_course_coach() && !api_is_element_in_the_session(TOOL_AGENDA, $eventId))) {
// a coach can only delete an element belonging to his session
showhide_agenda_item($event_id);
$action = 'view';
}
if (!empty($_GET['agenda_id'])) {
display_one_agenda_item($_GET['agenda_id']);
}
break;
case "delete_attach": //delete attachment file
$id_attach = $_GET['id_attach'];
if (!empty($id_attach)) {
delete_attachment_file($id_attach);
$content = showhide_agenda_item($eventId);
$action = 'view';
}
}*/
break;
}
}
// The footer is displayed only if we are not in the learnpath
if ($origin != 'learnpath') {
Display::display_footer();
if (!empty($group_id)) {
$group_properties = GroupManager :: get_group_properties($group_id);
$interbreadcrumb[] = array(
"url" => api_get_path(WEB_CODE_PATH)."group/group.php",
"name" => get_lang('Groups')
);
$interbreadcrumb[] = array(
"url" => api_get_path(WEB_CODE_PATH)."group/group_space.php?gidReq=".$group_id,
"name" => get_lang('GroupSpace').' '.$group_properties['name']
);
}
// Tool introduction
$introduction = Display::return_introduction_section(TOOL_CALENDAR_EVENT);
$message = Session::read('message');
Session::erase('message');
$tpl = new Template(get_lang('Agenda'));
$tpl->assign('content', $content);
$tpl->assign('actions', $actions);
// Loading main Chamilo 1 col template
$tpl->display_one_col_template();

@ -6,6 +6,8 @@
/**
* INIT SECTION
*/
use \ChamiloSession as Session;
// name of the language file that needs to be included
$language_file = array('agenda', 'group', 'announcements');
@ -13,7 +15,7 @@ $language_file = array('agenda', 'group', 'announcements');
$use_anonymous = true;
// Calendar type
$type = isset($_REQUEST['type']) && in_array($_REQUEST['type'], array('personal', 'course', 'admin')) ? $_REQUEST['type'] : 'personal';
$type = isset($_REQUEST['type']) && in_array($_REQUEST['type'], array('personal', 'course', 'admin', 'platform')) ? $_REQUEST['type'] : 'personal';
if ($type == 'personal') {
$cidReset = true; // fixes #5162
@ -24,7 +26,6 @@ require_once 'agenda.lib.php';
require_once 'agenda.inc.php';
$current_course_tool = TOOL_CALENDAR_EVENT;
$this_section = SECTION_MYAGENDA;
$htmlHeadXtra[] = api_get_jquery_libraries_js(array('jquery-ui', 'jquery-ui-i18n'));
@ -34,7 +35,7 @@ $htmlHeadXtra[] = api_get_js('fullcalendar/gcal.js');
$htmlHeadXtra[] = api_get_css(api_get_path(WEB_LIBRARY_PATH).'javascript/fullcalendar/fullcalendar.css');
$htmlHeadXtra[] = api_get_css(api_get_path(WEB_LIBRARY_PATH).'javascript/qtip2/jquery.qtip.min.css');
if (api_is_platform_admin() && $type == 'admin') {
if (api_is_platform_admin() && ($type == 'admin' || $type == 'platform')) {
$type = 'admin';
}
@ -42,19 +43,27 @@ if (isset($_REQUEST['cidReq']) && !empty($_REQUEST['cidReq'])) {
$type = 'course';
}
$agenda = new Agenda();
$agenda->type = $type;
$is_group_tutor = false;
$session_id = api_get_session_id();
$group_id = api_get_group_id();
if (!empty($group_id)) {
$is_group_tutor = GroupManager::is_tutor_of_group(api_get_user_id(), $group_id);
$group_properties = GroupManager :: get_group_properties($group_id);
$interbreadcrumb[] = array("url" => "../group/group.php", "name" => get_lang('Groups'));
$interbreadcrumb[] = array("url" => "../group/group_space.php?gidReq=".$group_id, "name" => get_lang('GroupSpace').' '.$group_properties['name']);
$group_properties = GroupManager::get_group_properties($group_id);
$interbreadcrumb[] = array(
"url" => api_get_path(WEB_CODE_PATH)."group/group.php?".api_get_cidreq(),
"name" => get_lang('Groups')
);
$interbreadcrumb[] = array(
"url" => api_get_path(WEB_CODE_PATH)."group/group_space.php?".api_get_cidreq(),
"name" => get_lang('GroupSpace').' '.$group_properties['name']
);
}
$tpl = new Template(get_lang('Agenda'));
$tpl->assign('use_google_calendar', 0);
$can_add_events = 0;
@ -68,7 +77,7 @@ switch ($type) {
}
break;
case 'course':
api_protect_course_script();
api_protect_course_script(true);
$this_section = SECTION_COURSES;
if (api_is_allowed_to_edit()) {
$can_add_events = 1;
@ -126,31 +135,17 @@ if ($region_value == 'en') {
}
$tpl->assign('region_value', $region_value);
$export_icon = '../img/export.png';
$export_icon_low = '../img/export_low_fade.png';
$export_icon_high = '../img/export_high_fade.png';
$export_icon = api_get_path(WEB_IMG_PATH).'img/export.png';
$export_icon_low = api_get_path(WEB_IMG_PATH).'img/export_low_fade.png';
$export_icon_high = api_get_path(WEB_IMG_PATH).'img/export_high_fade.png';
$tpl->assign('export_ical_confidential_icon', Display::return_icon($export_icon_high, get_lang('ExportiCalConfidential')));
$tpl->assign(
'export_ical_confidential_icon',
Display::return_icon($export_icon_high, get_lang('ExportiCalConfidential'))
);
$actions = null;
$filter = null;
if (api_is_allowed_to_edit(false, true) OR
(api_get_course_setting('allow_user_edit_agenda') && !api_is_anonymous()) &&
api_is_allowed_to_session_edit(false, true) OR
$is_group_tutor
) {
if ($type == 'course') {
if (isset($_GET['user_id'])) {
$filter = $_GET['user_id'];
}
$actions = display_courseadmin_links($filter);
}
} else {
if ($type == 'course') {
$actions = "<a href='agenda_list.php?type=course&".api_get_cidreq()."'>".Display::return_icon('week.png', get_lang('Agenda'), '', ICON_SIZE_MEDIUM)."</a>";
}
}
$actions = $agenda->displayActions('calendar', $filter);
$tpl->assign('actions', $actions);
@ -170,7 +165,7 @@ if (empty($defaultView)) {
$defaultView = 'month';
}
/* month, basicWeek,agendaWeek,agendaDay */
/* month, basicWeek, agendaWeek, agendaDay */
$tpl->assign('default_view', $defaultView);
@ -182,7 +177,7 @@ if ($type == 'course' && !empty($session_id)) {
$tpl->assign('type_label', $type_label);
$tpl->assign('type_event_class', $type_event_class);
//Current user can add event?
// Current user can add event?
$tpl->assign('can_add_events', $can_add_events);
//Setting AJAX caller
@ -209,15 +204,43 @@ if ((api_is_allowed_to_edit() || $is_group_tutor) && $course_code != '-1' && $ty
$group_list = CourseManager::get_group_list_of_course(api_get_course_id(), api_get_session_id());
}
$agenda = new Agenda();
//This will fill the select called #users_to_send_id
$select = $agenda->construct_not_selected_select_form($group_list, $user_list, array());
// This will fill the select called #users_to_send_id.
if (isset($_REQUEST['user_id'])) {
if (in_array($_REQUEST['user_id'], array_keys($user_list))) {
$userInfo = api_get_user_info($_REQUEST['user_id']);
if (!empty($userInfo)) {
$user_list = array($userInfo['user_id'] => $userInfo);
$group_list = array();
}
}
$param = explode(':', $_REQUEST['user_id']);
if (isset($param[1]) && in_array($param[1], array_keys($group_list))) {
$groupInfo = GroupManager::get_group_properties($param[1]);
if ($groupInfo) {
$group_list = array($groupInfo['id'] => $groupInfo);
$user_list = array();
}
}
}
$select = $agenda->construct_not_selected_select_form(
$group_list,
$user_list,
array()
);
$tpl->assign('visible_to', $select);
}
// Loading Agenda template
// Loading Agenda template.
$content = $tpl->fetch('default/agenda/month.tpl');
$message = Session::read('message');
$tpl->assign('message', $message);
Session::erase('message');
$tpl->assign('content', $content);
// Loading main Chamilo 1 col template

@ -13,19 +13,29 @@ require_once '../inc/global.inc.php';
require_once 'agenda.lib.php';
require_once 'agenda.inc.php';
$interbreadcrumb[] = array("url" => api_get_path(WEB_CODE_PATH)."calendar/agenda_js.php?".api_get_cidreq(), "name" => get_lang('Agenda'));
$interbreadcrumb[] = array(
'url' => api_get_path(WEB_CODE_PATH)."calendar/agenda_js.php?".api_get_cidreq(),
'name' => get_lang('Agenda')
);
$tpl = new Template(get_lang('Events'));
$agenda = new Agenda();
$agenda->type = 'course'; //course,admin or personal
$events = $agenda->get_events(null, null, api_get_course_int_id(), api_get_group_id(), null, 'array');
$type = isset($_REQUEST['type']) ? $_REQUEST['type'] : null;
$agenda->type = $type;
$events = $agenda->get_events(
null,
null,
api_get_course_int_id(),
api_get_group_id(),
null,
'array'
);
$url = api_get_path(WEB_CODE_PATH).'calendar/agenda_list.php?'.api_get_cidreq();
$tpl->assign('url', $url);
$tpl->assign('agenda_events', $events);
$actions = '<a href="'.api_get_path(WEB_CODE_PATH).'calendar/agenda_js.php?type=course&'.api_get_cidreq().'">'.
Display::return_icon('calendar.png', get_lang('Agenda'), '', ICON_SIZE_MEDIUM)."</a>";
$actions = $agenda->displayActions('list');
$tpl->assign('actions', $actions);
$tpl->assign('is_allowed_to_edit', api_is_allowed_to_edit());

@ -3,6 +3,7 @@
/**
* Responses to AJAX calls
*/
$type = isset($_REQUEST['type']) && in_array($_REQUEST['type'], array('personal', 'course', 'admin')) ? $_REQUEST['type'] : 'personal';
if ($type == 'personal') {
@ -27,7 +28,7 @@ $group_id = api_get_group_id();
$is_group_tutor = GroupManager::is_tutor_of_group(api_get_user_id(), $group_id);
$agenda = new Agenda();
$agenda->type = $type; //course,admin or personal
$agenda->type = $type;
switch ($action) {
case 'add_event':
@ -35,7 +36,15 @@ switch ($action) {
break;
}
$add_as_announcement = isset($_REQUEST['add_as_annonuncement']) ? $_REQUEST['add_as_annonuncement'] : null;
echo $agenda->add_event($_REQUEST['start'], $_REQUEST['end'], $_REQUEST['all_day'], $_REQUEST['view'], $_REQUEST['title'], $_REQUEST['content'], $_REQUEST['users_to_send'], $add_as_announcement);
echo $agenda->add_event(
$_REQUEST['start'],
$_REQUEST['end'],
$_REQUEST['all_day'],
$_REQUEST['title'],
$_REQUEST['content'],
$_REQUEST['users_to_send'],
$add_as_announcement
);
break;
case 'edit_event':
if (!api_is_allowed_to_edit(null, true) && $type == 'course') {
@ -43,7 +52,14 @@ switch ($action) {
}
$id_list = explode('_', $_REQUEST['id']);
$id = $id_list[1];
$agenda->edit_event($id, $_REQUEST['start'], $_REQUEST['end'], $_REQUEST['all_day'], $_REQUEST['view'], $_REQUEST['title'], $_REQUEST['content']);
$agenda->edit_event(
$id,
$_REQUEST['start'],
$_REQUEST['end'],
$_REQUEST['all_day'],
$_REQUEST['title'],
$_REQUEST['content']
);
break;
case 'delete_event':
if (!api_is_allowed_to_edit(null, true) && $type == 'course') {
@ -51,7 +67,8 @@ switch ($action) {
}
$id_list = explode('_', $_REQUEST['id']);
$id = $id_list[1];
$agenda->delete_event($id);
$deleteAllEventsFromSerie = isset($_REQUEST['delete_all_events']) ? true : false;
$agenda->delete_event($id, $deleteAllEventsFromSerie);
break;
case 'resize_event':
if (!api_is_allowed_to_edit(null, true) && $type == 'course') {
@ -82,7 +99,13 @@ switch ($action) {
$start = isset($_REQUEST['start']) ? $_REQUEST['start'] : null;
$end = isset($_REQUEST['end']) ? $_REQUEST['end'] : null;
$events = $agenda->get_events($start, $end, api_get_course_int_id(), $group_id, $user_id);
$events = $agenda->get_events(
$start,
$end,
api_get_course_int_id(),
$group_id,
$user_id
);
echo $events;
break;
case 'get_user_agenda':

@ -0,0 +1,62 @@
<?php
/* For licensing terms, see /license.txt */
require_once 'HTML/QuickForm/date.php';
/**
* Form element to select a date and hour (with popup datepicker)
*/
class DatePicker extends HTML_QuickForm_text
{
public $addLibrary = false;
/**
* Constructor
*/
public function DatePicker($elementName = null, $elementLabel = null, $attributes = null)
{
if (!isset($attributes['id'])) {
$attributes['id'] = $elementName;
}
HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes);
$this->_appendName = true;
$this->_type = 'date_picker';
}
/**
* HTML code to display this datepicker
*/
public function toHtml()
{
$js = $this->getElementJS();
return $js.parent::toHtml();
}
function setValue($value)
{
$value = substr($value, 0, 16);
$this->updateAttributes(
array(
'value' => $value
)
);
}
/**
* Get the necessary javascript for this datepicker
*/
private function getElementJS()
{
$js = null;
$id = $this->getAttribute('id');
$js .= "<script>
$(function() {
$('#$id').datepicker({
dateFormat: 'yy-mm-dd'
});
});
</script>";
return $js;
}
}

@ -0,0 +1,138 @@
<?php
/* For licensing terms, see /license.txt */
require_once 'HTML/QuickForm/date.php';
/**
* Form element to select a date and hour (with popup datepicker)
*/
class DateRangePicker extends HTML_QuickForm_text
{
public $addLibrary = false;
/**
* Constructor
*/
public function DateRangePicker($elementName = null, $elementLabel = null, $attributes = null)
{
if (!isset($attributes['id'])) {
$attributes['id'] = $elementName;
}
$attributes['class'] = 'span3';
HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes);
$this->_appendName = true;
$this->_type = 'date_range_picker';
}
/**
* HTML code to display this datepicker
*/
public function toHtml()
{
$js = $this->getElementJS();
return $js.parent::toHtml();
}
function setValue($value)
{
$this->updateAttributes(
array(
'value' => $value
)
);
}
/**
* Get the necessary javascript for this datepicker
*/
private function getElementJS()
{
$js = null;
if ($this->addLibrary == true) {
$js .= '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/daterange/moment.min.js" type="text/javascript"></script>';
$js .= '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/daterange/daterangepicker.js" type="text/javascript"></script>';
$js .='<link href="'.api_get_path(WEB_LIBRARY_PATH).'javascript/daterange/daterangepicker-bs2.css" rel="stylesheet" type="text/css" />';
$isocode = api_get_language_isocode();
if ($isocode != 'en') {
$js .= api_get_js('jquery-ui/jquery-ui-i18n.min.js');
$js .= '<script>
$(function(){
moment.lang("'.$isocode.'");
});
</script>';
}
}
$id = $this->getAttribute('id');
//timeFormat: 'hh:mm'
$js .= "<script>
$(function() {
$('#$id').daterangepicker({
format: 'YYYY-MM-DD HH:mm',
timePicker: true,
timePickerIncrement: 30,
timePicker12Hour: false,
ranges: {
'".get_lang('Today')."': [moment(), moment()],
'".get_lang('ThisWeek')."': [moment().weekday(1), moment().weekday(5)],
'".get_lang('NextWeek')."': [moment().weekday(8), moment().weekday(12)]
},
//showDropdowns : true,
separator: ' / ',
locale: {
applyLabel: '".get_lang('Apply')."',
cancelLabel: '".get_lang('Cancel')."',
fromLabel: '".get_lang('From')."',
toLabel: '".get_lang('To')."',
customRangeLabel: '".get_lang('CustomRange')."',
}
});
});
</script>";
return $js;
}
/**
* @param array $dateRange
* @return array
*/
function parseDateRange($dateRange)
{
$dates = explode('/', $dateRange);
$dates = array_map('trim', $dates);
return array(
'start' => $dates[0],
'end' => $dates[1]
);
}
/**
* @param array $dates result of parseDateRange()
* @return bool
*/
function validateDates($dates)
{
if (empty($dates['start']) || empty($dates['end'])) {
return false;
}
$format = 'Y-m-d H:i';
$d = DateTime::createFromFormat($format, $dates['start']);
$resultStart = $d && $d->format($format) == $dates['start'];
$d = DateTime::createFromFormat($format, $dates['end']);
$resultEnd = $d && $d->format($format) == $dates['end'];
if (!($resultStart) || !$resultEnd) {
return false;
}
return true;
}
}

@ -19,7 +19,7 @@ class DateTimePicker extends HTML_QuickForm_text
HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes);
$this->_appendName = true;
$this->_type = 'datetimepicker';
$this->_type = 'date_time_picker';
}
/**
@ -70,7 +70,8 @@ class DateTimePicker extends HTML_QuickForm_text
$js .= "<script>
$(function() {
$('#$id').datetimepicker({
dateFormat: 'yy-mm-dd'
dateFormat: 'yy-mm-dd',
timeFormat: 'HH:mm'
});
});
</script>";

@ -126,7 +126,10 @@ class FormValidator extends HTML_QuickForm
$dir = api_get_path(LIBRARY_PATH) . 'formvalidator/';
$this->registerElementType('html_editor', $dir . 'Element/html_editor.php', 'HTML_QuickForm_html_editor');
$this->registerElementType('datetimepicker', $dir . 'Element/DateTimePicker.php', 'DateTimePicker');
$this->registerElementType('date_range_picker', $dir . 'Element/DateRangePicker.php', 'DateRangePicker');
$this->registerElementType('date_time_picker', $dir . 'Element/DateTimePicker.php', 'DateTimePicker');
$this->registerElementType('date_picker', $dir . 'Element/DatePicker.php', 'DatePicker');
$this->registerElementType('datepicker', $dir . 'Element/datepicker.php', 'HTML_QuickForm_datepicker');
$this->registerElementType('datepickerdate', $dir . 'Element/datepickerdate.php', 'HTML_QuickForm_datepickerdate');
$this->registerElementType('receivers', $dir . 'Element/receivers.php', 'HTML_QuickForm_receivers');
@ -252,6 +255,18 @@ EOT;
}
}
public function addDateRangePicker($name, $label, $required = true, $attributes = array())
{
$this->addElement('date_range_picker', $name, $label, $attributes);
$this->addElement('hidden', $name.'_start');
$this->addElement('hidden', $name.'_end');
if ($required) {
$this->addRule($name, get_lang('ThisFieldIsRequired'), 'required');
}
}
function add_hidden($name, $value)
{
$this->addElement('hidden', $name, $value);
@ -543,6 +558,8 @@ EOT;
return $return_value;
}
}
/**

@ -85,23 +85,16 @@ class GroupManager
public static function get_group_list($category = null, $course_code = null)
{
$my_user_id = api_get_user_id();
$course_info = api_get_course_info($course_code);
$course_info = api_get_course_info($course_code);
$course_id = $course_info['real_id'];
$table_group_user = Database :: get_course_table(TABLE_GROUP_USER);
$table_group = Database :: get_course_table(TABLE_GROUP);
$course_id = $course_info['real_id'];
$table_group_user = Database :: get_course_table(TABLE_GROUP_USER);
$table_group = Database :: get_course_table(TABLE_GROUP);
//condition for the session
$session_id = api_get_session_id();
$my_status_of_user_in_course = CourseManager::get_user_in_course_status($my_user_id, $course_info['code']);
$is_student_in_session = false;
if (is_null($my_status_of_user_in_course) || $my_status_of_user_in_course=='') {
if ($session_id>0) {
$is_student_in_session=true;
}
}
// COURSEMANAGER or STUDENT
if ($my_status_of_user_in_course == COURSEMANAGER || api_is_allowed_to_edit(null, true) || api_is_drh()) {
$can_see_groups = 1;
@ -118,8 +111,9 @@ class GroupManager
FROM $table_group g
LEFT JOIN $table_group_user ug
ON (ug.group_id = g.id AND ug.user_id = '".api_get_user_id()."' AND ug.c_id = $course_id AND g.c_id = $course_id)";
} elseif ($my_status_of_user_in_course==STUDENT || $is_student_in_session === true || $_SESSION['studentview'] == 'studentview') {
} elseif ($my_status_of_user_in_course == STUDENT || $_SESSION['studentview'] == 'studentview') {
$can_see_groups = 1;
$sql = "SELECT g.id,
g.name,
g.description,
@ -133,6 +127,8 @@ class GroupManager
FROM $table_group g
LEFT JOIN $table_group_user ug
ON (ug.group_id = g.id AND ug.user_id = '".api_get_user_id()."' AND ug.c_id = $course_id AND g.c_id = $course_id)";
} else {
return array();
}
$sql .= " WHERE 1=1 ";
@ -168,12 +164,13 @@ class GroupManager
$sql = 'SELECT name FROM '.Database::get_main_table(TABLE_MAIN_SESSION).'
WHERE id='.$thisGroup['session_id'];
$rs_session = Database::query($sql);
if (Database::num_rows($rs_session)>0) {
if (Database::num_rows($rs_session) > 0) {
$thisGroup['session_name'] = Database::result($rs_session, 0, 0);
}
}
$groups[] = $thisGroup;
}
return $groups;
}

@ -0,0 +1,256 @@
/*!
* Stylesheet for the Date Range Picker, for use with Bootstrap 2.x
*
* Copyright 2013 Dan Grossman ( http://www.dangrossman.info )
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Built for http://www.improvely.com
*/
.daterangepicker.dropdown-menu {
max-width: none;
z-index: 3000;
}
.daterangepicker.opensleft .ranges, .daterangepicker.opensleft .calendar {
float: left;
margin: 4px;
}
.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar {
float: right;
margin: 4px;
}
.daterangepicker .ranges {
width: 160px;
text-align: left;
}
.daterangepicker .ranges .range_inputs>div {
float: left;
}
.daterangepicker .ranges .range_inputs>div:nth-child(2) {
padding-left: 11px;
}
.daterangepicker .calendar {
display: none;
max-width: 250px;
}
.daterangepicker.show-calendar .calendar {
display: block;
}
.daterangepicker .calendar.single .calendar-date {
border: none;
}
.daterangepicker .calendar th, .daterangepicker .calendar td {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
white-space: nowrap;
text-align: center;
}
.daterangepicker .daterangepicker_start_input label,
.daterangepicker .daterangepicker_end_input label {
color: #333;
font-size: 11px;
margin-bottom: 2px;
text-transform: uppercase;
text-shadow: 1px 1px 0 #fff;
}
.daterangepicker .ranges input {
font-size: 11px;
}
.daterangepicker .ranges ul {
list-style: none;
margin: 0;
padding: 0;
}
.daterangepicker .ranges li {
font-size: 13px;
background: #f5f5f5;
border: 1px solid #f5f5f5;
color: #08c;
padding: 3px 12px;
margin-bottom: 8px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
cursor: pointer;
}
.daterangepicker .ranges li.active, .daterangepicker .ranges li:hover {
background: #08c;
border: 1px solid #08c;
color: #fff;
}
.daterangepicker .calendar-date {
border: 1px solid #ddd;
padding: 4px;
border-radius: 4px;
background: #fff;
}
.daterangepicker .calendar-time {
text-align: center;
margin: 8px auto 0 auto;
line-height: 30px;
}
.daterangepicker {
position: absolute;
background: #fff;
top: 100px;
left: 20px;
padding: 4px;
margin-top: 1px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.daterangepicker.opensleft:before {
position: absolute;
top: -7px;
right: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensleft:after {
position: absolute;
top: -6px;
right: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.opensright:before {
position: absolute;
top: -7px;
left: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensright:after {
position: absolute;
top: -6px;
left: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker table {
width: 100%;
margin: 0;
}
.daterangepicker td, .daterangepicker th {
text-align: center;
width: 20px;
height: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
cursor: pointer;
white-space: nowrap;
}
.daterangepicker td.off {
color: #999;
}
.daterangepicker td.disabled {
color: #999;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background: #eee;
}
.daterangepicker td.in-range {
background: #ebf4f8;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.daterangepicker td.active, .daterangepicker td.active:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
background-image: linear-gradient(top, #0088cc, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
font-size: 12px;
padding: 1px;
height: auto;
margin: 0;
cursor: default;
}
.daterangepicker select.monthselect {
margin-right: 2%;
width: 56%;
}
.daterangepicker select.yearselect {
width: 40%;
}
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.ampmselect {
width: 60px;
margin-bottom: 0;
}
.daterangepicker_start_input {
float: left;
}
.daterangepicker_end_input {
float: left;
padding-left: 11px
}
.daterangepicker th.month {
width: auto;
}

@ -0,0 +1,267 @@
/*!
* Stylesheet for the Date Range Picker, for use with Bootstrap 3.x
*
* Copyright 2013 Dan Grossman ( http://www.dangrossman.info )
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Built for http://www.improvely.com
*/
.daterangepicker.dropdown-menu {
max-width: none;
z-index: 3000;
}
.daterangepicker.opensleft .ranges, .daterangepicker.opensleft .calendar {
float: left;
margin: 4px;
}
.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar {
float: right;
margin: 4px;
}
.daterangepicker .ranges {
width: 160px;
text-align: left;
}
.daterangepicker .ranges .range_inputs>div {
float: left;
}
.daterangepicker .ranges .range_inputs>div:nth-child(2) {
padding-left: 11px;
}
.daterangepicker .calendar {
display: none;
max-width: 270px;
}
.daterangepicker.show-calendar .calendar {
display: block;
}
.daterangepicker .calendar.single .calendar-date {
border: none;
}
.daterangepicker .calendar th, .daterangepicker .calendar td {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
white-space: nowrap;
text-align: center;
min-width: 32px;
}
.daterangepicker .daterangepicker_start_input label,
.daterangepicker .daterangepicker_end_input label {
color: #333;
display: block;
font-size: 11px;
font-weight: normal;
height: 20px;
line-height: 20px;
margin-bottom: 2px;
text-shadow: #fff 1px 1px 0px;
text-transform: uppercase;
width: 74px;
}
.daterangepicker .ranges input {
font-size: 11px;
}
.daterangepicker .ranges .input-mini {
background-color: #eee;
border: 1px solid #ccc;
border-radius: 4px;
color: #555;
display: block;
font-size: 11px;
height: 30px;
line-height: 30px;
vertical-align: middle;
margin: 0 0 10px 0;
padding: 0 6px;
width: 74px;
}
.daterangepicker .ranges ul {
list-style: none;
margin: 0;
padding: 0;
}
.daterangepicker .ranges li {
font-size: 13px;
background: #f5f5f5;
border: 1px solid #f5f5f5;
color: #08c;
padding: 3px 12px;
margin-bottom: 8px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
cursor: pointer;
}
.daterangepicker .ranges li.active, .daterangepicker .ranges li:hover {
background: #08c;
border: 1px solid #08c;
color: #fff;
}
.daterangepicker .calendar-date {
border: 1px solid #ddd;
padding: 4px;
border-radius: 4px;
background: #fff;
}
.daterangepicker .calendar-time {
text-align: center;
margin: 8px auto 0 auto;
line-height: 30px;
}
.daterangepicker {
position: absolute;
background: #fff;
top: 100px;
left: 20px;
padding: 4px;
margin-top: 1px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.daterangepicker.opensleft:before {
position: absolute;
top: -7px;
right: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensleft:after {
position: absolute;
top: -6px;
right: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.opensright:before {
position: absolute;
top: -7px;
left: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensright:after {
position: absolute;
top: -6px;
left: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker table {
width: 100%;
margin: 0;
}
.daterangepicker td, .daterangepicker th {
text-align: center;
width: 20px;
height: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
cursor: pointer;
white-space: nowrap;
}
.daterangepicker td.off {
color: #999;
}
.daterangepicker td.disabled {
color: #999;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background: #eee;
}
.daterangepicker td.in-range {
background: #ebf4f8;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.daterangepicker td.active, .daterangepicker td.active:hover {
background-color: #357ebd;
border-color: #3071a9;
color: #fff;
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
font-size: 12px;
padding: 1px;
height: auto;
margin: 0;
cursor: default;
}
.daterangepicker select.monthselect {
margin-right: 2%;
width: 56%;
}
.daterangepicker select.yearselect {
width: 40%;
}
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.ampmselect {
width: 50px;
margin-bottom: 0;
}
.daterangepicker_start_input {
float: left;
}
.daterangepicker_end_input {
float: left;
padding-left: 11px
}
.daterangepicker th.month {
width: auto;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -1,11 +1,12 @@
/* css for timepicker */
.ui-timepicker-div .ui-widget-header { margin-bottom: 8px; }
.ui-timepicker-div dl { text-align: left; }
.ui-timepicker-div dl dt { float: left; clear:left; padding: 0 0 0 5px; }
.ui-timepicker-div dl dd { margin: 0 10px 10px 40%; }
.ui-timepicker-div dl dd { margin: 0 10px 10px 45%; }
.ui-timepicker-div td { font-size: 90%; }
.ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; }
.ui-timepicker-rtl{ direction: rtl; }
.ui-timepicker-rtl dl { text-align: right; padding: 0 5px 0 0; }
.ui-timepicker-rtl dl dt{ float: right; clear: right; }
.ui-timepicker-rtl dl dd { margin: 0 40% 10px 10px; }
.ui-timepicker-rtl dl dd { margin: 0 45% 10px 10px; }

File diff suppressed because it is too large Load Diff

@ -3290,7 +3290,8 @@ function api_item_property_update(
}
}
//$filter .= $to_filter;
// Adding filter if set.
$filter .= $to_filter;
// Update if possible
$set_type = '';
@ -3396,7 +3397,7 @@ function api_item_property_update(
default : // The item will be added or updated.
$set_type = ", lastedit_type='$lastedit_type' ";
$visibility = '1';
$filter .= $to_filter;
//$filter .= $to_filter; already added
$sql = "UPDATE $TABLE_ITEMPROPERTY
SET
lastedit_date = '$time',
@ -3719,7 +3720,7 @@ function api_get_language_from_type($lang_type){
$toreturn = ($_SESSION['user_language_choice']);
break;
case 'course_lang' :
if ($_course['language'] && !empty($_course['language']) )
if (isset($_course['language']) && !empty($_course['language']) )
$toreturn = $_course['language'];
break;
default :

@ -663,7 +663,7 @@ class HTML_QuickForm extends HTML_Common
$elementName = $elementObject->getName();
$type = $elementObject->getType();
if ($type == 'datetimepicker' && $this->dateTimePickerLibraryAdded == false) {
if (in_array($type, array('date_time_picker', 'date_picker', 'date_range_picker')) && $this->dateTimePickerLibraryAdded == false) {
$elementObject->addLibrary = true;
$this->dateTimePickerLibraryAdded = true;
} else {
@ -924,11 +924,23 @@ class HTML_QuickForm extends HTML_Common
}
}
}
return $value;
} // end func getSubmitValue
// }}}
// {{{ _reindexFiles()
if ($this->getElementType($elementName) == 'date_range_picker') {
/** @var DateRangePicker $element */
$element = $this->getElement($elementName);
$parsedDates = $element->parseDateRange($value);
if (!$element->validateDates($parsedDates)) {
$this->_errors[$elementName] = get_lang('CheckDates');
}
$this->_submitValues[$elementName.'_start'] = $parsedDates['start'];
$this->_submitValues[$elementName.'_end'] = $parsedDates['end'];
}
return $value;
}
/**
* A helper function to change the indexes in $_FILES array

@ -51,7 +51,7 @@ class HTML_QuickForm_select extends HTML_QuickForm_element {
* @access private
*/
var $_options = array();
var $_optgroups = array();
/**
@ -324,8 +324,8 @@ class HTML_QuickForm_select extends HTML_QuickForm_element {
$this->_options[] = array('text' => $text, 'attr' => $attributes);
}
} // end func addOption
/**
* Adds a new OPTION to the SELECT
*
@ -338,15 +338,12 @@ class HTML_QuickForm_select extends HTML_QuickForm_element {
* @return void
*/
function addOptGroup($options, $label)
{
foreach ($options as &$option) {
$option[] = $this->addOption($option['text'], $option['value'], null, true);
}
$this->_optgroups[] = array('label' => $label, 'options' => $options);
} // end func addOption
// }}}
// {{{ loadArray()
{
foreach ($options as $option) {
$this->addOption($option['text'], $option['value'], $option, true);
}
$this->_optgroups[] = array('label' => $label, 'options' => $options);
}
/**
* Loads the options from an associative array
@ -520,6 +517,7 @@ class HTML_QuickForm_select extends HTML_QuickForm_element {
$attrString = $this->_getAttrString($this->_attributes);
$this->setName($myName);
}
$strHtml .= $tabs . '<select' . $attrString . ">\n";
$strValues = is_array($this->_values)? array_map('strval', $this->_values): array();
@ -530,23 +528,19 @@ class HTML_QuickForm_select extends HTML_QuickForm_element {
$strHtml .= $tabs . "\t<option" . $this->_getAttrString($option['attr']) . '>' .
$option['text'] . "</option>\n";
}
foreach ($this->_optgroups as $optgroup) {
$strHtml .= $tabs . "\t<optgroup label=" . $optgroup['label'].">";
foreach($optgroup['options'] as $option) {
$strHtml .= $tabs . "\t<option" . $this->_getAttrString($option['attr']) . '>' .
$option['text'] . "</option>\n";
foreach ($this->_optgroups as $optgroup) {
$strHtml .= $tabs . "<optgroup label=" . $optgroup['label'].">";
foreach ($optgroup['options'] as $option) {
$text = $option['text'];
unset($option['text']);
$strHtml .= $tabs . " <option" . $this->_getAttrString($option) . '>' .$text . "</option>";
}
$strHtml .= "</optgroup>\n";
$strHtml .= "</optgroup>";
}
return $strHtml . $tabs . '</select>';
}
} //end func toHtml
}
// }}}
// {{{ getFrozenHtml()
/**
* Returns the value of field without HTML tags

@ -18,19 +18,19 @@
{% for event in agenda_events %}
<tr>
<td style="width:20%">
{{ event.start |date("m/d/Y h:i:s") }}
{{ event.start_date_localtime }}
</td>
<td style="width:20%">
{% if event.allDay %}
{{ 'AllDay' | get_lang }}
{% else %}
{{ event.end |date("m/d/Y h:i:s") }}
{{ event.end_date_localtime }}
{% endif %}
</td>
<td style="width:50%">
{{ event.title }}
<p>{{ event.description}}</p>
{{ event.attachment }}
</td>
{% if is_allowed_to_edit %}

@ -166,13 +166,11 @@ $(document).ready(function() {
$('#color_calendar').addClass('label_tag');
$('#color_calendar').addClass('{{ type_event_class }}');
allFields.removeClass( "ui-state-error" );
allFields.removeClass("ui-state-error");
$("#dialog-form").dialog("open");
$("#dialog-form").dialog({
buttons: {
'{{ "Add"|get_lang}}' : function() {
'{{ "Add" | get_lang }}' : function() {
var bValid = true;
bValid = bValid && checkLength( title, "title", 1, 255 );
//bValid = bValid && checkLength( content, "content", 1, 255 );
@ -183,7 +181,6 @@ $(document).ready(function() {
success:function(data) {
var user = $('#users_to_send_id').val();
if (user) {
if (user.length > 1) {
user = 0;
} else {
@ -192,7 +189,7 @@ $(document).ready(function() {
var user_length = String(user).length;
if (String(user).substring(0,1) == 'G') {
var user_id = String(user).substring(6,user_length);
var user_id = "G:"+user_id;
var user_id = "G:"+user_id;
} else {
var user_id = String(user).substring(5,user_length);
}
@ -201,17 +198,20 @@ $(document).ready(function() {
var url_length = String(window.location).length;
var url = String(window.location).substring(0, position)+temp;
if (position > 0) {
/*if (position > 0) {
window.location.replace(url);
} else {
url = String(window.location)+temp;
window.location.replace(url);
}
}*/
} else {
calendar.fullCalendar("refetchEvents");
calendar.fullCalendar("rerenderEvents");
/* calendar.fullCalendar("refetchEvents");
calendar.fullCalendar("rerenderEvents");*/
}
calendar.fullCalendar("refetchEvents");
calendar.fullCalendar("rerenderEvents");
$("#dialog-form").dialog("close");
}
});
@ -222,8 +222,10 @@ $(document).ready(function() {
$("#content").attr('value', '');
}
});
//Don't follow the link
// Don't follow the link.
return false;
calendar.fullCalendar('unselect');
//Reload events
calendar.fullCalendar("refetchEvents");
@ -307,27 +309,25 @@ $(document).ready(function() {
$("#dialog-form").dialog("open");
var url = '{{web_agenda_ajax_url}}&a=edit_event&id='+calEvent.id+'&start='+start_date+'&end='+end_date+'&all_day='+calEvent.allDay+'&view='+view.name;
var delete_url = '{{web_agenda_ajax_url}}&a=delete_event&id='+calEvent.id;
var url = '{{ web_agenda_ajax_url }}&a=edit_event&id='+calEvent.id+'&start='+start_date+'&end='+end_date+'&all_day='+calEvent.allDay+'&view='+view.name;
var delete_url = '{{ web_agenda_ajax_url }}&a=delete_event&id='+calEvent.id;
$("#dialog-form").dialog({
buttons: {
'{{ "ExportiCalConfidential"|get_lang }}' : function() {
url = "ical_export.php?id=" + calEvent.id+'&course_id='+calEvent.course_id+"&class=confidential";
url = "{{ _p.web_main }}calendar/ical_export.php?id=" + calEvent.id+'&course_id='+calEvent.course_id+"&class=confidential";
window.location.href = url;
},
'{{ "ExportiCalPrivate"|get_lang }}': function() {
url = "ical_export.php?id=" + calEvent.id+'&course_id='+calEvent.course_id+"&class=private";
url = "{{ _p.web_main }}calendar/ical_export.php?id=" + calEvent.id+'&course_id='+calEvent.course_id+"&class=private";
window.location.href = url;
},
'{{ "ExportiCalPublic"|get_lang }}': function() {
url = "ical_export.php?id=" + calEvent.id+'&course_id='+calEvent.course_id+"&class=public";
url = "{{ _p.web_main }}calendar/ical_export.php?id=" + calEvent.id+'&course_id='+calEvent.course_id+"&class=public";
window.location.href = url;
},
{% if type == 'not_available' %}
'{{ "Edit"|get_lang }}' : function() {
'{{ "Edit" | get_lang }}' : function() {
var bValid = true;
bValid = bValid && checkLength( title, "title", 1, 255 );
@ -352,12 +352,67 @@ $(document).ready(function() {
{% endif %}
'{{ "Edit"|get_lang }}' : function() {
url = "agenda.php?action=edit&type=fromjs&id=" + calEvent.id+'&course_id='+calEvent.course_id+"";
url = "{{ _p.web_main }}calendar/agenda.php?action=edit&type=fromjs&id=" + calEvent.id+'&course_id='+calEvent.course_id+"";
window.location.href = url;
$("#dialog-form").dialog( "close" );
},
'{{ "Delete"|get_lang }}': function() {
if (calEvent.parent_event_id || calEvent.has_children != '' ) {
var newDiv = $(document.createElement('div'));
//newDiv.html('{{ "" |get_lang }}');
newDiv.dialog({
modal: true,
title: "{{ 'Confirmation' | get_lang }}"
});
var buttons = newDiv.dialog("option", "buttons");
if (calEvent.has_children == '0') {
$.extend(buttons, {
'{{ "DeleteThisItem" | get_lang }}' : function() {
$.ajax({
url: delete_url,
success:function() {
calendar.fullCalendar('removeEvents',
calEvent
);
calendar.fullCalendar("refetchEvents");
calendar.fullCalendar("rerenderEvents");
$("#dialog-form").dialog( "close" );
newDiv.dialog( "close" );
}
});
}
});
newDiv.dialog("option", "buttons", buttons);
}
var buttons = newDiv.dialog("option", "buttons");
$.extend(buttons, {
'{{ "DeleteAllItems" | get_lang }}' : function() {
$.ajax({
url: delete_url+'&delete_all_events=1',
success:function() {
calendar.fullCalendar('removeEvents',
calEvent
);
calendar.fullCalendar("refetchEvents");
calendar.fullCalendar("rerenderEvents");
$("#dialog-form").dialog( "close" );
newDiv.dialog( "close" );
}
});
}
});
newDiv.dialog("option", "buttons", buttons);
return true;
}
$.ajax({
url: delete_url,
success:function() {
@ -399,32 +454,30 @@ $(document).ready(function() {
$("#simple_title").html(calEvent.title);
$("#simple_content").html(calEvent.description);
$("#simple-dialog-form").dialog("open");
$("#simple-dialog-form").dialog({
buttons: {
'{{"ExportiCalConfidential"|get_lang}}' : function() {
url = "ical_export.php?id=" + calEvent.id+'&course_id='+calEvent.course_id+"&class=confidential";
window.location.href = url;
url = "ical_export.php?id=" + calEvent.id+'&course_id='+calEvent.course_id+"&class=confidential";
window.location.href = url;
},
'{{"ExportiCalPrivate"|get_lang}}': function() {
url = "ical_export.php?id=" + calEvent.id+'&course_id='+calEvent.course_id+"&class=private";
window.location.href = url;
url = "ical_export.php?id=" + calEvent.id+'&course_id='+calEvent.course_id+"&class=private";
window.location.href = url;
},
'{{"ExportiCalPublic"|get_lang}}': function() {
url = "ical_export.php?id=" + calEvent.id+'&course_id='+calEvent.course_id+"&class=public";
window.location.href = url;
url = "ical_export.php?id=" + calEvent.id+'&course_id='+calEvent.course_id+"&class=public";
window.location.href = url;
}
}
});
}
},
editable: true,
events: "{{web_agenda_ajax_url}}&a=get_events",
eventDrop: function(event, day_delta, minute_delta, all_day, revert_func) {
$.ajax({
url: '{{web_agenda_ajax_url}}',
url: '{{ web_agenda_ajax_url }}',
data: {
a:'move_event', id: event.id, day_delta: day_delta, minute_delta: minute_delta
}
@ -432,7 +485,7 @@ $(document).ready(function() {
},
eventResize: function(event, day_delta, minute_delta, revert_func) {
$.ajax({
url: '{{web_agenda_ajax_url}}',
url: '{{ web_agenda_ajax_url }}',
data: {
a:'resize_event', id: event.id, day_delta: day_delta, minute_delta: minute_delta
}
@ -447,25 +500,26 @@ $(document).ready(function() {
});
});
</script>
{{ actions_div }}
<div id="simple-dialog-form" style="display:none;">
<div style="width:500px">
<form name="form-simple" class="form-vertical" >
<div class="control-group">
<label class="control-label"><b>{{"Date"|get_lang}}</b></label>
<label class="control-label"><b>{{ "Date" |get_lang}}</b></label>
<div class="controls">
<span id="simple_start_date"></span><span id="simple_end_date"></span>
</div>
</div>
<div class="control-group">
<label class="control-label"><b>{{"Title"|get_lang}}</b></label>
<label class="control-label"><b>{{ "Title" |get_lang}}</b></label>
<div class="controls">
<div id="simple_title"></div>
</div>
</div>
<div class="control-group">
<label class="control-label"><b>{{"Description"|get_lang}}</b></label>
<label class="control-label"><b>{{ "Description" |get_lang}}</b></label>
<div class="controls">
<div id="simple_content"></div>
</div>
@ -536,5 +590,7 @@ $(document).ready(function() {
</form>
</div>
</div>
<div id="loading" style="margin-left:150px;position:absolute;display:none">{{ "Loading"|get_lang }}...</div>
<div id="loading" style="margin-left:150px;position:absolute;display:none">
{{ "Loading"|get_lang }}...
</div>
<div id="calendar"></div>

@ -3531,9 +3531,23 @@ function updatePublicationAssignment($workId, $params, $courseInfo, $groupId)
$agenda->type = 'course';
if (empty($agendaId)) {
$agendaId = $agenda->add_event($date, $end_date, 'false', null, $title, $content, array('GROUP:'.$groupId));
$agendaId = $agenda->add_event(
$date,
$end_date,
'false',
$title,
$content,
array('GROUP:'.$groupId)
);
} else {
$agenda->edit_event($agendaId, $end_date, $end_date, 'false', null, $title, $content);
$agenda->edit_event(
$agendaId,
$end_date,
$end_date,
'false',
$title,
$content
);
}
}
@ -3783,7 +3797,7 @@ function getFormWork($form, $defaults = array())
$defaults['ends_on'] = $date.' 23:59';
}
$form->addElement('datetimepicker', 'expires_on', get_lang('ExpiresAt'));
$form->addElement('date_time_picker', 'expires_on', get_lang('ExpiresAt'));
$form->addElement('html', '</div>');
$form->addElement('checkbox', 'enableEndDate', null, get_lang('EnableEndDate'), 'id="end_date"');
@ -3794,7 +3808,7 @@ function getFormWork($form, $defaults = array())
$form->addElement('html', '<div id="option3" style="display: none;">');
}
$form->addElement('datetimepicker', 'ends_on', get_lang('EndsAt'));
$form->addElement('date_time_picker', 'ends_on', get_lang('EndsAt'));
$form->addElement('html', '</div>');
$form->addElement('checkbox', 'add_to_calendar', null, get_lang('AddToCalendar'));

@ -32,7 +32,14 @@ if ($teacher) {
$title = sprintf(get_lang('VideoConferenceXCourseX'), $id, $course_info['name']);
$content = Display::url(get_lang('GoToTheVideoConference'), $_GET['url']);
$event_id = $agenda->add_event($_REQUEST['start'], null, 'true', null, $title, $content, array('everyone'));
$event_id = $agenda->add_event(
$_REQUEST['start'],
null,
'true',
$title,
$content,
array('everyone')
);
if (!empty($event_id)) {
$message = Display::return_message(get_lang('VideoConferenceAddedToTheCalendar'), 'success');
} else {

@ -31,7 +31,14 @@ if ($teacher) {
$title = sprintf(get_lang('VideoConferenceXCourseX'), $id, $course_info['name']);
$content = Display::url(get_lang('GoToTheVideoConference'), $_GET['url']);
$event_id = $agenda->add_event($_REQUEST['start'], null, 'true', null, $title, $content, array('everyone'));
$event_id = $agenda->add_event(
$_REQUEST['start'],
null,
'true',
$title,
$content,
array('everyone')
);
if (!empty($event_id)) {
$message = Display::return_message(get_lang('VideoConferenceAddedToTheCalendar'), 'success');
} else {

@ -354,18 +354,6 @@ class TestCalendar extends UnitTestCase {
$this->assertTrue(is_null($res));
}
public function testGetAgendaitems(){
global $_user;
global $_configuration;
$month=01;
$year=2010;
$res = get_agendaitems($month, $year);
if(is_array($res)) {
$this->assertTrue(is_array($res));
}
}
public function testDisplayUpcomingEvents(){
ob_start();
$res = display_upcoming_events();
@ -376,6 +364,4 @@ class TestCalendar extends UnitTestCase {
public function testIsRepeatedEvent() {
//This is deprecated or not used
}
}
?>

@ -4,4 +4,4 @@
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit66dd73b468c5607bacc52774d2bd8959::getLoader();
return ComposerAutoloaderInitc8544b9da483382b715efa095e59273d::getLoader();

@ -266,7 +266,7 @@ class ClassLoader
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
include $file;
includeFile($file);
return true;
}
@ -291,8 +291,25 @@ class ClassLoader
return $this->classMap[$class];
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if ($file === null && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if ($file === null) {
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php';
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
@ -321,7 +338,7 @@ class ClassLoader
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php';
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
@ -347,8 +364,15 @@ class ClassLoader
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

@ -8,9 +8,9 @@ $baseDir = dirname($vendorDir);
return array(
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'),
'Sabre\\VObject' => array($vendorDir . '/sabre/vobject/lib'),
'Psr\\Log\\' => array($vendorDir . '/psr/log'),
'Neutron' => array($vendorDir . '/neutron/temporary-filesystem/src'),
'Monolog' => array($vendorDir . '/monolog/monolog/src'),
'FFMpeg' => array($vendorDir . '/php-ffmpeg/php-ffmpeg/src'),
'Evenement' => array($vendorDir . '/evenement/evenement/src'),
'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib'),

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit66dd73b468c5607bacc52774d2bd8959
class ComposerAutoloaderInitc8544b9da483382b715efa095e59273d
{
private static $loader;
@ -19,9 +19,9 @@ class ComposerAutoloaderInit66dd73b468c5607bacc52774d2bd8959
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit66dd73b468c5607bacc52774d2bd8959', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInitc8544b9da483382b715efa095e59273d', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit66dd73b468c5607bacc52774d2bd8959', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInitc8544b9da483382b715efa095e59273d', 'loadClassLoader'));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
@ -46,3 +46,8 @@ class ComposerAutoloaderInit66dd73b468c5607bacc52774d2bd8959
return $loader;
}
}
function composerRequirec8544b9da483382b715efa095e59273d($file)
{
require $file;
}

@ -1,28 +1,34 @@
[
{
"name": "evenement/evenement",
"version": "v1.0.0",
"version_normalized": "1.0.0.0",
"name": "symfony/filesystem",
"version": "v2.4.3",
"version_normalized": "2.4.3.0",
"target-dir": "Symfony/Component/Filesystem",
"source": {
"type": "git",
"url": "https://github.com/igorw/evenement.git",
"reference": "fa966683e7df3e5dd5929d984a44abfbd6bafe8d"
"url": "https://github.com/symfony/Filesystem.git",
"reference": "b717952487176a993e9b12a08b87beae25ae419c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/igorw/evenement/zipball/fa966683e7df3e5dd5929d984a44abfbd6bafe8d",
"reference": "fa966683e7df3e5dd5929d984a44abfbd6bafe8d",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/b717952487176a993e9b12a08b87beae25ae419c",
"reference": "b717952487176a993e9b12a08b87beae25ae419c",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
"php": ">=5.3.3"
},
"time": "2012-05-30 15:01:08",
"time": "2014-03-26 11:55:03",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Evenement": "src"
"Symfony\\Component\\Filesystem\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@ -31,47 +37,46 @@
],
"authors": [
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch",
"homepage": "http://wiedler.ch/igor/"
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Événement is a very simple event dispatching library for PHP 5.3",
"keywords": [
"event-dispatcher"
]
"description": "Symfony Filesystem Component",
"homepage": "http://symfony.com"
},
{
"name": "alchemy/binary-driver",
"version": "1.5.0",
"version_normalized": "1.5.0.0",
"name": "neutron/temporary-filesystem",
"version": "2.1.1",
"version_normalized": "2.1.1.0",
"source": {
"type": "git",
"url": "https://github.com/alchemy-fr/BinaryDriver.git",
"reference": "b32c03d4b56ce29f783051eac55887adae654b41"
"url": "https://github.com/romainneutron/Temporary-Filesystem.git",
"reference": "6b21fa99fd452efea16b9a7adb7304ccfff854d0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/alchemy-fr/BinaryDriver/zipball/b32c03d4b56ce29f783051eac55887adae654b41",
"reference": "b32c03d4b56ce29f783051eac55887adae654b41",
"url": "https://api.github.com/repos/romainneutron/Temporary-Filesystem/zipball/6b21fa99fd452efea16b9a7adb7304ccfff854d0",
"reference": "6b21fa99fd452efea16b9a7adb7304ccfff854d0",
"shasum": ""
},
"require": {
"evenement/evenement": "~1.0",
"monolog/monolog": "~1.3",
"php": ">=5.3.3",
"psr/log": "~1.0",
"symfony/process": "~2.0"
"symfony/filesystem": "~2.0"
},
"require-dev": {
"phpunit/phpunit": "~3.7"
},
"time": "2013-06-21 15:51:20",
"time": "2013-10-10 10:58:09",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"Alchemy": "src"
"Neutron": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -83,64 +88,34 @@
"name": "Romain Neutron",
"email": "imprec@gmail.com",
"homepage": "http://www.lickmychip.com/"
},
{
"name": "Nicolas Le Goff",
"email": "legoff.n@gmail.com"
},
{
"name": "Phraseanet Team",
"email": "info@alchemy.fr",
"homepage": "http://www.phraseanet.com/"
}
],
"description": "A set of tools to build binary drivers",
"keywords": [
"binary",
"driver"
]
"description": "Symfony filesystem extension to handle temporary files"
},
{
"name": "php-ffmpeg/php-ffmpeg",
"version": "0.3.x-dev",
"version_normalized": "0.3.9999999.9999999-dev",
"name": "evenement/evenement",
"version": "v1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/alchemy-fr/PHP-FFmpeg.git",
"reference": "185fb5c33da594db7ffbf3f89d48e9841bc666a0"
"url": "https://github.com/igorw/evenement.git",
"reference": "fa966683e7df3e5dd5929d984a44abfbd6bafe8d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/alchemy-fr/PHP-FFmpeg/zipball/185fb5c33da594db7ffbf3f89d48e9841bc666a0",
"reference": "185fb5c33da594db7ffbf3f89d48e9841bc666a0",
"url": "https://api.github.com/repos/igorw/evenement/zipball/fa966683e7df3e5dd5929d984a44abfbd6bafe8d",
"reference": "fa966683e7df3e5dd5929d984a44abfbd6bafe8d",
"shasum": ""
},
"require": {
"alchemy/binary-driver": "~1.5",
"doctrine/cache": "~1.0",
"evenement/evenement": "~1.0",
"neutron/temporary-filesystem": "~2.1, >=2.1.1",
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~3.7",
"sami/sami": "~1.0",
"silex/silex": "~1.0"
},
"suggest": {
"php-ffmpeg/extras": "A compilation of common audio & video drivers for PHP-FFMpeg"
"php": ">=5.3.0"
},
"time": "2013-10-21 12:13:46",
"time": "2012-05-30 15:01:08",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.4-dev"
}
},
"installation-source": "source",
"installation-source": "dist",
"autoload": {
"psr-0": {
"FFMpeg": "src"
"Evenement": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -149,26 +124,14 @@
],
"authors": [
{
"name": "Romain Neutron",
"email": "imprec@gmail.com",
"homepage": "http://www.lickmychip.com/"
},
{
"name": "Phraseanet Team",
"email": "info@alchemy.fr",
"homepage": "http://www.phraseanet.com/"
"name": "Igor Wiedler",
"email": "igor@wiedler.ch",
"homepage": "http://wiedler.ch/igor/"
}
],
"description": "FFMpeg PHP, an Object Oriented library to communicate with AVconv / ffmpeg",
"description": "Événement is a very simple event dispatching library for PHP 5.3",
"keywords": [
"audio",
"audio processing",
"avconv",
"avprobe",
"ffmpeg",
"ffprobe",
"video",
"video processing"
"event-dispatcher"
]
},
{
@ -215,7 +178,7 @@
],
"authors": [
{
"name": "Jonathan Wage",
"name": "Jonathan H. Wage",
"email": "jonwage@gmail.com",
"homepage": "http://www.jwage.com/",
"role": "Creator"
@ -247,6 +210,57 @@
"caching"
]
},
{
"name": "symfony/process",
"version": "v2.4.3",
"version_normalized": "2.4.3.0",
"target-dir": "Symfony/Component/Process",
"source": {
"type": "git",
"url": "https://github.com/symfony/Process.git",
"reference": "c09c3b08455c35688eee3e481fdfc85518ef01d7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/c09c3b08455c35688eee3e481fdfc85518ef01d7",
"reference": "c09c3b08455c35688eee3e481fdfc85518ef01d7",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2014-03-26 11:35:33",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Symfony\\Component\\Process\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Process Component",
"homepage": "http://symfony.com"
},
{
"name": "psr/log",
"version": "1.0.0",
@ -289,17 +303,17 @@
},
{
"name": "monolog/monolog",
"version": "1.7.0",
"version_normalized": "1.7.0.0",
"version": "1.8.0",
"version_normalized": "1.8.0.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "6225b22de9dcf36546be3a0b2fa8e3d986153f57"
"reference": "392ef35fd470638e08d0160d6b1cbab63cb23174"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/6225b22de9dcf36546be3a0b2fa8e3d986153f57",
"reference": "6225b22de9dcf36546be3a0b2fa8e3d986153f57",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/392ef35fd470638e08d0160d6b1cbab63cb23174",
"reference": "392ef35fd470638e08d0160d6b1cbab63cb23174",
"shasum": ""
},
"require": {
@ -307,11 +321,11 @@
"psr/log": "~1.0"
},
"require-dev": {
"aws/aws-sdk-php": "~2.4.8",
"doctrine/couchdb": "dev-master",
"mlehner/gelf-php": "1.0.*",
"aws/aws-sdk-php": "~2.4, >2.4.8",
"doctrine/couchdb": "~1.0@dev",
"graylog2/gelf-php": "~1.0",
"phpunit/phpunit": "~3.7.0",
"raven/raven": "0.5.*",
"raven/raven": "~0.5",
"ruflin/elastica": "0.90.*"
},
"suggest": {
@ -319,21 +333,22 @@
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
"ext-mongo": "Allow sending log messages to a MongoDB server",
"mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server",
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
"raven/raven": "Allow sending log messages to a Sentry server",
"rollbar/rollbar": "Allow sending log messages to Rollbar",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server"
},
"time": "2013-11-14 19:48:31",
"time": "2014-03-23 19:50:26",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.7.x-dev"
"dev-master": "1.8.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Monolog": "src/"
"psr-4": {
"Monolog\\": "src/Monolog"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -357,35 +372,36 @@
]
},
{
"name": "symfony/process",
"version": "v2.4.1",
"version_normalized": "2.4.1.0",
"target-dir": "Symfony/Component/Process",
"name": "alchemy/binary-driver",
"version": "1.5.0",
"version_normalized": "1.5.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/Process.git",
"reference": "58fdccb311e44f28866f976c2d7b3227e9f713db"
"url": "https://github.com/alchemy-fr/BinaryDriver.git",
"reference": "b32c03d4b56ce29f783051eac55887adae654b41"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/58fdccb311e44f28866f976c2d7b3227e9f713db",
"reference": "58fdccb311e44f28866f976c2d7b3227e9f713db",
"url": "https://api.github.com/repos/alchemy-fr/BinaryDriver/zipball/b32c03d4b56ce29f783051eac55887adae654b41",
"reference": "b32c03d4b56ce29f783051eac55887adae654b41",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
"evenement/evenement": "~1.0",
"monolog/monolog": "~1.3",
"php": ">=5.3.3",
"psr/log": "~1.0",
"symfony/process": "~2.0"
},
"time": "2014-01-05 02:10:50",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
"require-dev": {
"phpunit/phpunit": "~3.7"
},
"time": "2013-06-21 15:51:20",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"Symfony\\Component\\Process\\": ""
"Alchemy": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -394,47 +410,67 @@
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
"name": "Romain Neutron",
"email": "imprec@gmail.com",
"homepage": "http://www.lickmychip.com/"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
"name": "Nicolas Le Goff",
"email": "legoff.n@gmail.com"
},
{
"name": "Phraseanet Team",
"email": "info@alchemy.fr",
"homepage": "http://www.phraseanet.com/"
}
],
"description": "Symfony Process Component",
"homepage": "http://symfony.com"
"description": "A set of tools to build binary drivers",
"keywords": [
"binary",
"driver"
]
},
{
"name": "symfony/filesystem",
"version": "v2.4.1",
"version_normalized": "2.4.1.0",
"target-dir": "Symfony/Component/Filesystem",
"name": "php-ffmpeg/php-ffmpeg",
"version": "0.3.x-dev",
"version_normalized": "0.3.9999999.9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/symfony/Filesystem.git",
"reference": "b3c3b5a8108b3e5d604dc23241b4ea84a067fc78"
"url": "https://github.com/PHP-FFMpeg/PHP-FFMpeg.git",
"reference": "185fb5c33da594db7ffbf3f89d48e9841bc666a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/b3c3b5a8108b3e5d604dc23241b4ea84a067fc78",
"reference": "b3c3b5a8108b3e5d604dc23241b4ea84a067fc78",
"url": "https://api.github.com/repos/PHP-FFMpeg/PHP-FFMpeg/zipball/185fb5c33da594db7ffbf3f89d48e9841bc666a0",
"reference": "185fb5c33da594db7ffbf3f89d48e9841bc666a0",
"shasum": ""
},
"require": {
"alchemy/binary-driver": "~1.5",
"doctrine/cache": "~1.0",
"evenement/evenement": "~1.0",
"neutron/temporary-filesystem": "~2.1, >=2.1.1",
"php": ">=5.3.3"
},
"time": "2013-12-31 13:43:26",
"require-dev": {
"phpunit/phpunit": "~3.7",
"sami/sami": "~1.0",
"silex/silex": "~1.0"
},
"suggest": {
"php-ffmpeg/extras": "A compilation of common audio & video drivers for PHP-FFMpeg"
},
"time": "2013-10-21 12:13:46",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
"dev-master": "0.4-dev"
}
},
"installation-source": "dist",
"installation-source": "source",
"autoload": {
"psr-0": {
"Symfony\\Component\\Filesystem\\": ""
"FFMpeg": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -443,57 +479,87 @@
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
"name": "Romain Neutron",
"email": "imprec@gmail.com",
"homepage": "http://www.lickmychip.com/"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
"name": "Phraseanet Team",
"email": "info@alchemy.fr",
"homepage": "http://www.phraseanet.com/"
}
],
"description": "Symfony Filesystem Component",
"homepage": "http://symfony.com"
"description": "FFMpeg PHP, an Object Oriented library to communicate with AVconv / ffmpeg",
"keywords": [
"audio",
"audio processing",
"avconv",
"avprobe",
"ffmpeg",
"ffprobe",
"video",
"video processing"
]
},
{
"name": "neutron/temporary-filesystem",
"version": "2.1.1",
"version_normalized": "2.1.1.0",
"name": "sabre/vobject",
"version": "3.2.0",
"version_normalized": "3.2.0.0",
"source": {
"type": "git",
"url": "https://github.com/romainneutron/Temporary-Filesystem.git",
"reference": "6b21fa99fd452efea16b9a7adb7304ccfff854d0"
"url": "https://github.com/fruux/sabre-vobject.git",
"reference": "dd1dd19b6601cdc99408b70ec260052825f70b8f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/romainneutron/Temporary-Filesystem/zipball/6b21fa99fd452efea16b9a7adb7304ccfff854d0",
"reference": "6b21fa99fd452efea16b9a7adb7304ccfff854d0",
"url": "https://api.github.com/repos/fruux/sabre-vobject/zipball/dd1dd19b6601cdc99408b70ec260052825f70b8f",
"reference": "dd1dd19b6601cdc99408b70ec260052825f70b8f",
"shasum": ""
},
"require": {
"symfony/filesystem": "~2.0"
"ext-mbstring": "*",
"php": ">=5.3.1"
},
"require-dev": {
"phpunit/phpunit": "~3.7"
"phpunit/phpunit": "*"
},
"time": "2013-10-10 10:58:09",
"time": "2014-04-02 23:53:27",
"bin": [
"bin/vobject",
"bin/generate_vcards"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Neutron": "src"
"Sabre\\VObject": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
"BSD-3-Clause"
],
"authors": [
{
"name": "Romain Neutron",
"email": "imprec@gmail.com",
"homepage": "http://www.lickmychip.com/"
"name": "Evert Pot",
"email": "evert@rooftopsolutions.nl",
"homepage": "http://evertpot.com/",
"role": "Developer"
}
],
"description": "Symfony filesystem extension to handle temporary files"
"description": "The VObject library for PHP allows you to easily parse and manipulate iCalendar and vCard objects",
"homepage": "https://github.com/fruux/sabre-vobject",
"keywords": [
"VObject",
"iCalendar",
"jCal",
"jCard",
"vCard"
]
}
]

@ -1,3 +1,20 @@
### 1.8.0 (2014-03-23)
* Break: the LineFormatter now strips newlines by default because this was a bug, set $allowInlineLineBreaks to true if you need them
* Added BrowserConsoleHandler to send logs to any browser's console via console.log() injection in the output
* Added FilterHandler to filter records and only allow those of a given list of levels through to the wrapped handler
* Added FlowdockHandler to send logs to a Flowdock account
* Added RollbarHandler to send logs to a Rollbar account
* Added HtmlFormatter to send prettier log emails with colors for each log level
* Added GitProcessor to add the current branch/commit to extra record data
* Added a Monolog\Registry class to allow easier global access to pre-configured loggers
* Added support for the new official graylog2/gelf-php lib for GelfHandler, upgrade if you can by replacing the mlehner/gelf-php requirement
* Added support for HHVM
* Added support for Loggly batch uploads
* Added support for tweaking the content type and encoding in NativeMailerHandler
* Added $skipClassesPartials to tweak the ignored classes in the IntrospectionProcessor
* Fixed batch request support in GelfHandler
### 1.7.0 (2013-11-14)
* Added ElasticSearchHandler to send logs to an Elastic Search server

@ -119,6 +119,7 @@ Handlers
- _SwiftMailerHandler_: Sends emails using a [`Swift_Mailer`](http://swiftmailer.org/) instance.
- _PushoverHandler_: Sends mobile notifications via the [Pushover](https://www.pushover.net/) API.
- _HipChatHandler_: Logs records to a [HipChat](http://hipchat.com) chat room using its API.
- _FlowdockHandler_: Logs records to a [Flowdock](https://www.flowdock.com/) account.
### Log specific servers and networked logging
@ -133,6 +134,7 @@ Handlers
- _ZendMonitorHandler_: Logs records to the Zend Monitor present in Zend Server.
- _NewRelicHandler_: Logs records to a [NewRelic](http://newrelic.com/) application.
- _LogglyHandler_: Logs records to a [Loggly](http://www.loggly.com/) account.
- _RollbarHandler_: Logs records to a [Rollbar](https://rollbar.com/) account.
- _SyslogUdpHandler_: Logs records to a remote [Syslogd](http://www.rsyslog.com/) server.
### Logging in development
@ -141,6 +143,8 @@ Handlers
inline `console` messages within [FireBug](http://getfirebug.com/).
- _ChromePHPHandler_: Handler for [ChromePHP](http://www.chromephp.com/), providing
inline `console` messages within Chrome.
- _BrowserConsoleHandler_: Handler to send logs to browser's Javascript `console` with
no browser extension required. Most browsers supporting `console` API are supported.
### Log to databases
@ -171,6 +175,8 @@ Handlers
for every log record.
- _GroupHandler_: This handler groups other handlers. Every record received is
sent to all the handlers it is configured with.
- _FilterHandler_: This handler only lets records of the given levels through
to the wrapped handler.
- _TestHandler_: Used for testing, it records everything that is sent to it and
has accessors to read out the information.
@ -178,14 +184,17 @@ Formatters
----------
- _LineFormatter_: Formats a log record into a one-line string.
- _HtmlFormatter_: Used to format log records into a human readable html table, mainly suitable for emails.
- _NormalizerFormatter_: Normalizes objects/resources down to strings so a record can easily be serialized/encoded.
- _ScalarFormatter_: Used to format log records into an associative array of scalar values.
- _JsonFormatter_: Encodes a log record into json.
- _WildfireFormatter_: Used to format log records into the Wildfire/FirePHP protocol, only useful for the FirePHPHandler.
- _ChromePHPFormatter_: Used to format log records into the ChromePHP format, only useful for the ChromePHPHandler.
- _GelfFormatter_: Used to format log records into Gelf message instances, only useful for the GelfHandler.
- _GelfMessageFormatter_: Used to format log records into Gelf message instances, only useful for the GelfHandler.
- _LogstashFormatter_: Used to format log records into [logstash](http://logstash.net/) event json, useful for any handler listed under inputs [here](http://logstash.net/docs/1.1.5/).
- _ElasticaFormatter_: Used to format log records into an Elastica\Document object, only useful for the ElasticSearchHandler.
- _LogglyFormatter_: Used to format log records into Loggly messages, only useful for the LogglyHandler.
- _FlowdockFormatter_: Used to format log records into Flowdock messages, only useful for the FlowdockHandler.
Processors
----------
@ -196,6 +205,7 @@ Processors
- _MemoryPeakUsageProcessor_: Adds the peak memory usage to a log record.
- _ProcessIdProcessor_: Adds the process id to a log record.
- _UidProcessor_: Adds a unique identifier to a log record.
- _GitProcessor_: Adds the current git branch and commit to a log record.
Utilities
---------
@ -233,6 +243,7 @@ Frameworks Integration
- [CakePHP](http://cakephp.org/) is usable with Monolog via the [cakephp-monolog](https://github.com/jadb/cakephp-monolog) plugin.
- [Slim](http://www.slimframework.com/) is usable with Monolog via the [Slim-Monolog](https://github.com/Flynsarmy/Slim-Monolog) log writer.
- [XOOPS 2.6](http://xoops.org/) comes out of the box with Monolog.
- [Aura.Web_Project](https://github.com/auraphp/Aura.Web_Project) comes out of the box with Monolog.
Author
------

@ -18,27 +18,28 @@
},
"require-dev": {
"phpunit/phpunit": "~3.7.0",
"mlehner/gelf-php": "1.0.*",
"raven/raven": "0.5.*",
"graylog2/gelf-php": "~1.0",
"raven/raven": "~0.5",
"ruflin/elastica": "0.90.*",
"doctrine/couchdb": "dev-master",
"aws/aws-sdk-php": "~2.4.8"
"doctrine/couchdb": "~1.0@dev",
"aws/aws-sdk-php": "~2.4, >2.4.8"
},
"suggest": {
"mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server",
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
"raven/raven": "Allow sending log messages to a Sentry server",
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server",
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
"ext-mongo": "Allow sending log messages to a MongoDB server",
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB"
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
"rollbar/rollbar": "Allow sending log messages to Rollbar"
},
"autoload": {
"psr-0": {"Monolog": "src/"}
"psr-4": {"Monolog\\": "src/Monolog"}
},
"extra": {
"branch-alias": {
"dev-master": "1.7.x-dev"
"dev-master": "1.8.x-dev"
}
}
}

@ -65,7 +65,7 @@ You can now use this handler in your logger:
```php
<?php
$logger->pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite'));
$logger->pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite')));
// You can now use your logger
$logger->addInfo('My logger is now ready');

@ -8,7 +8,7 @@ Monolog is available on Packagist ([monolog/monolog](http://packagist.org/packag
and as such installable via [Composer](http://getcomposer.org/).
```bash
php composer.phar require monolog/monolog '~1.4'
php composer.phar require monolog/monolog '~1.7'
```
If you do not use Composer, you can grab the code from GitHub, and use any

@ -20,6 +20,34 @@ namespace Monolog\Formatter;
*/
class JsonFormatter implements FormatterInterface
{
protected $batch_mode;
const BATCH_MODE_JSON = 1;
const BATCH_MODE_NEWLINES = 2;
/**
* @param int $batch_mode
*/
public function __construct($batch_mode = self::BATCH_MODE_JSON)
{
$this->batch_mode = $batch_mode;
}
/**
* The batch mode option configures the formatting style for
* multiple records. By default, multiple records will be
* formatted as a JSON-encoded array. However, for
* compatibility with some API endpoints, alternive styles
* are available.
*
* @return int
*/
public function getBatchMode()
{
return $this->batch_mode;
}
/**
* {@inheritdoc}
*/
@ -32,7 +60,46 @@ class JsonFormatter implements FormatterInterface
* {@inheritdoc}
*/
public function formatBatch(array $records)
{
switch ($this->batch_mode) {
case static::BATCH_MODE_NEWLINES:
return $this->formatBatchNewlines($records);
case static::BATCH_MODE_JSON:
default:
return $this->formatBatchJson($records);
}
}
/**
* Return a JSON-encoded array of records.
*
* @param array $records
* @return string
*/
protected function formatBatchJson(array $records)
{
return json_encode($records);
}
/**
* Use new lines to separate records instead of a
* JSON-encoded array.
*
* @param array $records
* @return string
*/
protected function formatBatchNewlines(array $records)
{
$instance = $this;
array_walk($records, function (&$value, $key) use ($instance) {
$value = $instance->format($value);
});
return implode("\n", $records);
}
}

@ -11,6 +11,8 @@
namespace Monolog\Formatter;
use Exception;
/**
* Formats incoming records into a one-line string
*
@ -24,14 +26,17 @@ class LineFormatter extends NormalizerFormatter
const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
protected $format;
protected $allowInlineLineBreaks;
/**
* @param string $format The format of the message
* @param string $dateFormat The format of the timestamp: one supported by DateTime::format
* @param string $format The format of the message
* @param string $dateFormat The format of the timestamp: one supported by DateTime::format
* @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries
*/
public function __construct($format = null, $dateFormat = null)
public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false)
{
$this->format = $format ?: static::SIMPLE_FORMAT;
$this->allowInlineLineBreaks = $allowInlineLineBreaks;
parent::__construct($dateFormat);
}
@ -45,13 +50,13 @@ class LineFormatter extends NormalizerFormatter
$output = $this->format;
foreach ($vars['extra'] as $var => $val) {
if (false !== strpos($output, '%extra.'.$var.'%')) {
$output = str_replace('%extra.'.$var.'%', $this->convertToString($val), $output);
$output = str_replace('%extra.'.$var.'%', $this->replaceNewlines($this->convertToString($val)), $output);
unset($vars['extra'][$var]);
}
}
foreach ($vars as $var => $val) {
if (false !== strpos($output, '%'.$var.'%')) {
$output = str_replace('%'.$var.'%', $this->convertToString($val), $output);
$output = str_replace('%'.$var.'%', $this->replaceNewlines($this->convertToString($val)), $output);
}
}
@ -68,37 +73,41 @@ class LineFormatter extends NormalizerFormatter
return $message;
}
protected function normalize($data)
protected function normalizeException(Exception $e)
{
if (is_bool($data) || is_null($data)) {
return var_export($data, true);
$previousText = '';
if ($previous = $e->getPrevious()) {
do {
$previousText .= ', '.get_class($previous).': '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine();
} while ($previous = $previous->getPrevious());
}
if ($data instanceof \Exception) {
$previousText = '';
if ($previous = $data->getPrevious()) {
do {
$previousText .= ', '.get_class($previous).': '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine();
} while ($previous = $previous->getPrevious());
}
return '[object] ('.get_class($data).': '.$data->getMessage().' at '.$data->getFile().':'.$data->getLine().$previousText.')';
}
return parent::normalize($data);
return '[object] ('.get_class($e).': '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')';
}
protected function convertToString($data)
{
if (null === $data || is_scalar($data)) {
if (null === $data || is_bool($data)) {
return var_export($data, true);
}
if (is_scalar($data)) {
return (string) $data;
}
$data = $this->normalize($data);
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
return $this->toJson($data, true);
}
return str_replace('\\/', '/', @json_encode($data));
}
protected function replaceNewlines($str)
{
if ($this->allowInlineLineBreaks) {
return $str;
}
return preg_replace('{[\r\n]+}', ' ', $str);
}
}

@ -101,7 +101,6 @@ class NormalizerFormatter implements FormatterInterface
);
$trace = $e->getTrace();
array_shift($trace);
foreach ($trace as $frame) {
if (isset($frame['file'])) {
$data['trace'][] = $frame['file'].':'.$frame['line'];

@ -141,8 +141,8 @@ abstract class AbstractHandler implements HandlerInterface
/**
* Sets the bubbling behavior.
*
* @param Boolean $bubble true means that this handler allows bubbling.
* false means that bubbling is not permitted.
* @param Boolean $bubble true means that this handler allows bubbling.
* false means that bubbling is not permitted.
* @return self
*/
public function setBubble($bubble)

@ -32,8 +32,8 @@ class CubeHandler extends AbstractProcessingHandler
* Create a Cube handler
*
* @throws UnexpectedValueException when given url is not a valid url.
* A valid url must consists of three parts : protocol://host:port
* Only valid protocol used by Cube are http and udp
* A valid url must consists of three parts : protocol://host:port
* Only valid protocol used by Cube are http and udp
*/
public function __construct($url, $level = Logger::DEBUG, $bubble = true)
{

@ -12,31 +12,37 @@
namespace Monolog\Handler;
use Gelf\IMessagePublisher;
use Gelf\PublisherInterface;
use InvalidArgumentException;
use Monolog\Logger;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Formatter\GelfMessageFormatter;
/**
* Handler to send messages to a Graylog2 (http://www.graylog2.org) server
*
* @author Matt Lehner <mlehner@gmail.com>
* @author Benjamin Zikarsky <benjamin@zikarsky.de>
*/
class GelfHandler extends AbstractProcessingHandler
{
/**
* @var Gelf\IMessagePublisher the publisher object that sends the message to the server
* @var Publisher the publisher object that sends the message to the server
*/
protected $publisher;
/**
* @param Gelf\IMessagePublisher $publisher a publisher object
* @param integer $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
* @param PublisherInterface|IMessagePublisher $publisher a publisher object
* @param integer $level The minimum logging level at which this handler will be triggered
* @param boolean $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct(IMessagePublisher $publisher, $level = Logger::DEBUG, $bubble = true)
public function __construct($publisher, $level = Logger::DEBUG, $bubble = true)
{
parent::__construct($level, $bubble);
if (!$publisher instanceof IMessagePublisher && !$publisher instanceof PublisherInterface) {
throw new InvalidArgumentException("Invalid publisher, expected a Gelf\IMessagePublisher or Gelf\PublisherInterface instance");
}
$this->publisher = $publisher;
}

@ -47,7 +47,7 @@ interface HandlerInterface
*
* @param array $record The record to handle
* @return Boolean true means that this handler handled the record, and that bubbling is not permitted.
* false means the record was either not processed or that this handler allows bubbling.
* false means the record was either not processed or that this handler allows bubbling.
*/
public function handle(array $record);

@ -27,6 +27,16 @@ use Monolog\Logger;
*/
class HipChatHandler extends SocketHandler
{
/**
* The maximum allowed length for the name used in the "from" field.
*/
const MAXIMUM_NAME_LENGTH = 15;
/**
* The maximum allowed length for the message.
*/
const MAXIMUM_MESSAGE_LENGTH = 9500;
/**
* @var string
*/
@ -47,6 +57,11 @@ class HipChatHandler extends SocketHandler
*/
private $notify;
/**
* @var string
*/
private $format;
/**
* @param string $token HipChat API Token
* @param string $room The room that should be alerted of the message (Id or Name)
@ -55,9 +70,14 @@ class HipChatHandler extends SocketHandler
* @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
* @param Boolean $useSSL Whether to connect via SSL.
* @param string $format The format of the messages (default to text, can be set to html if you have html in the messages)
*/
public function __construct($token, $room, $name = 'Monolog', $notify = false, $level = Logger::CRITICAL, $bubble = true, $useSSL = true)
public function __construct($token, $room, $name = 'Monolog', $notify = false, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $format = 'text')
{
if (!$this->validateStringLength($name, static::MAXIMUM_NAME_LENGTH)) {
throw new \InvalidArgumentException('The supplied name is too long. HipChat\'s v1 API supports names up to 15 UTF-8 characters.');
}
$connectionString = $useSSL ? 'ssl://api.hipchat.com:443' : 'api.hipchat.com:80';
parent::__construct($connectionString, $level, $bubble);
@ -65,6 +85,7 @@ class HipChatHandler extends SocketHandler
$this->name = $name;
$this->notify = $notify;
$this->room = $room;
$this->format = $format;
}
/**
@ -93,7 +114,7 @@ class HipChatHandler extends SocketHandler
'room_id' => $this->room,
'notify' => $this->notify,
'message' => $record['formatted'],
'message_format' => 'text',
'message_format' => $this->format,
'color' => $this->getAlertColor($record['level']),
);
@ -159,13 +180,19 @@ class HipChatHandler extends SocketHandler
return true;
}
$batchRecord = $this->combineRecords($records);
$batchRecords = $this->combineRecords($records);
if (!$this->isHandling($batchRecord)) {
return false;
$handled = false;
foreach ($batchRecords as $batchRecord) {
if ($this->isHandling($batchRecord)) {
$this->write($batchRecord);
$handled = true;
}
}
$this->write($batchRecord);
if (!$handled) {
return false;
}
return false === $this->bubble;
}
@ -180,6 +207,9 @@ class HipChatHandler extends SocketHandler
*/
private function combineRecords($records)
{
$batchRecord = null;
$batchRecords = array();
$batchedMessages = array();
$messages = array();
$formattedMessages = array();
$level = 0;
@ -187,12 +217,7 @@ class HipChatHandler extends SocketHandler
$datetime = null;
foreach ($records as $record) {
$record = $this->processRecord($record);
$record['formatted'] = $this->getFormatter()->format($record);
$messages[] = $record['message'];
$formattedMessages[] = $record['formatted'];
if ($record['level'] > $level) {
$level = $record['level'];
@ -202,18 +227,74 @@ class HipChatHandler extends SocketHandler
if (null === $datetime) {
$datetime = $record['datetime'];
}
$messages[] = $record['message'];
$messgeStr = implode(PHP_EOL, $messages);
$formattedMessages[] = $this->getFormatter()->format($record);
$formattedMessageStr = implode('', $formattedMessages);
$batchRecord = array(
'message' => $messgeStr,
'formatted' => $formattedMessageStr,
'context' => array(),
'extra' => array(),
);
if (!$this->validateStringLength($batchRecord['formatted'], static::MAXIMUM_MESSAGE_LENGTH)) {
// Pop the last message and implode the remainging messages
$lastMessage = array_pop($messages);
$lastFormattedMessage = array_pop($formattedMessages);
$batchRecord['message'] = implode(PHP_EOL, $messages);
$batchRecord['formatted'] = implode('', $formattedMessages);
$batchRecords[] = $batchRecord;
$messages = array($lastMessage);
$formattedMessages = array($lastFormattedMessage);
$batchRecord = null;
}
}
$batchRecord = array(
'message' => implode(PHP_EOL, $messages),
'formatted' => implode('', $formattedMessages),
'level' => $level,
'level_name' => $levelName,
'datetime' => $datetime,
'context' => array(),
'extra' => array(),
);
if (null !== $batchRecord) {
$batchRecords[] = $batchRecord;
}
// Set the max level and datetime for all records
foreach ($batchRecords as &$batchRecord) {
$batchRecord = array_merge(
$batchRecord,
array(
'level' => $level,
'level_name' => $levelName,
'datetime' => $datetime
)
);
}
return $batchRecords;
}
/**
* Validates the length of a string.
*
* If the `mb_strlen()` function is available, it will use that, as HipChat
* allows UTF-8 characters. Otherwise, it will fall back to `strlen()`.
*
* Note that this might cause false failures in the specific case of using
* a valid name with less than 16 characters, but 16 or more bytes, on a
* system where `mb_strlen()` is unavailable.
*
* @param string $str
* @param int $length
*
* @return bool
*/
private function validateStringLength($str, $length)
{
if (function_exists('mb_strlen')) {
return (mb_strlen($str) <= $length);
}
return $batchRecord;
return (strlen($str) <= $length);
}
}

@ -17,16 +17,46 @@ use Monolog\Logger;
* NativeMailerHandler uses the mail() function to send the emails
*
* @author Christophe Coevoet <stof@notk.org>
* @author Mark Garrett <mark@moderndeveloperllc.com>
*/
class NativeMailerHandler extends MailHandler
{
/**
* The email address to which the message is delivered
* @var string
*/
protected $to;
/**
* The subject of the email
* @var string
*/
protected $subject;
protected $headers = array(
'Content-type: text/plain; charset=utf-8'
);
/**
* Optional headers for the message
* @var array
*/
protected $headers = array();
/**
* The wordwrap length for the message
* @var integer
*/
protected $maxColumnWidth;
/**
* The Content-type for the message
* @var string
*/
protected $contentType = 'text/plain';
/**
* The encoding for the message
* @var string
*/
protected $encoding = 'utf-8';
/**
* @param string|array $to The receiver of the mail
* @param string $subject The subject of the mail
@ -45,7 +75,10 @@ class NativeMailerHandler extends MailHandler
}
/**
* @param string|array $headers Custom added headers
* Add headers to the message
*
* @param string|array $headers Custom added headers
* @return null
*/
public function addHeader($headers)
{
@ -63,9 +96,52 @@ class NativeMailerHandler extends MailHandler
protected function send($content, array $records)
{
$content = wordwrap($content, $this->maxColumnWidth);
$headers = implode("\r\n", $this->headers) . "\r\n";
$headers = ltrim(implode("\r\n", $this->headers) . "\r\n", "\r\n");
$headers .= 'Content-type: ' . $this->getContentType() . '; charset=' . $this->getEncoding() . "\r\n";
if ($this->getContentType() == 'text/html' && false === strpos($headers, 'MIME-Version:')) {
$headers .= 'MIME-Version: 1.0' . "\r\n";
}
foreach ($this->to as $to) {
mail($to, $this->subject, $content, $headers);
}
}
/**
* @return string $contentType
*/
public function getContentType()
{
return $this->contentType;
}
/**
* @return string $encoding
*/
public function getEncoding()
{
return $this->encoding;
}
/**
* @param string $contentType The content type of the email - Defaults to text/plain. Use text/html for HTML
* messages.
* @return self
*/
public function setContentType($contentType)
{
$this->contentType = $contentType;
return $this;
}
/**
* @param string $encoding
* @return self
*/
public function setEncoding($encoding)
{
$this->encoding = $encoding;
return $this;
}
}

@ -32,19 +32,19 @@ class PushoverHandler extends SocketHandler
private $emergencyLevel;
/**
* @param string $token Pushover api token
* @param string|array $users Pushover user id or array of ids the message will be sent to
* @param string $title Title sent to the Pushover API
* @param integer $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
* @param Boolean $useSSL Whether to connect via SSL. Required when pushing messages to users that are not
* the pushover.net app owner. OpenSSL is required for this option.
* @param integer $highPriorityLevel The minimum logging level at which this handler will start
* sending "high priority" requests to the Pushover API
* @param integer $emergencyLevel The minimum logging level at which this handler will start
* sending "emergency" requests to the Pushover API
* @param integer $retry The retry parameter specifies how often (in seconds) the Pushover servers will send the same notification to the user.
* @param integer $expire The expire parameter specifies how many seconds your notification will continue to be retried for (every retry seconds).
* @param string $token Pushover api token
* @param string|array $users Pushover user id or array of ids the message will be sent to
* @param string $title Title sent to the Pushover API
* @param integer $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
* @param Boolean $useSSL Whether to connect via SSL. Required when pushing messages to users that are not
* the pushover.net app owner. OpenSSL is required for this option.
* @param integer $highPriorityLevel The minimum logging level at which this handler will start
* sending "high priority" requests to the Pushover API
* @param integer $emergencyLevel The minimum logging level at which this handler will start
* sending "emergency" requests to the Pushover API
* @param integer $retry The retry parameter specifies how often (in seconds) the Pushover servers will send the same notification to the user.
* @param integer $expire The expire parameter specifies how many seconds your notification will continue to be retried for (every retry seconds).
*/
public function __construct($token, $users, $title = null, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $highPriorityLevel = Logger::CRITICAL, $emergencyLevel = Logger::EMERGENCY, $retry = 30, $expire = 25200)
{

@ -14,7 +14,6 @@ namespace Monolog\Handler;
use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\Logger;
use Monolog\Handler\AbstractProcessingHandler;
use Raven_Client;
/**
@ -69,7 +68,7 @@ class RavenHandler extends AbstractProcessingHandler
$level = $this->level;
// filter records based on their level
$records = array_filter($records, function($record) use ($level) {
$records = array_filter($records, function ($record) use ($level) {
return $record['level'] >= $level;
});
@ -78,7 +77,7 @@ class RavenHandler extends AbstractProcessingHandler
}
// the record with the highest severity is the "main" one
$record = array_reduce($records, function($highest, $record) {
$record = array_reduce($records, function ($highest, $record) {
if ($record['level'] >= $highest['level']) {
return $record;
}

@ -105,7 +105,7 @@ class RotatingFileHandler extends StreamHandler
}
// Sorting the files by name to remove the older ones
usort($logFiles, function($a, $b) {
usort($logFiles, function ($a, $b) {
return strcmp($b, $a);
});

@ -73,7 +73,8 @@ class StreamHandler extends AbstractProcessingHandler
fwrite($this->stream, (string) $record['formatted']);
}
private function customErrorHandler($code, $msg) {
private function customErrorHandler($code, $msg)
{
$this->errorMessage = preg_replace('{^fopen\(.*?\): }', '', $msg);
}
}

@ -107,12 +107,15 @@ class Logger implements LoggerInterface
*/
protected static $timezone;
/**
* @var string
*/
protected $name;
/**
* The handler stack
*
* @var array of Monolog\Handler\HandlerInterface
* @var HandlerInterface[]
*/
protected $handlers;
@ -121,14 +124,14 @@ class Logger implements LoggerInterface
*
* To process records of a single handler instead, add the processor on that specific handler
*
* @var array of callables
* @var callable[]
*/
protected $processors;
/**
* @param string $name The logging channel
* @param array $handlers Optional stack of handlers, the first one in the array is called first, etc.
* @param array $processors Optional array of processors
* @param string $name The logging channel
* @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc.
* @param callable[] $processors Optional array of processors
*/
public function __construct($name, array $handlers = array(), array $processors = array())
{
@ -341,7 +344,7 @@ class Logger implements LoggerInterface
*/
public function addEmergency($message, array $context = array())
{
return $this->addRecord(static::EMERGENCY, $message, $context);
return $this->addRecord(static::EMERGENCY, $message, $context);
}
/**

@ -28,9 +28,12 @@ class IntrospectionProcessor
{
private $level;
public function __construct($level = Logger::DEBUG)
private $skipClassesPartials;
public function __construct($level = Logger::DEBUG, array $skipClassesPartials = array('Monolog\\'))
{
$this->level = $level;
$this->skipClassesPartials = $skipClassesPartials;
}
/**
@ -52,8 +55,15 @@ class IntrospectionProcessor
array_shift($trace);
$i = 0;
while (isset($trace[$i]['class']) && false !== strpos($trace[$i]['class'], 'Monolog\\')) {
$i++;
while (isset($trace[$i]['class'])) {
foreach ($this->skipClassesPartials as $part) {
if (strpos($trace[$i]['class'], $part) !== false) {
$i++;
continue 2;
}
}
break;
}
// we should have the call source now

@ -1,4 +1,4 @@
Copyright (c) 2004-2013 Fabien Potencier
Copyright (c) 2004-2014 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

@ -17,7 +17,7 @@ use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Process\Exception\RuntimeException;
/**
* Process is a thin wrapper around proc_* functions to ease
* Process is a thin wrapper around proc_* functions to easily
* start independent PHP processes.
*
* @author Fabien Potencier <fabien@symfony.com>
@ -59,8 +59,8 @@ class Process
private $enhanceSigchildCompatibility;
private $process;
private $status = self::STATUS_READY;
private $incrementalOutputOffset;
private $incrementalErrorOutputOffset;
private $incrementalOutputOffset = 0;
private $incrementalErrorOutputOffset = 0;
private $tty;
private $useFileHandles = false;
@ -189,7 +189,8 @@ class Process
*
* @return integer The exit status code
*
* @throws RuntimeException When process can't be launch or is stopped
* @throws RuntimeException When process can't be launched
* @throws RuntimeException When process stopped after receiving signal
*
* @api
*/
@ -220,7 +221,7 @@ class Process
*
* @return Process The process itself
*
* @throws RuntimeException When process can't be launch or is stopped
* @throws RuntimeException When process can't be launched
* @throws RuntimeException When process is already running
*/
public function start($callback = null)
@ -237,7 +238,11 @@ class Process
$commandline = $this->commandline;
if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->enhanceWindowsCompatibility) {
$commandline = 'cmd /V:ON /E:ON /C "'.$commandline.'"';
$commandline = 'cmd /V:ON /E:ON /C "('.$commandline.')"';
foreach ($this->processPipes->getFiles() as $offset => $filename) {
$commandline .= ' '.$offset.'>'.$filename;
}
if (!isset($this->options['bypass_shell'])) {
$this->options['bypass_shell'] = true;
}
@ -251,6 +256,11 @@ class Process
$this->status = self::STATUS_STARTED;
$this->processPipes->unblock();
if ($this->tty) {
return;
}
$this->processPipes->write(false, $this->stdin);
$this->updateStatus(false);
$this->checkTimeout();
@ -266,7 +276,7 @@ class Process
*
* @return Process The new process
*
* @throws RuntimeException When process can't be launch or is stopped
* @throws RuntimeException When process can't be launched
* @throws RuntimeException When process is already running
*
* @see start()
@ -296,9 +306,12 @@ class Process
*
* @throws RuntimeException When process timed out
* @throws RuntimeException When process stopped after receiving signal
* @throws LogicException When process is not yet started
*/
public function wait($callback = null)
{
$this->requireProcessIsStarted(__FUNCTION__);
$this->updateStatus(false);
if (null !== $callback) {
$this->callback = $this->buildCallback($callback);
@ -316,10 +329,6 @@ class Process
}
if ($this->processInformation['signaled']) {
if ($this->isSigchildEnabled()) {
throw new RuntimeException('The process has been signaled.');
}
throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig']));
}
@ -348,6 +357,7 @@ class Process
* Sends a POSIX signal to the process.
*
* @param integer $signal A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php)
*
* @return Process
*
* @throws LogicException In case the process is not running
@ -356,17 +366,7 @@ class Process
*/
public function signal($signal)
{
if (!$this->isRunning()) {
throw new LogicException('Can not send signal on a non running process.');
}
if ($this->isSigchildEnabled()) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
}
if (true !== @proc_terminate($this->process, $signal)) {
throw new RuntimeException(sprintf('Error while sending signal `%d`.', $signal));
}
$this->doSignal($signal, true);
return $this;
}
@ -376,10 +376,14 @@ class Process
*
* @return string The process output
*
* @throws LogicException In case the process is not started
*
* @api
*/
public function getOutput()
{
$this->requireProcessIsStarted(__FUNCTION__);
$this->readPipes(false, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true);
return $this->stdout;
@ -391,10 +395,14 @@ class Process
* In comparison with the getOutput method which always return the whole
* output, this one returns the new output since the last call.
*
* @throws LogicException In case the process is not started
*
* @return string The process output since the last call
*/
public function getIncrementalOutput()
{
$this->requireProcessIsStarted(__FUNCTION__);
$data = $this->getOutput();
$latest = substr($data, $this->incrementalOutputOffset);
@ -421,10 +429,14 @@ class Process
*
* @return string The process error output
*
* @throws LogicException In case the process is not started
*
* @api
*/
public function getErrorOutput()
{
$this->requireProcessIsStarted(__FUNCTION__);
$this->readPipes(false, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true);
return $this->stderr;
@ -437,10 +449,14 @@ class Process
* whole error output, this one returns the new error output since the last
* call.
*
* @throws LogicException In case the process is not started
*
* @return string The process error output since the last call
*/
public function getIncrementalErrorOutput()
{
$this->requireProcessIsStarted(__FUNCTION__);
$data = $this->getErrorOutput();
$latest = substr($data, $this->incrementalErrorOutputOffset);
@ -465,7 +481,7 @@ class Process
/**
* Returns the exit code returned by the process.
*
* @return integer The exit status code
* @return null|integer The exit status code, null if the Process is not terminated
*
* @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
*
@ -474,7 +490,7 @@ class Process
public function getExitCode()
{
if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method');
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.');
}
$this->updateStatus(false);
@ -488,14 +504,18 @@ class Process
* This method relies on the Unix exit code status standardization
* and might not be relevant for other operating systems.
*
* @return string A string representation for the exit status code
* @return null|string A string representation for the exit status code, null if the Process is not terminated.
*
* @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
*
* @see http://tldp.org/LDP/abs/html/exitcodes.html
* @see http://en.wikipedia.org/wiki/Unix_signal
*/
public function getExitCodeText()
{
$exitcode = $this->getExitCode();
if (null === $exitcode = $this->getExitCode()) {
return;
}
return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error';
}
@ -520,13 +540,16 @@ class Process
* @return Boolean
*
* @throws RuntimeException In case --enable-sigchild is activated
* @throws LogicException In case the process is not terminated
*
* @api
*/
public function hasBeenSignaled()
{
$this->requireProcessIsTerminated(__FUNCTION__);
if ($this->isSigchildEnabled()) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
}
$this->updateStatus(false);
@ -542,13 +565,16 @@ class Process
* @return integer
*
* @throws RuntimeException In case --enable-sigchild is activated
* @throws LogicException In case the process is not terminated
*
* @api
*/
public function getTermSignal()
{
$this->requireProcessIsTerminated(__FUNCTION__);
if ($this->isSigchildEnabled()) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
}
$this->updateStatus(false);
@ -563,10 +589,14 @@ class Process
*
* @return Boolean
*
* @throws LogicException In case the process is not terminated
*
* @api
*/
public function hasBeenStopped()
{
$this->requireProcessIsTerminated(__FUNCTION__);
$this->updateStatus(false);
return $this->processInformation['stopped'];
@ -579,10 +609,14 @@ class Process
*
* @return integer
*
* @throws LogicException In case the process is not terminated
*
* @api
*/
public function getStopSignal()
{
$this->requireProcessIsTerminated(__FUNCTION__);
$this->updateStatus(false);
return $this->processInformation['stopsig'];
@ -654,6 +688,12 @@ class Process
{
$timeoutMicro = microtime(true) + $timeout;
if ($this->isRunning()) {
if (defined('PHP_WINDOWS_VERSION_BUILD') && !$this->isSigchildEnabled()) {
exec(sprintf("taskkill /F /T /PID %d 2>&1", $this->getPid()), $output, $exitCode);
if ($exitCode > 0) {
throw new RuntimeException('Unable to kill the process');
}
}
proc_terminate($this->process);
do {
usleep(1000);
@ -661,7 +701,11 @@ class Process
if ($this->isRunning() && !$this->isSigchildEnabled()) {
if (null !== $signal || defined('SIGKILL')) {
$this->signal($signal ?: SIGKILL);
// avoid exception here :
// process is supposed to be running, but it might have stop
// just after this line.
// in any case, let's silently discard the error, we can not do anything
$this->doSignal($signal ?: SIGKILL, false);
}
}
}
@ -671,8 +715,6 @@ class Process
$this->close();
}
$this->status = self::STATUS_TERMINATED;
return $this->exitcode;
}
@ -723,7 +765,7 @@ class Process
}
/**
* Gets the process timeout.
* Gets the process timeout (max. runtime).
*
* @return float|null The timeout in seconds or null if it's disabled
*/
@ -733,9 +775,9 @@ class Process
}
/**
* Gets the process idle timeout.
* Gets the process idle timeout (max. time since last output).
*
* @return float|null
* @return float|null The timeout in seconds or null if it's disabled
*/
public function getIdleTimeout()
{
@ -743,7 +785,7 @@ class Process
}
/**
* Sets the process timeout.
* Sets the process timeout (max. runtime).
*
* To disable the timeout, set this value to null.
*
@ -761,9 +803,11 @@ class Process
}
/**
* Sets the process idle timeout.
* Sets the process idle timeout (max. time since last output).
*
* @param integer|float|null $timeout
* To disable the timeout, set this value to null.
*
* @param integer|float|null $timeout The timeout in seconds
*
* @return self The current Process instance.
*
@ -791,7 +835,7 @@ class Process
}
/**
* Checks if the TTY mode is enabled.
* Checks if the TTY mode is enabled.
*
* @return Boolean true if the TTY mode is enabled, false otherwise
*/
@ -856,7 +900,9 @@ class Process
public function setEnv(array $env)
{
// Process can not handle env values that are arrays
$env = array_filter($env, function ($value) { if (!is_array($value)) { return true; } });
$env = array_filter($env, function ($value) {
return !is_array($value);
});
$this->env = array();
foreach ($env as $key => $value) {
@ -978,13 +1024,17 @@ class Process
*/
public function checkTimeout()
{
if ($this->status !== self::STATUS_STARTED) {
return;
}
if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) {
$this->stop(0);
throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_GENERAL);
}
if (0 < $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) {
if (null !== $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) {
$this->stop(0);
throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_IDLE);
@ -998,7 +1048,7 @@ class Process
*/
private function getDescriptors()
{
$this->processPipes = new ProcessPipes($this->useFileHandles);
$this->processPipes = new ProcessPipes($this->useFileHandles, $this->tty);
$descriptors = $this->processPipes->getDescriptors();
if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
@ -1044,7 +1094,7 @@ class Process
/**
* Updates the status of the process, reads pipes.
*
* @param Boolean $blocking Whether to use a clocking read call.
* @param Boolean $blocking Whether to use a blocking read call.
*/
protected function updateStatus($blocking)
{
@ -1059,7 +1109,6 @@ class Process
if (!$this->processInformation['running']) {
$this->close();
$this->status = self::STATUS_TERMINATED;
}
}
@ -1074,6 +1123,10 @@ class Process
return self::$sigchild;
}
if (!function_exists('phpinfo')) {
return self::$sigchild = false;
}
ob_start();
phpinfo(INFO_GENERAL);
@ -1104,6 +1157,7 @@ class Process
* Reads pipes, executes callback.
*
* @param Boolean $blocking Whether to use blocking calls or not.
* @param Boolean $close Whether to close file handles or not.
*/
private function readPipes($blocking, $close)
{
@ -1139,17 +1193,17 @@ class Process
*/
private function close()
{
$exitcode = -1;
$this->processPipes->close();
if (is_resource($this->process)) {
$exitcode = proc_close($this->process);
} else {
$exitcode = -1;
}
$this->exitcode = $this->exitcode !== null ? $this->exitcode : -1;
$this->exitcode = -1 != $exitcode ? $exitcode : $this->exitcode;
$this->exitcode = -1 !== $exitcode ? $exitcode : (null !== $this->exitcode ? $this->exitcode : -1);
$this->status = self::STATUS_TERMINATED;
if (-1 == $this->exitcode && null !== $this->fallbackExitcode) {
if (-1 === $this->exitcode && null !== $this->fallbackExitcode) {
$this->exitcode = $this->fallbackExitcode;
} elseif (-1 === $this->exitcode && $this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) {
// if process has been signaled, no exitcode but a valid termsig, apply Unix convention
@ -1176,4 +1230,73 @@ class Process
$this->incrementalOutputOffset = 0;
$this->incrementalErrorOutputOffset = 0;
}
/**
* Sends a POSIX signal to the process.
*
* @param integer $signal A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php)
* @param Boolean $throwException Whether to throw exception in case signal failed
*
* @return Boolean True if the signal was sent successfully, false otherwise
*
* @throws LogicException In case the process is not running
* @throws RuntimeException In case --enable-sigchild is activated
* @throws RuntimeException In case of failure
*/
private function doSignal($signal, $throwException)
{
if (!$this->isRunning()) {
if ($throwException) {
throw new LogicException('Can not send signal on a non running process.');
}
return false;
}
if ($this->isSigchildEnabled()) {
if ($throwException) {
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
}
return false;
}
if (true !== @proc_terminate($this->process, $signal)) {
if ($throwException) {
throw new RuntimeException(sprintf('Error while sending signal `%s`.', $signal));
}
return false;
}
return true;
}
/**
* Ensures the process is running or terminated, throws a LogicException if the process has a not started.
*
* @param $functionName The function name that was called.
*
* @throws LogicException If the process has not run.
*/
private function requireProcessIsStarted($functionName)
{
if (!$this->isStarted()) {
throw new LogicException(sprintf('Process must be started before calling %s.', $functionName));
}
}
/**
* Ensures the process is terminated, throws a LogicException if the process has a status different than `terminated`.
*
* @param $functionName The function name that was called.
*
* @throws LogicException If the process is not yet terminated.
*/
private function requireProcessIsTerminated($functionName)
{
if (!$this->isTerminated()) {
throw new LogicException(sprintf('Process must be terminated before calling %s.', $functionName));
}
}
}

@ -46,19 +46,34 @@ class ProcessUtils
}
$escapedArgument = '';
foreach (preg_split('/([%"])/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
$quote = false;
foreach (preg_split('/(")/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
if ('"' === $part) {
$escapedArgument .= '\\"';
} elseif ('%' === $part) {
$escapedArgument .= '^%';
} elseif (self::isSurroundedBy($part, '%')) {
// Avoid environment variable expansion
$escapedArgument .= '^%"'.substr($part, 1, -1).'"^%';
} else {
$escapedArgument .= escapeshellarg($part);
// escape trailing backslash
if ('\\' === substr($part, -1)) {
$part .= '\\';
}
$quote = true;
$escapedArgument .= $part;
}
}
if ($quote) {
$escapedArgument = '"'.$escapedArgument.'"';
}
return $escapedArgument;
}
return escapeshellarg($argument);
}
private static function isSurroundedBy($arg, $char)
{
return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1];
}
}

@ -14,6 +14,7 @@ namespace Symfony\Component\Process\Tests;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\RuntimeException;
use Symfony\Component\Process\ProcessPipes;
/**
* @author Robert Schönthal <seroscho@googlemail.com>
@ -81,6 +82,34 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
$this->assertLessThan(1.8, $duration);
}
public function testAllOutputIsActuallyReadOnTermination()
{
// this code will result in a maximum of 2 reads of 8192 bytes by calling
// start() and isRunning(). by the time getOutput() is called the process
// has terminated so the internal pipes array is already empty. normally
// the call to start() will not read any data as the process will not have
// generated output, but this is non-deterministic so we must count it as
// a possibility. therefore we need 2 * ProcessPipes::CHUNK_SIZE plus
// another byte which will never be read.
$expectedOutputSize = ProcessPipes::CHUNK_SIZE * 2 + 2;
$code = sprintf('echo str_repeat(\'*\', %d);', $expectedOutputSize);
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg($code)));
$p->start();
// Let's wait enough time for process to finish...
// Here we don't call Process::run or Process::wait to avoid any read of pipes
usleep(500000);
if ($p->isRunning()) {
$this->markTestSkipped('Process execution did not complete in the required time frame');
}
$o = $p->getOutput();
$this->assertEquals($expectedOutputSize, strlen($o));
}
public function testCallbacksAreExecutedWithStart()
{
$data = '';
@ -167,7 +196,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testGetErrorOutput()
{
$p = new Process(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
$p->run();
$this->assertEquals(3, preg_match_all('/ERROR/', $p->getErrorOutput(), $matches));
@ -175,7 +204,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testGetIncrementalErrorOutput()
{
$p = new Process(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { usleep(50000); file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { usleep(100000); file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
$p->start();
while ($p->isRunning()) {
@ -186,7 +215,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testFlushErrorOutput()
{
$p = new Process(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
$p->run();
$p->clearErrorOutput();
@ -195,7 +224,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testGetOutput()
{
$p = new Process(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++; usleep(500); }')));
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++; usleep(500); }')));
$p->run();
$this->assertEquals(3, preg_match_all('/foo/', $p->getOutput(), $matches));
@ -203,7 +232,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testGetIncrementalOutput()
{
$p = new Process(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) { echo \' foo \'; usleep(50000); $n++; }')));
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) { echo \' foo \'; usleep(50000); $n++; }')));
$p->start();
while ($p->isRunning()) {
@ -214,7 +243,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testFlushOutput()
{
$p = new Process(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++;}')));
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++;}')));
$p->run();
$p->clearOutput();
@ -240,11 +269,32 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
$this->markTestSkipped('Windows does have /dev/tty support');
}
$process = $this->getProcess('echo "foo" >> /dev/null && php -r "usleep(100000);"');
$process->setTTY(true);
$process->start();
$this->assertTrue($process->isRunning());
$process->wait();
$this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
}
public function testTTYCommandExitCode()
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->markTestSkipped('Windows does have /dev/tty support');
}
$process = $this->getProcess('echo "foo" >> /dev/null');
$process->setTTY(true);
$process->run();
$this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
$this->assertTrue($process->isSuccessful());
}
public function testExitCodeTextIsNullWhenExitCodeIsNull()
{
$process = $this->getProcess('');
$this->assertNull($process->getExitCodeText());
}
public function testExitCodeText()
@ -260,11 +310,12 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testStartIsNonBlocking()
{
$process = $this->getProcess('php -r "sleep(4);"');
$process = $this->getProcess('php -r "usleep(500000);"');
$start = microtime(true);
$process->start();
$end = microtime(true);
$this->assertLessThan(1 , $end-$start);
$this->assertLessThan(0.2, $end-$start);
$process->wait();
}
public function testUpdateStatus()
@ -299,7 +350,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
{
$process = $this->getProcess('php -m');
$process->run();
$this->assertEquals(0, $process->getExitCode());
$this->assertSame(0, $process->getExitCode());
}
public function testStatus()
@ -351,10 +402,10 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testIsNotSuccessful()
{
$process = $this->getProcess('php -r "sleep(4);"');
$process = $this->getProcess('php -r "usleep(500000);throw new \Exception(\'BOUM\');"');
$process->start();
$this->assertTrue($process->isRunning());
$process->stop();
$process->wait();
$this->assertFalse($process->isSuccessful());
}
@ -445,7 +496,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
$process1->run();
$process2 = $process1->restart();
usleep(300000); // wait for output
$process2->wait(); // wait for output
// Ensure that both processed finished and the output is numeric
$this->assertFalse($process1->isRunning());
@ -472,7 +523,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testRunProcessWithTimeout()
{
$timeout = 0.5;
$process = $this->getProcess('php -r "sleep(3);"');
$process = $this->getProcess('php -r "usleep(600000);"');
$process->setTimeout($timeout);
$start = microtime(true);
try {
@ -486,6 +537,19 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
$this->assertLessThan($timeout + Process::TIMEOUT_PRECISION, $duration);
}
public function testCheckTimeoutOnNonStartedProcess()
{
$process = $this->getProcess('php -r "sleep(3);"');
$process->checkTimeout();
}
public function testCheckTimeoutOnTerminatedProcess()
{
$process = $this->getProcess('php -v');
$process->run();
$process->checkTimeout();
}
public function testCheckTimeoutOnStartedProcess()
{
$timeout = 0.5;
@ -552,7 +616,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testStartAfterATimeout()
{
$process = $this->getProcess('php -r "while (true) {echo \'\'; usleep(1000); }"');
$process = $this->getProcess('php -r "$n = 1000; while ($n--) {echo \'\'; usleep(1000); }"');
$process->setTimeout(0.1);
try {
$process->run();
@ -567,10 +631,10 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testGetPid()
{
$process = $this->getProcess('php -r "sleep(1);"');
$process = $this->getProcess('php -r "usleep(500000);"');
$process->start();
$this->assertGreaterThan(0, $process->getPid());
$process->stop();
$process->wait();
}
public function testGetPidIsNullBeforeStart()
@ -630,6 +694,55 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
$process->signal(SIGHUP);
}
/**
* @dataProvider provideMethodsThatNeedARunningProcess
*/
public function testMethodsThatNeedARunningProcess($method)
{
$process = $this->getProcess('php -m');
$this->setExpectedException('Symfony\Component\Process\Exception\LogicException', sprintf('Process must be started before calling %s.', $method));
call_user_func(array($process, $method));
}
public function provideMethodsThatNeedARunningProcess()
{
return array(
array('getOutput'),
array('getIncrementalOutput'),
array('getErrorOutput'),
array('getIncrementalErrorOutput'),
array('wait'),
);
}
/**
* @dataProvider provideMethodsThatNeedATerminatedProcess
*/
public function testMethodsThatNeedATerminatedProcess($method)
{
$process = $this->getProcess('php -r "sleep(1);"');
$process->start();
try {
call_user_func(array($process, $method));
$process->stop(0);
$this->fail('A LogicException must have been thrown');
} catch (\Exception $e) {
$this->assertInstanceOf('Symfony\Component\Process\Exception\LogicException', $e);
$this->assertEquals(sprintf('Process must be terminated before calling %s.', $method), $e->getMessage());
}
$process->stop(0);
}
public function provideMethodsThatNeedATerminatedProcess()
{
return array(
array('hasBeenSignaled'),
array('getTermSignal'),
array('hasBeenStopped'),
array('getStopSignal'),
);
}
private function verifyPosixIsEnabled()
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {

@ -25,7 +25,7 @@ function handleSignal($signal)
echo "received signal $name\n";
}
declare(ticks=1);
declare(ticks = 1);
pcntl_signal(SIGTERM, 'handleSignal');
pcntl_signal(SIGINT, 'handleSignal');

@ -138,13 +138,13 @@ class ProcessBuilderTest extends \PHPUnit_Framework_TestCase
public function testShouldEscapeArguments()
{
$pb = new ProcessBuilder(array('%path%', 'foo " bar'));
$pb = new ProcessBuilder(array('%path%', 'foo " bar', '%baz%baz'));
$proc = $pb->getProcess();
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->assertSame('^%"path"^% "foo "\\"" bar"', $proc->getCommandLine());
$this->assertSame('^%"path"^% "foo \\" bar" "%baz%baz"', $proc->getCommandLine());
} else {
$this->assertSame("'%path%' 'foo \" bar'", $proc->getCommandLine());
$this->assertSame("'%path%' 'foo \" bar' '%baz%baz'", $proc->getCommandLine());
}
}

@ -27,18 +27,22 @@ class ProcessUtilsTest extends \PHPUnit_Framework_TestCase
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
return array(
array('"\"php\" \"-v\""', '"php" "-v"'),
array('"foo bar"', 'foo bar'),
array('^%"path"^%', '%path%'),
array('"<|>"\\"" "\\""\'f"', '<|>" "\'f'),
array('"<|>\\" \\"\'f"', '<|>" "\'f'),
array('""', ''),
array('"with\trailingbs\\\\"', 'with\trailingbs\\'),
);
}
return array(
array("'\"php\" \"-v\"'", '"php" "-v"'),
array("'foo bar'", 'foo bar'),
array("'%path%'", '%path%'),
array("'<|>\" \"'\\''f'", '<|>" "\'f'),
array("''", ''),
array("'with\\trailingbs\\'", 'with\trailingbs\\'),
);
}
}

@ -15,6 +15,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
{
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
*/
public function testGetExitCode()
{
@ -23,6 +24,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
*/
public function testGetExitCodeIsNullOnStart()
{
@ -31,6 +33,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
*/
public function testGetExitCodeIsNullOnWhenStartingAgain()
{
@ -39,6 +42,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
*/
public function testExitCodeCommandFailed()
{
@ -47,6 +51,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
*/
public function testProcessIsSignaledIfStopped()
{
@ -55,6 +60,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
*/
public function testProcessWithTermSignal()
{
@ -63,6 +69,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
*/
public function testProcessIsNotSignaled()
{
@ -71,6 +78,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
*/
public function testProcessWithoutTermSignal()
{
@ -79,6 +87,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
*/
public function testCheckTimeoutOnStartedProcess()
{
@ -87,6 +96,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.
*/
public function testGetPid()
{
@ -95,6 +105,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.
*/
public function testGetPidIsNullBeforeStart()
{
@ -103,6 +114,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.
*/
public function testGetPidIsNullAfterRun()
{
@ -111,6 +123,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
*/
public function testExitCodeText()
{
@ -122,6 +135,16 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
*/
public function testExitCodeTextIsNullWhenExitCodeIsNull()
{
parent::testExitCodeTextIsNullWhenExitCodeIsNull();
}
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
*/
public function testIsSuccessful()
{
@ -130,6 +153,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
*/
public function testIsSuccessfulOnlyAfterTerminated()
{
@ -138,6 +162,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
*/
public function testIsNotSuccessful()
{
@ -146,6 +171,16 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
*/
public function testTTYCommandExitCode()
{
parent::testTTYCommandExitCode();
}
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process can not be signaled.
*/
public function testSignal()
{
@ -154,6 +189,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
*/
public function testProcessWithoutTermSignalIsNotSignaled()
{

@ -15,6 +15,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
{
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
*/
public function testProcessIsSignaledIfStopped()
{
@ -23,6 +24,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
*/
public function testProcessWithTermSignal()
{
@ -31,6 +33,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
*/
public function testProcessIsNotSignaled()
{
@ -39,6 +42,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
*/
public function testProcessWithoutTermSignal()
{
@ -47,6 +51,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.
*/
public function testGetPid()
{
@ -55,6 +60,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.
*/
public function testGetPidIsNullBeforeStart()
{
@ -63,6 +69,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.
*/
public function testGetPidIsNullAfterRun()
{
@ -79,6 +86,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process can not be signaled.
*/
public function testSignal()
{
@ -87,6 +95,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
*/
public function testProcessWithoutTermSignalIsNotSignaled()
{
@ -103,6 +112,14 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
$this->markTestSkipped('Signal is not supported in sigchild environment');
}
public function testStartAfterATimeout()
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->markTestSkipped('Restarting a timed-out process on Windows is not supported in sigchild environment');
}
parent::testStartAfterATimeout();
}
/**
* {@inheritdoc}
*/

@ -27,106 +27,123 @@ class SimpleProcessTest extends AbstractProcessTest
public function testGetExitCode()
{
$this->skipIfPHPSigchild();
$this->skipIfPHPSigchild(); // This test use exitcode that is not available in this case
parent::testGetExitCode();
}
public function testExitCodeCommandFailed()
{
$this->skipIfPHPSigchild();
$this->skipIfPHPSigchild(); // This test use exitcode that is not available in this case
parent::testExitCodeCommandFailed();
}
public function testProcessIsSignaledIfStopped()
{
$this->skipIfPHPSigchild();
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
parent::testProcessIsSignaledIfStopped();
}
public function testProcessWithTermSignal()
{
$this->skipIfPHPSigchild();
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
parent::testProcessWithTermSignal();
}
public function testProcessIsNotSignaled()
{
$this->skipIfPHPSigchild();
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
parent::testProcessIsNotSignaled();
}
public function testProcessWithoutTermSignal()
{
$this->skipIfPHPSigchild();
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
parent::testProcessWithoutTermSignal();
}
public function testExitCodeText()
{
$this->skipIfPHPSigchild();
$this->skipIfPHPSigchild(); // This test use exitcode that is not available in this case
parent::testExitCodeText();
}
public function testIsSuccessful()
{
$this->skipIfPHPSigchild();
$this->skipIfPHPSigchild(); // This test use PID that is not available in this case
parent::testIsSuccessful();
}
public function testIsNotSuccessful()
{
$this->skipIfPHPSigchild();
$this->skipIfPHPSigchild(); // This test use PID that is not available in this case
parent::testIsNotSuccessful();
}
public function testGetPid()
{
$this->skipIfPHPSigchild();
$this->skipIfPHPSigchild(); // This test use PID that is not available in this case
parent::testGetPid();
}
public function testGetPidIsNullBeforeStart()
{
$this->skipIfPHPSigchild();
$this->skipIfPHPSigchild(); // This test use PID that is not available in this case
parent::testGetPidIsNullBeforeStart();
}
public function testGetPidIsNullAfterRun()
{
$this->skipIfPHPSigchild();
$this->skipIfPHPSigchild(); // This test use PID that is not available in this case
parent::testGetPidIsNullAfterRun();
}
public function testSignal()
{
$this->skipIfPHPSigchild();
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
parent::testSignal();
}
/**
* @expectedException \Symfony\Component\Process\Exception\LogicException
*/
public function testProcessWithoutTermSignalIsNotSignaled()
{
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
parent::testProcessWithoutTermSignalIsNotSignaled();
}
public function testProcessThrowsExceptionWhenExternallySignaled()
{
$this->skipIfPHPSigchild(); // This test use PID that is not available in this case
parent::testProcessThrowsExceptionWhenExternallySignaled();
}
public function testExitCodeIsAvailableAfterSignal()
{
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
parent::testExitCodeIsAvailableAfterSignal();
}
public function testSignalProcessNotRunning()
{
$this->skipIfPHPSigchild();
$this->setExpectedException('Symfony\Component\Process\Exception\LogicException', 'Can not send signal on a non running process.');
parent::testSignalProcessNotRunning();
}
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
*/
public function testSignalWithWrongIntSignal()
{
$this->skipIfPHPSigchild();
if ($this->enabledSigchild) {
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
} else {
$this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'Error while sending signal `-4`.');
}
parent::testSignalWithWrongIntSignal();
}
/**
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
*/
public function testSignalWithWrongNonIntSignal()
{
$this->skipIfPHPSigchild();
if ($this->enabledSigchild) {
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
} else {
$this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'Error while sending signal `Céphalopodes`.');
}
parent::testSignalWithWrongNonIntSignal();
}
@ -144,4 +161,11 @@ class SimpleProcessTest extends AbstractProcessTest
$this->markTestSkipped('Your PHP has been compiled with --enable-sigchild, this test can not be executed');
}
}
private function expectExceptionIfPHPSigchild($classname, $message)
{
if ($this->enabledSigchild) {
$this->setExpectedException($classname, $message);
}
}
}

Loading…
Cancel
Save