Recurring events are not saved in xml file

Could someone please check over my code to give me some pointers as to why, when I save the file, it does not save the changes to the xml file?

<!doctype html>
<head>
	<meta http-equiv="Content-type" content="text/html; charset=utf-8">
	<title>The Penfound Calendar</title>
	


	<script src="dhtmlxscheduler.js" type="text/javascript" charset="utf-8"></script>
	<script src="dhtmlxscheduler_recurring.js" type="text/javascript" charset="utf-8"></script>
	<script src='dhtmlxscheduler_minical.js' type="text/javascript"></script>
	<script src="dhtmlxscheduler_serialize.js" type="text/javascript" charset="utf-8"></script>
	<script src="dhtmlxscheduler_year_view.js" type="text/javascript" charset="utf-8"></script>
	<link rel="stylesheet" type="text/css" href="css/dhtmlxscheduler.css" />
 
	<style type="text/css" media="screen">
		html, body {
			margin: 0;
			padding: 0;
			height: 100%;
			overflow: hidden;
		}

		.dhx_cal_event div.dhx_footer,
		.dhx_cal_event.past_event div.dhx_footer,
		.dhx_cal_event.event_anytime div.dhx_footer,
		.dhx_cal_event.event_family div.dhx_footer,
		.dhx_cal_event.event_medical div.dhx_footer,
		.dhx_cal_event.event_metric div.dhx_footer,
		.dhx_cal_event.event_westcountry div.dhx_footer,
		.dhx_cal_event.event_early div.dhx_footer,
		.dhx_cal_event.event_teaching div.dhx_footer {
			background-color: transparent !important;
		}

		.dhx_cal_event .dhx_body {
			-webkit-transition: opacity 0.1s;
			transition: opacity 0.1s;
			opacity: 0.7;
		}

		.dhx_cal_event .dhx_title {
			line-height: 12px;
		}

		.dhx_cal_event_line:hover,
		.dhx_cal_event:hover .dhx_body,
		.dhx_cal_event.selected .dhx_body,
		.dhx_cal_event.dhx_cal_select_menu .dhx_body {
			opacity: 1;
		}
		/*EARLY*/
		.dhx_cal_event.event_early div, .dhx_cal_event_line.event_early {
			background-color: orange !important;
			border-color: #a36800 !important;
			color: black;
		}

		.dhx_cal_event_clear.event_early {
			color: orange !important;
		}

		/*MEDICAL*/
		.dhx_cal_event.event_medical div, .dhx_cal_event_line.event_medical {
			background-color: red !important;
			border-color: #a36800 !important;
			color: black;
		}

		.dhx_cal_event_clear.event_medical {
			color: red !important;
		}

		/*HOLIDAY*/
		.dhx_cal_event.event_holiday div, .dhx_cal_event_line.event_holiday {
			background-color: chartreuse !important;
			border-color: #a36800 !important;
			color: black;
		}

		.dhx_cal_event_clear.event_holiday {
			color: chartreuse !important;
		}

		/*TEACHING*/
		.dhx_cal_event.event_teaching div, .dhx_cal_event_line.event_teaching {
			background-color: darkseagreen !important;
			border-color: #698490 !important;
			color: black;
		}

		.dhx_cal_event_clear.event_teaching {
			color: darkseagreen !important;
		}

		/*ANYTIME*/
		.dhx_cal_event.event_anytime div, .dhx_cal_event_line.event_anytime {
			background-color: darkblue !important;
			border-color: #839595 !important;
			color: white;
		}

		.dhx_cal_event_clear.event_anytime {
			color: darkblue !important;
		}

		/*METRIC*/
		.dhx_cal_event.event_metric div, .dhx_cal_event_line.event_metric {
			background-color: cyan !important;
			border-color: #839595 !important;
			color: black;
		}

		.dhx_cal_event_clear.event_metric {
			color: cyan !important;
		}

		/*WESTCOUNTRY*/
		.dhx_cal_event.event_westcountry div, .dhx_cal_event_line.event_westcountry {
			background-color: purple !important;
			border-color: #839595 !important;
			color: white;
		}

		.dhx_cal_event_clear.event_westcountry {
			color: purple !important;
		}

		/*FAMILY*/
		.dhx_cal_event.event_family div, .dhx_cal_event_line.event_family {
			background-color: pink !important;
			border-color: #839595 !important;
			color: black;
		}

		.dhx_cal_event_clear.event_family {
			color: pink !important;
		}

 
	</style>
    <style type="text/css" media="screen">
        html, body {
            margin: 0px;
            padding: 0px;
            height: 100%;
            overflow: hidden;
        }

        .filters_wrapper {
            line-height: 12px;
            font-size: 12px;
        }

            .filters_wrapper span {
                font-weight: bold;
                padding-right: 5px;
            }

            .filters_wrapper label {
                padding-right: 3px;
            }
    </style>

	<script type="text/javascript" charset="utf-8">
		function init() {
			scheduler.config.xml_date = "%Y-%m-%d %H:%i";
			scheduler.config.prevent_cache = true;
			scheduler.config.time_step = 15;
			scheduler.config.multi_day = true;
			scheduler.locale.labels.section_subject = "Subject";
			scheduler.config.first_hour = 7;
			scheduler.config.last_hour = 22;
			scheduler.config.limit_time_select = true;
			scheduler.config.details_on_dblclick = true;
			scheduler.config.details_on_create = true;
			scheduler.config.occurrence_timestamp_in_utc = true;
			scheduler.config.repeat_precise = true;
			scheduler.config.include_end_by = true;
			scheduler.config.repeat_precise = true;
			scheduler.config.event_duration = 30;
			scheduler.config.auto_end_date = true;

			var format = scheduler.date.date_to_str("%H:%i");
			var step = 30;

			scheduler.templates.hour_scale = function (date) {
				html = "";
				for (var i = 0; i < 60 / step; i++) {
					html += "<div style='height:21px;line-height:21px;'>" + format(date) + "</div>";
					date = scheduler.date.add(date, step, "minute");
				}
				return html;
			}
			scheduler.templates.event_class = function (start, end, event, category) {
				var css = "";

				if (event.category) // if event has category property then special class should be assigned
					css += "event_" + event.category;

				if (event.id == scheduler.getState().select_id) {
					css += " selected";
				}
				return css; // default return

				/*
					Note that it is possible to create more complex checks
					events with the same properties could have different CSS classes depending on the current view:

					var mode = scheduler.getState().mode;
					if(mode == "day"){
						// custom logic here
					}
					else {
						// custom logic here
					}
				*/
			};


			var category = [
			{ key: '', label: 'Category' },
			{ key: 'anytime', label: 'Anytime' },
			{ key: 'early', label: 'Early' },
			{ key: 'family', label: 'Family' },
			{ key: 'holiday', label: 'Holiday' },
			{ key: 'medical', label: 'Medical' },
			{ key: 'metric', label: 'Mettric' },
			{ key: 'teaching', label: 'Teachng' },
			{ key: 'westcountry', label: 'Westcountry' }
			];
			var person = [
				{ key: '', label: 'Person' },
				{ key: 'graham', label: 'Graham' },
				{ key: 'karen', label: 'Karen' },
				{ key: 'james', label: 'James' },
	    		{ key: 'all', label: 'All' },
		        { key: 'both', label: 'Both' }
	        ];

		    // default values for filters
			var filters = {
			    graham: true,
			    karen: true,
			    james: true
			};

			var filter_inputs = document.getElementById("filters_wrapper").getElementsByTagName("input");
			for (var i = 0; i < filter_inputs.length; i++) {
			    var filter_input = filter_inputs[i];

			    // set initial input value based on filters settings
			    filter_input.checked = filters[filter_input.name];

			    // attach event handler to update filters object and refresh view (so filters will be applied)
			    filter_input.onchange = function () {
			        filters[this.name] = !!this.checked;
			        scheduler.updateView();
			    }
			}

		    // here we are using single function for all filters but we can have different logic for each view
			scheduler.filter_month = scheduler.filter_day = scheduler.filter_week = function (id, event) {
			    // display event only if its type is set to true in filters obj
			    // or it was not defined yet - for newly created event
			    if (filters[event.person] || event.person == scheduler.undefined) {
			        return true;
			    }

			    // default, do not display event
			    return false;
			};


			scheduler.config.lightbox.sections = [
                { name: "description", height: 43, map_to: "text", type: "textarea", focus: true },
                { name: "category", height: 20, type: "select", options: category, map_to: "category" },
                { name: "recurring", height: 115, type: "recurring", map_to: "rec_type", button: "recurring" },
                { name: "person", height: 20, width: 150, type: "select", options: person, map_to: "person" },
                { name: "time", height: 72, type: "time", map_to: "auto" }
			];

			scheduler.attachEvent("onBeforeEventChanged", function (dev) {
			var parts = this._drag_id.toString().split("#");
			if (parts.length > 1) {
				this._drag_id = parts[0];
				var ev = this.getEvent(parts[0]);
				ev._end_date = ev.end_date;
				ev.start_date = dev.start_date;
				ev.end_date = dev.end_date;
			}
			return true;
		});
			scheduler.data_attributes = function () {
			    var empty = function (a) { return a || ""; }
			    return [["id"],
                    ["text"],
                    ["category"],
                    ["person"],
                    ["start_date", scheduler.templates.xml_format],
                    ["end_date", scheduler.templates.xml_format],
                    ["rec_type", empty],
                    ["event_length", empty],
                    ["event_pid", empty]];
			}


			scheduler.init('scheduler_here', new Date(), "week");
    		scheduler.load("data/data.xml?uid=" + (new Date()).valueOf());
        	}
		function show() {
			alert(scheduler.toXML());
		}
		function save() {
			var form = document.forms[0];
			form.action = "data/xml_writer.php";
			form.elements.data.value = scheduler.toXML();
			form.submit();
		}
		function download() {
			var form = document.forms[0];
			form.action = "data/xml_download.php";
			form.elements.data.value = scheduler.toXML();
			form.submit();
		}
		function show_minical() {
			if (scheduler.isCalendarVisible()) {
				scheduler.destroyCalendar();
			} else {
				scheduler.renderCalendar({
					position: "dhx_minical_icon",
					date: scheduler._date,
					navigation: true,
					handler: function (date, calendar) {
						scheduler.setCurrentView(date);
						scheduler.destroyCalendar()
					}
				});
			}
		}
	</script>
</head>
<body onload="init();">
	<div style='height:20px; padding:5px 10px;'>
		<input type="button" name="download" value="Download" onclick="download()" style="right:500px;" />
		<input type="button" name="show" value="Show" onclick="show()" style="right:400px;" />
		<input type="button" name="save" value="Save" onclick="save()" style="right:300px;" />
	</div>
    <div style='height:20px; padding:5px;position:absolute; top:5px;left:250px;'>
        <div class="filters_wrapper" id="filters_wrapper">
            <span>Display:</span>
            <label>
                <input type="checkbox" name="graham" />
                Graham
            </label>
            <label>
                <input type="checkbox" name="karen" />
                Karen
            </label>
            <label>
                <input type="checkbox" name="james" />
                James
            </label>
        </div>
    </div>
	<form action="data/xml_writer.php" method="post" target="hidden_frame" accept-charset="utf-8">
		<input type="hidden" name="data" value="" id="data">
	</form>
	<iframe src='about:blank' frameborder="0" style="width:0px; height:0px;" id="hidden_frame" name="hidden_frame"></iframe>
	<div id="scheduler_here" class="dhx_cal_container" style='width:100%; height:100%;'>
		<div class="dhx_cal_navline">
			<div class="dhx_cal_prev_button">&nbsp;</div>
			<div class="dhx_cal_next_button">&nbsp;</div>
			<div class="dhx_cal_today_button"></div>
			<div class="dhx_cal_date"></div>
			<div class="dhx_minical_icon" id="dhx_minical_icon" onclick="show_minical()">&nbsp;</div>
			<div class="dhx_cal_tab" name="day_tab" style="right:204px;"></div>
			<div class="dhx_cal_tab" name="week_tab" style="right:140px;"></div>
			<div class="dhx_cal_tab" name="month_tab" style="right:76px;"></div>
			<div class="dhx_cal_tab" name="year_tab" style="right:50px;"></div>
		</div>
		<div class="dhx_cal_header">
		</div>
		<div class="dhx_cal_data">
		</div>
	</div>
</body>

Thanks

Hello,
can you clarify what exactly happens when you try to save the data. Is there a js error and the data is not submitted to the php handler? Or the data is submitted successfully, but the file is not saved? In the later case, problem might be in your data/xml_writer.php handler

Everything appears to work correctly without errors appearing. It simply does not save, i.e. when you refresh the page the events are gone.

Still hoping for a solution to this problem - any ideas are welcome :slight_smile:

(a) scheduler will serialize series of recurring events as one record in the xml

(b) you need to configure xml serializer to include extra fields.

docs.dhtmlx.com/scheduler/export … ialization

scheduler.data_attributes = function(){ return [ ["id"], ["text"], ["start_date",scheduler.templates.xml_format], ["end_date",scheduler.templates.xml_format], ["rec_type"], ["event_length"] ]; };

I had already included this piece of code.

			scheduler.data_attributes = function () {
			    var empty = function (a) { return a || ""; }
			    return [["id"],
                    ["text"],
                    ["start_date", scheduler.templates.xml_format],
                    ["end_date", scheduler.templates.xml_format],
                    ["rec_type", empty],
                    ["event_length", empty],
                    ["event_pid", empty],
                    ["category"],
                    ["person"]];
			}

After further digging I have found that just one line stops it from saving does anyone know why please?

{ name: "recurring", type: "recurring", map_to: "rec_type", button: "recurring" },

Without this line I can save single events and, obviously, not recurring ones. When this line is in place I can’t save either single or recurring events.

Sorted :slight_smile:

The error was in the data.attributes section

			scheduler.data_attributes = function () {
			    var empty = function (a) { return a || ""; }
			    return [["id"],
                    ["text"],
                    ["start_date", scheduler.templates.xml_format],
                    ["end_date", scheduler.templates.xml_format],
                    ["category"],
                    ["person"],
                    ["rec_type", empty],
                    ["event_length", empty],
                    ["event_pid", empty]];
			}

should have been

			scheduler.data_attributes = function () {
			    var empty = function (a) { return a || ""; }
			    return [["id"],
                    ["text"],
                    ["start_date", scheduler.templates.xml_format],
                    ["end_date", scheduler.templates.xml_format],
                    ["category", scheduler.templates.event_class.category],
                    ["person",scheduler.templates.event_class.person],
                    ["rec_type", empty],
                    ["event_length", empty],
                    ["event_pid", empty]];
			}

Hope this helps anyone else with a similar problem.