diff --git a/assets/javascripts/legacy/discourse-loader.js b/assets/javascripts/legacy/discourse-loader.js index ec77a6d2..ee9a2a07 100644 --- a/assets/javascripts/legacy/discourse-loader.js +++ b/assets/javascripts/legacy/discourse-loader.js @@ -7,7 +7,8 @@ var define, requirejs; let ALIASES = { "ember-addons/ember-computed-decorators": "discourse-common/utils/decorators", - "discourse/lib/raw-templates": "discourse-common/lib/raw-templates", + "discourse/lib/raw-templates": "discourse/plugins/discourse-custom-wizard/legacy/raw-templates", + "discourse-common/lib/raw-templates": "discourse/plugins/discourse-custom-wizard/legacy/raw-templates", "preload-store": "discourse/lib/preload-store", "fixtures/user_fixtures": "discourse/tests/fixtures/user-fixtures", }; diff --git a/assets/javascripts/legacy/raw-templates.js b/assets/javascripts/legacy/raw-templates.js new file mode 100644 index 00000000..5e67bbd8 --- /dev/null +++ b/assets/javascripts/legacy/raw-templates.js @@ -0,0 +1,42 @@ +import { getResolverOption } from "./resolver"; + +export const __DISCOURSE_RAW_TEMPLATES = {}; + +export function addRawTemplate(name, template, opts = {}) { + // Core templates should never overwrite themes / plugins + if (opts.core && __DISCOURSE_RAW_TEMPLATES[name]) { + return; + } + __DISCOURSE_RAW_TEMPLATES[name] = template; +} + +export function removeRawTemplate(name) { + delete __DISCOURSE_RAW_TEMPLATES[name]; +} + +export function findRawTemplate(name) { + if (getResolverOption("mobileView")) { + return ( + __DISCOURSE_RAW_TEMPLATES[`javascripts/mobile/${name}`] || + __DISCOURSE_RAW_TEMPLATES[`javascripts/${name}`] || + __DISCOURSE_RAW_TEMPLATES[`mobile/${name}`] || + __DISCOURSE_RAW_TEMPLATES[name] + ); + } + + return ( + __DISCOURSE_RAW_TEMPLATES[`javascripts/${name}`] || + __DISCOURSE_RAW_TEMPLATES[name] + ); +} + +export function buildRawConnectorCache(findOutlets) { + let result = {}; + findOutlets(__DISCOURSE_RAW_TEMPLATES, (outletName, resource) => { + result[outletName] = result[outletName] || []; + result[outletName].push({ + template: __DISCOURSE_RAW_TEMPLATES[resource], + }); + }); + return result; +} diff --git a/assets/javascripts/legacy/resolver.js b/assets/javascripts/legacy/resolver.js new file mode 100644 index 00000000..5980b39b --- /dev/null +++ b/assets/javascripts/legacy/resolver.js @@ -0,0 +1,337 @@ + +/* global Ember */ +import { classify, dasherize, decamelize } from "@ember/string"; +import deprecated from "discourse-common/lib/deprecated"; +import { findHelper } from "discourse-common/lib/helpers"; +import { get } from "@ember/object"; +import SuffixTrie from "discourse-common/lib/suffix-trie"; + +let _options = {}; +let moduleSuffixTrie = null; + +export function setResolverOption(name, value) { + _options[name] = value; +} + +export function getResolverOption(name) { + return _options[name]; +} + +export function clearResolverOptions() { + _options = {}; +} + +function parseName(fullName) { + const nameParts = fullName.split(":"); + const type = nameParts[0]; + let fullNameWithoutType = nameParts[1]; + const namespace = get(this, "namespace"); + const root = namespace; + + return { + fullName, + type, + fullNameWithoutType, + name: fullNameWithoutType, + root, + resolveMethodName: "resolve" + classify(type), + }; +} + +function lookupModuleBySuffix(suffix) { + if (!moduleSuffixTrie) { + moduleSuffixTrie = new SuffixTrie("/"); + Object.keys(requirejs.entries).forEach((name) => { + if (!name.includes("/templates/")) { + moduleSuffixTrie.add(name); + } + }); + } + return moduleSuffixTrie.withSuffix(suffix, 1)[0]; +} + +export function buildResolver(baseName) { + return Ember.DefaultResolver.extend({ + parseName, + + resolveRouter(parsedName) { + const routerPath = `${baseName}/router`; + if (requirejs.entries[routerPath]) { + const module = requirejs(routerPath, null, null, true); + return module.default; + } + return this._super(parsedName); + }, + + normalize(fullName) { + if (fullName === "app-events:main") { + deprecated( + "`app-events:main` has been replaced with `service:app-events`", + { since: "2.4.0", dropFrom: "2.9.0.beta1" } + ); + return "service:app-events"; + } + + for (const [key, value] of Object.entries({ + "controller:discovery.categoryWithID": "controller:discovery.category", + "controller:discovery.parentCategory": "controller:discovery.category", + "controller:tags-show": "controller:tag-show", + "controller:tags.show": "controller:tag.show", + "controller:tagsShow": "controller:tagShow", + "route:discovery.categoryWithID": "route:discovery.category", + "route:discovery.parentCategory": "route:discovery.category", + "route:tags-show": "route:tag-show", + "route:tags.show": "route:tag.show", + "route:tagsShow": "route:tagShow", + })) { + if (fullName === key) { + deprecated(`${key} was replaced with ${value}`, { since: "2.6.0" }); + return value; + } + } + + const split = fullName.split(":"); + if (split.length > 1) { + const appBase = `${baseName}/${split[0]}s/`; + const adminBase = "admin/" + split[0] + "s/"; + const wizardBase = "wizard/" + split[0] + "s/"; + + // Allow render 'admin/templates/xyz' too + split[1] = split[1].replace(".templates", "").replace("/templates", ""); + + // Try slashes + let dashed = dasherize(split[1].replace(/\./g, "/")); + if ( + requirejs.entries[appBase + dashed] || + requirejs.entries[adminBase + dashed] || + requirejs.entries[wizardBase + dashed] + ) { + return split[0] + ":" + dashed; + } + + // Try with dashes instead of slashes + dashed = dasherize(split[1].replace(/\./g, "-")); + if ( + requirejs.entries[appBase + dashed] || + requirejs.entries[adminBase + dashed] || + requirejs.entries[wizardBase + dashed] + ) { + return split[0] + ":" + dashed; + } + } + return this._super(fullName); + }, + + customResolve(parsedName) { + // If we end with the name we want, use it. This allows us to define components within plugins. + const suffix = parsedName.type + "s/" + parsedName.fullNameWithoutType, + dashed = dasherize(suffix), + moduleName = lookupModuleBySuffix(dashed); + + let module; + if (moduleName) { + module = requirejs(moduleName, null, null, true /* force sync */); + if (module && module["default"]) { + module = module["default"]; + } + } + return module; + }, + + resolveWidget(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveAdapter(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveModel(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveView(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveHelper(parsedName) { + return ( + findHelper(parsedName.fullNameWithoutType) || + this.customResolve(parsedName) || + this._super(parsedName) + ); + }, + + resolveController(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveComponent(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveService(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveRawView(parsedName) { + return this.customResolve(parsedName) || this._super(parsedName); + }, + + resolveRoute(parsedName) { + if (parsedName.fullNameWithoutType === "basic") { + return requirejs("discourse/routes/discourse", null, null, true) + .default; + } + + return this.customResolve(parsedName) || this._super(parsedName); + }, + + findLoadingTemplate(parsedName) { + if (parsedName.fullNameWithoutType.match(/loading$/)) { + return Ember.TEMPLATES.loading; + } + }, + + findConnectorTemplate(parsedName) { + const full = parsedName.fullNameWithoutType.replace("components/", ""); + if (full.indexOf("connectors") === 0) { + return Ember.TEMPLATES[`javascripts/${full}`]; + } + }, + + resolveTemplate(parsedName) { + return ( + this.findPluginMobileTemplate(parsedName) || + this.findPluginTemplate(parsedName) || + this.findMobileTemplate(parsedName) || + this.findTemplate(parsedName) || + this.findLoadingTemplate(parsedName) || + this.findConnectorTemplate(parsedName) || + Ember.TEMPLATES.not_found + ); + }, + + findPluginTemplate(parsedName) { + const pluginParsedName = this.parseName( + parsedName.fullName.replace("template:", "template:javascripts/") + ); + return this.findTemplate(pluginParsedName); + }, + + findPluginMobileTemplate(parsedName) { + if (_options.mobileView) { + let pluginParsedName = this.parseName( + parsedName.fullName.replace( + "template:", + "template:javascripts/mobile/" + ) + ); + return this.findTemplate(pluginParsedName); + } + }, + + findMobileTemplate(parsedName) { + if (_options.mobileView) { + let mobileParsedName = this.parseName( + parsedName.fullName.replace("template:", "template:mobile/") + ); + return this.findTemplate(mobileParsedName); + } + }, + + findTemplate(parsedName) { + const withoutType = parsedName.fullNameWithoutType, + slashedType = withoutType.replace(/\./g, "/"), + decamelized = decamelize(withoutType), + dashed = decamelized.replace(/\./g, "-").replace(/\_/g, "-"), + templates = Ember.TEMPLATES; + + return ( + this._super(parsedName) || + templates[slashedType] || + templates[withoutType] || + templates[withoutType.replace(/\.raw$/, "")] || + templates[dashed] || + templates[decamelized.replace(/\./, "/")] || + templates[decamelized.replace(/\_/, "/")] || + templates[`${baseName}/templates/${withoutType}`] || + this.findAdminTemplate(parsedName) || + this.findWizardTemplate(parsedName) || + this.findUnderscoredTemplate(parsedName) + ); + }, + + findUnderscoredTemplate(parsedName) { + let decamelized = decamelize(parsedName.fullNameWithoutType); + let underscored = decamelized.replace(/\-/g, "_"); + return Ember.TEMPLATES[underscored]; + }, + + // Try to find a template within a special admin namespace, e.g. adminEmail => admin/templates/email + // (similar to how discourse lays out templates) + findAdminTemplate(parsedName) { + let decamelized = decamelize(parsedName.fullNameWithoutType); + if (decamelized.indexOf("components") === 0) { + let comPath = `admin/templates/${decamelized}`; + const compTemplate = + Ember.TEMPLATES[`javascripts/${comPath}`] || Ember.TEMPLATES[comPath]; + if (compTemplate) { + return compTemplate; + } + } + + if (decamelized === "javascripts/admin") { + return Ember.TEMPLATES["admin/templates/admin"]; + } + + if ( + decamelized.indexOf("admin") === 0 || + decamelized.indexOf("javascripts/admin") === 0 + ) { + decamelized = decamelized.replace(/^admin\_/, "admin/templates/"); + decamelized = decamelized.replace(/^admin\./, "admin/templates/"); + decamelized = decamelized.replace(/\./g, "_"); + + const dashed = decamelized.replace(/_/g, "-"); + return ( + Ember.TEMPLATES[decamelized] || + Ember.TEMPLATES[dashed] || + Ember.TEMPLATES[dashed.replace("admin-", "admin/")] + ); + } + }, + + findWizardTemplate(parsedName) { + let decamelized = decamelize(parsedName.fullNameWithoutType); + if (decamelized.startsWith("components")) { + let comPath = `wizard/templates/${decamelized}`; + const compTemplate = + Ember.TEMPLATES[`javascripts/${comPath}`] || Ember.TEMPLATES[comPath]; + if (compTemplate) { + return compTemplate; + } + } + + if (decamelized === "javascripts/wizard") { + return Ember.TEMPLATES["wizard/templates/wizard"]; + } + + if ( + decamelized.startsWith("wizard") || + decamelized.startsWith("javascripts/wizard") + ) { + decamelized = decamelized.replace(/^wizard\_/, "wizard/templates/"); + decamelized = decamelized.replace(/^wizard\./, "wizard/templates/"); + decamelized = decamelized.replace(/\./g, "_"); + + const dashed = decamelized.replace(/_/g, "-"); + return ( + Ember.TEMPLATES[decamelized] || + Ember.TEMPLATES[dashed] || + Ember.TEMPLATES[dashed.replace("wizard-", "wizard/")] + ); + } + }, + }); +} diff --git a/assets/javascripts/wizard-custom.js b/assets/javascripts/wizard-custom.js index 05a6fcb3..0dd67833 100644 --- a/assets/javascripts/wizard-custom.js +++ b/assets/javascripts/wizard-custom.js @@ -1,7 +1,27 @@ // discourse-skip-module //= require_tree_discourse truth-helpers/addon -//= require_tree_discourse discourse-common/addon + +//= require legacy/resolver +//= require legacy/raw-templates + +//= require_tree_discourse discourse-common/addon/config +//= require_tree_discourse discourse-common/addon/helpers +//= require discourse-common/addon/lib/get-owner +//= require discourse-common/addon/lib/object +//= require discourse-common/addon/lib/helpers +//= require discourse-common/addon/lib/get-url +//= require discourse-common/addon/lib/deprecated +//= require discourse-common/addon/lib/suffix-trie +//= require discourse-common/addon/lib/debounce +//= require discourse-common/addon/lib/raw-handlebars +//= require discourse-common/addon/lib/raw-handlebars-helpers +//= require discourse-common/addon/lib/escape +//= require discourse-common/addon/lib/icon-library +//= require discourse-common/addon/lib/attribute-hook +//= require discourse-common/addon/lib/dom-from-string +//= require_tree_discourse discourse-common/addon/utils + //= require_tree_discourse select-kit/addon //= require_tree_discourse discourse/app/lib //= require_tree_discourse discourse/app/mixins diff --git a/assets/javascripts/wizard-raw-templates.js.erb b/assets/javascripts/wizard-raw-templates.js.erb index 55b255e0..ff216180 100644 --- a/assets/javascripts/wizard-raw-templates.js.erb +++ b/assets/javascripts/wizard-raw-templates.js.erb @@ -22,14 +22,14 @@ Discourse.unofficial_plugins.each do |plugin| compiled = Barber::Precompiler.new().compile(File.read(f)) result << " (function() { - requirejs('discourse-common/lib/raw-templates').addRawTemplate(#{compiled}); + requirejs('discourse/plugins/discourse-custom-wizard/legacy/raw-templates').addRawTemplate(#{compiled}); })(); " end result << " (function() { - window.__DISCOURSE_RAW_TEMPLATES = requirejs('discourse-common/lib/raw-templates').__DISCOURSE_RAW_TEMPLATES; + window.__DISCOURSE_RAW_TEMPLATES = requirejs('discourse/plugins/discourse-custom-wizard/legacy/raw-templates').__DISCOURSE_RAW_TEMPLATES; })(); " end diff --git a/assets/javascripts/wizard/application.js.es6 b/assets/javascripts/wizard/application.js.es6 index 012949b3..dd2d09f8 100644 --- a/assets/javascripts/wizard/application.js.es6 +++ b/assets/javascripts/wizard/application.js.es6 @@ -1,4 +1,4 @@ -import { buildResolver } from "discourse-common/resolver"; +import { buildResolver } from "discourse/plugins/discourse-custom-wizard/legacy/resolver"; import Application from "@ember/application"; import WizardInitializer from "./lib/initialize/wizard"; import { isTesting } from "discourse-common/config/environment"; diff --git a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 index 4f44d439..de70986b 100644 --- a/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 +++ b/assets/javascripts/wizard/components/wizard-composer-editor.js.es6 @@ -3,7 +3,7 @@ import { default as discourseComputed, on, } from "discourse-common/utils/decorators"; -import { findRawTemplate } from "discourse-common/lib/raw-templates"; +import { findRawTemplate } from "discourse/plugins/discourse-custom-wizard/legacy/raw-templates"; import { scheduleOnce } from "@ember/runloop"; import { caretPosition, inCodeBlock } from "discourse/lib/utilities"; import highlightSyntax from "discourse/lib/highlight-syntax";