no-issue
The JWT library we used does not throw an error which can be used by
Ghost. So we need to catch and wrap it in our own errors from
@tryghost/errors.
refs https://github.com/TryGhost/Team/issues/635
This is to ensure we don't break migrations for any sites which have
imported external subscriptions which have an interval of 'week' or
'day'
The bump to members-api includes the handling of these intervals for
ongoing population of mrr events
refs https://github.com/TryGhost/Team/issues/610
- Before introducing a new test for the refed issue doing a linting cleanup. The result will be removing one of `File has too many lines ` lint warnings
refs https://github.com/TryGhost/Team/issues/598
Stripe Webhooks require SSL in production, and so we should not be
allowing connecting to Stripe in production mode unless the site is
running with SSL.
refs https://github.com/TryGhost/Team/issues/598
We now have several pre-conditions related to members which determine
whether or not Ghost is allowed to start. Rather than burying this
within the members-api module, we have now surfaced them to an init
method which can be called during the boot sequence of Ghost. This will
allow us to exit early and explicitly.
no-issue
Our linter now requires that files named index.js have less than 50
lines, so this renames the index.js file to service.js and reexports
service.js from index.js so that linting will pass.
refs https://github.com/TryGhost/Team/issues/693
Since we've got rid of the concept of Complimentary with the Custom
Prices work, we're removing the 'comped' status from members. This
involves a migration for existing members, a schema update for the
validation, and a bump to members-api to no longer use the 'comped'
status for new members.
We also update the aggregation of the MemberStatusEvent to consider the
'comped' status as 'paid', and that there are 0 'comped' status events
in the database.
We can consider a migration for this data in the future, either adding
new status events moving from 'comped' to 'paid', or by modifying
existing status events. However both of these are very difficulty to
write a down migration for, and might be best saved for a major version.
- @tryghost/members-api@1.7.0 is the version that includes the required
changes, however we have already bumped to 1.8.0 in Ghost
refs https://github.com/TryGhost/Team/issues/693
Since we no longer have the concept the "comped" we update the v3 API to
always have a `comped` flag of `false` - maintaining backwards
compatibility.
refs a4c78dbf19
Updates member data on edit to include products data when comped status is changed, as by default we don't include products data when member goes from free to paid subscription due to comped being added.
closes https://github.com/TryGhost/Team/issues/699
With custom products, saving a member with subscriptions on member detail page in Admin throws errors on console, though the save is successful. This breaks the Admin as user needs to refresh the screen again to get rid of error. This change -
- updates the response on member save to return `price` object in subscription
- updates tests
no issue
Keeping CSRF enabled there would prevent oauth from working as users are redirected from the provider domain to the /callback route, where they are logged-in
no refs
Filters active prices in Portal settings to only contain the selected prices by site owner in new monthly/yearly price id settings, ignoring all other prices for now.
no refs
Since backend now allows multiple prices but we want the prices to be currently limited to monthly/yearly on UI, we need new settings to store the current monthly/yearly price by the site owner. These settings determine the active prices shown in Admin / Portal for the site till we allow all custom products/prices again.
closes https://github.com/TryGhost/Team/issues/675
Outlook will display images at their native resolution if no `width` attribute is supplied. Content images were fixed a while ago but feature images would still render very wide and cause horizontal scroll and text size/alignment issues.
- modify `post.feature_image` and add a `post.feature_image_width` property before passing it through to the email template
- for Unsplash images we assume all images are larger than 600px so we change the URL to reference a 1200px image and set the image width to 600 (to keep images on retina displays crisp)
- for other images we probe the image to fetch the original dimensions and give set an image width of 600 if needed, if it's a locally-hosted image we update the URL to point at a max 1200px version
- updated email template to output a `width` attribute on the feature image `<img>` tag if it's set
no issue
- `getLocalSize()` is useful outside of the mobiledoc populate-image-sizes function
- expanded `ImageSize` class with new methods
- `getOriginalImageSizeFromStoragePath()` - takes the "original" image extraction and test from `getLocalSize()` and makes it more generally available
- `getImageSizeFromStorageUrl()` - takes the path extraction from `getLocalSize()` to make image sizes from local urls more generally available
- `getOriginalImageSizeFromStorageUrl()` - URL version of the new `getOriginalImageSizeFromStoragePath()` method
closes https://github.com/TryGhost/Team/issues/560
closes https://github.com/TryGhost/Ghost/issues/12870
The endpoint `/members/api/member/` is used by Portal for fetching member details on site load to setup different flows. The response from this endpoint for logged out member has now changed from 401 Unauthorized to 204 No Content.
Ghost API was previously returning 401 Unauthorized error for logged-out member as this seemed to be technically correct response for unauthorized access to membership features. This resulted in a lot of confusion for end users where visible 401 errors on console were perceived as errors in the script as well as caught by loggers as erroneous traffic. Also for an end user, in the context of visiting a website - the user themselves is not trying to gain access to anything so this becomes cause for more confusion.
After internal discussion, the endpoint - [SITE_URL]/members/api/member- now returns 204 No Content instead of 401 for logged out member, denoting server was able to process the request but did not find any associated member. This should avoid any unwanted error logging on Portal load on a site, as well as make Portal functioning more transparent for a site.
no-issue
When importing Members it is possible to have both the
complimentary_plan and the stripe_customer_id columns set, this can
result in unusual outcomes, for example when importing a customer with a
zero-amount subscription, they would end up with two "comped"
subscriptions, and there would be two "comped" prices in the database.
As we are deprecating the use of "comped" in favour of creating a
subscription with a specific price, we're updating the import to prefer
`stripe_customer_id` column, only using the `complimentary_plan` column
when it is the only of the two columns passed.
refs https://github.com/TryGhost/Team/issues/581
closes https://github.com/TryGhost/Team/issues/582
Emails can now be sent to members with specific associated labels or products by specifying an NQL string. We want to bring the same members segment feature to content by allowing `visibility` to be an NQL filter string on top of the `public/members/paid` special-case strings.
As an example it's possible to set `posts.visibility` to `label:vip` to make a post available only to those members with the `vip` label.
- removed enum validations for `visibility` so it now accepts any string or `null`
- bumped `@tryghost/admin-api-schema` for API-level validation changes
- added nql validation to API input validators by running the visibility query against the members model
- added transform of NQL to special-case visibility values when saving post model
- ensures there's a single way of representing "members" and "paid" where NQL gives multiple ways of representing the same segment
- useful for keeping theme-level checks such as `{{#has visibility="paid"}}` working as expected
- updated content-gating to parse nql from post's visibility and use it to query the currently logged in member to see if there's a match
- bumped @tryghost/members-api to include label and product data when loading member
refs https://github.com/TryGhost/Team/issues/667
On clean and existing installs, the default product created should be named the same as the site title in the first setup so the UX on Portal and everywhere is consistent. This change adds a migration to update existing sites which already have a default product created via fixture, and rename them to their current site title. The rename is only done if the Product name is still the same as in fixture - `Default Product`.
refs https://github.com/TryGhost/Team/issues/671
When turning on custom products, existing sites should have default price descriptions that match existing values for prices. This change sets the default description for Free price to match existing hardcoded value.
refs https://github.com/TryGhost/Team/issues/667
On clean and existing installs, the default product created should be named the same as the site title instead of the name in fixture. This change updates the default product's name to site title during the site setup. We use the Product name in Portal.
refs https://github.com/TryGhost/Team/issues/637
With custom products it's possible to change the name and description of any price. This assumes that people would want to change the same properties of a Free membership, and wires up the values for free membership price settings to Portal site settings API for Portal UI
no-issue
Our base model will only automatically convert numbers to booleans if
the type is 'bool' - however this column was incorrectly added with a
type of 'boolean'. Lucklily - knex with both MySQL & SQLite3 will add
a column with the same type for both of these, so no migration is needed
to fix it.
refs https://github.com/TryGhost/Team/issues/637
The "free" price - when Members signup without using Stripe, should have
a name and description, so that it can be displayed in Portal in a
similar way to paid price's. As there is only ever one, and it is not a
fully fledged price, a setting makes more sense than a dedicated db
table.
refs https://github.com/TryGhost/Team/issues/586
We are no longer using the `stripe_plans` setting, instead we are using
the `stripe_prices` database table. However, we must keep the setting as
the migration from the setting to the database is not done as a standard
migration, but in code. This means our code has to still read and pass
the setting because we will never know if the migration in code has run
yet.
The `portal_plans` setting has been updated to only include 'free' by
default, because the setting must include id's now rather than names.
refs https://github.com/TryGhost/Team/issues/588
refs d72ba77aba
- When limit is in place we don't want to allow sending out a new batch of emails if it would go over limit
- See referenced commit for example configuration
refs https://github.com/TryGhost/Team/issues/588
- This is a new type of limit allowing to measure resource use (e.g. sent emails) per period (e.g. subscription, billing, cycle, etc)
- To enable periodical limit add following values under `hostSettings.limits`:
```
"emails": {
"maxPeriodic": 10,
"error": "Your plan supports up to {{max}} emails. Please upgrade to reenable sending emails."
}
```
and following under `hostSettings.subscription`:
```
"subscription": {
"start": "2020-04-02T15:53:55.000Z",
"interval": "month"
}
```
- Above config would allow checking if 10 emails per month starting on the 2nd of every month has been reached untill now
refs https://github.com/TryGhost/Team/issues/665
Portal only needs to work with active prices(not archived), this change filters prices sent to Portal to only include active prices
refs https://github.com/TryGhost/Team/issues/586
A discussion in the Members team resulted in us determining that we do
not need to enforce unique names for Products. Stripe does not enforce
uniqueness for their Products, and we feel it's not necessary for us to.
refs 37ebe723c6
- `package-json` was a standalone library using dependency injection so
we could pull it out into its own package in Utils
- this was done in the commit referenced above
- this commit removes the implementation and tests in Ghost and replaces
the require in the initialization wrapper with the new package
refs https://github.com/TryGhost/Team/issues/581
refs https://github.com/TryGhost/Team/issues/582
When publishing a post via the API it was possible to send it using `?email_recipient_filter=all/free/paid` which allowed you to send to members only based on their payment status which is quite limiting for some sites.
This PR updates the `?email_recipient_filter` query param to support Ghost's `?filter` param syntax which enables more specific recipient lists, eg:
`?email_recipient_filter=status:free` = free members only
`?email_recipient_filter=status:paid` = paid members only
`?email_recipient_filter=label:vip` = members that have the `vip` label attached
`?email_recipient_filter=status:paid,label:vip` = paid members and members that have the `vip` label attached
The older `free/paid` values are still supported by the API for backwards compatibility.
- updates `Post` and `Email` models to transform legacy `free` and `paid` values to their NQL equivalents on read/write
- lets us not worry about supporting legacy values elsewhere in the code
- cleanup migration to transform all rows slated for 5.0
- removes schema and API `isIn` validations for recipient filters so allow free-form filters
- updates posts API input serializers to transform `free` and `paid` values in the `?email_recipient_filter` param to their NQL equivalents for backwards compatibility
- updates Post API controllers `edit` methods to run a query using the supplied filter to verify that it's valid
- updates `mega` service to use the filter directly when selecting recipients
refs https://github.com/TryGhost/Team/issues/581
Editors are allowed to restrict post visibility and send emails to particular member segments, they need to be able to read labels so that they can select them in a member segment.
refs https://github.com/TryGhost/Team/issues/637
refs 75169b705b
With custom prices, Portal now needs to show all available custom prices in the UI as well as product's name and description in the Portal UI. This change adds product information to member site settings for Portal UI.
refs https://github.com/TryGhost/Team/issues/586
refs aa12770329
Using `id` as ghost id for subscription prices can be confusing as everything in the method refers ids to be stripe ids. This change updates the ghost id value to use `price_id` key in the serialization
refs https://github.com/TryGhost/Team/issues/496
We want to give more control over the default selection of email recipients when publishing a post, to do that we need somewhere to store those settings. These settings are site-wide and intended for use by admins to control the default editor behaviour for all staff users. They _do not_ control API behaviour, if you want to send email when publishing via the API it's still necessary to explicitly opt in to that using the `?email_recipients_filter=` query param.
- new `editor` settings group to indicate that these settings only affect the UI rather than the API
- `editor_default_email_recipients` controls overall behaviour, string/enum with these allowed values:
- `'disabled'`: no option to send email is shown in the editor's publishing dropdown
- `'visibility'`: (default) selected member segment is dynamic and matches the post visibility filter
- `'filter'`: specific member filter defined in `editor_default_email_recipients_filter` setting
- `editor_default_email_recipients_filter` is an NQL string for selecting members, used when `editor_default_email_recipients` is set to `'filter'`
- default value is `'all'`
- the segment string can be any valid NQL filter with the additional special-case values of `'all'` and `'none'`
no issue
- `Error` is very generic for this case and `IncorrectUsageError`
will populate the resulting error with the correct error code
- the `message` was pulled out to its own statement so we can avoid long
lines
no issue
- we're preparing the `package-json` lib to be extracted out of Ghost into
its own package so moving the initialization wrapper outside of the
folder makes the process a lot easier
refs https://github.com/TryGhost/Team/issues/586
refs 33f26fbf32
As part of serializing subscriptions with prices, we previously attached only the stripe price id to the price object for subscription. This change updates the price object to include both Ghost id and stripe price id for the object, as Portal needs to check the Ghost price id for logged in members to verify their current plan.
- calling i18n as a global const like this requires it to be loaded before anything else, when we have to manage this with the init() flow
- wrapping it inside the function where it's used ensures we don't call i18n til we need it
- also improved the i18n called without init error to include the key it was called with