Editorial Calendar - Version 0.3

Version Description

  • Additional bug fixes and stabalizations as well as the ability to add new posts to a given date from the calendar.
Download this release

Release Info

Developer MaryVogt
Plugin Icon wp plugin Editorial Calendar
Version 0.3
Comparing to
See all releases

Code changes from version 0.2 to 0.3

Files changed (4) hide show
  1. edcal.css +118 -105
  2. edcal.js +1029 -954
  3. edcal.php +398 -345
  4. readme.txt +22 -7
edcal.css CHANGED
@@ -15,212 +15,225 @@
15
  ******************************************************************************/
16
 
17
  #edcal_scrollable {
18
- width: 98%;
19
- /* required settings */
20
- position:relative;
21
- overflow:hidden;
22
 
23
- /* This height will be reset with JavaScript */
24
- height: 100%;
25
  }
26
 
27
  #cal {
28
- position:absolute;
29
 
30
- /* this time we have very large space for the height */
31
- height:100%;
32
- margin-right: 10px;
33
  }
34
 
35
  .day {
36
- /*background-color: #F2F2F2;*/
37
- background-color: #e0e0e0;;
38
  }
39
 
40
  .scrollHover {
41
- background: lightblue;
42
  }
43
 
44
  .row {
45
- height: 100%;
46
  }
47
 
48
  .jan, .mar, .may, .jul, .sep, .nov {
49
- background-color: #F1F1F1;
50
  }
51
 
52
  .today {
53
- background-color: wheat;
54
  }
55
 
56
  .today .daylabel {
57
- font-weight: bold;
58
  }
59
 
60
  .dayobj {
61
- position: relative;
62
- width: 100%;
63
- height: 100%;
64
  }
65
 
66
  .daylabel {
67
- position: absolute;
68
- top: 0px;
69
- right: 5px;
 
70
  }
71
 
72
  .post {
73
- display: block;
74
- list-style-type: none;
75
- background: #FFFFE0;
76
- z-index: 10;
77
- cursor: move;
78
- -moz-border-radius:6px;
79
- -webkit-border-radius:6px;
80
- margin-left: 2px;
81
- margin-right: 2px;
82
- padding: 5px;
83
  }
84
 
85
  .post:hover {
86
- color: white;
87
- background: black;
88
  }
89
 
90
  .day-active {
91
- background-color: lightyellow;
92
  }
93
 
94
  .dayhead {
95
- font-weight: bold;
96
- text-align: center;
97
- color: gray;
98
  }
99
 
100
  #rowhead {
101
- height: 1.5em;
102
- top: 0px;
103
- width: 98%
104
  }
105
 
106
  #topbar {
107
- margin-bottom: 1em;
108
- width: 98%;
109
- margin-top: 1em;
110
- position: relative;
111
- height: 2em;
112
  }
113
 
114
  #topleft {
115
- position: absolute;
116
- left: 10px;
117
- top: -14px;
118
- width: auto;
119
  }
120
 
121
  #topright {
122
- position: absolute;
123
- right: 2em;
124
- top: 0px;
125
- width: auto;
126
- text-align: right;
127
  }
128
 
129
  #moveToToday {
130
- margin-left: 2em;
131
- text-decoration: none;
132
  }
133
 
134
  .future {
135
- background-color: #FFFFE0;
136
  }
137
 
138
- .draft {
139
- background-color: #cccccc;
140
-
141
  }
142
 
143
  .publish {
144
- background-color: #dde4fb;
145
  }
146
 
147
  #loadingcont {
148
- position: relative;
149
  }
150
 
151
  #loading {
152
- background-image: url('images/loading.gif');
153
- background-repeat: no-repeat;
154
- position: absolute;
155
- width: 43px;
156
- height: 11px;
157
- display: block;
158
- left: 20em;
159
- top: -1.5em;
160
  }
161
 
162
  .loadingclass, .loadingclass:hover {
163
- background-image: url('images/loading_post.gif');
164
- background-position: top right;
165
- background-repeat: no-repeat;
166
- cursor: auto;
167
  }
168
 
169
  .postlist {
170
- position: relative;
171
- top: 1.5em;
172
- height: 85%;
173
- overflow: auto;
174
  }
175
 
176
  .page-numbers {
177
- text-decoration: none;
178
- border: none;
179
- border-bottom-style: none;
180
  }
181
 
182
  #tooltip p {
183
- margin-left: 1.5em;
184
  }
185
 
186
  #tooltip h3 {
187
- max-width: 20em;
188
- margin-bottom: -10px;
189
- padding-right: 15px;
190
  }
191
 
192
  #tooltip {
193
- background: white;
194
- opacity: 1;
195
- padding: 1em;
196
- padding-bottom: 0em;
197
- border: thin solid lightgray;
198
- -moz-border-radius:11px;
199
- -webkit-border-radius:11px;
200
- -moz-box-shadow:0 4px 18px #C8C8C8;
201
  }
202
 
203
  .ui-draggable-dragging {
204
- width: 8em;
205
  }
206
 
207
  #tipclose {
208
- width: 24px;
209
- height: 23px;
210
- position: absolute;
211
- top: 0px;
212
- right: -10px;
213
  }
214
 
215
  .tooltip {
216
- position: relative;
217
  }
218
 
219
  #edcal-title-box {
220
- display: none;
221
- padding-right: 15px;
222
  }
223
 
224
  #edcal-title-edit {
225
- font-size: 11px;
 
 
 
 
 
 
 
 
 
 
 
 
226
  }
15
  ******************************************************************************/
16
 
17
  #edcal_scrollable {
18
+ width: 98%;
19
+ /* required settings */
20
+ position:relative;
21
+ overflow:hidden;
22
 
23
+ /* This height will be reset with JavaScript */
24
+ height: 100%;
25
  }
26
 
27
  #cal {
28
+ position:absolute;
29
 
30
+ /* this time we have very large space for the height */
31
+ height:100%;
32
+ margin-right: 10px;
33
  }
34
 
35
  .day {
36
+ /*background-color: #F2F2F2;*/
37
+ background-color: #e0e0e0;;
38
  }
39
 
40
  .scrollHover {
41
+ background: lightblue;
42
  }
43
 
44
  .row {
45
+ height: 100%;
46
  }
47
 
48
  .jan, .mar, .may, .jul, .sep, .nov {
49
+ background-color: #F1F1F1;
50
  }
51
 
52
  .today {
53
+ background-color: wheat;
54
  }
55
 
56
  .today .daylabel {
57
+ font-weight: bold;
58
  }
59
 
60
  .dayobj {
61
+ position: relative;
62
+ width: 100%;
63
+ height: 100%;
64
  }
65
 
66
  .daylabel {
67
+ position: absolute;
68
+ top: 0px;
69
+ right: 5px;
70
+ font-size: 1.25em;
71
  }
72
 
73
  .post {
74
+ display: block;
75
+ list-style-type: none;
76
+ background: #FFFFE0;
77
+ z-index: 10;
78
+ cursor: move;
79
+ -moz-border-radius:6px;
80
+ -webkit-border-radius:6px;
81
+ margin-left: 2px;
82
+ margin-right: 2px;
83
+ padding: 5px;
84
  }
85
 
86
  .post:hover {
87
+ color: white;
88
+ background: black;
89
  }
90
 
91
  .day-active {
92
+ background-color: lightyellow;
93
  }
94
 
95
  .dayhead {
96
+ font-weight: bold;
97
+ text-align: center;
98
+ color: gray;
99
  }
100
 
101
  #rowhead {
102
+ height: 1.5em;
103
+ top: 0px;
104
+ width: 98%
105
  }
106
 
107
  #topbar {
108
+ margin-bottom: 1em;
109
+ width: 98%;
110
+ margin-top: 1em;
111
+ position: relative;
112
+ height: 2em;
113
  }
114
 
115
  #topleft {
116
+ position: absolute;
117
+ left: 10px;
118
+ top: -14px;
119
+ width: auto;
120
  }
121
 
122
  #topright {
123
+ position: absolute;
124
+ right: 2em;
125
+ top: 0px;
126
+ width: auto;
127
+ text-align: right;
128
  }
129
 
130
  #moveToToday {
131
+ margin-left: 2em;
132
+ text-decoration: none;
133
  }
134
 
135
  .future {
136
+ background-color: #FFFFE0;
137
  }
138
 
139
+ .draft, .pending {
140
+ background-color: #cccccc;
141
+
142
  }
143
 
144
  .publish {
145
+ background-color: #dde4fb;
146
  }
147
 
148
  #loadingcont {
149
+ position: relative;
150
  }
151
 
152
  #loading {
153
+ background-image: url('images/loading.gif');
154
+ background-repeat: no-repeat;
155
+ position: absolute;
156
+ width: 43px;
157
+ height: 11px;
158
+ display: block;
159
+ left: 20em;
160
+ top: -1.5em;
161
  }
162
 
163
  .loadingclass, .loadingclass:hover {
164
+ background-image: url('images/loading_post.gif');
165
+ background-position: top right;
166
+ background-repeat: no-repeat;
167
+ cursor: auto;
168
  }
169
 
170
  .postlist {
171
+ position: relative;
172
+ top: 1.5em;
173
+ height: 85%;
174
+ overflow: auto;
175
  }
176
 
177
  .page-numbers {
178
+ text-decoration: none;
179
+ border: none;
180
+ border-bottom-style: none;
181
  }
182
 
183
  #tooltip p {
184
+ margin-left: 1.5em;
185
  }
186
 
187
  #tooltip h3 {
188
+ max-width: 20em;
189
+ margin-bottom: -10px;
190
+ padding-right: 15px;
191
  }
192
 
193
  #tooltip {
194
+ background: white;
195
+ opacity: 1;
196
+ padding: 1em;
197
+ padding-bottom: 0em;
198
+ border: thin solid lightgray;
199
+ -moz-border-radius:11px;
200
+ -webkit-border-radius:11px;
201
+ -moz-box-shadow:0 4px 18px #C8C8C8;
202
  }
203
 
204
  .ui-draggable-dragging {
205
+ width: 8em;
206
  }
207
 
208
  #tipclose {
209
+ width: 24px;
210
+ height: 23px;
211
+ position: absolute;
212
+ top: 0px;
213
+ right: -10px;
214
  }
215
 
216
  .tooltip {
217
+ position: relative;
218
  }
219
 
220
  #edcal-title-box {
221
+ display: none;
222
+ padding-right: 15px;
223
  }
224
 
225
  #edcal-title-edit {
226
+ font-size: 11px;
227
+ }
228
+
229
+ .daynewlink {
230
+ display: none;
231
+ padding-left: 0.5em;
232
+ padding-right: 0.5em;
233
+ text-decoration: none;
234
+ font-weight: bold;
235
+ }
236
+
237
+ #nextmonth, #prevmonth {
238
+ padding: 0.5em;
239
  }
edcal.js CHANGED
@@ -21,7 +21,7 @@
21
  * @param msg the message to write
22
  */
23
  function output(msg) {
24
- //console.info(msg);
25
  }
26
 
27
  /*
@@ -35,17 +35,17 @@ function output(msg) {
35
  The calendar shows five weeks visible at a time and maintains 11
36
  weeks of rendered HTML. Only the middle weeks are visible.
37
 
38
- Week 1
39
- Week 2
40
- Week 3
41
- - Week 4 -
42
- | Week 5 |
43
- | Week 6 |
44
- | Week 7 |
45
- - Week 8 -
46
- Week 9
47
- Week 10
48
- Week 11
49
 
50
  When the user scrolls down one week the new week is added at the
51
  end of the calendar and the first week is removed. In this way
@@ -58,953 +58,1028 @@ function output(msg) {
58
  The HTML structure of the calendar is:
59
 
60
  <div id="cal">
61
- <div id="row08Nov2009">
62
- <div id="row08Nov2009row">
63
- <div class="day sunday nov" id="08Nov2009">
64
- <div class="dayobj">
65
- <div class="daylabel">8</div>
66
- <ul class="postlist">
67
- </ul>
68
- </div>
69
- </div>
70
- </div>
71
- </div>
72
  </div>
73
  */
74
  var edcal = {
75
- /*
76
- * True if the calendar is in the process of moving
77
- */
78
- isMoving: false,
79
-
80
- /*
81
- This is the base URL we use to make AJAX calls back
82
- to the server. This value is set in code generated
83
- from edcal.php that run in a script tag in the main
84
- page.
85
- */
86
- ajax_url: '',
87
-
88
- /*
89
- * The cache of dates we have already loaded posts for.
90
- */
91
- cacheDates : [],
92
-
93
- /*
94
- * The ID of the timer we use to batch new post requests
95
- */
96
- tID: null,
97
-
98
- /*
99
- * The number of steps moving for this timer.
100
- */
101
- steps: 0,
102
-
103
- /*
104
- * The constant for the concurrency error.
105
- */
106
- CONCURRENCY_ERROR: 4,
107
-
108
- /*
109
- * The constant for the user permission error
110
- */
111
- PERMISSION_ERROR: 5,
112
-
113
- /*
114
- The direction the calendar last moved.
115
- true = down = to the future
116
- false = up = to the past
117
-
118
- */
119
- currentDirection: true,
120
-
121
- /*
122
- This date is our index. When the calendar moves we
123
- update this date to indicate the next rows we need
124
- to add.
125
- */
126
- _wDate: Date.today(),
127
-
128
- /*
129
- * The date since the previous move
130
- */
131
- moveDate: null,
132
-
133
- /*
134
- A cache of all the posts we have loaded so far. The
135
- data structure is:
136
 
137
- posts [date - ddMMMyyyy][posts array - post object from JSON data]
138
- */
139
- posts: new Array(50),
140
-
141
- /*
142
- IE will sometimes fire the resize event twice for the same resize
143
- action. We save it so we only resize the calendar once and avoid
144
- any flickering.
145
- */
146
- windowHeight: 0,
147
-
148
- /*
149
- This function aligns the grid in two directions. There
150
- is a vertical grid with a row of each week and a horizontal
151
- grid for each week with a list of days.
152
- */
153
- alignGrid: function(/*string*/ gridid, /*int*/ cols, /*int*/ cellWidth, /*int*/ cellHeight, /*int*/ padding) {
154
- var x = 0;
155
- var y = 0;
156
- var count = 1;
157
-
158
- jQuery(gridid).each(function(){
159
- jQuery(this).css("position", "relative");
160
-
161
- jQuery(this).children("div").each(function(){
162
- jQuery(this).css({
163
- width: cellWidth + "%",
164
- height: cellHeight + "%",
165
- position: "absolute",
166
- left: x + "%",
167
- top: y + "%"
168
- });
169
-
170
- if ((count % cols) === 0) {
171
- x = 0;
172
- y += cellHeight + padding;
173
- } else {
174
- x += cellWidth + padding;
175
- }
176
-
177
- count++;
178
- });
179
- });
180
- },
181
-
182
- /*
183
- This is a helper function to align the calendar so we don't
184
- have to change the cell sizes in multiple places.
185
- */
186
- alignCal: function() {
187
- edcal.alignGrid("#cal", 1, 100, 20, 0.25);
188
- },
189
-
190
-
191
- /*
192
- This function creates the days header at the top of the
193
- calendar.
194
 
195
- TODO: We should localize these values
196
- */
197
- createDaysHeader: function() {
198
- var html = '<div class="dayhead">Sunday</div>';
199
- html += '<div class="dayhead">Monday</div>';
200
- html += '<div class="dayhead">Tuesday</div>';
201
- html += '<div class="dayhead">Wednesday</div>';
202
- html += '<div class="dayhead">Thursday</div>';
203
- html += '<div class="dayhead">Friday</div>';
204
- html += '<div class="dayhead">Saturday</div>';
205
-
206
- jQuery("#rowhead").append(html);
207
-
208
- edcal.alignGrid("#rowhead", 7, 14.2, 100, 0.25);
209
- },
210
-
211
- /*
212
- Creates a row of the calendar and adds all of the CSS classes
213
- and listeners for each calendar day.
214
- */
215
- createRow: function(/*jQuery*/ parent, /*bool*/ append) {
216
- var _date = edcal._wDate.clone();
217
-
218
- var newrow = '<div class="rowcont" id="' + 'row' + edcal._wDate.toString("ddMMMyyyy") + '">' +
219
- '<div id="' + 'row' + edcal._wDate.toString("ddMMMyyyy") + 'row" class="row">';
220
- for (var i = 0; i < 7; i++) {
221
- newrow += '<div id="' + _date.toString("ddMMMyyyy") + '" class="day ' +
222
- _date.toString("dddd").toLowerCase() + ' ' +
223
- _date.toString("MMM").toLowerCase() + '">';
224
-
225
- newrow += '<div class="dayobj">';
226
-
227
- newrow += '<div class="daylabel">';
228
- if (_date.toString("dd") == "01") {
229
- newrow += _date.toString("MMM d");
230
- } else {
231
- newrow += _date.toString("d");
232
- }
233
- newrow += '</div>';
234
-
235
- newrow += '<ul class="postlist">';
236
-
237
- newrow += edcal.getPostItems(_date.toString("ddMMMyyyy"));
238
-
239
- newrow += '</ul>';
240
-
241
- newrow += '</div>';
242
- newrow += '</div>';
243
- _date.add(1).days();
244
- }
245
-
246
- newrow += '</div></div';
247
-
248
- if (append) {
249
- parent.append(newrow);
250
-
251
- } else {
252
- parent.prepend(newrow);
253
- }
254
-
255
- edcal.alignGrid("#row" + edcal._wDate.toString("ddMMMyyyy") + "row", 7, 14.2, 100, 0.25);
256
-
257
- jQuery('#row' + edcal._wDate.toString("ddMMMyyyy") + ' .day').each( function() {
258
- edcal.addTooltip(jQuery(this).attr("id"));
259
- });
260
-
261
- edcal.draggablePost('#row' + edcal._wDate.toString("ddMMMyyyy") + ' .post');
262
-
263
- jQuery('#row' + edcal._wDate.toString("ddMMMyyyy") + ' .day').droppable({
264
- hoverClass: 'day-active',
265
- accept: '.post',
266
- greedy: true,
267
- tolerance: 'pointer',
268
- drop: function(event, ui) {
269
- //output('dropped ui.draggable.attr("id"): ' + ui.draggable.attr("id"));
270
- //output('dropped on jQuery(this).attr("id"): ' + jQuery(this).attr("id"));
271
- //output('ui.draggable.html(): ' + ui.draggable.html());
272
-
273
- var dayId = ui.draggable.parent().parent().parent().attr("id");
274
-
275
- // Step 0. Get the post object from the map
276
- var post = edcal.findPostForId(ui.draggable.parent().parent().parent().attr("id"),
277
- ui.draggable.attr("id"));
278
-
279
- // Step 1. Remove the post from the posts map
280
- edcal.removePostFromMap(ui.draggable.parent().parent().parent().attr("id"),
281
- ui.draggable.attr("id"));
282
-
283
- // Step 2. Remove the old element from the old parent.
284
- jQuery('#' + ui.draggable.attr("id")).remove();
285
-
286
- // Step 3. Add the item to the new DOM parent
287
- jQuery('#' + jQuery(this).attr("id") + ' .postlist').append(edcal.createPostItem(post,
288
- jQuery(this).attr("id")));
289
-
290
- // Step 4. And add the tooltip
291
- edcal.addTooltip(jQuery(this).attr("id"));
292
-
293
- if (dayId == jQuery(this).attr("id")) {
294
- /*
295
- If they dropped back on to the day they started with we
296
- don't want to go back to the server.
297
- */
298
- edcal.draggablePost('#' + jQuery(this).attr("id") + ' .post');
299
- } else {
300
- // Step6. Update the date on the server
301
- edcal.changeDate(jQuery(this).attr("id"), post);
302
- }
303
- }
304
- });
305
-
306
- return jQuery('row' + edcal._wDate.toString("ddMMMyyyy"));
307
- },
308
-
309
- /*
310
- * This is a helper method to make an individual post item draggable.
311
- */
312
- draggablePost: function(/*post selector*/ post) {
313
- /*
314
- * Click is a different operation than drag in our UI. The problem is if
315
- * the user is moving their mouse just a little bit we can think it is a
316
- * drag instead of a click. We stop this by delaying the drag event until
317
- * the user has dragged at least 10 pixels. This works fine on IE and
318
- * Firefox, but on Chrome it causes text selection so we don't delay on
319
- * Chrome.
320
- */
321
- var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
322
- var is_safari = navigator.userAgent.toLowerCase().indexOf('safari') > -1;
323
-
324
- if (is_chrome || is_safari) {
325
- jQuery(post).draggable({
326
- revert: 'invalid',
327
- appendTo: 'body',
328
- helper: "clone",
329
- addClasses: false
330
- });
331
- } else {
332
- jQuery(post).draggable({
333
- revert: 'invalid',
334
- appendTo: 'body',
335
- distance: 10,
336
- helper: "clone",
337
- addClasses: false
338
- });
339
- }
340
- },
341
-
342
- /*
343
- This is a utility method to find a post and remove it
344
- from the cache map.
345
- */
346
- removePostFromMap: function(/*string*/ dayobjId, /*string*/ postId) {
347
- if (edcal.posts[dayobjId]) {
348
- for (var i = 0; i < edcal.posts[dayobjId].length; i++) {
349
- if (edcal.posts[dayobjId][i] &&
350
- "post-" + edcal.posts[dayobjId][i].id === postId) {
351
- edcal.posts[dayobjId][i] = null;
352
- return true;
353
- }
354
- }
355
- }
356
-
357
- return false;
358
- },
359
-
360
- /*
361
- * Adds a post to al already existing calendar day.
362
- */
363
- addPostItem: function(/*post*/ post, /*string*/ dayobjId) {
364
- jQuery('#' + dayobjId + ' .postlist').append(edcal.createPostItem(post, dayobjId));
365
- },
366
-
367
- /*
368
- Makes all the posts in the specified day draggable
369
- and adds the tooltip.
370
- */
371
- addPostItemDragAndToolltip: function(/*string*/ dayobjId) {
372
- edcal.draggablePost('#' + dayobjId + ' .post');
373
- edcal.addTooltip(dayobjId);
374
- },
375
-
376
- /*
377
- * Confirms if you want to delete the specified post
378
- */
379
- confirmDelete: function(/*string*/ posttitle) {
380
- if (confirm('You are about to delete this post ' + posttitle + '.\n\n Press cancel to stop, OK to delete.')) {
381
- return true;
382
- } else {
383
- return false;
384
- }
385
- },
386
-
387
- /*
388
- This is an AJAX function to save the past title when
389
- the user presses the save button on the tooltip.
390
- */
391
- saveTitle: function(/*string*/ postId) {
392
-
393
- var url = edcal.ajax_url + "?action=edcal_changetitle&postid=" + postId +
394
- "&title=" + jQuery.URLEncode(jQuery("#edcal-title-edit-field").val());
395
-
396
- jQuery("#post-" + postId).addClass("loadingclass");
397
-
398
- jQuery("#tooltip").hide();
399
-
400
- jQuery.ajax( {
401
- url: url,
402
- type: "POST",
403
- processData: false,
404
- timeout: 100000,
405
- dataType: "json",
406
- success: function(res) {
407
- edcal.removePostItem(res.post.date, "post-" + res.post.id);
408
- edcal.addPostItem(res.post, res.post.date);
409
- edcal.addPostItemDragAndToolltip(res.post.date);
410
- },
411
- error: function(xhr) {
412
- edcal.showError("There was an error contacting your blog.");
413
- if (xhr.responseText) {
414
- output("xhr.responseText: " + xhr.responseText);
415
- }
416
- }
417
- });
418
- },
419
-
420
- /*
421
- * Cancels the edit title action in the tooltip.
422
- */
423
- cancelEditTitle: function(/*string*/ postTitle) {
424
- jQuery("#edcal-title-edit-field").val(postTitle);
425
-
426
- jQuery("#edcal-title-box").hide();
427
- jQuery("#edcal-title").show();
428
-
429
- },
430
-
431
- /*
432
- * Shows the edit title UI in the tooltip.
433
- */
434
- editTitle: function() {
435
- jQuery("#edcal-title").hide();
436
- jQuery("#edcal-title-box").show();
437
-
438
- jQuery("#edcal-title-edit-field").focus();
439
- jQuery("#edcal-title-edit-field").select();
440
- },
441
-
442
- /*
443
- Switches back to the normal tooltip title view
444
- and closes the tooltip.
445
- */
446
- closeTooltip: function() {
447
- edcal.cancelEditTitle();
448
- jQuery("#tooltip").hide();
449
- },
450
-
451
- /*
452
- * Adds a tooltip to every post in the specified day.
453
- */
454
- addTooltip: function(/*string*/ dayobjId) {
455
- jQuery('#' + dayobjId + ' .post').tooltip({
456
- delay: 1500,
457
- bodyHandler: function() {
458
- var post = edcal.findPostForId(dayobjId, jQuery(this).attr("id"));
459
- var tooltip = '<div class="tooltip">' +
460
- '<a href="#" id="tipclose" onclick="edcal.closeTooltip(); return false;" title="close"> </a>' +
461
- '<h3 id="edcal-title">' + post.title +
462
- ' <a href="#" onclick="edcal.editTitle(); return false;" class="edit-post-status" id="edcal-title-edit">Edit</a>' +
463
- '</h3>' +
464
- '<div id="edcal-title-box">' +
465
- '<input type="text" value="' + post.title + '" id="edcal-title-edit-field"/> &nbsp;&nbsp;' +
466
- '<span id="edit-slug-buttons">' +
467
- '<a class="save button" href="#" onclick="edcal.saveTitle(\'' + post.id + '\'); return false;">Save</a> ' +
468
- '<a href="#" onclick="edcal.cancelEditTitle(\'' + post.title + '\'); return false;" class="cancel">Cancel</a></span>' +
469
- '</div>' +
470
- '<p>' +
471
- '<i>by</i> ' + post.author + ' <i>on</i> ' +
472
- edcal.getDayFromDayId(post.date).toString("MMMM d, yyyy") +
473
- '</p>' +
474
- '<p>' +
475
- 'Status<b>: ' + post.status + '</b>' +
476
- '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
477
- if (post.editlink) {
478
- /*
479
- * If the user doesn't have permission to edit a post then
480
- * then server won't send the edit link URL and we shouldn't
481
- * show the edit link
482
- */
483
- tooltip += '<a href="' + post.editlink + '" title="Edit ' + post.title +
484
- '">Edit</a>&nbsp; | &nbsp;';
485
- }
486
-
487
- if (post.dellink) {
488
- tooltip += '<a class="submitdelete" href="' + post.dellink + '" ' +
489
- 'onclick="return edcal.confirmDelete(\'' + post.title + '\');"' +
490
- 'title="Delete ' + post.title + '">Delete</a> &nbsp; | &nbsp;';
491
- }
492
-
493
- tooltip += '<a href="' + post.permalink + '" title="View ' + post.title + '">View</a>' +
494
- '</p>' +
495
- '</div>';
496
-
497
- return tooltip;
498
- }
499
- });
500
- },
501
-
502
- /*
503
- Creates the HTML for a post item and adds the data for
504
- the post to the posts cache.
505
- */
506
- createPostItem: function(/*post*/ post, /*string*/ dayobjId) {
507
- var postHtml = edcal.getPostItemString(post);
508
- if (!edcal.posts[dayobjId]) {
509
- edcal.posts[dayobjId] = new Array(0);
510
- }
511
-
512
- edcal.posts[dayobjId][edcal.posts[dayobjId].length] = post;
513
-
514
- return postHtml;
515
- },
516
-
517
- /*
518
- Finds the post object for the specified post ID in the
519
- specified day.
520
- */
521
- findPostForId: function(/*string*/ dayobjId, /*string*/ postId) {
522
- if (edcal.posts[dayobjId]) {
523
- for (var i = 0; i < edcal.posts[dayobjId].length; i++) {
524
- if (edcal.posts[dayobjId][i] &&
525
- "post-" + edcal.posts[dayobjId][i].id === postId) {
526
- return edcal.posts[dayobjId][i];
527
- }
528
- }
529
- }
530
- },
531
-
532
- /*
533
- * Removes a post from the HTML and the posts cache.
534
- */
535
- removePostItem: function(/*string*/ dayobjId, /*string*/ postId) {
536
- if (edcal.findPostForId(dayobjId, postId)) {
537
- for (var i = 0; i < edcal.posts[dayobjId].length; i++) {
538
- if (edcal.posts[dayobjId][i] &&
539
- "post-" + edcal.posts[dayobjId][i].id === postId) {
540
- edcal.posts[dayobjId][i] = null;
541
- jQuery("#" + postId).remove();
542
- }
543
- }
544
-
545
- }
546
- },
547
-
548
- /*
549
- Gets all the post items for the specified day from
550
- the post cache.
551
- */
552
- getPostItems: function(/*string*/ dayobjId) {
553
- var postsString = "";
554
-
555
- if (edcal.posts[dayobjId]) {
556
- for (var i = 0; i < edcal.posts[dayobjId].length; i++) {
557
- if (edcal.posts[dayobjId][i]) {
558
- postsString += edcal.getPostItemString(edcal.posts[dayobjId][i]);
559
- }
560
- }
561
- }
562
-
563
- return postsString;
564
- },
565
-
566
- /*
567
- * Gets the HTML string for a post.
568
- */
569
- getPostItemString: function(/*post*/ post) {
570
- return '<li id="post-' + post.id + '" class="post ' + post.status + '">' + post.title + '</li>';
571
- },
572
-
573
- /*
574
- Finds the calendar cell for the current day and adds the
575
- class "today" to that cell.
576
- */
577
- setClassforToday: function() {
578
- /*
579
- We want to set a class for the cell that represents the current day so we ca
580
- give it a background color.
581
- */
582
- jQuery('#' + Date.today().toString("ddMMMyyyy")).addClass("today");
583
- },
584
-
585
- /*
586
- Most browsers need us to set a calendar height in pixels instead
587
- of percent. This function get the correct pixel height for the
588
- calendar based on the window height.
589
- */
590
- getCalHeight: function() {
591
- var myHeight = 0;
592
- if ( typeof( window.innerWidth ) == 'number' ) {
593
- //Non-IE
594
- myHeight = window.innerHeight;
595
- } else if ( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
596
- //IE 6+ in 'standards compliant mode'
597
- myHeight = document.documentElement.clientHeight;
598
- } else if ( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
599
- //IE 4 compatible
600
- myHeight = document.body.clientHeight;
601
- }
602
- return myHeight - 150;
603
- },
604
-
605
- /*
606
- Moves the calendar a certain number of steps in the specified direction.
607
- True moves the calendar down into the future and false moves the calendar
608
- up into the past.
609
- */
610
- move: function(steps, direction) {
611
- /*
612
- The working date is a marker for the last calendar row we created.
613
- If we are moving forward that will be the last row, if we are moving
614
- backward it will be the first row. If we switch direction we need
615
- to bump up our date by 11 rows times 7 days a week or 77 days.
616
- */
617
- if (edcal.currentDirection != direction) {
618
- if (direction) { // into the future
619
- edcal._wDate = edcal._wDate.add(77).days();
620
- } else { // into the past
621
- edcal._wDate = edcal._wDate.add(-77).days();
622
- }
623
-
624
- edcal.steps = 0;
625
- edcal.moveDate = edcal._wDate;
626
- }
627
-
628
- edcal.currentDirection = direction;
629
-
630
- var i;
631
-
632
-
633
- if (direction) {
634
- for (i = 0; i < steps; i++) {
635
- jQuery("#cal > div:first").remove();
636
- edcal.createRow(jQuery("#cal"), true);
637
- edcal._wDate.add(7).days();
638
- }
639
- edcal.alignCal();
640
- } else {
641
- for (i = 0; i < steps; i++) {
642
- jQuery("#cal > div:last").remove();
643
- edcal.createRow(jQuery("#cal"), false);
644
- edcal._wDate.add(-7).days();
645
- }
646
- edcal.alignCal();
647
- }
648
-
649
- edcal.setClassforToday();
650
- edcal.setDateLabel();
651
-
652
- /*
653
- * If the user clicks quickly or uses the mouse wheel they can
654
- * get a lot of move events very quickly and we need to batch
655
- * them up together. We set a timeout and clear it if there is
656
- * another move before the timeout happens.
657
- */
658
- edcal.steps += steps;
659
- if (edcal.tID) {
660
- clearTimeout(edcal.tID);
661
- } else {
662
- edcal.moveDate = edcal._wDate;
663
- }
664
-
665
- edcal.tID = setTimeout(function() {
666
- /*
667
- * Now that we are done moving the calendar we need to get the posts for the
668
- * new dates. We want to load the posts between the place the calendar was
669
- * at when the user started moving it and the place the calendar is at now.
670
- */
671
- if (!direction) {
672
- edcal.getPosts(edcal._wDate.clone(),
673
- edcal._wDate.clone().add(7 * (edcal.steps + 1)).days());
674
- } else {
675
- edcal.getPosts(edcal._wDate.clone().add(-7 * (edcal.steps + 1)).days(),
676
- edcal._wDate.clone());
677
- }
678
-
679
- edcal.steps = 0;
680
- edcal.tID = null;
681
- edcal.moveDate = edcal._wDate;
682
- }, 1000);
683
- },
684
-
685
- /*
686
- We use the date as the ID for day elements, but the Date
687
- library can't parse the date without spaces and using
688
- spaces in IDs can cause problems. We work around the
689
- issue by adding the spaces back before we parse.
690
- */
691
- getDayFromDayId: function(/*dayId*/ day) {
692
- return Date.parse(day.substring(0, 2) + ' ' + day.substring(2, 5) + ' ' + day.substring(5));
693
- },
694
-
695
- /*
696
- This is a helper method to set the date label on the top of
697
- the calendar. It looks like November 2009-December2009
698
- */
699
- setDateLabel: function(year) {
700
- var api = jQuery(".edcal_scrollable").scrollable();
701
- var items = api.getVisibleItems();
702
-
703
- /*
704
- We need to get the first day in the first week and the
705
- last day in the last week. We call children twice to
706
- work around a small JQuery issue.
707
- */
708
- var firstDate = edcal.getDayFromDayId(items.eq(0).children(".row").children(".day:first").attr("id"));
709
- var lastDate = edcal.getDayFromDayId(items.eq(items.length - 1).children(".row").children(".day:last").attr("id"));
710
-
711
- jQuery("#currentRange").text(firstDate.toString("MMMM yyyy") + " - " + lastDate.toString("MMMM yyyy"));
712
- },
713
-
714
- /*
715
- * Moves the calendar to the specified date.
716
- */
717
- moveTo : function(/*Date*/ date) {
718
- edcal.isMoving = true;
719
- jQuery("#cal").empty();
720
-
721
- /*
722
- When we first start up our working date is 4 weeks before
723
- the next Sunday.
724
- */
725
- edcal._wDate = date.next().sunday().add(-28).days();
726
-
727
- /*
728
- After we remove and readd all the rows we are back to
729
- moving in a going down direction.
730
- */
731
- edcal.currentDirection = true;
732
-
733
- for (var i = 0; i < 10; i++) {
734
- edcal.createRow(jQuery("#cal"), true);
735
- edcal._wDate.add(7).days();
736
- }
737
-
738
- edcal.alignCal();
739
-
740
- var api = jQuery(".edcal_scrollable").scrollable();
741
- api.move(2);
742
-
743
- edcal.setDateLabel();
744
- edcal.setClassforToday();
745
- edcal.isMoving = false;
746
-
747
- },
748
-
749
- /*
750
- * Initializes the calendar
751
- */
752
- init : function() {
753
- if (jQuery("#edcal_scrollable").length === 0) {
754
- /*
755
- * This means we are on a page without the editorial
756
- * calendar
757
- */
758
- return;
759
- }
760
-
761
- jQuery("#loading").hide();
762
-
763
- jQuery("#edcal_scrollable").css("height", edcal.getCalHeight() + "px");
764
- edcal.windowHeight = jQuery(window).height();
765
-
766
- /*
767
- * Add the days of the week
768
- */
769
- edcal.createDaysHeader();
770
-
771
- // initialize scrollable
772
- jQuery(".edcal_scrollable").scrollable({
773
- vertical:true,
774
- size: 5,
775
- keyboardSteps: 1,
776
- speed: 100
777
- // use mousewheel plugin
778
- }).mousewheel();
779
-
780
- var api = jQuery(".edcal_scrollable").scrollable();
781
-
782
- edcal.moveTo(Date.today());
783
-
784
- /*
785
- * The scrollable handles some basic binding. This gets us
786
- * up arrow, down arrow and the mouse wheel.
787
- */
788
- api.onBeforeSeek(function(evt, direction) {
789
- // inside callbacks the "this" variable is a reference to the API
790
- if (!edcal.isMoving) {
791
- edcal.move(1, direction);
792
- }
793
-
794
- return false;
795
- });
796
-
797
- /*
798
- * We also want to listen for a few other key events
799
- */
800
- jQuery(document).bind("keydown", function(evt) {
801
- //if (evt.altKey || evt.ctrlKey) { return; }
802
- //output("evt.altKey: " + evt.altKey);
803
- //output("evt.keyCode: " + evt.keyCode);
804
- //output("evt.ctrlKey: " + evt.ctrlKey);
805
-
806
- if ((evt.keyCode === 34 && !(evt.altKey || evt.ctrlKey)) || //page down
807
- evt.keyCode === 40 && evt.ctrlKey){ // Ctrl+down down arrow
808
- edcal.move(4, true);
809
- return false;
810
- } else if ((evt.keyCode === 33 && !(evt.altKey || evt.ctrlKey)) || //page up
811
- evt.keyCode === 38 && evt.ctrlKey){ // Ctrl+up up arrow
812
- edcal.move(4, false);
813
- return false;
814
- } else if (evt.keyCode === 27) { //escape key
815
- edcal.closeTooltip();
816
- return false;
817
- }
818
- });
819
-
820
- edcal.getPosts(Date.today().next().sunday().add(-28).days(),
821
- Date.today().next().sunday().add(35).days());
822
-
823
- /*
824
- Now we bind the listeners for all of our links and the window
825
- resize.
826
- */
827
- jQuery("#moveToToday").click(function() {
828
- edcal.moveTo(Date.today());
829
- return false;
830
- });
831
-
832
- jQuery("#prevmonth").click(function() {
833
- edcal.move(4, false);
834
- return false;
835
- });
836
-
837
- jQuery("#nextmonth").click(function() {
838
- edcal.move(4, true);
839
- return false;
840
- });
841
-
842
- function resizeWindow(e) {
843
- if (edcal.windowHeight != jQuery(window).height()) {
844
- jQuery("#edcal_scrollable").css("height", edcal.getCalHeight() + "px");
845
- edcal.windowHeight = jQuery(window).height();
846
- }
847
- }
848
- jQuery(window).bind("resize", resizeWindow);
849
- },
850
-
851
- /*
852
- This function makes an AJAX call and changes the date of
853
- the specified post on the server.
854
- */
855
- changeDate: function(/*string*/ newdate, /*Post*/ post) {
856
- newdate = edcal.getDayFromDayId(newdate).toString("yyyy-MM-dd");
857
-
858
- var postStatus = "";
859
-
860
- if (post.status === "draft") {
861
- /*
862
- * If the status of the post was a draft we leave it as a draft
863
- */
864
- postStatus = "draft";
865
- } else {
866
- /*
867
- If the post status was published or future we need to adjust
868
- it. If you take a post that is published a move it after
869
- the current day we change the status to future. If the post
870
- was scheduled to get published in the future and they drag
871
- it into the past we change the status to publish.
872
- */
873
- var compare = Date.parse(newdate).compareTo(Date.today());
874
- if (compare === -1) {
875
- if (post.status === "publish") {
876
- postStatus = "publish";
877
- } else {
878
- postStatus = "draft";
879
- }
880
-
881
- } else {
882
- postStatus = "future";
883
- }
884
- }
885
-
886
- var url = edcal.ajax_url + "?action=edcal_changedate&postid=" + post.id +
887
- "&postStatus=" + postStatus +
888
- "&newdate=" + newdate + "&olddate=" + edcal.getDayFromDayId(post.date).toString("yyyy-MM-dd");
889
-
890
- jQuery("#post-" + post.id).addClass("loadingclass");
891
-
892
- jQuery.ajax( {
893
- url: url,
894
- type: "POST",
895
- processData: false,
896
- timeout: 100000,
897
- dataType: "json",
898
- success: function(res) {
899
- edcal.removePostItem(res.post.date, "post-" + res.post.id);
900
- edcal.addPostItem(res.post, res.post.date);
901
- edcal.addPostItemDragAndToolltip(res.post.date);
902
-
903
- if (res.error) {
904
- if (res.error === edcal.CONCURRENCY_ERROR) {
905
- edcal.showError("Someone else already moved " + res.post.title);
906
- } else if (res.error === edcal.PERMISSION_ERROR) {
907
- edcal.showError("You don't have permission to edit posts");
908
- }
909
- }
910
- },
911
- error: function(xhr) {
912
- showError("There was an error contacting your blog.");
913
- if (xhr.responseText) {
914
- output("xhr.responseText: " + xhr.responseText);
915
- }
916
- }
917
- });
918
-
919
- },
920
-
921
- /*
922
- * Shows an error message
923
- */
924
- showError: function(/*string*/ msg) {
925
- humanMsg.displayMsg(msg);
926
- },
927
-
928
- /*
929
- Makes an AJAX call to get the posts from the server within the
930
- specified dates.
931
- */
932
- getPosts: function(/*Date*/ from, /*Date*/ to) {
933
- //output("getPosts(" + from.toString("dd-MMM-yyyy") + ", " + to.toString("dd-MMM-yyyy") + ")");
934
-
935
- var shouldGet = edcal.cacheDates[from];
936
-
937
- if (shouldGet) {
938
- /*
939
- * TODO: We don't want to make extra AJAX calls for dates
940
- * that we have already coverred. This is cutting down on
941
- * it somewhat, but we could get much better about this.
942
- */
943
- output("We already have the results.");
944
- return;
945
- }
946
-
947
- edcal.cacheDates[from] = true;
948
-
949
-
950
- var url = edcal.ajax_url + "?action=edcal_posts&from=" + from.toString("yyyy-MM-dd") + "&to=" + to.toString("yyyy-MM-dd");
951
- output("url: " + url);
952
-
953
- jQuery("#loading").show();
954
-
955
- jQuery.ajax( {
956
- url: url,
957
- type: "GET",
958
- processData: false,
959
- timeout: 100000,
960
- dataType: "text",
961
- success: function(res) {
962
- jQuery("#loading").hide();
963
-
964
- /*
965
- * These result here can get pretty large on a busy blog and
966
- * the JSON parser from JSON.org works faster than the native
967
- * one used by JQuery.
968
- */
969
- var parsedRes = JSON.parseIt(res);
970
- var postDates = [];
971
- jQuery.each(parsedRes, function(i, post) {
972
- if (post) {
973
- edcal.removePostItem(post.date, "post-" + post.id);
974
- edcal.addPostItem(post, post.date);
975
- postDates[postDates.length] = post.date;
976
- }
977
- });
978
-
979
- /*
980
- * If the blog has a very larger number of posts then adding
981
- * them all can make the UI a little slow. Particularly IE
982
- * pops up a warning giving the user a chance to abort the
983
- * script. Adding tooltips and making the items draggable is
984
- * a lot of what makes things slow. Delaying those two operations
985
- * makes the UI show up much faster and the user has to wait
986
- * three seconds before they can drag. It also makes IE
987
- * stop complaining.
988
- */
989
- setTimeout(function() {
990
- output("adding tooltips and draggables to " + postDates.length + " items.");
991
- jQuery.each(postDates, function(i, postDate) {
992
- edcal.addPostItemDragAndToolltip(postDate);
993
- });
994
- }, 300);
995
- },
996
- error: function(xhr) {
997
- edcal.showError("There was an error contacting your blog.");
998
- if (xhr.responseText) {
999
- output("xhr.responseText: " + xhr.responseText);
1000
- }
1001
- }
1002
- });
1003
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1004
  };
1005
 
1006
  jQuery(document).ready(function(){
1007
- edcal.init();
1008
  });
1009
 
1010
  /*
@@ -1012,9 +1087,9 @@ jQuery(document).ready(function(){
1012
  */
1013
  jQuery.extend({URLEncode:function(c){var o='';var x=0;c=c.toString();var r=/(^[a-zA-Z0-9_.]*)/;
1014
  while(x<c.length){var m=r.exec(c.substr(x));
1015
- if(m!==null && m.length>1 && m[1]!=''){o+=m[1];x+=m[1].length;
1016
- }else{if(c[x]===' '){o+='+';}else{var d=c.charCodeAt(x);var h=d.toString(16);
1017
- o+='%'+(h.length<2?'0':'')+h.toUpperCase();}x++;}}return o;},
1018
  URLDecode:function(s){var o=s;var binVal,t;var r=/(%[^%]{2})/;
1019
  while((m=r.exec(o))!==null && m.length>1 && m[1]!==''){b=parseInt(m[1].substr(1),16);
1020
  t=String.fromCharCode(b);o=o.replace(m[1],t);}return o;}
21
  * @param msg the message to write
22
  */
23
  function output(msg) {
24
+ //console.info(msg);
25
  }
26
 
27
  /*
35
  The calendar shows five weeks visible at a time and maintains 11
36
  weeks of rendered HTML. Only the middle weeks are visible.
37
 
38
+ Week 1
39
+ Week 2
40
+ Week 3
41
+ - Week 4 -
42
+ | Week 5 |
43
+ | Week 6 |
44
+ | Week 7 |
45
+ - Week 8 -
46
+ Week 9
47
+ Week 10
48
+ Week 11
49
 
50
  When the user scrolls down one week the new week is added at the
51
  end of the calendar and the first week is removed. In this way
58
  The HTML structure of the calendar is:
59
 
60
  <div id="cal">
61
+ <div id="row08Nov2009">
62
+ <div id="row08Nov2009row">
63
+ <div class="day sunday nov" id="08Nov2009">
64
+ <div class="dayobj">
65
+ <div class="daylabel">8</div>
66
+ <ul class="postlist">
67
+ </ul>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
  </div>
73
  */
74
  var edcal = {
75
+ /*
76
+ * True if the calendar is in the process of moving
77
+ */
78
+ isMoving: false,
79
+
80
+ /*
81
+ This is the base URL we use to make AJAX calls back
82
+ to the server. This value is set in code generated
83
+ from edcal.php that run in a script tag in the main
84
+ page.
85
+ */
86
+ ajax_url: '',
87
+
88
+ /*
89
+ * The cache of dates we have already loaded posts for.
90
+ */
91
+ cacheDates : [],
92
+
93
+ /*
94
+ * The ID of the timer we use to batch new post requests
95
+ */
96
+ tID: null,
97
+
98
+ /*
99
+ * The number of steps moving for this timer.
100
+ */
101
+ steps: 0,
102
+
103
+ /*
104
+ * The constant for the concurrency error.
105
+ */
106
+ CONCURRENCY_ERROR: 4,
107
+
108
+ /*
109
+ * The constant for the user permission error
110
+ */
111
+ PERMISSION_ERROR: 5,
112
+
113
+ /*
114
+ The direction the calendar last moved.
115
+ true = down = to the future
116
+ false = up = to the past
117
+
118
+ */
119
+ currentDirection: true,
120
+
121
+ /*
122
+ This date is our index. When the calendar moves we
123
+ update this date to indicate the next rows we need
124
+ to add.
125
+ */
126
+ _wDate: Date.today(),
127
+
128
+ /*
129
+ * The date since the previous move
130
+ */
131
+ moveDate: null,
132
+
133
+ /*
134
+ A cache of all the posts we have loaded so far. The
135
+ data structure is:
136
 
137
+ posts [date - ddMMMyyyy][posts array - post object from JSON data]
138
+ */
139
+ posts: new Array(50),
140
+
141
+ /*
142
+ IE will sometimes fire the resize event twice for the same resize
143
+ action. We save it so we only resize the calendar once and avoid
144
+ any flickering.
145
+ */
146
+ windowHeight: 0,
147
+
148
+ /*
149
+ This function aligns the grid in two directions. There
150
+ is a vertical grid with a row of each week and a horizontal
151
+ grid for each week with a list of days.
152
+ */
153
+ alignGrid: function(/*string*/ gridid, /*int*/ cols, /*int*/ cellWidth, /*int*/ cellHeight, /*int*/ padding) {
154
+ var x = 0;
155
+ var y = 0;
156
+ var count = 1;
157
+
158
+ jQuery(gridid).each(function(){
159
+ jQuery(this).css("position", "relative");
160
+
161
+ jQuery(this).children("div").each(function(){
162
+ jQuery(this).css({
163
+ width: cellWidth + "%",
164
+ height: cellHeight + "%",
165
+ position: "absolute",
166
+ left: x + "%",
167
+ top: y + "%"
168
+ });
169
+
170
+ if ((count % cols) === 0) {
171
+ x = 0;
172
+ y += cellHeight + padding;
173
+ } else {
174
+ x += cellWidth + padding;
175
+ }
176
+
177
+ count++;
178
+ });
179
+ });
180
+ },
181
+
182
+ /*
183
+ This is a helper function to align the calendar so we don't
184
+ have to change the cell sizes in multiple places.
185
+ */
186
+ alignCal: function() {
187
+ edcal.alignGrid("#cal", 1, 100, 20, 0.25);
188
+ },
189
+
190
+
191
+ /*
192
+ This function creates the days header at the top of the
193
+ calendar.
194
 
195
+ TODO: We should localize these values
196
+ */
197
+ createDaysHeader: function() {
198
+ var html = '<div class="dayhead">Sunday</div>';
199
+ html += '<div class="dayhead">Monday</div>';
200
+ html += '<div class="dayhead">Tuesday</div>';
201
+ html += '<div class="dayhead">Wednesday</div>';
202
+ html += '<div class="dayhead">Thursday</div>';
203
+ html += '<div class="dayhead">Friday</div>';
204
+ html += '<div class="dayhead">Saturday</div>';
205
+
206
+ jQuery("#rowhead").append(html);
207
+
208
+ edcal.alignGrid("#rowhead", 7, 14.2, 100, 0.25);
209
+ },
210
+
211
+ /*
212
+ Creates a row of the calendar and adds all of the CSS classes
213
+ and listeners for each calendar day.
214
+ */
215
+ createRow: function(/*jQuery*/ parent, /*bool*/ append) {
216
+ var _date = edcal._wDate.clone();
217
+
218
+ var newrow = '<div class="rowcont" id="' + 'row' + edcal._wDate.toString("ddMMMyyyy") + '">' +
219
+ '<div id="' + 'row' + edcal._wDate.toString("ddMMMyyyy") + 'row" class="row">';
220
+ for (var i = 0; i < 7; i++) {
221
+ newrow += '<div id="' + _date.toString("ddMMMyyyy") + '" class="day ' +
222
+ _date.toString("dddd").toLowerCase() + ' ' +
223
+ _date.toString("MMM").toLowerCase() + '">';
224
+
225
+ newrow += '<div class="dayobj">';
226
+
227
+ newrow += '<div class="daylabel">';
228
+ newrow += '<a href="#" class="daynewlink" title="Add new post on ' + _date.toString("MMMM d") + '" ' +
229
+ 'onclick="return false;">+</a>';
230
+ if (_date.toString("dd") == "01") {
231
+ newrow += _date.toString("MMM d");
232
+ } else {
233
+ newrow += _date.toString("d");
234
+ }
235
+ newrow += '</div>';
236
+
237
+ newrow += '<ul class="postlist">';
238
+
239
+ newrow += edcal.getPostItems(_date.toString("ddMMMyyyy"));
240
+
241
+ newrow += '</ul>';
242
+
243
+ newrow += '</div>';
244
+ newrow += '</div>';
245
+ _date.add(1).days();
246
+ }
247
+
248
+ newrow += '</div></div';
249
+
250
+ if (append) {
251
+ parent.append(newrow);
252
+
253
+ } else {
254
+ parent.prepend(newrow);
255
+ }
256
+
257
+ edcal.alignGrid("#row" + edcal._wDate.toString("ddMMMyyyy") + "row", 7, 14.2, 100, 0.25);
258
+
259
+ jQuery('#row' + edcal._wDate.toString("ddMMMyyyy") + ' .day').each( function() {
260
+ edcal.addTooltip(jQuery(this).attr("id"));
261
+ });
262
+
263
+ edcal.draggablePost('#row' + edcal._wDate.toString("ddMMMyyyy") + ' .post');
264
+
265
+ jQuery('#row' + edcal._wDate.toString("ddMMMyyyy") + ' .day').droppable({
266
+ hoverClass: 'day-active',
267
+ accept: '.post',
268
+ greedy: true,
269
+ tolerance: 'pointer',
270
+ drop: function(event, ui) {
271
+ //output('dropped ui.draggable.attr("id"): ' + ui.draggable.attr("id"));
272
+ //output('dropped on jQuery(this).attr("id"): ' + jQuery(this).attr("id"));
273
+ //output('ui.draggable.html(): ' + ui.draggable.html());
274
+
275
+ var dayId = ui.draggable.parent().parent().parent().attr("id");
276
+
277
+ // Step 0. Get the post object from the map
278
+ var post = edcal.findPostForId(ui.draggable.parent().parent().parent().attr("id"),
279
+ ui.draggable.attr("id"));
280
+
281
+ // Step 1. Remove the post from the posts map
282
+ edcal.removePostFromMap(ui.draggable.parent().parent().parent().attr("id"),
283
+ ui.draggable.attr("id"));
284
+
285
+ // Step 2. Remove the old element from the old parent.
286
+ jQuery('#' + ui.draggable.attr("id")).remove();
287
+
288
+ // Step 3. Add the item to the new DOM parent
289
+ jQuery('#' + jQuery(this).attr("id") + ' .postlist').append(edcal.createPostItem(post,
290
+ jQuery(this).attr("id")));
291
+
292
+ // Step 4. And add the tooltip
293
+ edcal.addTooltip(jQuery(this).attr("id"));
294
+
295
+ if (dayId == jQuery(this).attr("id")) {
296
+ /*
297
+ If they dropped back on to the day they started with we
298
+ don't want to go back to the server.
299
+ */
300
+ edcal.draggablePost('#' + jQuery(this).attr("id") + ' .post');
301
+ } else {
302
+ // Step6. Update the date on the server
303
+ edcal.changeDate(jQuery(this).attr("id"), post);
304
+ }
305
+ }
306
+ });
307
+
308
+ return jQuery('row' + edcal._wDate.toString("ddMMMyyyy"));
309
+ },
310
+
311
+ /*
312
+ * This is a helper method to make an individual post item draggable.
313
+ */
314
+ draggablePost: function(/*post selector*/ post) {
315
+ /*
316
+ * Click is a different operation than drag in our UI. The problem is if
317
+ * the user is moving their mouse just a little bit we can think it is a
318
+ * drag instead of a click. We stop this by delaying the drag event until
319
+ * the user has dragged at least 10 pixels. This works fine on IE and
320
+ * Firefox, but on Chrome it causes text selection so we don't delay on
321
+ * Chrome.
322
+ */
323
+ var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
324
+ var is_safari = navigator.userAgent.toLowerCase().indexOf('safari') > -1;
325
+
326
+ if (is_chrome || is_safari) {
327
+ jQuery(post).draggable({
328
+ revert: 'invalid',
329
+ appendTo: 'body',
330
+ helper: "clone",
331
+ addClasses: false
332
+ });
333
+ } else {
334
+ jQuery(post).draggable({
335
+ revert: 'invalid',
336
+ appendTo: 'body',
337
+ distance: 10,
338
+ helper: "clone",
339
+ addClasses: false
340
+ });
341
+ }
342
+ },
343
+
344
+ /*
345
+ This is a utility method to find a post and remove it
346
+ from the cache map.
347
+ */
348
+ removePostFromMap: function(/*string*/ dayobjId, /*string*/ postId) {
349
+ if (edcal.posts[dayobjId]) {
350
+ for (var i = 0; i < edcal.posts[dayobjId].length; i++) {
351
+ if (edcal.posts[dayobjId][i] &&
352
+ "post-" + edcal.posts[dayobjId][i].id === postId) {
353
+ edcal.posts[dayobjId][i] = null;
354
+ return true;
355
+ }
356
+ }
357
+ }
358
+
359
+ return false;
360
+ },
361
+
362
+ /*
363
+ * Adds a post to al already existing calendar day.
364
+ */
365
+ addPostItem: function(/*post*/ post, /*string*/ dayobjId) {
366
+ jQuery('#' + dayobjId + ' .postlist').append(edcal.createPostItem(post, dayobjId));
367
+ },
368
+
369
+ /*
370
+ Makes all the posts in the specified day draggable
371
+ and adds the tooltip.
372
+ */
373
+ addPostItemDragAndToolltip: function(/*string*/ dayobjId) {
374
+ edcal.draggablePost('#' + dayobjId + ' .post');
375
+ edcal.addTooltip(dayobjId);
376
+ },
377
+
378
+ /*
379
+ * Confirms if you want to delete the specified post
380
+ */
381
+ confirmDelete: function(/*string*/ posttitle) {
382
+ if (confirm('You are about to delete this post ' + posttitle + '.\n\n Press cancel to stop, OK to delete.')) {
383
+ return true;
384
+ } else {
385
+ return false;
386
+ }
387
+ },
388
+
389
+ /*
390
+ This is an AJAX function to save the past title when
391
+ the user presses the save button on the tooltip.
392
+ */
393
+ saveTitle: function(/*string*/ postId) {
394
+
395
+ var url = edcal.ajax_url + "?action=edcal_changetitle&postid=" + postId +
396
+ "&title=" + jQuery.URLEncode(jQuery("#edcal-title-edit-field").val());
397
+
398
+ jQuery("#post-" + postId).addClass("loadingclass");
399
+
400
+ jQuery("#tooltip").hide();
401
+
402
+ jQuery.ajax( {
403
+ url: url,
404
+ type: "POST",
405
+ processData: false,
406
+ timeout: 100000,
407
+ dataType: "json",
408
+ success: function(res) {
409
+ edcal.removePostItem(res.post.date, "post-" + res.post.id);
410
+ edcal.addPostItem(res.post, res.post.date);
411
+ edcal.addPostItemDragAndToolltip(res.post.date);
412
+ },
413
+ error: function(xhr) {
414
+ edcal.showError("There was an error contacting your blog.");
415
+ if (xhr.responseText) {
416
+ output("xhr.responseText: " + xhr.responseText);
417
+ }
418
+ }
419
+ });
420
+ },
421
+
422
+ /*
423
+ * Cancels the edit title action in the tooltip.
424
+ */
425
+ cancelEditTitle: function(/*string*/ postTitle) {
426
+ jQuery("#edcal-title-edit-field").val(postTitle);
427
+
428
+ jQuery("#edcal-title-box").hide();
429
+ jQuery("#edcal-title").show();
430
+
431
+ },
432
+
433
+ /*
434
+ * Shows the edit title UI in the tooltip.
435
+ */
436
+ editTitle: function() {
437
+ jQuery("#edcal-title").hide();
438
+ jQuery("#edcal-title-box").show();
439
+
440
+ jQuery("#edcal-title-edit-field").focus();
441
+ jQuery("#edcal-title-edit-field").select();
442
+ },
443
+
444
+ /*
445
+ Switches back to the normal tooltip title view
446
+ and closes the tooltip.
447
+ */
448
+ closeTooltip: function() {
449
+ edcal.cancelEditTitle();
450
+ jQuery("#tooltip").hide();
451
+ },
452
+
453
+ /*
454
+ * Adds a tooltip to every post in the specified day.
455
+ */
456
+ addTooltip: function(/*string*/ dayobjId) {
457
+ jQuery('#' + dayobjId + ' .post').tooltip({
458
+ delay: 1500,
459
+ bodyHandler: function() {
460
+ var post = edcal.findPostForId(dayobjId, jQuery(this).attr("id"));
461
+
462
+ var posttitle = post.title;
463
+
464
+ if (posttitle === "") {
465
+ posttitle = "[No Title]";
466
+ }
467
+
468
+ var tooltip = '<div class="tooltip">' +
469
+ '<a href="#" id="tipclose" onclick="edcal.closeTooltip(); return false;" title="close"> </a>' +
470
+ '<h3 id="edcal-title">' + posttitle +
471
+ ' <a href="#" onclick="edcal.editTitle(); return false;" class="edit-post-status" id="edcal-title-edit">Edit</a>' +
472
+ '</h3>' +
473
+ '<div id="edcal-title-box">' +
474
+ '<input type="text" value="' + post.title + '" id="edcal-title-edit-field"/> &nbsp;&nbsp;' +
475
+ '<span id="edit-slug-buttons">' +
476
+ '<a class="save button" href="#" onclick="edcal.saveTitle(\'' + post.id + '\'); return false;">Save</a> ' +
477
+ '<a href="#" onclick="edcal.cancelEditTitle(\'' + post.title + '\'); return false;" class="cancel">Cancel</a></span>' +
478
+ '</div>' +
479
+ '<p>' +
480
+ '<i>by</i> ' + post.author + ' <i>on</i> ' +
481
+ edcal.getDayFromDayId(post.date).toString("MMMM d, yyyy") + ' at ' +
482
+ post.time +
483
+ '</p>' +
484
+ '<p>' +
485
+ 'Status<b>: ' + post.status + '</b>' +
486
+ '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
487
+ if (post.editlink) {
488
+ /*
489
+ * If the user doesn't have permission to edit a post then
490
+ * then server won't send the edit link URL and we shouldn't
491
+ * show the edit link
492
+ */
493
+ tooltip += '<a href="' + post.editlink + '" title="Edit ' + post.title +
494
+ '">Edit</a>&nbsp; | &nbsp;';
495
+ }
496
+
497
+ if (post.dellink) {
498
+ tooltip += '<a class="submitdelete" href="' + post.dellink + '" ' +
499
+ 'onclick="return edcal.confirmDelete(\'' + post.title + '\');"' +
500
+ 'title="Delete ' + post.title + '">Delete</a> &nbsp; | &nbsp;';
501
+ }
502
+
503
+ tooltip += '<a href="' + post.permalink + '" title="View ' + post.title + '">View</a>' +
504
+ '</p>' +
505
+ '</div>';
506
+
507
+ return tooltip;
508
+ }
509
+ });
510
+ },
511
+
512
+ /*
513
+ Creates the HTML for a post item and adds the data for
514
+ the post to the posts cache.
515
+ */
516
+ createPostItem: function(/*post*/ post, /*string*/ dayobjId) {
517
+ var postHtml = edcal.getPostItemString(post);
518
+ if (!edcal.posts[dayobjId]) {
519
+ edcal.posts[dayobjId] = new Array(0);
520
+ }
521
+
522
+ edcal.posts[dayobjId][edcal.posts[dayobjId].length] = post;
523
+
524
+ return postHtml;
525
+ },
526
+
527
+ /*
528
+ Finds the post object for the specified post ID in the
529
+ specified day.
530
+ */
531
+ findPostForId: function(/*string*/ dayobjId, /*string*/ postId) {
532
+ if (edcal.posts[dayobjId]) {
533
+ for (var i = 0; i < edcal.posts[dayobjId].length; i++) {
534
+ if (edcal.posts[dayobjId][i] &&
535
+ "post-" + edcal.posts[dayobjId][i].id === postId) {
536
+ return edcal.posts[dayobjId][i];
537
+ }
538
+ }
539
+ }
540
+ },
541
+
542
+ /*
543
+ * Removes a post from the HTML and the posts cache.
544
+ */
545
+ removePostItem: function(/*string*/ dayobjId, /*string*/ postId) {
546
+ if (edcal.findPostForId(dayobjId, postId)) {
547
+ for (var i = 0; i < edcal.posts[dayobjId].length; i++) {
548
+ if (edcal.posts[dayobjId][i] &&
549
+ "post-" + edcal.posts[dayobjId][i].id === postId) {
550
+ edcal.posts[dayobjId][i] = null;
551
+ jQuery("#" + postId).remove();
552
+ }
553
+ }
554
+
555
+ }
556
+ },
557
+
558
+ /*
559
+ Gets all the post items for the specified day from
560
+ the post cache.
561
+ */
562
+ getPostItems: function(/*string*/ dayobjId) {
563
+ var postsString = "";
564
+
565
+ if (edcal.posts[dayobjId]) {
566
+ for (var i = 0; i < edcal.posts[dayobjId].length; i++) {
567
+ if (edcal.posts[dayobjId][i]) {
568
+ postsString += edcal.getPostItemString(edcal.posts[dayobjId][i]);
569
+ }
570
+ }
571
+ }
572
+
573
+ return postsString;
574
+ },
575
+
576
+ /*
577
+ * Gets the HTML string for a post.
578
+ */
579
+ getPostItemString: function(/*post*/ post) {
580
+ var posttitle = post.title;
581
+
582
+ if (posttitle === "") {
583
+ posttitle = "[No Title]";
584
+ }
585
+ return '<li id="post-' + post.id + '" class="post ' + post.status + '">' + posttitle + '</li>';
586
+ },
587
+
588
+ /*
589
+ Finds the calendar cell for the current day and adds the
590
+ class "today" to that cell.
591
+ */
592
+ setClassforToday: function() {
593
+ /*
594
+ We want to set a class for the cell that represents the current day so we ca
595
+ give it a background color.
596
+ */
597
+ jQuery('#' + Date.today().toString("ddMMMyyyy")).addClass("today");
598
+ },
599
+
600
+ /*
601
+ Most browsers need us to set a calendar height in pixels instead
602
+ of percent. This function get the correct pixel height for the
603
+ calendar based on the window height.
604
+ */
605
+ getCalHeight: function() {
606
+ var myHeight = 0;
607
+ if ( typeof( window.innerWidth ) == 'number' ) {
608
+ //Non-IE
609
+ myHeight = window.innerHeight;
610
+ } else if ( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
611
+ //IE 6+ in 'standards compliant mode'
612
+ myHeight = document.documentElement.clientHeight;
613
+ } else if ( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
614
+ //IE 4 compatible
615
+ myHeight = document.body.clientHeight;
616
+ }
617
+ return myHeight - 150;
618
+ },
619
+
620
+ /*
621
+ Moves the calendar a certain number of steps in the specified direction.
622
+ True moves the calendar down into the future and false moves the calendar
623
+ up into the past.
624
+ */
625
+ move: function(steps, direction) {
626
+ /*
627
+ The working date is a marker for the last calendar row we created.
628
+ If we are moving forward that will be the last row, if we are moving
629
+ backward it will be the first row. If we switch direction we need
630
+ to bump up our date by 11 rows times 7 days a week or 77 days.
631
+ */
632
+ if (edcal.currentDirection != direction) {
633
+ if (direction) { // into the future
634
+ edcal._wDate = edcal._wDate.add(77).days();
635
+ } else { // into the past
636
+ edcal._wDate = edcal._wDate.add(-77).days();
637
+ }
638
+
639
+ edcal.steps = 0;
640
+ edcal.moveDate = edcal._wDate;
641
+ }
642
+
643
+ edcal.currentDirection = direction;
644
+
645
+ var i;
646
+
647
+
648
+ if (direction) {
649
+ for (i = 0; i < steps; i++) {
650
+ jQuery("#cal > div:first").remove();
651
+ edcal.createRow(jQuery("#cal"), true);
652
+ edcal._wDate.add(7).days();
653
+ }
654
+ edcal.alignCal();
655
+ } else {
656
+ for (i = 0; i < steps; i++) {
657
+ jQuery("#cal > div:last").remove();
658
+ edcal.createRow(jQuery("#cal"), false);
659
+ edcal._wDate.add(-7).days();
660
+ }
661
+ edcal.alignCal();
662
+ }
663
+
664
+ edcal.setClassforToday();
665
+ edcal.setDateLabel();
666
+
667
+ /*
668
+ * If the user clicks quickly or uses the mouse wheel they can
669
+ * get a lot of move events very quickly and we need to batch
670
+ * them up together. We set a timeout and clear it if there is
671
+ * another move before the timeout happens.
672
+ */
673
+ edcal.steps += steps;
674
+ if (edcal.tID) {
675
+ clearTimeout(edcal.tID);
676
+ } else {
677
+ edcal.moveDate = edcal._wDate;
678
+ }
679
+
680
+ edcal.tID = setTimeout(function() {
681
+ /*
682
+ * Now that we are done moving the calendar we need to get the posts for the
683
+ * new dates. We want to load the posts between the place the calendar was
684
+ * at when the user started moving it and the place the calendar is at now.
685
+ */
686
+ if (!direction) {
687
+ edcal.getPosts(edcal._wDate.clone(),
688
+ edcal._wDate.clone().add(7 * (edcal.steps + 1)).days());
689
+ } else {
690
+ edcal.getPosts(edcal._wDate.clone().add(-7 * (edcal.steps + 1)).days(),
691
+ edcal._wDate.clone());
692
+ }
693
+
694
+ edcal.steps = 0;
695
+ edcal.tID = null;
696
+ edcal.moveDate = edcal._wDate;
697
+ }, 1000);
698
+ },
699
+
700
+ /*
701
+ We use the date as the ID for day elements, but the Date
702
+ library can't parse the date without spaces and using
703
+ spaces in IDs can cause problems. We work around the
704
+ issue by adding the spaces back before we parse.
705
+ */
706
+ getDayFromDayId: function(/*dayId*/ day) {
707
+ return Date.parse(day.substring(0, 2) + ' ' + day.substring(2, 5) + ' ' + day.substring(5));
708
+ },
709
+
710
+ /*
711
+ This is a helper method to set the date label on the top of
712
+ the calendar. It looks like November 2009-December2009
713
+ */
714
+ setDateLabel: function(year) {
715
+ var api = jQuery(".edcal_scrollable").scrollable();
716
+ var items = api.getVisibleItems();
717
+
718
+ /*
719
+ We need to get the first day in the first week and the
720
+ last day in the last week. We call children twice to
721
+ work around a small JQuery issue.
722
+ */
723
+ var firstDate = edcal.getDayFromDayId(items.eq(0).children(".row").children(".day:first").attr("id"));
724
+ var lastDate = edcal.getDayFromDayId(items.eq(items.length - 1).children(".row").children(".day:last").attr("id"));
725
+
726
+ jQuery("#currentRange").text(firstDate.toString("MMMM yyyy") + " - " + lastDate.toString("MMMM yyyy"));
727
+ },
728
+
729
+ /*
730
+ * Moves the calendar to the specified date.
731
+ */
732
+ moveTo : function(/*Date*/ date) {
733
+ edcal.isMoving = true;
734
+ jQuery("#cal").empty();
735
+
736
+ /*
737
+ When we first start up our working date is 4 weeks before
738
+ the next Sunday.
739
+ */
740
+ edcal._wDate = date.next().sunday().add(-28).days();
741
+
742
+ /*
743
+ After we remove and readd all the rows we are back to
744
+ moving in a going down direction.
745
+ */
746
+ edcal.currentDirection = true;
747
+
748
+ for (var i = 0; i < 10; i++) {
749
+ edcal.createRow(jQuery("#cal"), true);
750
+ edcal._wDate.add(7).days();
751
+ }
752
+
753
+ edcal.alignCal();
754
+
755
+ var api = jQuery(".edcal_scrollable").scrollable();
756
+ api.move(2);
757
+
758
+ edcal.setDateLabel();
759
+ edcal.setClassforToday();
760
+ edcal.isMoving = false;
761
+
762
+ },
763
+
764
+ /*
765
+ * Initializes the calendar
766
+ */
767
+ init : function() {
768
+ if (jQuery("#edcal_scrollable").length === 0) {
769
+ /*
770
+ * This means we are on a page without the editorial
771
+ * calendar
772
+ */
773
+ return;
774
+ }
775
+
776
+ jQuery("#loading").hide();
777
+
778
+ jQuery("#edcal_scrollable").css("height", edcal.getCalHeight() + "px");
779
+ edcal.windowHeight = jQuery(window).height();
780
+
781
+ /*
782
+ * Add the days of the week
783
+ */
784
+ edcal.createDaysHeader();
785
+
786
+ // initialize scrollable
787
+ jQuery(".edcal_scrollable").scrollable({
788
+ vertical:true,
789
+ size: 5,
790
+ keyboardSteps: 1,
791
+ speed: 100
792
+ // use mousewheel plugin
793
+ }).mousewheel();
794
+
795
+ var api = jQuery(".edcal_scrollable").scrollable();
796
+
797
+ edcal.moveTo(Date.today());
798
+
799
+ /*
800
+ * The scrollable handles some basic binding. This gets us
801
+ * up arrow, down arrow and the mouse wheel.
802
+ */
803
+ api.onBeforeSeek(function(evt, direction) {
804
+ // inside callbacks the "this" variable is a reference to the API
805
+ if (!edcal.isMoving) {
806
+ edcal.move(1, direction);
807
+ }
808
+
809
+ return false;
810
+ });
811
+
812
+ /*
813
+ * We also want to listen for a few other key events
814
+ */
815
+ jQuery(document).bind("keydown", function(evt) {
816
+ //if (evt.altKey || evt.ctrlKey) { return; }
817
+ //output("evt.altKey: " + evt.altKey);
818
+ //output("evt.keyCode: " + evt.keyCode);
819
+ //output("evt.ctrlKey: " + evt.ctrlKey);
820
+
821
+ if ((evt.keyCode === 34 && !(evt.altKey || evt.ctrlKey)) || //page down
822
+ evt.keyCode === 40 && evt.ctrlKey){ // Ctrl+down down arrow
823
+ edcal.move(4, true);
824
+ return false;
825
+ } else if ((evt.keyCode === 33 && !(evt.altKey || evt.ctrlKey)) || //page up
826
+ evt.keyCode === 38 && evt.ctrlKey){ // Ctrl+up up arrow
827
+ edcal.move(4, false);
828
+ return false;
829
+ } else if (evt.keyCode === 27) { //escape key
830
+ edcal.closeTooltip();
831
+ return false;
832
+ }
833
+ });
834
+
835
+ edcal.getPosts(Date.today().next().sunday().add(-28).days(),
836
+ Date.today().next().sunday().add(35).days());
837
+
838
+ /*
839
+ Now we bind the listeners for all of our links and the window
840
+ resize.
841
+ */
842
+ jQuery("#moveToToday").click(function() {
843
+ edcal.moveTo(Date.today());
844
+ return false;
845
+ });
846
+
847
+ jQuery("#prevmonth").click(function() {
848
+ edcal.move(4, false);
849
+ return false;
850
+ });
851
+
852
+ jQuery("#nextmonth").click(function() {
853
+ edcal.move(4, true);
854
+ return false;
855
+ });
856
+
857
+ function resizeWindow(e) {
858
+ if (edcal.windowHeight != jQuery(window).height()) {
859
+ jQuery("#edcal_scrollable").css("height", edcal.getCalHeight() + "px");
860
+ edcal.windowHeight = jQuery(window).height();
861
+ }
862
+ }
863
+ jQuery(window).bind("resize", resizeWindow);
864
+
865
+ jQuery(".day").live("mouseover", function(evt) {
866
+ jQuery("#" + jQuery(this).attr("id") + " .daynewlink").show();
867
+ });
868
+
869
+ jQuery(".day").live("mouseout", function(evt) {
870
+ jQuery("#" + jQuery(this).attr("id") + " .daynewlink").hide();
871
+ });
872
+
873
+ jQuery(".daynewlink").live("click", function(evt) {
874
+ edcal.createEmptyDraft(jQuery(this).parent().parent().parent().attr("id"));
875
+ });
876
+ },
877
+
878
+ /*
879
+ * This function responds to the new post link on each calendar day.
880
+ * The function is kind of funny. What I would really like to do is
881
+ * redirect the user to the edit page with a parameter on the URL to
882
+ * set a default post date, but WordPress doesn't support that. Instead
883
+ * I create a new empty draft post on that day and take them to edit
884
+ * that post.
885
+ */
886
+ createEmptyDraft: function(/*string*/ date) {
887
+ /*
888
+ * We don't really let them set a time in the calendar, so we
889
+ * put a default post time of 10:00 AM.
890
+ */
891
+ var formattedDate = jQuery.URLEncode(edcal.getDayFromDayId(date).toString("yyyy-MM-dd") + " 10:00:00");
892
+ var url = edcal.ajax_url + "?action=edcal_newdraft&date=" + formattedDate;
893
+
894
+ jQuery.ajax( {
895
+ url: url,
896
+ type: "POST",
897
+ processData: false,
898
+ timeout: 100000,
899
+ dataType: "json",
900
+ success: function(res) {
901
+ if (res.editlink) {
902
+ window.location = res.editlink.replace("&amp;", "&");
903
+ } else {
904
+ edcal.showError("There was an error creating a new post for your blog.");
905
+ }
906
+ },
907
+ error: function(xhr) {
908
+ edcal.showError("There was an error contacting your blog.");
909
+ if (xhr.responseText) {
910
+ output("xhr.responseText: " + xhr.responseText);
911
+ }
912
+ }
913
+ });
914
+ },
915
+
916
+ /*
917
+ This function makes an AJAX call and changes the date of
918
+ the specified post on the server.
919
+ */
920
+ changeDate: function(/*string*/ newdate, /*Post*/ post) {
921
+ var newdateFormatted = edcal.getDayFromDayId(newdate).toString("yyyy-MM-dd");
922
+
923
+ var postStatus = "";
924
+
925
+ if (post.status === "draft") {
926
+ /*
927
+ * If the status of the post was a draft we leave it as a draft
928
+ */
929
+ postStatus = "draft";
930
+ } else if (post.status === "pending") {
931
+ /*
932
+ * If the status of the post was a pending we leave it as a draft
933
+ */
934
+ postStatus = "pending";
935
+ } else {
936
+ /*
937
+ If the post status was published or future we need to adjust
938
+ it. If you take a post that is published a move it after
939
+ the current day we change the status to future. If the post
940
+ was scheduled to get published in the future and they drag
941
+ it into the past we change the status to publish.
942
+ */
943
+ var compare = Date.parse(newdateFormatted).compareTo(Date.today());
944
+ if (compare === -1) {
945
+ if (post.status === "publish") {
946
+ postStatus = "publish";
947
+ } else {
948
+ postStatus = "draft";
949
+ }
950
+
951
+ } else {
952
+ postStatus = "future";
953
+ }
954
+ }
955
+
956
+ var url = edcal.ajax_url + "?action=edcal_changedate&postid=" + post.id +
957
+ "&postStatus=" + postStatus +
958
+ "&newdate=" + newdateFormatted + "&olddate=" + edcal.getDayFromDayId(post.date).toString("yyyy-MM-dd");
959
+
960
+ jQuery("#post-" + post.id).addClass("loadingclass");
961
+
962
+ jQuery.ajax( {
963
+ url: url,
964
+ type: "POST",
965
+ processData: false,
966
+ timeout: 100000,
967
+ dataType: "json",
968
+ success: function(res) {
969
+ edcal.removePostItem(res.post.date, "post-" + res.post.id);
970
+ edcal.addPostItem(res.post, res.post.date);
971
+ edcal.addPostItemDragAndToolltip(res.post.date);
972
+
973
+ if (res.error) {
974
+ /*
975
+ * If there was an error we need to remove the dropped
976
+ * post item.
977
+ */
978
+ edcal.removePostItem(newdate, "post-" + res.post.id);
979
+ if (res.error === edcal.CONCURRENCY_ERROR) {
980
+ edcal.showError("Someone else already moved " + res.post.title);
981
+ } else if (res.error === edcal.PERMISSION_ERROR) {
982
+ edcal.showError("You don't have permission to edit posts");
983
+ }
984
+ }
985
+ },
986
+ error: function(xhr) {
987
+ edcal.showError("There was an error contacting your blog.");
988
+ if (xhr.responseText) {
989
+ output("xhr.responseText: " + xhr.responseText);
990
+ }
991
+ }
992
+ });
993
+
994
+ },
995
+
996
+ /*
997
+ * Shows an error message
998
+ */
999
+ showError: function(/*string*/ msg) {
1000
+ humanMsg.displayMsg(msg);
1001
+ },
1002
+
1003
+ /*
1004
+ Makes an AJAX call to get the posts from the server within the
1005
+ specified dates.
1006
+ */
1007
+ getPosts: function(/*Date*/ from, /*Date*/ to) {
1008
+ //output("getPosts(" + from.toString("dd-MMM-yyyy") + ", " + to.toString("dd-MMM-yyyy") + ")");
1009
+
1010
+ var shouldGet = edcal.cacheDates[from];
1011
+
1012
+ if (shouldGet) {
1013
+ /*
1014
+ * TODO: We don't want to make extra AJAX calls for dates
1015
+ * that we have already coverred. This is cutting down on
1016
+ * it somewhat, but we could get much better about this.
1017
+ */
1018
+ output("We already have the results.");
1019
+ return;
1020
+ }
1021
+
1022
+ edcal.cacheDates[from] = true;
1023
+
1024
+
1025
+ var url = edcal.ajax_url + "?action=edcal_posts&from=" + from.toString("yyyy-MM-dd") + "&to=" + to.toString("yyyy-MM-dd");
1026
+ output("url: " + url);
1027
+
1028
+ jQuery("#loading").show();
1029
+
1030
+ jQuery.ajax( {
1031
+ url: url,
1032
+ type: "GET",
1033
+ processData: false,
1034
+ timeout: 100000,
1035
+ dataType: "text",
1036
+ success: function(res) {
1037
+ jQuery("#loading").hide();
1038
+
1039
+ /*
1040
+ * These result here can get pretty large on a busy blog and
1041
+ * the JSON parser from JSON.org works faster than the native
1042
+ * one used by JQuery.
1043
+ */
1044
+ var parsedRes = JSON.parseIt(res);
1045
+ var postDates = [];
1046
+ jQuery.each(parsedRes, function(i, post) {
1047
+ if (post) {
1048
+ edcal.removePostItem(post.date, "post-" + post.id);
1049
+ edcal.addPostItem(post, post.date);
1050
+ postDates[postDates.length] = post.date;
1051
+ }
1052
+ });
1053
+
1054
+ /*
1055
+ * If the blog has a very larger number of posts then adding
1056
+ * them all can make the UI a little slow. Particularly IE
1057
+ * pops up a warning giving the user a chance to abort the
1058
+ * script. Adding tooltips and making the items draggable is
1059
+ * a lot of what makes things slow. Delaying those two operations
1060
+ * makes the UI show up much faster and the user has to wait
1061
+ * three seconds before they can drag. It also makes IE
1062
+ * stop complaining.
1063
+ */
1064
+ setTimeout(function() {
1065
+ output("adding tooltips and draggables to " + postDates.length + " items.");
1066
+ jQuery.each(postDates, function(i, postDate) {
1067
+ edcal.addPostItemDragAndToolltip(postDate);
1068
+ });
1069
+ }, 300);
1070
+ },
1071
+ error: function(xhr) {
1072
+ edcal.showError("There was an error contacting your blog.");
1073
+ if (xhr.responseText) {
1074
+ output("xhr.responseText: " + xhr.responseText);
1075
+ }
1076
+ }
1077
+ });
1078
+ }
1079
  };
1080
 
1081
  jQuery(document).ready(function(){
1082
+ edcal.init();
1083
  });
1084
 
1085
  /*
1087
  */
1088
  jQuery.extend({URLEncode:function(c){var o='';var x=0;c=c.toString();var r=/(^[a-zA-Z0-9_.]*)/;
1089
  while(x<c.length){var m=r.exec(c.substr(x));
1090
+ if(m!==null && m.length>1 && m[1]!=''){o+=m[1];x+=m[1].length;
1091
+ }else{if(c[x]===' '){o+='+';}else{var d=c.charCodeAt(x);var h=d.toString(16);
1092
+ o+='%'+(h.length<2?'0':'')+h.toUpperCase();}x++;}}return o;},
1093
  URLDecode:function(s){var o=s;var binVal,t;var r=/(%[^%]{2})/;
1094
  while((m=r.exec(o))!==null && m.length>1 && m[1]!==''){b=parseInt(m[1].substr(1),16);
1095
  t=String.fromCharCode(b);o=o.replace(m[1],t);}return o;}
edcal.php CHANGED
@@ -18,12 +18,13 @@
18
  /*
19
  Plugin Name: WordPress Editorial Calendar
20
  Description: An editorial calendar for managing the dates of your WordPress posts
 
21
  Author: Mary Vogt and Zack Grossbart
22
- Version: 0.2
23
  Author URI: http://www.zackgrossbart.com
24
  */
25
 
26
  add_action('wp_ajax_edcal_changedate', 'edcal_changedate' );
 
27
  add_action('wp_ajax_edcal_changetitle', 'edcal_changetitle' );
28
  add_action('admin_menu', 'edcal_list_add_management_page');
29
  add_action('wp_ajax_edcal_posts', 'edcal_posts' );
@@ -33,9 +34,9 @@ add_action("admin_print_scripts", 'edcal_scripts');
33
  * This function adds our calendar page to the admin UI
34
  */
35
  function edcal_list_add_management_page( ) {
36
- if ( function_exists('add_management_page') ) {
37
- $page = add_posts_page( 'Calendar', 'Calendar', 'manage_categories', 'posts_list', 'edcal_list_admin' );
38
- }
39
  }
40
 
41
  /*
@@ -44,10 +45,10 @@ function edcal_list_add_management_page( ) {
44
  * files and cut down on the number of HTTP requests.
45
  */
46
  function echoEdCalFile($myFile) {
47
- $fh = fopen($myFile, 'r');
48
- $theData = fread($fh, filesize($myFile));
49
- fclose($fh);
50
- echo $theData;
51
  }
52
 
53
  /*
@@ -55,92 +56,96 @@ function echoEdCalFile($myFile) {
55
  * generates the divs that we need for the JavaScript to work.
56
  */
57
  function edcal_list_admin() {
58
- include_once('edcal.php');
59
-
60
- /*
61
- * This section of code embeds certain CSS and
62
- * JavaScript files into the HTML. This has the
63
- * advantage of fewer HTTP requests, but the
64
- * disadvantage that the browser can't cache the
65
- * results. We only do this for files that will
66
- * be used on this page and nowhere else.
67
- */
68
-
69
- echo '<!-- This is the styles from jquery.tooltip.css -->';
70
- echo '<style type="text/css">';
71
- echoEdCalFile(dirname( __FILE__ ) . "/lib/jquery.tooltip.css");
72
- echo '</style>';
73
-
74
- echo '<!-- This is the styles from humanmsg.css -->';
75
- echo '<style type="text/css">';
76
- echoEdCalFile(dirname( __FILE__ ) . "/lib/humanmsg.css");
77
- echo '</style>';
78
-
79
- echo '<!-- This is the styles from edcal.css -->';
80
- echo '<style type="text/css">';
81
- echoEdCalFile(dirname( __FILE__ ) . "/edcal.css");
82
- echo '</style>';
83
-
84
- ?>
85
- <!-- This is just a little script so we can pass the AJAX URL -->
86
- <script type="text/javascript">
87
- jQuery(document).ready(function(){
88
- edcal.ajax_url = '<?php echo admin_url("admin-ajax.php"); ?>';
89
- });
90
- </script>
91
-
92
- <style type="text/css">
93
- .loadingclass {
94
- background-image: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/loading_post.gif")); ?>');
95
- }
96
-
97
- .loadingclass:hover {
98
- background-image: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/loading_post_hover.gif")); ?>');
99
- }
100
-
101
- #loading {
102
- background-image: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/loading.gif")); ?>');
103
- }
104
-
105
- #tipclose {
106
- background-image: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/tip_close.gif")); ?>');
107
- }
108
- </style>
109
- <?php
110
-
111
- echo '<!-- This is the code from edcal.js -->';
112
- echo '<script type="text/javascript">';
113
- echoEdCalFile(dirname( __FILE__ ) . "/edcal.js");
114
- echo '</script>';
115
-
116
- ?>
117
- <div class="wrap">
118
- <div class="icon32" id="icon-edit"><br/></div>
119
- <h2>Posts Calendar</h2>
120
-
121
- <div id="loadingcont">
122
- <div id="loading"> </div>
123
- </div>
124
-
125
- <div id="topbar">
126
- <div id="topleft">
127
- <h3>
128
- <a href="#" title="Go back four weeks." class="prev page-numbers" id="prevmonth" style="border: none;">&laquo;</a>
129
- <span id="currentRange"></span>
130
- <a href="#" title="Skip ahead four weeks." class="next page-numbers" id="nextmonth" style="border: none;">&raquo;</a>
131
- </h2>
132
- </div>
133
- <div id="topright">
134
- <a href="#" id="moveToToday">Today</a>
135
- </div>
136
- </div>
137
-
138
- <div id="rowhead"></div>
139
- <div id="edcal_scrollable" class="edcal_scrollable vertical">
140
- <div id="cal"></div>
141
- </div>
142
- </div>
143
- <?php
 
 
 
 
144
  }
145
 
146
  /*
@@ -156,44 +161,44 @@ $edcal_endDate;
156
  * we want.
157
  */
158
  function edcal_filter_where($where = '') {
159
- global $edcal_startDate, $edcal_endDate;
160
- //posts in the last 30 days
161
- //$where .= " AND post_date > '" . date('Y-m-d', strtotime('-30 days')) . "'";
162
- //posts 30 to 60 days old
163
- //$where .= " AND post_date >= '" . date('Y-m-d', strtotime('-60 days')) . "'" . " AND post_date <= '" . date('Y-m-d', strtotime('-30 days')) . "'";
164
- //posts for March 1 to March 15, 2009
165
- $where .= " AND post_date >= '" . $edcal_startDate . "' AND post_date < '" . $edcal_endDate . "'";
166
- return $where;
167
  }
168
 
169
  /*
170
  * This function adds all of the JavaScript files we need.
171
  *
172
  */
173
- function edcal_scripts( ) {
174
- wp_enqueue_script( "edcal-lib", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/edcallib.min.js"), array( 'jquery' ) );
175
- return;
176
-
177
- /*
178
- * If you're using one of the specific libraries you should comment out the two lines
179
- * above this comment.
180
- */
181
-
182
- wp_enqueue_script( "ui-core", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/ui.core.js"), array( 'jquery' ) );
183
- wp_enqueue_script( "ui-draggable", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/ui.draggable.js"), array( 'jquery' ) );
184
- wp_enqueue_script( "ui-droppable", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/ui.droppable.js"), array( 'jquery' ) );
185
-
186
-
187
- wp_enqueue_script( "bgiframe", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/jquery.bgiframe.js"), array( 'jquery' ) );
188
- wp_enqueue_script( "tooltip", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/jquery.tooltip.js"), array( 'jquery' ) );
189
- wp_enqueue_script( "humanMsg", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/humanmsg.js"), array( 'jquery' ) );
190
-
191
-
192
- wp_enqueue_script( "date", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/date.js"), array( 'jquery' ) );
193
- wp_enqueue_script( "scrollable", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/tools.scrollable-1.1.2.js"), array( 'jquery' ) );
194
- wp_enqueue_script( "mouse-wheel", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/tools.scrollable.mousewheel-1.0.1.js"), array( 'jquery' ) );
195
-
196
- wp_enqueue_script( "json-parse2", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/json2.js"), array( 'jquery' ) );
197
  }
198
 
199
  /*
@@ -201,33 +206,43 @@ function edcal_scripts( ) {
201
  * and the to date.
202
  */
203
  function edcal_posts() {
204
- header("Content-Type: application/json");
205
- global $edcal_startDate, $edcal_endDate;
206
- $edcal_startDate = isset($_GET['from'])?$_GET['from']:null;
207
- $edcal_endDate = isset($_GET['to'])?$_GET['to']:null;
208
- global $post;
209
- $args = array(
210
- 'posts_per_page' => -1,
211
- 'post_status' => "publish&future&draft",
212
- 'post_parent' => null, // any parent
213
- 'post_type' => 'post',
214
  );
215
-
216
- add_filter('posts_where', 'edcal_filter_where');
217
- $myposts = query_posts($args);
218
- remove_filter('posts_where', 'edcal_filter_where');
219
-
220
- ?>[
221
- <?php
222
- foreach($myposts as $post) {
223
- if (!is_sticky($id)) {
224
- edcal_postJSON($post);
225
- }
226
- }
227
-
228
- ?> ]
229
- <?php
230
- die;
 
 
 
 
 
 
 
 
 
 
231
  }
232
 
233
  /*
@@ -236,26 +251,27 @@ function edcal_posts() {
236
  * value part.
237
  */
238
  function edcal_postJSON($post) {
239
- setup_postdata($post);
240
- ?>
241
- {
242
- "date" : "<?php the_time('d') ?><?php the_time('M') ?><?php the_time('Y') ?>",
243
- "url" : "<?php the_permalink(); ?>",
244
- "status" : "<?php echo(get_post_status()); ?>",
245
- "title" : "<?php the_title(); ?>",
246
- "author" : "<?php the_author(); ?>",
247
- <?php if ( current_user_can('edit_post', $post->ID) ) {?>
248
- "editlink" : "<?php echo(get_edit_post_link($id)); ?>",
249
- <?php } ?>
250
-
251
- <?php if ( current_user_can('delete_post', $post->ID) ) {?>
252
- "dellink" : "<?php echo(wp_nonce_url("post.php?action=delete&amp;post=$post->ID", 'delete-post_' . $post->ID)); ?>",
253
- <?php } ?>
254
-
255
- "permalink" : "<?php echo(get_permalink($id)); ?>",
256
- "id" : "<?php the_ID(); ?>"
257
- },
258
- <?php
 
259
  }
260
 
261
  /*
@@ -264,39 +280,82 @@ function edcal_postJSON($post) {
264
  * post title in a calendar.
265
  */
266
  function edcal_changetitle() {
267
- header("Content-Type: application/json");
268
- $edcal_postid = isset($_GET['postid'])?$_GET['postid']:null;
269
- $edcal_newTitle = isset($_GET['title'])?$_GET['title']:null;
270
-
271
- $post = get_post($edcal_postid, ARRAY_A);
272
- setup_postdata($post);
273
-
274
- $post['post_title'] = $edcal_newTitle;
275
-
276
- /*
277
- * Now we finally update the post into the database
278
- */
279
- wp_update_post( $post );
280
-
281
- /*
282
- * We finish by returning the latest data for the post in the JSON
283
- */
284
- global $post;
285
- $args = array(
286
- 'posts_id' => $edcal_postid,
287
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
 
289
- $post = get_post($edcal_postid);
290
- ?>{
291
- "post" :
292
- <?php
293
- edcal_postJSON($post);
294
- ?>
295
- }
296
- <?php
297
-
298
-
299
- die;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  }
301
 
302
  /*
@@ -308,147 +367,141 @@ function edcal_changetitle() {
308
  * If the call is successful then it returns the updated post data.
309
  */
310
  function edcal_changedate() {
311
- if (!current_user_can('edit_others_posts')) {
312
- /*
313
- * This is just a sanity check to make sure that the current
314
- * user has permission to edit posts. Most of the time this
315
- * will never be run because you can't see the calendar unless
316
- * you are at least an editor
317
- */
318
- ?>
319
- {
320
- "error": 5,
321
- <?php
322
-
323
-
324
- global $post;
325
- $args = array(
326
- 'posts_id' => $edcal_postid,
327
- );
328
-
329
- $post = get_post($edcal_postid);
330
- ?>
331
- "post" :
332
- <?php
333
- edcal_postJSON($post);
334
-
335
-
336
-
337
- ?> }
338
- <?php
339
- }
340
-
341
- header("Content-Type: application/json");
342
- global $edcal_startDate, $edcal_endDate;
343
- $edcal_postid = isset($_GET['postid'])?$_GET['postid']:null;
344
- $edcal_newDate = isset($_GET['newdate'])?$_GET['newdate']:null;
345
- $edcal_oldDate = isset($_GET['olddate'])?$_GET['olddate']:null;
346
- $edcal_postStatus = isset($_GET['postStatus'])?$_GET['postStatus']:null;
347
-
348
- $post = get_post($edcal_postid, ARRAY_A);
349
- setup_postdata($post);
350
-
351
- $matches = strpos($post['post_date'], $edcal_oldDate) === 0;
352
-
353
- /*
354
- * We are doing optimistic concurrency checking on the dates. If
355
- * the user tries to move a post we want to make sure nobody else
356
- * has moved that post since the page was last updated. If the
357
- * old date in the database doesn't match the old date from the
358
- * browser then we return an error to the browser along with the
359
- * updated post data.
360
- */
361
- if ($matches != 1) {
362
- ?>
363
- {
364
- "error": 4,
365
- <?php
366
-
367
-
368
- global $post;
369
- $args = array(
370
- 'posts_id' => $edcal_postid,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
  );
372
-
373
- $post = get_post($edcal_postid);
374
- ?>
375
- "post" :
376
- <?php
377
- edcal_postJSON($post);
378
-
379
- ?> }
380
- <?php
381
- //return new WP_Error('broke', __("no match error"));
382
- die();
383
- }
384
-
385
- /*
386
- * Posts in WordPress have more than one date. There is the GMT date,
387
- * the date in the local time zone, the modified date in GMT and the
388
- * modified date in the local time zone. We update all of them.
389
- */
390
- $post['post_date_gmt'] = $post['post_date'];
391
-
392
- /*
393
- * When a user creates a draft and never sets a date or publishes it
394
- * then the GMT date will have a timestamp of 00:00:00 to indicate
395
- * that the date hasn't been set. In that case we need to specify
396
- * an edit date or the wp_update_post function will strip our new
397
- * date out and leave the post as publish immediately.
398
- */
399
- $needsEditDate = strpos($post['post_date_gmt'], "00:00:00") === 0;
400
-
401
- $updated_post = array();
402
- $updated_post['ID'] = $edcal_postid;
403
- $updated_post['post_date'] = $edcal_newDate . substr($post['post_date'], strlen($edcal_newDate));
404
- if ($needsEditDate != -1) {
405
- $updated_post['edit_date'] = $edcal_newDate . substr($post['post_date'], strlen($edcal_newDate));
406
- }
407
- $updated_post['post_date_gmt'] = $edcal_newDate . substr($post['post_date_gmt'], strlen($edcal_newDate));
408
- $updated_post['post_modified'] = $edcal_newDate . substr($post['post_modified'], strlen($edcal_newDate));
409
- $updated_post['post_modified_gmt'] = $edcal_newDate . substr($post['post_modified_gmt'], strlen($edcal_newDate));
410
-
411
- if ( $edcal_postStatus != $post['post_status'] ) {
412
- /*
413
- * We only want to update the post status if it has changed.
414
- * If the post status has changed that takes a few more steps
415
- */
416
- wp_transition_post_status($edcal_postStatus, $post['post_status'], $post);
417
- $updated_post['post_status'] = $edcal_postStatus;
418
-
419
- // Update counts for the post's terms.
420
- foreach ( (array) get_object_taxonomies('post') as $taxonomy ) {
421
- $tt_ids = wp_get_object_terms($post_id, $taxonomy, 'fields=tt_ids');
422
- wp_update_term_count($tt_ids, $taxonomy);
423
- }
424
-
425
- do_action('edit_post', $edcal_postid, $post);
426
- do_action('save_post', $edcal_postid, $post);
427
- do_action('wp_insert_post', $edcal_postid, $post);
428
- }
429
-
430
- /*
431
- * Now we finally update the post into the database
432
- */
433
- wp_update_post( $updated_post );
434
-
435
- /*
436
- * We finish by returning the latest data for the post in the JSON
437
- */
438
- global $post;
439
- $args = array(
440
- 'posts_id' => $edcal_postid,
441
- );
442
-
443
- $post = get_post($edcal_postid);
444
- ?>{
445
- "post" :
446
- <?php
447
- edcal_postJSON($post);
448
- ?>
449
- }
450
- <?php
451
-
452
-
453
- die;
454
  }
18
  /*
19
  Plugin Name: WordPress Editorial Calendar
20
  Description: An editorial calendar for managing the dates of your WordPress posts
21
+ Version: 0.3
22
  Author: Mary Vogt and Zack Grossbart
 
23
  Author URI: http://www.zackgrossbart.com
24
  */
25
 
26
  add_action('wp_ajax_edcal_changedate', 'edcal_changedate' );
27
+ add_action('wp_ajax_edcal_newdraft', 'edcal_newdraft' );
28
  add_action('wp_ajax_edcal_changetitle', 'edcal_changetitle' );
29
  add_action('admin_menu', 'edcal_list_add_management_page');
30
  add_action('wp_ajax_edcal_posts', 'edcal_posts' );
34
  * This function adds our calendar page to the admin UI
35
  */
36
  function edcal_list_add_management_page( ) {
37
+ if ( function_exists('add_management_page') ) {
38
+ $page = add_posts_page( 'Calendar', 'Calendar', 'manage_categories', 'posts_list', 'edcal_list_admin' );
39
+ }
40
  }
41
 
42
  /*
45
  * files and cut down on the number of HTTP requests.
46
  */
47
  function echoEdCalFile($myFile) {
48
+ $fh = fopen($myFile, 'r');
49
+ $theData = fread($fh, filesize($myFile));
50
+ fclose($fh);
51
+ echo $theData;
52
  }
53
 
54
  /*
56
  * generates the divs that we need for the JavaScript to work.
57
  */
58
  function edcal_list_admin() {
59
+ include_once('edcal.php');
60
+
61
+
62
+ /*
63
+ * This section of code embeds certain CSS and
64
+ * JavaScript files into the HTML. This has the
65
+ * advantage of fewer HTTP requests, but the
66
+ * disadvantage that the browser can't cache the
67
+ * results. We only do this for files that will
68
+ * be used on this page and nowhere else.
69
+ */
70
+
71
+ echo '<!-- This is the styles from jquery.tooltip.css -->';
72
+ echo '<style type="text/css">';
73
+ echoEdCalFile(dirname( __FILE__ ) . "/lib/jquery.tooltip.css");
74
+ echo '</style>';
75
+
76
+ echo '<!-- This is the styles from humanmsg.css -->';
77
+ echo '<style type="text/css">';
78
+ echoEdCalFile(dirname( __FILE__ ) . "/lib/humanmsg.css");
79
+ echo '</style>';
80
+
81
+ echo '<!-- This is the styles from edcal.css -->';
82
+ echo '<style type="text/css">';
83
+ echoEdCalFile(dirname( __FILE__ ) . "/edcal.css");
84
+ echo '</style>';
85
+
86
+ ?>
87
+ <!-- This is just a little script so we can pass the AJAX URL -->
88
+ <script type="text/javascript">
89
+ jQuery(document).ready(function(){
90
+ edcal.ajax_url = '<?php echo admin_url("admin-ajax.php"); ?>';
91
+ });
92
+ </script>
93
+
94
+ <style type="text/css">
95
+ .loadingclass {
96
+ background-image: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/loading_post.gif")); ?>');
97
+ }
98
+
99
+ .loadingclass:hover {
100
+ background-image: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/loading_post_hover.gif")); ?>');
101
+ }
102
+
103
+ #loading {
104
+ background-image: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/loading.gif")); ?>');
105
+ }
106
+
107
+ #tipclose {
108
+ background-image: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/tip_close.gif")); ?>');
109
+ }
110
+ </style>
111
+
112
+ <?php
113
+ echo '<!-- This is the code from edcal.js -->';
114
+ echo '<script type="text/javascript">';
115
+ echoEdCalFile(dirname( __FILE__ ) . "/edcal.js");
116
+ echo '</script>';
117
+
118
+ ?>
119
+
120
+ <div class="wrap">
121
+ <div class="icon32" id="icon-edit"><br/></div>
122
+ <h2>Posts Calendar</h2>
123
+
124
+ <div id="loadingcont">
125
+ <div id="loading"> </div>
126
+ </div>
127
+
128
+ <div id="topbar">
129
+ <div id="topleft">
130
+ <h2>
131
+ <a href="#" title="Go back four weeks." class="prev page-numbers" id="prevmonth" style="border: none;">&laquo;</a>
132
+ <span id="currentRange"></span>
133
+ <a href="#" title="Skip ahead four weeks." class="next page-numbers" id="nextmonth" style="border: none;">&raquo;</a>
134
+ </h2>
135
+ </div>
136
+
137
+ <div id="topright">
138
+ <a href="#" id="moveToToday">Today</a>
139
+ </div>
140
+ </div>
141
+
142
+ <div id="rowhead"></div>
143
+ <div id="edcal_scrollable" class="edcal_scrollable vertical">
144
+ <div id="cal"></div>
145
+ </div>
146
+ </div>
147
+
148
+ <?php
149
  }
150
 
151
  /*
161
  * we want.
162
  */
163
  function edcal_filter_where($where = '') {
164
+ global $edcal_startDate, $edcal_endDate;
165
+ //posts in the last 30 days
166
+ //$where .= " AND post_date > '" . date('Y-m-d', strtotime('-30 days')) . "'";
167
+ //posts 30 to 60 days old
168
+ //$where .= " AND post_date >= '" . date('Y-m-d', strtotime('-60 days')) . "'" . " AND post_date <= '" . date('Y-m-d', strtotime('-30 days')) . "'";
169
+ //posts for March 1 to March 15, 2009
170
+ $where .= " AND post_date >= '" . $edcal_startDate . "' AND post_date < '" . $edcal_endDate . "'";
171
+ return $where;
172
  }
173
 
174
  /*
175
  * This function adds all of the JavaScript files we need.
176
  *
177
  */
178
+ function edcal_scripts() {
179
+ wp_enqueue_script( "edcal-lib", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/edcallib.min.js"), array( 'jquery' ) );
180
+ return;
181
+
182
+ /*
183
+ * If you're using one of the specific libraries you should comment out the two lines
184
+ * above this comment.
185
+ */
186
+
187
+ wp_enqueue_script( "ui-core", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/ui.core.js"), array( 'jquery' ) );
188
+ wp_enqueue_script( "ui-draggable", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/ui.draggable.js"), array( 'jquery' ) );
189
+ wp_enqueue_script( "ui-droppable", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/ui.droppable.js"), array( 'jquery' ) );
190
+
191
+
192
+ wp_enqueue_script( "bgiframe", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/jquery.bgiframe.js"), array( 'jquery' ) );
193
+ wp_enqueue_script( "tooltip", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/jquery.tooltip.js"), array( 'jquery' ) );
194
+ wp_enqueue_script( "humanMsg", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/humanmsg.js"), array( 'jquery' ) );
195
+
196
+
197
+ wp_enqueue_script( "date", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/date.js"), array( 'jquery' ) );
198
+ wp_enqueue_script( "scrollable", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/tools.scrollable-1.1.2.js"), array( 'jquery' ) );
199
+ wp_enqueue_script( "mouse-wheel", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/tools.scrollable.mousewheel-1.0.1.js"), array( 'jquery' ) );
200
+
201
+ wp_enqueue_script( "json-parse2", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/json2.js"), array( 'jquery' ) );
202
  }
203
 
204
  /*
206
  * and the to date.
207
  */
208
  function edcal_posts() {
209
+ header("Content-Type: application/json");
210
+ global $edcal_startDate, $edcal_endDate;
211
+ $edcal_startDate = isset($_GET['from'])?$_GET['from']:null;
212
+ $edcal_endDate = isset($_GET['to'])?$_GET['to']:null;
213
+ global $post;
214
+ $args = array(
215
+ 'posts_per_page' => -1,
216
+ 'post_status' => "publish&future&draft",
217
+ 'post_parent' => null, // any parent
218
+ 'post_type' => 'post',
219
  );
220
+
221
+ add_filter('posts_where', 'edcal_filter_where');
222
+ $myposts = query_posts($args);
223
+ remove_filter('posts_where', 'edcal_filter_where');
224
+
225
+ ?>[
226
+ <?php
227
+
228
+ foreach($myposts as $post) {
229
+ /*
230
+ * Sticky posts are ones that stick to the front page.
231
+ * They do technically have a date, but it doesn't
232
+ * really make sense to drag and drop them around since
233
+ * the user has already indicated that they want them
234
+ * to stay on the front page.
235
+ */
236
+
237
+ if (!is_sticky($post->ID)) {
238
+ edcal_postJSON($post);
239
+ }
240
+ }
241
+
242
+ ?> ]
243
+ <?php
244
+
245
+ die;
246
  }
247
 
248
  /*
251
  * value part.
252
  */
253
  function edcal_postJSON($post) {
254
+ setup_postdata($post);
255
+ ?>
256
+ {
257
+ "date" : "<?php the_time('d') ?><?php the_time('M') ?><?php the_time('Y') ?>",
258
+ "time" : "<?php the_time() ?>",
259
+ "url" : "<?php the_permalink(); ?>",
260
+ "status" : "<?php echo(get_post_status()); ?>",
261
+ "title" : "<?php the_title(); ?>",
262
+ "author" : "<?php the_author(); ?>",
263
+ <?php if ( current_user_can('edit_post', $post->ID) ) {?>
264
+ "editlink" : "<?php echo(get_edit_post_link($id)); ?>",
265
+ <?php } ?>
266
+
267
+ <?php if ( current_user_can('delete_post', $post->ID) ) {?>
268
+ "dellink" : "<?php echo(wp_nonce_url("post.php?action=delete&amp;post=$post->ID", 'delete-post_' . $post->ID)); ?>",
269
+ <?php } ?>
270
+
271
+ "permalink" : "<?php echo(get_permalink($id)); ?>",
272
+ "id" : "<?php the_ID(); ?>"
273
+ },
274
+ <?php
275
  }
276
 
277
  /*
280
  * post title in a calendar.
281
  */
282
  function edcal_changetitle() {
283
+ header("Content-Type: application/json");
284
+ $edcal_postid = isset($_GET['postid'])?$_GET['postid']:null;
285
+ $edcal_newTitle = isset($_GET['title'])?$_GET['title']:null;
286
+
287
+ $post = get_post($edcal_postid, ARRAY_A);
288
+ setup_postdata($post);
289
+
290
+ $post['post_title'] = $edcal_newTitle;
291
+
292
+ /*
293
+ * Now we finally update the post into the database
294
+ */
295
+ wp_update_post( $post );
296
+
297
+ /*
298
+ * We finish by returning the latest data for the post in the JSON
299
+ */
300
+ global $post;
301
+ $args = array(
302
+ 'posts_id' => $edcal_postid,
303
  );
304
+
305
+ $post = get_post($edcal_postid);
306
+
307
+ ?>{
308
+ "post" :
309
+ <?php
310
+
311
+ edcal_postJSON($post);
312
+
313
+ ?>
314
+ }
315
+ <?php
316
+
317
+
318
+ die;
319
+ }
320
 
321
+ /*
322
+ * This is a helper function to create a new blank draft
323
+ * post on a specified date.
324
+ */
325
+ function edcal_newdraft() {
326
+ header("Content-Type: application/json");
327
+ $edcal_date = isset($_GET['date'])?$_GET['date']:null;
328
+
329
+ $my_post = array();
330
+ $my_post['post_title'] = ' ';
331
+ $my_post['post_status'] = 'draft';
332
+
333
+ $my_post['post_date'] = $edcal_date;
334
+ $my_post['post_date_gmt'] = $edcal_date;
335
+ $my_post['post_modified'] = $edcal_date;
336
+ $my_post['post_modified_gmt'] = $edcal_date;
337
+
338
+ // Insert the post into the database
339
+ $my_post_id = wp_insert_post( $my_post );
340
+
341
+
342
+ /*
343
+ * We finish by returning the latest data for the post in the JSON
344
+ */
345
+ global $post;
346
+ $args = array(
347
+ 'posts_id' => $edcal_postid,
348
+ );
349
+
350
+ $post = get_post($edcal_postid);
351
+ ?>{
352
+ "newpostid" : "<?php echo($my_post_id); ?>",
353
+ "editlink" : "<?php echo(get_edit_post_link($my_post_id)); ?>",
354
+ }
355
+
356
+ <?php
357
+
358
+ die;
359
  }
360
 
361
  /*
367
  * If the call is successful then it returns the updated post data.
368
  */
369
  function edcal_changedate() {
370
+ if (!current_user_can('edit_others_posts')) {
371
+ /*
372
+ * This is just a sanity check to make sure that the current
373
+ * user has permission to edit posts. Most of the time this
374
+ * will never be run because you can't see the calendar unless
375
+ * you are at least an editor
376
+ */
377
+ ?>
378
+ {
379
+ "error": 5,
380
+ <?php
381
+
382
+ global $post;
383
+ $args = array(
384
+ 'posts_id' => $edcal_postid,
385
+ );
386
+
387
+ $post = get_post($edcal_postid);
388
+ ?>
389
+ "post" :
390
+ <?php
391
+ edcal_postJSON($post);
392
+ ?> }
393
+
394
+ <?php
395
+ }
396
+
397
+ header("Content-Type: application/json");
398
+ global $edcal_startDate, $edcal_endDate;
399
+ $edcal_postid = isset($_GET['postid'])?$_GET['postid']:null;
400
+ $edcal_newDate = isset($_GET['newdate'])?$_GET['newdate']:null;
401
+ $edcal_oldDate = isset($_GET['olddate'])?$_GET['olddate']:null;
402
+ $edcal_postStatus = isset($_GET['postStatus'])?$_GET['postStatus']:null;
403
+
404
+ $post = get_post($edcal_postid, ARRAY_A);
405
+ setup_postdata($post);
406
+
407
+ $matches = strpos($post['post_date'], $edcal_oldDate) === 0;
408
+
409
+ /*
410
+ * We are doing optimistic concurrency checking on the dates. If
411
+ * the user tries to move a post we want to make sure nobody else
412
+ * has moved that post since the page was last updated. If the
413
+ * old date in the database doesn't match the old date from the
414
+ * browser then we return an error to the browser along with the
415
+ * updated post data.
416
+ */
417
+
418
+ if ($matches != 1) {
419
+ ?> {
420
+ "error": 4,
421
+ <?php
422
+
423
+ global $post;
424
+ $args = array(
425
+ 'posts_id' => $edcal_postid,
426
+ );
427
+
428
+ $post = get_post($edcal_postid);
429
+ ?>
430
+ "post" :
431
+ <?php
432
+ edcal_postJSON($post);
433
+ ?> }
434
+
435
+ <?php
436
+ die();
437
+ }
438
+
439
+ /*
440
+ * Posts in WordPress have more than one date. There is the GMT date,
441
+ * the date in the local time zone, the modified date in GMT and the
442
+ * modified date in the local time zone. We update all of them.
443
+ */
444
+ $post['post_date_gmt'] = $post['post_date'];
445
+
446
+ /*
447
+ * When a user creates a draft and never sets a date or publishes it
448
+ * then the GMT date will have a timestamp of 00:00:00 to indicate
449
+ * that the date hasn't been set. In that case we need to specify
450
+ * an edit date or the wp_update_post function will strip our new
451
+ * date out and leave the post as publish immediately.
452
+ */
453
+ $needsEditDate = strpos($post['post_date_gmt'], "0000-00-00 00:00:00") === 0;
454
+
455
+ $updated_post = array();
456
+ $updated_post['ID'] = $edcal_postid;
457
+ $updated_post['post_date'] = $edcal_newDate . substr($post['post_date'], strlen($edcal_newDate));
458
+ if ($needsEditDate != -1) {
459
+ $updated_post['edit_date'] = $edcal_newDate . substr($post['post_date'], strlen($edcal_newDate));
460
+ }
461
+ $updated_post['post_date_gmt'] = $edcal_newDate . substr($post['post_date_gmt'], strlen($edcal_newDate));
462
+ $updated_post['post_modified'] = $edcal_newDate . substr($post['post_modified'], strlen($edcal_newDate));
463
+ $updated_post['post_modified_gmt'] = $edcal_newDate . substr($post['post_modified_gmt'], strlen($edcal_newDate));
464
+
465
+ if ( $edcal_postStatus != $post['post_status'] ) {
466
+ /*
467
+ * We only want to update the post status if it has changed.
468
+ * If the post status has changed that takes a few more steps
469
+ */
470
+ wp_transition_post_status($edcal_postStatus, $post['post_status'], $post);
471
+ $updated_post['post_status'] = $edcal_postStatus;
472
+
473
+ // Update counts for the post's terms.
474
+ foreach ( (array) get_object_taxonomies('post') as $taxonomy ) {
475
+ $tt_ids = wp_get_object_terms($post_id, $taxonomy, 'fields=tt_ids');
476
+ wp_update_term_count($tt_ids, $taxonomy);
477
+ }
478
+
479
+ do_action('edit_post', $edcal_postid, $post);
480
+ do_action('save_post', $edcal_postid, $post);
481
+ do_action('wp_insert_post', $edcal_postid, $post);
482
+ }
483
+
484
+ /*
485
+ * Now we finally update the post into the database
486
+ */
487
+ wp_update_post( $updated_post );
488
+
489
+ /*
490
+ * We finish by returning the latest data for the post in the JSON
491
+ */
492
+ global $post;
493
+ $args = array(
494
+ 'posts_id' => $edcal_postid,
495
  );
496
+
497
+ $post = get_post($edcal_postid);
498
+ ?>{
499
+ "post" :
500
+
501
+ <?php
502
+ edcal_postJSON($post);
503
+ ?>}
504
+ <?php
505
+
506
+ die;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
  }
readme.txt CHANGED
@@ -3,16 +3,23 @@ Contributors: MaryVogt, zgrossbart
3
  Tags: posts, post, calendar, AJAX, admin, administration
4
  Requires at least: 2.8.5
5
  Tested up to: 2.8.6
6
- Stable tag: 0.2
7
 
8
- THIS PLUGIN IS NOT READY FOR DOWNLOAD. Editorial calendar lets you see all your posts and drag and drop them to change the date.
9
 
10
  == Description ==
11
 
12
- *THIS PLUGIN IS NOT READY FOR DOWNLOAD. DO NOT RUN IT OUTSIDE OF A TEST ENVIRONMENT.*
13
-
14
  The editorial calendar lets WordPress administrators and editors manage the dates for multiple posts at once. You see all of your posts in a calendar view and can arrange them via an easy drag and drop interface. The editorial calendar greatly improves any blog that plans posts in advance or takes contributions from multiple users.
15
 
 
 
 
 
 
 
 
 
 
16
  == Installation ==
17
 
18
  1. <b>Backup your WordPress database</b>.
@@ -38,18 +45,26 @@ The different color of each post indicates the status. Gray is <code>draft</cod
38
 
39
  The calendar works with three types of post status: <code>draft</code>, <code>publish</code>, and <code>future</code>. If you move a <code>publish</code> post to a date after the current day it becomes a <code>future</code> post. If you drag a <code>future</code> post to a day before the current day it becomes a <code>draft</code> post.
40
 
 
 
 
 
41
  == Screenshots ==
42
 
43
- 1. See all of your posts and when they'll be posted.
44
  2. Get more information and edit post titles right in the calendar.
45
  3. Drag and drop posts to easily change dates and arrange your calendar.
46
 
47
  == Changelog ==
48
 
49
- = 0.1 =
50
- * This version is just for beta testers
51
 
52
  = 0.2 =
53
  * This version fixes a large number of bugs, makes many usability improvements, and has some significant performance increases.
54
 
 
 
 
 
55
  `<?php code(); // goes in backticks ?>`
3
  Tags: posts, post, calendar, AJAX, admin, administration
4
  Requires at least: 2.8.5
5
  Tested up to: 2.8.6
6
+ Stable tag: 0.3
7
 
8
+ Editorial calendar makes it possible to see all your posts and drag and drop them to manage your blog.
9
 
10
  == Description ==
11
 
 
 
12
  The editorial calendar lets WordPress administrators and editors manage the dates for multiple posts at once. You see all of your posts in a calendar view and can arrange them via an easy drag and drop interface. The editorial calendar greatly improves any blog that plans posts in advance or takes contributions from multiple users.
13
 
14
+ This is the beta version and the first one ready for public use. Please help us make this plugin better by giving us <a href="mailto:zack@grossbart.com">feedback</a>.
15
+
16
+ Features:
17
+
18
+ 1. See all of your posts and when they'll be posted.
19
+ 1. Drag and drop to change your post dates.
20
+ 1. Edit and arrange post titles.
21
+ 1. Easily see the status of your posts.
22
+
23
  == Installation ==
24
 
25
  1. <b>Backup your WordPress database</b>.
45
 
46
  The calendar works with three types of post status: <code>draft</code>, <code>publish</code>, and <code>future</code>. If you move a <code>publish</code> post to a date after the current day it becomes a <code>future</code> post. If you drag a <code>future</code> post to a day before the current day it becomes a <code>draft</code> post.
47
 
48
+ = Why do I have posts named [No Title]? =
49
+
50
+ If you add a new post from the calendar it will get inserted as a draft. If you then cancel or just go backwards in your browser you'll be left with an empty post. They don't cause any problems. I would like to make this work better, but I'm waiting for a fix in a future version of WordPress.
51
+
52
  == Screenshots ==
53
 
54
+ 1. See all of your posts and when they'll be published.
55
  2. Get more information and edit post titles right in the calendar.
56
  3. Drag and drop posts to easily change dates and arrange your calendar.
57
 
58
  == Changelog ==
59
 
60
+ = 0.3 =
61
+ * Additional bug fixes and stabalizations as well as the ability to add new posts to a given date from the calendar.
62
 
63
  = 0.2 =
64
  * This version fixes a large number of bugs, makes many usability improvements, and has some significant performance increases.
65
 
66
+ = 0.1 =
67
+ * This version is just for beta testers
68
+
69
+
70
  `<?php code(); // goes in backticks ?>`