Bug 25590: Street number is missing from the alternate address in the OPAC
[koha-ffzg.git] / gulpfile.js
1 /* eslint-env node */
2 /* eslint no-console:"off" */
3
4 const { dest, parallel, series, src, watch } = require('gulp');
5
6 const child_process = require('child_process');
7 const fs = require('fs');
8 const os = require('os');
9 const path = require('path');
10 const util = require('util');
11
12 const sass = require('gulp-sass')(require('sass'));
13 const rtlcss = require('gulp-rtlcss');
14 const sourcemaps = require('gulp-sourcemaps');
15 const autoprefixer = require('gulp-autoprefixer');
16 const concatPo = require('gulp-concat-po');
17 const exec = require('gulp-exec');
18 const merge = require('merge-stream');
19 const through2 = require('through2');
20 const Vinyl = require('vinyl');
21 const args = require('minimist')(process.argv.slice(2));
22 const rename = require('gulp-rename');
23
24 const STAFF_CSS_BASE = "koha-tmpl/intranet-tmpl/prog/css";
25 const OPAC_CSS_BASE = "koha-tmpl/opac-tmpl/bootstrap/css";
26
27 var CSS_BASE = args.view == "opac"
28     ? OPAC_CSS_BASE
29     : STAFF_CSS_BASE;
30
31 var sassOptions = {
32     includePaths: [
33         __dirname + '/node_modules',
34         __dirname + '/../node_modules'
35     ]
36 }
37
38 // CSS processing for development
39 function css(css_base) {
40     css_base = css_base || CSS_BASE
41     var stream = src(css_base + "/src/**/*.scss")
42         .pipe(sourcemaps.init())
43         .pipe(sass(sassOptions).on('error', sass.logError))
44         .pipe(autoprefixer())
45         .pipe(dest(css_base));
46
47     if (args.view == "opac") {
48         stream = stream
49             .pipe(rtlcss())
50             .pipe(rename({
51                 suffix: '-rtl'
52             })) // Append "-rtl" to the filename.
53             .pipe(dest(css_base));
54     }
55
56     stream = stream.pipe(sourcemaps.write('./maps'))
57         .pipe(dest(css_base));
58
59     return stream;
60
61 }
62 // CSS processing for production
63 function build(css_base) {
64     css_base = css_base || CSS_BASE;
65     sassOptions.outputStyle = "compressed";
66     var stream = src(css_base + "/src/**/*.scss")
67         .pipe(sass(sassOptions).on('error', sass.logError))
68         .pipe(autoprefixer())
69         .pipe(dest(css_base));
70
71     if( args.view == "opac" ){
72         stream = stream.pipe(rtlcss())
73         .pipe(rename({
74             suffix: '-rtl'
75         })) // Append "-rtl" to the filename.
76         .pipe(dest(css_base));
77     }
78
79     return stream;
80 }
81
82 function opac_css(){
83     return css(OPAC_CSS_BASE);
84 }
85
86 function staff_css(){
87     return css(STAFF_CSS_BASE);
88 }
89
90 const poTasks = {
91     'marc-MARC21': {
92         extract: po_extract_marc_marc21,
93         create: po_create_marc_marc21,
94         update: po_update_marc_marc21,
95     },
96     'marc-UNIMARC': {
97         extract: po_extract_marc_unimarc,
98         create: po_create_marc_unimarc,
99         update: po_update_marc_unimarc,
100     },
101     'staff-prog': {
102         extract: po_extract_staff,
103         create: po_create_staff,
104         update: po_update_staff,
105     },
106     'opac-bootstrap': {
107         extract: po_extract_opac,
108         create: po_create_opac,
109         update: po_update_opac,
110     },
111     'pref': {
112         extract: po_extract_pref,
113         create: po_create_pref,
114         update: po_update_pref,
115     },
116     'messages': {
117         extract: po_extract_messages,
118         create: po_create_messages,
119         update: po_update_messages,
120     },
121     'messages-js': {
122         extract: po_extract_messages_js,
123         create: po_create_messages_js,
124         update: po_update_messages_js,
125     },
126     'installer': {
127         extract: po_extract_installer,
128         create: po_create_installer,
129         update: po_update_installer,
130     },
131     'installer-MARC21': {
132         extract: po_extract_installer_marc21,
133         create: po_create_installer_marc21,
134         update: po_update_installer_marc21,
135     },
136     'installer-UNIMARC': {
137         extract: po_extract_installer_unimarc,
138         create: po_create_installer_unimarc,
139         update: po_update_installer_unimarc,
140     },
141 };
142
143 const poTypes = Object.keys(poTasks);
144
145 function po_extract_marc (type) {
146     return src(`koha-tmpl/*-tmpl/*/en/**/*${type}*`, { read: false, nocase: true })
147         .pipe(xgettext('misc/translator/xgettext.pl --charset=UTF-8 -s', `Koha-marc-${type}.pot`))
148         .pipe(dest('misc/translator'))
149 }
150
151 function po_extract_marc_marc21 ()  { return po_extract_marc('MARC21') }
152 function po_extract_marc_unimarc () { return po_extract_marc('UNIMARC') }
153
154 function po_extract_staff () {
155     const globs = [
156         'koha-tmpl/intranet-tmpl/prog/en/**/*.tt',
157         'koha-tmpl/intranet-tmpl/prog/en/**/*.inc',
158         'koha-tmpl/intranet-tmpl/prog/en/xslt/*.xsl',
159         '!koha-tmpl/intranet-tmpl/prog/en/**/*MARC21*',
160         '!koha-tmpl/intranet-tmpl/prog/en/**/*UNIMARC*',
161         '!koha-tmpl/intranet-tmpl/prog/en/**/*marc21*',
162         '!koha-tmpl/intranet-tmpl/prog/en/**/*unimarc*',
163     ];
164
165     return src(globs, { read: false, nocase: true })
166         .pipe(xgettext('misc/translator/xgettext.pl --charset=UTF-8 -s', 'Koha-staff-prog.pot'))
167         .pipe(dest('misc/translator'))
168 }
169
170 function po_extract_opac () {
171     const globs = [
172         'koha-tmpl/opac-tmpl/bootstrap/en/**/*.tt',
173         'koha-tmpl/opac-tmpl/bootstrap/en/**/*.inc',
174         'koha-tmpl/opac-tmpl/bootstrap/en/xslt/*.xsl',
175         '!koha-tmpl/opac-tmpl/bootstrap/en/**/*MARC21*',
176         '!koha-tmpl/opac-tmpl/bootstrap/en/**/*UNIMARC*',
177         '!koha-tmpl/opac-tmpl/bootstrap/en/**/*marc21*',
178         '!koha-tmpl/opac-tmpl/bootstrap/en/**/*unimarc*',
179     ];
180
181     return src(globs, { read: false, nocase: true })
182         .pipe(xgettext('misc/translator/xgettext.pl --charset=UTF-8 -s', 'Koha-opac-bootstrap.pot'))
183         .pipe(dest('misc/translator'))
184 }
185
186 const xgettext_options = '--from-code=UTF-8 --package-name Koha '
187     + '--package-version= -k -k__ -k__x -k__n:1,2 -k__nx:1,2 -k__xn:1,2 '
188     + '-k__p:1c,2 -k__px:1c,2 -k__np:1c,2,3 -k__npx:1c,2,3 -kN__ '
189     + '-kN__n:1,2 -kN__p:1c,2 -kN__np:1c,2,3 '
190     + '-k -k$__ -k$__x -k$__n:1,2 -k$__nx:1,2 -k$__xn:1,2 '
191     + '--force-po';
192
193 function po_extract_messages_js () {
194     const globs = [
195         'koha-tmpl/intranet-tmpl/prog/js/vue/**/*.vue',
196         'koha-tmpl/intranet-tmpl/prog/js/**/*.js',
197         'koha-tmpl/opac-tmpl/bootstrap/js/**/*.js',
198     ];
199
200     return src(globs, { read: false, nocase: true })
201         .pipe(xgettext(`xgettext -L JavaScript ${xgettext_options}`, 'Koha-messages-js.pot'))
202         .pipe(dest('misc/translator'))
203 }
204
205 function po_extract_messages () {
206     const perlStream = src(['**/*.pl', '**/*.pm'], { read: false, nocase: true })
207         .pipe(xgettext(`xgettext -L Perl ${xgettext_options}`, 'Koha-perl.pot'))
208
209     const ttStream = src([
210             'koha-tmpl/intranet-tmpl/prog/en/**/*.tt',
211             'koha-tmpl/intranet-tmpl/prog/en/**/*.inc',
212             'koha-tmpl/opac-tmpl/bootstrap/en/**/*.tt',
213             'koha-tmpl/opac-tmpl/bootstrap/en/**/*.inc',
214         ], { read: false, nocase: true })
215         .pipe(xgettext('misc/translator/xgettext-tt2 --from-code=UTF-8', 'Koha-tt.pot'))
216
217     const headers = {
218         'Project-Id-Version': 'Koha',
219         'Content-Type': 'text/plain; charset=UTF-8',
220     };
221
222     return merge(perlStream, ttStream)
223         .pipe(concatPo('Koha-messages.pot', { headers }))
224         .pipe(dest('misc/translator'))
225 }
226
227 function po_extract_pref () {
228     return src('koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/*.pref', { read: false })
229         .pipe(xgettext('misc/translator/xgettext-pref', 'Koha-pref.pot'))
230         .pipe(dest('misc/translator'))
231 }
232
233 function po_extract_installer () {
234     const globs = [
235         'installer/data/mysql/en/mandatory/*.yml',
236         'installer/data/mysql/en/optional/*.yml',
237     ];
238
239     return src(globs, { read: false, nocase: true })
240         .pipe(xgettext('misc/translator/xgettext-installer', 'Koha-installer.pot'))
241         .pipe(dest('misc/translator'))
242 }
243
244 function po_extract_installer_marc (type) {
245     const globs = `installer/data/mysql/en/marcflavour/${type}/**/*.yml`;
246
247     return src(globs, { read: false, nocase: true })
248         .pipe(xgettext('misc/translator/xgettext-installer', `Koha-installer-${type}.pot`))
249         .pipe(dest('misc/translator'))
250 }
251
252 function po_extract_installer_marc21 ()  { return po_extract_installer_marc('MARC21') }
253
254 function po_extract_installer_unimarc ()  { return po_extract_installer_marc('UNIMARC') }
255
256 function po_create_type (type) {
257     const access = util.promisify(fs.access);
258     const exec = util.promisify(child_process.exec);
259
260     const languages = getLanguages();
261     const promises = [];
262     for (const language of languages) {
263         const locale = language.split('-').filter(s => s.length !== 4).join('_');
264         const po = `misc/translator/po/${language}-${type}.po`;
265         const pot = `misc/translator/Koha-${type}.pot`;
266
267         const promise = access(po)
268             .catch(() => exec(`msginit -o ${po} -i ${pot} -l ${locale} --no-translator`))
269         promises.push(promise);
270     }
271
272     return Promise.all(promises);
273 }
274
275 function po_create_marc_marc21 ()       { return po_create_type('marc-MARC21') }
276 function po_create_marc_unimarc ()      { return po_create_type('marc-UNIMARC') }
277 function po_create_staff ()             { return po_create_type('staff-prog') }
278 function po_create_opac ()              { return po_create_type('opac-bootstrap') }
279 function po_create_pref ()              { return po_create_type('pref') }
280 function po_create_messages ()          { return po_create_type('messages') }
281 function po_create_messages_js ()       { return po_create_type('messages-js') }
282 function po_create_installer ()         { return po_create_type('installer') }
283 function po_create_installer_marc21 ()  { return po_create_type('installer-MARC21') }
284 function po_create_installer_unimarc () { return po_create_type('installer-UNIMARC') }
285
286 function po_update_type (type) {
287     const msgmerge_opts = '--backup=off --quiet --sort-output --update';
288     const cmd = `msgmerge ${msgmerge_opts} <%= file.path %> misc/translator/Koha-${type}.pot`;
289     const languages = getLanguages();
290     const globs = languages.map(language => `misc/translator/po/${language}-${type}.po`);
291
292     return src(globs)
293         .pipe(exec(cmd, { continueOnError: true }))
294         .pipe(exec.reporter({ err: false, stdout: false }))
295 }
296
297 function po_update_marc_marc21 ()       { return po_update_type('marc-MARC21') }
298 function po_update_marc_unimarc ()      { return po_update_type('marc-UNIMARC') }
299 function po_update_staff ()             { return po_update_type('staff-prog') }
300 function po_update_opac ()              { return po_update_type('opac-bootstrap') }
301 function po_update_pref ()              { return po_update_type('pref') }
302 function po_update_messages ()          { return po_update_type('messages') }
303 function po_update_messages_js ()       { return po_update_type('messages-js') }
304 function po_update_installer ()         { return po_update_type('installer') }
305 function po_update_installer_marc21 ()  { return po_update_type('installer-MARC21') }
306 function po_update_installer_unimarc () { return po_update_type('installer-UNIMARC') }
307
308 /**
309  * Gulp plugin that executes xgettext-like command `cmd` on all files given as
310  * input, and then outputs the result as a POT file named `filename`.
311  * `cmd` should accept -o and -f options
312  */
313 function xgettext (cmd, filename) {
314     const filenames = [];
315
316     function transform (file, encoding, callback) {
317         filenames.push(path.relative(file.cwd, file.path));
318         callback();
319     }
320
321     function flush (callback) {
322         fs.mkdtemp(path.join(os.tmpdir(), 'koha-'), (err, folder) => {
323             const outputFilename = path.join(folder, filename);
324             const filesFilename = path.join(folder, 'files');
325             fs.writeFile(filesFilename, filenames.join(os.EOL), err => {
326                 if (err) return callback(err);
327
328                 const command = `${cmd} -o ${outputFilename} -f ${filesFilename}`;
329                 child_process.exec(command, err => {
330                     if (err) return callback(err);
331
332                     fs.readFile(outputFilename, (err, data) => {
333                         if (err) return callback(err);
334
335                         const file = new Vinyl();
336                         file.path = path.join(file.base, filename);
337                         file.contents = data;
338                         callback(null, file);
339                     });
340                 });
341             });
342         })
343     }
344
345     return through2.obj(transform, flush);
346 }
347
348 /**
349  * Return languages selected for PO-related tasks
350  *
351  * This can be either languages given on command-line with --lang option, or
352  * all the languages found in misc/translator/po otherwise
353  */
354 function getLanguages () {
355     if (Array.isArray(args.lang)) {
356         return args.lang;
357     }
358
359     if (args.lang) {
360         return [args.lang];
361     }
362
363     const filenames = fs.readdirSync('misc/translator/po')
364         .filter(filename => filename.endsWith('.po'))
365         .filter(filename => !filename.startsWith('.'))
366
367     const re = new RegExp('-(' + poTypes.join('|') + ')\.po$');
368     languages = filenames.map(filename => filename.replace(re, ''))
369
370     return Array.from(new Set(languages));
371 }
372
373 exports.build = function(next){build(); next();};
374 exports.css = function(next){css(); next();};
375 exports.opac_css = opac_css;
376 exports.staff_css = staff_css;
377 exports.watch = function () {
378     watch(OPAC_CSS_BASE + "/src/**/*.scss", series('opac_css'));
379     watch(STAFF_CSS_BASE + "/src/**/*.scss", series('staff_css'));
380 };
381
382 exports['po:create'] = parallel(...poTypes.map(type => series(poTasks[type].extract, poTasks[type].create)));
383 exports['po:update'] = parallel(...poTypes.map(type => series(poTasks[type].extract, poTasks[type].update)));
384 exports['po:extract'] = parallel(...poTypes.map(type => poTasks[type].extract));