the full example, can be found at :
https://github.com/pipiscrew/fullcalendar-php-example
When is not logged-in, view full calendar, with read only permissions.
this is the view of admin
introduction :
the application runs in index.php, all workers are available to view the calendar. When user logged-in (login.php) has the point3 - point2 - at point4 can move the event(s).
point1 - anchor drives to login.php / when logged-in drivers to logout.php
point2 - lists the workers, added from point3 (saved to local sqlite)
I will not explain the login/logout already done, on previous articles. We will focus, on security and how manipulate the fullcalendar.js aka https://fullcalendar.io/
all the php scripts, contains as header :
index.php - references
the calendar html is as the following :
Line 09 = if admin logged-in make the drag&drop area visible, make a DB connection enumerate the users.
Line 39 = when enumerating the users, add HTML5 attribute (userid) to each div, will be used for dbase record.
Line 53 = when admin use
otherwise use
for calendar.
Line 68 = modal with dayoff type.
Line 79-87 = modal hardcoded buttons, represent the dayoff types. All has the same ID (btn_dayoff_type) and different HTML5 data attribute (eventid), will be used for dbase record.
the js has as :
Line 23 = get data from dbase the query (GET) as : get_events.php?start=2016-08-29&end=2016-10-08&_=1474657959047
Line 33 = remove edit/drop/click events & properties when is not admin
Line 36-49 = when a worker dropped to calendar, store to public vars the worker_html_element (contains the data-userid) + in which day dropped (*story1)
Line 51 = this event occured when the user move the timebar to another day, we read the dropped day and update the record (the get_events.php, returns also the record ID, will see it later on get_events.php)
Line 81 = this occured when the admin make a plain click to timebar, I used it for deletion!
Line 104 = after success del_dayoff.php postback remove the timebar from calendar
*story1 :
this opens the modal form, admin is able to choose the dayoff type as :
when a button type clicked - raise this event :
Line 2 = Init the calendar.
Line 27 = Call store_day.
the store_day is as:
get timebars :
add time bar :
update time bar :
the sqlite dbase is :
sqlite doesnt have date field type, using dddd/mm/yy on TEXT field type, we are able to execute a query (get_events.php) like :
more at https://www.sqlite.org/lang_datefunc.html
another example FullCalendar (display appointments, saved from erp)
https://www.pipiscrew.com/2015/01/js-fullcalendar/
https://github.com/pipiscrew/fullcalendar-php-example
When is not logged-in, view full calendar, with read only permissions.

this is the view of admin

introduction :
the application runs in index.php, all workers are available to view the calendar. When user logged-in (login.php) has the point3 - point2 - at point4 can move the event(s).
point1 - anchor drives to login.php / when logged-in drivers to logout.php
point2 - lists the workers, added from point3 (saved to local sqlite)
I will not explain the login/logout already done, on previous articles. We will focus, on security and how manipulate the fullcalendar.js aka https://fullcalendar.io/
all the php scripts, contains as header :
JavaScript:
<?php
$is_admin=false;
@session_start();
if (isset($_SESSION["id"])) {
date_default_timezone_set("UTC");
if ($_SESSION["login_expiration"] != date("Y-m-d"))
{
session_destroy();
header("Location: login.php");
exit ;
} else {
$is_admin=true;
}
}
?>
<!DOCTYPE html>
index.php - references
JavaScript:
<script type="text/javascript" src="assets/jquery-3.1.0.min.js"></script>
<script src="assets/bootstrap.min.js"></script>
<script type="text/javascript" src="assets/moment.min.js"></script>
<script type="text/javascript" src="assets/fullcalendar.min.js"></script>
<script src="assets/jquery-ui.min.js"></script> <!-- needed for drag&drop (customized download at official site final size 72kb)
the calendar html is as the following :
JavaScript:
<div class="row">
<div class="col-md-12">
<div class="panel">
<div class="panel-heading">
<div class="panel-title"><h4>CALENDAR</h4></div>
</div>
<div class="panel-body">
<div class="row">
<?php if($is_admin){ ?>
<div class="col-md-3">
<div id="external-events">
<div class="legend">Create Event</div>
<form action="add_user.php" id="create-event">
<div class="input-group">
<div class="inputer">
<div class="input-wrapper">
<input id="event-title" name="event-title" type="text" class="form-control">
</div>
</div>
<div class="input-group-btn">
<button id="add-event" type="submit" class="btn btn-flat btn-default">Add</button>
</div>
</div>
</form>
<div class="legend">Draggable Events</div>
<div class="draggable-events">
<?php
include 'config.php';
$db = connect();
$r = getSet($db,"select * from users order by user_mail", null);
$elements = "";
foreach($r as $row){
if(strpos($row["user_mail"], '@') == FALSE )
echo "<div class=\"fc-event\" data-userid=\"{$row["user_id"]}\">[B]{$row["user_mail"]}[/B] <button type=\"button\" class=\"close\">×</button></div>\r\n";
}
?>
</div>
<input type="checkbox" id="drop-remove" checked/>
<label for="drop-remove">Remove After Drop</label>
<input type="checkbox" id="drop-type"/>
<label for="drop-type">Add as day off</label>
</div>
</div>
<div class="col-md-9">
<?php } else{ ?>
<div class="col-md-12">
<?php }?>
<div id="calendar"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="modaloSUBSECTOR" class="modal fade bs-example-modal-sm" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">[B]×[/B][B]Close[/B]</button>
<h4 class="modal-title" id="mySmallModalLabel">Please choose type </h4>
</div>
<div class="modal-body">
<button type="button" id="btn_dayoff_type" data-eventid="1" class="btn btn-purple btn-block btn-ripple">home office</button>
<button type="button" id="btn_dayoff_type" data-eventid="2" class="btn btn-blue btn-block btn-ripple">vacation</button>
<button type="button" id="btn_dayoff_type" data-eventid="3" class="btn btn-teal btn-block btn-ripple">half-day vacation</button>
<button type="button" id="btn_dayoff_type" data-eventid="4" class="btn btn-deep-orange btn-block btn-ripple">left</button>
<button type="button" id="btn_dayoff_type" data-eventid="5" class="btn btn-success btn-block btn-ripple">compensate</button>
<button type="button" id="btn_dayoff_type" data-eventid="6" class="btn btn-yellow btn-block btn-ripple">sickness/doctor</button>
<button type="button" id="btn_dayoff_type" data-eventid="7" class="btn btn-brown btn-block btn-ripple">training</button>
<button type="button" id="btn_dayoff_type" data-eventid="8" class="btn btn-blue-grey btn-block btn-ripple">public holiday</button>
<button type="button" id="btn_dayoff_type" data-eventid="9" class="btn btn-inverted btn-block btn-ripple">personal leave</button>
<div class="input-group">
<div class="inputer">
<div class="input-wrapper">
<input id="event_comment" name="event_comment" type="text" class="form-control">
</div>
</div>
</div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
Line 09 = if admin logged-in make the drag&drop area visible, make a DB connection enumerate the users.
Line 39 = when enumerating the users, add HTML5 attribute (userid) to each div, will be used for dbase record.
Line 53 = when admin use
JavaScript:
class="col-md-9"
JavaScript:
class="col-md-12"
Line 68 = modal with dayoff type.
Line 79-87 = modal hardcoded buttons, represent the dayoff types. All has the same ID (btn_dayoff_type) and different HTML5 data attribute (eventid), will be used for dbase record.
the js has as :
JavaScript:
//js
<script>
//hold the user selection from modal
var modal_selected_type;
var modal_selected_color;
var modal_event_html_element;
var modal_event_date;
//instatiate calendar element, with our options
var Calendar = {
createCalendar: function() {
var date = new Date(),
m = date.getMonth(),
d = date.getDate(),
y = date.getFullYear();
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
events: {
url: 'get_events.php',
/*error: function() {
$('#script-warning').show();
}*/
},
firstDay: 0,
isRTL: false,
eventLimit: true,
weekends : false,
eventDurationEditable : false, //disable resize
<?php if ($is_admin) { ?>
editable: true,
droppable: true,
drop: function(date) {
//https://fullcalendar.io/docs1/dropping/drop/ -- function( date, allDay, jsEvent, ui ) { }
//the object is
console.log(date);
if ($('#drop-type').is(':checked')) {
} else {
$("#modaloSUBSECTOR").modal('toggle');
}
//store to public variables - use it on modal button-click event + add_to_calendar procedure
modal_event_html_element = $(this);
modal_event_date = date;
},
eventDrop: function(event, delta, revertFunc) {
//https://fullcalendar.io/docs/event_ui/eventDrop/
console.log(event);
var date_dropped = event.start['_d'];
date_dropped = moment(date_dropped).format('YYYY-MM-DD');
$.ajax({
url : "update_dayoff.php",
type: "POST",
data : {day_off_id:event.id, eventdate : date_dropped},
dataType : "json",
success:function(data, textStatus, jqXHR)
{
console.log(data);
if (data!=100){
revertFunc(); //everts the event's start/end date to the values before the drag. This is useful if an ajax call should fail.
alert("ERROR");
}
},
error: function(jqXHR, textStatus, errorThrown)
{
revertFunc();
alert("ERROR - connection error");
}
});
},
eventClick: function(calEvent, jsEvent, view) {
var rec_id = calEvent.id;
console.log(rec_id);
if (confirm(calEvent.title + "\r\n\r\nDo you want to delete")) {
var del_confirm = prompt("Please write the world : delete", "PipisCrew");
if (del_confirm != null) {
if (del_confirm=="delete"){
$.ajax({
url : "del_dayoff.php",
type: "POST",
data : {day_off_id:rec_id},
dataType : "json",
success:function(data, textStatus, jqXHR)
{
console.log(data);
if (data!=100){
alert("ERROR");
}
else
$("#calendar").fullCalendar('removeEvents', rec_id);
//or refresh the calendar only via
//$("#calendar").fullCalendar("refetchEvents");
},
error: function(jqXHR, textStatus, errorThrown)
{
alert("ERROR - connection error");
}
});
}
}
}
}
<?php } ?>
});
},
handleEventDragging: function(obj) {
var eventObject = {
title: $.trim(obj.find('span').text())
};
obj.data('eventObject', eventObject);
obj.draggable({
revert: true,
revertDuration: 0,
zIndex: 1999
});
},
addEvent: function(title) {
title = title.length === 0 ? "Missing Event Title" : title;
var html = $('<div class="fc-event">[B]' + title + '[/B] <button type="button" class="close">×</button></div>');
$('.draggable-events').append(html);
Calendar.handleEventDragging(html);
},
init: function() {
this.createCalendar();
}
}
Line 23 = get data from dbase the query (GET) as : get_events.php?start=2016-08-29&end=2016-10-08&_=1474657959047
Line 33 = remove edit/drop/click events & properties when is not admin
Line 36-49 = when a worker dropped to calendar, store to public vars the worker_html_element (contains the data-userid) + in which day dropped (*story1)
Line 51 = this event occured when the user move the timebar to another day, we read the dropped day and update the record (the get_events.php, returns also the record ID, will see it later on get_events.php)
Line 81 = this occured when the admin make a plain click to timebar, I used it for deletion!
Line 104 = after success del_dayoff.php postback remove the timebar from calendar
*story1 :
this opens the modal form, admin is able to choose the dayoff type as :

when a button type clicked - raise this event :
JavaScript:
$(function() {
Calendar.init();
//modal - event button click
$(document).on("click", "#btn_dayoff_type", function(e) {
e.preventDefault();
//take button properties > assign to public vars
modal_selected_type = $(this).data("eventid"); //eventtype id-is hardcoded to HTMLelement - needed to store it on the record
modal_selected_color = $(this).css("background-color"); //get the color from HTMLelement - needed for calendar draw
console.log("eventid :", modal_selected_type);
console.log("color :", modal_selected_color);
//get HTML5 attribute - is the dbase user_id (var assigned at drop callback)
var user_id = modal_event_html_element.data("userid");
//get the dropped date (var assigned at drop callback)
var date_dropped = modal_event_date['_d']; //ex. Mon Sep 05 2016 02:00:00 GMT+0200 (Central Europe Daylight Time)
date_dropped = moment(date_dropped).format('YYYY-MM-DD'); //moment.min.js - 2016-09-05
var comment = $("#event_comment").val();
console.log("userid :", user_id);
console.log("date dropped :", date_dropped);
console.log("comment :", comment);
store_day(user_id, modal_selected_type, date_dropped, comment);
});
Line 2 = Init the calendar.
Line 27 = Call store_day.
the store_day is as:
JavaScript:
function store_day(userid, typeid, eventdate, comment){
console.log(userid,typeid,eventdate,comment);
$.ajax({
url : "add_dayoff.php",
type: "POST",
data : {userid:userid, eventdate : eventdate, typeid: typeid, comment: comment},
dataType : "json",
success:function(data, textStatus, jqXHR)
{
console.log(data);
if (data==100){
//close modal
$("#modaloSUBSECTOR").modal('toggle');
add_to_calendar();
}
else
alert("ERROR");
},
error: function(jqXHR, textStatus, errorThrown)
{
alert("ERROR - connection error");
}
});
}
//draw it at calendar
function add_to_calendar()
{
////////////////////////////////////////////////////////////////////////
//add to calendar
////////////////////////////////////////////////////////////////////////
var originalEventObject = modal_event_html_element.data('eventObject');
var extendedEventObject = $.extend({}, originalEventObject);
extendedEventObject.start = modal_event_date;
//http://stackoverflow.com/a/7918786
extendedEventObject.color = modal_selected_color; //"red";
$('#calendar').fullCalendar('renderEvent', extendedEventObject, true);
//remove from event list, when option is checked
if ($('#drop-remove').is(':checked')) {
modal_event_html_element.remove();
}
//clear global vars
modal_selected_type = "";
modal_selected_color = "";
modal_event_html_element = "";
modal_event_date = "";
$("#event_comment").val('');
console.log(modal_selected_type, modal_selected_color, modal_event_html_element, modal_event_date);
}
get timebars :
JavaScript:
//get_events.php
<?php
if (!isset($_GET["start"]) || !isset($_GET["end"]) || !isset($_GET["_"])) {
echo json_encode(3);
exit;
}
include 'config.php';
$db = connect();
$rows = getSet($db, "select * from day_offs left join users on users.user_id=day_offs.user_id where date_occur between ? and ?",array($_GET["start"],$_GET["end"]));
//create an array
$record = array();
//for each record
foreach($rows as $row) {
$datetime = new DateTime($row['date_occur']);
$event_type = $row['day_off_type'];
//give to calendar bar the proper color
switch ($event_type) {
case 1 :
$color = "#9B26AF";
break;
case 2 :
$color = "#2095F2";
break;
case 3 :
$color = "#009587";
break;
case 4 :
$color = "#FE5621";
break;
case 5 :
$color = "#5CB85C";
break;
case 6 :
$color = "#FEEA3A";
break;
case 7 :
$color = "#785447";
break;
case 8 :
$color = "#5F7C8A";
break;
case 9 :
$color = "#212121";
break;
default:
$color = "#212121";
}
//https://fullcalendar.io/docs/agenda/allDaySlot/
//https://fullcalendar.io/docs/event_data/Event_Object/
//convert mysql datetime to ISO8601 format FullCalendar compatible
$record[] = array("id" => $row['day_off_id'],"title" => $row['user_mail'].$row['comment'],"color" => $color, "allDay" => true, "start" => $datetime->format(DateTime::ISO8601));
//add it to array
}
echo json_encode($record);
?>
add time bar :
JavaScript:
//add_dayoff.php
<?php
@session_start();
if (!isset($_SESSION["id"])) {
echo json_encode(1);
exit;
}
else {
date_default_timezone_set("UTC");
if ($_SESSION["login_expiration"] != date("Y-m-d"))
{
session_destroy();
echo json_encode(2);
exit;
}
}
if (!isset($_POST["userid"]) || !isset($_POST["eventdate"]) || !isset($_POST["typeid"]) || !isset($_POST["comment"])) {
echo json_encode(3);
exit;
}
include 'config.php';
$db = connect();
$sql = "INSERT INTO day_offs (day_off_type, user_id, date_occur, comment) VALUES (:day_off_type, :user_id, :date_occur, :comment)";
$stmt = $db->prepare($sql);
$stmt->bindValue(':day_off_type' , $_POST['typeid']);
$stmt->bindValue(':user_id' , $_POST['userid']);
$stmt->bindValue(':date_occur' , $_POST["eventdate"]);
$stmt->bindValue(':comment' , $_POST["comment"]);
$stmt->execute();
$res = $stmt->rowCount();
if($res == 1)
echo json_encode(100);
else
echo json_encode(0);
?>
update time bar :
JavaScript:
//update_dayoff.php
<?php
@session_start();
if (!isset($_SESSION["id"])) {
echo json_encode(1);
exit;
}
else {
date_default_timezone_set("UTC");
if ($_SESSION["login_expiration"] != date("Y-m-d"))
{
session_destroy();
echo json_encode(2);
exit;
}
}
if (!isset($_POST["day_off_id"]) || !isset($_POST["eventdate"])) {
echo json_encode(3);
exit;
}
include 'config.php';
$db = connect();
$sql = "update day_offs set date_occur=:date_occur where day_off_id=:day_off_id";
$stmt = $db->prepare($sql);
$stmt->bindValue(':date_occur' , $_POST['eventdate']);
$stmt->bindValue(':day_off_id' , $_POST['day_off_id']);
$stmt->execute();
$res = $stmt->rowCount();
if($res == 1)
echo json_encode(100);
else
echo json_encode(0);
?>
the sqlite dbase is :
JavaScript:
-- ----------------------------
-- Table structure for day_offs
-- ----------------------------
CREATE TABLE "day_offs" (
`day_off_id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
`day_off_type` INTEGER,
`user_id` INTEGER,
`date_occur` TEXT,
`comment` TEXT
);
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS "main"."users";
CREATE TABLE [users] (user_id INTEGER PRIMARY KEY, user_mail TEXT, user_password TEXT, user_level INTEGER);
sqlite doesnt have date field type, using dddd/mm/yy on TEXT field type, we are able to execute a query (get_events.php) like :
JavaScript:
select * from day_offs
left join users on users.user_id=day_offs.user_id
where date_occur between '2016-09-23' and '2016-09-29'
more at https://www.sqlite.org/lang_datefunc.html
another example FullCalendar (display appointments, saved from erp)
https://www.pipiscrew.com/2015/01/js-fullcalendar/