Description

Each weekday we schedule one pastor to be on-call as the Pastor of the Day (POD) so that we always have a pastor available when needed. As a part of our ongoing effort to replace various Google products with Rock, we recently moved our POD schedules over to the group scheduling system in Rock instead.

pod-home-page-widget-screenshot.jpg

This recipe will walk you through setting up the following features:

  • A POD group for managing and scheduling your PODs
  • A widget on the Rock home page that displays today's POD, complete with phone extension, email address and optionally a link to start a new direct message in Slack
  • An upcoming POD page that shows the scheduled pastors for the next 14 days, as well as all scheduled dates for each pastor for the next 12 months
  • A schedule preferences page for pastors (and staff) to manage their schedule from their My Settings area in Rock
  • A Lava webhook that will serve as an iCal feed for the POD schedule

If you'd prefer to not use the the group scheduling feature or if my solution just isn't quite what you were looking for, be sure to check out the Pastor On Call Block recipe by Morgan Woods. Morgan's recipe uses content channels instead of group scheduling.

(Please let me know if there are any other similar recipes I may have missed)

Prerequisites

This recipe uses some of our own specific Rock customizations that may or may not match your situation. You may have to either make some extra changes to your Rock instance or modify the code in this recipe to work with your situation.

Staff Person Tag

We identify staff with an person tag:

  • Name: Staff
  • Scope: Organizational
  • Entity Type: Person
Staff-Related Person Attributes
Staff Photo

We keep a separate staff photo so everyone has a professional photo that's consistent with the rest of staff, rather than relying on the main profile photo

  • Key: StaffPhoto
  • Field Type: Image
Phone Extension
  • Key: StaffPhoneExtension
  • Field Type: Text
Slack User ID

If your organization uses Slack for communication, this recipe can include a link for starting a new DM directly in Slack. You'll just need to store each staff member's Slack User ID. You'll also need to make a note of your organization's Slack Team ID.

  • Key: SlackUserID
  • Field Type: Text

How-To

Core POD Scheduling System

  1. Pastor of the Day Schedule

    Create a schedule of days that your pastors will be scheduled for.

    • Name: Pastor of the Day
    • Schedule: Set the schedule for your situation. Ours is weekly on Monday through Thursday from 9am to 5pm.
  2. Group Type

    You can either create a new group type or use an existing one. There are only a few options that must be set on the group type:

    • General > Enable Location Schedules: Yes
    • General > Location Types: Meeting Location
    • Scheduling > Scheduling Enabled: Yes
  3. Pastor of the Day Group

    Create a new group using the group type specified above for keeping track of your POD pastors.

    • Name: Pastor of the Day
    • Meeting Details: Add a Group Location
      • Location: Select the named location for your offices
      • Type: Meeting Location
      • Schedule(s): Select the schedule you created above
      • Capacities: Set the capacities as needed. Ours are set to 1, 1, 1.

    Make a note of the ID of your new group for use in upcoming steps.

  4. Add your pastors and start scheduling

    Go ahead and add everyone who is eligible to be pastor of the day to this group. Once they have been added, you can go to the Group Scheduling page or just click the Group Scheduler button () on the group to start scheduling your pastors.

Home Page Widget

pod-home-page-widget-screenshot.jpg
  1. Add a new HTML Content block to your internal Rock home page. I recommend adding it to the top of the Sidebar 1 zone, but you can add it wherever you want.
  2. Edit the block properties and enable the Cache and Rock Entity Lava Commands.
  3. Edit the block content and add the following HTML/Lava. Replace the ID number on the first line with the ID of your POD group, and the Slack Team ID with your team ID.
    {%- assign podGroupID = 12345 -%}
    {%- assign slackTeamID = 'A0BCDE1FG' -%}
    //-----------------------------------------------------------
    {%- capture podHTML %}
        {%- cache key:'current-pod' tags:'internal-news' duration:'3600' -%}
            {%- assign dateKey = 'Now' | Date:'yyyyMMdd' -%}
            {%- assign pastorCount = 0 -%}
            {%- attendanceoccurrence where:'GroupId == {{ podGroupID }} && OccurrenceDateKey == "{{ dateKey }}"' disableattributeprefetch:'true' securetyenabled:'false' -%}
                {%- for occurrence in attendanceoccurrenceItems -%}
                    {%- for attendee in occurrence.Attendees -%}
                        {%- if attendee.ScheduledToAttend == true -%}
                            {%- assign pastorCount = pastorCount | Plus:1 -%}
                            {%- assign pastor = attendee.PersonAlias.Person -%}
                            {%- assign phoneExt = pastor | Attribute:'StaffPhoneExtension' | Default:'' -%}
                            {%- assign slackUserID = pastor | Attribute:'SlackUserID' | Default:'' -%}
    <div class="d-flex align-items-center">
        <div class="d-none d-sm-block mr-2">
            <a href="/person/{{ pastor.Id }}" title="View {{ pastor.NickName }}'s profile">
                <img src="/GetAvatar.ashx?Style=icon&PhotoId={{ pastor | Attribute:'StaffPhoto','Id' }}&Gender={{ pastor.Gender }}&BackgroundColor=eceae8&ForegroundColor=c0bebc" alt="{{ pastor.FullName }}" style="max-width:100px;width:100%;border-radius:50%;display:inline-block;float:none;margin:5px auto;padding:2px;border:3px solid #06c;" />
            </a>
        </div>
        <div class="text-left">
            <a href="/person/{{ pastor.Id }}" title="View {{ pastor.NickName }}'s profile">
                <strong>{{ pastor.FullName }}</strong>
            </a>
            <small class="text-muted d-block">
                            {%- if phoneExt != '' -%}
                <div class="d-flex">
                    <i class="fa fa-fw fa-phone mr-1" style="line-height:inherit"></i> 
                    <span class="flex-grow-1">x{{ phoneExt }}</span>
                </div>
                            {%- endif -%}
                            {%- if slackUserID != '' -%}
                <div class="d-flex">
                    <i class="fab fa-fw fa-slack mr-1" style="line-height:inherit"></i>
                    <a href="slack://user?team={{ slackTeamID }}&id={{ slackUserID }}" target="_blank">Send Slack DM</a>
                </div>
                            {%- endif -%}
                <div class="d-flex">
                    <i class="fa fa-fw fa-envelope mr-1" style="line-height:inherit"></i>
                    <a class="js-copy-clipboard" title="Copy {{ pastor.NickName }}'s email address to your clipboard" data-clipboard-text="{{ pastor.Email }}" style="cursor:pointer">Copy Email</a>
                </div>
            </small>
        </div>
    </div>
                        {%- endif -%}
                    {%- endfor -%}
                {%- endfor -%}
                {%- if pastorCount == 0 -%}
    <div class="alert alert-info mb-0">No one is scheduled as POD today.</div> 
                {%- endif -%}
            {%- endattendanceoccurrence -%}
        {%- endcache -%}
    {% endcapture -%}
    {%- capture footerHTML -%}
        <small><a class="btn btn-xs btn-link btn-icon" href="/staff/pod"><i class="fa fa-calendar-alt"></i> <span class="text">POD Schedule</span></a></small>
        {%- assign isPastor = CurrentPerson | Group:podGroupID | Size | AsBoolean -%}
        {%- if isPastor == true -%}
            <small><a class="btn btn-xs btn-link btn-icon" href="/my/schedules"><i class="fa fa-edit"></i> <span class="text">My Availability</span></a></small>
        {%- endif -%}
    {%- endcapture -%}
    {[ panel type:'block' title:'Pastor of the Day' footer:'{{ footerHTML }}' ]}
        {{ podHTML }}
    {[ endpanel ]}
    <script src="/scripts/clipboard.js/clipboard.min.js?v=637511048140000000" type="text/javascript"></script>
    <script>
        $('.js-copy-clipboard').tooltip(
        {
            trigger: 'click',
            placement: 'bottom'
        });
    
        function setTooltip(btn, message) 
        {
            btn.tooltip('hide')
                .attr('data-original-title', message)
                .tooltip('show');
        }
    
        function hideTooltip(btn) 
        {
            setTimeout(function() { btn.tooltip('hide'); }, 2000);
        }
    
        var clipboard = new ClipboardJS('.js-copy-clipboard');
    
        clipboard.on('success', function(e) 
        {
            var btn = $(e.trigger);
            setTooltip(btn, 'Copied!');
            hideTooltip(btn);
        });
    </script>
    <style>
        .pod .panel-body { padding: 6px 18px; }
        .pod .panel-footer { padding: 5px 10px; }
    </style>

Upcoming PODs Page

pod-upcoming-schedule-page-screenshot.jpg
  1. Create a new page:

    • Page Title: Upcoming PODs
    • Site: Rock RMS
    • Layout: Full Width
    • Icon CSS Class: fas fa-bible
    • Page Routes: staff/pod
  2. Add a new HTML Content block to the page's Main zone.
  3. Edit the block properties and enable the Rock Entity Lava Command.
  4. Edit the block content and add the following HTML/Lava. Replace the ID number on the first line with the ID of your POD group.
    {%- assign podGroupID = 12345 -%}
    //-----------------------------------------------------------
    {%- assign today = 'Now' | Date:'MM/dd/yyyy' | AsDateTime -%}
    {%- assign minDateKey = today | Date:'yyyyMMdd' -%}
    {%- assign maxDateKey = today | DateAdd:13,'d' | Date:'yyyyMMdd' -%}
    {%- assign minSundayDate = today | SundayDate | AsDateTime -%}
    {%- if today < minSundayDate %}{% assign minSundayDate = minSundayDate | DateAdd:-7,'d' %}{% endif -%}
    {%- attendanceoccurrence where:'GroupId == {{ podGroupID }} && OccurrenceDateKey >= "{{ minDateKey }}" && OccurrenceDateKey <= "{{ maxDateKey }}"' disableattributeprefetch:'true' securityenabled:'false' -%}
        {%- assign maxOccurDate = attendanceoccurrenceItems | OrderBy:'OccurrenceDate desc' | First | Property:'OccurrenceDate' -%}
        {%- assign maxSaturdayDate = maxOccurDate | SundayDate | DateAdd:-1,'d' | AsDateTime -%}
        {%- if maxOccurDate > maxSaturdayDate %}{% assign maxSaturdayDate = maxSaturdayDate | DateAdd:7,'d' %}{% endif -%}
        {%- if maxSaturdayDate == null %}{% assign maxSaturdayDate = minSundayDate | DateAdd:13,'d' %}{% endif -%}
        {%- assign dayCount = minSundayDate | DateDiff:maxSaturdayDate,'d' -%}
        {%- capture podHTML %}
    <div class="clearfix mb-3"><div class="pull-right"><small><a class="btn btn-xs btn-default" href="{{ 'Global' | Attribute:'PublicApplicationRoot' }}webhooks/lava.ashx/ical/pod"><i class="fa fa-calendar-check"></i> <span class="text">iCal Feed</span></a></small></div></div>
    <table class="calendar-view month-layout table-responsive">
        <thead>
            <tr>
                <th scope="col" class="cell">Sunday</th>
                <th scope="col" class="cell">Monday</th>
                <th scope="col" class="cell">Tuesday</th>
                <th scope="col" class="cell">Wednesday</th>
                <th scope="col" class="cell">Thursday</th>
                <th scope="col" class="cell">Friday</th>
                <th scope="col" class="cell">Saturday</th>
            </tr>
        </thead>
        <tbody>
            {%- assign prevMonth = '' -%}
            {%- for i in (0..dayCount) -%}
                {%- assign date = minSundayDate | DateAdd:i,'d' -%}
                {%- assign dateKey = date | Date:'yyyyMMdd' -%}
                {%- assign weekday = date | Date:'dddd' -%}
                {%- assign month = date | Date:'MMMM' -%}
                {%- assign occurrences = attendanceoccurrenceItems | Where:'OccurrenceDate',date -%}
                {%- if weekday == 'Sunday' -%}
            <tr class="week">
                {%- endif -%}
                <td class="cell day{% if date == today %} today{% endif %}">
                {%- if month != prevMonth -%}
                    {%- assign prevMonth = month -%}
                    <div class="daynum">{{ date | Date:'MMMM d' }}</div>
                {%- else -%}
                    <div class="daynum">{{ date | Date:'d' }}</div>
                {%- endif -%}
                    <div class="pastors">
                    {%- for occurrence in attendanceoccurrenceItems -%}
                        {%- if occurrence.OccurrenceDateKey == dateKey -%}
                            {%- for attendee in occurrence.Attendees -%}
                                {%- if attendee.ScheduledToAttend == true -%}
                                    {%- assign pastor = attendee.PersonAlias.Person -%}
                                    {%- assign phoneExt = pastor | Attribute:'StaffPhoneExtension' | Default:'' -%}
                                    {%- assign slackUserID = pastor | Attribute:'SlackUserID' | Default:'' -%}
                                    {%- assign photoURL = pastor | Attribute:'StaffPhoto','RawValue' | ImageUrl:'' -%}
                                    {%- capture photoHTML -%}
                        <a href="/person/{{ pastor.Id }}" title="View {{ pastor.NickName }}'s profile">
                            <img src="/GetAvatar.ashx?Style=icon&PhotoId={{ pastor | Attribute:'StaffPhoto','Id' }}&Gender={{ pastor.Gender }}&BackgroundColor=eceae8&ForegroundColor=c0bebc" alt="{{ pastor.FullName }}" style="max-width:100px;width:100%;border-radius:50%;display:inline-block;float:none;margin:5px auto;padding:2px;border:3px solid #06c;" />
                        </a>
                                    {%- endcapture -%}
                        <div class="d-lg-flex align-items-center">
                            <div class="d-none d-sm-block mr-2">{{ photoHTML }}</div>
                            <div class="text-left">
                                <a href="/person/{{ pastor.Id }}" title="View {{ pastor.NickName }}'s profile">
                                    <strong>{{ pastor.FullName }}</strong>
                                </a>
                                <small class="text-muted d-block">
                                    {%- if phoneExt != '' -%}
                                    <div class="d-flex">
                                        <i class="fa fa-fw fa-phone mr-1" style="line-height:inherit"></i> 
                                        <span class="flex-grow-1">x{{ phoneExt }}</span>
                                    </div>
                                    {%- endif -%}
                                    {%- if slackUserID != '' -%}
                                    <div class="d-flex">
                                        <i class="fab fa-fw fa-slack mr-1" style="line-height:inherit"></i>
                                        <a href="slack://user?team=T0JDKG0RM&id={{ slackUserID }}" target="_blank">Send Slack DM</a>
                                    </div>
                                    {%- endif -%}
                                    <div class="d-flex">
                                        <i class="fa fa-fw fa-envelope mr-1" style="line-height:inherit"></i>
                                        <a class="js-copy-clipboard" title="Copy {{ pastor.NickName }}'s email address to your clipboard" data-clipboard-text="{{ pastor.Email }}" style="cursor:pointer">Copy Email</a>
                                    </div>
                                </small>
                            </div>
                        </div>
                                {%- endif -%}
                            {%- endfor -%}
                        {%- endif -%}
                    {%- endfor -%}
                    </div>
                </td>
                {%- if weekday == 'Saturday' -%}
            </tr>
                {%- endif -%}
            {%- endfor -%}
        </tbody>
    </table>
        {% endcapture -%}
    {%- endattendanceoccurrence -%}
    {[ panel title:'Next 14 Days' ]}
        {{ podHTML }}
    {[ endpanel ]}
    <script src="/scripts/clipboard.js/clipboard.min.js?v=637511048140000000" type="text/javascript"></script>
    <script>
        $('.js-copy-clipboard').tooltip(
        {
            trigger: 'click',
            placement: 'bottom'
        });
    
        function setTooltip(btn, message) 
        {
            btn.tooltip('hide')
                .attr('data-original-title', message)
                .tooltip('show');
        }
    
        function hideTooltip(btn) 
        {
            setTimeout(function() { btn.tooltip('hide'); }, 2000);
        }
    
        var clipboard = new ClipboardJS('.js-copy-clipboard');
    
        clipboard.on('success', function(e) 
        {
            var btn = $(e.trigger);
            setTooltip(btn, 'Copied!');
            hideTooltip(btn);
        });
    </script>
    <style>
        .calendar-view th
        {
            padding: 5px 10px;
            border: 1px solid #ccc; 
            text-align: center;
            background: #eee;
        }
        .calendar-view .daynum 
        {
            padding: 3px 5px;
            text-align: right;
            font-size: 20px;
            background-color: #f6f6f6;
        }
        .calendar-view .today .daynum
        {
            background-color: #cfdbeb;
        }
        .calendar-view .pastors
        {
            padding: 5px 10px;
            min-height: 60px;
        }
        .calendar-view .week .cell 
        { 
            width: 14.2%;
            min-height: 200px;
            border: 1px solid #ccc; 
            padding: 0;
            vertical-align: top;
        }
        .calendar-view .cell.today .indicator
        {
            background-color: #cfdbeb;
            color: #333;
        }
    </style>
    
    {[ panel title:'Next 12 Months' ]}
        <div class="row d-flex flex-wrap align-items-stretch">
    {%- assign minDateKey = today | Date:'yyyyMMdd' -%}
    {%- assign maxDateKey = today | DateAdd:12,'M' | Date:'yyyyMMdd' -%}
    {%- assign pastors = podGroupID | GroupById | Property:'Members' | Select:'Person' | OrderBy:'LastName, NickName' -%}
    {%- attendanceoccurrence where:'GroupId == {{ podGroupID }} && OccurrenceDateKey >= "{{ minDateKey }}" && OccurrenceDateKey <= "{{ maxDateKey }}"' disableattributeprefetch:'true' securityenabled:'false' -%}
        {%- for pastor in pastors -%}
            {%- assign photoURL = pastor | Attribute:'StaffPhoto','RawValue' | ImageUrl:'' -%}
            {%- capture photoHTML -%}
                <a href="/person/{{ pastor.Id }}" title="View {{ pastor.NickName }}'s profile">
                    <img src="/GetAvatar.ashx?Style=icon&PhotoId={{ pastor | Attribute:'StaffPhoto','Id' }}&Gender={{ pastor.Gender }}&Radius=Circle&BackgroundColor=eceae8&ForegroundColor=c0bebc" alt="{{ pastor.FullName }}" style="width:36px;height:36px;border-radius:50%;display:inline-block;float:none;margin:5px auto;padding:2px;border:2px solid #06c;" />
    
                </a>
            {%- endcapture -%}
            <div class="col-sm-6 col-md-4 col-lg-3 d-flex align-items-stretch">
                <div class="panel panel-block w-100">
                    <div class="panel-heading">
                        <h4 class="panel-title">{{ photoHTML }} <a href="/person/{{ pastor.Id }}" title="View {{ pastor.NickName }}'s profile">{{ pastor.FullName | Escape }}</a></h4>
                    </div>
                    <div class="panel-body">
            {%- assign occurrenceCount = 0 -%}
            {%- for occurrence in attendanceoccurrenceItems -%}
                {%- for attendee in occurrence.Attendees -%}
                    {%- if attendee.PersonAliasId == pastor.PrimaryAliasId and attendee.ScheduledToAttend == true -%}
                        {{ occurrence.OccurrenceDate | Date:'dddd, M/d/yyyy' }}<br>
                        {%- assign occurrenceCount = occurrenceCount | Plus:1 -%}
                        {%- break -%}
                    {%- endif -%}
                {%- endfor -%}
            {%- endfor -%}
            {%- if occurrenceCount == 0 -%}
                        <em class="text-muted">Not Currently Scheduled</em>
            {%- endif -%}
                    </div>
                </div>
            </div>
        {%- endfor -%}
    {%- endattendanceoccurrence -%}
        </div>
    {[ endpanel ]}

Schedule Preferences Page

  1. Create a new page under Internal Homepage > My Settings

    • Page Title: Schedule Preferences
    • Site: Rock RMS
    • Layout: Full Width
    • Icon CSS Class: fa fa-calendar-check
    • Page Routes: my/schedules
  2. Add a new Group Schedule Toolbox block to the page's Main zone. If you haven't updated to Rock v16 yet, then use the one that ends with "v2".
  3. Edit the block settings and change the Future Week Date Range to Next 13 Weeks

iCal Feed

  1. Create a new value under the Lava Webhooks defined type:
    • Value: /ical/pod
    • Description: Generates an iCal feed for the Pastor of the Day schedule
    • Method: GET
    • Enabled Lava Commands: Rock Entity
    • Response Content Type: text/calendar
    • Template: Replace the ID number on the first line with the ID of your POD group, and change all of the time zone information between BEGIN:VTIMEZONE and END:VTIMEZONE to match your location.
      {%- assign podGroupID = 12345 -%}
      //-----------------------------------------------------------
      {%- assign today = 'Now' | Date:'MM/dd/yyyy' | AsDateTime -%}
      {%- assign minDateKey = today | DateAdd:-1,'M' | Date:'yyyyMMdd' -%}
      {%- assign maxDateKey = today | DateAdd:12,'M' | Date:'yyyyMMdd' -%}
      BEGIN:VCALENDAR
      PRODID:-//{{ 'Global' | Attribute:'OrganizationName' }}//Rock POD Scheduler//EN
      VERSION:2.0
      BEGIN:VTIMEZONE
      TZID:America/Los_Angeles
      X-LIC-LOCATION:America/Los_Angeles
      BEGIN:STANDARD
      DTSTART:20211107T020000
      RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
      TZNAME:PST
      TZOFFSETFROM:-0700
      TZOFFSETTO:-0800
      END:STANDARD
      BEGIN:DAYLIGHT
      DTSTART:20220313T020000
      RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
      TZNAME:PDT
      TZOFFSETFROM:-0800
      TZOFFSETTO:-0700
      END:DAYLIGHT
      END:VTIMEZONE
      {%- attendanceoccurrence where:'GroupId == {{ podGroupID }} && OccurrenceDateKey >= "{{ minDateKey }}" && OccurrenceDateKey <= "{{ maxDateKey }}"' -%}
          {%- assign occurrenceCount = 0 -%}
          {%- for occurrence in attendanceoccurrenceItems -%}
              {%- assign startDateTime = occurrence.OccurrenceDate | Date:'M/d/yyyy 00:00:00' | AsDateTime -%}
              {%- assign endDateTime = startDateTime | DateAdd:24,'h' %}
              {%- assign pastorCount = 0 -%}
              {%- assign pastorList = '' -%}
              {%- for pastor in occurrence.Attendees -%}
                  {%- if pastor.ScheduledToAttend == true -%}
                      {%- assign pastorCount = pastorCount | Plus:1 -%}
                      {%- capture pastorList %}{{ pastorList }}{{ pastor.PersonAlias.Person.FullName }}{% if forloop.last == false %}\,{% endif %}{% endcapture -%}
                  {%- endif -%}
              {%- endfor -%}
              {%- if pastorCount > 0 %}
      BEGIN:VEVENT
      DTSTAMP:{{ startDateTime | AsDateTimeUtc | Date:'yyyyMMddThhmmssZ' }}
      UID:POD{{ startDateTime | Date:'yyyyMMdd' }}
      DTSTART;VALUE=DATE:{{ startDateTime | Date:'yyyyMMdd' }}
      SUMMARY:POD: {{ pastorList }}
      END:VEVENT
              {%- endif -%}
          {%- endfor -%}
      {%- endattendanceoccurrence %}
      END:VCALENDAR

All done!

As soon as you have all of your pastors scheduled and confirmed, they should start showing up on the home page POD widget and the Upcoming PODs page.

Follow Up

Please don't hesitate to leave a comment below or hit me up on Rock Chat (@JeffRichmond) if you have questions or find any issues with this recipe.

If you come up with better or more efficient ways of doing anything in this recipe, please let me know. Thanks!


Change Log

  • 2023-12-15 - Initial Version