| 
									
										
										
										
											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'), | 
					
						
							| 
									
										
										
										
											2014-09-28 18:35:24 -04:00
										 |  |  | 	cluster = require('cluster'), | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	plugins = require('../plugins'), | 
					
						
							|  |  |  | 	emitter = require('../emitter'), | 
					
						
							|  |  |  | 	db = require('../database'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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
										 |  |  | 	Meta.css.branding = {}; | 
					
						
							|  |  |  | 	Meta.css.defaultBranding = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-25 10:13:01 -04:00
										 |  |  | 	Meta.css.minify = function(callback) { | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 		if (!cluster.isWorker || process.env.cluster_setup === 'true') { | 
					
						
							| 
									
										
										
										
											2014-11-17 11:24:46 -05:00
										 |  |  | 			winston.verbose('[meta/css] Minifying LESS/CSS'); | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 			db.getObjectFields('config', ['theme:type', 'theme:id'], function(err, themeData) { | 
					
						
							|  |  |  | 				var themeId = (themeData['theme:id'] || 'nodebb-theme-vanilla'), | 
					
						
							|  |  |  | 					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";', | 
					
						
							|  |  |  | 					acpSource, | 
					
						
							|  |  |  | 					x; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				plugins.lessFiles = filterMissingFiles(plugins.lessFiles); | 
					
						
							|  |  |  | 				for(x=0; x<plugins.lessFiles.length; ++x) { | 
					
						
							|  |  |  | 					source += '\n@import ".' + path.sep + plugins.lessFiles[x] + '";'; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 				plugins.cssFiles = filterMissingFiles(plugins.cssFiles); | 
					
						
							|  |  |  | 				for(x=0; x<plugins.cssFiles.length; ++x) { | 
					
						
							|  |  |  | 					source += '\n@import (inline) ".' + path.sep + plugins.cssFiles[x] + '";'; | 
					
						
							| 
									
										
										
										
											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";'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				acpSource = '\n@import "..' + path.sep + 'public/less/admin/admin";\n' + source; | 
					
						
							|  |  |  | 				source = '@import "./theme";\n' + source; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				async.parallel([ | 
					
						
							|  |  |  | 					function(next) { | 
					
						
							|  |  |  | 						minify(source, paths, 'cache', next); | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					function(next) { | 
					
						
							|  |  |  | 						minify(acpSource, paths, 'acpCache', next); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				], function(err, minified) { | 
					
						
							|  |  |  | 					// Propagate to other workers
 | 
					
						
							|  |  |  | 					if (cluster.isWorker) { | 
					
						
							|  |  |  | 						process.send({ | 
					
						
							|  |  |  | 							action: 'css-propagate', | 
					
						
							|  |  |  | 							cache: minified[0], | 
					
						
							| 
									
										
										
										
											2014-12-01 14:05:20 -05:00
										 |  |  | 							acpCache: minified[1], | 
					
						
							|  |  |  | 							hash: Meta.css.hash | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-04 16:18:33 -04:00
										 |  |  | 					emitter.emit('meta:css.compiled'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 					if (typeof callback === 'function') { | 
					
						
							|  |  |  | 						callback(); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2014-11-17 11:24:46 -05:00
										 |  |  | 			winston.verbose('[meta/css] Cluster worker ' + cluster.worker.id + ' skipping LESS/CSS compilation'); | 
					
						
							| 
									
										
										
										
											2014-09-29 19:31:27 -04:00
										 |  |  | 			if (typeof callback === 'function') { | 
					
						
							|  |  |  | 				callback(); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -04:00
										 |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2014-07-04 18:20:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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'); | 
					
						
							|  |  |  | 		fs.exists(cachePath, function(exists) { | 
					
						
							|  |  |  | 			if (exists) { | 
					
						
							|  |  |  | 				if (!cluster.isWorker || process.env.cluster_setup === 'true') { | 
					
						
							| 
									
										
										
										
											2014-11-17 11:24:46 -05:00
										 |  |  | 					winston.verbose('[meta/css] (Experimental) Reading stylesheets from file'); | 
					
						
							| 
									
										
										
										
											2014-10-21 15:25:16 -04:00
										 |  |  | 					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(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					callback(); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				winston.warn('[meta/css] (Experimental) No stylesheets found on disk, re-minifying'); | 
					
						
							|  |  |  | 				Meta.css.minify.apply(Meta.css, arguments); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -04: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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-17 11:24:46 -05:00
										 |  |  | 			Meta.css[destination] = lessOutput.css; | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-01 14:05:20 -05:00
										 |  |  | 			if (destination === 'cache') { | 
					
						
							|  |  |  | 				// Calculate css buster
 | 
					
						
							|  |  |  | 				var hasher = crypto.createHash('md5'); | 
					
						
							| 
									
										
										
										
											2014-10-08 16:53:22 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-01 14:05:20 -05:00
										 |  |  | 				hasher.update(lessOutput.css, 'utf-8'); | 
					
						
							|  |  |  | 				Meta.css.hash = hasher.digest('hex').slice(0, 8); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-28 18:33:27 -04:00
										 |  |  | 			// Save the compiled CSS in public/ so things like nginx can serve it
 | 
					
						
							| 
									
										
										
										
											2014-09-28 18:35:24 -04:00
										 |  |  | 			if (!cluster.isWorker || process.env.cluster_setup === 'true') { | 
					
						
							|  |  |  | 				Meta.css.commitToFile(destination); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2014-09-28 18:33:27 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -04:00
										 |  |  | 			if (typeof callback === 'function') { | 
					
						
							| 
									
										
										
										
											2014-11-17 11:24:46 -05:00
										 |  |  | 				callback(null, lessOutput.css); | 
					
						
							| 
									
										
										
										
											2014-09-24 18:36:30 -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) { | 
					
						
							|  |  |  | 		return files.filter(function(file) { | 
					
						
							|  |  |  | 			var exists = fs.existsSync(path.join(__dirname, '../../node_modules', file)); | 
					
						
							|  |  |  | 			if (!exists) { | 
					
						
							|  |  |  | 				winston.warn('[meta/css] File not found! ' + file); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return exists; | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; |