wiki-server 0.25.9 → 0.26.0-rc.0

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.
@@ -1,246 +0,0 @@
1
- ###
2
- * Federated Wiki : Node Server
3
- *
4
- * Copyright Ward Cunningham and other contributors
5
- * Licensed under the MIT license.
6
- * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt
7
- ###
8
-
9
- # **sitemap.coffee**
10
-
11
- fs = require 'fs'
12
- path = require 'path'
13
- events = require 'events'
14
- writeFileAtomic = require 'write-file-atomic'
15
- xml2js = require 'xml2js'
16
-
17
- synopsis = require 'wiki-client/lib/synopsis'
18
-
19
- asSlug = (name) ->
20
- name.replace(/\s/g, '-').replace(/[^A-Za-z0-9-]/g, '').toLowerCase()
21
-
22
-
23
- module.exports = exports = (argv) ->
24
-
25
- wikiName = new URL(argv.url).hostname
26
-
27
- sitemap = []
28
-
29
- queue = []
30
-
31
- sitemapPageHandler = null
32
-
33
- # ms since last update we will remove sitemap from memory
34
- sitemapTimeoutMs = 120000
35
- sitemapTimeoutHandler = null
36
-
37
- sitemapLoc = path.join(argv.status, 'sitemap.json')
38
- xmlSitemapLoc = path.join(argv.status, 'sitemap.xml')
39
-
40
- working = false
41
-
42
- lastEdit = (journal) ->
43
- for action in (journal || []) by -1
44
- return action.date if action.date and action.type != 'fork'
45
- undefined
46
-
47
- sitemapUpdate = (file, page, cb) ->
48
-
49
- extractPageLinks = (collaborativeLinks, currentItem, currentIndex, array) ->
50
- # extract collaborative links
51
- # - this will need extending if we also extract the id of the item containing the link
52
- try
53
- linkRe = /\[\[([^\]]+)\]\]/g
54
- match = undefined
55
- while (match = linkRe.exec(currentItem.text)) != null
56
- if not collaborativeLinks.has(asSlug(match[1]))
57
- collaborativeLinks.set(asSlug(match[1]), currentItem.id)
58
- if 'reference' == currentItem.type
59
- if not collaborativeLinks.has(currentItem.slug)
60
- collaborativeLinks.set(currentItem.slug, currentItem.id)
61
- catch err
62
- console.log "METADATA *** #{wikiName} Error extracting links from #{currentIndex} of #{JSON.stringify(array)}", err.message
63
- collaborativeLinks
64
-
65
- try
66
- pageLinksMap = page.story.reduce( extractPageLinks, new Map())
67
- catch err
68
- console.log "METADATA *** #{wikiName} reduce to extract links on #{file} failed", err.message
69
- pageLinksMap = []
70
- #
71
- if pageLinksMap.size > 0
72
- pageLinks = Object.fromEntries(pageLinksMap)
73
- else
74
- pageLinks = undefined
75
-
76
- entry = {
77
- 'slug': file
78
- 'title': page.title
79
- 'date': lastEdit(page.journal)
80
- 'synopsis': synopsis(page)
81
- 'links': pageLinks
82
- }
83
-
84
- slugs = sitemap.map (page) -> page.slug
85
-
86
- idx = slugs.indexOf(file)
87
-
88
- if ~idx
89
- sitemap[idx] = entry
90
- else
91
- sitemap.push entry
92
-
93
- cb()
94
-
95
- sitemapRemovePage = (file, cb) ->
96
- slugs = sitemap.map (page) -> page.slug
97
- idx = slugs.indexOf(file)
98
-
99
- if ~idx
100
- sitemap.splice(idx,1)
101
-
102
- cb()
103
-
104
- sitemapSave = (sitemap, cb) ->
105
- fs.exists argv.status, (exists) ->
106
- if exists
107
- writeFileAtomic sitemapLoc, JSON.stringify(sitemap), (e) ->
108
- return cb(e) if e
109
- cb()
110
- else
111
- fs.mkdir argv.status, { recursive: true }, ->
112
- writeFileAtomic sitemapLoc, JSON.stringify(sitemap), (e) ->
113
- return cb(e) if e
114
- cb()
115
-
116
- sitemapRestore = (cb) ->
117
- fs.exists sitemapLoc, (exists) ->
118
- if exists
119
- fs.readFile(sitemapLoc, (err, data) ->
120
- return cb(err) if err
121
- try
122
- sitemap = JSON.parse(data)
123
- catch e
124
- return cb(e)
125
- process.nextTick( ->
126
- serial(queue.shift()))
127
- )
128
- else
129
- # sitemap file does not exist, so needs creating
130
- itself.createSitemap(sitemapPageHandler)
131
-
132
- xmlSitemapSave = (sitemap, cb) ->
133
- xmlmap = []
134
- sitemap.forEach (page) ->
135
- result = {}
136
- result["loc"] = argv.url + "/" + page.slug + ".html"
137
- if page.date?
138
- date = new Date(page.date)
139
- if !(isNaN(date.valueOf()))
140
- result["lastmod"] = date.toISOString().substring(0,10)
141
- xmlmap.push result
142
- xmlmap = {'urlset': {"$": {"xmlns": "http://www.sitemaps.org/schemas/sitemap/0.9"},'url': xmlmap}}
143
- builder = new xml2js.Builder()
144
- xml = builder.buildObject(xmlmap)
145
- fs.exists argv.status, (exists) ->
146
- if exists
147
- writeFileAtomic xmlSitemapLoc, xml, (e) ->
148
- return cb(e) if e
149
- cb()
150
- else
151
- fs.mkdir argv.status, { recursive: true }, ->
152
- writeFileAtomic xmlSitemapLoc, xml, (e) ->
153
- return cb(e) if e
154
- cb()
155
-
156
- serial = (item) ->
157
- if item
158
- switch item.action
159
- when "update"
160
- itself.start()
161
- sitemapUpdate(item.file, item.page, (e) ->
162
- process.nextTick( ->
163
- serial(queue.shift())
164
- )
165
- )
166
- when "remove"
167
- itself.start()
168
- sitemapRemovePage(item.file, (e) ->
169
- process.nextTick( ->
170
- serial(queue.shift())
171
- )
172
- )
173
- else
174
- console.log "Sitemap unexpected action #{item.action} for #{item.page} in #{wikiName}"
175
- process.nextTick( ->
176
- serial(queue.shift))
177
- else
178
- sitemapSave sitemap, (e) ->
179
- console.log "Problems saving sitemap #{wikiName}: "+ e if e
180
- itself.stop()
181
- xmlSitemapSave sitemap, (e) ->
182
- console.log "Problems saving sitemap(xml) #{wikiName}"+ e if e
183
-
184
-
185
- #### Public stuff ####
186
-
187
- itself = new events.EventEmitter
188
- itself.start = ->
189
- clearTimeout(sitemapTimeoutHandler)
190
- working = true
191
- @emit 'working'
192
- itself.stop = ->
193
- clearsitemap = ->
194
- console.log "removing sitemap #{wikiName} from memory"
195
- sitemap = []
196
- clearTimeout(sitemapTimeoutHandler)
197
- sitemapTimeoutHandler = setTimeout clearsitemap, sitemapTimeoutMs
198
- working = false
199
- @emit 'finished'
200
-
201
- itself.isWorking = ->
202
- working
203
-
204
- itself.createSitemap = (pagehandler) ->
205
-
206
- itself.start()
207
-
208
- # we save the pagehandler, so we can recreate the sitemap if it is removed
209
- sitemapPageHandler = pagehandler if !sitemapPageHandler?
210
-
211
- pagehandler.pages (e, newsitemap) ->
212
- if e
213
- console.log "createSitemap #{wikiName} : error " + e
214
- itself.stop()
215
- return e
216
- sitemap = newsitemap
217
-
218
- process.nextTick ( ->
219
- serial(queue.shift()))
220
-
221
- itself.removePage = (file) ->
222
- action = "remove"
223
- queue.push({action, file, ""})
224
- if sitemap.length is 0 and !working
225
- itself.start()
226
- sitemapRestore (e) ->
227
- console.log "Problems restoring sitemap #{wikiName} : " + e if e
228
- itself.createSitemap(sitemapPageHandler)
229
- else
230
- serial(queue.shift()) unless working
231
-
232
-
233
- itself.update = (file, page) ->
234
- action = "update"
235
- queue.push({action, file, page})
236
- if sitemap.length is 0 and !working
237
- itself.start()
238
- sitemapRestore (e) ->
239
- console.log "Problems restoring sitemap #{wikiName} : " + e if e
240
- itself.createSitemap(sitemapPageHandler)
241
- else
242
- serial(queue.shift()) unless working
243
-
244
-
245
-
246
- itself
@@ -1,8 +0,0 @@
1
- { "title": "Asdf Test Page", "story": [
2
- {"id": "a1", "type": "paragraph", "text": "this is the first paragraph"},
3
- {"id": "a2", "type": "paragraph", "text": "this is the second paragraph"},
4
- {"id": "a3", "type": "paragraph", "text": "this is the third paragraph"},
5
- {"id": "a4", "type": "paragraph", "text": "this is the fourth paragraph"}
6
- ],
7
- journal: []
8
- }
@@ -1,10 +0,0 @@
1
- defaultargs = require '../lib/defaultargs'
2
-
3
- describe 'defaultargs', ->
4
- describe '#defaultargs()', ->
5
- it 'should not write over give args', ->
6
- defaultargs({port: 1234}).port.should.equal(1234)
7
- it 'should write non give args', ->
8
- defaultargs().port.should.equal(3000)
9
- it 'should modify dependant args', ->
10
- defaultargs({data: '/tmp/asdf/'}).db.should.equal('/tmp/asdf/pages')
package/test/mocha.opts DELETED
@@ -1,2 +0,0 @@
1
- --require should
2
- --compilers coffee:coffeescript/register
package/test/page.coffee DELETED
@@ -1,61 +0,0 @@
1
- path = require('path')
2
- random = require('../lib/random_id')
3
- testid = random()
4
- argv = require('../lib/defaultargs.coffee')({data: path.join('/tmp', 'sfwtests', testid), root: path.join(__dirname, '..'), packageDir: path.join(__dirname, '..', 'node_modules'), security_legacy: true})
5
- page = require('../lib/page.coffee')(argv)
6
- fs = require('fs')
7
-
8
- testpage = {title: 'Asdf'}
9
-
10
- describe 'page', ->
11
- describe '#page.put()', ->
12
- it 'should save a page', (done) ->
13
- page.put('asdf', testpage, (e) ->
14
- done(e)
15
- )
16
- describe '#page.get()', ->
17
- it 'should get a page if it exists', (done) ->
18
- page.get('asdf', (e, got) ->
19
- if e then throw e
20
- got.title.should.equal 'Asdf'
21
- done()
22
- )
23
- it 'should copy a page from default if nonexistant in db', (done) ->
24
- page.get('welcome-visitors', (e, got) ->
25
- if e then throw e
26
- got.title.should.equal 'Welcome Visitors'
27
- done()
28
- )
29
- # note: here we assume the wiki-plugin-activity repo has been cloned into an adjacent directory
30
- it 'should copy a page from plugins if nonexistant in db', (done) ->
31
- page.get('recent-changes', (e, got) ->
32
- if e then throw e
33
- got.title.should.equal 'Recent Changes'
34
- done()
35
- )
36
- # note: here we assume the wiki-plugin-activity repo has been cloned into an adjacent directory
37
- it 'should mark a page from plugins with the plugin name', (done) ->
38
- page.get('recent-changes', (e, got) ->
39
- if e then throw e
40
- got.plugin.should.equal 'activity'
41
- done()
42
- )
43
- it 'should create a page if it exists nowhere', (done) ->
44
- page.get(random(), (e, got) ->
45
- if e then throw e
46
- got.should.equal('Page not found')
47
- done()
48
- )
49
- it 'should eventually write the page to disk', (done) ->
50
- test = ->
51
- fs.readFile(path.join(argv.db, 'asdf'), (err, data) ->
52
- if err then throw err
53
- readPage = JSON.parse(data)
54
- page.get('asdf', (e, got) ->
55
- readPage.title.should.equal got.title
56
- done()
57
- )
58
- )
59
- if page.isWorking()
60
- page.on('finished', -> test())
61
- else test()
@@ -1,8 +0,0 @@
1
- random = require '../lib/random_id'
2
-
3
- describe 'random', ->
4
- describe '#random_id', ->
5
- it 'should not be the same twice', ->
6
- random().should.not.equal random()
7
- it 'should be 16 digits', ->
8
- random().length.should.equal 16
@@ -1,228 +0,0 @@
1
- request = require 'supertest'
2
- fs = require 'fs'
3
- server = require '..'
4
- path = require 'path'
5
- random = require '../lib/random_id'
6
- testid = random()
7
- argv = require('../lib/defaultargs.coffee')({data: path.join('/tmp', 'sfwtests', testid), packageDir: path.join(__dirname, '..', 'node_modules'), port: 55557, security_legacy: true})
8
-
9
- describe 'server', ->
10
- app = {}
11
- before((done) ->
12
- # as starting the server this was does not create a sitemap file, create an empty one
13
- sitemapLoc = path.join('/tmp', 'sfwtests', testid, 'status', 'sitemap.json')
14
- fs.mkdirSync path.join('/tmp', 'sfwtests', testid)
15
- fs.mkdirSync path.join('/tmp', 'sfwtests', testid, 'status')
16
- fs.writeFileSync sitemapLoc, JSON.stringify([])
17
-
18
- app = server(argv)
19
- app.once("owner-set", ->
20
- app.listen app.startOpts.port, app.startOpts.host, done
21
- ))
22
-
23
-
24
- request = request('http://localhost:55557')
25
-
26
- # location of the test page
27
- loc = path.join('/tmp', 'sfwtests', testid, 'pages', 'adsf-test-page')
28
-
29
- it 'factories should return a list of plugin', () ->
30
- await request
31
- .get('/system/factories.json')
32
- .expect(200)
33
- .expect('Content-Type', /json/)
34
- .then (res) ->
35
- res.body[1].name.should.equal('Video')
36
- res.body[1].category.should.equal('format')
37
-
38
- it 'new site should have an empty list of pages', () ->
39
- await request
40
- .get('/system/slugs.json')
41
- .expect(200)
42
- .expect('Content-Type', /json/)
43
- .then (res) ->
44
- res.body.should.be.empty
45
- , (error) ->
46
- throw error
47
- .catch (error) ->
48
- throw error
49
-
50
- it 'should create a page', () ->
51
- body = JSON.stringify({
52
- type: 'create'
53
- item: {
54
- title: "Asdf Test Page"
55
- story: [
56
- {id: "a1", type: "paragraph", text: "this is the first paragraph"}
57
- {id: "a2", type: "paragraph", text: "this is the second paragraph"}
58
- {id: "a3", type: "paragraph", text: "this is the third paragraph"}
59
- {id: "a4", type: "paragraph", text: "this is the fourth paragraph"}
60
- ]
61
- }
62
- date: 1234567890123
63
- })
64
-
65
- request
66
- .put('/page/adsf-test-page/action')
67
- .send("action=" + body)
68
- .expect(200)
69
- .catch (err) ->
70
- throw err
71
-
72
- it 'should move the paragraphs to the order given ', () ->
73
- body = '{ "type": "move", "order": [ "a1", "a3", "a2", "a4"] }'
74
-
75
- request
76
- .put('/page/adsf-test-page/action')
77
- .send("action=" + body)
78
- .expect(200)
79
- .then (res) ->
80
- if err
81
- throw err
82
- try
83
- page = JSON.parse(fs.readFileSync(loc))
84
- catch err
85
- throw err
86
- page.story[1].id.should.equal('a3')
87
- page.story[2].id.should.equal('a2')
88
- page.journal[1].type.should.equal('move')
89
- , (err) ->
90
- throw err
91
- .catch (err) ->
92
- throw err
93
-
94
- it 'should add a paragraph', () ->
95
- body = JSON.stringify({
96
- type: 'add'
97
- after: 'a2'
98
- item: {id: 'a5', type: 'paragraph', text: 'this is the NEW paragrpah'}
99
- })
100
-
101
- request
102
- .put('/page/adsf-test-page/action')
103
- .send("action=" + body)
104
- .expect(200)
105
- .then (res) ->
106
- try
107
- page = JSON.parse(fs.readFileSync(loc))
108
- catch err
109
- throw err
110
- page.story.length.should.equal(5)
111
- page.story[3].id.should.equal('a5')
112
- page.journal[2].type.should.equal('add')
113
- , (err) ->
114
- throw err
115
- .catch (err) ->
116
- throw err
117
-
118
- it 'should remove a paragraph with given id', () ->
119
- body = JSON.stringify({
120
- type: 'remove'
121
- id: 'a2'
122
- })
123
-
124
- request
125
- .put('/page/adsf-test-page/action')
126
- .send("action=" + body)
127
- .expect(200)
128
- .then (res) ->
129
- try
130
- page = JSON.parse(fs.readFileSync(loc))
131
- catch err
132
- throw err
133
- page.story.length.should.equal(4)
134
- page.story[1].id.should.equal('a3')
135
- page.story[2].id.should.not.equal('a2')
136
- page.story[2].id.should.equal('a5')
137
- page.journal[3].type.should.equal('remove')
138
- , (err) ->
139
- throw err
140
- .catch (err) ->
141
- throw err
142
-
143
- it 'should edit a paragraph in place', () ->
144
- body = JSON.stringify({
145
- type: 'edit'
146
- item: {id: 'a3', type: 'paragraph', text: 'edited'}
147
- id: 'a3'
148
- })
149
-
150
- request
151
- .put('/page/adsf-test-page/action')
152
- .send("action=" + body)
153
- .expect(200)
154
- .then (res) ->
155
- try
156
- page = JSON.parse(fs.readFileSync(loc))
157
- catch err
158
- throw err
159
- page.story[1].text.should.equal('edited')
160
- page.journal[4].type.should.equal('edit')
161
- , (err) ->
162
- throw err
163
- .catch (err) ->
164
- throw err
165
-
166
- it 'should default to no change', () ->
167
- body = JSON.stringify({
168
- type: 'asdf'
169
- })
170
-
171
- request
172
- .put('/page/adsf-test-page/action')
173
- .send("action=" + body)
174
- .expect(500)
175
- .then (res) ->
176
- try
177
- page = JSON.parse(fs.readFileSync(loc))
178
- catch err
179
- throw err
180
- page.story.length.should.equal(4)
181
- page.journal.length.should.equal(5)
182
- page.story[0].id.should.equal('a1')
183
- page.story[3].text.should.equal('this is the fourth paragraph')
184
- page.journal[4].type.should.equal('edit')
185
- , (err) ->
186
- throw err
187
- .catch (err) ->
188
- throw err
189
-
190
- it 'should refuse to create over a page', () ->
191
- body = JSON.stringify({
192
- type: 'create'
193
- item: { title: 'Doh'}
194
- id: 'c1'
195
- })
196
-
197
- request
198
- .put('/page/adsf-test-page/action')
199
- .send("action=" + body)
200
- .expect(409)
201
- .then (res) ->
202
- try
203
- page = JSON.parse(fs.readFileSync(loc))
204
- catch err
205
- throw err
206
- page.title.should.not.equal('Doh')
207
- , (err) ->
208
- throw err
209
- .catch (err) ->
210
- throw err
211
-
212
- it 'site should now have one page', () ->
213
- request
214
- .get('/system/slugs.json')
215
- .expect(200)
216
- .expect('Content-Type', /json/)
217
- .then (res) ->
218
- res.body.length.should.equal[1]
219
- res.body[0].should.equal['adsf-test-page']
220
- , (err) ->
221
- throw err
222
- .catch (err) ->
223
- throw err
224
-
225
-
226
-
227
- after( ->
228
- app.close() if app.close)
@@ -1,110 +0,0 @@
1
- request = require 'supertest'
2
- fs = require 'fs'
3
- server = require '..'
4
- path = require 'path'
5
- random = require '../lib/random_id'
6
- testid = random()
7
- argv = require('../lib/defaultargs.coffee')({data: path.join('/tmp', 'sfwtests', testid), port: 55556, security_legacy: true})
8
-
9
- describe 'sitemap', ->
10
- app = {}
11
- runningServer = null
12
- beforeEach((done) ->
13
- app = server(argv)
14
- app.once("owner-set", ->
15
- runningServer = app.listen app.startOpts.port, app.startOpts.host, done
16
- ))
17
- afterEach () ->
18
- runningServer.close()
19
-
20
-
21
- request = request('http://localhost:55556')
22
- fs.mkdirSync path.join('/tmp', 'sfwtests', testid, 'pages'), {recursive: true}
23
-
24
- # location of the sitemap
25
- sitemapLoc = path.join('/tmp', 'sfwtests', testid, 'status', 'sitemap.json')
26
-
27
- it 'new site should have an empty sitemap', () ->
28
- request
29
- .get('/system/sitemap.json')
30
- .expect(200)
31
- .expect('Content-Type', /json/)
32
- .then (res) ->
33
- res.body.should.be.empty
34
- , (err) ->
35
- throw err
36
-
37
-
38
- it 'creating a page should add it to the sitemap', () ->
39
- body = JSON.stringify({
40
- type: 'create'
41
- item: {
42
- title: "Asdf Test Page"
43
- story: [
44
- {id: "a1", type: "paragraph", text: "this is the first paragraph"}
45
- {id: "a2", type: "paragraph", text: "this is the second paragraph"}
46
- {id: "a3", type: "paragraph", text: "this is the third paragraph"}
47
- {id: "a4", type: "paragraph", text: "this is the fourth paragraph"}
48
- ]
49
- }
50
- date: 1234567890123
51
- })
52
-
53
- request
54
- .put('/page/adsf-test-page/action')
55
- .send("action=" + body)
56
- .expect(200)
57
- .then (res) ->
58
- # sitemap update does not happen until after the put has returned, so wait for it to finish
59
- app.sitemaphandler.once 'finished', ->
60
- try
61
- sitemap = JSON.parse(fs.readFileSync(sitemapLoc))
62
- catch err
63
- throw err
64
- sitemap[0].slug.should.equal['adsf-test-page']
65
- sitemap[0].synopsis.should.equal['this is the first paragraph']
66
- , (err) ->
67
- throw err
68
-
69
- it 'synopsis should reflect edit to first paragraph', () ->
70
- body = JSON.stringify({
71
- type: 'edit'
72
- item: {id: 'a1', type: 'paragraph', text: 'edited'}
73
- id: 'a1'
74
- })
75
-
76
- request
77
- .put('/page/adsf-test-page/action')
78
- .send("action=" + body)
79
- .expect(200)
80
- .then (res) ->
81
- app.sitemaphandler.once 'finished', ->
82
- try
83
- sitemap = JSON.parse(fs.readFileSync(sitemapLoc))
84
- catch err
85
- throw err
86
- sitemap[0].slug.should.equal['adsf-test-page']
87
- sitemap[0].synopsis.should.equal['edited']
88
- , (err) ->
89
- throw err
90
-
91
- it 'deleting a page should remove it from the sitemap', () ->
92
-
93
- request
94
- .delete('/adsf-test-page.json')
95
- .send()
96
- .expect(200)
97
- .then (res) ->
98
- app.sitemaphandler.once 'finished', ->
99
- try
100
- sitemap = JSON.parse(fs.readFileSync(sitemapLoc))
101
- catch error
102
- throw err
103
- sitemap.should.be.empty
104
- , (err) ->
105
- throw err
106
-
107
-
108
-
109
- after( ->
110
- app.close() if app.close)