refs https://github.com/TryGhost/Toolbox/issues/114
- This adapter is meant to power Files API
- The files will be stored under `/files/` in the configured content path and under `/content/files/` URL.
refs https://github.com/TryGhost/Toolbox/issues/95
- The ThemeStorage was never supposed to inherit image specific mehods. The LocalStorageBase is all it needs, might be even too much
- Look mum no saveRaw!
refs https://github.com/TryGhost/Toolbox/issues/95
- The MediaStorage adapter and LocalFileStorage were almost identical, having a common base class makes sense here.
- Having a distinct class for the "LocalFileStorage" makes it easy to spot the implementation difference from the StorageBase - the "saveRaw" method, which is not present in the StorageBase
- The LocalFileStorage will become an LocalImageStorage in next commit as that name corresponds way better to what it does!
- Test files need a good cleanup
- This comment removes the block on themes controlling card assets via config
- It also changes the default behaviour from "false" config (doing nothing) to excluding bookmark and gallery card assets
- This is essentially the same thing, as only bookmark and gallery card assets exist at the moment, but it's being done because it makes this feature future-proof for all theme developers.
- As we add new cards, all themes will automatically get the assets to make them work
- As theme developers want to, they can create their own custom assets and disble assets for any cards they support by adding them to the exclude list
- They can also remove any custom code they currently have to support bookmark and gallery cards, and set card_assets: true in package.json to use the defaults instead
- Wired up the forntend to include and serve the minified css and js card assets if they exist
- This is a very naive implementation - ideally we wouldn't have to inject this in multiple places
- This allows us to add files to src/cards and have them included in themes
- The system is currently disabled due to an override in the theme config setting assets to false
- If we register the serve public file middleware for a file that doesn't exist, this will currently throw an ENOENT error
- Instead, we want to fall back to a standard 404 so that this behaves normally
- This will be useful for the card asset service, where the cards.min.css and cards.min.js files may or may not exist
refs https://github.com/TryGhost/Toolbox/issues/95
- Media files uploaded through the Admin Media API should be accessible throught the frontend under `/content/media/`
- Note the feature is behind an alpha "mediaAPI" flag that has to be enabled in the labs first
refs https://github.com/TryGhost/Toolbox/issues/95
- The uploaded media thumbnail name should have a "_thumb" postfix to be able to distinguish thumbnails from other files. This can be handy if we decide to store them in a different location in the future.
refs https://github.com/TryGhost/Team/issues/807
The launch wizard completed flag was previously stored at per user level in accessibility column of user table, so an administrator still got the option to complete the launch wizard even if the owner had completed it previously, which is not expected pattern. This change moves the launch complete flag for Admin to common settings from per user level so a site only needs to complete the launch wizard once irrespective of which user completes it
- adds new `editor_is_launch_complete` setting to track if a site launch steps are completed in Admin
- adds new migration util to easily allow adding new setting
- adds migration to introduce new `editor_is_launch_complete` setting
- adds migration to update launch complete flag for a site if any of the users have already completed the launch steps
- Requires the new @tryghost/minifier package
- Adds a new service that will handle taking config from the theme and optionally including assets for Koenig editor cards
- It supports both css and js as cards may need one or both
- For any given config, the tool can find the matching files to include and concat and minify them into one file per type
- Currently has an override in place so that this is not yet customisable in the theme - will remove this override when we're ready for the feature
closes https://github.com/TryGhost/Toolbox/issues/111
- Just like with invalid JSON redirects files we should return a BadRequestError instead of throwing a generic 500 when the redirects.yaml file fails parsing
refs https://github.com/TryGhost/Toolbox/issues/95
- Each media file quires a thumbnail and these changes provide a capability to upload them along with media files.
- The thumbnail file is always required and has to be the format of already supported image formats
- The thumbnail should be uploaded as a part of "thumbnail" attachment in the request
- The regression tests added with this changeset will be claened up and moved to unit-tests (this is a dirty-but-working version!)
- The thumbnail always gets a name of the uploaded media file and keeps it's own extension.
- The thumbnails is accessible under the url present in the "thumbnail_url" reponse field
refs https://github.com/TryGhost/Team/issues/1115
This feature is now GA, and the flag has been hardcoded to `true`, here we clean up the
remaining references as they're no longer needed.
refs https://github.com/TryGhost/Team/issues/1190
- The assets were broken in Admin when the frontend and admin urls were different
- Fixed the issue by changing the `asset` helper to output absolute URLs when the frontend/admin urls are differents
refs 91efa4605c
- When the instance is booted without any redirects files configured it's not supposed to error but rather default to an "empty" [] redirects configuration.
- Ideally the logic shoudl not contain try/catch block at all and fail as soon as there's any error during the initialization. This wasn't changed at this time due to possible break of existing Ghost instances
refs https://linear.app/tryghost/issue/CORE-84/have-a-look-at-the-eggs-redirects-refactor-branch
- The problem this change is addressing is inability to override config values once the code is extracted into a class+DI pattern
- The work around is restarting the instance with the configuration testing expected behavior - in this case missing or existing types of redirects files
refs https://github.com/TryGhost/Members/commit/9e59f5a9
Since we have a DynamicRedirectManager for handling adding/removing
redirects at runtime, we no longer need the custom-redirects middleware.
The redirects service does however need an init method now to add the
custom redirects at Ghost boot, so it's been refactored into our Class &
DI pattern.
refs https://linear.app/tryghost/issue/CORE-121/create-a-video-storage-adapter
- Uploading large files is costly and might cause DDoS. Limits would allow fair use of the site instance.
- The configureation in hostSettings to enable an "uploads" limit would look like following:
```
"hostSettings": {
"limits": {
"uploads": {
"max": 5,
"error": "Your plan supports uploads of max size up to {{max}}MB. Please upgrade to reenable uploading."
}
}
}
```
- Read more at ba37890be4/packages/limit-service (usage)
refs https://linear.app/tryghost/issue/CORE-121/create-a-video-storage-adapter
- This is an experimental implementation of video file upload support (audio is yet to follow)
- The storage adapter still needs more thinking as it's almost the same as the "LocalStorgeAdapter" that stores images.
- Also the output serializer skipped use of url utils in favor of inline implementatoin - this should almost certainly be it's own package
refs https://github.com/TryGhost/Team/issues/1197
We were always sending the 'signin' email, rather than respecting the
email_type param passed to the API. This updates our email sending when
creating members to force the requested type if it's present.
refs https://github.com/TryGhost/Team/issues/1178
The "up" migration that this util generates correctly throws if the
pre-requisite data cannot be found in the database. The "down" migration
however was incorrectly mirroring this behaviour of throwing - which
meant that it wasn't idempotent, as it does not require a permission or
role to existing if it wants to move relations between them.
refs https://github.com/TryGhost/Ghost/pull/10534
- The original PR has no explanation around the purpose of the ref field and it's easy to forget without knowing a wider context. Documented it to remember next time we come around working on this part of the codebase!
refs 74280cfbea
- We allow to send email-only posts when using v4 Admin API, but it's possible to configure a v3 Theme with a site instance which resulta in an unsupported behavior throwing a 500.
- With this fix a 404 will be returned when an email-only post is viewed through the public email-only post URL
refs 74280cfbea
- We allow to send email-only posts when using v4 Admin API, but it's possible to configure a v3 Theme with a site instance which resulta in an unsupported behavior throwing a 500.
- With this fix a 404 will be returned when an email-only post is viewed through the public email-only post URL
no-issue
This removes the logic to check if stripe connect is allowed into the
stripe connect service, which makes the feature easier to maintain, as
well as fixes the v3 API - which previously did not have this check.
no-issue
This removes logic from the Members API controller, and into the Members
BREAD service, this allows our controllers to be simpler and easier to
maintain, as well as keeping the important logic all together.
no issue
The way GA flags were introduced means that they stop existing in the `'labs'` setting in the db and are instead forced to always return `true` when checking the flag in the labs service. However, Admin which uses the flags fetches them via the `/settings/` API endpoint which was only returning the raw labs setting db value meaning GA flags appeared to be disabled unless the flag had previously been enabled and no settings save had occured.
- updated the settings bread service to replace the labs setting value with the JSON stringified output of `labs.getAll()` which is the ultimate source-of-truth for a feature being enabled/disabled
- extracted `browse()` behaviour to an internal `_formatBrowse()` method so we can apply the same filtering/modification for output of `browse()` and `edit()`
Co-authored-by: Fabien O'Carroll <fabien@allou.is>
refs https://github.com/TryGhost/Team/issues/1115
This allows users to create Offers for Tier/Cadence pairs in order to
provide discounted subscriptions to Members! We have support for
percentage based discounts & fixed price discounts, either for the first
payment, all payments, or a number of monthly payments.
Offers also have a code, which can be used as an easy way to share them,
as visiting https://site.com/offer-code will automatically open Portal
with the Offer prepopulated.
closes https://github.com/TryGhost/Team/issues/1164
Themes can now define custom settings via their `package.json` file, and use them in templates via `@custom.{setting}`. Values for custom settings can be changed by site owners through a redesigned "Design settings" area in the admin interface.
Full announcement, documentation, and examples will be made available soon.
Co-authored-by:
- Sanne de Vries (@sanne-san)
- Thibaut Patel (@tpatel)
no issue
- author line was appearing as "Author One,Author Two,..." with no space
- fixed the generation of `post.authors` when serializing a post before it's rendered for email
no issue
- if the verification threshold is 0, `_.get(..)` becomes falsy so we
fallback to Infinity
- this is not correct - we only want to default to Infinity if the value
is not set
- this commit explicitly compares the config value to `undefined` and sets
the fallback accordingly
refs https://github.com/TryGhost/Team/issues/1175
We found the ETag header sent when serving the Admin template for /ghost/ was not changing between versions which after an upgrade could result in out of date cached content being served containing links to JS/CSS files that no longer existed.
The culprit is weak etags served by Node's `send` package, coupled with Admin template filesize not changing between versions and `npm pack` setting a fixed modification date for every file. See https://github.com/pillarjs/send/issues/176 for more details.
- updated the Admin app's controller to read the template and generate an md5 hash of the contents so we can serve a strong ETag header value when serving the `/ghost/` html
refs https://github.com/TryGhost/Team/issues/1163
We want to make the title for Offers optional, our nullable validation
means that we cannot store an empty string, so we must remove the NOT
NULL constraint from the column if we want to store either an empty
value or null.
There is a bug with editing columns in SQLite with `knex` which strips all
the indexes, so we have to manually add them afterwards.
no issue
- following on from f4fb0fcbaa,
this commit moves around some package requires in Ghost
- these are often niche packages that do something in a subsystem of
Ghost, and are not necessarily be needed to boot the application
- these packages use non-negligible CPU and memory when they are
required, so it makes sense to lazy-require them
- the concern here is that we obscure the code too much by moving
random requires further into code, but the changes are small and the
improvements big
- this commit bring the boot time since 4.19.0 down ~31% and initial
memory usage down by a total of ~12%
- we're slowly trying to draw the lines between the backend and the frontend correctly
- these files deal only with serving the frontend so they should live there
- there are lots of mixed requires in these files, so having them in the right place makes that clear
- this has been a niggle for ages, we shouldn't need to care what order our models are loaded in
- this is stopping us from having a built-in model loader and "frameworkizing" models
closes https://github.com/TryGhost/Team/issues/1172
Custom theme settings sync and cache population had been left to complete in the background as it wasn't essential for it to be complete for the front-end to start. However that was causing problems for the API where theme activation and custom theme settings list requests happen very close together, with the latter often not containing the theme settings data when it is expected to.
- changed `activationBridge.*` methods to `async` so they can `await` the completion of custom theme settings sync before activating a theme
refs https://linear.app/tryghost/issue/CORE-1/multiple-adapters-per-type
- Having this preemptive change allows to separate implementation of "image" storage from future usecases like "videos", "audios" etc. Even if the "image" adapter is not configured the default behavior will fallback to use the "active" storage adapter. If there's a need to handle "images" differently through a custom apapter that'll work out of the box ;)
refs https://linear.app/tryghost/issue/CORE-1/multiple-adapters-per-type
- When the storage is requested the caller can now specify a "feature" they want to use the storage for. For example there could be different configurations for "images" or "vidoes" storages and the caller would not necessarily have to know about the details of how the feature is implemented.
refs https://linear.app/tryghost/issue/CORE-1/multiple-adapters-per-type
- There's a need to support multiple adapter variations per given adapter type (storage, sso, etc.)
- With the introduced changes we can specify a version of an adapter that should be fetched based on `:feature` postfix. For example:
`adapterManager.getAdapter('storage')` - would return the default adapter listed under "active" configuration
`adapterManager.getAdapter('storage:videos') - would return an adapter configured for videos *feature*
- Here's an example configuration for a custom video storage:
```
"storage": {
"active": "LocalFileStorage",
"videos": "ghost-storage-custom-video",
"ghost-storage-custom-video": {
"custom": "configHere"
}
}
```
- This is a minor refactor to reorder functions, use async/await & fat arrows
- It will make the coming bigger refactor slightly easier to read :D
- This should not make any functional changes
closes https://github.com/TryGhost/Team/issues/1150
Our override of the base Bookshelf `insert` operation so that our own `formatOnWrite()` method is called on attributes was working on a false assumption that an `attrs` attribute is passed in as it is for the `update` operation. Instead Bookshelf's base update uses the `model.attributes` values to create an `attrs` object that is then passed through the usual `.format()` method meaning that our `insert` override was not actually doing anything.
- added a failing regression test for the `formatOnWrite()` override behaviour
- adjusted our insert/update overrides to set an internal `_isWriting` property on the model, then if that property is true our `.format()` override (which is called by Bookshelf on a generated `attrs` object during inserts) we manually call our `.formatOnWrite()` method
- updated both overrides even though `update` was working for consistency and less cognitive overhead for reasoning between two different approaches
refs https://github.com/TryGhost/Team/issues/1149
Ghost allows different themes to have the same `name` value in `package.json` but gives them a custom name based on the zip file used to upload it. The custom theme settings service however was always using the package.json name meaning that when syncing a theme with a duplicate name it was overwriting the settings for all other themes.
- bumped `@tryghost/custom-theme-settings-service` which changes the `.activateTheme()` signature to accept a theme name as the first argument
- updated our calls to `.activateTheme()` to pass Ghost's known theme name as the first argument so we're always syncing with the correct `theme` field value in the `custom_theme_settings` table
no-issue
* Removed NOT_NULL constraint from stripe_coupon_id
When handling disconnecting from Stripe - we remove all Stripe data from
our database to ensure we do not have bad/invalid data stored. Removing
this constraint will allow us to set the value to NULL.
* Added created_at column to offer_redemptions
Offer Redemptions are not just a joining table, but an event. A created_at
date allows them to be ordered
Because this is in alpha it is simpler to just drop the tables and
re-add them, due to offer_redemptions depending on offers, we also drop
this table and re-add it.
refs https://linear.app/tryghost/issue/CORE-1/multiple-adapters-per-type
- While digging around this area refactored the code to use more readable async/await syntax instead of chaining then's
- Dropped unneeded "catch" block in save method as all it was doing was rethrowing an error with out additional handling
refs https://linear.app/tryghost/issue/CORE-104/decouple-frontend-routing-events-from-urlserver-events
- The "bootstrap" didn't give enough credit to everything this module was doing - it's responsible for managing correct initialization and reinitialization of the frontend Routes as well as passing router creation information back to the frontend's URL service
- The refactor is done in two steps - the "bootstrap.js" will be renamed in the follow-up commit to have a clean history of how the file evolved
refs https://linear.app/tryghost/issue/CORE-104/decouple-frontend-routing-events-from-urlserver-events
- "routerCreated" call was causing a need to create a dependency on the frontend Router level which didn't fit nicely with the refactor of the bootstrap into a class, it's also makes way more sense having it as an independent parameter instead of a call on a module (makes testing way more readable too!)
refs https://linear.app/tryghost/issue/CORE-104/decouple-frontend-routing-events-from-urlserver-events
- The 'settings.timezone.edited' event triggers a roundtrip chain of calls in the frontend routing to the url services. It was all handled by event listeners and handler that clearly don't belong there.
- Extracted event realted listeners/handlers into methods and moved most of the logic to the "bootstrap" module, which soon is going to become a "RoutesManger"
- The result of this refactor - no more events going back and forth between frontend routing and the backend!
- The files in the public folder are all specific to the frontend:
- ghost.css / ghost.min.css contains styles for default templates like unsubscribe, private and error
- favicon is the default for the frontend
- robots.txt is the default robots.txt for the frontend
- sitemap.xsl is the stylesheet for the sitemap
- Therefore, they should live in the frontend!
- core/shared/i18n is no longer used. Remove it to prevent temptation!
- this class needs merging with the one in themes, but for now just co-locate them as that's quicker and easier
no issue
- right now, we mount all API endpoints (v2, v3 and canary), alongside some
other routes, when Ghost is booting. This is wasteful because we don't
necessarily need any of the endpoints to get Ghost up and running
- even when Admin is used, it uses `canary` so `v2` and `v3` sit in memory
- the better approach here is to lazy load these endpoints, so they only
get mounted when needed
- this commit adds the `lazyUse` function into our Express lib,
which takes a mount path and a module function to execute down the
line. This gets passed to the wonderful `express-lazy-router` lib which
detects when we're calling an unmounted module and will mount it for
us
- from local testing, this speeds up boot time by about 18% and reduces
initial memory usage by about 6% 🚀
refs https://github.com/TryGhost/Ghost/issues/13380
- Now that i18n.t has been removed everywhere, we can cleanup the final usages
- Still TODO: merge the i18n logic into themeI18n, and get rid of shared/i18n entirely
refs https://github.com/TryGhost/Team/issues/1138
Portal offer urls use offer id to load offer details UI and stripe checkout in frontend. This endpoint allows portal to fetch offer details from offer id to create Portal UI with relevant details for potential members.
refs: https://github.com/TryGhost/Team/issues/759
- The match helper allows for basic equals and not equals comparisons,
Example:
{{match title "=" "Getting Started"}}
{{match slug "!=" "welcome"}}
- There's a lot more functionality we want to add here, so that it ends up being a replacement for {{#has}}
- However, this first iteration is already useful, especially in the context of custom theme settings
- Therefore we are adding it early, and will document it along with custom theme settings when that goes GA very soon
refs https://github.com/TryGhost/Team/issues/1107
- updated schema validation to allow `'image'` through as a known setting type now that Admin has support
- added transformation of setting values for `'image'` types because they will be URLs and should be stored with `__GHOST_URL__`
refs: https://github.com/TryGhost/Team/issues/759
- No matter what, a handlebars helper outputs a string. So if you return true, you'll always get 'true'.
- SafeStrings are handlebars's way of passing around a string whilst also maintaining a record of the original value e.g. new SafeString(true) results in {string: true}
- We need this for the match helper, so that we know when doing a comparison that we're meant to be comparing against a boolean true, not a string true
- Therefore, we need to putput SafeStrings, but also process them when passed in
The logic
- Figuring out the correct logic here has been a little tricky but essentially:
- {{match safestring}} with a single arg, will return true for any truthy value
- {{match safestring "=" true}} does a direct comparison with the original value of the safe string, so if it was a boolean true, the match will be true else false
- {{match (match something) "=" true}} will therefore work for any level of nesting
- this can result in slightly inconsistent results, but feels correct and documentable
This is documented extensively through the test cases
refs https://linear.app/tryghost/issue/CORE-103/decouple-internal-frontend-code-from-url-module
- We need to decouple all frontend services from URL service as much as possible. "bootstrap" module is now a central point to substitute (proxy really) function previously done by the URL service and this move changes direct usage of URL service to "bootstraps" internal proxy function
refs https://linear.app/tryghost/issue/CORE-103/decouple-internal-frontend-code-from-url-module
- By becoming a parameter in the routing bootstrap process URL is Service no longer a "require" inside the frontend controllers but rather becomes a part of the "internal API" of the bootstrapper. This is not the end form of it, rather a step closer to decouplint routing from the URL serivce.
- The bootstrap module needs a facelift to have cleaner distinction between init/start methods. This is left for another time
refs https://github.com/TryGhost/Team/issues/1106
- updated schema validation to add `'boolean'` as an allowed `type` value
- added `format()` and `parse()` methods to `CustomThemeSetting` model to match `Settings` model behaviour for boolean-type settings
refs https://github.com/TryGhost/Team/issues/1132
This will be used to store Offer Redemptions, which will be used to list
the Offers which a Member has redeemed, as well as the number of times
an Offer has been redeemed.
no-issue
The MembersAPI using the OfferRepository coupled it to the internals of
the Offers Module - instead we pass the "external" API - so that we can
change the internals, and not have to update the MembersAPI's usage.
refs https://github.com/TryGhost/Team/issues/1131
This will allow Ghost Admin to display separate lists of active and
archived Offers! We must pass the options through so that the
OffersService is able to handle the filter passed in the request.
no issue
Boot-time metrics are really valuable for understanding how releases affect the performance of Ghost. The new @tryghost/metrics package lets us ship these metrics to different sources using the same configuration framework as @tryghost/logging introduced.
no-issue
Because adding and editing Offers can update the redirects for Ghost, we
should ensure that any site cache is cleared so as to not conflict with
Offer redirects.
no-issue
Because we only called `init` if the labs flag is enabled, when starting
up a site without the flag enabled - the listener for adding redirects
wasn't active. So new Offers would not have their redirects setup.
- Ghost has a set of core packages that it is safe to require directly in any file - tpl is one of them!
- This keeps the DI signature smaller and easier to reason about
refs c33b596e9c
- initializing the themes service before the custom theme settings service meant that `customThemeSettings.api` was not available when it's called as part of theme initialization
- swapped the boot order around so that the supporting service initialization is performed before the full themes service is initialized
refs https://github.com/TryGhost/Team/issues/1104
- bumped `@tryghost/custom-theme-settings-service` so it throws a more appropriate `ValidationError` when setting keys don't exist or a select value is not known
- changed the custom theme settings service to have a `.init()` method which creates an instance of the service under `.api` so that we're able to create the instance at a particular point in the boot process when we know the models have been initialised
- there were problems in tests because the service was being initialised through the require chain before models were initialised through the boot process
- fixed incorrect `camelCase` of resource name in API responses
refs: #13380
- The i18n package is deprecated. It is being replaced with the tpl package.
Co-authored-by: Aleksander Chromik <aleksander.chromik@footballco.com>
refs: #13380
- The i18n package is deprecated. It is being replaced with the tpl package.
Co-authored-by: Aleksander Chromik <aleksander.chromik@footballco.com>
refs: #13380
- The i18n package is deprecated. It is being replaced with the tpl package.
Co-authored-by: Aleksander Chromik <aleksander.chromik@footballco.com>
refs: #13380
- The i18n package is deprecated. It is being replaced with the tpl package.
Co-authored-by: Aleksander Chromik <aleksander.chromik@footballco.com>