├── .gitignore ├── LICENSE ├── README.md ├── build ├── discourse.css ├── templates ├── custom_css_bundle.js └── plugin.json └── version /.gitignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Civilized Discourse Construction Kit 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Discourse CSS customizations for Mattermost 2 | 3 | This repo contains a few CSS tweaks the Discourse team find helpful for Mattermost. 4 | 5 | To build simply run `./build` a file called `./dist/custom_css.tar.gz` will be created. 6 | 7 | Upload that plugin to Mattermost. 8 | -------------------------------------------------------------------------------- /build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'fileutils' 4 | require 'digest' 5 | 6 | path = File.expand_path(File.dirname(__FILE__)) 7 | 8 | def apply_vars(text, vars) 9 | vars.each do |name, val| 10 | text = text.gsub(name, val.inspect) 11 | end 12 | text 13 | end 14 | 15 | def bump_version(version) 16 | s = version.split(".") 17 | s[-1] = (s[-1].to_i + 1).to_s 18 | s.join(".").strip 19 | end 20 | 21 | 22 | Dir.chdir(path) do 23 | 24 | 25 | css = File.read("discourse.css") 26 | 27 | bundle = File.read("templates/custom_css_bundle.js") 28 | 29 | plugin = File.read("templates/plugin.json") 30 | 31 | version = bump_version(File.read("version")) 32 | File.write("version", version) 33 | 34 | id = "custom-css-#{version.split(".").join}" 35 | 36 | bundle = apply_vars(bundle, "$id$" => id, "$CSS$" => css) 37 | bundle_name = "custom_css_#{Digest::SHA1.hexdigest(css)}.js" 38 | plugin = apply_vars(plugin, "$id$" => id, "$VERSION$" => version, "$BUNDLE_PATH$" => bundle_name) 39 | 40 | FileUtils.mkdir_p("dist") 41 | 42 | Dir.chdir("dist") do 43 | `rm -f *.gz` 44 | File.write(bundle_name, bundle) 45 | File.write("plugin.json", plugin) 46 | `tar -czvf custom_css.tar.gz .` 47 | `rm plugin.json` 48 | `rm *.js` 49 | end 50 | 51 | end 52 | 53 | -------------------------------------------------------------------------------- /discourse.css: -------------------------------------------------------------------------------- 1 | /* change font-family and increase size for flagged users */ 2 | @import url('https://fonts.googleapis.com/css?family=Lato:400,700'); 3 | 4 | [data-hack-font=true].app__body.font--open_sans { 5 | font-family: 'Lato', sans-serif; 6 | } 7 | 8 | [data-hack-font=true] .form-control, [data-hack-font=true] .post p { 9 | font-size: 15px; 10 | } 11 | -------------------------------------------------------------------------------- /templates/custom_css_bundle.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // identity function for calling harmony imports with the correct context 37 | /******/ __webpack_require__.i = function(value) { return value; }; 38 | /******/ 39 | /******/ // define getter function for harmony exports 40 | /******/ __webpack_require__.d = function(exports, name, getter) { 41 | /******/ if(!__webpack_require__.o(exports, name)) { 42 | /******/ Object.defineProperty(exports, name, { 43 | /******/ configurable: false, 44 | /******/ enumerable: true, 45 | /******/ get: getter 46 | /******/ }); 47 | /******/ } 48 | /******/ }; 49 | /******/ 50 | /******/ // getDefaultExport function for compatibility with non-harmony modules 51 | /******/ __webpack_require__.n = function(module) { 52 | /******/ var getter = module && module.__esModule ? 53 | /******/ function getDefault() { return module['default']; } : 54 | /******/ function getModuleExports() { return module; }; 55 | /******/ __webpack_require__.d(getter, 'a', getter); 56 | /******/ return getter; 57 | /******/ }; 58 | /******/ 59 | /******/ // Object.prototype.hasOwnProperty.call 60 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 61 | /******/ 62 | /******/ // __webpack_public_path__ 63 | /******/ __webpack_require__.p = ""; 64 | /******/ 65 | /******/ // Load entry module and return exports 66 | /******/ return __webpack_require__(__webpack_require__.s = 22); 67 | /******/ }) 68 | /************************************************************************/ 69 | /******/ ([ 70 | /* 0 */ 71 | /***/ (function(module, exports, __webpack_require__) { 72 | 73 | // Thank's IE8 for his funny defineProperty 74 | module.exports = !__webpack_require__(3)(function () { 75 | return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7; 76 | }); 77 | 78 | 79 | /***/ }), 80 | /* 1 */ 81 | /***/ (function(module, exports) { 82 | 83 | module.exports = function (it) { 84 | return typeof it === 'object' ? it !== null : typeof it === 'function'; 85 | }; 86 | 87 | 88 | /***/ }), 89 | /* 2 */ 90 | /***/ (function(module, exports) { 91 | 92 | var core = module.exports = { version: '2.5.5' }; 93 | if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef 94 | 95 | 96 | /***/ }), 97 | /* 3 */ 98 | /***/ (function(module, exports) { 99 | 100 | module.exports = function (exec) { 101 | try { 102 | return !!exec(); 103 | } catch (e) { 104 | return true; 105 | } 106 | }; 107 | 108 | 109 | /***/ }), 110 | /* 4 */ 111 | /***/ (function(module, exports) { 112 | 113 | // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 114 | var global = module.exports = typeof window != 'undefined' && window.Math == Math 115 | ? window : typeof self != 'undefined' && self.Math == Math ? self 116 | // eslint-disable-next-line no-new-func 117 | : Function('return this')(); 118 | if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef 119 | 120 | 121 | /***/ }), 122 | /* 5 */ 123 | /***/ (function(module, exports, __webpack_require__) { 124 | 125 | var anObject = __webpack_require__(12); 126 | var IE8_DOM_DEFINE = __webpack_require__(18); 127 | var toPrimitive = __webpack_require__(20); 128 | var dP = Object.defineProperty; 129 | 130 | exports.f = __webpack_require__(0) ? Object.defineProperty : function defineProperty(O, P, Attributes) { 131 | anObject(O); 132 | P = toPrimitive(P, true); 133 | anObject(Attributes); 134 | if (IE8_DOM_DEFINE) try { 135 | return dP(O, P, Attributes); 136 | } catch (e) { /* empty */ } 137 | if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!'); 138 | if ('value' in Attributes) O[P] = Attributes.value; 139 | return O; 140 | }; 141 | 142 | 143 | /***/ }), 144 | /* 6 */ 145 | /***/ (function(module, exports, __webpack_require__) { 146 | 147 | "use strict"; 148 | 149 | 150 | exports.__esModule = true; 151 | 152 | exports.default = function (instance, Constructor) { 153 | if (!(instance instanceof Constructor)) { 154 | throw new TypeError("Cannot call a class as a function"); 155 | } 156 | }; 157 | 158 | /***/ }), 159 | /* 7 */ 160 | /***/ (function(module, exports, __webpack_require__) { 161 | 162 | "use strict"; 163 | 164 | 165 | exports.__esModule = true; 166 | 167 | var _defineProperty = __webpack_require__(9); 168 | 169 | var _defineProperty2 = _interopRequireDefault(_defineProperty); 170 | 171 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 172 | 173 | exports.default = function () { 174 | function defineProperties(target, props) { 175 | for (var i = 0; i < props.length; i++) { 176 | var descriptor = props[i]; 177 | descriptor.enumerable = descriptor.enumerable || false; 178 | descriptor.configurable = true; 179 | if ("value" in descriptor) descriptor.writable = true; 180 | (0, _defineProperty2.default)(target, descriptor.key, descriptor); 181 | } 182 | } 183 | 184 | return function (Constructor, protoProps, staticProps) { 185 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 186 | if (staticProps) defineProperties(Constructor, staticProps); 187 | return Constructor; 188 | }; 189 | }(); 190 | 191 | /***/ }), 192 | /* 8 */ 193 | /***/ (function(module, exports) { 194 | 195 | var g; 196 | 197 | // This works in non-strict mode 198 | g = (function() { 199 | return this; 200 | })(); 201 | 202 | try { 203 | // This works if eval is allowed (see CSP) 204 | g = g || Function("return this")() || (1,eval)("this"); 205 | } catch(e) { 206 | // This works if the window reference is available 207 | if(typeof window === "object") 208 | g = window; 209 | } 210 | 211 | // g can still be undefined, but nothing to do about it... 212 | // We return undefined, instead of nothing here, so it's 213 | // easier to handle this case. if(!global) { ...} 214 | 215 | module.exports = g; 216 | 217 | 218 | /***/ }), 219 | /* 9 */ 220 | /***/ (function(module, exports, __webpack_require__) { 221 | 222 | module.exports = { "default": __webpack_require__(10), __esModule: true }; 223 | 224 | /***/ }), 225 | /* 10 */ 226 | /***/ (function(module, exports, __webpack_require__) { 227 | 228 | __webpack_require__(21); 229 | var $Object = __webpack_require__(2).Object; 230 | module.exports = function defineProperty(it, key, desc) { 231 | return $Object.defineProperty(it, key, desc); 232 | }; 233 | 234 | 235 | /***/ }), 236 | /* 11 */ 237 | /***/ (function(module, exports) { 238 | 239 | module.exports = function (it) { 240 | if (typeof it != 'function') throw TypeError(it + ' is not a function!'); 241 | return it; 242 | }; 243 | 244 | 245 | /***/ }), 246 | /* 12 */ 247 | /***/ (function(module, exports, __webpack_require__) { 248 | 249 | var isObject = __webpack_require__(1); 250 | module.exports = function (it) { 251 | if (!isObject(it)) throw TypeError(it + ' is not an object!'); 252 | return it; 253 | }; 254 | 255 | 256 | /***/ }), 257 | /* 13 */ 258 | /***/ (function(module, exports, __webpack_require__) { 259 | 260 | // optional / simple context binding 261 | var aFunction = __webpack_require__(11); 262 | module.exports = function (fn, that, length) { 263 | aFunction(fn); 264 | if (that === undefined) return fn; 265 | switch (length) { 266 | case 1: return function (a) { 267 | return fn.call(that, a); 268 | }; 269 | case 2: return function (a, b) { 270 | return fn.call(that, a, b); 271 | }; 272 | case 3: return function (a, b, c) { 273 | return fn.call(that, a, b, c); 274 | }; 275 | } 276 | return function (/* ...args */) { 277 | return fn.apply(that, arguments); 278 | }; 279 | }; 280 | 281 | 282 | /***/ }), 283 | /* 14 */ 284 | /***/ (function(module, exports, __webpack_require__) { 285 | 286 | var isObject = __webpack_require__(1); 287 | var document = __webpack_require__(4).document; 288 | // typeof document.createElement is 'object' in old IE 289 | var is = isObject(document) && isObject(document.createElement); 290 | module.exports = function (it) { 291 | return is ? document.createElement(it) : {}; 292 | }; 293 | 294 | 295 | /***/ }), 296 | /* 15 */ 297 | /***/ (function(module, exports, __webpack_require__) { 298 | 299 | var global = __webpack_require__(4); 300 | var core = __webpack_require__(2); 301 | var ctx = __webpack_require__(13); 302 | var hide = __webpack_require__(17); 303 | var has = __webpack_require__(16); 304 | var PROTOTYPE = 'prototype'; 305 | 306 | var $export = function (type, name, source) { 307 | var IS_FORCED = type & $export.F; 308 | var IS_GLOBAL = type & $export.G; 309 | var IS_STATIC = type & $export.S; 310 | var IS_PROTO = type & $export.P; 311 | var IS_BIND = type & $export.B; 312 | var IS_WRAP = type & $export.W; 313 | var exports = IS_GLOBAL ? core : core[name] || (core[name] = {}); 314 | var expProto = exports[PROTOTYPE]; 315 | var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE]; 316 | var key, own, out; 317 | if (IS_GLOBAL) source = name; 318 | for (key in source) { 319 | // contains in native 320 | own = !IS_FORCED && target && target[key] !== undefined; 321 | if (own && has(exports, key)) continue; 322 | // export native or passed 323 | out = own ? target[key] : source[key]; 324 | // prevent global pollution for namespaces 325 | exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] 326 | // bind timers to global for call from export context 327 | : IS_BIND && own ? ctx(out, global) 328 | // wrap global constructors for prevent change them in library 329 | : IS_WRAP && target[key] == out ? (function (C) { 330 | var F = function (a, b, c) { 331 | if (this instanceof C) { 332 | switch (arguments.length) { 333 | case 0: return new C(); 334 | case 1: return new C(a); 335 | case 2: return new C(a, b); 336 | } return new C(a, b, c); 337 | } return C.apply(this, arguments); 338 | }; 339 | F[PROTOTYPE] = C[PROTOTYPE]; 340 | return F; 341 | // make static versions for prototype methods 342 | })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; 343 | // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% 344 | if (IS_PROTO) { 345 | (exports.virtual || (exports.virtual = {}))[key] = out; 346 | // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% 347 | if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out); 348 | } 349 | } 350 | }; 351 | // type bitmap 352 | $export.F = 1; // forced 353 | $export.G = 2; // global 354 | $export.S = 4; // static 355 | $export.P = 8; // proto 356 | $export.B = 16; // bind 357 | $export.W = 32; // wrap 358 | $export.U = 64; // safe 359 | $export.R = 128; // real proto method for `library` 360 | module.exports = $export; 361 | 362 | 363 | /***/ }), 364 | /* 16 */ 365 | /***/ (function(module, exports) { 366 | 367 | var hasOwnProperty = {}.hasOwnProperty; 368 | module.exports = function (it, key) { 369 | return hasOwnProperty.call(it, key); 370 | }; 371 | 372 | 373 | /***/ }), 374 | /* 17 */ 375 | /***/ (function(module, exports, __webpack_require__) { 376 | 377 | var dP = __webpack_require__(5); 378 | var createDesc = __webpack_require__(19); 379 | module.exports = __webpack_require__(0) ? function (object, key, value) { 380 | return dP.f(object, key, createDesc(1, value)); 381 | } : function (object, key, value) { 382 | object[key] = value; 383 | return object; 384 | }; 385 | 386 | 387 | /***/ }), 388 | /* 18 */ 389 | /***/ (function(module, exports, __webpack_require__) { 390 | 391 | module.exports = !__webpack_require__(0) && !__webpack_require__(3)(function () { 392 | return Object.defineProperty(__webpack_require__(14)('div'), 'a', { get: function () { return 7; } }).a != 7; 393 | }); 394 | 395 | 396 | /***/ }), 397 | /* 19 */ 398 | /***/ (function(module, exports) { 399 | 400 | module.exports = function (bitmap, value) { 401 | return { 402 | enumerable: !(bitmap & 1), 403 | configurable: !(bitmap & 2), 404 | writable: !(bitmap & 4), 405 | value: value 406 | }; 407 | }; 408 | 409 | 410 | /***/ }), 411 | /* 20 */ 412 | /***/ (function(module, exports, __webpack_require__) { 413 | 414 | // 7.1.1 ToPrimitive(input [, PreferredType]) 415 | var isObject = __webpack_require__(1); 416 | // instead of the ES6 spec version, we didn't implement @@toPrimitive case 417 | // and the second argument - flag - preferred type is a string 418 | module.exports = function (it, S) { 419 | if (!isObject(it)) return it; 420 | var fn, val; 421 | if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; 422 | if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val; 423 | if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; 424 | throw TypeError("Can't convert object to primitive value"); 425 | }; 426 | 427 | 428 | /***/ }), 429 | /* 21 */ 430 | /***/ (function(module, exports, __webpack_require__) { 431 | 432 | var $export = __webpack_require__(15); 433 | // 19.1.2.4 / 15.2.3.6 Object.defineProperty(O, P, Attributes) 434 | $export($export.S + $export.F * !__webpack_require__(0), 'Object', { defineProperty: __webpack_require__(5).f }); 435 | 436 | 437 | /***/ }), 438 | /* 22 */ 439 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 440 | 441 | "use strict"; 442 | /* WEBPACK VAR INJECTION */(function(global) {Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); 443 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck__ = __webpack_require__(6); 444 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck__); 445 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_createClass__ = __webpack_require__(7); 446 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_createClass___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_createClass__); 447 | 448 | 449 | 450 | var PluginClass = function () { 451 | function PluginClass() { 452 | __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck___default()(this, PluginClass); 453 | } 454 | 455 | __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_createClass___default()(PluginClass, [{ 456 | key: 'initialize', 457 | value: function initialize(registerComponents, store) { 458 | 459 | var username = ""; 460 | 461 | try { 462 | var state = store.getState(); 463 | var currentUser = state.entities.users.profiles[state.entities.users.currentUserId]; 464 | username = currentUser && currentUser.username; 465 | } 466 | catch(err) { 467 | // lets not explode here 468 | } 469 | 470 | if (username) { 471 | var fontOverride = ["sam", "jomaxro", "jordan.vidrine"]; 472 | 473 | if (username.substr(username.length - 2) === "-v") { 474 | username = username.substr(0, username.length - 2); 475 | } 476 | 477 | var override = fontOverride.indexOf(username) > -1; 478 | 479 | global.document.body.setAttribute("data-hack-font", override.toString()); 480 | } 481 | 482 | var css = global.document.createElement("style"); 483 | css.type = "text/css"; 484 | css.innerHTML = $CSS$; 485 | global.document.body.appendChild(css); 486 | } 487 | }]); 488 | 489 | return PluginClass; 490 | }(); 491 | 492 | global.window.plugins[$id$] = new PluginClass(); 493 | /* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(8))) 494 | 495 | /***/ }) 496 | /******/ ]); 497 | -------------------------------------------------------------------------------- /templates/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": $id$, 3 | "name": "Custom CSS", 4 | "version": $VERSION$, 5 | "webapp": { 6 | "bundle_path": $BUNDLE_PATH$ 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | 1.0.125 --------------------------------------------------------------------------------