refs https://linear.app/tryghost/issue/CORE-35/refactor-route-and-redirect-settings
- The only allowed route settings name is 'routes.yaml', which removes a need to parameterize the function as the location is permanent anyway
- Simplifying the function in any possible way before extracting the common bits into an external lib
refs https://linear.app/tryghost/issue/CORE-35/refactor-route-and-redirect-settings
- Frontend is not meant to know about the underlying source of the "routes" configuration, so any reads/edits/validations are being moved into a backend service. This should also simplify the coupling of the backend with the frontend where the latter will get a JSON blob with all needed configuration during the boot
- Nother problem the "get" method had was hiding an underlying function it was doing - reading the file from the filesystem SYNCRONOUSLY. It might be a thing we need to do during the "web" app initialization, but there's no clear need to do this in a sync fassion during the bootup for example. Also having a more explicit name should help :)
refs https://linear.app/tryghost/issue/CORE-35/refactor-route-and-redirect-settings
refs c1c9bf0866
- Actions logic related to file system operations (like ensuring files exist) should be done on the backend. Now the route settings initialization logic lives on the backend it makes sense to keep the file closer to the source.
- The move is the opposite to the one refed in the commit with a
difference that the file now lives in "route-settings"
refs https://linear.app/tryghost/issue/CORE-35/refactor-route-and-redirect-settings
- 'knowSettings' was based on a "configurable" array of settings that might be configured in Ghost. The multitude never happened! The only setting the frontend takes care of is routes.yaml file (redirects is also kind of a setting but is a separate concept for now).
- Having just one type of file to deal with allows to simplify implementation significantly, which helps before a big refactor
refs https://linear.app/tryghost/issue/CORE-35/refactor-route-and-redirect-settings
- This is a micro-step towards getting rid of multiple "knownSettings" concept. Since the introduction of an array of knowSettings there was never-ever a need to handle anything but a single `routes.yaml` file. Getting rid of this concept first to have a simpler module. Next step would be getting rid of filesystem reads/writes in the "frontend"
refs https://github.com/TryGhost/Team/issues/1070
- added `@tryghost/custom-theme-settings-service` as a dependency
- `core/server/services/custom-theme-settings` creates an instance of the new service passing in the model used for storing the setting keys/values and a cache instance
- requiring `core/shared/services/custom-theme-settings-cache` creates a cache instance, it has no dependencies so can be required anywhere and the first require will initialize the shared instance
- updated the theme activation bridge to trigger the theme settings service to sync the newly activated theme settings and populate the cache
- updated theme validation to pass `labs` through as an option so that we get custom theme settings back as part of the checked theme as that's what is passed to the custom theme settings service
refs https://github.com/TryGhost/Team/issues/1063
Member activity is a labs alpha feature which aims at capturing member events for site owner if switched on. The event metadata captures the site page/post where the event originates from, and the post/page id is included as content of new ghost analytics meta tag. The meta tag is only aded on the site if member activity is switched on from labs.
closes: CORE-34
refs: https://github.com/TryGhost/Team/issues/1044
- this is a super basic fix, it adds a max nodes concept and limits the node in each sub-sitemap to 50k by default
- this will prevent the error in google console
- a better fix is in progress, but we want to at least solve the errors ASAP
closes: CORE-33
Two bugs:
- lodash isEmpty and handlebars util isEmpty are not the same
- I literally had the truthy and falsy cases the wrong way around 🙈
Notes:
- I have, for now, copied the isEmpty util from handlebars. It's so small it doesn't seem worth trying to require the util right now, although in future it'd be nice if that was easier to do
- Adding the management for the conditional being a SafeString allows the match helper to be a subexpression of itself, I can see this pattern being useful later in combo with the any and all helpers
refs https://github.com/TryGhost/Team/issues/1004
- adds new `{{products}}` helper behind `multipleProducts` flag
- `{{products}}` outputs a string with list of products that have access to specific post when used in a post context in theme
- outputs empty string when used out of a post context and without access to `visibility` property
- uses all available posts for a site via the global products data
- updates {{content}} helper cta to use this new helper to show list of tiers with access to post
refs https://github.com/TryGhost/Team/issues/990
- Relying on uuid instead of slug makes the posts less discoverable and partially soves discoverability through overriden robots.txt files
refs https://github.com/TryGhost/Team/issues/953
- Emails posts should be not explorable by the rest of the frontend similarly to the draft or scheduled posts. Email posts should also keep the content gating, so that specific parts of content can still be gated based on the post's visibility setup
- A separate frontend router was chosen to implement this part of the system instead of a moutable express app due to increased complexity to introduce the latter approach.
- All "sent" email-only posts will be accessible through the `/email/:slug/` route
refs https://github.com/TryGhost/Team/issues/948
- The frontend route `/email/:uuid` is aliased to the preview as a temporary solution. It fulfills the premise of the email-only post anyway - not being accessible publicly and only shared through email.
- The tests for the new route are missing as adding them was way more problematic than I envisoned. They are in the works and will be added as a follow up commit next.
refs https://github.com/TryGhost/Team/issues/907
The logic to populate the `@price` data and the `@products` data both
rely on the same product data, but were each making their own request to
the API. This refactor removes the request from the legacy `@price`
data, which should cut the database queries in half.
refs https://github.com/TryGhost/Team/issues/527
refs https://github.com/TryGhost/Ghost/issues/10790
- Frontent has to have as few as possible coupling points with the Ghost Server API. By design that point has been a "proxy.api" property that will become more and more constraint in the future based to limit the surface of frontend interaction with servers's API
- Removing `.../server/api` requires in favor of using a proxy decreases direct coupling
- This isn't really a "service" - it's a set of utilities for working with labs flags
- It's also required all over the place, and doesn't require anything that isn't shared
- Therefore, it should live in shared
- This isn't really a "service" - it's a set of utilities for working with labs flags
- It's also required all over the place, and doesn't require anything that isn't shared
- Therefore, it should live in shared
- This is a precursor to trying to split apart into:
- model events + webhooks system which makes sense
- frontend events which should be independent or removed
- maybe some concept of a settings manager that we can use in various places to bind logic 🤔
- other usages of events that should be refactored to not use events
refs: 076ad99593
- as of 076ad99593 we no longer use the error property of the active theme anywhere
- cleaning up and removing this usage reduces the code pathways and makes the init fn a bit clearer
refs https://github.com/TryGhost/Team/issues/856
- This syntax gives easier understanding of modules dependencies and improves searchability. For exampke, I was looking for all "api" uses exposed by the server proxy and didn't have a clear picture into which modules used it.
- The change was made during a short-lived try to limit the use of "api" in the server proxy :) I thought it would be helpful when bumping the defult server API exposed internally. Next time!
- The router bootstrap is no longer allowed to fetch it's own settings, but rather is passed them
- This moves the call to the site routes.js file, which isn't much better but it's a start
- The goal is to always pass these in from the boot process, or from the bridge reloader
- Reduced the number of levels in our debug naming in the frontend
- Unified components like "themes" and "routing" under one name
- Should help to make debug slightly more useful again
refs https://github.com/TryGhost/Team/issues/849
With multiple products, we have re-enabled segmentation by product for posts behind alpha feature flag. This change handles the default content cta to show custom message if the post's access is restricted to specific products when behind the flag.
refs https://github.com/TryGhost/Team/issues/781
refs 813d288eb2#
- The 500 error was introduced through a refed commit long time ago when (probably?) there were no other safeguards preventing from serving content through a theme with errors. Since than we have multiple safeguards when aploading/activating the theme with errors and the default handling when such error occurs is more graceful - a 400 with specific error details is shown
- We need this change to land before bumping gscan that introduces more suphisticated error detaction in theme templates. Otherwise, people upgrading to new version and having an error on an unused template or somewhere undetected previously woul end up with a bricked site showing a generic 500 - not a great experience!
refs: fbf0636936
- I renamed this pattern in a bunch of places, but missed a few, leaving the code messy and confusing
- This makes the naming consistent
refs https://github.com/TryGhost/Team/issues/845
refs 517d2abc5c
- updated router response formatting functions and `{{#get}}` helper response handling to make any `feature_image_caption` properties in the response a `SafeString` instance so triple-curlies are not needed when using the property in themes
- The main aim here is to end up with a simple and clear public API for the meta module
- Secondarily, we want to make it a bit clearer which bits don't really belong here so we can see what to do with them
- To achieve this, the main logic has been moved into get-meta (although there's still some logic here which needs moving further)
- The index.js now has a small clear public API, and the proxy, which is the only way this is consumed, is able to use the public API directly
- This function is quite different to the others, as it generates an excerpt from HTML (truncating)
- Most functions in the meta data folder just contain content negotiation logic, like if post then feature_image else cover_image type things
- This function is more like a library and shouldn't live in Ghost, it should probably be in @tryghost/helpers
- It's definitely something we'd love to rewrite to work better tooooo
- getting rid of instances of new Error as we should always use @tryghost/errors
- Whilst here, got rid of i18n but discovered the messages were missing!
- This is my fault, they disappeared when I removed external apps and clearly removed too much: 8c1a0b8d0c (diff-0f5cc40aa8906a1be1bad2002a35361bbf9e766e46b3b29be10f4f479265426a)
- Therefore, I have restored these messages in the places where they were used, except amp_content, where I have written a new message, as the message that was there was not relevant
- This is part of the quest to separate the frontend and server & get rid of all the places where there are cross-requires
- At the moment the settings cache is one big shared cache used by the frontend and server liberally
- This change doesn't really solve the fundamental problems, as we still depend on events, and requires from inside frontend
- However it allows us to control the misuse slightly better by getting rid of restricted requires and turning on that eslint ruleset
- At the moment the bootstrap.start method asks the settings service for its settings
- This couples the routing and settings services together - when maybe we want to use a different method to generate settings
- By passing the settings to the routing service at the right time, we open up possibilities for refactoring
- The main goal here is getting this settings related code out of the routing service as it really doesn't belong there
- This settings file is used purely by the API to get and set files - its not really anything to do with actual routing
- This file calls out to the bridge to do a reload, which helps decouple slightly
- More refactoring is needed to get rid of the urlService dependency
- Note this file is really similar to the redirects one, it would be good to merge them
- At the moment the bootstrap.start method asks the settings service for its settings
- This couples the routing and settings services together - when maybe we want to use a different method to generate settings
- By passing the settings to the routing service at the right time, we open up possibilities for refactoring
- broken down large function into smaller functions to reduce repeated code
- try to make this and the redirects equivalent look similar
- this code is the getter and setter for the API
- TODO: I think this can be further refactored into a settings file class
- Allows for slight decoupling of API and frontend with route settings being updated
- Activate theme now calls the same codepath to reload the frontend
- Yet another step on the path to make it possible to init/reload/run the frontend independently from the server
no refs
- without crossorigin=anonymous attribute, browsers obfuscate error messages from external scripts, which makes error tracking with sentry impossible
- with crossorigin attribute, portal script needs to be served with cors header or browsers will block it
- unpkg already serves the script with `access-control-allow-origin: *`
- Part of the effort to split Ghost down into smaller, decoupled pieces
- Moved out our internal validator tooling to a separate library
- Replaced all usage of our own tooling and validatorjs directly with @tryghost/validator
- Removed the validatorjs dependency and removed the renovate pin
- This gives us a consistant, smaller, clearer public API for validations
- It will eventually be used on Ghost Admin too
- This way we can start getting up to date with validator whilst not increasing build size
no issue
The only pieces of Ghost-Ignition used in Ghost were debug and
logging. Both of these modules have been superceded by the Framework
monorepo, and all usages of Ignition have now been removed, replaced
with @tryghost/debug and @tryghost/logging.
- Traditionally all of Ghost's public-facing text was written in British English
- We're changing that to US English because that's more common
- US English should also be used in code e.g. properties are called color not colour
- most of these changes are in comments, but I've changed them so that we have US English in front of us always
- fixed a few other typos I noticed whilst there
closes https://github.com/TryGhost/Team/issues/761
With multiple products, each product can have an active monthly/yearly price, so we no longer store the monthly/yearly price ids in global settings but instead store them in product table directly. This means we need to update our global `@price` helper to also use the updated schema and use the monthly/yearly prices from product table instead of settings data.
refs: https://github.com/TryGhost/Team/issues/759
- wired up a matchHelper feature flag & used the labsEnabledHelper tool to gate the helper
- added a first version of the match helper, which is intended to replace the has helper
- this is an experimental helper and may or may not make it to GA
- match is a simple comparison helper, right now it does a very basic equals or not equals comparison
- much more functionality is needed to reach parity with has
- we don't need to use _.escape from lodash as we already have escapeExpression from handlebars
- it's more correct to use the escape utility from our theme engine when escaping strings _for_ our theme engine!
- Note there is a minor difference between the two:
- Lodash: &, <, >, " and '
- refs: https://lodash.com/docs/4.17.15#escape
- Handlebars: &, <, >, ", ', ` and =
- refs: https://handlebarsjs.com/api-reference/utilities.html#helper-utilities
- This could cause slightly weird behaviour in themes around ` and = characters, but as it's just convering to html entities it should be fine
refs https://github.com/TryGhost/Team/issues/708
- Defaults to an empty array on `@products` so we have valid data
(product should be null if products isn't)
- This is the first step toward supporting multiple products at the
theme level
closes https://github.com/TryGhost/Ghost/issues/12986
refs 1345268089
As part of changes in 4.6, the default price ids for monthly/yearly prices are stored in new settings - `members_monthly_price_id`, `members_yearly_price_id` - which are used to determine current active prices for the site from list of all existing prices. While the last commit updated the prices to use the settings, the data for currency was still used from non-zero prices instead of the new settings value.
- Updated tests to check price currency
closes https://github.com/TryGhost/Ghost/issues/12980
closes https://github.com/TryGhost/Team/issues/730
As part of changes in 4.6, the default price ids for monthly/yearly prices are stored in new settings - `members_monthly_price_id`, `members_yearly_price_id` - which are used to determine current active prices for the site from list of all existing prices. The `@price` helper was incorrectly still relying on the old logic for active monthly/yearly price using the first active price with matching nickname, and resulted in showing incorrect price data on the theme.
- Updated tests to check price data using settings value
no-issue
Since we now allow archiving prices, we should filter them out from
being considered the monthly or yearly plan, as they are unable to be
subscribed to.
no-issue
Themes which use the `@price` data will have a 400 error if they are not
setup prices. This adds default price data so that the theme will not
error.
refs https://github.com/TryGhost/Team/issues/668
Since we no longer store price data in the settings we must use the api
to read the stripe prices for the default price, so that we can maintain
backwards compatibility for the `@price` data in themes.
- we need the basePath concept for the main i18n class so we can pull it out into a module
- we already had this in the themeI18n class, so I just had to move it up
- also I added a default of __dirname, so we don't have to declare this constantly in the tests
- Reworking the location of i18n in boot has fixed the main error
- However, many of our tests depend on i18n being loaded but don't explicitly call init
- There are many ways we could fix this in our tests, but I don't want to spend more time on this now
- final preparation for moving i18n out of Ghost core
- logging is passed in via DI
- theme i18n needs a config value, but no need to pass all of config for one parameter, a better pattern is to pass the one value needed
- preparation for moving the base class out of Ghost
- refactored so that all the logic for file loading and fallbacks live in the base class
- theme i18n now only overrides init with the properties it needs, filepath generation and error handling
- this makes it much easier to move the i18n file out, and eventually have theme i18n live elsewhere too
- also prepares for using DI for logging
- when activating a theme, we need to load the current locale
- this request used to be buried deep in the themeI18n init call
- now we surface it in the bridge and pass it down, which is closer to what we want to do with eventually initialising the frontend
with everything it needs up front (or not initialising it, if it isn't needed)
- in the related helpers we depend on the site.locale value instead of proxy -> themeI18n -> settingsCache drastically simplifying the code and removing deep requires
- site.locale is updated via middleware and can be relied upon
- the core i18n library and theme i18n library have slightly different methods of getting a candidate string
- both of them use forms of jsonpath, meaning they both require jsonpath as a dependency
- to try to get to a point of being able to rip more things out of ghost, we want to have less dependencies
- so instead of overloading the method, we pass in a stringMode as an argument
- eventually we might not need an overloaded class for themeI18n at all, which would simplify the codebase
- preparation for using DI instead of requires, so we can move this out of Ghost
- have done this for both the main i18n and theme i18n file
- refactored the constructor
- single authors were deprecated in v1.22 when we added multiple authors
- we always thought we'd clean this up a lot sooner, but it's stuck because it's an annoying thing to break people's shit over
- still saying "remove in vX" isn't useful, we need to know how long a feature has been deprecated so we can judge whether it's safe to remove
refs 829e8ed010
- i18n is used everywhere but only requires shared or external packages, therefore it's a good candidate for living in shared
- this reduces invalid requires across frontend and server, and lets us use it everywhere until we come up with a better option
- Having these as destructured from the same package is hindering refactoring now
- Events should really only ever be used server-side
- i18n should be a shared module for now so it can be used everywhere until we figure out something better
- Having them seperate also allows us to lint them properly
refs: bf0823c9a2
refs: ae86254972
- continuing the work of splitting up the theme service into logical components
Themes Service
- The serverside theme service now serves just the API and boot
- It loads the theme and passes it to the theme-engine via the bridge
This achieves the bare minimum goal of removing all the cross requires between server and frontend around themes
There is still a lot more to do to achieve an ideal architecture here as laid out in ae86254972
refs: bf0823c9a2
- continuing the work of splitting up the theme service into logical components
- am about to move the theme service to core/server so it should require i18n directly
refs: bf0823c9a2
- continuing the work of splitting up the theme service into logical components
- this file is not part of the theme engine so it should use the bridge not the engine
- am about to move the theme service to core/server so this will make even more sense then
refs: bf0823c9a2
- continuing the work of splitting up the theme service into logical components
Theme activations are a trickier piece of the theme split puzzle because they are called from the API and theme service on boot in different ways.
Activations require a theme to have been validated at different levels. Validations are also tricky - do they belong to the theme engine, or the theme service?
There are then several different flows for activations:
- On Boot
- API "activate" call
- API override on upload or install via setFromZip, which is a method in the storage layer
These calls all have quite different logical flows at the moment, and need to be unified
For now, I've moved the existing "activate" function onto the bridge. This allows the theme service to be split from the frontend, and refactoring can start from there.
I hope to move this so there is less code in the actual bridge very soon, but my goal is not to require any server packages in the frontend part of this
I think ideally:
- all activation code, including validation, should probably be part of the theme engine
- the theme engine should offer 3 methods: getActive() canActivate() and activate()
- the theme service is then only responsible for loading themes in and out of storage, JSON responses for the API, and handing themes to the frontend via the bridge at the appropriate moment
- Modules in /shared are supposed to be standalone modules that can be required by the server or frontend
- As the server shouldn't require the frontend, and vice versa, shared modules should require neither
- Otherwise it just becomes a crutch for allowing cross-depenencies, and will create circular dependencies
The Bridge
- The bridge file is not meant to be a crutch sat allowing cross-dependencies, but rather a new component that manages the flow of data
- That data flows from the server/boot process TO the frontend, and should not flow in the other direction
- The management of that flow of data is necessarily hacky at the moment, but over time the architecture here should get clearer and better
- Still, for the time being it will need to handle requiring across components until that architecture matures
- Therefore, it should live in core root, not in core/shared
refs: bf0823c9a2
- continuing the work of splitting up the theme service into logical components
- This one is a little more involved, as the i18n initialisation was unnecessarily spread over several locations.
- I moved it into being part of the ActiveTheme class and called in the constructor, meaning we don't need the services.theme.activated event anymore as the constructor is called in the same cases.
- Also moved the event listener for locales into the bridge, as I don't want that inside of theme-engine, and we don't want circular dependencies. We'll figure out a wayto refactor this soon too.
refs: bf0823c9a2
- Added a new bridge class that lives in shared. This should eventually be responsible for all cross-communication between the frontend and the server
- Having all the gnarly shared bits in one place should help us refactor more easily
- For now it also reduces requires between the core/server and core/frontend folders that are meant to be separate
- All calls to getApiVersion have also been renamed to getFrontendApiVersion, as this is different to the "default" API version
- Slowly getting to the point where frontend/services/themes can be moved to server/services/themes :)
refs: bf0823c9a2
- continuing the work of splitting up the theme service into logical components
- this is where it starts to get fiddly as the getActive function in themeService index is required across the frontend/backend mostly due to its use in the getApiVersion method
- for now left one usage of the getActive method in place in ghost-locals middleware ready for the next phase of the refactor, which will move some of the themeService index into a shared location
refs: 9f50e941eb
refs: bf0823c9a2
- Still working towards splitting the theme service into logical components
- The engine defaults were required in the index file, in a way that creates tight coupling across what would otherwise
be distinct components
- Also meant there was another hardcoded 'v4' in the codebase
- This fixes both issues by depending on the value from config
- Currently this adds Yet Another Config Require, but it should be fine for now until we have a new pattern for the frontend
- Note: We only care about the ghost-api engine, we used to care about both ghost and ghost-api. Now that there is only one there was no need for the more complex code structures
refs https://github.com/TryGhost/Team/issues/579
- when members signup is enabled returns `#/portal` otherwise returns feedly subscription URL
- allows for themes to have subscription buttons without condititionals, eg `<a href="{{@site.signup_url}}">Subscribe</a>`