Skip to content

Commit b7a96c6

Browse files
committed
Merge branch 'master' into release
2 parents 4b645a8 + 9126da6 commit b7a96c6

32 files changed

+1053
-111
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ after_failure:
2525
- cat storage/logs/laravel.log
2626

2727
script:
28-
- phpunit
28+
- phpunit

config/app.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
*/
5959

6060
'locale' => env('APP_LANG', 'en'),
61-
'locales' => ['en', 'de', 'es', 'fr', 'nl', 'pt_BR', 'sk', 'ja'],
61+
'locales' => ['en', 'de', 'es', 'fr', 'nl', 'pt_BR', 'sk', 'ja', 'pl'],
6262

6363
/*
6464
|--------------------------------------------------------------------------

gulpfile.js

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ const babelify = require("babelify");
1212
const watchify = require("watchify");
1313
const envify = require("envify");
1414
const gutil = require("gulp-util");
15+
const liveReload = require('gulp-livereload');
1516

1617
if (argv.production) process.env.NODE_ENV = 'production';
18+
let isProduction = argv.production || process.env.NODE_ENV === 'production';
1719

1820
gulp.task('styles', () => {
1921
let chain = gulp.src(['resources/assets/sass/**/*.scss'])
@@ -24,31 +26,40 @@ gulp.task('styles', () => {
2426
}}))
2527
.pipe(sass())
2628
.pipe(autoprefixer('last 2 versions'));
27-
if (argv.production) chain = chain.pipe(minifycss());
28-
return chain.pipe(gulp.dest('public/css/'));
29+
if (isProduction) chain = chain.pipe(minifycss());
30+
return chain.pipe(gulp.dest('public/css/')).pipe(liveReload());
2931
});
3032

3133

32-
function scriptTask(watch=false) {
34+
function scriptTask(watch = false) {
3335

3436
let props = {
3537
basedir: 'resources/assets/js',
3638
debug: true,
37-
entries: ['global.js']
39+
entries: ['global.js'],
40+
fast: !isProduction,
41+
cache: {},
42+
packageCache: {},
3843
};
3944

4045
let bundler = watch ? watchify(browserify(props), { poll: true }) : browserify(props);
41-
bundler.transform(envify, {global: true}).transform(babelify, {presets: ['es2015']});
46+
47+
if (isProduction) {
48+
bundler.transform(envify, {global: true}).transform(babelify, {presets: ['es2015']});
49+
}
50+
4251
function rebundle() {
4352
let stream = bundler.bundle();
4453
stream = stream.pipe(source('common.js'));
45-
if (argv.production) stream = stream.pipe(buffer()).pipe(uglify());
46-
return stream.pipe(gulp.dest('public/js/'));
54+
if (isProduction) stream = stream.pipe(buffer()).pipe(uglify());
55+
return stream.pipe(gulp.dest('public/js/')).pipe(liveReload());
4756
}
57+
4858
bundler.on('update', function() {
4959
rebundle();
50-
gutil.log('Rebundle...');
60+
gutil.log('Rebundling assets...');
5161
});
62+
5263
bundler.on('log', gutil.log);
5364
return rebundle();
5465
}
@@ -57,6 +68,7 @@ gulp.task('scripts', () => {scriptTask(false)});
5768
gulp.task('scripts-watch', () => {scriptTask(true)});
5869

5970
gulp.task('default', ['styles', 'scripts-watch'], () => {
71+
liveReload.listen();
6072
gulp.watch("resources/assets/sass/**/*.scss", ['styles']);
6173
});
6274

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"build": "gulp build",
55
"production": "gulp build --production",
66
"dev": "gulp",
7-
"watch": "gulp"
7+
"watch": "gulp",
8+
"permissions": "chown -R $USER:$USER bootstrap/cache storage public/uploads"
89
},
910
"devDependencies": {
1011
"babelify": "^7.3.0",
@@ -13,6 +14,7 @@
1314
"gulp": "3.9.1",
1415
"gulp-autoprefixer": "3.1.1",
1516
"gulp-clean-css": "^3.0.4",
17+
"gulp-livereload": "^3.8.1",
1618
"gulp-minify-css": "1.2.4",
1719
"gulp-plumber": "1.1.0",
1820
"gulp-sass": "3.1.0",

readme.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ All development on BookStack is currently done on the master branch. When it's t
2222
SASS is used to help the CSS development and the JavaScript is run through browserify/babel to allow for writing ES6 code. Both of these are done using gulp. To run the build task you can use the following commands:
2323

2424
``` bash
25-
# Build and minify for production
25+
# Build assets for development
2626
npm run-script build
2727

28+
# Build and minify assets for production
29+
npm run-script production
30+
2831
# Build for dev (With sourcemaps) and watch for changes
2932
npm run-script dev
3033
```

resources/assets/js/directives.js

Lines changed: 92 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -193,30 +193,6 @@ module.exports = function (ngApp, events) {
193193
}
194194

195195
scope.tinymce.extraSetups.push(tinyMceSetup);
196-
197-
// Custom tinyMCE plugins
198-
tinymce.PluginManager.add('customhr', function (editor) {
199-
editor.addCommand('InsertHorizontalRule', function () {
200-
let hrElem = document.createElement('hr');
201-
let cNode = editor.selection.getNode();
202-
let parentNode = cNode.parentNode;
203-
parentNode.insertBefore(hrElem, cNode);
204-
});
205-
206-
editor.addButton('hr', {
207-
icon: 'hr',
208-
tooltip: 'Horizontal line',
209-
cmd: 'InsertHorizontalRule'
210-
});
211-
212-
editor.addMenuItem('hr', {
213-
icon: 'hr',
214-
text: 'Horizontal line',
215-
cmd: 'InsertHorizontalRule',
216-
context: 'insert'
217-
});
218-
});
219-
220196
tinymce.init(scope.tinymce);
221197
}
222198
}
@@ -257,6 +233,21 @@ module.exports = function (ngApp, events) {
257233
extraKeys[`${metaKey}-S`] = function(cm) {scope.$emit('save-draft');};
258234
// Show link selector
259235
extraKeys[`Shift-${metaKey}-K`] = function(cm) {showLinkSelector()};
236+
// Insert Link
237+
extraKeys[`${metaKey}-K`] = function(cm) {insertLink()};
238+
// FormatShortcuts
239+
extraKeys[`${metaKey}-1`] = function(cm) {replaceLineStart('##');};
240+
extraKeys[`${metaKey}-2`] = function(cm) {replaceLineStart('###');};
241+
extraKeys[`${metaKey}-3`] = function(cm) {replaceLineStart('####');};
242+
extraKeys[`${metaKey}-4`] = function(cm) {replaceLineStart('#####');};
243+
extraKeys[`${metaKey}-5`] = function(cm) {replaceLineStart('');};
244+
extraKeys[`${metaKey}-d`] = function(cm) {replaceLineStart('');};
245+
extraKeys[`${metaKey}-6`] = function(cm) {replaceLineStart('>');};
246+
extraKeys[`${metaKey}-q`] = function(cm) {replaceLineStart('>');};
247+
extraKeys[`${metaKey}-7`] = function(cm) {wrapSelection('\n```\n', '\n```');};
248+
extraKeys[`${metaKey}-8`] = function(cm) {wrapSelection('`', '`');};
249+
extraKeys[`Shift-${metaKey}-E`] = function(cm) {wrapSelection('`', '`');};
250+
extraKeys[`${metaKey}-9`] = function(cm) {wrapSelection('<p class="callout info">', '</div>');};
260251
cm.setOption('extraKeys', extraKeys);
261252

262253
// Update data on content change
@@ -309,6 +300,73 @@ module.exports = function (ngApp, events) {
309300
cm.setSelections(cursor);
310301
}
311302

303+
// Helper to replace the start of the line
304+
function replaceLineStart(newStart) {
305+
let cursor = cm.getCursor();
306+
let lineContent = cm.getLine(cursor.line);
307+
let lineLen = lineContent.length;
308+
let lineStart = lineContent.split(' ')[0];
309+
310+
// Remove symbol if already set
311+
if (lineStart === newStart) {
312+
lineContent = lineContent.replace(`${newStart} `, '');
313+
cm.replaceRange(lineContent, {line: cursor.line, ch: 0}, {line: cursor.line, ch: lineLen});
314+
cm.setCursor({line: cursor.line, ch: cursor.ch - (newStart.length + 1)});
315+
return;
316+
}
317+
318+
let alreadySymbol = /^[#>`]/.test(lineStart);
319+
let posDif = 0;
320+
if (alreadySymbol) {
321+
posDif = newStart.length - lineStart.length;
322+
lineContent = lineContent.replace(lineStart, newStart).trim();
323+
} else if (newStart !== '') {
324+
posDif = newStart.length + 1;
325+
lineContent = newStart + ' ' + lineContent;
326+
}
327+
cm.replaceRange(lineContent, {line: cursor.line, ch: 0}, {line: cursor.line, ch: lineLen});
328+
cm.setCursor({line: cursor.line, ch: cursor.ch + posDif});
329+
}
330+
331+
function wrapLine(start, end) {
332+
let cursor = cm.getCursor();
333+
let lineContent = cm.getLine(cursor.line);
334+
let lineLen = lineContent.length;
335+
let newLineContent = lineContent;
336+
337+
if (lineContent.indexOf(start) === 0 && lineContent.slice(-end.length) === end) {
338+
newLineContent = lineContent.slice(start.length, lineContent.length - end.length);
339+
} else {
340+
newLineContent = `${start}${lineContent}${end}`;
341+
}
342+
343+
cm.replaceRange(newLineContent, {line: cursor.line, ch: 0}, {line: cursor.line, ch: lineLen});
344+
cm.setCursor({line: cursor.line, ch: cursor.ch + (newLineContent.length - lineLen)});
345+
}
346+
347+
function wrapSelection(start, end) {
348+
let selection = cm.getSelection();
349+
if (selection === '') return wrapLine(start, end);
350+
let newSelection = selection;
351+
let frontDiff = 0;
352+
let endDiff = 0;
353+
354+
if (selection.indexOf(start) === 0 && selection.slice(-end.length) === end) {
355+
newSelection = selection.slice(start.length, selection.length - end.length);
356+
endDiff = -(end.length + start.length);
357+
} else {
358+
newSelection = `${start}${selection}${end}`;
359+
endDiff = start.length + end.length;
360+
}
361+
362+
let selections = cm.listSelections()[0];
363+
cm.replaceSelection(newSelection);
364+
let headFirst = selections.head.ch <= selections.anchor.ch;
365+
selections.head.ch += headFirst ? frontDiff : endDiff;
366+
selections.anchor.ch += headFirst ? endDiff : frontDiff;
367+
cm.setSelections([selections]);
368+
}
369+
312370
// Handle image upload and add image into markdown content
313371
function uploadImage(file) {
314372
if (file === null || file.type.indexOf('image') !== 0) return;
@@ -351,6 +409,16 @@ module.exports = function (ngApp, events) {
351409
});
352410
}
353411

412+
function insertLink() {
413+
let cursorPos = cm.getCursor('from');
414+
let selectedText = cm.getSelection() || '';
415+
let newText = `[${selectedText}]()`;
416+
cm.focus();
417+
cm.replaceSelection(newText);
418+
let cursorPosDiff = (selectedText === '') ? -3 : -1;
419+
cm.setCursor(cursorPos.line, cursorPos.ch + newText.length+cursorPosDiff);
420+
}
421+
354422
// Show the image manager and handle image insertion
355423
function showImageManager() {
356424
let cursorPos = cm.getCursor('from');

resources/assets/js/pages/page-form.js

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,36 @@ function editorPaste(e, editor) {
5252
function registerEditorShortcuts(editor) {
5353
// Headers
5454
for (let i = 1; i < 5; i++) {
55-
editor.addShortcut('meta+' + i, '', ['FormatBlock', false, 'h' + i]);
55+
editor.shortcuts.add('meta+' + i, '', ['FormatBlock', false, 'h' + (i+1)]);
5656
}
5757

5858
// Other block shortcuts
59-
editor.addShortcut('meta+q', '', ['FormatBlock', false, 'blockquote']);
60-
editor.addShortcut('meta+d', '', ['FormatBlock', false, 'p']);
61-
editor.addShortcut('meta+e', '', ['codeeditor', false, 'pre']);
62-
editor.addShortcut('meta+shift+E', '', ['FormatBlock', false, 'code']);
59+
editor.shortcuts.add('meta+5', '', ['FormatBlock', false, 'p']);
60+
editor.shortcuts.add('meta+d', '', ['FormatBlock', false, 'p']);
61+
editor.shortcuts.add('meta+6', '', ['FormatBlock', false, 'blockquote']);
62+
editor.shortcuts.add('meta+q', '', ['FormatBlock', false, 'blockquote']);
63+
editor.shortcuts.add('meta+7', '', ['codeeditor', false, 'pre']);
64+
editor.shortcuts.add('meta+e', '', ['codeeditor', false, 'pre']);
65+
editor.shortcuts.add('meta+8', '', ['FormatBlock', false, 'code']);
66+
editor.shortcuts.add('meta+shift+E', '', ['FormatBlock', false, 'code']);
67+
// Loop through callout styles
68+
editor.shortcuts.add('meta+9', '', function() {
69+
let selectedNode = editor.selection.getNode();
70+
let formats = ['info', 'success', 'warning', 'danger'];
71+
72+
if (!selectedNode || selectedNode.className.indexOf('callout') === -1) {
73+
editor.formatter.apply('calloutinfo');
74+
return;
75+
}
76+
77+
for (let i = 0; i < formats.length; i++) {
78+
if (selectedNode.className.indexOf(formats[i]) === -1) continue;
79+
let newFormat = (i === formats.length -1) ? formats[0] : formats[i+1];
80+
editor.formatter.apply('callout' + newFormat);
81+
return;
82+
}
83+
editor.formatter.apply('p');
84+
});
6385
}
6486

6587

@@ -120,7 +142,7 @@ function codePlugin() {
120142
$codeMirrorContainer.replaceWith($pre);
121143
}
122144

123-
window.tinymce.PluginManager.add('codeeditor', (editor, url) => {
145+
window.tinymce.PluginManager.add('codeeditor', function(editor, url) {
124146

125147
let $ = editor.$;
126148

@@ -173,7 +195,32 @@ function codePlugin() {
173195
});
174196
}
175197

198+
function hrPlugin() {
199+
window.tinymce.PluginManager.add('customhr', function (editor) {
200+
editor.addCommand('InsertHorizontalRule', function () {
201+
let hrElem = document.createElement('hr');
202+
let cNode = editor.selection.getNode();
203+
let parentNode = cNode.parentNode;
204+
parentNode.insertBefore(hrElem, cNode);
205+
});
206+
207+
editor.addButton('hr', {
208+
icon: 'hr',
209+
tooltip: 'Horizontal line',
210+
cmd: 'InsertHorizontalRule'
211+
});
212+
213+
editor.addMenuItem('hr', {
214+
icon: 'hr',
215+
text: 'Horizontal line',
216+
cmd: 'InsertHorizontalRule',
217+
context: 'insert'
218+
});
219+
});
220+
}
221+
176222
module.exports = function() {
223+
hrPlugin();
177224
codePlugin();
178225
let settings = {
179226
selector: '#html-editor',
@@ -207,10 +254,10 @@ module.exports = function() {
207254
{title: "Code Block", icon: "code", cmd: 'codeeditor', format: 'codeeditor'},
208255
{title: "Inline Code", icon: "code", inline: "code"},
209256
{title: "Callouts", items: [
210-
{title: "Success", block: 'p', exact: true, attributes : {'class' : 'callout success'}},
211-
{title: "Info", block: 'p', exact: true, attributes : {'class' : 'callout info'}},
212-
{title: "Warning", block: 'p', exact: true, attributes : {'class' : 'callout warning'}},
213-
{title: "Danger", block: 'p', exact: true, attributes : {'class' : 'callout danger'}}
257+
{title: "Info", format: 'calloutinfo'},
258+
{title: "Success", format: 'calloutsuccess'},
259+
{title: "Warning", format: 'calloutwarning'},
260+
{title: "Danger", format: 'calloutdanger'}
214261
]},
215262
],
216263
style_formats_merge: false,
@@ -219,6 +266,10 @@ module.exports = function() {
219266
alignleft: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-left'},
220267
aligncenter: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-center'},
221268
alignright: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-right'},
269+
calloutsuccess: {block: 'p', exact: true, attributes: {class: 'callout success'}},
270+
calloutinfo: {block: 'p', exact: true, attributes: {class: 'callout info'}},
271+
calloutwarning: {block: 'p', exact: true, attributes: {class: 'callout warning'}},
272+
calloutdanger: {block: 'p', exact: true, attributes: {class: 'callout danger'}}
222273
},
223274
file_browser_callback: function (field_name, url, type, win) {
224275

resources/assets/js/vues/search.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ let methods = {
149149

150150
updateSearch(e) {
151151
e.preventDefault();
152-
window.location = '/search?term=' + encodeURIComponent(this.termString);
152+
window.location = window.baseUrl('/search?term=' + encodeURIComponent(this.termString));
153153
},
154154

155155
enableDate(optionName) {
@@ -192,4 +192,4 @@ function created() {
192192

193193
module.exports = {
194194
data, computed, methods, created
195-
};
195+
};

resources/lang/en/settings.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
'pt_BR' => 'Português do Brasil',
123123
'sk' => 'Slovensky',
124124
'ja' => '日本語',
125+
'pl' => 'Polski',
125126
]
126127
///////////////////////////////////
127128
];

0 commit comments

Comments
 (0)