| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var winston = require('winston'), | 
					
						
							|  |  |  | 	nconf = require('nconf'), | 
					
						
							|  |  |  | 	fs = require('fs'), | 
					
						
							|  |  |  | 	path = require('path'), | 
					
						
							|  |  |  | 	less = require('less'), | 
					
						
							| 
									
										
										
										
											2014-07-28 15:25:04 -04:00
										 |  |  | 	crypto = require('crypto'), | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -04:00
										 |  |  | 	async = require('async'), | 
					
						
							| 
									
										
										
										
											2015-12-31 12:15:31 -05:00
										 |  |  | 	autoprefixer = require('autoprefixer'), | 
					
						
							|  |  |  | 	postcss = require('postcss'), | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	plugins = require('../plugins'), | 
					
						
							|  |  |  | 	emitter = require('../emitter'), | 
					
						
							| 
									
										
										
										
											2015-03-20 19:36:18 -04:00
										 |  |  | 	db = require('../database'), | 
					
						
							| 
									
										
										
										
											2015-09-29 18:22:41 -04:00
										 |  |  | 	file = require('../file'), | 
					
						
							| 
									
										
										
										
											2015-03-20 19:36:18 -04:00
										 |  |  | 	utils = require('../../public/src/utils'); | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | module.exports = function(Meta) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-08 16:53:22 -04:00
										 |  |  | 	Meta.css = {}; | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 	Meta.css.cache = undefined; | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -04:00
										 |  |  | 	Meta.css.acpCache = undefined; | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-25 10:13:01 -04:00
										 |  |  | 	Meta.css.minify = function(callback) { | 
					
						
							| 
									
										
										
										
											2015-03-20 19:36:18 -04:00
										 |  |  | 		callback = callback || function() {}; | 
					
						
							|  |  |  | 		if (nconf.get('isPrimary') !== 'true') { | 
					
						
							|  |  |  | 			winston.verbose('[meta/css] Cluster worker ' + process.pid + ' skipping LESS/CSS compilation'); | 
					
						
							|  |  |  | 			return callback(); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-20 19:36:18 -04:00
										 |  |  | 		winston.verbose('[meta/css] Minifying LESS/CSS'); | 
					
						
							|  |  |  | 		db.getObjectFields('config', ['theme:type', 'theme:id'], function(err, themeData) { | 
					
						
							|  |  |  | 			if (err) { | 
					
						
							|  |  |  | 				return callback(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-19 14:57:49 -04:00
										 |  |  | 			var themeId = (themeData['theme:id'] || 'nodebb-theme-persona'), | 
					
						
							| 
									
										
										
										
											2015-03-20 19:36:18 -04:00
										 |  |  | 				baseThemePath = path.join(nconf.get('themes_path'), (themeData['theme:type'] && themeData['theme:type'] === 'local' ? themeId : 'nodebb-theme-vanilla')), | 
					
						
							|  |  |  | 				paths = [ | 
					
						
							|  |  |  | 					baseThemePath, | 
					
						
							|  |  |  | 					path.join(__dirname, '../../node_modules'), | 
					
						
							|  |  |  | 					path.join(__dirname, '../../public/vendor/fontawesome/less'), | 
					
						
							|  |  |  | 					path.join(__dirname, '../../public/vendor/bootstrap/less') | 
					
						
							|  |  |  | 				], | 
					
						
							|  |  |  | 				source = '@import "font-awesome";'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			plugins.lessFiles = filterMissingFiles(plugins.lessFiles); | 
					
						
							|  |  |  | 			plugins.cssFiles = filterMissingFiles(plugins.cssFiles); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			async.waterfall([ | 
					
						
							|  |  |  | 				function(next) { | 
					
						
							|  |  |  | 					getStyleSource(plugins.lessFiles, '\n@import ".', '.less', next); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				function(src, next) { | 
					
						
							|  |  |  | 					source += src; | 
					
						
							|  |  |  | 					getStyleSource(plugins.cssFiles, '\n@import (inline) ".', '.css', next); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				function(src, next) { | 
					
						
							|  |  |  | 					source += src; | 
					
						
							|  |  |  | 					next(); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			], function(err) { | 
					
						
							|  |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return callback(err); | 
					
						
							| 
									
										
										
										
											2014-07-15 16:32:51 -04:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/css/smoothness/jquery-ui-1.10.4.custom.min.css";'; | 
					
						
							|  |  |  | 				source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";'; | 
					
						
							| 
									
										
										
										
											2015-03-13 15:01:52 -04:00
										 |  |  | 				source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/textcomplete/jquery.textcomplete.css";'; | 
					
						
							| 
									
										
										
										
											2015-01-09 13:51:27 -05:00
										 |  |  | 				source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";'; | 
					
						
							| 
									
										
										
										
											2016-02-04 14:36:22 -05:00
										 |  |  | 				source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/flags.less";'; | 
					
						
							| 
									
										
										
										
											2016-03-15 12:13:35 +02:00
										 |  |  | 				source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/blacklist.less";'; | 
					
						
							| 
									
										
										
										
											2015-03-25 17:21:38 -04:00
										 |  |  | 				source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/generics.less";'; | 
					
						
							| 
									
										
										
										
											2015-08-04 10:34:25 -04:00
										 |  |  | 				source += '\n@import "..' + path.sep + '..' + path.sep + 'public/less/mixins.less";'; | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-20 19:36:18 -04:00
										 |  |  | 				var acpSource = '\n@import "..' + path.sep + 'public/less/admin/admin";\n' + source; | 
					
						
							| 
									
										
										
										
											2015-03-25 17:21:38 -04:00
										 |  |  | 				acpSource += '\n@import "..' + path.sep + 'public/less/generics.less";'; | 
					
						
							| 
									
										
										
										
											2015-01-09 13:51:27 -05:00
										 |  |  | 				acpSource += '\n@import (inline) "..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 				source = '@import "./theme";\n' + source; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				async.parallel([ | 
					
						
							|  |  |  | 					function(next) { | 
					
						
							|  |  |  | 						minify(source, paths, 'cache', next); | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					function(next) { | 
					
						
							|  |  |  | 						minify(acpSource, paths, 'acpCache', next); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				], function(err, minified) { | 
					
						
							| 
									
										
										
										
											2015-03-20 19:36:18 -04:00
										 |  |  | 					if (err) { | 
					
						
							|  |  |  | 						return callback(err); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 					// Propagate to other workers
 | 
					
						
							| 
									
										
										
										
											2014-12-03 14:03:41 -05:00
										 |  |  | 					if (process.send) { | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 						process.send({ | 
					
						
							|  |  |  | 							action: 'css-propagate', | 
					
						
							|  |  |  | 							cache: minified[0], | 
					
						
							| 
									
										
										
										
											2015-09-11 16:18:32 -04:00
										 |  |  | 							acpCache: minified[1] | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-04 16:18:33 -04:00
										 |  |  | 					emitter.emit('meta:css.compiled'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-20 19:36:18 -04:00
										 |  |  | 					callback(); | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2015-03-20 19:36:18 -04:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -04:00
										 |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-20 19:36:18 -04:00
										 |  |  | 	function getStyleSource(files, prefix, extension, callback) { | 
					
						
							|  |  |  | 		var	pluginDirectories = [], | 
					
						
							|  |  |  | 			source = ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		files.forEach(function(styleFile) { | 
					
						
							|  |  |  | 			if (styleFile.endsWith(extension)) { | 
					
						
							|  |  |  | 				source += prefix + path.sep + styleFile + '";'; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				pluginDirectories.push(styleFile); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		async.each(pluginDirectories, function(directory, next) { | 
					
						
							|  |  |  | 			utils.walk(directory, function(err, styleFiles) { | 
					
						
							|  |  |  | 				if (err) { | 
					
						
							|  |  |  | 					return next(err); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				styleFiles.forEach(function(styleFile) { | 
					
						
							|  |  |  | 					source += prefix + path.sep + styleFile + '";'; | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				next(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}, function(err) { | 
					
						
							|  |  |  | 			callback(err, source); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-28 18:33:27 -04:00
										 |  |  | 	Meta.css.commitToFile = function(filename) { | 
					
						
							| 
									
										
										
										
											2014-10-08 15:05:02 -04:00
										 |  |  | 		var file = (filename === 'acpCache' ? 'admin' : 'stylesheet') + '.css'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		fs.writeFile(path.join(__dirname, '../../public/' + file), Meta.css[filename], function(err) { | 
					
						
							| 
									
										
										
										
											2014-09-28 18:33:27 -04:00
										 |  |  | 			if (!err) { | 
					
						
							| 
									
										
										
										
											2014-11-17 11:24:46 -05:00
										 |  |  | 				winston.verbose('[meta/css] ' + file + ' committed to disk.'); | 
					
						
							| 
									
										
										
										
											2014-09-28 18:33:27 -04:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				winston.error('[meta/css] ' + err.message); | 
					
						
							|  |  |  | 				process.exit(0); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2014-10-04 18:56:33 -04:00
										 |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2014-09-28 18:33:27 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-21 15:25:16 -04:00
										 |  |  | 	Meta.css.getFromFile = function(callback) { | 
					
						
							|  |  |  | 		var cachePath = path.join(__dirname, '../../public/stylesheet.css'), | 
					
						
							|  |  |  | 			acpCachePath = path.join(__dirname, '../../public/admin.css'); | 
					
						
							| 
									
										
										
										
											2015-09-29 18:22:41 -04:00
										 |  |  | 		file.exists(cachePath, function(exists) { | 
					
						
							|  |  |  | 			if (!exists) { | 
					
						
							| 
									
										
										
										
											2015-02-24 17:57:18 -05:00
										 |  |  | 				winston.warn('[meta/css] No stylesheets found on disk, re-minifying'); | 
					
						
							| 
									
										
										
										
											2015-09-29 18:22:41 -04:00
										 |  |  | 				Meta.css.minify(callback); | 
					
						
							|  |  |  | 				return; | 
					
						
							| 
									
										
										
										
											2014-10-21 15:25:16 -04:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-09-29 18:22:41 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if (nconf.get('isPrimary') !== 'true') { | 
					
						
							|  |  |  | 				return callback(); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			winston.verbose('[meta/css] Reading stylesheets from file'); | 
					
						
							|  |  |  | 			async.map([cachePath, acpCachePath], fs.readFile, function(err, files) { | 
					
						
							|  |  |  | 				Meta.css.cache = files[0]; | 
					
						
							|  |  |  | 				Meta.css.acpCache = files[1]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				emitter.emit('meta:css.compiled'); | 
					
						
							|  |  |  | 				callback(); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2014-10-21 15:25:16 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-03 14:03:41 -05:00
										 |  |  | 	function minify(source, paths, destination, callback) { | 
					
						
							| 
									
										
										
										
											2014-11-17 11:24:46 -05:00
										 |  |  | 		less.render(source, { | 
					
						
							| 
									
										
										
										
											2014-11-23 15:53:42 -05:00
										 |  |  | 			paths: paths, | 
					
						
							|  |  |  | 			compress: true | 
					
						
							| 
									
										
										
										
											2014-11-17 11:24:46 -05:00
										 |  |  | 		}, function(err, lessOutput) { | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -04:00
										 |  |  | 			if (err) { | 
					
						
							|  |  |  | 				winston.error('[meta/css] Could not minify LESS/CSS: ' + err.message); | 
					
						
							|  |  |  | 				if (typeof callback === 'function') { | 
					
						
							|  |  |  | 					callback(err); | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -04:00
										 |  |  | 				return; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 12:15:31 -05:00
										 |  |  | 			winston.verbose('[meta/css] Running PostCSS Plugins'); | 
					
						
							|  |  |  | 			postcss([ autoprefixer ]).process(lessOutput.css).then(function (result) { | 
					
						
							|  |  |  | 				result.warnings().forEach(function (warn) { | 
					
						
							|  |  |  | 					winston.verbose(warn.toString()); | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 12:15:31 -05:00
										 |  |  | 				Meta.css[destination] = result.css; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// Save the compiled CSS in public/ so things like nginx can serve it
 | 
					
						
							|  |  |  | 				if (nconf.get('isPrimary') === 'true') { | 
					
						
							|  |  |  | 					Meta.css.commitToFile(destination); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (typeof callback === 'function') { | 
					
						
							|  |  |  | 					callback(null, result.css); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2014-09-28 18:33:27 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -04:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	function filterMissingFiles(files) { | 
					
						
							| 
									
										
										
										
											2015-09-29 18:22:41 -04:00
										 |  |  | 		return files.filter(function(filePath) { | 
					
						
							|  |  |  | 			var exists = file.existsSync(path.join(__dirname, '../../node_modules', filePath)); | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 			if (!exists) { | 
					
						
							| 
									
										
										
										
											2015-09-29 18:22:41 -04:00
										 |  |  | 				winston.warn('[meta/css] File not found! ' + filePath); | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			return exists; | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; |