refs https://github.com/TryGhost/Team/issues/1500
The newsletter table schema has bunch of changes to go through for new and existing columns, this consolidates the schema changes into a single re-create table migration that drops and adds the newsletter table with correct schema. The table re-create migration needs to run before any of the tables using newsletter as foreign key. The changes include -
- new columns for design related fields
- new slug column for filtering
- unique constraint to `name` column
- remove `default` column (noops the existing default column migration)
- `sender_reply_to` has a default of newsletter and a validation of ['newsletter', 'support']
- updated default values for `subscribe_on_signup` and `recipient_filter`
refs https://github.com/TryGhost/Team/issues/1248
This is the underlying cause of the problems we've seen whilst handling
Stripe webhooks. A transaction ensures that the operations are atomic,
but not that they can run concurrently.
If you have some code which does this, concurrently:
1. Starts a transaction
2. Reads a value
3. Changes the values
4. Ends the transaction
Without applying the `FOR UPDATE` lock - you will have both sequenes
read the same value at step 2.
With the `FOR UPDATE` lock - one of the sequences will hang at step 2,
waiting for the other transaction to end, at which point it will resume
and read the _changed_ value.
Because the `edit` method explicitly does a read followed by a write, we
have also add the `FOR UPDATE` lock to this by default, to avoid any race
conditions. This does however require that `edit` is called within a
transaction. An issue here https://github.com/TryGhost/Team/issues/1503
considers running in a transaction by default.
refs https://github.com/TryGhost/Team/issues/1471
- This is a many-to-one relation so that many posts can be linked to a specific newsletter
- The `newsletters` table had to come first in the schema file so that it's initialized before the `posts` table (because of the foreign key)
- Updated the model to make sure the new field doesn't leak in the API for now
- This migration isn't using the `createAddColumnMigration` util because of a performance issue. In MySQL, adding/dropping a column without `algorithm=copy` uses the INPLACE algorithm which was too slow on big posts tables (~3 minutes for 10k posts). Switching to the COPY algorithm fixed the issue (~3 seconds for 10k posts).
- SQLite isn't using the codepath where we run a raw SQL query because `knex` is doing multiple queries to add/remove a column
refs https://github.com/TryGhost/Team/issues/1469
We have an after hook to update `sort_order` for pivot tables after update as base bookshelf plugin. Since new `members_newsletters` table doesn't has a `sort_order` column on it, this change disables the after hook for it to avoid errors.
refs https://github.com/TryGhost/Team/issues/1469
- updates member model to add relation to newsletter via pivot table
- updates member api serializer to include newsletter data
- updates tests
refs https://github.com/TryGhost/Team/issues/1463
- This enables listing, creating and editing newsletters
- The tests are commented out as the permissions will be added in a follow-up commit
- we send the roles data array in when we're changing the role of the
user
- if we send an empty array, we don't want to edit the user's role
- the code _thought_ that's what it was doing, but we only check the
falsiness of the array, which is truthy for `[]`
- it also needs to check the length of the array
- this commit includes a test which would fail with a 500 error without
the fix
refs https://github.com/TryGhost/Team/issues/1284
When you create a new post with a tag slug that contains spaces, those spaces will get replaced by dashes. But instead of reusing an existing tag, a new tag is always created.
- New tag slugs are cleaned up before matching with existing tags in the Post model onSaving method
- Cleaned up multiple loops in onSaving of Post model
- Cleaned up syntax when cleaning up tag slug
- Added tests for slugs with spaces
- Added test for too long tag slug causing duplication
refs https://github.com/TryGhost/Team/issues/1408
- switched from `@nexes/nql` to `@tryghost/nql` and bumped `@tryghost/bookshelf-plugins` to get access to the latest NQL version across the app
- adds "contains" operator support
- `:~'string'` - contains
- `:-~'string'` - does not contain
- `:~^'string'` - starts with
- `:-~^'string'` - does not start with
- `:~$'string'` - ends with
- `:-~$'string'` - does not end with
- enables `'` escaping in strings, eg `'O\'Nolan'`
- We have an existing pattern for using `visibility: public` instead of `visible: true|false`
- We no-op the existing migration and roll forward so that we don't have to manually revert db changes
refs https://github.com/TryGhost/Team/issues/1387
We are moving away from the portal_products setting to instead store
each tiers visiblity on the tier itself. This column will be used for
that data.
Both of the default Tiers should be visible, but newly created tiers
should not be.
refs https://github.com/TryGhost/Toolbox/issues/202
- this code suffers from two problems:
- when we don't have any new settings to insert, we still end up
fetching the columnInfo and owner info, even though we only need
them if we're inserting data. This results in 3 extra queries upon
boot
- secondly, we insert every setting with a separate query - MySQL and
SQLite both support batch inserts and knex has a utility to help us
that I've [used
before](38821c5242).
With 95 settings at the time of writing, this adds 94 extra queries
during the DB init
- this commit refactors the code so that we only fetch the columnInfo and
owner data if we've got new settings to insert, and batches the
inserts using knex's batchInsert util
- this query results in ~95 less queries during DB init and saves a
couple of queries during boot
refs https://github.com/TryGhost/Team/issues/1071
Default visibility for a post when set to specific tiers needs special handling as data for specific tiers is stored as an array of tiers on a pivot table. This change handles the default visibility for a new post when set to specific tiers to generate the right default values in model.
no-issue
Knex.js aliases "bool" -> "boolean" - this means that you can use either
one in our schema.json file and it'll correctly create the column. Our
model plugin however would only handle columns which used the "bool"
alias. This fixes the plugin to handle both strings.
refs https://github.com/TryGhost/Team/issues/1252
We need a way to signal whether or not a Tier is active or archived, and
we'll be using the active flag in the same way we do for Offers.
closes https://github.com/TryGhost/Zapier/issues/56
- fixes tag creation when creating posts with `tags: [{slug: 'new'}]` which should be supported
- assigning tags with only `{slug: 'new'}` was triggering our validation for the required `name` property then bubbling up to the `bookshelf-relations` library resulting in a 500 error
- the fix applied here is to set the `name` field to the same as the `slug` field if a name is not provided
refs https://github.com/TryGhost/Team/issues/1216
Some email security clients are scanning links at delivery, rather than
at the point the user clicks on them. This is causing magic links to
expire. To get around this we're increasing the grace period in which a
link can be used multiple times to 10 minutes.
refs: TryGhost/Toolbox#147
* Replaces all references to isIgnitionError with isGhostError
* Switches use of GhostError to InternalServerError - as GhostError is no longer public
There are places where InternalServerError is not the valid error, and new errors should be added to the @tryghost/errors package to ensure that we can use semantically correct errors in those cases.
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%
- 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/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/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/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: #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 https://github.com/TryGhost/Team/issues/1070
- stores values of custom theme settings
- will be merged with full settings data parsed from themes for API output
- will be cached and made available for lookup in themes to avoid db roundtrips
- stores type of custom theme settings so we can coerce values and know if the type has changed when syncing
- records will be synced with themes upon activation
refs https://github.com/TryGhost/Team/issues/1053
This table is going to be completely deleted at some point in the
future. It serves as a persistent datastore for a spike into collection
analytic events for members. We've opted for a generic table, rather
than a table for each event, so that we can push the DB to the limit in
terms of the length of the table, and find out performance issues A$AP
- as per 5a5a5d162e, the Bookshelf registry plugin is now in core
- we no longer need to explicitly load the plugin, and it displays a
warning if you do
- this change also turns `._models` into `.registry.models`, so our code has
been updated to reflect that
- as per https://github.com/bookshelf/bookshelf/wiki/Migrating-from-0.15.1-to-1.0.0#default-to-require-true-on-modelfetch-and-collectionfetchone, models will now default to `{require:true}` during a fetch, which changes how Bookshelf will respond when a models yields no results
- instead of passing a `null` result, it will reject with an error, so we'd need to switch to `.catch`ing everything
- our code is set up to handle all these null results and switching style is not currently on the cards so we want to use the existing behaviour for now
- to enable this, the `requireFetch` option needs to be added to the model definitions
refs https://github.com/TryGhost/Team/issues/947
- During the work of the UI and moving `email_only` flag to publish menu it created the situation where the publishing of the post was at the same time as adding `email_only` flag, resulted in not picking up teh `sent` status as the `posts_meta` model and record were's available during save.
- Adding the incoming attribute check for email_only flag covers this situation
no-issue
Writing code outside of Ghost which deals with the models is currently
done by passing the models which are needed to the external module,
rather than the instance of ghostBookshelf. This does not give us a way
to create transaction to run queries in. This method is designed as a
simple way to give all models the power to create a transaction for
themselves.
This will be used in @tryghost/members-api for example to ensure that
failures in communication with the Stripe API will rollback the related
inserts in the database.
refs https://github.com/TryGhost/Team/issues/873
We need a relation between members and their product events so that we
can pull out the events for a particular member to generate the start
date of their comped access to a product.
refs https://github.com/TryGhost/Team/issues/946
In order to bulk remove relations between members and labels we need a
way to get hold of all of the existing relations between a label and a
set of members.
refs https://github.com/TryGhost/Team/issues/714
In order to order products by their monthly price we need to apply a
join with the stripe_prices table when querying so we have access to the
amount column of stripe_prices.
As this ordering is core to how the tiers feature is intended to work,
we have added it as the default order. But this can be overriden by
manually passing the order option.
Also ensured that we do not create duplicate products in test fixtures
refs https://github.com/TryGhost/Team/issues/946
This refactor pulls out the core logic so that we can easily add other
bulk operations without having to duplicate even more logic.
It also gives a consistent return value between bulk operations, renaming
`unsuccessfulIds` and `unsuccessfulRecords` to `unsuccessfulData`
We also add a bulkEdit method which will be used to bulk unsubscribe members
from the newsletter.
refs https://github.com/TryGhost/Team/issues/953
- We need to track email-only posts that have been sent out. New status was chosen as a way to differenciate such posts.
- Introducing a new "email post" type, conceptually like "page", was considered. Because there is no clear roadmap for "email post" becoming a bigger part of the product yet and a lot of uncertainty around this concept, overhead needed to introduce a new type was just too much to do at this moment. It's still a possibility in the future
refs https://github.com/TryGhost/Team/issues/949
- When post is marked as "email-only" we can send it out to the selected audience when publishing without making the post publicly available
- The feature is available for experimentation behind "email only" alpha flag available in labs
refs https://github.com/TryGhost/Team/issues/949
- It's relly hard to grasp what's going on in ifs with multiple conditions that are written down in a signle, gazzilion-line format. Having a nice column as way more readable