cloudcms-server 4.0.0-beta.2 → 4.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -5
- package/cloudcms-server.iml +1 -0
- package/index.js +58 -32
- package/middleware/authentication/authentication.js +40 -12
- package/middleware/authentication/providers/saml.js +8 -4
- package/middleware/awareness/awareness.js +8 -7
- package/middleware/awareness/plugins/api_event.js +105 -0
- package/middleware/awareness/plugins/editorial.js +54 -3
- package/middleware/awareness/plugins/resources.js +13 -5
- package/middleware/config/adapter.js +0 -44
- package/middleware/deployment/deployment.js +22 -24
- package/middleware/driver/driver.js +24 -1
- package/middleware/driver-config/driver-config.js +0 -6
- package/middleware/modules/modules.js +11 -5
- package/middleware/perf/perf.js +3 -2
- package/middleware/registration/registration.js +0 -5
- package/middleware/stores/engines/empty.js +0 -4
- package/middleware/stores/engines/fs-caching-adapter.js +0 -5
- package/middleware/stores/engines/fs.js +0 -9
- package/middleware/stores/engines/s3.js +0 -5
- package/middleware/stores/engines/s3fs.js +0 -5
- package/middleware/stores/multistore.js +0 -29
- package/middleware/stores/store.js +0 -10
- package/middleware/stores/stores.js +2 -2
- package/middleware/virtual-config/virtual-config.js +253 -206
- package/middleware/virtual-files/virtual-files.js +0 -3
- package/middleware/welcome/welcome.js +0 -3
- package/notifications/notifications.js +72 -10
- package/notifications/providers/kafka.js +182 -0
- package/notifications/providers/stomp.js +4 -0
- package/package.json +40 -56
- package/server/index.js +216 -123
- package/server/standalone.js +1 -6
- package/util/auth.js +10 -4
- package/util/cloudcms.js +77 -35
- package/util/loaders.js +113 -0
- package/util/proxy-factory.js +143 -168
- package/util/request.js +6 -2
- package/util/workqueue.js +111 -0
- package/.last_command +0 -7
- package/duster/helpers/core/cloudcms/associations.js +0 -34
- package/duster/helpers/core/cloudcms/beta/markdown.js +0 -46
- package/duster/helpers/core/cloudcms/beta/nodeAttachmentText.js +0 -46
- package/duster/helpers/core/cloudcms/beta/params.js +0 -33
- package/duster/helpers/core/cloudcms/beta/processTemplate.js +0 -82
- package/duster/helpers/core/cloudcms/content.js +0 -34
- package/duster/helpers/core/cloudcms/expand.js +0 -38
- package/duster/helpers/core/cloudcms/form.js +0 -34
- package/duster/helpers/core/cloudcms/query.js +0 -34
- package/duster/helpers/core/cloudcms/queryOne.js +0 -34
- package/duster/helpers/core/cloudcms/relatives.js +0 -34
- package/duster/helpers/core/cloudcms/search.js +0 -34
- package/duster/helpers/core/cloudcms/searchOne.js +0 -34
- package/duster/helpers/core/cloudcms/wcm/dependency.js +0 -83
- package/duster/helpers/core/cloudcms/wcm/fragment.js +0 -34
- package/duster/helpers/core/dev/debug.js +0 -42
- package/duster/helpers/core/dom/block.js +0 -49
- package/duster/helpers/core/dom/include.js +0 -38
- package/duster/helpers/core/dom/layout.js +0 -49
- package/duster/helpers/core/dom/link.js +0 -81
- package/duster/helpers/core/dom/resource.js +0 -77
- package/duster/helpers/core/engine.js +0 -1580
- package/duster/helpers/core/ice/value.js +0 -65
- package/duster/helpers/core/index.js +0 -49
- package/duster/helpers/core/operators/if.js +0 -64
- package/duster/helpers/core/operators/iter.js +0 -45
- package/duster/helpers/core/operators/iterate.js +0 -129
- package/duster/helpers/sample/nyt.js +0 -114
- package/duster/index.js +0 -319
- package/duster/support.js +0 -436
- package/duster/tracker.js +0 -262
- package/middleware/authentication/providers/cas.js +0 -73
- package/middleware/authentication/providers/facebook.js +0 -120
- package/middleware/authentication/providers/github.js +0 -88
- package/middleware/authentication/providers/linkedin.js +0 -112
- package/middleware/authentication/providers/twitter.js +0 -120
- package/middleware/server-tags/server-tags.js +0 -113
- package/middleware/wcm/wcm.js +0 -1437
package/middleware/wcm/wcm.js
DELETED
|
@@ -1,1437 +0,0 @@
|
|
|
1
|
-
var path = require('path');
|
|
2
|
-
var fs = require('fs');
|
|
3
|
-
var http = require('http');
|
|
4
|
-
var util = require("../../util/util");
|
|
5
|
-
var cloudcms = require("../../util/cloudcms");
|
|
6
|
-
var duster = require("../../duster/index");
|
|
7
|
-
var async = require("async");
|
|
8
|
-
|
|
9
|
-
var support = require("../../duster/support")(duster.getDust());
|
|
10
|
-
|
|
11
|
-
var renditions = require("../../util/renditions");
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* WCM middleware.
|
|
15
|
-
*
|
|
16
|
-
* Serves up HTML pages based on WCM configuration. Applies duster tag processing.
|
|
17
|
-
*
|
|
18
|
-
* @type {Function}
|
|
19
|
-
*/
|
|
20
|
-
exports = module.exports = function()
|
|
21
|
-
{
|
|
22
|
-
// cache keys
|
|
23
|
-
var WCM_PAGES = "wcmPages";
|
|
24
|
-
var WCM_PAGES_CACHE_TIME = "wcmPagesTime";
|
|
25
|
-
|
|
26
|
-
var isEmpty = function(thing)
|
|
27
|
-
{
|
|
28
|
-
return (typeof(thing) === "undefined") || (thing === null);
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
var startsWith = function(text, prefix) {
|
|
32
|
-
return text.substr(0, prefix.length) === prefix;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
var executeMatch = function(matcher, text)
|
|
36
|
-
{
|
|
37
|
-
// strip matcher from "/a/b/c" to "a/b/c"
|
|
38
|
-
if (matcher && matcher.length > 0 && matcher.substring(0,1) === "/")
|
|
39
|
-
{
|
|
40
|
-
matcher = matcher.substring(1);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// strip text from "/a/b/c" to "a/b/c"
|
|
44
|
-
if (text && text.length > 0 && text.substring(0,1) === "/")
|
|
45
|
-
{
|
|
46
|
-
text = text.substring(1);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
var tokens = {};
|
|
50
|
-
|
|
51
|
-
var printDebug = function()
|
|
52
|
-
{
|
|
53
|
-
if (process.env.NODE_ENV === "production") {
|
|
54
|
-
// skip
|
|
55
|
-
} else {
|
|
56
|
-
process.log("Matched - pattern: " + matcher + ", text: " + text + ", tokens: " + JSON.stringify(tokens));
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
var array1 = [];
|
|
61
|
-
if (matcher)
|
|
62
|
-
{
|
|
63
|
-
array1 = matcher.split("/");
|
|
64
|
-
}
|
|
65
|
-
var array2 = [];
|
|
66
|
-
if (text)
|
|
67
|
-
{
|
|
68
|
-
array2 = text.split("/");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// short cut - zero length matches
|
|
72
|
-
if ((array1.length === 0) && (array2.length === 0))
|
|
73
|
-
{
|
|
74
|
-
printDebug();
|
|
75
|
-
return tokens;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (matcher)
|
|
79
|
-
{
|
|
80
|
-
// short cut - **
|
|
81
|
-
if (matcher === "**")
|
|
82
|
-
{
|
|
83
|
-
// it's a match, pull out wildcard token
|
|
84
|
-
tokens["**"] = text;
|
|
85
|
-
printDebug();
|
|
86
|
-
return tokens;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// if matcher has no wildcards or tokens...
|
|
90
|
-
if ((matcher.indexOf("{") === -1) && (matcher.indexOf("*") === -1))
|
|
91
|
-
{
|
|
92
|
-
// if they're equal...
|
|
93
|
-
if (matcher === text)
|
|
94
|
-
{
|
|
95
|
-
// it's a match, no tokens
|
|
96
|
-
tokens["_exact"] = true;
|
|
97
|
-
printDebug();
|
|
98
|
-
return tokens;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
var pattern = null;
|
|
104
|
-
var value = null;
|
|
105
|
-
do
|
|
106
|
-
{
|
|
107
|
-
pattern = array1.shift();
|
|
108
|
-
value = array2.shift();
|
|
109
|
-
|
|
110
|
-
var patternEmpty = (isEmpty(pattern) || pattern === "");
|
|
111
|
-
var valueEmpty = (isEmpty(value) || value === "");
|
|
112
|
-
|
|
113
|
-
// if there are remaining pattern and value elements
|
|
114
|
-
if (!patternEmpty && !valueEmpty)
|
|
115
|
-
{
|
|
116
|
-
if (pattern === "*")
|
|
117
|
-
{
|
|
118
|
-
// wildcard - element matches
|
|
119
|
-
}
|
|
120
|
-
else if (pattern === "**")
|
|
121
|
-
{
|
|
122
|
-
// wildcard - match everything else, so break out
|
|
123
|
-
tokens["**"] = "/" + [].concat(value, array2).join("/");
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
else if (pattern.indexOf("{") > -1)
|
|
127
|
-
{
|
|
128
|
-
var startIndex = pattern.indexOf("{");
|
|
129
|
-
var stopIndex = pattern.indexOf("}");
|
|
130
|
-
|
|
131
|
-
var prefix = null;
|
|
132
|
-
if (startIndex > 0)
|
|
133
|
-
{
|
|
134
|
-
prefix = pattern.substring(0, startIndex);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
var suffix = null;
|
|
138
|
-
if (stopIndex < pattern.length - 1)
|
|
139
|
-
{
|
|
140
|
-
suffix = pattern.substring(stopIndex);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (prefix)
|
|
144
|
-
{
|
|
145
|
-
value = value.substring(prefix.length);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (suffix)
|
|
149
|
-
{
|
|
150
|
-
value = value.substring(0, value.length - suffix.length + 1);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
var key = pattern.substring(startIndex + 1, stopIndex);
|
|
154
|
-
|
|
155
|
-
// assign to token collection
|
|
156
|
-
if (value)
|
|
157
|
-
{
|
|
158
|
-
// URL decode the value
|
|
159
|
-
value = decodeURIComponent(value);
|
|
160
|
-
|
|
161
|
-
// assign to tokens
|
|
162
|
-
tokens[key] = value;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
else
|
|
166
|
-
{
|
|
167
|
-
// check for exact match
|
|
168
|
-
if (matchCase() && pattern === value)
|
|
169
|
-
{
|
|
170
|
-
// exact match
|
|
171
|
-
}
|
|
172
|
-
else if (!matchCase() && (pattern+"").toLowerCase() === (value+"").toLowerCase())
|
|
173
|
-
{
|
|
174
|
-
// case insensitive match
|
|
175
|
-
}
|
|
176
|
-
else
|
|
177
|
-
{
|
|
178
|
-
// not a match, thus fail
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
else
|
|
184
|
-
{
|
|
185
|
-
// if we expected a pattern but empty value or we have a value but no pattern
|
|
186
|
-
// then it is a mismatch
|
|
187
|
-
if ((pattern && valueEmpty) || (patternEmpty && value))
|
|
188
|
-
{
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
while (!isEmpty(pattern) && !isEmpty(value));
|
|
194
|
-
|
|
195
|
-
printDebug();
|
|
196
|
-
return tokens;
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
var findMatchingPage = function(pages, offsetPath, callback)
|
|
200
|
-
{
|
|
201
|
-
// walk through the routes and find one that matches this URI and method
|
|
202
|
-
var discoveredTokensArray = [];
|
|
203
|
-
var discoveredPages = [];
|
|
204
|
-
var discoveredPageOffsetPaths = [];
|
|
205
|
-
for (var pageOffsetPath in pages)
|
|
206
|
-
{
|
|
207
|
-
var matchedTokens = executeMatch(pageOffsetPath, offsetPath);
|
|
208
|
-
if (matchedTokens)
|
|
209
|
-
{
|
|
210
|
-
discoveredPages.push(pages[pageOffsetPath]);
|
|
211
|
-
discoveredTokensArray.push(matchedTokens);
|
|
212
|
-
discoveredPageOffsetPaths.push(pageOffsetPath);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// pick the closest page (overrides are sorted first)
|
|
217
|
-
var discoveredPage = null;
|
|
218
|
-
var discoveredTokens = null;
|
|
219
|
-
var discoveredPageOffsetPath = null;
|
|
220
|
-
if (discoveredPages.length > 0)
|
|
221
|
-
{
|
|
222
|
-
// find the index with the greatest # of tokens
|
|
223
|
-
var index = 0;
|
|
224
|
-
var maxLen = 0;
|
|
225
|
-
for (var i = 0; i < discoveredTokensArray.length; i++)
|
|
226
|
-
{
|
|
227
|
-
var len = discoveredTokensArray[i].length;
|
|
228
|
-
if (len > maxLen) {
|
|
229
|
-
index = i;
|
|
230
|
-
maxLen = len;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (discoveredTokensArray[i]["_exact"])
|
|
234
|
-
{
|
|
235
|
-
index = i;
|
|
236
|
-
break;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// hand back the discovered page that matches the greatest # of tokens
|
|
241
|
-
discoveredPage = discoveredPages[index];
|
|
242
|
-
discoveredTokens = discoveredTokensArray[index];
|
|
243
|
-
discoveredPageOffsetPath = discoveredPageOffsetPaths[index];
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
callback(null, discoveredPage, discoveredTokens, discoveredPageOffsetPath);
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
var preloadPages = function(req, finished)
|
|
250
|
-
{
|
|
251
|
-
var WCM_PAGES_CACHE_TTL = getPagesCacheTTL();
|
|
252
|
-
|
|
253
|
-
// retry in 30 seconds if Cloud CMS unavailable for page cache reload
|
|
254
|
-
var WCM_PAGES_CACHE_RETRY_TIME_MS = getPagesCacheRetryTimeout();
|
|
255
|
-
|
|
256
|
-
// set driver timeout to 30 seconds
|
|
257
|
-
var Gitana = require("gitana");
|
|
258
|
-
Gitana.HTTP_TIMEOUT = 30 * 1000;
|
|
259
|
-
|
|
260
|
-
var ensureInvalidate = function(callback) {
|
|
261
|
-
|
|
262
|
-
// allow for forced invalidation via req param
|
|
263
|
-
if (req.query["invalidate"]) {
|
|
264
|
-
req.cache.remove(WCM_PAGES, function() {
|
|
265
|
-
callback();
|
|
266
|
-
});
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
callback();
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
ensureInvalidate(function() {
|
|
274
|
-
|
|
275
|
-
var now = new Date().getTime();
|
|
276
|
-
|
|
277
|
-
req.cache.read(WCM_PAGES, function (err, cachedPages) {
|
|
278
|
-
req.cache.read(WCM_PAGES_CACHE_TIME, function(err, cachedPagesTime) {
|
|
279
|
-
|
|
280
|
-
// if we received cachedPages, try to determine whether they're dirty (in which case we should reload)
|
|
281
|
-
// or whether we can serve them back
|
|
282
|
-
var load = (cachedPages ? false : true);
|
|
283
|
-
if (cachedPages && !cachedPagesTime)
|
|
284
|
-
{
|
|
285
|
-
cachedPages = null;
|
|
286
|
-
load = true;
|
|
287
|
-
}
|
|
288
|
-
if (cachedPages && typeof(cachedPagesTime.ms) === "undefined")
|
|
289
|
-
{
|
|
290
|
-
cachedPages = null;
|
|
291
|
-
load = true;
|
|
292
|
-
}
|
|
293
|
-
if (cachedPages && cachedPagesTime && cachedPagesTime.ms > -1)
|
|
294
|
-
{
|
|
295
|
-
if ((cachedPagesTime.ms + WCM_PAGES_CACHE_TTL) < now)
|
|
296
|
-
{
|
|
297
|
-
load = true;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
if (!load && cachedPages)
|
|
301
|
-
{
|
|
302
|
-
return finished(null, cachedPages);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
// at this point, we MAY need to reload pages
|
|
307
|
-
// we take out a lock and so that only one thread loads at at time per application
|
|
308
|
-
|
|
309
|
-
req.application(function (err, application) {
|
|
310
|
-
|
|
311
|
-
var loadingPagesCacheKey = application._doc + "-wcm-loading-pages";
|
|
312
|
-
_LOCK(null, loadingPagesCacheKey, function (err, releaseLockFn) {
|
|
313
|
-
|
|
314
|
-
// check again inside lock in case another request preloaded this before we arrived
|
|
315
|
-
req.cache.read(WCM_PAGES, function (err, cachedPages) {
|
|
316
|
-
req.cache.read(WCM_PAGES_CACHE_TIME, function (err, cachedPagesTime) {
|
|
317
|
-
|
|
318
|
-
// if we received cachedPages, try to determine whether they're dirty (in which case we should reload)
|
|
319
|
-
// or whether we can serve them back
|
|
320
|
-
|
|
321
|
-
var load = (cachedPages ? false : true);
|
|
322
|
-
if (cachedPages && !cachedPagesTime)
|
|
323
|
-
{
|
|
324
|
-
cachedPages = null;
|
|
325
|
-
load = true;
|
|
326
|
-
}
|
|
327
|
-
if (cachedPages && typeof(cachedPagesTime.ms) === "undefined")
|
|
328
|
-
{
|
|
329
|
-
cachedPages = null;
|
|
330
|
-
load = true;
|
|
331
|
-
}
|
|
332
|
-
if (cachedPages && cachedPagesTime && cachedPagesTime.ms > -1)
|
|
333
|
-
{
|
|
334
|
-
if (cachedPagesTime.ms + WCM_PAGES_CACHE_TTL < now)
|
|
335
|
-
{
|
|
336
|
-
load = true;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
if (!load && cachedPages)
|
|
340
|
-
{
|
|
341
|
-
releaseLockFn();
|
|
342
|
-
return finished(null, cachedPages);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
req.log("Loading Web Pages into cache");
|
|
346
|
-
|
|
347
|
-
// handles after the load either completed or failed
|
|
348
|
-
var afterHandler = function (err, loadedPages) {
|
|
349
|
-
|
|
350
|
-
if (err)
|
|
351
|
-
{
|
|
352
|
-
req.log("Error while loading web pages: " + JSON.stringify(err));
|
|
353
|
-
|
|
354
|
-
// if we have cached pages, just use those
|
|
355
|
-
if (cachedPages)
|
|
356
|
-
{
|
|
357
|
-
req.log("Falling back to using cached pages, will retain for " + WCM_PAGES_CACHE_RETRY_TIME_MS + " ms before trying again");
|
|
358
|
-
req.cache.write(WCM_PAGES_CACHE_TIME, {
|
|
359
|
-
"ms": (new Date().getTime() + WCM_PAGES_CACHE_RETRY_TIME_MS)
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
return finished(null, cachedPages);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
req.log("No cached pages were present, throwing error");
|
|
366
|
-
|
|
367
|
-
return finished(err);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// hand back the loaded pages
|
|
371
|
-
return finished(null, loadedPages);
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
// load all wcm pages from the server
|
|
375
|
-
req.branch(function (err, branch) {
|
|
376
|
-
|
|
377
|
-
if (err)
|
|
378
|
-
{
|
|
379
|
-
// release the lock
|
|
380
|
-
releaseLockFn();
|
|
381
|
-
|
|
382
|
-
// fire the error handler with no loaded pages
|
|
383
|
-
return afterHandler(err);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// build out pages
|
|
387
|
-
var loadedPages = {};
|
|
388
|
-
|
|
389
|
-
var queryTimeMs = new Date().getTime();
|
|
390
|
-
|
|
391
|
-
branch.trap(function (err) {
|
|
392
|
-
|
|
393
|
-
// release the lock
|
|
394
|
-
releaseLockFn();
|
|
395
|
-
|
|
396
|
-
// fire the error handler with no loaded pages
|
|
397
|
-
afterHandler(err);
|
|
398
|
-
|
|
399
|
-
return false;
|
|
400
|
-
}).then(function () {
|
|
401
|
-
|
|
402
|
-
var fns = [];
|
|
403
|
-
|
|
404
|
-
req.log("Querying for WCM web pages");
|
|
405
|
-
|
|
406
|
-
// load all of the pages
|
|
407
|
-
this.trap(function (err) {
|
|
408
|
-
|
|
409
|
-
// release the lock
|
|
410
|
-
releaseLockFn();
|
|
411
|
-
|
|
412
|
-
// fire the error handler with no loaded pages
|
|
413
|
-
afterHandler(err);
|
|
414
|
-
|
|
415
|
-
return false;
|
|
416
|
-
|
|
417
|
-
}).queryNodes({
|
|
418
|
-
_type: "wcm:page",
|
|
419
|
-
_fields: {
|
|
420
|
-
title: 1,
|
|
421
|
-
template: 1,
|
|
422
|
-
templatePath: 1,
|
|
423
|
-
uris: 1
|
|
424
|
-
}
|
|
425
|
-
}, {
|
|
426
|
-
limit: -1,
|
|
427
|
-
metadata: false,
|
|
428
|
-
full: false
|
|
429
|
-
}).each(function () {
|
|
430
|
-
|
|
431
|
-
// THIS = wcm:page
|
|
432
|
-
var page = this;
|
|
433
|
-
|
|
434
|
-
// if the page has a template
|
|
435
|
-
if (page.template)
|
|
436
|
-
{
|
|
437
|
-
var fn = function (branch, page) {
|
|
438
|
-
return function (allDone) {
|
|
439
|
-
|
|
440
|
-
var completionFn = function () {
|
|
441
|
-
|
|
442
|
-
if (page.templatePath)
|
|
443
|
-
{
|
|
444
|
-
if (page.uris)
|
|
445
|
-
{
|
|
446
|
-
// merge into our pages collection
|
|
447
|
-
for (var i = 0; i < page.uris.length; i++)
|
|
448
|
-
{
|
|
449
|
-
loadedPages[page.uris[i]] = page;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
allDone();
|
|
455
|
-
};
|
|
456
|
-
|
|
457
|
-
// is the template a GUID or a path to the template file?
|
|
458
|
-
if ((page.template.indexOf("/") > -1) || (page.template.indexOf(".") > -1))
|
|
459
|
-
{
|
|
460
|
-
page.templatePath = page.template;
|
|
461
|
-
completionFn();
|
|
462
|
-
}
|
|
463
|
-
else
|
|
464
|
-
{
|
|
465
|
-
// load the template
|
|
466
|
-
Chain(branch).trap(function (e2) {
|
|
467
|
-
// skip it
|
|
468
|
-
completionFn();
|
|
469
|
-
return false;
|
|
470
|
-
}).readNode(page.template).then(function () {
|
|
471
|
-
|
|
472
|
-
// THIS = wcm:template
|
|
473
|
-
var template = this;
|
|
474
|
-
|
|
475
|
-
if (template.path)
|
|
476
|
-
{
|
|
477
|
-
page.templatePath = template.path;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
completionFn();
|
|
481
|
-
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
};
|
|
485
|
-
}(branch, page);
|
|
486
|
-
fns.push(fn);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
}).then(function () {
|
|
490
|
-
|
|
491
|
-
req.log("Loading " + fns.length + " web pages");
|
|
492
|
-
|
|
493
|
-
async.series(fns, function (err) {
|
|
494
|
-
|
|
495
|
-
for (var uri in loadedPages)
|
|
496
|
-
{
|
|
497
|
-
req.log("Loaded Web Page -> " + uri);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
req.log("Web Page loading complete");
|
|
501
|
-
|
|
502
|
-
// write to cache
|
|
503
|
-
req.cache.write(WCM_PAGES, loadedPages);
|
|
504
|
-
req.cache.write(WCM_PAGES_CACHE_TIME, {
|
|
505
|
-
"ms": queryTimeMs
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
releaseLockFn();
|
|
509
|
-
|
|
510
|
-
afterHandler(null, loadedPages);
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
});
|
|
514
|
-
});
|
|
515
|
-
});
|
|
516
|
-
});
|
|
517
|
-
});
|
|
518
|
-
});
|
|
519
|
-
});
|
|
520
|
-
});
|
|
521
|
-
});
|
|
522
|
-
});
|
|
523
|
-
};
|
|
524
|
-
|
|
525
|
-
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
526
|
-
//
|
|
527
|
-
// PAGE CACHE (WITH DEPENDENCIES)
|
|
528
|
-
//
|
|
529
|
-
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
530
|
-
|
|
531
|
-
var matchCase = function()
|
|
532
|
-
{
|
|
533
|
-
if (!process.configuration.wcm) {
|
|
534
|
-
process.configuration.wcm = {};
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
if (typeof(process.configuration.wcm.matchCase) === "undefined") {
|
|
538
|
-
process.configuration.wcm.matchCase = true;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
return process.configuration.wcm.matchCase;
|
|
542
|
-
};
|
|
543
|
-
|
|
544
|
-
var isEnabled = function()
|
|
545
|
-
{
|
|
546
|
-
if (!process.configuration.wcm) {
|
|
547
|
-
process.configuration.wcm = {};
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
if (typeof(process.configuration.wcm.enabled) === "undefined") {
|
|
551
|
-
process.configuration.wcm.enabled = false;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
return process.configuration.wcm.enabled;
|
|
555
|
-
};
|
|
556
|
-
|
|
557
|
-
/**
|
|
558
|
-
* Determines whether to use the page cache. We use this cache if we're instructed to and if we're in
|
|
559
|
-
* production model.
|
|
560
|
-
*
|
|
561
|
-
* @returns {boolean}
|
|
562
|
-
*/
|
|
563
|
-
var isPageCacheEnabled = function(req)
|
|
564
|
-
{
|
|
565
|
-
if (!process.configuration.wcm) {
|
|
566
|
-
process.configuration.wcm = {};
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
if (typeof(process.configuration.wcm.enabled) === "undefined") {
|
|
570
|
-
process.configuration.wcm.enabled = false;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// some helpful compatibility
|
|
574
|
-
if (typeof(process.configuration.wcm.enabled) === "false") {
|
|
575
|
-
process.configuration.wcm.enabled = false;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
if (typeof(process.configuration.wcm.cache) === "undefined") {
|
|
579
|
-
process.configuration.wcm.cache = false;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
// some helpful compatibility
|
|
583
|
-
if (typeof(process.configuration.wcm.enabled) === "false") {
|
|
584
|
-
process.configuration.wcm.enabled = false;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
if (process.env.FORCE_CLOUDCMS_WCM_PAGE_CACHE === "true")
|
|
589
|
-
{
|
|
590
|
-
process.configuration.wcm.cache = true;
|
|
591
|
-
}
|
|
592
|
-
else if (process.env.FORCE_CLOUDCMS_WCM_PAGE_CACHE === "false")
|
|
593
|
-
{
|
|
594
|
-
process.configuration.wcm.cache = false;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
if (process.env.CLOUDCMS_APPSERVER_MODE !== "production")
|
|
598
|
-
{
|
|
599
|
-
process.configuration.wcm.cache = false;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
var enabled = (process.configuration.wcm.enabled && process.configuration.wcm.cache);
|
|
603
|
-
|
|
604
|
-
// is it disabled per request?
|
|
605
|
-
if (req && req.disablePageCache)
|
|
606
|
-
{
|
|
607
|
-
enabled = false;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
return enabled;
|
|
611
|
-
};
|
|
612
|
-
|
|
613
|
-
var getPageCacheKeyConfig = function(req)
|
|
614
|
-
{
|
|
615
|
-
if (!process.configuration.wcm) {
|
|
616
|
-
process.configuration.wcm = {};
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
if (!process.configuration.wcm.cacheKey) {
|
|
620
|
-
process.configuration.wcm.cacheKey = {};
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
if (!process.configuration.wcm.cacheKey.params) {
|
|
624
|
-
process.configuration.wcm.cacheKey.params = {};
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
if (!process.configuration.wcm.cacheKey.params.includes) {
|
|
628
|
-
process.configuration.wcm.cacheKey.params.includes = [];
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
if (!process.configuration.wcm.cacheKey.params.excludes) {
|
|
632
|
-
process.configuration.wcm.cacheKey.params.excludes = [];
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
return process.configuration.wcm.cacheKey;
|
|
636
|
-
};
|
|
637
|
-
|
|
638
|
-
var getPagesCacheTTL = function()
|
|
639
|
-
{
|
|
640
|
-
if (!process.configuration.wcm) {
|
|
641
|
-
process.configuration.wcm = {};
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
if (!(process.configuration.wcm.pageCacheTTL) || process.configuration.wcm.pageCacheTTL === -1)
|
|
645
|
-
{
|
|
646
|
-
// assume 60 seconds (development mode)
|
|
647
|
-
process.configuration.wcm.pageCacheTTL = 60 * 1000;
|
|
648
|
-
|
|
649
|
-
// in production, assume 24 hours
|
|
650
|
-
if (process.env.CLOUDCMS_APPSERVER_MODE === "production")
|
|
651
|
-
{
|
|
652
|
-
process.configuration.wcm.pageCacheTTL = (24 * 60 * 60) * 1000;
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
return process.configuration.wcm.pageCacheTTL;
|
|
657
|
-
};
|
|
658
|
-
|
|
659
|
-
var getPagesCacheRetryTimeout = function()
|
|
660
|
-
{
|
|
661
|
-
if (!process.configuration.wcm) {
|
|
662
|
-
process.configuration.wcm = {};
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
if (!(process.configuration.wcm.pageCacheRetryTimeout) || process.configuration.wcm.pageCacheRetryTimeout === -1)
|
|
666
|
-
{
|
|
667
|
-
// default to 30 seconds
|
|
668
|
-
process.configuration.wcm.pageCacheRetryTimeout = 30 * 1000;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
return process.configuration.wcm.pageCacheRetryTimeout;
|
|
672
|
-
};
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
var cleanup = function(store, pageFilePath, cacheFilePath, afterCleanup)
|
|
676
|
-
{
|
|
677
|
-
cloudcms.safeRemove(store, pageFilePath, function() {
|
|
678
|
-
cloudcms.safeRemove(store, cacheFilePath, function() {
|
|
679
|
-
|
|
680
|
-
if (afterCleanup) {
|
|
681
|
-
afterCleanup();
|
|
682
|
-
}
|
|
683
|
-
});
|
|
684
|
-
});
|
|
685
|
-
};
|
|
686
|
-
|
|
687
|
-
var handleCachePageWrite = function(req, res, descriptor, pageBasePath, dependencies, text, callback)
|
|
688
|
-
{
|
|
689
|
-
// if page cache isn't enabled, return right away
|
|
690
|
-
if (!isPageCacheEnabled(req))
|
|
691
|
-
{
|
|
692
|
-
return callback();
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
var contentStore = req.stores.content;
|
|
696
|
-
|
|
697
|
-
// mark the rendition
|
|
698
|
-
if (dependencies)
|
|
699
|
-
{
|
|
700
|
-
process.log("marking rendition from wcm, " + (descriptor.fragmentId ? "fragmentId: " + descriptor.fragmentId : "no fragmentId"));
|
|
701
|
-
renditions.markRendition(req, descriptor, dependencies, function (err) {
|
|
702
|
-
|
|
703
|
-
// if we got an error writing the page, then we have to roll back and invalidate disk cache
|
|
704
|
-
if (err)
|
|
705
|
-
{
|
|
706
|
-
process.log("Caught error on WCM markRendition, invalidating: " + pageBasePath + ", err:" + err);
|
|
707
|
-
_handleCachePageInvalidate(contentStore, pageBasePath, function() {
|
|
708
|
-
// done
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
var pageFilePath = path.join(pageBasePath, "page.html");
|
|
716
|
-
var cacheFilePath = cloudcms.toCacheFilePath(pageFilePath);
|
|
717
|
-
|
|
718
|
-
// take out a lock so that only one "request" can write to cache at a time for this path
|
|
719
|
-
_LOCK(contentStore, _lock_identifier(pageBasePath), function(err, releaseLockFn) {
|
|
720
|
-
|
|
721
|
-
// write page file
|
|
722
|
-
contentStore.writeFile(pageFilePath, text, function (err) {
|
|
723
|
-
|
|
724
|
-
if (err)
|
|
725
|
-
{
|
|
726
|
-
// failed to write page file, start cleanup
|
|
727
|
-
return cleanup(contentStore, pageFilePath, cacheFilePath, function() {
|
|
728
|
-
releaseLockFn();
|
|
729
|
-
callback(err);
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
var cacheInfo = cloudcms.buildCacheInfo(res);
|
|
734
|
-
if (!cacheInfo)
|
|
735
|
-
{
|
|
736
|
-
cacheInfo = {
|
|
737
|
-
"mimetype": "text/html"
|
|
738
|
-
};
|
|
739
|
-
}
|
|
740
|
-
if (!cacheInfo.mimetype)
|
|
741
|
-
{
|
|
742
|
-
cacheInfo.mimetype = "text/html";
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
// write cache info file
|
|
746
|
-
contentStore.writeFile(cacheFilePath, JSON.stringify(cacheInfo, null, " "), function (err) {
|
|
747
|
-
|
|
748
|
-
if (err)
|
|
749
|
-
{
|
|
750
|
-
// failed to write page file, start cleanup
|
|
751
|
-
return cleanup(contentStore, pageFilePath, cacheFilePath, function() {
|
|
752
|
-
releaseLockFn();
|
|
753
|
-
callback(err);
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
releaseLockFn();
|
|
758
|
-
callback(null, pageFilePath, cacheInfo);
|
|
759
|
-
});
|
|
760
|
-
});
|
|
761
|
-
});
|
|
762
|
-
};
|
|
763
|
-
|
|
764
|
-
var handleCachePageRead = function(req, descriptor, pageBasePath, callback)
|
|
765
|
-
{
|
|
766
|
-
if (!isPageCacheEnabled(req))
|
|
767
|
-
{
|
|
768
|
-
return callback();
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
var contentStore = req.stores.content;
|
|
772
|
-
var pageFilePath = path.join(pageBasePath, "page.html");
|
|
773
|
-
var cacheFilePath = cloudcms.toCacheFilePath(pageFilePath);
|
|
774
|
-
|
|
775
|
-
// take out a lock so that only one "request" can read from cache at a time for this path
|
|
776
|
-
_LOCK(contentStore, _lock_identifier(pageBasePath), function(err, releaseLockFn) {
|
|
777
|
-
|
|
778
|
-
contentStore.readFile(cacheFilePath, function(err, cacheInfoString) {
|
|
779
|
-
|
|
780
|
-
if (err)
|
|
781
|
-
{
|
|
782
|
-
// failed to write page file, start cleanup
|
|
783
|
-
return cleanup(contentStore, pageFilePath, cacheFilePath, function() {
|
|
784
|
-
releaseLockFn();
|
|
785
|
-
callback(err);
|
|
786
|
-
});
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
util.safeReadStream(contentStore, pageFilePath, function (err, stream) {
|
|
790
|
-
|
|
791
|
-
if (err)
|
|
792
|
-
{
|
|
793
|
-
// failed to write page file, start cleanup
|
|
794
|
-
return cleanup(contentStore, pageFilePath, cacheFilePath, function() {
|
|
795
|
-
releaseLockFn();
|
|
796
|
-
callback(err);
|
|
797
|
-
});
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
// make sure cache info is valid
|
|
801
|
-
// we do this in case a non-JSON string comes back, we don't want to bomb out the server
|
|
802
|
-
var cacheInfo = null;
|
|
803
|
-
try {
|
|
804
|
-
cacheInfo = JSON.parse(cacheInfoString)
|
|
805
|
-
} catch (e) {
|
|
806
|
-
// swallow
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
if (!cacheInfo)
|
|
810
|
-
{
|
|
811
|
-
// failed to write page file, start cleanup
|
|
812
|
-
return cleanup(contentStore, pageFilePath, cacheFilePath, function() {
|
|
813
|
-
releaseLockFn();
|
|
814
|
-
callback({
|
|
815
|
-
"message": "Failed to parse cache info for path: " + cacheFilePath
|
|
816
|
-
});
|
|
817
|
-
});
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
releaseLockFn();
|
|
821
|
-
callback(null, stream, cacheInfo);
|
|
822
|
-
});
|
|
823
|
-
});
|
|
824
|
-
});
|
|
825
|
-
};
|
|
826
|
-
|
|
827
|
-
var handleCachePageInvalidate = function(host, repositoryId, branchId, pageCacheKey, callback)
|
|
828
|
-
{
|
|
829
|
-
// this is the path to the page folder
|
|
830
|
-
// if we blow away this folder, we blow away all page fragments as well
|
|
831
|
-
var pageBasePath = path.join("wcm");
|
|
832
|
-
if (repositoryId)
|
|
833
|
-
{
|
|
834
|
-
pageBasePath = path.join(pageBasePath, "repositories", repositoryId);
|
|
835
|
-
}
|
|
836
|
-
if (branchId)
|
|
837
|
-
{
|
|
838
|
-
pageBasePath = path.join(pageBasePath, "branches", branchId);
|
|
839
|
-
}
|
|
840
|
-
if (pageCacheKey)
|
|
841
|
-
{
|
|
842
|
-
pageBasePath = path.join(pageBasePath, "pages", pageCacheKey);
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
// list all of the hosts
|
|
846
|
-
var stores = require("../stores/stores");
|
|
847
|
-
stores.produce(host, function (err, stores) {
|
|
848
|
-
|
|
849
|
-
if (err) {
|
|
850
|
-
return callback(err);
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
_handleCachePageInvalidate(stores.content, pageBasePath, function() {
|
|
854
|
-
callback();
|
|
855
|
-
});
|
|
856
|
-
});
|
|
857
|
-
};
|
|
858
|
-
|
|
859
|
-
var _handleCachePageInvalidate = function(contentStore, pageBasePath, callback)
|
|
860
|
-
{
|
|
861
|
-
_LOCK(contentStore, _lock_identifier(pageBasePath), function(err, releaseLockFn) {
|
|
862
|
-
|
|
863
|
-
contentStore.existsDirectory(pageBasePath, function (exists) {
|
|
864
|
-
|
|
865
|
-
if (!exists)
|
|
866
|
-
{
|
|
867
|
-
releaseLockFn();
|
|
868
|
-
return callback();
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
contentStore.removeDirectory(pageBasePath, function () {
|
|
872
|
-
releaseLockFn();
|
|
873
|
-
callback();
|
|
874
|
-
});
|
|
875
|
-
});
|
|
876
|
-
});
|
|
877
|
-
};
|
|
878
|
-
|
|
879
|
-
var _lock_identifier = function()
|
|
880
|
-
{
|
|
881
|
-
var args = Array.prototype.slice.call(arguments);
|
|
882
|
-
|
|
883
|
-
return args.join("_");
|
|
884
|
-
};
|
|
885
|
-
|
|
886
|
-
var _LOCK = function(store, lockIdentifier, workFunction)
|
|
887
|
-
{
|
|
888
|
-
var lockKeys = [];
|
|
889
|
-
if (store) {
|
|
890
|
-
lockKeys.push(store.id);
|
|
891
|
-
}
|
|
892
|
-
if (lockIdentifier) {
|
|
893
|
-
lockKeys.push(lockIdentifier);
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
process.locks.lock(lockKeys.join("_"), workFunction);
|
|
897
|
-
};
|
|
898
|
-
|
|
899
|
-
var bindSubscriptions = function()
|
|
900
|
-
{
|
|
901
|
-
if (process.broadcast)
|
|
902
|
-
{
|
|
903
|
-
// NOTE: all page rendition invalidation based on changes to nodes happens on the server side within the
|
|
904
|
-
// Cloud CMS API itself. Cloud CMS maintains a master record of how page renditions and nodes are related.
|
|
905
|
-
// When a node changes in Cloud CMS, the API finds any page renditions that need to invalidate and then
|
|
906
|
-
// sends those along as page rendition invalidation events. These are handled here...
|
|
907
|
-
|
|
908
|
-
// LISTEN: "invalidate_page_rendition"
|
|
909
|
-
process.broadcast.subscribe("invalidate_page_rendition", function (message, channel, invalidationDone) {
|
|
910
|
-
|
|
911
|
-
if (!invalidationDone) {
|
|
912
|
-
invalidationDone = function() { };
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
process.log("HEARD: invalidate_page_rendition");
|
|
916
|
-
|
|
917
|
-
var clearFragmentCacheFn = function(message)
|
|
918
|
-
{
|
|
919
|
-
var pageCacheKey = message.pageCacheKey;
|
|
920
|
-
var fragmentCacheKey = message.fragmentCacheKey;
|
|
921
|
-
|
|
922
|
-
var scope = message.scope;
|
|
923
|
-
var host = message.host;
|
|
924
|
-
|
|
925
|
-
var repositoryId = message.repositoryId;
|
|
926
|
-
var branchId = message.branchId;
|
|
927
|
-
// at the moment, caching on disk uses "master" for the master branch instead of the actual branch id
|
|
928
|
-
var isMasterBranch = message.isMasterBranch;
|
|
929
|
-
|
|
930
|
-
return function(done3)
|
|
931
|
-
{
|
|
932
|
-
if (scope === "FRAGMENT" || scope === "ALL")
|
|
933
|
-
{
|
|
934
|
-
var buildFragmentsBasePath = function(branchId) {
|
|
935
|
-
if (pageCacheKey) {
|
|
936
|
-
return path.join("wcm", "repositories", repositoryId, "branches", branchId, "pages", pageCacheKey, "fragments");
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
return path.join("duster", "repositories", repositoryId, "branches", branchId, "fragments");
|
|
940
|
-
};
|
|
941
|
-
|
|
942
|
-
if (support.isFragmentCacheEnabled())
|
|
943
|
-
{
|
|
944
|
-
// for master branch, we make a silent attempt using "master" as the branch ID
|
|
945
|
-
if (isMasterBranch)
|
|
946
|
-
{
|
|
947
|
-
var fragmentsBasePath = buildFragmentsBasePath("master");
|
|
948
|
-
support.handleCacheFragmentInvalidate(host, fragmentsBasePath, fragmentCacheKey, function(err) {
|
|
949
|
-
// done
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
var fragmentsBasePath = buildFragmentsBasePath(branchId);
|
|
954
|
-
support.handleCacheFragmentInvalidate(host, fragmentsBasePath, fragmentCacheKey, function(err, invalidatedPath) {
|
|
955
|
-
|
|
956
|
-
if (!err) {
|
|
957
|
-
process.log(" > Invalidated fragment [host: " + host + ", path: " + invalidatedPath + "]");
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
return done3();
|
|
961
|
-
});
|
|
962
|
-
}
|
|
963
|
-
else
|
|
964
|
-
{
|
|
965
|
-
return done3();
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
else
|
|
969
|
-
{
|
|
970
|
-
done3();
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
}(message);
|
|
974
|
-
|
|
975
|
-
var clearPageCacheFn = function(message)
|
|
976
|
-
{
|
|
977
|
-
var pageCacheKey = message.pageCacheKey;
|
|
978
|
-
var scope = message.scope;
|
|
979
|
-
var host = message.host;
|
|
980
|
-
|
|
981
|
-
var repositoryId = message.repositoryId;
|
|
982
|
-
var branchId = message.branchId;
|
|
983
|
-
// at the moment, caching on disk uses "master" for the master branch instead of the actual branch id
|
|
984
|
-
var isMasterBranch = message.isMasterBranch;
|
|
985
|
-
|
|
986
|
-
return function(done2)
|
|
987
|
-
{
|
|
988
|
-
if (scope === "PAGE" || scope === "ALL")
|
|
989
|
-
{
|
|
990
|
-
if (isPageCacheEnabled())
|
|
991
|
-
{
|
|
992
|
-
// for master branch, we make a silent attempt using "master" as the branch ID
|
|
993
|
-
if (isMasterBranch)
|
|
994
|
-
{
|
|
995
|
-
handleCachePageInvalidate(host, repositoryId, "master", pageCacheKey, function() {
|
|
996
|
-
|
|
997
|
-
});
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
handleCachePageInvalidate(host, repositoryId, branchId, pageCacheKey, function(err) {
|
|
1001
|
-
|
|
1002
|
-
if (!err) {
|
|
1003
|
-
process.log(" > Invalidated page [host: " + host + ", repository: " + repositoryId + ", branch: " + branchId + ", page: " + pageCacheKey + "]");
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
return done2();
|
|
1007
|
-
});
|
|
1008
|
-
}
|
|
1009
|
-
else
|
|
1010
|
-
{
|
|
1011
|
-
return done2();
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
else
|
|
1015
|
-
{
|
|
1016
|
-
done2();
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
}
|
|
1020
|
-
}(message);
|
|
1021
|
-
|
|
1022
|
-
async.waterfall([
|
|
1023
|
-
clearFragmentCacheFn,
|
|
1024
|
-
clearPageCacheFn
|
|
1025
|
-
], function() {
|
|
1026
|
-
invalidationDone();
|
|
1027
|
-
});
|
|
1028
|
-
|
|
1029
|
-
});
|
|
1030
|
-
|
|
1031
|
-
// LISTEN: "invalidate_all_page_renditions"
|
|
1032
|
-
process.broadcast.subscribe("invalidate_all_page_renditions", function (message, channel, invalidationDone) {
|
|
1033
|
-
|
|
1034
|
-
if (!invalidationDone) {
|
|
1035
|
-
invalidationDone = function() { };
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
// process.log("HEARD: invalidate_all_page_renditions");
|
|
1039
|
-
|
|
1040
|
-
var clearFragmentCacheFn = function(message)
|
|
1041
|
-
{
|
|
1042
|
-
//var host = message.host;
|
|
1043
|
-
var scope = message.scope;
|
|
1044
|
-
|
|
1045
|
-
return function(done2)
|
|
1046
|
-
{
|
|
1047
|
-
if (scope === "FRAGMENT" || scope === "ALL")
|
|
1048
|
-
{
|
|
1049
|
-
// TODO: fragment level invalidation
|
|
1050
|
-
return done2();
|
|
1051
|
-
}
|
|
1052
|
-
else
|
|
1053
|
-
{
|
|
1054
|
-
done2();
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
}(message);
|
|
1058
|
-
|
|
1059
|
-
var clearPageCacheFn = function(message)
|
|
1060
|
-
{
|
|
1061
|
-
var host = message.host;
|
|
1062
|
-
var scope = message.scope;
|
|
1063
|
-
|
|
1064
|
-
return function(done2)
|
|
1065
|
-
{
|
|
1066
|
-
if (scope === "PAGE" || scope === "ALL")
|
|
1067
|
-
{
|
|
1068
|
-
if (isPageCacheEnabled())
|
|
1069
|
-
{
|
|
1070
|
-
handleCachePageInvalidate(host, null, null, null, function(err) {
|
|
1071
|
-
|
|
1072
|
-
if (!err) {
|
|
1073
|
-
process.log(" > Invalidated all pages [host: " + host + "]");
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
return done2();
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
else
|
|
1080
|
-
{
|
|
1081
|
-
return done2();
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
}
|
|
1086
|
-
}(message);
|
|
1087
|
-
|
|
1088
|
-
async.waterfall([
|
|
1089
|
-
clearFragmentCacheFn,
|
|
1090
|
-
clearPageCacheFn
|
|
1091
|
-
], function() {
|
|
1092
|
-
invalidationDone();
|
|
1093
|
-
});
|
|
1094
|
-
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
};
|
|
1098
|
-
|
|
1099
|
-
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
1100
|
-
//
|
|
1101
|
-
// RESULTING OBJECT
|
|
1102
|
-
//
|
|
1103
|
-
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
1104
|
-
|
|
1105
|
-
var r = {};
|
|
1106
|
-
|
|
1107
|
-
r.wcmInterceptor = function()
|
|
1108
|
-
{
|
|
1109
|
-
return util.createInterceptor("wcm", function(req, res, next, stores, cache, configuration) {
|
|
1110
|
-
|
|
1111
|
-
if (!isEnabled())
|
|
1112
|
-
{
|
|
1113
|
-
return next();
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
if (!req.gitana)
|
|
1117
|
-
{
|
|
1118
|
-
return next();
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
if (req.method.toLowerCase() !== "get")
|
|
1122
|
-
{
|
|
1123
|
-
return next();
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
// ensures that the WCM PAGES cache is preloaded for the current branch
|
|
1127
|
-
// pages must be loaded ahead of time so that matching can be performed
|
|
1128
|
-
preloadPages(req, function(err, pages) {
|
|
1129
|
-
|
|
1130
|
-
if (err)
|
|
1131
|
-
{
|
|
1132
|
-
return next(err);
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
var offsetPath = req.path;
|
|
1136
|
-
|
|
1137
|
-
// find a page for this path
|
|
1138
|
-
// this looks at wcm:page urls and finds a best fit, extracting tokens
|
|
1139
|
-
findMatchingPage(pages, offsetPath, function(err, page, tokens, matchingPath) {
|
|
1140
|
-
|
|
1141
|
-
if (err)
|
|
1142
|
-
{
|
|
1143
|
-
req.log("An error occurred while attempting to match path: " + offsetPath);
|
|
1144
|
-
|
|
1145
|
-
return next();
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
// if we found a page, then store it on the request and adjust the request to reflect things we extract
|
|
1149
|
-
if (page)
|
|
1150
|
-
{
|
|
1151
|
-
req.page = page;
|
|
1152
|
-
|
|
1153
|
-
// ensure empty set of page attributes
|
|
1154
|
-
if (!req.pageAttributes) {
|
|
1155
|
-
req.pageAttributes = {};
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
req.pageTokens = tokens ? tokens : {};
|
|
1159
|
-
req.pageMatchingPath = matchingPath;
|
|
1160
|
-
|
|
1161
|
-
// override the param() method so that token values are handed back as well
|
|
1162
|
-
var _param = req.param;
|
|
1163
|
-
req.param = function(name) {
|
|
1164
|
-
|
|
1165
|
-
var v = undefined;
|
|
1166
|
-
|
|
1167
|
-
if (this.pageTokens)
|
|
1168
|
-
{
|
|
1169
|
-
v = this.pageTokens[name];
|
|
1170
|
-
}
|
|
1171
|
-
if (!v)
|
|
1172
|
-
{
|
|
1173
|
-
v = _param.call(this, name);
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
return v;
|
|
1177
|
-
};
|
|
1178
|
-
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
next();
|
|
1182
|
-
});
|
|
1183
|
-
});
|
|
1184
|
-
});
|
|
1185
|
-
};
|
|
1186
|
-
|
|
1187
|
-
/**
|
|
1188
|
-
* Provides WCM page retrieval from Cloud CMS.
|
|
1189
|
-
*
|
|
1190
|
-
* @param configuration
|
|
1191
|
-
* @return {Function}
|
|
1192
|
-
*/
|
|
1193
|
-
r.wcmHandler = function()
|
|
1194
|
-
{
|
|
1195
|
-
// bind listeners for broadcast events
|
|
1196
|
-
bindSubscriptions();
|
|
1197
|
-
|
|
1198
|
-
// wcm handler
|
|
1199
|
-
return util.createHandler("wcm", function(req, res, next, stores, cache, configuration) {
|
|
1200
|
-
|
|
1201
|
-
if (!isEnabled())
|
|
1202
|
-
{
|
|
1203
|
-
return next();
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
if (!req.gitana)
|
|
1207
|
-
{
|
|
1208
|
-
return next();
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
var page = req.page;
|
|
1212
|
-
if (!page)
|
|
1213
|
-
{
|
|
1214
|
-
return next();
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
var offsetPath = req.path;
|
|
1218
|
-
|
|
1219
|
-
var tokens = req.pageTokens;
|
|
1220
|
-
var matchingPath = req.pageMatchingPath;
|
|
1221
|
-
|
|
1222
|
-
var webStore = stores.web;
|
|
1223
|
-
|
|
1224
|
-
// either serve the page back from cache or run dust over it
|
|
1225
|
-
// after dust is run over it, we can stuff it into cache for the next request to benefit from
|
|
1226
|
-
var descriptor = {
|
|
1227
|
-
"url": req.protocol + "://" + req.domainHost + offsetPath,
|
|
1228
|
-
"host": req.domainHost,
|
|
1229
|
-
"protocol": req.protocol,
|
|
1230
|
-
"path": offsetPath,
|
|
1231
|
-
"params": req.query ? req.query : {},
|
|
1232
|
-
"pageAttributes": req.pageAttributes ? req.pageAttributes : {},
|
|
1233
|
-
"headers": req.headers,
|
|
1234
|
-
"matchingTokens": tokens,
|
|
1235
|
-
"matchingPath": matchingPath,
|
|
1236
|
-
"matchingUrl": req.protocol + "://" + req.domainHost + matchingPath,
|
|
1237
|
-
"matchingPageId": page._doc,
|
|
1238
|
-
"matchingPageTitle": page.title ? page.title : page._doc,
|
|
1239
|
-
"scope": "PAGE"
|
|
1240
|
-
};
|
|
1241
|
-
|
|
1242
|
-
if (req.repositoryId) {
|
|
1243
|
-
descriptor.repositoryId = req.repositoryId;
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
if (req.branchId) {
|
|
1247
|
-
descriptor.branchId = req.branchId;
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
// support stripping out specific parameters
|
|
1251
|
-
var cacheKeyConfig = getPageCacheKeyConfig(req);
|
|
1252
|
-
if (cacheKeyConfig.params)
|
|
1253
|
-
{
|
|
1254
|
-
// exclude all by default?
|
|
1255
|
-
if (cacheKeyConfig.params.excludeAll)
|
|
1256
|
-
{
|
|
1257
|
-
descriptor.params = {};
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
// exclude specific parameters
|
|
1261
|
-
if (cacheKeyConfig.params.excludes && cacheKeyConfig.params.excludes.length > 0)
|
|
1262
|
-
{
|
|
1263
|
-
for (var i = 0; i < cacheKeyConfig.params.excludes.length; i++)
|
|
1264
|
-
{
|
|
1265
|
-
delete descriptor.params[cacheKeyConfig.params.excludes[i]];
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
// include specific parameters
|
|
1270
|
-
if (cacheKeyConfig.params.includes && cacheKeyConfig.params.includes.length > 0)
|
|
1271
|
-
{
|
|
1272
|
-
var keepers = {};
|
|
1273
|
-
|
|
1274
|
-
for (var i = 0; i < cacheKeyConfig.params.includes.length; i++)
|
|
1275
|
-
{
|
|
1276
|
-
var v = descriptor.params[cacheKeyConfig.params.includes[i]];
|
|
1277
|
-
if (v)
|
|
1278
|
-
{
|
|
1279
|
-
keepers[cacheKeyConfig.params.includes[i]] = v;
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
descriptor.params = keepers;
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
// generate a page cache key from the descriptor (and store on the descriptor)
|
|
1288
|
-
var pageCacheKey = util.generatePageCacheKey(descriptor);
|
|
1289
|
-
descriptor.pageCacheKey = pageCacheKey;
|
|
1290
|
-
|
|
1291
|
-
// base path for storage
|
|
1292
|
-
var pageBasePath = path.join("wcm", "repositories", req.repositoryId, "branches", req.branchId, "pages", pageCacheKey);
|
|
1293
|
-
|
|
1294
|
-
// is this already in cache?
|
|
1295
|
-
handleCachePageRead(req, descriptor, pageBasePath, function(err, readStream, cacheInfo) {
|
|
1296
|
-
|
|
1297
|
-
if (!err && readStream)
|
|
1298
|
-
{
|
|
1299
|
-
// yes, we found it in cache, so we'll simply pipe it back from disk
|
|
1300
|
-
|
|
1301
|
-
// log cache hit
|
|
1302
|
-
if (isPageCacheEnabled(req))
|
|
1303
|
-
{
|
|
1304
|
-
req.log("Page Cache Hit: " + req.url);
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
// set "cloudcms-cache-hit" header
|
|
1308
|
-
util.setHeaderOnce(res, "cloudcms-wcm-cache-hit", "true");
|
|
1309
|
-
|
|
1310
|
-
// SPECIAL HANDLING FOR OFFSET PATH "/" TO SUPPORT HTML CONTENT TYPE HEADER
|
|
1311
|
-
if (offsetPath === "/") {
|
|
1312
|
-
offsetPath += "index.html";
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
util.status(res, res.statusCode);
|
|
1316
|
-
util.applyResponseContentType(res, cacheInfo, offsetPath);
|
|
1317
|
-
return readStream.pipe(res);
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
// otherwise, we need to run dust...
|
|
1321
|
-
|
|
1322
|
-
// log cache miss
|
|
1323
|
-
if (isPageCacheEnabled(req))
|
|
1324
|
-
{
|
|
1325
|
-
req.log("Page Cache Miss: " + req.url);
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
// set "cloudcms-cache-hit" header
|
|
1329
|
-
util.setHeaderOnce(res, "cloudcms-wcm-cache-hit", "false");
|
|
1330
|
-
|
|
1331
|
-
var runDust = function()
|
|
1332
|
-
{
|
|
1333
|
-
// TODO: block here in case another thread is trying to dust this page at the same time?
|
|
1334
|
-
|
|
1335
|
-
if (!req.helpers) {
|
|
1336
|
-
req.helpers = {};
|
|
1337
|
-
}
|
|
1338
|
-
req.helpers.page = page;
|
|
1339
|
-
|
|
1340
|
-
// build the model
|
|
1341
|
-
var model = req.model;
|
|
1342
|
-
if (!model) {
|
|
1343
|
-
model = {};
|
|
1344
|
-
}
|
|
1345
|
-
model.page = {};
|
|
1346
|
-
model.template = {
|
|
1347
|
-
"path": page.templatePath
|
|
1348
|
-
};
|
|
1349
|
-
model.request = {
|
|
1350
|
-
"tokens": tokens,
|
|
1351
|
-
"matchingPath": matchingPath
|
|
1352
|
-
};
|
|
1353
|
-
|
|
1354
|
-
// model stores reference to page descriptor
|
|
1355
|
-
model._page_descriptor = descriptor;
|
|
1356
|
-
|
|
1357
|
-
// model stores a base path that we'll use for storage of fragments
|
|
1358
|
-
model._fragments_base_path = path.join(pageBasePath, "fragments");
|
|
1359
|
-
|
|
1360
|
-
// page keys to copy
|
|
1361
|
-
for (var k in page) {
|
|
1362
|
-
if (k.indexOf("_") === 0) {
|
|
1363
|
-
} else {
|
|
1364
|
-
model.page[k] = page[k];
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
// set _doc and id (equivalent)
|
|
1369
|
-
model.page._doc = model.page.id = page._doc;
|
|
1370
|
-
|
|
1371
|
-
// dust it up
|
|
1372
|
-
duster.execute(req, webStore, page.templatePath, model, function (err, text, dependencies, stats) {
|
|
1373
|
-
|
|
1374
|
-
if (err)
|
|
1375
|
-
{
|
|
1376
|
-
// something screwed up during the dust execution
|
|
1377
|
-
// it might be a bad template
|
|
1378
|
-
req.log("Failed to process dust template: " + page.templatePath + " for model: " + JSON.stringify(model, null, " ") + ", err: " + JSON.stringify(err, null, " "));
|
|
1379
|
-
|
|
1380
|
-
// assume a 500 error code
|
|
1381
|
-
if (!err.status) {
|
|
1382
|
-
err.status = 500;
|
|
1383
|
-
}
|
|
1384
|
-
if (!err.message) {
|
|
1385
|
-
err.message = "There was a problem rendering this web page, please contact your administrator";
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
return next(err);
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
// set "cloudcms-cache-hit" header
|
|
1392
|
-
util.setHeaderOnce(res, "cloudcms-wcm-cache-write", "true");
|
|
1393
|
-
|
|
1394
|
-
// we now have the result (text) and the dependencies that this page flagged (dependencies)
|
|
1395
|
-
// use these to write to the page cache
|
|
1396
|
-
// don't wait for this to complete, assume it completes in background
|
|
1397
|
-
handleCachePageWrite(req, res, descriptor, pageBasePath, dependencies, text, function(err, pageFilePath, pageCacheInfo) {
|
|
1398
|
-
|
|
1399
|
-
//res.status(200);
|
|
1400
|
-
//res.send(text);
|
|
1401
|
-
|
|
1402
|
-
req.log("Page Cache Write: " + req.url);
|
|
1403
|
-
});
|
|
1404
|
-
|
|
1405
|
-
// SPECIAL HANDLING FOR OFFSET PATH "/" TO SUPPORT HTML CONTENT TYPE HEADER
|
|
1406
|
-
if (offsetPath === "/") {
|
|
1407
|
-
offsetPath += "index.html";
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
// send back results right away
|
|
1411
|
-
util.status(res, res.statusCode);
|
|
1412
|
-
util.applyResponseContentType(res, null, offsetPath);
|
|
1413
|
-
util.setHeaderOnce(res, "cloudcms-dust-execution-time", stats.dustExecutionTime);
|
|
1414
|
-
res.send(text);
|
|
1415
|
-
});
|
|
1416
|
-
};
|
|
1417
|
-
runDust();
|
|
1418
|
-
});
|
|
1419
|
-
});
|
|
1420
|
-
};
|
|
1421
|
-
|
|
1422
|
-
/**
|
|
1423
|
-
* Manual method for resetting cache.
|
|
1424
|
-
*
|
|
1425
|
-
* @param host
|
|
1426
|
-
* @param repositoryId
|
|
1427
|
-
* @param branchId
|
|
1428
|
-
* @param pageCacheKey
|
|
1429
|
-
* @param callback
|
|
1430
|
-
*/
|
|
1431
|
-
r.resetCache = function(host, repositoryId, branchId, pageCacheKey, callback)
|
|
1432
|
-
{
|
|
1433
|
-
handleCachePageInvalidate(host, repositoryId, branchId, pageCacheKey, callback);
|
|
1434
|
-
};
|
|
1435
|
-
|
|
1436
|
-
return r;
|
|
1437
|
-
}();
|