Merge pull request #875 from twetech/0.1.8.2

0.1.8.2 (Scheduled tickets, and Email Improvements)
This commit is contained in:
Johnny 2024-02-10 14:30:30 -05:00 committed by GitHub
commit d78b667e0b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 4506 additions and 1350 deletions

View File

@ -25,6 +25,7 @@ require_once "calendar_event_add_modal.php";
require_once "calendar_add_modal.php";
//loop through IDs and create a modal for each
$sql = mysqli_query($mysqli, "SELECT * FROM events LEFT JOIN calendars ON event_calendar_id = calendar_id");
while ($row = mysqli_fetch_array($sql)) {
@ -40,18 +41,16 @@ while ($row = mysqli_fetch_array($sql)) {
$client_id = intval($row['event_client_id']);
require "calendar_event_edit_modal.php";
}
?>
<?php require_once "footer.php";
?>
?>
<script src='plugins/fullcalendar/main.min.js'></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
@ -120,7 +119,32 @@ while ($row = mysqli_fetch_array($sql)) {
$event_start = json_encode($row['ticket_created_at']);
echo "{ id: $event_id, title: $event_title, start: $event_start, color: 'orange', url: 'ticket.php?ticket_id=$event_id' },";
}
//Tickets Scheduled
$sql = mysqli_query($mysqli, "SELECT * FROM clients LEFT JOIN tickets ON client_id = ticket_client_id LEFT JOIN users ON ticket_assigned_to = user_id WHERE ticket_schedule IS NOT NULL");
while ($row = mysqli_fetch_array($sql)) {
$event_id = intval($row['ticket_id']);
if (empty($username)) {
$username = "Unassigned";
} else {
$username = $row['user_name'];
}
if (strtotime($row['ticket_schedule']) < time()) {
if ($row['ticket_status'] == 'Scheduled') {
$event_color = "red";
} else {
$event_color = "green";
}
} else {
$event_color = "grey";
}
$event_title = json_encode($row['ticket_prefix'] . $row['ticket_number'] . " " . $row['ticket_subject'] . " [" . $username . "]");
$event_start = json_encode($row['ticket_schedule']);
echo "{ id: $event_id, title: $event_title, start: $event_start, color: '$event_color', url: 'ticket.php?ticket_id=$event_id' },";
}
//Vendors Added Created
@ -148,13 +172,12 @@ while ($row = mysqli_fetch_array($sql)) {
],
eventClick: function(editEvent) {
$('#editEventModal'+editEvent.event.id).modal();
$('#editEventModal' + editEvent.event.id).modal();
}
});
calendar.render();
});
</script>
<!-- Automatically set new event end date to 1 hr after start date -->
@ -183,4 +206,4 @@ while ($row = mysqli_fetch_array($sql)) {
// Update the end date field
document.getElementById("event_add_end").value = new_end;
}
</script>
</script>

View File

@ -24,7 +24,7 @@ if ($config_enable_cron == 0) {
}
// Check Cron Key
if ( $argv[1] !== $config_cron_key ) {
if ($argv[1] !== $config_cron_key) {
exit("Cron Key invalid -- Quitting..");
}
@ -37,7 +37,7 @@ $lock_file_path = "{$temp_dir}/itflow_mail_queue_{$installation_id}.lock";
// Check for lock file to prevent concurrent script runs
if (file_exists($lock_file_path)) {
$file_age = time() - filemtime($lock_file_path);
// If file is older than 10 minutes (600 seconds), delete and continue
if ($file_age > 600) {
unlink($lock_file_path);
@ -61,7 +61,7 @@ file_put_contents($lock_file_path, "Locked");
// Get Mail Queue that has status of Queued and send it to the function sendSingleEmail() located in functions.php
$sql_queue = mysqli_query($mysqli, "SELECT * FROM email_queue WHERE email_status = 0 AND email_queued_at <= NOW()");
$sql_queue = mysqli_query($mysqli, "SELECT * FROM email_queue WHERE email_status = 0");
if (mysqli_num_rows($sql_queue) > 0) {
while ($row = mysqli_fetch_array($sql_queue)) {
@ -74,6 +74,7 @@ if (mysqli_num_rows($sql_queue) > 0) {
$email_content = $row['email_content'];
$email_queued_at = $row['email_queued_at'];
$email_sent_at = $row['email_sent_at'];
$email_ics_str = $row['email_cal_str'];
// Sanitized Input
$email_recipient_logging = sanitizeInput($row['email_recipient']);
@ -96,7 +97,8 @@ if (mysqli_num_rows($sql_queue) > 0) {
$email_recipient,
$email_recipient_name,
$email_subject,
$email_content
$email_content,
$email_ics_str
);
if ($mail !== true) {
@ -109,7 +111,7 @@ if (mysqli_num_rows($sql_queue) > 0) {
// Update Message
mysqli_query($mysqli, "UPDATE email_queue SET email_status = 3, email_sent_at = NOW(), email_attempts = 1 WHERE email_id = $email_id");
}
}
}
}
}
@ -129,6 +131,7 @@ if (mysqli_num_rows($sql_failed_queue) > 0) {
$email_content = $row['email_content'];
$email_queued_at = $row['email_queued_at'];
$email_sent_at = $row['email_sent_at'];
$email_ics_str = $row['email_cal_str'];
// Increment the attempts
$email_attempts = intval($row['email_attempts']) + 1;
@ -153,7 +156,8 @@ if (mysqli_num_rows($sql_failed_queue) > 0) {
$email_recipient,
$email_recipient_name,
$email_subject,
$email_content
$email_content,
$email_ics_str
);
if ($mail !== true) {
@ -166,7 +170,7 @@ if (mysqli_num_rows($sql_failed_queue) > 0) {
// Update Message
mysqli_query($mysqli, "UPDATE email_queue SET email_status = 3, email_sent_at = NOW(), email_attempts = $email_attempts WHERE email_id = $email_id");
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1559,6 +1559,8 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.0.2'");
}
if (CURRENT_DATABASE_VERSION == '1.0.2') {
//Insert queries here required to update to DB version 1.0.3
mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_stripe_expense_vendor` INT(11) NOT NULL DEFAULT 0 AFTER `config_stripe_account`");
@ -1586,10 +1588,21 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
// Please add this same comment block to the bottom of this file, and update the version number.
// Uncomment Below Lines, to add additional database updates
//
// if (CURRENT_DATABASE_VERSION == '1.0.4') {
// // Insert queries here required to update to DB version 1.0.5
if (CURRENT_DATABASE_VERSION == '1.0.4') {
//Insert queries here required to update to DB version 1.0.5
mysqli_query($mysqli, "ALTER TABLE `tickets` ADD `ticket_schedule` DATETIME DEFAULT NULL AFTER `ticket_billable`");
mysqli_query($mysqli, "ALTER TABLE `tickets` ADD `ticket_onsite` TINYINT(1) NOT NULL DEFAULT 0 AFTER `ticket_schedule`");
mysqli_query($mysqli, "ALTER TABLE `email_queue` ADD `email_cal_str` VARCHAR(1024) DEFAULT NULL AFTER `email_content`");
// Then, update the database to the next sequential version
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.0.5'");
}
// if (CURRENT_DATABASE_VERSION == '1.0.5') {
// // Insert queries here required to update to DB version 1.0.6
// // Then, update the database to the next sequential version
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.0.5'");
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.0.6'");
// }
} else {

View File

@ -5,5 +5,5 @@
* It is used in conjunction with database_updates.php
*/
DEFINE("LATEST_DATABASE_VERSION", "1.0.4");
DEFINE("LATEST_DATABASE_VERSION", "1.0.5");

1
db.sql
View File

@ -1573,6 +1573,7 @@ CREATE TABLE `tickets` (
`ticket_priority` varchar(200) DEFAULT NULL,
`ticket_status` varchar(200) NOT NULL,
`ticket_billable` tinyint(1) NOT NULL DEFAULT 0,
`ticket_schedule` DATETIME DEFAULT NULL,
`ticket_vendor_ticket_number` varchar(255) DEFAULT NULL,
`ticket_feedback` varchar(200) DEFAULT NULL,
`ticket_created_at` datetime NOT NULL DEFAULT current_timestamp(),

File diff suppressed because one or more lines are too long

View File

@ -15,7 +15,7 @@ function populateScheduledTicketEditModal(client_id, ticket_id) {
const assets = response.assets;
// Populate the scheduled ticket modal fields
document.getElementById("editHeader").innerText = " Edit Scheduled ticket: " + ticket.scheduled_ticket_subject;
document.getElementById("editHeader").innerText = " Edit Recurring ticket: " + ticket.scheduled_ticket_subject;
document.getElementById("editTicketId").value = ticket_id;
document.getElementById("editClientId").value = client_id;
document.getElementById("editTicketSubject").value = ticket.scheduled_ticket_subject;

127
plugins/zapcal/README.md Normal file
View File

@ -0,0 +1,127 @@
# Zap Calendar iCalendar Library
(https://github.com/zcontent/icalendar)
The Zap Calendar iCalendar Library is a PHP library for supporting the iCalendar (RFC 5545) standard.
This PHP library is for reading and writing iCalendar formatted feeds and
files. Features of the library include:
- Read AND write support for iCalendar files
- Object based creation and manipulation of iCalendar files
- Supports expansion of RRULE to a list of repeating dates
- Supports adding timezone info to iCalendar file
All iCalendar data is stored in a PHP object tree.
This allows any property to be added to the iCalendar feed without
requiring specialized library function calls.
With power comes responsibility. Missing or invalid properties can cause
the resulting iCalendar file to be invalid. Visit [iCalendar.org](http://icalendar.org) to view valid
properties and test your feed using the site's [iCalendar validator tool](http://icalendar.org/validator.html).
Library API documentation can be found at http://icalendar.org/zapcallibdocs
See the examples folder for programs that read and write iCalendar
files. At its simpliest, you need to include the library at the top of your program:
```php
require_once($path_to_library . "/zapcallib.php");
```
Create an ical object using the ZCiCal object:
```php
$icalobj = new ZCiCal();
```
Add an event object:
```php
$eventobj = new ZCiCalNode("VEVENT", $icalobj->curnode);
```
Add a start and end date to the event:
```php
// add start date
$eventobj->addNode(new ZCiCalDataNode("DTSTART:" . ZCiCal::fromSqlDateTime("2020-01-01 12:00:00")));
// add end date
$eventobj->addNode(new ZCiCalDataNode("DTEND:" . ZCiCal::fromSqlDateTime("2020-01-01 13:00:00")));
```
Write the object in iCalendar format using the export() function call:
```php
echo $icalobj->export();
```
This example will not validate since it is missing some required elements.
Look at the simpleevent.php example for the minimum # of elements
needed for a validated iCalendar file.
To create a multi-event iCalendar file, simply create multiple event objects. For example:
```php
$icalobj = new ZCiCal();
$eventobj1 = new ZCiCalNode("VEVENT", $icalobj->curnode);
$eventobj1->addNode(new ZCiCalDataNode("SUMMARY:Event 1"));
...
$eventobj2 = new ZCiCalNode("VEVENT", $icalobj->curnode);
$eventobj2->addNode(new ZCiCalDataNode("SUMMARY:Event 2"));
...
```
To read an existing iCalendar file/feed, create the ZCiCal object with a string representing the contents of the iCalendar file:
```php
$icalobj = new ZCiCal($icalstring);
```
Large iCalendar files can be read in chunks to reduce the amount of memory needed to hold the iCalendar feed in memory. This example reads 500 events at a time:
```php
$icalobj = null;
$eventcount = 0;
$maxevents = 500;
do
{
$icalobj = newZCiCal($icalstring, $maxevents, $eventcount);
...
$eventcount +=$maxevents;
}
while($icalobj->countEvents() >= $eventcount);
```
You can read the events from an imported (or created) iCalendar object in this manner:
```php
foreach($icalobj->tree->child as $node)
{
if($node->getName() == "VEVENT")
{
foreach($node->data as $key => $value)
{
if($key == "SUMMARY")
{
echo "event title: " . $value->getValues() . "\n";
}
}
}
}
```
## Known Limitations
- Since the library utilizes objects to read and write iCalendar data, the
size of the iCalendar data is limited to the amount of available memory on the machine.
The ZCiCal() object supports reading a range of events to minimize memory
space.
- The library ignores timezone info when importing files, instead utilizing PHP's timezone
library for calculations (timezones are supported when exporting files).
Imported timezones need to be aliased to a [PHP supported timezone](http://php.net/manual/en/timezones.php).
- At this time, the library does not support the "BYSETPOS" option in RRULE items.
- At this time, the maximum date supported is 2036 to avoid date math issues
with 32 bit systems.
- Repeating events are limited to a maximum of 5,000 dates to avoid memory or infinite loop issues

View File

@ -0,0 +1,568 @@
<?php
/**
* date.php - date helper class
*
* @package ZapCalLib
* @author Dan Cogliano <http://zcontent.net>
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
* @link http://icalendar.org/php-library.html
*/
// No direct access
defined('_ZAPCAL') or die( 'Restricted access' );
/**
* Zap Calendar Date Helper Class
*
* Helper class for various date functions
*/
class ZDateHelper {
/**
* Find the number of days in a month
*
* @param int $month Month is between 1 and 12 inclusive
*
* @param int $year is between 1 and 32767 inclusive
*
* @return int
*/
static function DayInMonth($month, $year) {
$daysInMonth = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
if ($month != 2) return $daysInMonth[$month - 1];
return (checkdate($month, 29, $year)) ? 29 : 28;
}
/**
* Is given date today?
*
* @param int $date date in Unix timestamp format
*
* @param int $tzid PHP recognized timezone (default is UTC)
*
* @return bool
*/
static function isToday($date, $tzid = "UTC") {
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return gmdate('Y-m-d', $date) == gmdate('Y-m-d', $now);
}
/**
* Is given date before today?
*
* @param int $date date in Unix timestamp format
*
* @param int $tzid PHP recognized timezone (default is UTC)
*
* @return bool
*/
static function isBeforeToday($date, $tzid = "UTC"){
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return mktime(0,0,0,date('m',$now),date('d',$now),date('Y',$now)) >
mktime(0,0,0,date('m',$date),date('d',$date),date('Y',$now));
}
/**
* Is given date after today?
*
* @param int $date date in Unix timestamp format
*
* @param int $tzid PHP recognized timezone (default is UTC)
*
* @return bool
*/
static function isAfterToday($date, $tzid = "UTC"){
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return mktime(0,0,0,date('m',$now),date('d',$now),date('Y',$now)) <
mktime(0,0,0,date('m',$date),date('d',$date),date('Y',$now));
}
/**
* Is given date tomorrow?
*
* @param int $date date in Unix timestamp format
*
* @param int $tzid PHP recognized timezone (default is UTC)
*
* @return bool
*/
static function isTomorrow($date, $tzid = "UTC") {
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return gmdate('Y-m-d', $date) == gmdate('Y-m-d', $now + 60 * 60 * 24);
}
/**
* Is given date in the future?
*
* This routine differs from isAfterToday() in that isFuture() will
* return true for date-time values later in the same day.
*
* @param int $date date in Unix timestamp format
*
* @param int $tzid PHP recognized timezone (default is UTC)
*
* @return bool
*/
static function isFuture($date, $tzid = "UTC"){
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return $date > $now;
}
/**
* Is given date in the past?
*
* This routine differs from isBeforeToday() in that isPast() will
* return true for date-time values earlier in the same day.
*
* @param int $date date in Unix timestamp format
*
* @param int $tzid PHP recognized timezone (default is UTC)
*
* @return bool
*/
static function isPast($date, $tzid = "UTC") {
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return $date < $now;
}
/**
* Return current Unix timestamp in local timezone
*
* @param string $tzid PHP recognized timezone
*
* @return int
*/
static function now($tzid = "UTC"){
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return $now;
}
/**
* Is given date fall on a weekend?
*
* @param int $date Unix timestamp
*
* @return bool
*/
static function isWeekend($date) {
$dow = gmdate('w',$date);
return $dow == 0 || $dow == 6;
}
/**
* Format Unix timestamp to SQL date-time
*
* @param int $t Unix timestamp
*
* @return string
*/
static function toSqlDateTime($t = 0)
{
date_default_timezone_set('GMT');
if($t == 0)
return gmdate('Y-m-d H:i:s',self::now());
return gmdate('Y-m-d H:i:s', $t);
}
/**
* Format Unix timestamp to SQL date
*
* @param int $t Unix timestamp
*
* @return string
*/
static function toSqlDate($t = 0)
{
date_default_timezone_set('GMT');
if($t == 0)
return gmdate('Y-m-d',self::now());
return gmdate('Y-m-d', $t);
}
/**
* Format iCal date-time string to Unix timestamp
*
* @param string $datetime in iCal time format ( YYYYMMDD or YYYYMMDDTHHMMSS or YYYYMMDDTHHMMSSZ )
*
* @return int Unix timestamp
*/
static function fromiCaltoUnixDateTime($datetime) {
// first check format
$formats = array();
$formats[] = "/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]/";
$formats[] = "/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9]/";
$formats[] = "/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9]Z/";
$ok = false;
foreach($formats as $format){
if(preg_match($format,$datetime)){
$ok = true;
break;
}
}
if(!$ok)
return null;
$year = substr($datetime,0,4);
$month = substr($datetime,4,2);
$day = substr($datetime,6,2);
$hour = 0;
$minute = 0;
$second = 0;
if(strlen($datetime) > 8 && $datetime[8] == "T") {
$hour = substr($datetime,9,2);
$minute = substr($datetime,11,2);
$second = substr($datetime,13,2);
}
return gmmktime($hour, $minute, $second, $month, $day, $year);
}
/**
* Format Unix timestamp to iCal date-time string
*
* @param int $datetime Unix timestamp
*
* @return string
*/
static function fromUnixDateTimetoiCal($datetime){
date_default_timezone_set('GMT');
return gmdate("Ymd\THis",$datetime);
}
/**
* Convert iCal duration string to # of seconds
*
* @param string $duration iCal duration string
*
* return int
*/
static function iCalDurationtoSeconds($duration) {
$secs = 0;
if($duration[0] == "P") {
$duration = str_replace(array("H","M","S","T","D","W","P"),array("H,","M,","S,","","D,","W,",""),$duration);
$dur2 = explode(",",$duration);
foreach($dur2 as $dur){
$val=intval($dur);
if(strlen($dur) > 0){
switch($dur[strlen($dur) - 1]) {
case "H":
$secs += 60*60 * $val;
break;
case "M":
$secs += 60 * $val;
break;
case "S":
$secs += $val;
break;
case "D":
$secs += 60*60*24 * $val;
break;
case "W":
$secs += 60*60*24*7 * $val;
break;
}
}
}
}
return $secs;
}
/**
* Check if day falls within date range
*
* @param int $daystart start of day in Unix timestamp format
*
* @param int $begin Unix timestamp of starting date range
*
* @param int $end Unix timestamp of end date range
*
* @return bool
*/
static function inDay($daystart, $begin, $end)
{
//$dayend = $daystart + 60*60*24 - 60;
// add 1 day to determine end of day
// don't use 24 hours, since twice a year DST Sundays are 23 hours and 25 hours in length
// adding 1 day takes this into account
$dayend = self::addDate($daystart, 0,0,0,0,1,0);
$end = max($begin, $end); // $end can't be less than $begin
$inday =
($daystart <= $begin && $begin < $dayend)
||($daystart < $end && $end < $dayend)
||($begin <= $daystart && $end > $dayend)
;
return $inday;
}
/**
* Convert SQL date or date-time to Unix timestamp
*
* @param string $datetime SQL date or date-time
*
* @return int Unix date-time timestamp
*/
static function toUnixDate($datetime)
{
$year = substr($datetime,0,4);
$month = substr($datetime,5,2);
$day = substr($datetime,8,2);
return mktime(0, 0, 0, $month, $day, $year);
}
/**
* Convert SQL date or date-time to Unix date timestamp
*
* @param string $datetime SQL date or date-time
*
* @return int Unix timestamp
*/
static function toUnixDateTime($datetime)
{
// convert to absolute dates if neccessary
$datetime = self::getAbsDate($datetime);
$year = substr($datetime,0,4);
$month = substr($datetime,5,2);
$day = substr($datetime,8,2);
$hour = 0;
$minute = 0;
$second = 0;
if(strlen($datetime) > 10) {
$hour = substr($datetime,11,2);
$minute = substr($datetime,14,2);
$second = substr($datetime,17,2);
}
return gmmktime($hour, $minute, $second, $month, $day, $year);
}
/**
* Date math: add or substract from current date to get a new date
*
* @param int $date date to add or subtract from
*
* @param int $hour add or subtract hours from date
*
* @param int $min add or subtract minutes from date
*
* @param int $sec add or subtract seconds from date
*
* @param int $month add or subtract months from date
*
* @param int $day add or subtract days from date
*
* @param int $year add or subtract years from date
*
* @param string $tzid PHP recognized timezone (default is UTC)
*/
static function addDate($date, $hour, $min, $sec, $month, $day, $year, $tzid = "UTC") {
date_default_timezone_set($tzid);
$sqldate = self::toSQLDateTime($date);
$tdate = array();
$tdate["year"] = substr($sqldate,0,4);
$tdate["mon"] = substr($sqldate,5,2);
$tdate["mday"] = substr($sqldate,8,2);
$tdate["hours"] = substr($sqldate,11,2);
$tdate["minutes"] = substr($sqldate,14,2);
$tdate["seconds"] = substr($sqldate,17,2);
$newdate=mktime($tdate["hours"] + $hour, $tdate["minutes"] + $min, $tdate["seconds"] + $sec, $tdate["mon"] + $month, $tdate["mday"] + $day, $tdate["year"] + $year);
date_default_timezone_set("UTC");
//echo self::toSQLDateTime($date) . " => " . self::toSQLDateTime($newdate) . " ($hour:$min:$sec $month/$day/$year)<br/>\n";
return $newdate;
}
/**
* Date math: get date from week and day in specifiec month
*
* This routine finds actual dates for the second Tuesday of the month, last Friday of the month, etc.
* For second Tuesday, use $week = 1, $wday = 2
* for last Friday, use $week = -1, $wday = 5
*
* @param int $date Unix timestamp
*
* @param int $week week number, 0 is first week, -1 is last
*
* @param int $wday day of week, 0 is Sunday, 6 is Saturday
*
* @param string $tzid PHP supported timezone
*
* @return int Unix timestamp
*/
static function getDateFromDay($date, $week, $wday,$tzid="UTC") {
//echo "getDateFromDay(" . self::toSqlDateTime($date) . ",$week,$wday)<br/>\n";
// determine first day in month
$tdate = getdate($date);
$monthbegin = gmmktime(0,0,0, $tdate["mon"],1,$tdate["year"]);
$monthend = self::addDate($monthbegin, 0,0,0,1,-1,0,$tzid); // add 1 month and subtract 1 day
$day = self::addDate($date,0,0,0,0,1 - $tdate["mday"],0,$tzid);
$month = array(array());
while($day <= $monthend) {
$tdate=getdate($day);
$month[$tdate["wday"]][]=$day;
//echo self::toSQLDateTime($day) . "<br/>\n";
$day = self::addDate($day, 0,0,0,0,1,0,$tzid); // add 1 day
}
$dayinmonth=0;
if($week >= 0)
$dayinmonth = $month[$wday][$week];
else
$dayinmonth = $month[$wday][count($month[$wday]) - 1];
//echo "return " . self::toSQLDateTime($dayinmonth);
//exit;
return $dayinmonth;
}
/**
* Convert UTC date-time to local date-time
*
* @param string $sqldate SQL date-time string
*
* @param string $tzid PHP recognized timezone (default is "UTC")
*
* @return string SQL date-time string
*/
static function toLocalDateTime($sqldate, $tzid = "UTC" ){
try
{
$timezone = new DateTimeZone($tzid);
}
catch(Exception $e)
{
// bad time zone specified
return $sqldate;
}
$udate = self::toUnixDateTime($sqldate);
$daydatetime = new DateTime("@" . $udate);
$tzoffset = $timezone->getOffset($daydatetime);
return self::toSqlDateTime($udate + $tzoffset);
}
/**
* Convert local date-time to UTC date-time
*
* @param string $sqldate SQL date-time string
*
* @param string $tzid PHP recognized timezone (default is "UTC")
*
* @return string SQL date-time string
*/
static function toUTCDateTime($sqldate, $tzid = "UTC" ){
date_default_timezone_set("UTC");
try
{
$date = new DateTime($sqldate, $tzid);
}
catch(Exception $e)
{
// bad time zone specified
return $sqldate;
}
$offset = $date->getOffsetFromGMT();
if($offset >= 0)
$date->sub(new DateInterval("PT".$offset."S"));
else
$date->add(new DateInterval("PT".abs($offset)."S"));
return $date->toSql(true);
}
/**
* Convert from a relative date to an absolute date
*
* Examples of relative dates are "-2y" for 2 years ago, "18m"
* for 18 months after today. Relative date uses "y", "m" and "d" for
* year, month and day. Relative date can be combined into comma
* separated list, i.e., "-1y,-1d" for 1 year and 1 day ago.
*
* @param string $date relative date string (i.e. "1y" for 1 year from today)
*
* @param string $rdate reference date, or blank for current date (in SQL date-time format)
*
* @return string in SQL date-time format
*/
static function getAbsDate($date,$rdate = ""){
if(str_replace(array("y","m","d","h","n"),"",strtolower($date)) != strtolower($date)){
date_default_timezone_set("UTC");
if($rdate == "")
$udate = time();
else
$udate = self::toUnixDateTime($rdate);
$values=explode(",",strtolower($date));
$y = 0;
$m = 0;
$d = 0;
$h = 0;
$n = 0;
foreach($values as $value){
$rtype = substr($value,strlen($value)-1);
$rvalue = intval(substr($value,0,strlen($value) - 1));
switch($rtype){
case 'y':
$y = $rvalue;
break;
case 'm':
$m = $rvalue;
break;
case 'd':
$d = $rvalue;
break;
case 'h':
$h = $rvalue;
break;
case 'n':
$n = $rvalue;
break;
}
// for "-" values, move to start of day , otherwise, move to end of day
if($rvalue[0] == '-')
$udate = mktime(0,0,0,date('m',$udate),date('d',$udate),date('Y',$udate));
else
$udate = mktime(0,-1,0,date('m',$udate),date('d',$udate)+1,date('Y',$udate));
$udate = self::addDate($udate,$h,$n,0,$m,$d,$y);
}
$date = self::toSqlDateTime($udate);
}
return $date;
}
/**
* Format Unix timestamp to iCal date-time format
*
* @param int $datetime Unix timestamp
*
* @return string iCal date-time string
*/
static function toiCalDateTime($datetime = null){
date_default_timezone_set('UTC');
if($datetime == null)
$datetime = time();
return gmdate("Ymd\THis",$datetime);
}
/**
* Format Unix timestamp to iCal date format
*
* @param int $datetime Unix timestamp
*
* @return string iCal date-time string
*/
static function toiCalDate($datetime = null){
date_default_timezone_set('UTC');
if($datetime == null)
$datetime = time();
return gmdate("Ymd",$datetime);
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* framework.php - framework file
*
* @package ZapCalLib
* @author Dan Cogliano <http://zcontent.net>
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
* @link http://icalendar.org/php-library.html
*/
// No direct access
defined('_ZAPCAL') or die( 'Restricted access' );
/**
* set MAXYEAR to 2036 for 32 bit systems, can be higher for 64 bit systems
*
* @var integer
*/
define('_ZAPCAL_MAXYEAR', 2036);
/**
* set MAXREVENTS to maximum # of repeating events
*
* @var integer
*/
define('_ZAPCAL_MAXREVENTS', 5000);
require_once(_ZAPCAL_BASE . '/includes/date.php');
require_once(_ZAPCAL_BASE . '/includes/recurringdate.php');
require_once(_ZAPCAL_BASE . '/includes/ical.php');
require_once(_ZAPCAL_BASE . '/includes/timezone.php');

View File

@ -0,0 +1,986 @@
<?php
/**
* ical.php create iCalendar data structure
*
* @package ZapCalLib
* @author Dan Cogliano <http://zcontent.net>
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
* @link http://icalendar.org/php-library.html
*/
// No direct access
defined('_ZAPCAL') or die( 'Restricted access' );
/**
* Object for storing an unfolded iCalendar line
*
* The ZCiCalDataNode class contains data from an unfolded iCalendar line
*
*/
class ZCiCalDataNode {
/**
* The name of the node
*
* @var string
*/
var $name = "";
/**
* Node parameters (before the colon ":")
*
* @var array
*/
var $parameter=array();
/**
* Node values (after the colon ":")
*
* @var array
*/
var $value=array();
/**
* Create an object from an unfolded iCalendar line
*
* @param string $line An unfolded iCalendar line
*
* @return void
*
*/
function __construct( $line ) {
//echo "ZCiCalDataNode($line)<br/>\n";
//separate line into parameters and value
// look for colon separating name or parameter and value
// first change any escaped colons temporarily to make it easier
$tline = str_replace("\\:", "`~", $line);
// see if first colon is inside a quoted string
$i = 0;
$datafind = false;
$inquotes = false;
while(!$datafind && ($i < strlen($tline))) {
//echo "$i: " . $tline[$i] . ", ord() = " . ord($tline[$i]) . "<br>\n";
if(!$inquotes && $tline[$i] == ':')
$datafind=true;
else{
$i += 1;
if(substr($tline,$i,1) == '"')
$inquotes = !$inquotes;
}
}
if($datafind){
$value = str_replace("`~","\\:",substr($line,$i+1));
// fix escaped characters (don't see double quotes in spec but Apple apparently uses it in iCal)
$value = str_replace(array('\\N' , '\\n', '\\"' ), array("\n", "\n" , '"'), $value);
$tvalue = str_replace("\\,", "`~", $value);
//echo "value: " . $tvalue . "<br>\n";
$tvalue = explode(",",$tvalue);
$value = str_replace("`~","\\,",$tvalue);
$this->value = $value;
}
$parameter = trim(substr($line,0,$i));
$parameter = str_replace("\\;", "`~", $parameter);
$parameters = explode(";", $parameter);
$parameters = str_replace("`~", "\\;", $parameters);
$this->name = array_shift($parameters);
foreach($parameters as $parameter){
$pos = strpos($parameter,"=");
if($pos > 0){
$param = substr($parameter,0,$pos);
$paramvalue = substr($parameter,$pos+1);
$tvalue = str_replace("\\,", "`~", $paramvalue);
//$tvalue = explode(",",$tvalue);
$paramvalue = str_replace("`~","\\,",$tvalue);
$this->parameter[strtolower($param)] = $paramvalue;
//$this->paramvalue[] = $paramvalue;
}
}
}
/**
* getName()
*
* Return the name of the object
*
* @return string
*/
function getName(){
return $this->name;
}
/**
* Get $ith parameter from array
* @param int $i
*
* @return var
*/
function getParameter($i){
return $this->parameter[$i];
}
/**
* Get parameter array
*
* @return array
*/
function getParameters(){
return $this->parameter;
}
/**
* Get comma separated values
*
* @return string
*/
function getValues(){
return implode(",",$this->value);
}
}
/**
* Object for storing a list of unfolded iCalendar lines (ZCiCalDataNode objects)
*
* @property object $parentnode Parent of this node
*
* @property array $child Array of children for this node
*
* @property data $data Array of data for this node
*
* @property object $next Next sibling of this node
*
* @property object $prev Previous sibling of this node
*/
class ZCiCalNode {
/**
* The name of the node
*
* @var string
*/
var $name="";
/**
* The parent of this node
*
* @var object
*/
var $parentnode=null;
/**
* Array of children for this node
*
* @var array
*/
var $child= array();
/**
* Array of $data for this node
*
* @var array
*/
var $data= array();
/**
* Next sibling of this node
*
* @var object
*/
var $next=null;
/**
* Previous sibling of this node
*
* @var object
*/
var $prev=null;
/**
* Create ZCiCalNode
*
* @param string $_name Name of node
*
* @param object $_parent Parent node for this node
*
* @param bool $first Is this the first child for this parent?
*/
function __construct( $_name, & $_parent, $first = false) {
$this->name = $_name;
$this->parentnode = $_parent;
if($_parent != null){
if(count($this->parentnode->child) > 0) {
if($first)
{
$first = & $this->parentnode->child[0];
$first->prev = & $this;
$this->next = & $first;
}
else
{
$prev =& $this->parentnode->child[count($this->parentnode->child)-1];
$prev->next =& $this;
$this->prev =& $prev;
}
}
if($first)
{
array_unshift($this->parentnode->child, $this);
}
else
{
$this->parentnode->child[] =& $this;
}
}
/*
echo "creating " . $this->getName();
if($_parent != null)
echo " child of " . $_parent->getName() . "/" . count($this->parentnode->child);
echo "<br/>";
*/
}
/**
* Return the name of the object
*
* @return string
*/
function getName() {
return $this->name;
}
/**
* Add node to list
*
* @param object $node
*
*/
function addNode($node) {
if(array_key_exists($node->getName(), $this->data))
{
if(!is_array($this->data[$node->getName()]))
{
$this->data[$node->getName()] = array($this->data[$node->getName()]);
}
$this->data[$node->getName()][] = $node;
}
else
{
$this->data[$node->getName()] = $node;
}
}
/**
* Get Attribute
*
* @param int $i array id of attribute to get
*
* @return string
*/
function getAttrib($i) {
return $this->attrib[$i];
}
/**
* Set Attribute
*
* @param string $value value of attribute to set
*
*/
function setAttrib($value) {
$this->attrib[] = $value;
}
/**
* Get the parent object of this object
*
* @return object parent of this object
*/
function &getParent() {
return $this->parentnode;
}
/**
* Get the first child of this object
*
* @return object The first child
*/
function &getFirstChild(){
static $nullguard = null;
if(count($this->child) > 0) {
//echo "moving from " . $this->getName() . " to " . $this->child[0]->getName() . "<br/>";
return $this->child[0];
}
else
return $nullguard;
}
/**
* Print object tree in HTML for debugging purposes
*
* @param object $node select part of tree to print, or leave blank for full tree
*
* @param int $level Level of recursion (usually leave this blank)
*
* @return string - HTML formatted display of object tree
*/
function printTree(& $node=null, $level=1){
$level += 1;
$html = "";
if($node == null)
$node = $this->parentnode;
if($level > 5)
{
die("levels nested too deep<br/>\n");
//return;
}
for($i = 0 ; $i < $level; $i ++)
$html .= "+";
$html .= $node->getName() . "<br/>\n";
foreach ($node->child as $c){
$html .= $node->printTree($c,$level);
}
$level -= 1;
return $html;
}
/**
* export tree to icalendar format
*
* @param object $node Top level node to export
*
* @param int $level Level of recursion (usually leave this blank)
*
* @return string iCalendar formatted output
*/
function export(& $node=null, $level=0){
$txtstr = "";
if($node == null)
$node = $this;
if($level > 5)
{
//die("levels nested too deep<br/>\n");
throw new Exception("levels nested too deep");
}
$txtstr .= "BEGIN:" . $node->getName() . "\r\n";
if(property_exists($node,"data"))
foreach ($node->data as $d){
if(is_array($d))
{
foreach ($d as $c)
{
//$txtstr .= $node->export($c,$level + 1);
$p = "";
$params = @$c->getParameters();
if(count($params) > 0)
{
foreach($params as $key => $value){
$p .= ";" . strtoupper($key) . "=" . $value;
}
}
$txtstr .= $this->printDataLine($c, $p);
}
}
else
{
$p = "";
$params = @$d->getParameters();
if(count($params) > 0)
{
foreach($params as $key => $value){
$p .= ";" . strtoupper($key) . "=" . $value;
}
}
$txtstr .= $this->printDataLine($d, $p);
/*
$values = $d->getValues();
// don't think we need this, Sunbird does not like it in the EXDATE field
//$values = str_replace(",", "\\,", $values);
$line = $d->getName() . $p . ":" . $values;
$line = str_replace(array("<br>","<BR>","<br/>","<BR/"),"\\n",$line);
$line = str_replace(array("\r\n","\n\r","\n","\r"),'\n',$line);
//$line = str_replace(array(',',';','\\'), array('\\,','\\;','\\\\'),$line);
//$line =strip_tags($line);
$linecount = 0;
while (strlen($line) > 0) {
$linewidth = ($linecount == 0? 75 : 74);
$linesize = (strlen($line) > $linewidth? $linewidth: strlen($line));
if($linecount > 0)
$txtstr .= " ";
$txtstr .= substr($line,0,$linesize) . "\r\n";
$linecount += 1;
$line = substr($line,$linewidth);
}
*/
}
//echo $line . "\n";
}
if(property_exists($node,"child"))
foreach ($node->child as $c){
$txtstr .= $node->export($c,$level + 1);
}
$txtstr .= "END:" . $node->getName() . "\r\n";
return $txtstr;
}
/**
* print an attribute line
* @param object $d attributes
* @param object $p properties
*
*/
function printDataLine($d, $p)
{
$txtstr = "";
$values = $d->getValues();
// don't think we need this, Sunbird does not like it in the EXDATE field
//$values = str_replace(",", "\\,", $values);
$line = $d->getName() . $p . ":" . $values;
$line = str_replace(array("<br>","<BR>","<br/>","<BR/"),"\\n",$line);
$line = str_replace(array("\r\n","\n\r","\n","\r"),'\n',$line);
//$line = str_replace(array(',',';','\\'), array('\\,','\\;','\\\\'),$line);
//$line =strip_tags($line);
$linecount = 0;
while (strlen($line) > 0) {
$linewidth = ($linecount == 0? 75 : 74);
$linesize = (strlen($line) > $linewidth? $linewidth: strlen($line));
if($linecount > 0)
$txtstr .= " ";
$txtstr .= substr($line,0,$linesize) . "\r\n";
$linecount += 1;
$line = substr($line,$linewidth);
}
return $txtstr;
}
}
/**
*
* The main iCalendar object containing ZCiCalDataNodes and ZCiCalNodes.
*
*/
class ZCiCal {
/**
* The root node of the object tree
*
* @var object
*/
var $tree=null;
/**
* The most recently created node in the tree
*
* @var object
*/
var $curnode=null;
/**
* The main iCalendar object containing ZCiCalDataNodes and ZCiCalNodes.
*
* use maxevents and startevent to read events in multiple passes (to save memory)
*
* @param string $data icalendar feed string (empty if creating new feed)
*
* @param int $maxevents maximum # of events to read
*
* @param int $startevent starting event to read
*
* @return void
*
*
*/
function __construct($data = "", $maxevents = 1000000, $startevent = 0) {
if($data != ""){
// unfold lines
// first change all eol chars to "\n"
$data = str_replace(array("\r\n", "\n\r", "\n", "\r"), "\n", $data);
// now unfold lines
//$data = str_replace(array("\n ", "\n "),"!?", $data);
$data = str_replace(array("\n ", "\n "),"", $data);
// replace special iCal chars
$data = str_replace(array("\\\\","\,"),array("\\",","), $data);
// parse each line
$lines = explode("\n", $data);
$linecount = 0;
$eventcount = 0;
$eventpos = 0;
foreach($lines as $line) {
//$line = str_replace("!?", "\n", $line); // add nl back into descriptions
// echo ($linecount + 1) . ": " . $line . "<br/>";
if(substr($line,0,6) == "BEGIN:") {
// start new object
$name = substr($line,6);
if($name == "VEVENT")
{
if($eventcount < $maxevents && $eventpos >= $startevent)
{
$this->curnode = new ZCiCalNode($name, $this->curnode);
if($this->tree == null)
$this->tree = $this->curnode;
}
}
else
{
$this->curnode = new ZCiCalNode($name, $this->curnode);
if($this->tree == null)
$this->tree = $this->curnode;
}
//echo "new node: " . $this->curnode->name . "<br/>\n";
/*
if($this->curnode->getParent() != null)
echo "parent of " . $this->curnode->getName() . " is " . $this->curnode->getParent()->getName() . "<br/>";
else
echo "parent of " . $this->curnode->getName() . " is null<br/>";
*/
}
else if(substr($line,0,4) == "END:") {
$name = substr($line,4);
if($name == "VEVENT")
{
if($eventcount < $maxevents && $eventpos >= $startevent)
{
$eventcount++;
if($this->curnode->getName() != $name) {
//panic, mismatch in iCal structure
//die("Can't read iCal file structure, expecting " . $this->curnode->getName() . " but reading $name instead");
throw new Exception("Can't read iCal file structure, expecting " . $this->curnode->getName() . " but reading $name instead");
}
if($this->curnode->getParent() != null) {
//echo "moving up from " . $this->curnode->getName() ;
$this->curnode = & $this->curnode->getParent();
//echo " to " . $this->curnode->getName() . "<br/>";
//echo $this->curnode->getName() . " has " . count($this->curnode->child) . " children<br/>";
}
}
$eventpos++;
}
else
{
if($this->curnode->getName() != $name) {
//panic, mismatch in iCal structure
//die("Can't read iCal file structure, expecting " . $this->curnode->getName() . " but reading $name instead");
throw new Exception("Can't read iCal file structure, expecting " . $this->curnode->getName() . " but reading $name instead");
}
if($this->curnode->getParent() != null) {
//echo "moving up from " . $this->curnode->getName() ;
$this->curnode = & $this->curnode->getParent();
//echo " to " . $this->curnode->getName() . "<br/>";
//echo $this->curnode->getName() . " has " . count($this->curnode->child) . " children<br/>";
}
}
}
else {
$datanode = new ZCiCalDataNode($line);
if($this->curnode->getName() == "VEVENT")
{
if($eventcount < $maxevents && $eventpos >= $startevent)
{
if($datanode->getName() == "EXDATE")
{
if(!array_key_exists($datanode->getName(),$this->curnode->data))
{
$this->curnode->data[$datanode->getName()] = $datanode;
}
else
{
$this->curnode->data[$datanode->getName()]->value[] = $datanode->value[0];
}
}
else
{
if(!array_key_exists($datanode->getName(),$this->curnode->data))
{
$this->curnode->data[$datanode->getName()] = $datanode;
}
else
{
$tnode = $this->curnode->data[$datanode->getName()];
$this->curnode->data[$datanode->getName()] = array();
$this->curnode->data[$datanode->getName()][] = $tnode;
$this->curnode->data[$datanode->getName()][] = $datanode;
}
}
}
}
else
{
if($datanode->getName() == "EXDATE")
{
if(!array_key_exists($datanode->getName(),$this->curnode->data))
{
$this->curnode->data[$datanode->getName()] = $datanode;
}
else
{
$this->curnode->data[$datanode->getName()]->value[] = $datanode->value[0];
}
}
else
{
if(!array_key_exists($datanode->getName(),$this->curnode->data))
{
$this->curnode->data[$datanode->getName()] = $datanode;
}
else
{
$tnode = $this->curnode->data[$datanode->getName()];
$this->curnode->data[$datanode->getName()] = array();
$this->curnode->data[$datanode->getName()][] = $tnode;
$this->curnode->data[$datanode->getName()][] = $datanode;
}
}
}
}
$linecount++;
}
}
else {
$name = "VCALENDAR";
$this->curnode = new ZCiCalNode($name, $this->curnode);
$this->tree = $this->curnode;
$datanode = new ZCiCalDataNode("VERSION:2.0");
$this->curnode->data[$datanode->getName()] = $datanode;
$datanode = new ZCiCalDataNode("PRODID:-//ZContent.net//ZapCalLib 1.0//EN");
$this->curnode->data[$datanode->getName()] = $datanode;
$datanode = new ZCiCalDataNode("CALSCALE:GREGORIAN");
$this->curnode->data[$datanode->getName()] = $datanode;
$datanode = new ZCiCalDataNode("METHOD:PUBLISH");
$this->curnode->data[$datanode->getName()] = $datanode;
}
}
/**
* CountEvents()
*
* Return the # of VEVENTs in the object
*
* @return int
*/
function countEvents() {
$count = 0;
if(isset($this->tree->child)){
foreach($this->tree->child as $child){
if($child->getName() == "VEVENT")
$count++;
}
}
return $count;
}
/**
* CountVenues()
*
* Return the # of VVENUEs in the object
*
* @return int
*/
function countVenues() {
$count = 0;
if(isset($this->tree->child)){
foreach($this->tree->child as $child){
if($child->getName() == "VVENUE")
$count++;
}
}
return $count;
}
/**
* Export object to string
*
* This function exports all objects to an iCalendar string
*
* @return string an iCalendar formatted string
*/
function export() {
return $this->tree->export($this->tree);
}
/**
* Get first event in object list
* Use getNextEvent() to navigate through list
*
* @return object The first event, or null
*/
function &getFirstEvent() {
static $nullguard = null;
if ($this->countEvents() > 0){
$child = $this->tree->child[0];
$event=false;
while(!$event && $child != null){
if($child->getName() == "VEVENT")
$event = true;
else
$child = $child->next;
}
return $child;
}
else
return $nullguard;
}
/**
* Get next event in object list
*
* @param object $event The current event object
*
* @return object Returns the next event or null if past last event
*/
function &getNextEvent($event){
do{
$event = $event->next;
} while($event != null && $event->getName() != "VEVENT");
return $event;
}
/**
* Get first venue in object list
* Use getNextVenue() to navigate through list
*
* @return object The first venue, or null
*/
function &getFirstVenue() {
static $nullguard = null;
if ($this->countVenues() > 0){
$child = $this->tree->child[0];
$event=false;
while(!$event && $child != null){
if($child->getName() == "VVENUE")
$event = true;
else
$child = $child->next;
}
return $child;
}
else
return $nullguard;
}
/**
* Get next venue in object list
*
* @param object $venue The current venue object
*
* @return object Returns the next venue or null if past last venue
*/
function &getNextVenue($venue){
do{
$venue = $venue->next;
} while($venue != null && $venue->getName() != "VVENUE");
return $venue;
}
/**
* Get first child in object list
* Use getNextSibling() and getPreviousSibling() to navigate through list
*
* @param object $thisnode The parent object
*
* @return object The child object
*/
function &getFirstChild(& $thisnode){
$nullvalue = null;
if(count($thisnode->child) > 0) {
//echo "moving from " . $thisnode->getName() . " to " . $thisnode->child[0]->getName() . "<br/>";
return $thisnode->child[0];
}
else
return $nullvalue;
}
/**
* Get next sibling in object list
*
* @param object $thisnode The current object
*
* @return object Returns the next sibling
*/
function &getNextSibling(& $thisnode){
return $thisnode->next;
}
/**
* Get previous sibling in object list
*
* @param object $thisnode The current object
*
* @return object Returns the previous sibling
*/
function &getPrevSibling(& $thisnode){
return $thisnode->prev;
}
/**
* Read date/time in iCal formatted string
*
* @param string iCal formated date/time string
*
* @return int Unix timestamp
* @deprecated Use ZDateHelper::toUnixDateTime() instead
*/
function toUnixDateTime($datetime){
$year = substr($datetime,0,4);
$month = substr($datetime,4,2);
$day = substr($datetime,6,2);
$hour = 0;
$minute = 0;
$second = 0;
if(strlen($datetime) > 8 && $datetime[8] == "T") {
$hour = substr($datetime,9,2);
$minute = substr($datetime,11,2);
$second = substr($datetime,13,2);
}
$d1 = mktime($hour, $minute, $second, $month, $day, $year);
}
/**
* fromUnixDateTime()
*
* Take Unix timestamp and format to iCal date/time string
*
* @param int $datetime Unix timestamp, leave blank for current date/time
*
* @return string formatted iCal date/time string
* @deprecated Use ZDateHelper::fromUnixDateTimetoiCal() instead
*/
static function fromUnixDateTime($datetime = null){
date_default_timezone_set('UTC');
if($datetime == null)
$datetime = time();
return date("Ymd\THis",$datetime);
}
/**
* fromUnixDate()
*
* Take Unix timestamp and format to iCal date string
*
* @param int $datetime Unix timestamp, leave blank for current date/time
*
* @return string formatted iCal date string
* @deprecated Use ZDateHelper::fromUnixDateTimetoiCal() instead
*/
static function fromUnixDate($datetime = null){
date_default_timezone_set('UTC');
if($datetime == null)
$datetime = time();
return date("Ymd",$datetime);
}
/**
* Format into iCal time format from SQL date or SQL date-time format
*
* @param string $datetime SQL date or SQL date-time string
*
* @return string iCal formatted string
* @deprecated Use ZDateHelper::fromSqlDateTime() instead
*/
static function fromSqlDateTime($datetime = ""){
if($datetime == "")
$datetime = ZDateHelper::toSqlDateTime();
if(strlen($datetime) > 10)
return sprintf('%04d%02d%02dT%02d%02d%02d',substr($datetime,0,4),substr($datetime,5,2),substr($datetime,8,2),
substr($datetime,11,2),substr($datetime,14,2),substr($datetime,17,2));
else
return sprintf('%04d%02d%02d',substr($datetime,0,4),substr($datetime,5,2),substr($datetime,8,2));
}
/**
* Format iCal time format to either SQL date or SQL date-time format
*
* @param string $datetime icalendar formatted date or date-time
* @return string SQL date or SQL date-time string
* @deprecated Use ZDateHelper::toSqlDateTime() instead
*/
static function toSqlDateTime($datetime = ""){
if($datetime == "")
return ZDateHelper::toSqlDateTime();
if(strlen($datetime) > 10)
return sprintf('%04d-%02d-%02d %02d:%02d:%02d',substr($datetime,0,4),substr($datetime,5,2),substr($datetime,8,2),
substr($datetime,11,2),substr($datetime,14,2),substr($datetime,17,2));
else
return sprintf('%04d-%02d-%02d',substr($datetime,0,4),substr($datetime,5,2),substr($datetime,8,2));
}
/**
* Pull timezone data from node and put in array
*
* Returning array contains the following array keys: tzoffsetfrom, tzoffsetto, tzname, dtstart, rrule
*
* @param array $node timezone object
*
* @return array
*/
static function getTZValues($node){
$tzvalues = array();
$tnode = @$node->data['TZOFFSETFROM'];
if($tnode != null){
$tzvalues["tzoffsetfrom"] = $tnode->getValues();
}
$tnode = @$node->data['TZOFFSETTO'];
if($tnode != null){
$tzvalues["tzoffsetto"] = $tnode->getValues();
}
$tnode = @$node->data['TZNAME'];
if($tnode != null){
$tzvalues["tzname"] = $tnode->getValues();
}
else
$tzvalues["tzname"] = "";
$tnode = @$node->data['DTSTART'];
if($tnode != null){
$tzvalues["dtstart"] = ZDateHelper::fromiCaltoUnixDateTime($tnode->getValues());
}
$tnode = @$node->data['RRULE'];
if($tnode != null){
$tzvalues["rrule"] = $tnode->getValues();
//echo "rule: " . $tzvalues["rrule"] . "<br/>\n";
}
else{
// no rule specified, let's create one from based on the date
$date = getdate($tzvalues["dtstart"]);
$month = $date["mon"];
$day = $date["mday"];
$tzvalues["rrule"] = "FREQ=YEARLY;INTERVAL=1;BYMONTH=$month;BYMONTHDAY=$day";
}
return $tzvalues;
}
/**
* Escape slashes, commas and semicolons in strings
*
* @param string $content
*
* @return string
*/
static function formatContent($content)
{
$content = str_replace(array('\\' , ',' , ';' ), array('\\\\' , '\\,' , '\\;' ),$content);
return $content;
}
}
?>

View File

@ -0,0 +1,6 @@
<html>
<head>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,796 @@
<?php
/**
* recurringdate.php - create list of dates from recurring rule
*
* @package ZapCalLib
* @author Dan Cogliano <http://zcontent.net>
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
* @link http://icalendar.org/php-library.html
*/
// No direct access
defined('_ZAPCAL') or die( 'Restricted access' );
/**
* Zap Calendar Recurring Date Helper Class
*
* Class to expand recurring rule to a list of dates
*/
class ZCRecurringDate {
/**
* rules string
*
* @var string
*/
var $rules = "";
/**
* start date in Unix Timestamp format (local timezone)
*
* @var integer
*/
var $startdate = null;
/**
* repeating frequency type (i.e. "y" for yearly, "m" for monthly)
*
* @var string
*/
var $freq = null;
/**
* timezone of event (using PHP timezones)
*
* @var string
*/
var $tzid = null;
/**
* repeat mode ('c': count, 'u': until)
*
* @var string
*/
var $repeatmode=null;
/**
* repeat until date (in UTC Unix Timestamp format)
*
* @var integer
*/
var $until=null;
/**
* repeat count when repeat mode is 'c'
*
* @var integer
*/
var $count=0;
/**
* array of repeat by seconds values
*
* @var array
*/
var $bysecond=array();
/**
* array of repeat by minutes values
*
* @var array
*/
var $byminute=array();
/**
* array of repeat by hour values
*
* @var array
*/
var $byhour=array();
/**
* array of repeat by day values
*
* @var array
*/
var $byday=array();
/**
* array of repeat by month day values
*
* @var array
*/
var $bymonthday=array();
/**
* array of repeat by month values
*
* @var array
*/
var $bymonth=array();
/**
* array of repeat by year values
*
* @var array
*/
var $byyear=array();
/**
* array of repeat by setpos values
*
* @var array
*/
var $bysetpos=array();
/**
* inteval of repeating event (i.e. every 2 weeks, every 6 months)
*
* @var integer
*/
var $interval = 1;
/**
* debug level (for testing only)
*
* @var integer
*/
var $debug = 0;
/**
* error string (future use)
*
* @var string
*/
var $error;
/**
* array of exception dates in Unix Timestamp format (UTC dates)
*
* @var array
*/
var $exdates=array();
/**
* Expand recurring rule to a list of dates
*
* @param string $rules iCalendar rules string
* @param integer $startdate start date in Unix Timestamp format
* @param array $exdates array of exception dates
* @param string $tzid timezone of event (using PHP timezones)
*/
function __construct($rules, $startdate, $exdates = array(),$tzid = "UTC"){
if(strlen($rules) > 0){
//move exdates to event timezone for comparing with event date
for($i = 0; $i < count($exdates); $i++)
{
$exdates[$i] = ZDateHelper::toUnixDateTime(ZDateHelper::toLocalDateTime(ZDateHelper::toSQLDateTime($exdates[$i]),$tzid));
}
$rules=str_replace("\'","",$rules);
$this->rules = $rules;
if($startdate == null){
// if not specified, use start date of beginning of last year
$tdate=getdate();
$startdate=mktime(0,0,0,1,1,$tdate["year"] - 1);
}
$this->startdate = $startdate;
$this->tzid = $tzid;
$this->exdates = $exdates;
$rules=explode(";", $rules);
$ruletype = "";
foreach($rules as $rule){
$item=explode("=",$rule);
//echo $item[0] . "=" . $item[1] . "<br/>\n";
switch($item[0]){
case "FREQ":
switch($item[1]){
case "YEARLY":
$this->freq="y";
break;
case "MONTHLY":
$this->freq="m";
break;
case "WEEKLY":
$this->freq="w";
break;
case "DAILY":
$this->freq="d";
break;
case "HOURLY":
$this->freq="h";
break;
case "MINUTELY":
$this->freq="i";
break;
case "SECONDLY":
$this->freq="s";
break;
}
break;
case "INTERVAL":
$this->interval = $item[1];
break;
case "BYSECOND":
$this->bysecond = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "BYMINUTE":
$this->byminute = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "BYHOUR":
$this->byhour = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "BYDAY":
$this->byday = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "BYMONTHDAY":
$this->bymonthday = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "BYMONTH":
$this->bymonth = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "BYYEAR":
$this->byyear = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "COUNT":
$this->count = intval($item[1]);
$this->repeatmode = "c";
break;
case "BYSETPOS":
$this->bysetpos = explode(",",$item[1]);
break;
case "UNTIL":
$this->until = ZDateHelper::fromiCaltoUnixDateTime($item[1]);
$this->repeatmode = "u";
break;
}
}
if(count($this->bysetpos) > 0){
switch($ruletype){
case "BYYEAR":
$this->byyear = $this->bySetPos($this->byyear,$this->bysetpos);
break;
case "BYMONTH":
$this->bymonth = $this->bySetPos($this->bymonth,$this->bysetpos);
break;
case "BYMONTHDAY":
$this->bymonthday = $this->bySetPos($this->bymonthday,$this->bysetpos);
break;
case "BYDAY":
$this->byday = $this->bySetPos($this->byday,$this->bysetpos);
break;
case "BYHOUR":
$this->byhour = $this->bySetPos($this->byhour,$this->bysetpos);
break;
case "BYMINUTE":
$this->byminute = $this->bySetPos($this->byminute,$this->bysetpos);
break;
case "BYSECOND":
$this->bysecond = $this->bySetPos($this->bysecond,$this->bysetpos);
break;
}
}
}
}
/**
* bysetpos rule support
*
* @param array $bytype
* @param array $bysetpos
*
* @return array
*/
function bySetPos($bytype, $bysetpos){
$result = array();
for($i=0; $i < count($bysetpos); $i++){
for($j=0; $j < count($bytype); $j++){
$result[] = $bysetpos[$i] . $bytype[$j];
}
}
return $result;
}
/**
* save error
*
* @param string $msg
*/
function setError($msg){
$this->error = $msg;
}
/**
* get error message
*
* @return string error message
*/
function getError(){
return $this->error;
}
/**
* set debug level (0: none, 1: minimal, 2: more output)
*
* @param integer $level
*
*/
function setDebug($level)
{
$this->debug = $level;
}
/**
* display debug message
*
* @param integer $level
* @param string $msg
*/
function debug($level, $msg){
if($this->debug >= $level)
echo $msg . "<br/>\n";
}
/**
* Get repeating dates by year
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function byYear($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"byYear(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$count = 0;
if(count($this->byyear) > 0){
foreach($this->byyear as $year){
$t = getdate($startdate);
$wdate = mktime($t[hours],$t[minutes],$t[seconds],$t[month],$t[mday],$year);
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->byMonth($wdate, $enddate, $rdates, $tzid);
if($count == 0) {
$rdates[] = $wdate;
$count++;
}
}
}
}
else if(!$this->maxDates($rdates))
$count = $this->byMonth($startdate, $enddate, $rdates, $tzid);
self::debug(1,"byYear() returned " . $count );
return $count;
}
/**
* Get repeating dates by month
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function byMonth($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"byMonth(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$count = 0;
if(count($this->bymonth) > 0){
foreach($this->bymonth as $month){
$t = getdate($startdate);
$wdate = mktime($t["hours"],$t["minutes"],$t["seconds"],$month,$t["mday"],$t["year"]);
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->byMonthDay($wdate, $enddate, $rdates, $tzid);
if($count == 0) {
$rdates[] = $wdate;
$count++;
}
}
}
}
else if(!$this->maxDates($rdates))
$count = $this->byMonthDay($startdate, $enddate, $rdates, $tzid);
self::debug(1,"byMonth() returned " . $count );
return $count;
}
/**
* Get repeating dates by month day
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function byMonthDay($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"byMonthDay(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$count = 0;
self::debug(1,"start date: " . ZDateHelper::toSqlDateTime($startdate));
if(count($this->bymonthday) > 0){
foreach($this->bymonthday as $day){
$day = intval($day);
$t = getdate($startdate);
$wdate = mktime($t['hours'],$t['minutes'],$t['seconds'],$t['mon'],$day,$t['year']);
self::debug(2,"mktime(" . $t['hours'] . ", " . $t['minutes']
. ", " . $t['mon'] . ", " . $day . ", " . $t['year'] . ") returned $wdate");
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->byDay($wdate, $enddate, $rdates, $tzid);
if($count == 0) {
$rdates[] = $wdate;
$count++;
}
}
}
}
else if(!$this->maxDates($rdates)) {
self::debug(1,"start date: " . ZDateHelper::toSqlDateTime($startdate));
$count = $this->byDay($startdate, $enddate, $rdates, $tzid);
}
self::debug(1,"byMonthDay() returned " . $count );
return $count;
}
/**
* Get repeating dates by day
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function byDay($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"byDay(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$days = array(
"SU" => 0,
"MO" => 1,
"TU" => 2,
"WE" => 3,
"TH" => 4,
"FR" => 5,
"SA" => 6);
$idays = array(
0 => "SU",
1 => "MO",
2 => "TU",
3 => "WE",
4 => "TH",
5 => "FR",
6 => "SA");
$count = 0;
if(count($this->byday) > 0){
if(empty($this->byday[0]))
{
$this->byday[0] = $idays[date("w",$startdate)];
}
foreach($this->byday as $tday){
$t = getdate($startdate);
$day = substr($tday,strlen($tday) - 2);
if(strlen($day) < 2)
{
// missing start day, use current date for DOW
$day = $idays[date("w",$startdate)];
}
if(strlen($tday) > 2) {
$imin = 1;
$imax = 5; // max # of occurances in a month
if(strlen($tday) > 2)
$imin = $imax = substr($tday,0,strlen($tday) - 2);
self::debug(2,"imin: $imin, imax: $imax, tday: $tday, day: $day, daynum: {$days[$day]}");
for($i = $imin; $i <= $imax; $i++){
$wdate = ZDateHelper::getDateFromDay($startdate,$i-1,$days[$day],$tzid);
self::debug(2,"getDateFromDay(" . ZDateHelper::toSqlDateTime($startdate)
. ",$i,{$days[$day]}) returned " . ZDateHelper::toSqlDateTime($wdate));
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->byHour($wdate, $enddate, $rdates);
if($count == 0){
$rdates[] = $wdate;
$count++;
//break;
}
}
}
}
else {
// day of week version
$startdate_dow = date("w",$startdate);
$datedelta = $days[$day] - $startdate_dow;
self::debug(2, "start_dow: $startdate_dow, datedelta: $datedelta");
if($datedelta >= 0)
{
$wdate = ZDateHelper::addDate($startdate,0,0,0,0,$datedelta,0,$this->tzid);
self::debug(2, "wdate: " . ZDateHelper::toSqlDateTime($wdate));
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->byHour($wdate, $enddate, $rdates);
if($count == 0){
$rdates[] = $wdate;
$count++;
self::debug(2,"adding date " . ZDateHelper::toSqlDateTime($wdate) );
}
}
}
}
}
}
else if(!$this->maxDates($rdates))
$count = $this->byHour($startdate, $enddate, $rdates);
self::debug(1,"byDay() returned " . $count );
return $count;
}
/**
* Get repeating dates by hour
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function byHour($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"byHour(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$count = 0;
if(count($this->byhour) > 0){
foreach($this->byhour as $hour){
$t = getdate($startdate);
$wdate = mktime($hour,$t["minutes"],$t["seconds"],$t["mon"],$t["mday"],$t["year"]);
self::debug(2,"checking date/time " . ZDateHelper::toSqlDateTime($wdate));
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->byMinute($wdate, $enddate, $rdates);
if($count == 0) {
$rdates[] = $wdate;
$count++;
}
}
}
}
else if(!$this->maxDates($rdates))
$count = $this->byMinute($startdate, $enddate, $rdates);
self::debug(1,"byHour() returned " . $count );
return $count;
}
/**
* Get repeating dates by minute
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function byMinute($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"byMinute(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$count = 0;
if(count($this->byminute) > 0){
foreach($this->byminute as $minute){
$t = getdate($startdate);
$wdate = mktime($t["hours"],$minute,$t["seconds"],$t["mon"],$t["mday"],$t["year"]);
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->bySecond($wdate, $enddate, $rdates);
if($count == 0) {
$rdates[] = $wdate;
$count++;
}
}
}
}
else if(!$this->maxDates($rdates))
$count = $this->bySecond($startdate, $enddate, $rdates);
self::debug(1,"byMinute() returned " . $count );
return $count;
}
/**
* Get repeating dates by second
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function bySecond($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"bySecond(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$count = 0;
if(count($this->bysecond) > 0){
foreach($this->bysecond as $second){
$t = getdate($startdate);
$wdate = mktime($t["hours"],$t["minutes"],$second,$t["mon"],$t["mday"],$t["year"]);
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$rdates[] = $wdate;
$count++;
}
}
}
self::debug(1,"bySecond() returned " . $count );
return $count;
}
/**
* Determine if the loop has reached the end date
*
* @param array $rdates array of repeating dates
*
* @return boolean
*/
private function maxDates($rdates){
if($this->repeatmode == "c" && count($rdates) >= $this->count)
return true; // exceeded count
else if(count($rdates) > 0 && $this->repeatmode == "u" && $rdates[count($rdates) - 1] > $this->until){
return true; //past date
}
return false;
}
/**
* Get array of dates from recurring rule
*
* @param $maxdate integer maximum date to appear in repeating dates in Unix timestamp format
*
* @return array
*/
public function getDates($maxdate = null){
//$this->debug = 2;
self::debug(1,"getDates()");
$nextdate = $enddate = $this->startdate;
$rdates = array();
$done = false;
$eventcount = 0;
$loopcount = 0;
self::debug(2,"freq: " . $this->freq . ", interval: " . $this->interval);
while(!$done){
self::debug(1,"<b>*** Frequency ({$this->freq}) loop pass $loopcount ***</b>");
switch($this->freq){
case "y":
if($eventcount > 0)
{
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,0,$this->interval,$this->tzid);
self::debug(2,"addDate() returned " . ZDateHelper::toSqlDateTime($nextdate));
if(!empty($this->byday)){
$t = getdate($nextdate);
$nextdate = gmmktime($t["hours"],$t["minutes"],$t["seconds"],$t["mon"],1,$t["year"]);
}
self::debug(2,"nextdate set to $nextdate (". ZDateHelper::toSQLDateTime($nextdate) . ")");
}
$enddate=ZDateHelper::addDate($nextdate,0,0,0,0,0,1);
break;
case "m":
if($eventcount > 0)
{
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,$this->interval,0,0,$this->tzid);
self::debug(2,"addDate() returned " . ZDateHelper::toSqlDateTime($nextdate));
}
if(count($this->byday) > 0)
{
$t = getdate($nextdate);
if($t["mday"] > 28)
{
//check for short months when using month by day, make sure we do not overshoot the counter and skip a month
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,$this->interval,0,0,$this->tzid);
$t2 = getdate($nextdate);
if($t2["mday"] < $t["mday"])
{
// oops, skipped a month, backup to previous month
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,$t2["mday"] - $t["mday"],0,$this->tzid);
}
}
$t = getdate($nextdate);
$nextdate = mktime($t["hours"],$t["minutes"],$t["seconds"],$t["mon"],1,$t["year"]);
}
self::debug(2,"nextdate set to $nextdate (". ZDateHelper::toSQLDateTime($nextdate) . ")");
$enddate=ZDateHelper::addDate($nextdate,0,0,0,$this->interval,0,0);
break;
case "w":
if($eventcount == 0)
$nextdate=$nextdate;
else {
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,$this->interval*7,0,$this->tzid);
if(count($this->byday) > 0){
$dow = date("w", $nextdate);
// move to beginning of week (Sunday)
$bow = 0;
$diff = $bow - $dow;
if($diff > 0)
$diff = $diff - 7;
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,$diff,0);
}
self::debug(2,"nextdate set to $nextdate (". ZDateHelper::toSQLDateTime($nextdate) . ")");
}
$enddate=ZDateHelper::addDate($nextdate,0,0,0,0,$this->interval*7,0);
break;
case "d":
$nextdate=($eventcount==0?$nextdate:
ZDateHelper::addDate($nextdate,0,0,0,0,$this->interval,0,$this->tzid));
$enddate=ZDateHelper::addDate($nextdate,0,0,0,0,1,0);
break;
}
$count = $this->byYear($nextdate,$enddate,$rdates,$this->tzid);
$eventcount += $count;
if($maxdate > 0 && $maxdate < $nextdate)
{
array_pop($rdates);
$done = true;
}
else if($count == 0 && !$this->maxDates($rdates)){
$rdates[] = $nextdate;
$eventcount++;
}
if($this->maxDates($rdates))
$done = true;
$year = date("Y", $nextdate);
if($year > _ZAPCAL_MAXYEAR)
{
$done = true;
}
$loopcount++;
if($loopcount > _ZAPCAL_MAXYEAR){
$done = true;
throw new Exception("Infinite loop detected in getDates()");
}
}
if($this->repeatmode == "u" && $rdates[count($rdates) - 1] > $this->until){
// erase last item
array_pop($rdates);
}
$count1 = count($rdates);
$rdates = array_unique($rdates);
$count2 = count($rdates);
$dups = $count1 - $count2;
$excount = 0;
foreach($this->exdates as $exdate)
{
if($pos = array_search($exdate,$rdates))
{
array_splice($rdates,$pos,1);
$excount++;
}
}
self::debug(1,"getDates() returned " . count($rdates) . " dates, removing $dups duplicates, $excount exceptions");
if($this->debug >= 2)
{
self::debug(2,"Recurring Dates:");
foreach($rdates as $rdate)
{
$d = getdate($rdate);
self::debug(2,ZDateHelper::toSQLDateTime($rdate) . " " . $d["wday"] );
}
self::debug(2,"Exception Dates:");
foreach($this->exdates as $exdate)
{
self::debug(2, ZDateHelper::toSQLDateTime($exdate));
}
//exit;
}
return $rdates;
}
}

View File

@ -0,0 +1,142 @@
<?php
/**
* timezone.php - create timezone data for use in icalendar file
*
* @package ZapCalLib
* @author Dan Cogliano <http://zcontent.net>
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
* @link http://icalendar.org/php-library.html
*/
// No direct access
defined('_ZAPCAL') or die( 'Restricted access' );
/**
* Zap Calendar Time Zone Helper Class
*
* Class to help create timezone section of iCalendar file
*
* @copyright Copyright (C) 2006 - 2016 by Dan Cogliano
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
class ZCTimeZoneHelper {
/**
* getTZNode creates VTIMEZONE section in an iCalendar file
*
* @param @startyear int start year of date range
*
* @param @endyear int end year of date range
*
* @param $tzid string PHP timezone, use underscore for multiple words (i.e. "New_York" for "New York")
*
* @param $parentnode object iCalendar object where VTIMEZONE will be created
*
* @return object return VTIMEZONE object
*/
static function getTZNode($startyear, $endyear, $tzid, $parentnode)
{
$tzmins = array();
$tzmaxs = array();
if(!array_key_exists($tzid,$tzmins) || $tzmins[$tzid] > $startyear)
{
$tzmins[$tzid] = $startyear;
}
if(!array_key_exists($tzid,$tzmaxs) || $tzmaxs[$tzid] < $endyear)
{
$tzmaxs[$tzid] = $endyear;
}
foreach(array_keys($tzmins) as $tzid)
{
$tmin = $tzmins[$tzid] - 1;
if(array_key_exists($tzid,$tzmaxs))
{
$tmax = $tzmaxs[$tzid] + 1;
}
else
{
$tmax = $tzmins[$tzid] + 1;
}
$tstart = gmmktime(0,0,0,1,1,$tmin);
$tend = gmmktime(23,59,59,12,31,$tmax);
$tz = new DateTimeZone($tzid);
$transitions = $tz->getTransitions($tstart,$tend);
$tzobj = new ZCiCalNode("VTIMEZONE", $parentnode, true);
$datanode = new ZCiCalDataNode("TZID:" . str_replace("_"," ",$tzid));
$tzobj->data[$datanode->getName()] = $datanode;
$count = 0;
$lasttransition = null;
if(count($transitions) == 1)
{
// not enough transitions found, probably UTC
// lets add fake transition at end for those systems that need it (i.e. Outlook)
$t2 = array();
$t2["isdst"] = $transitions[0]["isdst"];
$t2["offset"] = $transitions[0]["offset"];
$t2["ts"] = $tstart;
$t2["abbr"] = $transitions[0]["abbr"];
$transitions[] = $t2;
}
foreach($transitions as $transition)
{
$count++;
if($count == 1)
{
$lasttransition = $transition;
continue; // skip first item
}
if($transition["isdst"] == 1)
{
$tobj = new ZCiCalNode("DAYLIGHT", $tzobj);
}
else
{
$tobj = new ZCiCalNode("STANDARD", $tzobj);
}
//$tzobj->data[$tobj->getName()] == $tobj;
// convert timestamp to local time zone
$ts = ZDateHelper::toUnixDateTime(ZDateHelper::toLocalDateTime(ZDateHelper::toSQLDateTime($transition["ts"]),$tzid));
$datanode = new ZCiCalDataNode("DTSTART:".ZDateHelper::toICalDateTime($ts));
$tobj->data[$datanode->getName()] = $datanode;
//echo $ts . " => " . ZDateHelper::toICalDateTime($ts) . "<br/>\n"; exit;
$toffset = $lasttransition["offset"];
$thours = intval($toffset/60/60);
$tmins = abs($toffset)/60 - intval(abs($toffset)/60/60)*60;
if($thours < 0)
{
$offset = sprintf("%03d%02d",$thours,$tmins);
}
else
{
$offset = sprintf("+%02d%02d",$thours,$tmins);
}
$datanode = new ZCiCalDataNode("TZOFFSETFROM:".$offset);
$tobj->data[$datanode->getName()] = $datanode;
$toffset = $transition["offset"];
$thours = intval($toffset/60/60);
$tmins = abs($toffset)/60 - intval(abs($toffset)/60/60)*60;
if($thours < 0)
{
$offset = sprintf("%03d%02d",$thours,$tmins);
}
else
{
$offset = sprintf("+%02d%02d",$thours,$tmins);
}
$datanode = new ZCiCalDataNode("TZOFFSETTO:".$offset);
$tobj->data[$datanode->getName()] = $datanode;
$datanode = new ZCiCalDataNode("TZNAME:".$transition["abbr"]);
$tobj->data[$datanode->getName()] = $datanode;
$lasttransition = $transition;
}
}
return $tzobj;
}
}

View File

@ -0,0 +1,28 @@
<?php
/**
* zapcallib.php
*
* @package ZapCalLib
* @author Dan Cogliano <http://zcontent.net>
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
* @link http://icalendar.org/php-library.html
*/
/**
* used by ZapCalLib
* @var integer
*/
define('_ZAPCAL',1);
if(!defined('_ZAPCAL_BASE'))
{
/**
* the base folder of the library
* @var string
*/
define('_ZAPCAL_BASE',__DIR__);
}
require_once(_ZAPCAL_BASE . '/includes/framework.php');

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
<div class="modal-dialog modal-lg">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-calendar-check mr-2"></i>New Scheduled Ticket</h5>
<h5 class="modal-title"><i class="fa fa-fw fa-calendar-check mr-2"></i>New Recurring Ticket</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
@ -25,26 +25,28 @@
$contact_id = intval($row['contact_id']);
$contact_name = nullable_htmlentities($row['contact_name']);
$contact_primary = intval($row['contact_primary']);
if($contact_primary == 1) {
if ($contact_primary == 1) {
$contact_primary_display = " (Primary)";
} else {
$contact_primary_display = "";
}
$contact_technical = intval($row['contact_technical']);
if($contact_technical == 1) {
if ($contact_technical == 1) {
$contact_technical_display = " (Technical)";
} else {
$contact_technical_display = "";
}
$contact_title = nullable_htmlentities($row['contact_title']);
if(!empty($contact_title)) {
if (!empty($contact_title)) {
$contact_title_display = " - $contact_title";
} else {
$contact_title_display = "";
}
?>
<option value="<?php echo $contact_id; ?>" <?php if ($contact_primary == 1) { echo "selected"; } ?>><?php echo "$contact_name$contact_title_display$contact_primary_display$contact_technical_display"; ?></option>
?>
<option value="<?php echo $contact_id; ?>" <?php if ($contact_primary == 1) {
echo "selected";
} ?>><?php echo "$contact_name$contact_title_display$contact_primary_display$contact_technical_display"; ?></option>
<?php } ?>
</select>
</div>
@ -64,7 +66,7 @@
while ($row = mysqli_fetch_array($sql)) {
$selectable_client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
?>
?>
<option value="<?php echo $selectable_client_id; ?>"><?php echo $client_name; ?></option>
<?php } ?>
@ -139,7 +141,7 @@
while ($row = mysqli_fetch_array($sql_assets)) {
$asset_id_select = intval($row['asset_id']);
$asset_name_select = nullable_htmlentities($row['asset_name']);
?>
?>
<option value="<?php echo $asset_id_select; ?>"><?php echo $asset_name_select; ?></option>
<?php } ?>
@ -161,4 +163,4 @@
</form>
</div>
</div>
</div>
</div>

View File

@ -100,4 +100,4 @@
</form>
</div>
</div>
</div>
</div>

View File

@ -23,59 +23,60 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-calendar-check mr-2"></i>Scheduled Tickets</h3>
<div class='card-tools'>
<div class="float-left">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addScheduledTicketModal">
<i class="fas fa-plus mr-2"></i>New Scheduled Ticket
</button>
</div>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-calendar-check mr-2"></i>Recurring Tickets</h3>
<div class='card-tools'>
<div class="float-left">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addScheduledTicketModal">
<i class="fas fa-plus mr-2"></i>New Recurring Ticket
</button>
</div>
</div>
</div>
<div class="card-body">
<div class="card-body">
<form autocomplete="off">
<div class="row">
<form autocomplete="off">
<div class="row">
<div class="col-md-4">
<div class="input-group mb-3 mb-md-0">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search Scheduled Tickets">
<div class="input-group-append">
<button class="btn btn-dark"><i class="fa fa-search"></i></button>
</div>
<div class="col-md-4">
<div class="input-group mb-3 mb-md-0">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) {
echo stripslashes(nullable_htmlentities($q));
} ?>" placeholder="Search Scheduled Tickets">
<div class="input-group-append">
<button class="btn btn-dark"><i class="fa fa-search"></i></button>
</div>
</div>
<div class="col-md-8">
<div class="dropdown float-right" id="bulkActionButton" hidden>
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
</button>
<div class="dropdown-menu">
<button class="dropdown-item text-danger text-bold"
type="submit" form="multi_actions" name="bulk_delete_scheduled_tickets">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</button>
</div>
</div>
</div>
</div>
</form>
<hr>
<div class="col-md-8">
<div class="table-responsive-sm">
<div class="dropdown float-right" id="bulkActionButton" hidden>
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
</button>
<div class="dropdown-menu">
<button class="dropdown-item text-danger text-bold" type="submit" form="multi_actions" name="bulk_delete_scheduled_tickets">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</button>
</div>
</div>
<form id="bulkActions" action="post.php" method="post">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
</div>
</div>
</form>
<hr>
<table class="table table-striped table-borderless table-hover">
<thead class="<?php if (!$num_rows[0]) {
echo "d-none";
} ?>">
<div class="table-responsive-sm">
<form id="bulkActions" action="post.php" method="post">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<table class="table table-striped table-borderless table-hover">
<thead class="<?php if (!$num_rows[0]) {
echo "d-none";
} ?>">
<tr>
<td class="pr-0">
<div class="form-check">
@ -89,9 +90,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<th><a class="text-dark">Next Run Date</a></th>
<th class="text-center">Action</th>
</tr>
</thead>
</thead>
<tbody>
<tbody>
<?php
@ -103,7 +104,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$scheduled_ticket_frequency = nullable_htmlentities($row['scheduled_ticket_frequency']);
$scheduled_ticket_next_run = nullable_htmlentities($row['scheduled_ticket_next_run']);
$scheduled_ticket_client_name = nullable_htmlentities($row['client_name']);
?>
?>
<tr>
<td class="pr-0">
@ -116,8 +117,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</th>
<td>
<a href="#" data-toggle="modal" data-target="#editScheduledTicketModal"
onclick="populateScheduledTicketEditModal(<?php echo $scheduled_ticket_client_id, ",", $scheduled_ticket_id ?>)"> <?php echo $scheduled_ticket_subject ?>
<a href="#" data-toggle="modal" data-target="#editScheduledTicketModal" onclick="populateScheduledTicketEditModal(<?php echo $scheduled_ticket_client_id, ",", $scheduled_ticket_id ?>)"> <?php echo $scheduled_ticket_subject ?>
</a>
</td>
@ -133,8 +133,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#" data-toggle="modal"
data-target="#editScheduledTicketModal" onclick="populateScheduledTicketEditModal(<?php echo $scheduled_ticket_client_id, ",", $scheduled_ticket_id ?>)">
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editScheduledTicketModal" onclick="populateScheduledTicketEditModal(<?php echo $scheduled_ticket_client_id, ",", $scheduled_ticket_id ?>)">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<?php
@ -152,22 +151,22 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<?php } ?>
</tbody>
</tbody>
</table>
</table>
</form>
</div>
<?php require_once 'pagination.php';
?>
</form>
</div>
</div>
<script src="js/scheduled_tickets_edit_modal.js"></script>
<script src="js/bulk_actions.js"></script>
<?php require_once 'pagination.php';
?>
</div>
</div>
<script src="js/scheduled_tickets_edit_modal.js"></script>
<script src="js/bulk_actions.js"></script>
<?php
require_once "scheduled_ticket_add_modal.php";
@ -175,4 +174,3 @@ require_once "scheduled_ticket_add_modal.php";
require_once "scheduled_ticket_edit_modal.php";
require_once "footer.php";

View File

@ -1,7 +1,7 @@
<!-- Main Sidebar Container -->
<aside class="main-sidebar sidebar-dark-<?php echo nullable_htmlentities($config_theme); ?> d-print-none">
<div class="brand-link">
<div class="brand-link">
<h3 class="brand-text text-light mb-0"><?php echo nullable_htmlentities($session_company_name); ?></h3>
</div>
@ -14,14 +14,18 @@
<ul class="nav nav-pills nav-sidebar flex-column mt-3" data-widget="treeview" data-accordion="false">
<li class="nav-item">
<a href="dashboard.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "dashboard.php") { echo "active"; } ?>">
<a href="dashboard.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "dashboard.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-tachometer-alt"></i>
<p>Dashboard</p>
</a>
</li>
<li class="nav-item">
<a href="clients.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "clients.php") { echo "active"; } ?>">
<a href="clients.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "clients.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-user-friends"></i>
<p>Clients</p>
</a>
@ -31,22 +35,20 @@
<li class="nav-header mt-3">SUPPORT</li>
<li class="nav-item">
<a href="tickets.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "tickets.php" || basename($_SERVER["PHP_SELF"]) == "ticket.php") { echo "active"; } ?>">
<a href="tickets.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "tickets.php" || basename($_SERVER["PHP_SELF"]) == "ticket.php" || basename($_SERVER["PHP_SELF"]) == "scheduled_tickets.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-life-ring"></i>
<p>Tickets</p>
</a>
</li>
<li class="nav-item">
<a href="scheduled_tickets.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "scheduled_tickets.php") { echo "active"; } ?>">
<i class="nav-icon fas fa-calendar-check"></i>
<p>Scheduled Tickets</p>
</a>
</li>
<?php } ?>
<li class="nav-item">
<a href="calendar_events.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "calendar_events.php") { echo "active"; } ?>">
<a href="calendar_events.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "calendar_events.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-calendar-alt"></i>
<p>Calendar</p>
</a>
@ -56,31 +58,41 @@
<li class="nav-header mt-3">SALES</li>
<li class="nav-item">
<a href="quotes.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "quotes.php" || basename($_SERVER["PHP_SELF"]) == "quote.php") { echo "active"; } ?>">
<a href="quotes.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "quotes.php" || basename($_SERVER["PHP_SELF"]) == "quote.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-comment-dollar"></i>
<p>Quotes</p>
</a>
</li>
<li class="nav-item">
<a href="invoices.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "invoices.php" || basename($_SERVER["PHP_SELF"]) == "invoice.php") { echo "active"; } ?>">
<a href="invoices.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "invoices.php" || basename($_SERVER["PHP_SELF"]) == "invoice.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-file-invoice"></i>
<p>Invoices</p>
</a>
</li>
<li class="nav-item">
<a href="recurring_invoices.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "recurring_invoices.php") { echo "active"; } ?>">
<a href="recurring_invoices.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "recurring_invoices.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-redo-alt"></i>
<p>Recurring Invoices</p>
</a>
</li>
<li class="nav-item">
<a href="revenues.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "revenues.php") { echo "active"; } ?>">
<a href="revenues.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "revenues.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-hand-holding-usd"></i>
<p>Revenues</p>
</a>
</li>
<li class="nav-item">
<a href="products.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "products.php") { echo "active"; } ?>">
<a href="products.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "products.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-box-open"></i>
<p>Products</p>
</a>
@ -92,60 +104,78 @@
<li class="nav-header mt-3">FINANCE</li>
<li class="nav-item">
<a href="payments.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "payments.php") { echo "active"; } ?>">
<a href="payments.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "payments.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-credit-card"></i>
<p>Payments</p>
</a>
</li>
<li class="nav-item">
<a href="vendors.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "vendors.php") { echo "active"; } ?>">
<a href="vendors.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "vendors.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-building"></i>
<p>Vendors</p>
</a>
</li>
<li class="nav-item">
<a href="expenses.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "expenses.php") { echo "active"; } ?>">
<a href="expenses.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "expenses.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-shopping-cart"></i>
<p>Expenses</p>
</a>
</li>
<li class="nav-item">
<a href="recurring_expenses.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "recurring_expenses.php") { echo "active"; } ?>">
<i class="nav-icon fas fa-clock"></i>
<a href="recurring_expenses.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "recurring_expenses.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-redo-alt"></i>
<p>Recurring Expenses</p>
</a>
</li>
<li class="nav-item">
<a href="accounts.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "accounts.php") { echo "active"; } ?>">
<a href="accounts.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "accounts.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-piggy-bank"></i>
<p>Accounts</p>
</a>
</li>
<li class="nav-item">
<a href="transfers.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "transfers.php") { echo "active"; } ?>">
<a href="transfers.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "transfers.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-exchange-alt"></i>
<p>Transfers</p>
</a>
</li>
<li class="nav-item">
<a href="budget.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "budget.php") { echo "active"; } ?>">
<a href="budget.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "budget.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-balance-scale"></i>
<p>Budget</p>
</a>
</li>
<li class="nav-item">
<a href="trips.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "trips.php") { echo "active"; } ?>">
<i class="nav-icon fas fa-route"></i>
<p>Trips</p>
</a>
</li>
<a href="trips.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "trips.php") {
echo "active";
} ?>">
<i class="nav-icon fas fa-route"></i>
<p>Trips</p>
</a>
</li>
<?php } ?>
<li class="nav-header mt-3">MORE</li>
<li class="nav-item">
<a href="report_income_summary.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "report_income_summary.php") { echo "active"; } ?>">
<a href="report_income_summary.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "report_income_summary.php") {
echo "active";
} ?>">
<i class="fas fa-chart-line nav-icon"></i>
<p>Reports</p>
<i class="fas fa-angle-right nav-icon float-right"></i>
@ -154,13 +184,13 @@
<?php if ($session_user_role == 3) { ?>
<li class="nav-item">
<a href="users.php" class="nav-link">
<i class="nav-icon fas fa-cogs"></i>
<p>Administration</p>
<i class="fas fa-angle-right nav-icon float-right"></i>
</a>
</li>
<li class="nav-item">
<a href="users.php" class="nav-link">
<i class="nav-icon fas fa-cogs"></i>
<p>Administration</p>
<i class="fas fa-angle-right nav-icon float-right"></i>
</a>
</li>
<?php } ?>

View File

@ -34,8 +34,6 @@ if (isset($_GET['ticket_id'])) {
echo "<center><h1 class='text-secondary mt-5'>Nothing to see here</h1><a class='btn btn-lg btn-secondary mt-3' href='tickets.php'><i class='fa fa-fw fa-arrow-left'></i> Go Back</a></center>";
include_once "footer.php";
} else {
$row = mysqli_fetch_array($sql);
@ -58,6 +56,8 @@ if (isset($_GET['ticket_id'])) {
$ticket_details = $purifier->purify($row['ticket_details']);
$ticket_priority = nullable_htmlentities($row['ticket_priority']);
$ticket_billable = intval($row['ticket_billable']);
$ticket_scheduled_for = nullable_htmlentities($row['ticket_schedule']);
$ticket_onsite = nullable_htmlentities($row['ticket_onsite']);
//Set Ticket Bage Color based of priority
if ($ticket_priority == "High") {
@ -191,13 +191,15 @@ if (isset($_GET['ticket_id'])) {
$warranty_check = date('m/d/Y', strtotime('-8 hours'));
if ($dt_value <= $date) {
$dt_value = "Expired on $asset_warranty_expire"; $warranty_status_color ='red';
$dt_value = "Expired on $asset_warranty_expire";
$warranty_status_color = 'red';
} else {
$warranty_status_color = 'green';
}
if ($asset_warranty_expire == "NULL") {
$dt_value = "None"; $warranty_status_color ='red';
$dt_value = "None";
$warranty_status_color = 'red';
}
// Get all ticket replies
@ -226,7 +228,7 @@ if (isset($_GET['ticket_id'])) {
AND ticket_attachment_ticket_id = $ticket_id"
);
?>
?>
<!-- Breadcrumbs-->
<ol class="breadcrumb d-print-none">
@ -244,30 +246,30 @@ if (isset($_GET['ticket_id'])) {
<h3><i class="fas fa-fw fa-life-ring text-secondary mr-2"></i>Ticket <?php echo "$ticket_prefix$ticket_number"; ?> <?php echo $ticket_status_display; ?></h3>
</div>
<?php if ($ticket_status != "Closed") { ?>
<div class="col-3">
<div class="dropdown dropleft text-center d-print-none">
<button class="btn btn-secondary btn-sm float-right" type="button" id="dropdownMenuButton" data-toggle="dropdown">
<i class="fas fa-fw fa-ellipsis-v"></i>
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editTicketModal<?php echo $ticket_id; ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#mergeTicketModal<?php echo $ticket_id; ?>">
<i class="fas fa-fw fa-clone mr-2"></i>Merge
</a>
<a class="dropdown-item" href="#" data-toggle="modal" id="clientChangeTicketModalLoad" data-target="#clientChangeTicketModal">
<i class="fas fa-fw fa-people-carry mr-2"></i>Change Client
</a>
<?php if ($session_user_role == 3) { ?>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_ticket=<?php echo $ticket_id; ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
<div class="col-3">
<div class="dropdown dropleft text-center d-print-none">
<button class="btn btn-secondary btn-sm float-right" type="button" id="dropdownMenuButton" data-toggle="dropdown">
<i class="fas fa-fw fa-ellipsis-v"></i>
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editTicketModal<?php echo $ticket_id; ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<?php } ?>
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#mergeTicketModal<?php echo $ticket_id; ?>">
<i class="fas fa-fw fa-clone mr-2"></i>Merge
</a>
<a class="dropdown-item" href="#" data-toggle="modal" id="clientChangeTicketModalLoad" data-target="#clientChangeTicketModal">
<i class="fas fa-fw fa-people-carry mr-2"></i>Change Client
</a>
<?php if ($session_user_role == 3) { ?>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_ticket=<?php echo $ticket_id; ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
<?php } ?>
</div>
</div>
</div>
</div>
<?php } ?>
</div>
@ -310,18 +312,29 @@ if (isset($_GET['ticket_id'])) {
<span class="input-group-text"><i class="fa fa-fw fa-thermometer-half"></i></span>
</div>
<select class="form-control select2" name="status" required>
<option <?php if ($ticket_status == "In-Progress") {echo "selected";}?> >In-Progress</option>
<option <?php if ($ticket_status == "Pending-Client") {echo "selected";}?> >Pending-Client</option>
<option <?php if ($ticket_status == "Pending-Vendor") {echo "selected";}?> >Pending-Vendor</option>
<option <?php if ($ticket_status == "Pending-Shipment") {echo "selected";}?> >Pending-Shipment</option>
<option <?php if ($ticket_status == "Scheduled") {echo "selected";}?> >Scheduled</option>
<?php if($config_ticket_autoclose) { ?>
<option <?php if ($ticket_status == 'Auto Close') { echo "selected"; } ?> >Auto Close</option>
<option <?php if ($ticket_status == "In-Progress") {
echo "selected";
} ?>>In-Progress</option>
<option <?php if ($ticket_status == "Pending-Client") {
echo "selected";
} ?>>Pending-Client</option>
<option <?php if ($ticket_status == "Pending-Vendor") {
echo "selected";
} ?>>Pending-Vendor</option>
<option <?php if ($ticket_status == "Pending-Shipment") {
echo "selected";
} ?>>Pending-Shipment</option>
<?php if ($config_ticket_autoclose) { ?>
<option <?php if ($ticket_status == 'Auto Close') {
echo "selected";
} ?>>Auto Close</option>
<?php } ?>
</select>
</div>
</div>
<div class="custom-tt-horizontal-spacing"></div> <!-- Add custom class for smaller spacing -->
<!-- Time Tracking -->
@ -362,16 +375,16 @@ if (isset($_GET['ticket_id'])) {
<div class="form-row">
<?php if(!empty($contact_email && $contact_email !== $session_email)){ ?>
<?php if (!empty($contact_email && $contact_email !== $session_email)) { ?>
<div class="col-md-2">
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="ticket_reply_type_checkbox" name="public_reply_type" value="1" checked>
<label class="custom-control-label" for="ticket_reply_type_checkbox">Email contact<br><small class="text-secondary">(Public Update)</small></label>
<div class="col-md-2">
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="ticket_reply_type_checkbox" name="public_reply_type" value="1" checked>
<label class="custom-control-label" for="ticket_reply_type_checkbox">Email contact<br><small class="text-secondary">(Public Update)</small></label>
</div>
</div>
</div>
</div>
<?php } ?>
@ -421,9 +434,15 @@ if (isset($_GET['ticket_id'])) {
AND ticket_attachment_ticket_id = $ticket_id"
);
?>
?>
<div class="card card-outline <?php if ($ticket_reply_type == 'Internal') { echo "card-dark"; } elseif ($ticket_reply_type == 'Client') {echo "card-warning"; } else { echo "card-info"; } ?> mb-3">
<div class="card card-outline <?php if ($ticket_reply_type == 'Internal') {
echo "card-dark";
} elseif ($ticket_reply_type == 'Client') {
echo "card-warning";
} else {
echo "card-info";
} ?> mb-3">
<div class="card-header">
<h3 class="card-title">
<div class="media">
@ -439,7 +458,9 @@ if (isset($_GET['ticket_id'])) {
<div class="media-body">
<?php echo $ticket_reply_by_display; ?>
<div>
<small class="text-muted"><?php echo $ticket_reply_created_at; ?> <?php if (!empty($ticket_reply_updated_at)) { echo "modified: $ticket_reply_updated_at"; } ?></small>
<small class="text-muted"><?php echo $ticket_reply_created_at; ?> <?php if (!empty($ticket_reply_updated_at)) {
echo "modified: $ticket_reply_updated_at";
} ?></small>
</div>
<?php if ($ticket_reply_type !== "Client") { ?>
<div>
@ -487,11 +508,9 @@ if (isset($_GET['ticket_id'])) {
</div>
<?php
<?php
require "ticket_reply_edit_modal.php";
}
?>
@ -566,7 +585,7 @@ if (isset($_GET['ticket_id'])) {
$prev_ticket_id = intval($prev_ticket_row['ticket_id']);
$prev_ticket_subject = nullable_htmlentities($prev_ticket_row['ticket_subject']);
$prev_ticket_status = nullable_htmlentities($prev_ticket_row['ticket_status']);
?>
?>
<hr>
<div>
@ -577,7 +596,7 @@ if (isset($_GET['ticket_id'])) {
<i class="fa fa-fw fa-hourglass-start text-secondary ml-1 mr-2"></i><strong>Status:</strong>
<span class="text-success"><?php echo $prev_ticket_status; ?></span>
</div>
<?php } ?>
<?php } ?>
<?php } else { ?>
<div class="d-print-none">
@ -602,11 +621,10 @@ if (isset($_GET['ticket_id'])) {
while ($ticket_watcher_row = mysqli_fetch_array($sql_ticket_watchers)) {
$watcher_id = intval($ticket_watcher_row['watcher_id']);
$ticket_watcher_email = nullable_htmlentities($ticket_watcher_row['watcher_email']);
?>
?>
<div class='mt-1'>
<i class="fa fa-fw fa-eye text-secondary ml-1 mr-2"></i><?php echo $ticket_watcher_email; ?>
<a class="confirm-link"
href="post.php?delete_ticket_watcher=<?php echo $watcher_id; ?>">
<a class="confirm-link" href="post.php?delete_ticket_watcher=<?php echo $watcher_id; ?>">
<i class="fas fa-fw fa-times text-secondary ml-1"></i>
</a>
</div>
@ -642,15 +660,25 @@ if (isset($_GET['ticket_id'])) {
<div class="mt-1">
<i class="fa fa-fw fa-comment-dots text-secondary ml-1 mr-2"></i>Feedback: <?php echo $ticket_feedback; ?>
</div>
<?php } ?>
<?php }
<?php if (!empty($ticket_total_reply_time)) { ?>
if (!empty($ticket_scheduled_for)) { ?>
<div class="mt-1">
<i class="fa fa-fw fa-calendar-check text-secondary ml-1 mr-2"></i>Scheduled for: <a href="#" data-toggle="modal" data-target="#editTicketScheduleModal"><?php echo $ticket_scheduled_for; ?></a>
</div>
<?php } else { ?>
<div class="mt-1">
<i class="fa fa-fw fa-calendar-check text-secondary ml-1 mr-2"></i>Scheduled for: <a href="#" data-toggle="modal" data-target="#editTicketScheduleModal">Add</a>
</div>
<?php }
if (!empty($ticket_total_reply_time)) { ?>
<div class="mt-1">
<i class="far fa-fw fa-clock text-secondary ml-1 mr-2"></i>Total time worked: <?php echo $ticket_total_reply_time; ?>
</div>
<?php } ?>
<?php }
<?php if ($config_module_enable_accounting) { ?>
if ($config_module_enable_accounting) { ?>
<div class="mt-1">
<i class="fa fa-fw fa-dollar-sign text-secondary ml-1 mr-2"></i>Billable:
<a href="#" data-toggle="modal" data-target="#editTicketBillableModal<?php echo $ticket_id; ?>">
@ -680,7 +708,7 @@ if (isset($_GET['ticket_id'])) {
<?php } else { ?>
<div>
<a href='client_asset_details.php?client_id=<?php echo $client_id?>&asset_id=<?php echo $asset_id?>' ><i class="fa fa-fw fa-desktop text-secondary ml-1 mr-2"></i><strong><?php echo $asset_name; ?></strong></a>
<a href='client_asset_details.php?client_id=<?php echo $client_id ?>&asset_id=<?php echo $asset_id ?>'><i class="fa fa-fw fa-desktop text-secondary ml-1 mr-2"></i><strong><?php echo $asset_name; ?></strong></a>
</div>
<?php if (!empty($asset_os)) { ?>
@ -744,12 +772,12 @@ if (isset($_GET['ticket_id'])) {
$service_ticket_status = nullable_htmlentities($row['ticket_status']);
$service_ticket_created_at = nullable_htmlentities($row['ticket_created_at']);
$service_ticket_updated_at = nullable_htmlentities($row['ticket_updated_at']);
?>
?>
<p>
<i class="fas fa-fw fa-ticket-alt"></i>
Ticket: <a href="ticket.php?ticket_id=<?php echo $service_ticket_id; ?>"><?php echo "$service_ticket_prefix$service_ticket_number" ?></a> <?php echo "on $service_ticket_created_at - <b>$service_ticket_subject</b> ($service_ticket_status)"; ?>
</p>
<?php
<?php
}
?>
</div>
@ -761,9 +789,11 @@ if (isset($_GET['ticket_id'])) {
</div>
</div>
<?php } // End Ticket asset Count ?>
<?php } // End Ticket asset Count
?>
<?php } // End if asset_id == 0 else ?>
<?php } // End if asset_id == 0 else
?>
</div>
<!-- End Asset card -->
@ -811,7 +841,8 @@ if (isset($_GET['ticket_id'])) {
</div>
<?php } ?>
<?php } //End Else ?>
<?php } //End Else
?>
</div>
<!-- End Vendor card -->
@ -824,18 +855,24 @@ if (isset($_GET['ticket_id'])) {
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
</div>
<select class="form-control select2" name="assigned_to" <?php if ($ticket_status == "Closed") {echo "disabled";} ?>>
<select class="form-control select2" name="assigned_to" <?php if ($ticket_status == "Closed") {
echo "disabled";
} ?>>
<option value="0">Not Assigned</option>
<?php
while ($row = mysqli_fetch_array($sql_assign_to_select)) {
$user_id = intval($row['user_id']);
$user_name = nullable_htmlentities($row['user_name']); ?>
<option <?php if ($ticket_assigned_to == $user_id) { echo "selected"; } ?> value="<?php echo $user_id; ?>"><?php echo $user_name; ?></option>
<option <?php if ($ticket_assigned_to == $user_id) {
echo "selected";
} ?> value="<?php echo $user_id; ?>"><?php echo $user_name; ?></option>
<?php } ?>
</select>
<div class="input-group-append d-print-none">
<button type="submit" class="btn btn-primary" name="assign_ticket" <?php if ($ticket_status == "Closed") {echo "disabled";} ?>><i class="fas fa-check"></i></button>
<button type="submit" class="btn btn-primary" name="assign_ticket" <?php if ($ticket_status == "Closed") {
echo "disabled";
} ?>><i class="fas fa-check"></i></button>
</div>
</div>
</div>
@ -859,7 +896,7 @@ if (isset($_GET['ticket_id'])) {
</div>
<?php
<?php
require_once "ticket_edit_modal.php";
require_once "ticket_edit_contact_modal.php";
@ -874,22 +911,29 @@ if (isset($_GET['ticket_id'])) {
require_once "ticket_change_client_modal.php";
require_once "ticket_edit_schedule_modal.php";
require_once "ticket_merge_modal.php";
if ($config_module_enable_accounting) {
require_once "ticket_edit_billable_modal.php";
require_once "ticket_invoice_add_modal.php";
}
}
}
require_once "footer.php";
?> <script src="js/show_modals.js"></script> <?php
?>
if ($ticket_status !== "Closed") { ?>
<script src="js/show_modals.js"></script> <?php
if ($ticket_status !== "Closed") { ?>
<!-- Ticket Time Tracking JS -->
<script src="js/ticket_time_tracking.js"></script>
@ -898,5 +942,4 @@ if ($ticket_status !== "Closed") { ?>
<script src="js/ticket_button_respond_note.js"></script>
<?php } ?>
<script src="js/pretty_content.js"></script>
<script src="js/pretty_content.js"></script>

View File

@ -0,0 +1,43 @@
<div class="modal" id="editTicketScheduleModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title">
<i class="fa fa-fw fa-user mr-2"></i>
Edit Scheduled Time for <strong><?php echo "$ticket_prefix$ticket_number"; ?></strong>
</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body bg-white">
<input type="hidden" name="ticket_id" value="<?php echo $ticket_id; ?>">
<div class="form-group">
<label>Scheduled Date and Time</label>
<?php if (!$ticket_scheduled_for) { ?>
<input type="datetime-local" class="form-control" name="scheduled_date_time" placeholder="Scheduled Date & Time">
<?php } else { ?>
<input type="datetime-local" class="form-control" name="scheduled_date_time" value="<?php echo $ticket_scheduled_for; ?>">
<?php } ?>
</div>
<div class="form-group">
<label>Onsite</label>
<select class="form-control" name="onsite">
<option value="0" <?php if ($ticket_onsite == 0) echo "selected"; ?>>No</option>
<option value="1" <?php if ($ticket_onsite == 1) echo "selected"; ?>>Yes</option>
</select>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="edit_ticket_schedule" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -39,7 +39,7 @@ if (isset($_GET['assigned']) & !empty($_GET['assigned'])) {
if ($_GET['assigned'] == 'unassigned') {
$ticket_assigned_filter = 'AND ticket_assigned_to = 0';
} else {
$ticket_assigned_filter = 'AND ticket_assigned_to = '.intval($_GET['assigned']);
$ticket_assigned_filter = 'AND ticket_assigned_to = ' . intval($_GET['assigned']);
}
} else {
// Default - any
@ -77,6 +77,11 @@ $sql_total_tickets_closed = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS to
$row = mysqli_fetch_array($sql_total_tickets_closed);
$total_tickets_closed = intval($row['total_tickets_closed']);
//Get Total Scheduled tickets
$sql_total_scheduled_tickets = mysqli_query($mysqli, "SELECT COUNT(scheduled_ticket_id) AS total_scheduled_tickets FROM scheduled_tickets");
$row = mysqli_fetch_array($sql_total_scheduled_tickets);
$total_scheduled_tickets = intval($row['total_scheduled_tickets']);
//Get Unassigned tickets
$sql_total_tickets_unassigned = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS total_tickets_unassigned FROM tickets WHERE ticket_assigned_to = '0' AND ticket_status != 'Closed'");
$row = mysqli_fetch_array($sql_total_tickets_unassigned);
@ -112,10 +117,11 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']);
<div class="row">
<div class="col-sm-4">
<div class="input-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search Tickets">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) {
echo stripslashes(nullable_htmlentities($q));
} ?>" placeholder="Search Tickets">
<div class="input-group-append">
<button class="btn btn-secondary" type="button" data-toggle="collapse"
data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
@ -134,7 +140,11 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']);
<a href="?assigned=unassigned" class="btn btn-outline-danger">
<i class="fa fa-fw fa-exclamation-triangle mr-2"></i>Unassigned Tickets | <strong> <?php echo $total_tickets_unassigned; ?></strong>
</a>
<a href="scheduled_tickets.php" class="btn btn-outline-info">
<i class="fa fa-fw fa-redo-alt mr-2"></i>Recurring Tickets | <strong> <?php echo $total_scheduled_tickets; ?></strong>
</a>
<div class="dropdown ml-2" id="bulkActionButton" hidden>
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
@ -159,51 +169,53 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']);
</div>
</div>
</div>
</div>
<div class="collapse <?php if (!empty($_GET['dtf']) || (isset($_GET['canned_date']) && $_GET['canned_date'] !== "custom") || (isset($_GET['status']) && is_array($_GET['status']))) { echo "show"; } ?>" id="advancedFilter">
<div class="collapse <?php if (!empty($_GET['dtf']) || (isset($_GET['canned_date']) && $_GET['canned_date'] !== "custom") || (isset($_GET['status']) && is_array($_GET['status']))) {
echo "show";
} ?>" id="advancedFilter">
<div class="row">
<div class="col-md-2">
<div class="form-group">
<label>Canned Date</label>
<select class="form-control select2" name="canned_date">
<option <?php if ($_GET['canned_date'] == "custom") {
echo "selected";
} ?> value="custom">Custom
echo "selected";
} ?> value="custom">Custom
</option>
<option <?php if ($_GET['canned_date'] == "today") {
echo "selected";
} ?> value="today">Today
echo "selected";
} ?> value="today">Today
</option>
<option <?php if ($_GET['canned_date'] == "yesterday") {
echo "selected";
} ?> value="yesterday">Yesterday
echo "selected";
} ?> value="yesterday">Yesterday
</option>
<option <?php if ($_GET['canned_date'] == "thisweek") {
echo "selected";
} ?> value="thisweek">This Week
echo "selected";
} ?> value="thisweek">This Week
</option>
<option <?php if ($_GET['canned_date'] == "lastweek") {
echo "selected";
} ?> value="lastweek">Last Week
echo "selected";
} ?> value="lastweek">Last Week
</option>
<option <?php if ($_GET['canned_date'] == "thismonth") {
echo "selected";
} ?> value="thismonth">This Month
echo "selected";
} ?> value="thismonth">This Month
</option>
<option <?php if ($_GET['canned_date'] == "lastmonth") {
echo "selected";
} ?> value="lastmonth">Last Month
echo "selected";
} ?> value="lastmonth">Last Month
</option>
<option <?php if ($_GET['canned_date'] == "thisyear") {
echo "selected";
} ?> value="thisyear">This Year
echo "selected";
} ?> value="thisyear">This Year
</option>
<option <?php if ($_GET['canned_date'] == "lastyear") {
echo "selected";
} ?> value="lastyear">Last Year
echo "selected";
} ?> value="lastyear">Last Year
</option>
</select>
</div>
@ -223,14 +235,28 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']);
<div class="col-md-2">
<div class="form-group">
<label>Ticket Status</label>
<select class="form-control select2" name="status[]" data-placeholder = "Select Status" multiple>
<option value="In-Progress" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('In-Progress', $_GET['status'])) { echo 'selected'; } ?> >In-Progress</option>
<option value="Client-Replied" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('Client-Replied', $_GET['status'])) { echo 'selected'; } ?> >Client-Replied</option>
<option value="Pending-Client" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('Pending-Client', $_GET['status'])) { echo 'selected'; } ?> >Pending-Client</option>
<option value="Pending-Vendor" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('Pending-Vendor', $_GET['status'])) { echo 'selected'; } ?> >Pending-Vendor</option>
<option value="Pending-Shipment" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('Pending-Shipment', $_GET['status'])) { echo 'selected'; } ?> >Pending-Shipment</option>
<option value="Scheduled" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('Scheduled', $_GET['status'])) { echo 'selected'; } ?> >Scheduled</option>
<option value="Closed" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('Closed', $_GET['status'])) { echo 'selected'; } ?> >Closed</option>
<select class="form-control select2" name="status[]" data-placeholder="Select Status" multiple>
<option value="In-Progress" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('In-Progress', $_GET['status'])) {
echo 'selected';
} ?>>In-Progress</option>
<option value="Client-Replied" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('Client-Replied', $_GET['status'])) {
echo 'selected';
} ?>>Client-Replied</option>
<option value="Pending-Client" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('Pending-Client', $_GET['status'])) {
echo 'selected';
} ?>>Pending-Client</option>
<option value="Pending-Vendor" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('Pending-Vendor', $_GET['status'])) {
echo 'selected';
} ?>>Pending-Vendor</option>
<option value="Pending-Shipment" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('Pending-Shipment', $_GET['status'])) {
echo 'selected';
} ?>>Pending-Shipment</option>
<option value="Scheduled" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('Scheduled', $_GET['status'])) {
echo 'selected';
} ?>>Scheduled</option>
<option value="Closed" <?php if (isset($_GET['status']) && is_array($_GET['status']) && in_array('Closed', $_GET['status'])) {
echo 'selected';
} ?>>Closed</option>
</select>
</div>
</div>
@ -238,17 +264,23 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']);
<div class="form-group">
<label>Assigned to</label>
<select class="form-control select2" name="assigned">
<option value="" <?php if ($ticket_assigned_filter == "") {echo "selected";}?> >Any</option>
<option value="unassigned"<?php if ($ticket_assigned_filter == "0") {echo "selected";}?> >Unassigned</option>
<option value="" <?php if ($ticket_assigned_filter == "") {
echo "selected";
} ?>>Any</option>
<option value="unassigned" <?php if ($ticket_assigned_filter == "0") {
echo "selected";
} ?>>Unassigned</option>
<?php
$sql_assign_to = mysqli_query($mysqli, "SELECT * FROM users WHERE user_archived_at IS NULL ORDER BY user_name ASC");
while ($row = mysqli_fetch_array($sql_assign_to)) {
$user_id = intval($row['user_id']);
$user_name = nullable_htmlentities($row['user_name']);
?>
<option <?php if ($ticket_assigned_filter == $user_id) { echo "selected"; } ?> value="<?php echo $user_id; ?>"><?php echo $user_name; ?></option>
<?php
?>
<option <?php if ($ticket_assigned_filter == $user_id) {
echo "selected";
} ?> value="<?php echo $user_id; ?>"><?php echo $user_name; ?></option>
<?php
}
?>
@ -263,192 +295,186 @@ $user_active_assigned_tickets = intval($row['total_tickets_assigned']);
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if (!$num_rows[0]) { echo "d-none"; } ?>">
<tr>
<td>
<div class="form-check">
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)">
</div>
</td>
<th><a class="text-dark"
href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_number&order=<?php echo $disp; ?>">Number</a>
</th>
<th><a class="text-dark"
href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_subject&order=<?php echo $disp; ?>">Subject</a>
</th>
<th><a class="text-dark"
href="?<?php echo $url_query_strings_sort; ?>&sort=client_name&order=<?php echo $disp; ?>">Client / Contact</a>
</th>
<?php if ($config_module_enable_accounting) {
?>
<th class="text-center"><a class="text-dark"
href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_billable&order=<?php echo $disp; ?>">Billable</a>
<thead class="text-dark <?php if (!$num_rows[0]) {
echo "d-none";
} ?>">
<tr>
<td>
<div class="form-check">
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)">
</div>
</td>
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_number&order=<?php echo $disp; ?>">Number</a>
</th>
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_subject&order=<?php echo $disp; ?>">Subject</a>
</th>
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=client_name&order=<?php echo $disp; ?>">Client / Contact</a>
</th>
<?php if ($config_module_enable_accounting) {
?>
<th class="text-center"><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_billable&order=<?php echo $disp; ?>">Billable</a>
</th>
<?php
}
?>
}
?>
<th><a class="text-dark"
href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_priority&order=<?php echo $disp; ?>">Priority</a>
</th>
<th><a class="text-dark"
href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_status&order=<?php echo $disp; ?>">Status</a>
<th><a class="text-dark"
href="?<?php echo $url_query_strings_sort; ?>&sort=user_name&order=<?php echo $disp; ?>">Assigned</a>
</th>
<th><a class="text-dark"
href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_updated_at&order=<?php echo $disp; ?>">Last Response</a>
</th>
<th><a class="text-dark"
href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_created_at&order=<?php echo $disp; ?>">Created</a>
</th>
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_priority&order=<?php echo $disp; ?>">Priority</a>
</th>
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_status&order=<?php echo $disp; ?>">Status</a>
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=user_name&order=<?php echo $disp; ?>">Assigned</a>
</th>
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_updated_at&order=<?php echo $disp; ?>">Last Response</a>
</th>
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ticket_created_at&order=<?php echo $disp; ?>">Created</a>
</th>
</tr>
</tr>
</thead>
<tbody>
<?php
<?php
while ($row = mysqli_fetch_array($sql)) {
$ticket_id = intval($row['ticket_id']);
$ticket_prefix = nullable_htmlentities($row['ticket_prefix']);
$ticket_number = intval($row['ticket_number']);
$ticket_subject = nullable_htmlentities($row['ticket_subject']);
$ticket_priority = nullable_htmlentities($row['ticket_priority']);
$ticket_status = nullable_htmlentities($row['ticket_status']);
$ticket_billable = intval($row['ticket_billable']);
$ticket_vendor_ticket_number = nullable_htmlentities($row['ticket_vendor_ticket_number']);
$ticket_created_at = nullable_htmlentities($row['ticket_created_at']);
$ticket_created_at_time_ago = timeAgo($row['ticket_created_at']);
$ticket_updated_at = nullable_htmlentities($row['ticket_updated_at']);
$ticket_updated_at_time_ago = timeAgo($row['ticket_updated_at']);
if (empty($ticket_updated_at)) {
if ($ticket_status == "Closed") {
$ticket_updated_at_display = "<p>Never</p>";
while ($row = mysqli_fetch_array($sql)) {
$ticket_id = intval($row['ticket_id']);
$ticket_prefix = nullable_htmlentities($row['ticket_prefix']);
$ticket_number = intval($row['ticket_number']);
$ticket_subject = nullable_htmlentities($row['ticket_subject']);
$ticket_priority = nullable_htmlentities($row['ticket_priority']);
$ticket_status = nullable_htmlentities($row['ticket_status']);
$ticket_billable = intval($row['ticket_billable']);
$ticket_vendor_ticket_number = nullable_htmlentities($row['ticket_vendor_ticket_number']);
$ticket_created_at = nullable_htmlentities($row['ticket_created_at']);
$ticket_created_at_time_ago = timeAgo($row['ticket_created_at']);
$ticket_updated_at = nullable_htmlentities($row['ticket_updated_at']);
$ticket_updated_at_time_ago = timeAgo($row['ticket_updated_at']);
if (empty($ticket_updated_at)) {
if ($ticket_status == "Closed") {
$ticket_updated_at_display = "<p>Never</p>";
} else {
$ticket_updated_at_display = "<p class='text-danger'>Never</p>";
}
} else {
$ticket_updated_at_display = "<p class='text-danger'>Never</p>";
$ticket_updated_at_display = "$ticket_updated_at_time_ago<br><small class='text-secondary'>$ticket_updated_at</small>";
}
} else {
$ticket_updated_at_display = "$ticket_updated_at_time_ago<br><small class='text-secondary'>$ticket_updated_at</small>";
}
$ticket_closed_at = nullable_htmlentities($row['ticket_closed_at']);
$client_id = intval($row['ticket_client_id']);
$client_name = nullable_htmlentities($row['client_name']);
$contact_id = intval($row['ticket_contact_id']);
$contact_name = nullable_htmlentities($row['contact_name']);
$contact_title = nullable_htmlentities($row['contact_title']);
$contact_email = nullable_htmlentities($row['contact_email']);
$contact_phone = formatPhoneNumber($row['contact_phone']);
$contact_extension = nullable_htmlentities($row['contact_extension']);
$contact_mobile = formatPhoneNumber($row['contact_mobile']);
if ($ticket_status == "Pending-Assignment") {
$ticket_status_color = "danger";
} elseif ($ticket_status == "Assigned") {
$ticket_status_color = "primary";
} elseif ($ticket_status == "In-Progress") {
$ticket_status_color = "success";
} elseif ($ticket_status == "Closed") {
$ticket_status_color = "dark";
} elseif ($ticket_status == "Auto Close") {
$ticket_status_color = "dark";
} elseif ($ticket_status == "Client-Replied") {
$ticket_status_color = "warning";
} else{
$ticket_status_color = "secondary";
}
if ($ticket_priority == "High") {
$ticket_priority_color = "danger";
} elseif ($ticket_priority == "Medium") {
$ticket_priority_color = "warning";
} else{
$ticket_priority_color = "info";
}
$ticket_assigned_to = intval($row['ticket_assigned_to']);
if (empty($ticket_assigned_to)) {
if ($ticket_status == "Closed") {
$ticket_assigned_to_display = "<p>Not Assigned</p>";
$ticket_closed_at = nullable_htmlentities($row['ticket_closed_at']);
$client_id = intval($row['ticket_client_id']);
$client_name = nullable_htmlentities($row['client_name']);
$contact_id = intval($row['ticket_contact_id']);
$contact_name = nullable_htmlentities($row['contact_name']);
$contact_title = nullable_htmlentities($row['contact_title']);
$contact_email = nullable_htmlentities($row['contact_email']);
$contact_phone = formatPhoneNumber($row['contact_phone']);
$contact_extension = nullable_htmlentities($row['contact_extension']);
$contact_mobile = formatPhoneNumber($row['contact_mobile']);
if ($ticket_status == "Pending-Assignment") {
$ticket_status_color = "danger";
} elseif ($ticket_status == "Assigned") {
$ticket_status_color = "primary";
} elseif ($ticket_status == "In-Progress") {
$ticket_status_color = "success";
} elseif ($ticket_status == "Closed") {
$ticket_status_color = "dark";
} elseif ($ticket_status == "Auto Close") {
$ticket_status_color = "dark";
} elseif ($ticket_status == "Client-Replied") {
$ticket_status_color = "warning";
} else {
$ticket_assigned_to_display = "<p class='text-danger'>Not Assigned</p>";
$ticket_status_color = "secondary";
}
} else {
$ticket_assigned_to_display = nullable_htmlentities($row['user_name']);
}
if (empty($contact_name)) {
$contact_display = "-";
} else {
$contact_display = "$contact_name<br><small class='text-secondary'>$contact_email</small>";
}
if ($ticket_priority == "High") {
$ticket_priority_color = "danger";
} elseif ($ticket_priority == "Medium") {
$ticket_priority_color = "warning";
} else {
$ticket_priority_color = "info";
}
$ticket_assigned_to = intval($row['ticket_assigned_to']);
if (empty($ticket_assigned_to)) {
if ($ticket_status == "Closed") {
$ticket_assigned_to_display = "<p>Not Assigned</p>";
} else {
$ticket_assigned_to_display = "<p class='text-danger'>Not Assigned</p>";
}
} else {
$ticket_assigned_to_display = nullable_htmlentities($row['user_name']);
}
$asset_id = intval($row['ticket_asset_id']);
$vendor_id = intval($row['ticket_vendor_id']);
if (empty($contact_name)) {
$contact_display = "-";
} else {
$contact_display = "$contact_name<br><small class='text-secondary'>$contact_email</small>";
}
$asset_id = intval($row['ticket_asset_id']);
$vendor_id = intval($row['ticket_vendor_id']);
?>
<tr class="<?php if(empty($ticket_updated_at)) { echo "text-bold"; }?>">
<td>
<?php if($ticket_status !== "Closed") { ?>
<div class="form-check">
<input class="form-check-input bulk-select" type="checkbox" name="ticket_ids[]" value="<?php echo $ticket_id ?>">
</div>
<?php } ?>
</td>
<td>
<a href="ticket.php?ticket_id=<?php echo $ticket_id; ?>">
<span class="badge badge-pill badge-secondary p-3"><?php echo "$ticket_prefix$ticket_number"; ?></span>
</a>
</td>
<td>
<a href="ticket.php?ticket_id=<?php echo $ticket_id; ?>"><?php echo $ticket_subject; ?></a>
</td>
<td>
<a href="client_tickets.php?client_id=<?php echo $client_id; ?>"><strong><?php echo $client_name; ?></strong></a>
<tr class="<?php if (empty($ticket_updated_at)) {
echo "text-bold";
} ?>">
<td>
<?php if ($ticket_status !== "Closed") { ?>
<div class="form-check">
<input class="form-check-input bulk-select" type="checkbox" name="ticket_ids[]" value="<?php echo $ticket_id ?>">
</div>
<?php } ?>
</td>
<td>
<a href="ticket.php?ticket_id=<?php echo $ticket_id; ?>">
<span class="badge badge-pill badge-secondary p-3"><?php echo "$ticket_prefix$ticket_number"; ?></span>
</a>
</td>
<td>
<a href="ticket.php?ticket_id=<?php echo $ticket_id; ?>"><?php echo $ticket_subject; ?></a>
</td>
<td>
<a href="client_tickets.php?client_id=<?php echo $client_id; ?>"><strong><?php echo $client_name; ?></strong></a>
<div class="mt-1"><?php echo $contact_display; ?></div>
</td>
<?php if ($config_module_enable_accounting) {
?>
<td class="text-center">
<a href="#" data-toggle="modal" data-target="#editTicketBillableModal<?php echo $ticket_id; ?>">
<div class="mt-1"><?php echo $contact_display; ?></div>
</td>
<?php if ($config_module_enable_accounting) {
?>
<td class="text-center">
<a href="#" data-toggle="modal" data-target="#editTicketBillableModal<?php echo $ticket_id; ?>">
<?php
if ($ticket_billable == 1) {
echo "<span class='badge badge-pill badge-success'>$</span>";
} else {
echo "<span class='badge badge-pill badge-secondary'>X</span>";
}
?>
</td>
<?php
if ($ticket_billable == 1) {
echo "<span class='badge badge-pill badge-success'>$</span>";
} else {
echo "<span class='badge badge-pill badge-secondary'>X</span>";
}
?></td>
<?php
}
?>
<td><a href="#" data-toggle="modal" data-target="#editTicketPriorityModal<?php echo $ticket_id; ?>"><span class='p-2 badge badge-pill badge-<?php echo $ticket_priority_color; ?>'><?php echo $ticket_priority; ?></span></a></td>
<td><span class='p-2 badge badge-pill badge-<?php echo $ticket_status_color; ?>'><?php echo $ticket_status; ?></span></td>
<td><a href="#" data-toggle="modal" data-target="#assignTicketModal<?php echo $ticket_id; ?>"><?php echo $ticket_assigned_to_display; ?></a></td>
<td><?php echo $ticket_updated_at_display; ?></td>
<td>
<?php echo $ticket_created_at_time_ago; ?>
<br>
<small class="text-secondary"><?php echo $ticket_created_at; ?></small>
</td>
</tr>
?>
<td><a href="#" data-toggle="modal" data-target="#editTicketPriorityModal<?php echo $ticket_id; ?>"><span class='p-2 badge badge-pill badge-<?php echo $ticket_priority_color; ?>'><?php echo $ticket_priority; ?></span></a></td>
<td><span class='p-2 badge badge-pill badge-<?php echo $ticket_status_color; ?>'><?php echo $ticket_status; ?></span></td>
<td><a href="#" data-toggle="modal" data-target="#assignTicketModal<?php echo $ticket_id; ?>"><?php echo $ticket_assigned_to_display; ?></a></td>
<td><?php echo $ticket_updated_at_display; ?></td>
<td>
<?php echo $ticket_created_at_time_ago; ?>
<br>
<small class="text-secondary"><?php echo $ticket_created_at; ?></small>
</td>
</tr>
<?php
if ($ticket_status !== "Closed") {
// Temp performance boost for closed tickets, until we move to dynamic modals
if ($ticket_status !== "Closed") {
// Temp performance boost for closed tickets, until we move to dynamic modals
require "ticket_assign_modal.php";
require "ticket_assign_modal.php";
require "ticket_edit_priority_modal.php";
require "ticket_edit_priority_modal.php";
if ($config_module_enable_accounting) {
require "ticket_edit_billable_modal.php";
if ($config_module_enable_accounting) {
require "ticket_edit_billable_modal.php";
}
}
}
}
?>
?>
</tbody>
</table>