ref https://linear.app/tryghost/issue/MOM-25
This matches the way that mastodon handles the key url and may be the reason
these documents are incompatible. This also removes the `username` key as that
isn't used anywhere, instead we have a username property which is rendered as
the ActivityPub compat preferredUsername key.
refs #20050
- Renovate seems to be unable to bump the package past the security
release, but unfortunately this release contains a breaking bug
- this commit manually bumps the package so we can get things flowing
again
- the security release doesn't really affect us, but we should still try
and keep on the latest
no issue
- updated search to add `status` to the search results
- added filtering to the editor's `searchLinks()` method
- prevented TaskCancellation errors being thrown from the search task being cast to a Promise
ref
https://linear.app/tryghost/issue/ENG-845/error-attempted-to-set-lexical-on-the-deleted-record
ref
[https://linear.app/tryghost/issue/ENG-854/🐛-deleting-imported-posts-makes-ghost-unresponsive](https://linear.app/tryghost/issue/ENG-854/%F0%9F%90%9B-deleting-imported-posts-makes-ghost-unresponsive)
- When deleting a post in the editor's Post Settings Menu, if the post
has unsaved changes (indicated by the hasDirtyAttributes property in the
editor), Admin will crash because it tries to save a post revision
before leaving the editor, but the post has already been deleted so
saving fails.
- This can occur when editing a post and quickly deleting it from the
Post Settings Menu before saving is completed.
- It can also occur when attempting to delete an imported post, as the
editor will parse the lexical from the server and may make some minor,
invisible-to-the-user changes to the lexical string locally (e.g. JSON
formatting, or updating the JSON to use extended version of base lexical
nodes), which triggers the same error.
- This fix bypasses the attempt to save a post revision when leaving the
editor if the post is already deleted, which allows the transition back
to the Posts route to succeed.
refs f39d1d3aa3
- similar to the commit above, the JSON parser changed between Node 18
and Node 20, so the error message changed too
- we actually just want to check the error is forwarded to the user, so
we can do that by getting the error message from JSON.parse and check
against that
closes https://linear.app/tryghost/issue/MOM-1/
- added `feature.internalLinking` and `searchLinks` properties to the `cardConfig` object passed to the editor
- `searchLinks()` uses Admin's internal search to fetch and filter results
- called with no search term to obtain default links to show as soon as the bookmark card is inserted, in our case we show the last 5 published posts. Result is cached for the duration of the editing session to avoid API queries/loading state after the first fetch
- flattens search results for now because Koenig doesn't yet support grouped results
- bumps version of `@tryghost/koenig-lexical` to support the feature flag
ref https://linear.app/tryghost/issue/MOM-25
Whilst we're experimenting it's gonna be easier to not have to think about
caching affecting things. We'll disable it completely for now, and then decide
on a caching strategy that suits us down the line.
ref https://linear.app/tryghost/issue/MOM-25
This _might_ be the reason that Mastodon doesn't recognise our Actor, but
either way it's the correct thing to do so that JSON-LD parsers correctly
understand that publicKey field
ref https://linear.app/tryghost/issue/MOM-48
This required some structural changes to our NestJS setup so that we can mount
it on multiple parts of the Ghost express app.
We've used the RouterModule to allow adding submodules that are mounted on
different paths, and we've had to be explicit about the base path for each
module. We've also had to switch back to using the Module decorator, because
RouterModule doesn't work with DynamicModule definitions.
Now that the NestJS app has knowledge of the full path, we need to "reset" the
url & baseUrl when passing the request into NestJS so that it can correctly
match the path. This is probably needed for the frontend too, for subdirs, but
that causes further issues - as this in prototype stage, we'll look later
Another issue is that NestJS replaces the express app instance with its own,
which isn't an issue for the Admin API (though we've fixed it anyway for
consistency), but did cause problems for the frontend, because the express app
is where view engine and directory information is stored.
The fix for this is to save a reference to the original ghost express
application, and reattach it to the request if it is not handled by Nest
Now that we have the Nest app mounted on the frontend, we're able to have it
handle the /.well-known/webfinger route with a proper controller, which is nice!
ref https://linear.app/tryghost/issue/MOM-32
This adds the basic building blocks for an Outbox for an Actor, currently it's
hardcoded - which'll let us at lest test integration with other platforms.
JSONLDService is an awful name, but it's late and this is a prototype.
no-issue
This makes the code easier to understand and maintain, and reduces the overhead
of converting to/from a Map. It also changes the URLs and makes them path based
no-issue
This lets us have an unauthed endpoint for reading the outbox, long term we'll
probably wanna have this on the frontend URL but we don't have NestJS wired up
there yet.
no-issue
This is consistent with our main NestJS Module and allows the values to be
introspected by other code, rather than be stored internaly in decorator
metadata, which makes it easier to debug.
ref MOM-31
ref https://linear.app/tryghost/issue/MOM-31
We'll be building a lot of the new code for ActivityPub in Nest, so we'll need
to have it enabled in Ghost to work.
ref DES-205
- label name is now used as the title on label pills instead of static
text
- label names will now be truncated when it takes more than 2 lines
instead of 1
ref https://linear.app/tryghost/issue/MOM-1
- renamed `searchable` to `groupName` so it better matches usage and avoids leaking internal naming to external clients
- added `url` to the fetched data for each data type as the editor will want to use front-end URLs in content
- added acceptance tests to help avoid regressions as we further generalise/optimise the search behaviour
- The RSS cache has lived for a really long time, but I'm not sure it's useful
- Want to be able to determine if it gets used much, and if not, then we can remove it
ref 78311591d0
- updated tests to not click a button on the setup/done screen that is no longer shown
- fixed setup flow showing an alert bar due to not handling the `TransitionAborted` error that is thrown by the setup/done->dashboard redirect
ref https://linear.app/tryghost/issue/KTLO-1/members-spam-signups
- Some customers are seeing many spammy signups ("hundreds a day") — our
hypothesis is that bots and/or email link checkers are able to signup by
simply following the link in the email without even loading the page in
a browser.
- Currently new members signup by clicking a magic link in an email,
which is a simple GET request. When the user (or a bot) clicks that link, Ghost
creates the member and signs them in for the first time.
- This change, behind an alpha flag, requires a new member to click the
link in the email, which takes them to a new frontend route `/confirm_signup/`, then submit a form on the page which sends a POST request to the
server. If JavaScript is enabled, the form will be submitted
automatically so the only change to the user is an extra flash/redirect
before being signed in and redirected to the homepage.
- This change is behind the alpha flag `membersSpamPrevention` so we can
test it out on a few customer's sites and see if it helps reduce the
spam signups. With the flag off, the signup flow remains the same as
before.
Got some code for us? Awesome 🎊!
Please include a description of your change & check your PR against this
list, thanks!
- [x] There's a clear use-case for this code change, explained below
- [x] Commit message has a short title & references relevant issues
- [x] The build will pass (run `yarn test:all` and `yarn lint`)
We appreciate your contribution!
Explanation: There are some missing accents in:
4c598a1e6d/ghost/i18n/locales/es/comments.json (L18-L19)
And
4c598a1e6d/ghost/i18n/locales/es/comments.json (L37)
Specifically in: Conviertete, Se. So instead of including accents I just
used simpler words so it sounds as a more natural translation, I have
already translated my whole newsletter https://crecimientoconsciente.co/
to Spanish I'm just finishing some wording details.
Also if you could please give a check to this
[comment](https://github.com/TryGhost/Ghost/issues/16628#issuecomment-1990569446)
in milestone 3 of translations for official support in email paywall
cta.
Co-authored-by: Ryan Feigenbaum <48868107+royalfig@users.noreply.github.com>
This PR adds Japanese translation to the comment resources
(ghost/i18n/locales/ja/comments.json). Currently, all of them are empty
and Japanese translations are not supplied.
The PR also adds Japanese translations to a few missing phrases in the
portal language resources (ghost/i18n/locales/ja/portal.json).
---------
Co-authored-by: Ryan Feigenbaum <48868107+royalfig@users.noreply.github.com>
This PR will add Persian language locale (fa/fa_IR) for Ghost
- [x] The build will pass (run `yarn test:all` and `yarn lint`)
---------
Co-authored-by: Ryan Feigenbaum <48868107+royalfig@users.noreply.github.com>
ref https://linear.app/tryghost/issue/ENG-790/remove-use-of-sub-queries-in-email-analytics
- the `delivered_at` column is typically entirely/nearly entirely filled with values meaning the `IS NOT NULL` query matches a huge number of rows that MySQL has to fetch from the index to count
- using `IS NULL` switches that behaviour around as it will now match very few rows which has been shown in testing to be considerably quicker
- after switching to `IS NULL` the query returns an "undelivered" count rather than a "delivered" count, in order to keep the rest of the system behaviour the same we can calculate the delivered count by subtracting the query result from the total number of emails sent which we can fetch using a very fast primary key lookup query on the `emails` table
closes https://linear.app/tryghost/issue/ENG-790/remove-use-of-sub-queries-in-email-analytics
Avoiding sub queries means we don't have a process tied up for longer than necessary and we can more easily see if one of the queries is non-performant.
- extracted the count queries into separate queries and used the retrieved values in the final update query
- removed a query by moving the email open rate calculation into JS as we've already fetched the necessary data before that point
fix https://linear.app/tryghost/issue/ENG-805/
refs https://owasp.org/www-community/attacks/CSV_Injection
- it's possible for certain fields in a member CSV export to be executed
by software that opens the CSVs
- we can protect against this for the user by escaping any forumulae in
the CSV fields
- papaparse provides this option natively, so it's just a case of
providing the field to the unparse method
- credits to Harvey Spec (phulelouch) for reporting
closes https://linear.app/tryghost/issue/IPC-117/fix-ghost-orb-logo-not-being-animated-in-chrome-or-arc
- Chrome wasn't respecting the `muted` attribute when the dashboard is loaded without any interaction resulting in the video not auto playing
- fixed by adding a `{{autoplay}}` modifier that explicitly sets the `muted` property on the video before calling `.play()` which appears to bypass the interaction-required block
ref https://linear.app/tryghost/issue/ENG-801/unable-to-recommend-sites-with-long-excerpts
- recommending a site with a long excerpt was being blocked by a
validation error
- with this change, we truncate the excerpt to 2000 characters max. and
avoid showing an error in the UI
- with this change, the description length validation is also now
stricter; 200 characters max, instead of 2000, to match the UI
part of https://linear.app/tryghost/issue/IPC-92/add-logic-for-completing-steps
part of https://linear.app/tryghost/issue/IPC-115/make-skip-onboarding-button-work
- updated `onboarding` service to use the `user.accessibility` (poor naming, this is an old field used for general user settings) as it's backing store
- added `onboarding.allStepsCompleted` to allow for "completion" state to be shown before the checklist is marked as completed
- added `onboarding.{complete,dismiss}Checklist()` actions and wired those up to the template
When testing, if you need to reset the checklist you can run this in DevTools console
```
Ember.Namespace.NAMESPACES_BY_ID['ghost-admin'].__container__.lookup('service:onboarding').startChecklist()
```
ref https://linear.app/tryghost/issue/ENG-799
- recommendations were being stripped of query parameters and hash
fragments before save
- in particular, query parameters for attribution such as ?ref were not
being stored
ref https://linear.app/tryghost/issue/CFR-13
- enabled saving traces on browser test failure; this makes troubleshooting a lot easier
- updated handling in offers tests to ensure the tier has fully loaded in the UI (not just `networkidle`)
- updated publishing test to examine the publish button reaction to the save action response instead of a 300ms pause
In general, our tests use a lot of watching for 'networkidle' - and sometimes just raw timeouts - which do not scale well into running tests on CI. In particular, 'networkidle' does not work if we're expecting to see React components' state updates propagate and re-render. We should always instead look to the content which encapsulates the response and the UI updates. This is something we should tackle on a larger scale.
refs
https://linear.app/tryghost/issue/IPC-92/add-logic-for-completing-steps
- added `onboarding` service to manage logic and state for the onboarding display and it's various steps
- added basic "display onboarding checklist" state to replicate the basic feature flag toggle along with making sure it's only shown to owners
- added acceptance test file and missing mirage endpoints needed for the dashboard to load without error
Dear Ghost team,
Hope you're well.
While developing our website https://fayn.press (we just launched it),
we came across a few English-Turkish translations that needed
correction.
Following the guide you shared with me, I made the corrections & changes
using Github and am now submitting it.
I changed "Sign in": "Kayıt ol", to "Sign in": "Giriş Yap", as the
current translation is wrong. Kayit ol means Sign up, as opposed to sign
in. It's been confusing for our paying members to receive an email that
says "sign up" in Turkish (Kayit ol) right after they sign up for the
site by paying. We'd greatly appreciate it if this could be fixed as
soon as possible.
Other suggestions are mostly improvements in translations that sound
more natural in Turkish.
I went through all translations and other than these, they are all good.
Thank you,
Oktay
Co-authored-by: Ryan Feigenbaum <48868107+royalfig@users.noreply.github.com>
Ref TRI-27
- Published posts now show the published date in post list, instead of
updated date.
- The `gh-format-post-time` helper now has a `relative` and `absolute`
and option instead of formatting being tied to `draft` and `published`
state. This allows for more flexibility in how dates are displayed.
- Draft, scheduled and published posts now follow the same time
formatting pattern: today, yesterday, or explicit dates if further in
the past.
- Hover states for dates in the post list have been removed.
- Title attributes are added indicating whether timestamp refers to updated_at or published_at
- The scheduling logic on the publish page still uses relative
formatting.
refs KTLO-19
When we need to migrate subscriptions from a platform with platform
fees, we need to recreate the subscriptions. That can cause the same
subscription to be attached multiple times to the same member in Ghost.
This is a problem because all MRR, subscriptions and cancellations stats
are no longer correct. Ghost will add a MRR event for the duplicated
subscription from the start time, so there is a sudden peak in MRR and a
dip after the migration because all those duplicate subscriptions are
suddenly cancelled 'today'.
The migrator tool adds a ghost_migrated_to metadata field to the old
subscription. Ghost can use this to detect the old subscription and
delete the subscription and corresponding events.
- Changed the layout of the modal
- Added a fallback state for the cover image
- Added possibility to copy the publication link
- Correct hover states for social media buttons
---------
Co-authored-by: Ryan Feigenbaum <48868107+royalfig@users.noreply.github.com>
ref ENG-774
ref https://linear.app/tryghost/issue/ENG-774
Staff Tokens will have both a `user` and an `apiKey` present on the
`loadedPermissions`.
The check here for `apiKey` was written when we could assume that an
`apiKey` was an Admin Integration - so it completely overwrote the
previous `allowed` list. When we added the concept of Staff Tokens -
this resulted in a privilege escalation.
This is a good lesson in not using proxies or indicators for data, as
changes elsewhere can invalidate them - if we had been specific and
checked the role of the current actor we wouldn't've had this bug!
no issue
When testing Stripe migrations, it is useful to be able to clear the
database quickly without deleting admins and tokens. This is possible
with the data generator.
no issue
- Bumped Koenig-Lexical to a new minor.
- This change contains the new Unsplash selector which is a breaking
change as default headers are handled a touch different.
Ref DES-188
- the alignment of the main page title and the site title in the sidebar
was off
- also the top right dropdown's vertical positioning was off
- [x] There's a clear use-case for this code change, explained below
- [x] Commit message has a short title & references relevant issues
- [x] The build will pass (run `yarn test:all` and `yarn lint`)
I have improved the Korean translations by ensuring consistent tones,
using more polite phrases and correcting grammar errors.
ref https://linear.app/tryghost/issue/CFR-4/
- added request queueing middleware (express-queue) to handle high
request volume
- added new config option `optimization.requestQueue`
- added new config option `optimization.requestConcurrency`
- added logging of request queue depth - `req.queueDepth`
We've done a fair amount of investigation around improving Ghost's
resiliency to high request volume. While we believe this to be partly
due to database connection contention, it also seems Ghost gets
overwhelmed by the requests themselves. Implementing a simple queueing
system allows us a simple lever to change the volume of requests Ghost
is actually ingesting at any given time and gives us options besides
simply increasing database connection pool size.
---------
Co-authored-by: Michael Barrett <mike@ghost.org>
ref ENG-728
ref https://linear.app/tryghost/issue/ENG-728
This is not used anywhere, and makes the code more complicated, it's a good
step toward simplifying permissions and pulling them out of the database.
Updated the share modal design and functionality
ref IPC-90
• Rebuilt the bookmark card to match other components
• Added linking to the different social networks
• Added a close button that closes the modal
• Removed repetitive subtitle
ref ENG-728
ref https://linear.app/tryghost/issue/ENG-728
This is NOT a functionality change. The Post#permissible method unit
tests have been updated to pass `true` as `hasUserPermission` and we can
see that the permission functionality remains the same.
The permissible method of the post model is responsible for removing
permission based on the data that is being modified, but the permissions
module is setup to allow the permissible method to grant permission -
this means that we call permissible, even if the current actor doesn't
have permission, this results in code that is hard to understand and
manage.
We are going to be instead returning early if an actor does not have
permission, this will allow permissible method signatures to be greatly
simplified (removing the need for hasUserPermission, hasApiKeyPermission
& hasMemberPermission arguments).
ref https://linear.app/tryghost/issue/IPC-66/onboarding-checklist-v1
- Larger, 100vh onboarding checklist that’s currently on the dashboard,
but should be moved to it’s own component and route
- Every step links to the relevant screen, but the logic for completing
steps is missing
fixes DES-66
In case some batches succeeded sending, the button text will be
different if the email sending was partially successful.
For now this uses text matching with a warning in our E2E tests because
we don't have a straightforward way to check if an error is partial or
not yet.
fixes https://linear.app/tryghost/issue/ENG-746/http-500-responses-when-handle-image-sizes-middleware-hits-missing
- in the event a request comes in for a resized image, but the source
image does not exist, we return a rendered 404 page
- we do this because we pass the NotFoundError to `next`, which skips
over the static asset code where we return a plaintext 404
- also included a breaking test that ensure we go to the next middleware
without an error
ref ENG-761
ref https://linear.app/tryghost/issue/ENG-761
Creating these pipelines is expensive, and we don't want to do it
repeatedly for the same controller. Adding caching should reduce the
amount of time spent setting up pipelines for each usage of the `get`
helper.
ref https://linear.app/tryghost/issue/IPC-66/onboarding-checklist-v1
- Adds a basic version of a new onboarding checklist behind the feature
flag, without incomplete/complete state logic
- Links to Design settings, Members screen and new post
- Opens amodal that we’ll use as Share modal
---------
Co-authored-by: Daniël van der Winden <danielvanderwinden@ghost.org>
ref [ENG-747](https://linear.app/tryghost/issue/ENG-747/)
ref https://linear.app/tryghost/issue/ENG-747
H'okay - so what we're trying to do here is make get helper queries more
cacheable. The way we're doing that is by modifying the filter used when
we're trying to remove a single post from the query.
The idea is that we can remove that restriction on the filter, increase
the number of posts fetched by 1 and then filter the fetched posts back
down, this means that the same query, but filtering different posts,
will be updated to make _exactly_ the same query, and so share a cache!
We've been purposefully restrictive in the types of filters we
manipulate, so that we only deal with the simplest cases and the code is
easier to understand.
ref ENG-742
ref https://linear.app/tryghost/issue/ENG-742
We don't do any parsing of layouts in gscan, which means themes can be
uploaded which use non-existent files for their layout.
We can catch the error in the res.render call, and wrap it, just like we
do for missing templates (e.g. the StaticRoutesRouter)
closes ENG-632
- This listens to a new property in the `milestones` config to set a minimum value of Milestones we wanna use the Slack notification service for
refs https://linear.app/tryghost/issue/ENG-750
- when adding a recommendation, we fetch the recommended site's metadata
- before this change, if the metadata fetch failed for some reason, we'd show an error and block the recommendation from being added
- after this change, we use fallback values if the metadata fails to fetch, instead of blocking the recommendation from being added. We use the site domain as the title and leave the rest empty (no favicon, no description)
- this change also means we are not checking whether a site exists or not for the publisher anymore. It’s then up to the publisher to make sure they don’t enter broken URLs
ref ENG-747
ref https://linear.app/tryghost/issue/ENG-747
H'okay - so what we're trying to do here is make get helper queries more
cacheable. The way we're doing that is by modifying the filter used when
we're trying to remove a single post from the query.
The idea is that we can remove that restriction on the filter, increase
the number of posts fetched by 1 and then filter the fetched posts back
down, this means that the same query, but filtering different posts,
will be updated to make _exactly_ the same query, and so share a cache!
We've been purposefully restrictive in the types of filters we
manipulate, so that we only deal with the simplest cases and the code is
easier to understand.
ref https://linear.app/tryghost/issue/ENG-721
- when changing the response to a `204` for requests with no cookie we'd lost the `Cache-Control: public, max-age: 0` header which meant some cache systems weren't caching as efficiently as possible
ref https://linear.app/tryghost/issue/TRI-65
In the context of referrals, we want to understand how useful our
“Powered by Ghost” badges are.
There are currently a few places where the “Powered by Ghost” badge can
be rendered:
- in newsletters (enabled/disabled by publisher, on a newsletter basis)
- in Portal popups, e.g. member signup/signin/account settings
- in the footer of some themes, including Source & Casper
We're adding the query param ?via to evaluate the usage of the badge in
newsletters.
no-issue
This adds the barebones of a NestJS application wired up to the Admin API
behind a feature flag, so that we can experiement with how to use Nest in the
context of Ghost
closes ENG-730
closes https://linear.app/tryghost/issue/ENG-730/
We've updated the input serializer to parse the filter, and responded
with an error if it cannot be parsed correctly.
Now that it's parsed, we can pass a mongo query object through the
stack, which will lend itself to better typing for this code, which is a
direction we want to go in anyway. We've had to update all the internal
usages of the `browse` method to use mongo query objects.
ref https://linear.app/tryghost/issue/ENG-740/http-500-error-when-image-processing-fails
refs 4aad551c72
- upon further discussion, we've decided it's better to throw an error
in this case because the uploaded image is deemed invalid and storing
it on the filesystem might cause more issues with resizing/further
processing in the future
- this commit implements that and alters the tests
fixes ENG-740
fixes https://linear.app/tryghost/issue/ENG-740/http-500-error-when-image-processing-fails
- in the event the image transform library throws (which can happen for
many reasons; sharp/libvips can come across a number of errors), we
currently return this as a HTTP 500 error to the user
- in this case, we should just try-catch the call and jump to the
non-processing flow where it just saves the original image
- also added breaking test
closes https://linear.app/tryghost/issue/ENG-721
ref https://linear.app/tryghost/issue/ENG-708
Comments-UI loads `/ghost/admin-frame/` in an iframe to check if a Staff User is authenticated in order to show moderation options. That iframe request loads a HTML page which in turn contains a script that fires off an API request that attempts to fetch the logged-in user details, resulting in a 403 "error" showing up when not authenticated. In the vast majority of cases there will be no staff user authenticated so lots of extra requests and "errors" are seen unnecessarily.
- adjusted the `/ghost/auth-frame/` endpoint to check if the request contains an Admin session cookie
- if it does, continue as before with rendering the HTML page so the script is loaded
- if it doesn't, return an empty 204 response avoiding the script request and subsequent 403-generating API request
- eliminates the 403 error being generated for all typical visitor traffic, the error should only be seen when an Admin was previously logged in but their cookie is no longer valid (either from logging out, or going past the 6month validity period)
ref ENG-737
ref https://linear.app/tryghost/issue/ENG-737/http-500-errors-from-recommendations-check-endpoint
- it's still possible for `this.#externalRequest.get` to throw, like if
DNS resolution fails
- we want to try-catch this so we don't throw from this function and
return a HTTP 500 to the user
- instead, we can just return `undefined`, which is the fallback
- adds a breaking test too
fixes https://github.com/TryGhost/Product/issues/4237
- this fixes the fact that we return a HTTP 500 response when the oembed
library receives an error, such as a 401 or 403
- includes special handling for cases where we want to return a slightly
different error message
- also adds unit tests for @tryghost/oembed-service package
fixes ENG-733
ref https://linear.app/tryghost/issue/ENG-733/handle-image-uploads-where-name-is-too-long
- filesystems usually have a filename length limit; ie. on macOS it is
255 characters
- if a file is uploaded with a longer filename, we'll return a HTTP 500
- we shouldn't do this as it is user error, so we can just catch the
error code and return BadRequest
- this implements that, and adds a breaking test
I think my changes make the Slovak better. I changed just a few thins,
it's my first time trying Github, but if this is approved, I plan to do
more changes in the future and make the Slovak translation even btetter.
Co-authored-by: Ryan Feigenbaum <48868107+royalfig@users.noreply.github.com>