A recent upgrade to SharePoint 2013 disabled some of the calendars. In SharePoint 2010, the out-of-the-box Calendar view renders a maximum of three events by default with an option to click a "more items" link which renders all events for the entire week containing that day. For those wanting to have all the events expanded, the solution in SharePoint 2010 does not work for SharePoint 2013 and in fact prevents the calendars from rendering events.
Do You Have Broken Calendar Pages?
If you have a site recently migrated to SharePoint 2013 with calendar pages that no longer show events on the calendar, and which pages no longer allow the buttons in the ribbon to become active, skip to the last section of this post (Fixing WSS and SharePoint 2010 Calendars with Formerly Expanded Events).
SharePoint 2013 Uses a Lot of AJAX
The technique that works in SharePoint 2010 relies on the "more items" link posting back to the server for each click. As with a lot of SharePoint 2013, there is no post back to the server to draw the entire page; clicking the expand event links as well as calendar navigation (next and previous month, etc.) rely on client-side JavaScript to change the calendar content. We needed a new way to do it.
The jQuery Solution
Add this JavaScript to a Script Editor web part to a page with a single SharePoint calendar.
Edit Page > Add Web Part > Categories – Media and Content > Script Editor > Add > Edit Snippet and insert this code:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
function expandCal() {
setInterval(expandCalCheck, 900);
}
function expandCalCheck() {
var a=$("a.ms-cal-nav", $("div.ms-acal-rootdiv:last>div")).get(0);
if (a) {
for (var r=0; r<6; r++) {
var a=$("a.ms-cal-nav", $("div.ms-acal-rootdiv:last>div").get(r)).get(0);
if (a)
if (a.innerText.indexOf("more") >= 0)
a.click();
}
}
}
ExecuteOrDelayUntilScriptLoaded(expandCal, "sp.ui.applicationpages.calendar.js");
</script>
In production, I put the jQuery file into a document library called js, and put the rest of the code into a separate file called expand.js, ending up with these two lines in the Script Editor web part:
<script type="text/javascript" src="/js/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="/js/expand.js"></script>
Using this organization allows one to use an editor outside of the Web UI to edit the JavaScript file, gets the code away from users who might edit the page and separates code and content. For these reasons, it is a "best practice", but takes a little more effort which may not really be worth it for some people.
I got burned once when an external jQuery file went away. My policy now is to put jQuery source where we have control over it.
Caveats
This works, but it is not ideal.
The Timer
I tried associating the expansion with clicking on the different calendar navigation buttons, but sometimes it got lost. The above solution checks every 900 milliseconds, (just under one second), for links that need to be expanded. Monitoring the browser's CPU usage with the Windows Task Manager reveals that this check does chew up a measurable amount of the CPU. Checking every quarter of a second really put a significant load on. The longer between checks, the less of a load, but it also means that average time it takes for the events to expand the links is longer, forcing the user to wait. Nine-tenths of a second seems to be a good compromise.
See more below about avoiding the timer.
Only One Calendar is Affected Per Page
I have not tested it, but using this code with more than one calendar on the page will probably result in only the last calendar being expanded. You can reuse this script for as many calendars as desired as long as they are on different pages.
Localization
This depends on the word "more" be in the link to expand the events. For other languages, you probably need to use a translated word, like "mas" or "plus", or perhaps refer to the appropriate resource file from the JavaScript.
How Does it Work?
First, we wait until the calendar template has been initialized:
ExecuteOrDelayUntilScriptLoaded(expandCal, "sp.ui.applicationpages.calendar.js");
The expandCal() function sets up a timer to see if we need to expand the calendar every 900 ms.
SharePoint renders a calendar template server side and then populates it with JavaScript callbacks. Before it populates the events, there is one div with a class of "ms-acal-rootdiv". After the callbacks, there are two, and we want the last one which is where the jQuery selector
div.ms-acal-rootdiv:last
comes from. This div contains five or six classless div's, one for each week's row shown in the calendar's current month. Adding the jQuery selector ">div" selects these six rows:
div.ms-acal-rootdiv:last>div
The "more items" link exists in an HTML <A> tag with a class of "ms-cal-nav". Because clicking any one of these links expands or collapses the entire week, we only want to do one per week. Since we don't want to collapse already expanded events, we execute the click() only for links with the "more items" text in it.
The code above first determines whether there exists such an A element anywhere in the current calendar, and then loops through all six weeks, clicking on the first "more items" link it finds for each week.
Avoiding the Constant Timer?
I wrote code to avoid the timing loop, but it got complicated fast and does not work 100% of the time. Specifically, if you click on a month without any weeks that need to be expanded twice in a row, adding new events to the click() triggers failed. I tried and tried to get it to work. I believe it is a timing issue.
For some clicks, the opposite happened. Adding the click()'s was sometimes geometrically cumulative. Each click doubled the number of events added, resulting in way too many calls to the expand function.
Ideally, the SharePoint process that renders the actual events would throw a catchable callback notification, but this appears to happen only on initialization of the calendar template.
Fixing WSS and SharePoint 2010 Calendars with Formerly Expanded Events
This blog post explains how to expand all calendar events via HTML and JavaScript: http://moblog.bradleyit.com/2009/08/sharepoint-expand-calendar-month-view.html
Unfortunately, this code in SharePoint 2013 does not allow the callbacks that render the actual events to fire, probably because the window.onload function is overridden. Only the template is rendered. Even worse, the code suppresses the JavaScript that allows the ribbon's buttons to work. This means that while you can put the page into edit mode, you will find it hard to actually edit anything.
The Solution is to Edit with SharePoint Designer
You can remove this custom code with SharePoint Designer 2013, a free download at http://www.microsoft.com/en-us/download/details.aspx?id=35491. Choose the 32- or 64-bit version that matches the Office version of the target machine. Best practice is to use the 32-bit version unless you are using humungous Excel spreadsheets or something.
- Follow these steps for each calendar that is broken. A search on calendar.aspx can be a help to find them all.
-
Open the appropriate site or subsite in SPD 2013.
- Take everything up to the /Lists… in the URL. E.g., for http://site/Lists/SiteCalendar/calendar.aspx, use http://site
-
Navigate to the page's folder, make a backup of the page and open it
- E.g., for http://site/Lists/Client%20Calendar/calendar.aspx, click Navigation - Site Objects > All Files > Lists > Client Calendar
-
Copy and paste the page as a backup
- Right click > Copy > right click > Paste
- This typically creates calendar_copy(1).aspx
- Open the page by clicking on its name
-
Remove all code within the square brackets of <!CDATA[]>
-
For example,
<![CDATA[<div id="cover" style="background: white; left: 0px; top: 0px; width: 1920px; height: 940px; display: none; position: absolute;"></div><script type="text/javascript">var yScroll = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;var y = window.innerHeight || document.body.clientHeight || document.documentElement.clientHeight;var xScroll = window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft;var x = window.innerWidth || document.body.clientWidth || document.documentElement.clientWidth;var cover = document.getElementById("cover");cover.style.height = (y + yScroll) + "px";cover.style.width = (x + xScroll) + "px";function expand() {if (document.referrer != location.href && !location.href.match(/CalendarPeriod=((week)|(day))/)) { GetMonthView('11111111'); }else { cover.style.display = "none"; }}window.onload = function() { expand(); };</script>]]>
becomes just this:
<![CDATA[]]>
- Save the page and test that it renders events again
- Optional: remove the content editor web part from the page altogether by opening the page in a browser and then editing the page. Note that the ribbon buttons and therefore editing does not work until after the code has been removed from within CDATA in the previous step.