MODx — own ajax events calendar/news without Ditto

Asked me here the other day problem: to make the calendar of events for ModX Evolution.
Engine this I truly love, for his unprecedented flexibility and clarity to me personally.

The task seemed simple, but as you complete met with a couple of difficult moments.
So, who wants to make a site with a pop-up calendar list of events — ask Harakat!



For the task we need:
the
    the
  • Document (container) with the news. It should be a normal modx documents.
  • the
  • Javaскрипт library jQueryUI and qTip. The first is to draw a calendar and keep it in operation, and the second pop-up event.
  • the
  • in-house snippet eventsCalendar that we soon will together write.
  • the
  • 2 chunk (html design). First, where will the call scripts and the design of the calendar, and the second is the design of a single event in a popup window.
  • the
  • you have set the TV to document events. Is created in the admin panel of modx.


To begin, create a TV option for documents. Go to the admin panel of modx at Features->controls->Settings (TV)->New parameter (TV)
value Name: event_date
Title: event_date
Input type: Date
Templates: any, as long as he was exposed to a pattern of events.

Now, when you go to an event that will appear below this option, when you click will be the date picker, which we derive for this event in the calendar.

Next, we need to write a snippet which will output an array of events for jQueryUI.datepicker. In General, this plugin is not for
output of events and dates. Might use it for another function not quite right decision, but there are a few reasons why I use it so:

First, jQueryUI I already have the website, so why should I connect something else?
Second, the datepicker is fine adjusted, it is possible to specify a range of output dates to first event on date selection, on change of month and so on.
All this will create a nice event calendar with ajax requests without the invention of their own bike.
Pretty much what I thought.

In fact, it turned out that this approach carries a couple of inconveniences, but about them later.

Snippet eventsCalendar. Work on the server side.

So, write a snippet. For some time now, I have "hello world!" can't withdraw without writing a class, so, sorry in advance.

the
<?php
class eventsCalendar {

var $id; // the number of the document container events
var $dateFormat = '%d %b %Y %H:%M'; // date Format, use strftime()
var $dateTV = 'event_date'; // name of the modx TV option for a document
var $tplEvent = 'tplEvent'; // Template of each event
var $tplMain = 'tplCalendar'; // global template design calendar of events
var $conv = 0; // whether to convert events from cp1251 in utf8. Default - not needed. 

/* Error output */
function error($err) {
$arr = array(
'no_id' => 'You forgot to specify the id of the directory for the sample event',
'no_action' => 'is Not specified the method for processing ajax request.',
'no_result' => 'In a specified container, no papers.'
);
return $arr[$err];
}

/* The output events, taking the number of container events, month and year */
function getEvents($id = ", $month = ", $year = ") {
global $modx;

if (empty($id)) {return $this->error('no_id');}
if (empty($month)) {$month = date('m');}
if (empty($year)) {$year = date('Y');}
if (strlen($month) == 1) {$month = '0'.$month;}

/* Get the documents from the container, we are interested in their id and date */
$tmp = $modx->getDocumentChildrenTVars($id, array('id',$this->dateTV));

/* Gather the array in proper format for further processing */
if (empty($tmp)) {return $this->error('no_result');}
else {
foreach ($tmp as $v) {
$d = strftime('%Y-%m', strtotime($v[0]['value']));
if ($d == $year.'-'.$month) {$ids[] = $v[1]['value'];}

$dates_arr[$v[1]['value']] = $v[0]['value'];
} 
}

/* We got an array of the required documents $ids, and the array of dates of these documents with each other they are not connected, yet
Now get the documents completely. Sorting by date of publikatsii */
$arr = $modx- > getDocuments($ids, 1, 0, "id,pagetitle,introtext", "", 'pub_date ASC, id', 'ASC');

/* If the matching document is not found - return an empty array encoded to
bring an empty calendar */
if (empty($arr)) {return json_encode(array());}

/* If the documents are - treated in their properties, processing and stuffed into the array */
/* First get the specified template in each event */
$tpl = $modx- > getChunk($this->tplEvent);

$i = 1;
foreach ($arr as $v) {
/* define document variables to insert into the template */
$did = $v['id'];
$url = $modx- > makeUrl($did);
$date = strftime($this- > dateFormat, strtotime($dates_arr[$did]));
$date2 = strftime('%Y-%m-%d', strtotime($dates_arr[$did]));
$desc = $v['introtext'];
$title = $v['pagetitle']; 
/* This is for the event number if event is not one at the bottom */
if (isset($date3) && $date3 != $date2) {$i = 1;}
$date3 = $date2;

/* Standard replacement of the placeholders for variables in templates modx */
$placeholders = array('[+ec.date+]','[+ec.title+]','[+ec.url+]','[+ec.desc+]','[+ec.num+]');
$values = array($date, $title, $url, $desc, $i);
$text = str_replace($placeholders, $values, $tpl);

/* Plugging the result in the array, with possible conversion from cp1251. */
if ($this- > conv != 0) {$dates[$date2] .= iconv('cp1251', 'utf-8', $text);}
else {$dates[$date2] .= $text;}

$i++;
}

/* Because jqueryui.datepicker need an array of 'date','css class','text event' - given our result to this type */
foreach($dates as $k = > $v) {
$dates2[] = array($k, ", $v);
}
/* Return the data in json format */
return json_encode($dates2); 
}

/* Just output the main template on the screen, by default */ 
function output($tpl) {
global $modx;

$tpl = $modx- > getChunk($tpl);

echo $tpl;
}

}
/* End of class on piping for use in modx */

$Cal = new eventsCalendar;

$Cal->id = $id; // mandatory!
/* If you set the parameters when calling the snippet - override standard */
if (!empty($dateTV)) {$Cal->dateTV = $dateTV;}
if (!empty($dateFormat)) {$Cal->dateFormat = $dateFormat;}
if (!empty($tplEvent)) {$Cal->tplEvent = $tplEvent;}
if (!empty($tplMain)) {$Cal->tplMain = $tplMain;}
if (!empty($conv)) {$Cal->conv = $conv;}

/* If request is via an ajax call the required method and stop the work */
if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
$action = $_POST['action'];
if (!empty($action)) {
switch($action) {
case 'getEvents': echo $Cal->getEvents($Cal->id, $_POST['month'], $_POST['year']); break;
}
}
else {
echo $Cal->error('no_action');
}
die();
}
/* If the normal call - display the calendar on the screen */
else {
$Cal->output($Cal->tplMain);
}
?>


Next, go into the admin panel of modx in snippets, create a new one, call it and copy eventsCalendar inside the above.
We've created a new snippet and call it in any place on your page like this:
[!eventsCalendar?
&id=`13`
&dateTV=`event_date`
&dateFormat=`%d %b %Y %H:%M`
&tplMain=`tplCalendar`
&tplEvent=`tplEvent`
!]

see Options at the end of a topic.

Only as long as it outputs nothing, because we didn't write the template.


Chunks. Work on the client side.

Let's start with the small template of a single event tplEvent.
the
<div class='event'>
<span class='num'><b>[+ec.num+].</b></span>
<span class='date'>[+ec.date+]</span>
<span class='link'><a href='[+ec.url+]' target='_blank'>[+ec.title+]</a></span>
<br />
<span class='notice'>[+ec.desc+]</span>
</div>
<br />


Further, the pattern more.
The design I'm missing, as there is quite a lot of scribbling, here I will give only the main thing, the rest of the attachment.

You need to connect the library (follow the tracks).
the
<script type='text/javascript' src='[(site_url)]inc/js/jquery-1.4.4.min.js'></script> 
<script type='text/javascript' src='[(site_url)]inc/js/jquery-ui-1.8.6.custom.min.js'></script>
<script type='text/javascript' src='[(site_url)]inc/js/jquery.ui.datepicker-ru.js'></script> <!-- Localization of datepicker -->
<script type='text/javascript' src='[(site_url)]inc/js/jquery.qtip.js'></script>


Now, the magic.
the
<script type='text/javascript'>
$(document).ready(function() {
class_enabled = 'enabled'; // Class registration dates where there are events
class_disabled = 'disabled'; // No events
element = '#Calendar'; // DOM Element which is loaded in the calendar
url = '/[~[*id*]~]'; // Url of the page to send ajax by default in - current

dates = getEvents(); // Get the array of events via ajax

Calendar(dates); // Launch the calendar and give it an array with dates
Qtip(); // Run qTip to pop-up events
});

// Function call of the calendar on the screen. 

$(element).datepicker({
language: 'EN',
inline: true,
dateFormat: 'dd.mm.yy',
// When changing month - get a new array with dates. Then, after 1 second, run qTip, 
// to make the newly created DOM elements by vsplyvshie.
onChangeMonthYear: function(year, month) {
dates = getEvents(year, month);
window.setTimeout(
function() {
Qtip()
},
1000
);
},
// Before displaying each date - run it through the array of events to set the properties.
// Options 3: on/off, a class design and the text that is inserted into the title of the td element.
beforeShowDay: function(d) {
var date = $.datepicker.formatDate('yy-mm-dd', d);
for (i = 0, c = dates.length; i < c; i++) {
if (date == dates[i][0]) {
return [true, class_enabled, dates[i][2]];
}
}
return [false, class_disabled];
},
// What to do when clicking on date. I have nothing to do.
onSelect: function() {return false;}
});

}

// Query array of events via ajax from our snippet.
// There is an important point - the timing of the request, because, until the data - the calendar cannot be drawn.
// Otherwise it will be blank as the data coming in later than it will appear on the screen.
function getEvents(year, month) {
$.ajaxSetup({async: false});
$.post(url, {action: 'getEvents', month: month, year: year}, 
function(data) {
if (data == 'null') {data = '[]';}
dates = jQuery.parseJSON(data);
response = dates; 
} 
)
return response; 
}

// Run vsplyvshie tips qTip.
// All the parameters we can look at the author's website. Displays the text of the title element.
function Qtip() {
$(element + ' .' + class_enabled).qtip({
prerender: true,
show: {when: {event: 'mouseover'}, effect: {length: 0}, solo: true},
hide: {when: {event: 'unfocus'}, effect: {length: 0}},
position: {
corner: {
target: 'center',
tooltip: 'topLeft'
}
},
style: {height: 100,width: 300}
});
}
</script>

the
<div id='Calendar'></div> - Element, where will be loaded the calendar

I have used some stuff to darken the calendar when you change the month and display a loading GIF.
Nothing complicated, watch here.

How it works?

When you first open the page, we displayed a simple template tplCalendar. After downloading it will start a feature request array of dates.
As soon as there will come a array — loads a calendar with dates otrisovka assigns the style and tooltip text.
Then run the qTip and generates the tooltip.

When you change the month — requested a new array of dates and re-run splawski (because the DOM has changed).

At the same time, we can hang the event on click on the date (for example, the transition to the page with the news on this date). To change the month (I have
gets the loading indicator + obscured the calendar itself). View the pop-up window is fully customizable, both style and content.

Now the drawbacks. Several of them.
the
    the
  • the Need to call qTip after each operation with the calendar. Since it is impossible to know (at least, I can't think of how) that the calendar
    ready to have to do this for the timeout to 1sec. If the server brake, or events VERY much, seconds may not be enough. On the other hand,
    the timeout can be changed, and events to create so much that a normal server can process them for a second, I could not.
  • the
  • as the text for the popup window is written in the title attribute of the td element, you have to remember about quotes and escaping.

Disadvantages could be bypassed by writing your calendar in php under this specific task, but I decided that the advantages outweigh the disadvantages datepicker it use to output events.

Application.

Download the file with the snippet and templates
View (version Revolution)

Options eventsCalendar snippet:
&id
the default: none, but required
value: [int]
description: ID of an existing document container.

&dateFormat
the default: '%d %b %Y %H:%M'
value: variable strftime()
description: date Format.

the default: event_date
value: the Name of an existing tv
description: the name of the modx TV option for the document.

&tplEvent
the default: tplEvent
value: the Name of an existing chunk modx
description: design Template for each event.

&tplMain
the default: tplCalendar
value:
description: design template calendar of events.

&conv
the default: 0
value: [int]
description: do you Need to convert events from cp1251 in utf8.


Placeholder eventsCalendar snippet:
[+ec.num+]
the event in the day. True, only if you have more than one event per day. For correct numbering,
to set the date of publication of the document coincided with the TV setting the date of the event. Otherwise, the numbering may be incorrect.

[+ec.date+]
the event date.
[+ec.+url]
the Reference.
[+ec.title+]
Header is taken from the pagetitle of the document of modx.
[+ec.desc+]
the Brief description, taken from the introtext of the document of modx.


Links to relevant sites.
the

UPD.
Bug reports send to bezumkin@yandex.ru, or write in this thread.

UPD.2
Ready the second version of the calendar, WITHOUT the datepicker, and related flaws.
Article based on information from habrahabr.ru

Comments

Popular posts from this blog

Powershell and Cyrillic in the console (updated)

Active/Passive PostgreSQL Cluster, using Pacemaker, Corosync

Automatic deployment ElasticBeanstalk using Bitbucket Pipelines