Skip to content

Commit b9110b0

Browse files
Merge pull request #48 from AlexKlimenkov/master
[update] guide to the latest version of scheduler
2 parents bf3ca66 + 096fa80 commit b9110b0

1 file changed

Lines changed: 248 additions & 36 deletions

File tree

docs/integrations/dotnet/howtostart-dotnet.md

Lines changed: 248 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,15 @@ Open the newly created view and put the following code into it:
100100
<script>
101101
document.addEventListener("DOMContentLoaded", function(event) {
102102
// initializing scheduler
103-
scheduler.init("scheduler_here", new Date(2022,0,15));
103+
scheduler.init("scheduler_here", new Date(2026, 0, 14), "week");
104104
105-
// initiating data loading
105+
// load data from backend
106106
scheduler.load("/api/scheduler");
107-
// initializing dataProcessor
108-
var dp = scheduler.createDataProcessor("/api/scheduler");
109-
// and attaching it to scheduler
110-
dp.init(scheduler);
111-
// setting the REST mode for dataProcessor
112-
dp.setTransactionMode("REST");
107+
// connect backend to scheduler
108+
const dp = scheduler.createDataProcessor({
109+
url: "/api/scheduler",
110+
mode: "REST"
111+
});
113112
});
114113
</script>
115114
</head>
@@ -141,13 +140,13 @@ And also we told the scheduler that it's going to work with the RESTful API on a
141140

142141

143142
~~~js title="Views/Home/Index.cshtml"
143+
// load data from backend
144144
scheduler.load("/api/scheduler");
145-
// initializing dataProcessor
146-
var dp = scheduler.createDataProcessor("/api/scheduler");
147-
// and attaching it to scheduler
148-
dp.init(scheduler);
149-
// setting the REST mode for dataProcessor
150-
dp.setTransactionMode("REST");
145+
// connect backend to scheduler
146+
const dp = scheduler.createDataProcessor({
147+
url: "/api/scheduler",
148+
mode: "REST"
149+
});
151150
~~~
152151

153152
The server side itself will be implemented a bit later. For now, you can run the application and see that a scheduler is rendered on the page.
@@ -255,22 +254,22 @@ namespace DHX.Scheduler.Web.App_Start
255254
{
256255
Id = 1,
257256
Text = "Event 1",
258-
StartDate = new DateTime(2022, 1, 11, 2, 0, 0),
259-
EndDate = new DateTime(2022, 1, 11, 4, 0, 0)
257+
StartDate = new DateTime(2026, 1, 12, 2, 0, 0),
258+
EndDate = new DateTime(2026, 1, 12, 4, 0, 0)
260259
},
261260
new SchedulerEvent()
262261
{
263262
Id = 2,
264263
Text = "Event 2",
265-
StartDate = new DateTime(2022, 1, 14, 3, 0, 0),
266-
EndDate = new DateTime(2022, 1, 14, 6, 0, 0)
264+
StartDate = new DateTime(2026, 1, 14, 3, 0, 0),
265+
EndDate = new DateTime(2026, 1, 14, 6, 0, 0)
267266
},
268267
new SchedulerEvent()
269268
{
270269
Id = 3,
271270
Text = "Multiday event",
272-
StartDate = new DateTime(2022, 1, 11, 0, 0, 0),
273-
EndDate = new DateTime(2022, 1, 16, 0, 0, 0)
271+
StartDate = new DateTime(2026, 1, 12, 0, 0, 0),
272+
EndDate = new DateTime(2026, 1, 16, 0, 0, 0)
274273
}
275274
};
276275

@@ -513,7 +512,7 @@ In case Scheduler doesn't render events on a page, check the [Troubleshooting Ba
513512

514513
## Recurring events
515514

516-
In order to enable recurrence (e.g. "repeat event daily"), you'll need to enable an appropriate extension on the scheduler page:
515+
In order to enable recurrence (e.g. "repeat event daily"), you'll need to enable an appropriate extension on the scheduler page:
517516

518517
~~~js
519518
scheduler.plugins({
@@ -523,7 +522,224 @@ scheduler.plugins({
523522

524523
### Updating the Models
525524

526-
We also need to update our model in order for it to store the recurrence info:
525+
We also need to update our model in order for it to store the recurrence info:
526+
527+
528+
~~~js title="Models/SchedulerEvent.cs"
529+
using System;
530+
531+
namespace DHX.Scheduler.Web.Models
532+
{
533+
public class SchedulerEvent
534+
{
535+
public int Id { get; set; }
536+
public string Text { get; set; }
537+
public DateTime StartDate { get; set; }
538+
public DateTime EndDate { get; set; }
539+
public int? Duration { get; set; } /*!*/
540+
public string Rrule { get; set; } /*!*/
541+
public string RecurringEventId { get; set; } /*!*/
542+
public string OriginalStart { get; set; } /*!*/
543+
public bool? Deleted { get; set; } /*!*/
544+
}
545+
}
546+
~~~
547+
548+
as well as the DTO:
549+
550+
551+
~~~js title="Models/WebAPIEvent.cs"
552+
using System;
553+
554+
namespace DHX.Scheduler.Web.Models
555+
{
556+
public class WebAPIEvent
557+
{
558+
public int id { get; set; }
559+
public string text { get; set; }
560+
public string start_date { get; set; }
561+
public string end_date { get; set; }
562+
public int? duration { get; set; } /*!*/
563+
public string rrule { get; set; } /*!*/
564+
public string recurring_event_id { get; set; } /*!*/
565+
public string original_start { get; set; } /*!*/
566+
public bool? deleted { get; set; } /*!*/
567+
568+
public static explicit operator WebAPIEvent(SchedulerEvent schedulerEvent)
569+
{
570+
return new WebAPIEvent
571+
{
572+
id = schedulerEvent.Id,
573+
text = schedulerEvent.Text,
574+
start_date = schedulerEvent.StartDate.ToString("yyyy-MM-dd HH:mm"),
575+
end_date = schedulerEvent.EndDate.ToString("yyyy-MM-dd HH:mm"),
576+
duration = schedulerEvent.Duration, /*!*/
577+
rrule = schedulerEvent.Rrule, /*!*/
578+
recurring_event_id = schedulerEvent.RecurringEventId, /*!*/
579+
original_start = schedulerEvent.OriginalStart, /*!*/
580+
deleted = schedulerEvent.Deleted /*!*/
581+
};
582+
}
583+
584+
public static explicit operator SchedulerEvent(WebAPIEvent schedulerEvent)
585+
{
586+
return new SchedulerEvent
587+
{
588+
Id = schedulerEvent.id,
589+
Text = schedulerEvent.text,
590+
StartDate = DateTime.Parse(
591+
schedulerEvent.start_date,
592+
System.Globalization.CultureInfo.InvariantCulture),
593+
EndDate = DateTime.Parse(
594+
schedulerEvent.end_date,
595+
System.Globalization.CultureInfo.InvariantCulture),
596+
Duration = schedulerEvent.duration, /*!*/
597+
Rrule = schedulerEvent.rrule, /*!*/
598+
RecurringEventId = schedulerEvent.recurring_event_id, /*!*/
599+
OriginalStart = schedulerEvent.original_start, /*!*/
600+
Deleted = schedulerEvent.deleted /*!*/
601+
};
602+
}
603+
}
604+
}
605+
~~~
606+
607+
:::note
608+
If the database does not apply changes made to a Scheduler event, you need to delete the database so that the changes will be applied the next time you run the project.
609+
:::
610+
611+
### Updating API Controller
612+
613+
Lastly, we need to modify our PUT/POST/DELETE actions in order to handle [special rules of recurring events](guides/recurring-events.md#editingdeleting-a-certain-occurrence-in-the-series).
614+
615+
First, let's take a look at the POST action. We need to process a special case for recurring events: deletion of a specific occurrence of the recurring series requires creating a new database record and the client will call the insert action for it:
616+
617+
618+
~~~js title="Controllers/SchedulerController.cs"
619+
// POST: api/scheduler/5
620+
[HttpPost]
621+
public IHttpActionResult CreateSchedulerEvent(WebAPIEvent webAPIEvent)
622+
{
623+
var newSchedulerEvent = (SchedulerEvent)webAPIEvent;
624+
var action = "inserted";
625+
if (webAPIEvent.deleted == true) /*!*/
626+
{
627+
// delete a single occurrence from a recurring series
628+
action = "deleted"; /*!*/
629+
}
630+
db.SchedulerEvents.Add(newSchedulerEvent);
631+
db.SaveChanges();
632+
633+
return Ok(new
634+
{
635+
tid = newSchedulerEvent.Id,
636+
action
637+
});
638+
}
639+
~~~
640+
641+
In the PUT action we need to make sure to update all properties of the model. Additionally, we need to handle a different special case there: when a recurring series is modified, we need to delete all modified occurrences of that series:
642+
643+
644+
~~~js title="Controllers/SchedulerController.cs"
645+
// PUT: api/scheduler/5
646+
[HttpPut]
647+
public IHttpActionResult EditSchedulerEvent(int id, WebAPIEvent webAPIEvent)
648+
{
649+
var updatedSchedulerEvent = (SchedulerEvent)webAPIEvent;
650+
var dbEvent = db.SchedulerEvents.Find(id);
651+
if (dbEvent == null)
652+
{
653+
return NotFound();
654+
}
655+
dbEvent.Text = updatedSchedulerEvent.Text;
656+
dbEvent.StartDate = updatedSchedulerEvent.StartDate;
657+
dbEvent.EndDate = updatedSchedulerEvent.EndDate;
658+
dbEvent.Duration = updatedSchedulerEvent.Duration; /*!*/
659+
dbEvent.Rrule = updatedSchedulerEvent.Rrule; /*!*/
660+
dbEvent.RecurringEventId = updatedSchedulerEvent.RecurringEventId; /*!*/
661+
dbEvent.OriginalStart = updatedSchedulerEvent.OriginalStart; /*!*/
662+
if (!string.IsNullOrEmpty(dbEvent.Rrule) && dbEvent.RecurringEventId == null) /*!*/
663+
{
664+
// all modified occurrences must be deleted when we update a recurring series
665+
// https://docs.dhtmlx.com/scheduler/server_integration.html#savingrecurringevents
666+
var eventsToDelete = db.SchedulerEvents
667+
.Where(e => !string.IsNullOrEmpty(e.RecurringEventId)
668+
&& e.RecurringEventId == dbEvent.Id.ToString())
669+
.ToList();
670+
db.SchedulerEvents.RemoveRange(eventsToDelete);
671+
}
672+
673+
db.SaveChanges();
674+
675+
return Ok(new
676+
{
677+
action = "updated"
678+
});
679+
}
680+
~~~
681+
682+
And finally, the DELETE action. Here we have to check two special cases:
683+
684+
- if the event you are going to delete is a modified instance of the recurring series. Instead of deleting, update the record to mark `deleted`.
685+
- if a user deletes a whole recurring series, you also need to delete all the modified instances of that series.
686+
687+
688+
~~~js title="Controllers/SchedulerController.cs"
689+
// DELETE: api/scheduler/5
690+
[HttpDelete]
691+
public IHttpActionResult DeleteSchedulerEvent(int id)
692+
{
693+
var dbEvent = db.SchedulerEvents.Find(id);
694+
if (dbEvent != null)
695+
{
696+
if (dbEvent.RecurringEventId != null) /*!*/
697+
{
698+
// deleting a modified occurrence from a recurring series.
699+
// An event with a recurring_event_id value should be marked
700+
// with .deleted = true instead of being deleted.
701+
dbEvent.Deleted = true; /*!*/
702+
}
703+
else
704+
{
705+
if (dbEvent.Rrule != null) /*!*/
706+
{
707+
// if a recurring series was deleted, delete all modified occurrences of the series
708+
var eventsToDelete = db.SchedulerEvents
709+
.Where(e => !string.IsNullOrEmpty(e.RecurringEventId)
710+
&& e.RecurringEventId == dbEvent.Id.ToString())
711+
.ToList();
712+
db.SchedulerEvents.RemoveRange(eventsToDelete);
713+
}
714+
db.SchedulerEvents.Remove(dbEvent);
715+
}
716+
db.SaveChanges();
717+
}
718+
719+
return Ok(new
720+
{
721+
action = "deleted"
722+
});
723+
}
724+
~~~
725+
726+
### Recurring events (legacy, pre-7.1)
727+
728+
:::note
729+
The approach below applies to events stored in the legacy pre-7.1 format (`event_pid`, `rec_type`, `event_length`). Use it only if you still maintain data that uses those fields; new projects should use the rrule-based engine described above. See the [migration guide](migration.md#new-engine-for-recurring-events) and the [legacy recurring events guide](guides/recurring-events-legacy.md) for context.
730+
:::
731+
732+
To stay on the legacy engine, enable the `recurring_legacy` plugin instead of `recurring`:
733+
734+
~~~js
735+
scheduler.plugins({
736+
recurring_legacy: true
737+
});
738+
~~~
739+
740+
#### Updating the Models
741+
742+
The legacy engine stores recurrence info in three columns - `event_pid`, `rec_type`, `event_length`:
527743
528744
529745
~~~js title="Models/SchedulerEvent.cs"
@@ -546,7 +762,7 @@ namespace DHX.Scheduler.Web.Models
546762
}
547763
~~~
548764
549-
as well as the DTO:
765+
as well as the DTO:
550766
551767
~~~js title="Models/WebAPIEvent.cs"
552768
using System;
@@ -586,14 +802,14 @@ namespace DHX.Scheduler.Web.Models
586802
Id = schedulerEvent.id,
587803
Text = schedulerEvent.text,
588804
StartDate = DateTime.Parse(
589-
schedulerEvent.start_date,
805+
schedulerEvent.start_date,
590806
System.Globalization.CultureInfo.InvariantCulture),
591807
EndDate = DateTime.Parse(
592-
schedulerEvent.end_date,
808+
schedulerEvent.end_date,
593809
System.Globalization.CultureInfo.InvariantCulture),
594-
EventPID = schedulerEvent.event_pid != null ?
810+
EventPID = schedulerEvent.event_pid != null ?
595811
schedulerEvent.event_pid.Value : 0,
596-
EventLength = schedulerEvent.event_length != null ?
812+
EventLength = schedulerEvent.event_length != null ?
597813
schedulerEvent.event_length.Value : 0,
598814
RecType = schedulerEvent.rec_type
599815
};
@@ -603,12 +819,10 @@ namespace DHX.Scheduler.Web.Models
603819
}
604820
~~~
605821
606-
### Updating API Controller
822+
#### Updating API Controller
607823
608-
Lastly, we need to modify our PUT/POST/DELETE actions in order to handle [special rules of recurring events](guides/recurring-events.md#editingdeleting-a-certain-occurrence-in-the-series).
824+
The legacy POST/PUT/DELETE actions follow the same idea but key off `rec_type` and `event_pid`. Firstly, the `POST` action - a deleted occurrence of a recurring series is created as a new record with `rec_type == "none"`:
609825
610-
First, let's take a look at the POST action. We need to process a special case for recurring events: deletion of a specific occurrence of the recurring series
611-
requires creating a new database record and the client will call the insert action for it:
612826
613827
~~~js title="Controllers/SchedulerController.cs"
614828
// POST: api/scheduler/5
@@ -634,8 +848,7 @@ public IHttpActionResult CreateSchedulerEvent(WebAPIEvent webAPIEvent)
634848
}
635849
~~~
636850
637-
In the PUT action we need to make sure to update all properties of the model. Additionally, we need to handle a different special case there: when a recurring series
638-
is modified, we need to delete all modified occurrences of that series:
851+
In the PUT action we need to make sure to update all properties of the model. When a recurring series is modified, all modified occurrences of that series have to be deleted:
639852
640853
641854
~~~js title="Controllers/SchedulerController.cs"
@@ -667,10 +880,9 @@ public IHttpActionResult EditSchedulerEvent(int id, WebAPIEvent webAPIEvent)
667880
}
668881
~~~
669882
670-
And finally, the DELETE action. Here we have to check two special cases:
883+
And finally, the DELETE action. Two special cases:
671884
672-
- if the event you are going to delete has a non-empty event_pid, it means a user deletes a modified instance of the recurring series. Instead of deleting such a record from the database, you need to give it rec_type='none',
673-
in order for scheduler to skip this occurrence.
885+
- if the event you are going to delete has a non-empty event_pid, it means a user deletes a modified instance of the recurring series. Instead of deleting such a record from the database, you need to give it rec_type='none', in order for scheduler to skip this occurrence.
674886
- if a user deletes a whole recurring series, you also need to delete all the modified instances of that series.
675887
676888

0 commit comments

Comments
 (0)