closes#7313
- Adds `getSanitizedFileName` function to storage/base.js which replaces non A-Z0-9@. chacracters with -
- modifies /api/theme.js so that zip.shortName is consistent throughout.
closes#7192
- add 008 migration
- added script to re-run 006/01
- re-run 006/01 migration for postgres in any timezone (transform formats only)
- re-run 006/01 migration for sqlite (transform formats only)
- rely on format checks for sqlite, do not check server TZ
* 🐛 fix direct update
closes#7297
- move sitemap initialisation into sitemap handler
- initialise sitemap on first request to sitemap
* 🐛 fix how we pass options to migration files
refs #7317
- clone options when passing them into the migration/fixture files
- do not use default sequence, because it does not clone the arguments
no issue
This PR takes the existing function `processUrls` in `data/xml/rss` and refactors it to be a stand-alone util.
The change is needed, as this functionality will be accessed from `apps/amp` to convert relative URLs.
closes#7186
- Add a concept of validity to each generator
- Refactor base generator to handle invalid (empty) nodes for both events & the initial generation
- Update the tests a bit, to fix some bugs in the tests
- Ensure the homepage is always present
refs #2852
- improvement: ensure custom storage adapter has required functions
- serve, save and exists are from now on required functions for a custom storage adapter
- add delete as required storage function
closes#6588, #7095
* `ImageObject` with image dimensions (#7152, #7151, #7153)
- Returns meta data as promise
- returns a new Promise from meta data
- uses `Promise.props()` to resolve `getClient()` and `getMetaData()`
- Adds 'image-size' util
The util returns an object like this
```
{
height: 50,
url: 'http://myblog.com/images/cat.jpg',
width: 50
};
```
if the dimensions can be fetched and rejects with error, if not.
In case we get a locally stored image or a not complete url (like `//www.gravatar.com/andsoon`), we add the protocol to the incomplete one and use `urlFor()` to get the absolute URL. If the request fails or `image-size` is not able to read the file, we reject with error.
- adds 'image-size' module to dependencies
- adds `getImageSizeFromUrl` function that returns image dimensions
- In preparation of AMP support and to improve our schema.org JSON-LD and structured data, I made the following changes:
- Changes the following properties to be `Objects`, which have a `url` property by default and a `dimensions` property, if `width` and `height` are available:
- `metaData.coverImage`
- `metaData.authorImage`
- `metaData.blog.logo`
- Checks cache by calling `getCachedImageSizeFromUrl`. If image dimensions were fetched already, returns them from cache instead of fetching them again.
- If we have image dimensions on hand, the output in our JSON-LD changes from normal urls to be full `ImageObjects`. Applies to all images and logos.
- Special case for `publisher.logo` as it has size restrictions: if the image doesn't fulfil the restrictions (<=600 width and <=60 height), we simply output the url instead, so like before.
- Adds new property for schema.org JSON-LD: `mainEntityOfPage` as an Object.
- Adds additional Open Graph data (if we have the image size): `og:image:width` and `og:image:height`
- Adds/updates tests
* AMP router and controller (#7171, #7157)
Implements AMP in `/apps/`:
- renders `amp.hbs` if route is `/:slug/amp/`
- updates `setResponseContext` to set context to `['amp', 'post']` for a amp post and `['amp', 'page']` for a page, but will not render amp template for a page
- updates `context_spec`
- registers 'amp' as new internal app
- adds the `amp.hbs` template to `core/server/apps/amp` which will be the default template for AMP posts.
- adds `isAmpURL` to `post-lookup`
* 🎨 Use `context` in meta as array (#7205)
Instead of reading the first value of the context array, we're checking if it includes certain context values.
This is a preparation change for AMP, where the context will be delivered as `['amp', 'post']`.
* ✨ AMP helpers (#7174, #7216, #7215, #7223)
- Adds AMP helpers `{{amp_content}}`, `{{amp_component}}` and `{{amp_ghost_head}}` to support AMP:
- `{{amp_content}}`:
- Adds `Amperize` as dependency
- AMP app uses new helper `{{amp_content}}` to render AMP HTML
- `Amperize` transforms regular HTML into AMP HTML
- Adds test for `{{amp_content}}` helper
- Adds 'Sanitize-HTML` as dependendy
- After the HTML get 'amperized' we still might have some HTML tags, which are prohibited in AMP HTML, so we use `sanitize-html` to remove those. With every update, `Amperize` gets and it is able to transform more HTML tags, they valid AMP HTML tags (e. g. `video` and `amp-video`) and will therefore not be removed.
- `{{amp_ghost_head}}`:
- registers `{{amp_ghost_head}}` helper, but uses `{{ghost_head}}` code
- uses `{{amp_ghost_head}}` in `amp.hbs` instead of `{{ghost_head}}`
- `{{ghost_head}}`:
- Render `amphtml` link in metadata for post, which links to the amp post (`getAmpUrl`)
- Updates all test in metadata to support `amp` context
- Changes context conditionals to work with full array instead of first array value
- Adds conditionals, so no additional javascript gets rendered in `{{ghost_head}}`
- Removes trailing `/amp/` in URLs, so only `amphtml` link on regular post renders it
- Adds a conditional, so no code injection will be included, for an `amp` context.
- `{{amp_components}}`:
- AMP app uses new helper `{{amp_components}}` to render necessary script tags for AMP extended components as `amp-iframe`, `amp-anime` and `amp-form`
- Adds test for `{{amp_components}}`
closes#7182
When calling `config.set()` in the settings api, we want to set the active timezone of the blog to make it available in our `settingsCache`. But because the `theme` object in the `set` prototype was already set to `Etc/UTC` as default, the `_.merge` function would always overwrite our `activeTimezone` with the default value.
This PR changes the code in the way, that we always set 'Etc/UTC' for the timezone as default, _until_ we fetched our settings and therefore the `activeTimezone` setting, so we can overwrite it.
This issue had not only influence on the date helper, but everywhere in our codebase, where we rely on reading the `timezone` from our config, instead of our settings. The `{{@blog.timezone}}` helper reflected that quiet well, as it would always show `Etc/UTC`
no issue
- Source out validation logic into a upload validation middleware for all upload types (csv, image, subscribers). This unit can be later used for Ghost 1.0 as a pre validation core unit.
- More usage of route tests than controller tests. These are use case tests, a use case only changes if the product changes
no issue
- sets `settings.activeTimezone` to best-guess based on current server time when performing the timezones migration in order to prevent unexpected changes in timezone when upgrading
closes#6948
- the hbs engine was never initialised when server starts
- when you request a page which does not exist, express jumps directly into the error handlers
- delete some dynamic hbs engine setters in theme handler
refs #6958 (first task of it)
-includes additional tests
-Instead of removing all slashes "/g" we now specifically remove leading and trailing slashes.
closes#6932
- new default order of posts: scheduled, draft, published
- invent orderDefaultRaw fn for each model
- each model is able to create a default raw order query
- separate count and fetch query for fetchPage, because the count query where group/order statements attached
closes#7060
- changed meta referrer from origin to origin-when-cross-origi
- made referrer policy configurable via referrerPolicy option in config js
- added example to config.example.js
-modified test to reflect new defaul origin-when-cross-origin
-added a test for configuration changed referrerPolicy
refs #7023
The template can be used for all cases (different newsletter interval, different amount of blog posts, with or without blog picture).
The template can be filled with the following data structure:
- `blog.logo` is the blog logo from settings
- `blog.title` is the title of the current blog
- `blog.url` is the URL of the blog
- `blog.twitterURL` is the twitter profile URL of the blog from settings
- `blog.facebookURL` is the facebook page URL of the blog from settings
- `blog.unsubscribe` is the link for the reader to unsubscribe from the blog
- `newsletter.interval` is the interval in words like 'weekly', 'daily', 'monthly'
- `newsletter.date` is the date of the newsletter issue in a format like 'June 9th, 2016'
- `blog.post` is expected to be an array:
- `blog.post[i].picture` is the picture of the blog post. There are also conditionals, which change the inline CSS to either show a border-top if there's no picture (in that case the HTML code, that shows the picture is not active)
- `blog.post[i].title` is the title of the current blog post
- `blog.post[i].text` is the text of the current blog post, which needs to be cut down to 278 letters, plus ending with `…`
- `blog.post[i].url` is the URL of the current blog post
- `blog.post[i].tag` is the tag of the current blog post
- `blog.post[i].author` is the author of the current blog post
Important is, that only HEX HTML entities will work, especially in Outlook. So instead of `—` we need to use `—` and `’` instead of `'` or `'` and so on.
Added unit test for newsletter template in `test/unit/mail/utils_spec.js`.
no issue
This is a fix for the default-scheduler.
When a post never had a published_at value, the oldTime for removing the job would be null. And in this case we would try to delete a job with an invalidate date.
refs #6413
- PUT endpoint to publish a post/page for the scheduler
- fn endpoint to get all scheduled posts (with from/to query params) for the scheduler
- hardcoded permission handling for scheduler client
- fix event bug: unscheduled
- basic structure for scheduling
- post scheduling basics
- offer easy option to change adapter
- integrate the default scheduler adapter
- update scheduled posts when blog TZ changes
- safety check before scheduler can publish a post (not allowed to publish in the future or past)
- add force flag to allow publishing in the past
- invalidate cache header for /schedules/posts/:id
no issue
- config.theme.timezone can be undefined, when settings are not loaded from the database
- this PR will define the default blog TZ in config
- use `Etc/UTC` as default instead of `Europe/Dublin`
closes#6406
- created listeners.js connector
- merged listeners.js with events.js (in models/base)
- set a post to draft when published_at would be in the past
- reschedule a post when published_at would be in the future
refs #6949
- Adds a new VersionMismatchError with status 400 (bad request)
- Adds middleware that checks the X-Ghost-Version header if it is provided
- If it is not provided, the middleware does nothing
- If it is provided, and the versions match, the middleware does nothing
- If it is provided, and the versions don't match, the middleware returns a VersionMismatchError
- Includes both unit and a functional test to prove the middleware works alone and as part of the whole system
no issue
- changes xmlrcp & slack `init` function to be `listen`
- update the code to use `listen` instead of `init`
- changes the tests to make sure that event listeners are not wired up
- adds 100% test coverage
Since we added slack event listeners, the xmlrpc event tests have been throwing an error:
> Unhandled rejection Error
See: http://puu.sh/phvjZ.png
This is because both xmlrpc & slack are listening to `post.published` events.
xmlrpc didn't require any extra stubbing, but the slack listener did
By turning the listeners off after the tests, we reset the environment to not impact the next event test
We probably need to do more work like this to improve the systems around event handling and
make them more robust
issues #6406#6399
- all dates are stored as UTC with this commit
- use moment.tz.setDefault('UTC')
- add migration file to recalculate local datetimes to UTC
- store all dates in same format into our three supported databases
- add option to remeber migrations inside settings (core)
- support DST offset for migration
- ensure we force UTC in test env
- run whole migration as transaction
- extend: Settings.findOne function
closes#6406
- adding timeZone Service to get the offset (=timezone reg. moment-timezone) overall available
- new publishedAtOffset date as CP using timeZone service and moment-timezone to calculate offset incl. DST
- removing timezone-obj transform as it became obsolete with moment-timezone
- reading timezones from configuration/timezones api endpoint
- adding a moment-utc transform to only work with utc times in backend
- when switching the timezone in the select box, the user will be shown the local time of the selected timezone
- added clock service to show actual time ticking below select box
- default timezone is '(GMT) Greenwich Mean Time : Dublin, Edinburgh, London'
- if no timezone is saved in the settings yet, the default value will be used
- showing local time in 'Publish Date' when it's a draft and no actual publishedAt value exists
- Removed the format 'DD MMM YY @ HH:mm (UTC Z)' which resolves to '01 Jan 16 @ 14:00 (UTC +02:00)'
- Changing the date.js helper in core/server for moment-timezone
- Fix timezone select: updates `selectedTimezone` to return the matching object from `availableTimezones`
- Including timezones in test for date-helper
- update to moment-timezone 0.5.1
- moving form-group of 'selectTimezone' further up so
- Tests:
- Set except for clock service in test env
- adding fixtures to mirage
- adding 'service.ajax' to navigation-test.js
- adding 'service:ghostPaths' to navigation-test.js
- Code improvements
- Changing clockservice to ES6
refs #6534
- twitter & facebook fields are changing to store usernames only
- use the new social url util to generate urls where necessary
- update tests
refs #6534
- this PR assumes that we are now saving usernames only in the database for twitter & facebook
- adds a new social links utility which can generate twitter & facebook urls from the username
- adds a {{twitter_url}} and {{facebook_url}} helper which uses these
- adds a full suite of tests for the helpers & utils
refs #6534
- don't output publisher for the 'People' type on the author page
- change publisher to a full 'Organisation' for the 'Article' type on posts
Note:
Google's structured data validator also wants image & publisher.logo inside of Article to be full 'ImageObject's.
Currently, it output's an error for them: 'The attribute itemtype has an invalid value.'
However, the spec on Schema.org says a url is valid: https://schema.org/Article,
which is slightly different to Google's spec here: https://developers.google.com/structured-data/rich-snippets/articles#article_markup_properties
Ideally, we would output a full 'ImageObject', however we don't currently have the width & height info required.
Therefore, I think what we have is valid strictly speaking, but we should aim to fix this when we have better image tools.
no issue
- minor optical fix for schema.org metadata
- sameAs property was showing `null` value in array, if no data was provided
- instead of showing `null`, it will be empty, if no data (author website, facebook or twitter) it will be an empty array
- split out read CSV function into utility and add tests
- update API response to follow JSONAPI more closely
- update the UI to match the new API response
no issue
- add some more tests, optimise tests and finish tests
- subscriber model checks external context permissions in permissible fn
- add missing permissions for subscriber csv
closes#6534
- new input fields in general settings incl. validation
- facebook and twitter as new models in settings.js
- adds values for facebook and twitter to default-settings.js
- adds blog helpers for facebook and twittter
- rather than saving the whole URL, the Twitter username incl. '@' will be extracted from URL and saved in the settings. The User will still input the full URL. After saving the blog setting, the stored Twitter username will be parsed again as the full URL and available in the input field. A custom transform is used for this.
- adding meta fields to be rendered in {{ghost_head}}:
- '<meta property="article:publisher" content="https://www.facebook.com/page" />' and
- '<meta name="twitter:site" content="@user"/>'
- adds facebook and twitter to unit test for structured data
- adds unit test for general settings
- adds acceptance test for new input fields in general settings
- adds a custom transform for twitter model to save only the username to the server
- adds unit test for transform
closes#6584
- Frontend Changes:
- adds 'Apps' to Navigation Menu
- adds 'Slack' as nested page to Apps
- adds `apps.css`
- adds `slack-integration` model and uses `slack-settings` custom transform to parse JSON file
- adds validation for `slack` model
- adds fixtures and `slack/test` API endpoint to Mirage
- adds acceptance tests for `apps-test` and `slack-test`
- adds unit tests for `slack-settings` and `slack-integration`
- Backend Changes:
- adds API endpoint `slack/test` to send Test Notification
- adds default-values for slack model
- sends payload to slack:
- text: the url of the blogpost / test message
- icon_url: url to ghost logo
- username: Ghost
- adds `slack/index.js` to send webhook to slack if
- a new post is published (if slack webhook url is saved in settings)
- user clicks on 'Send Test Notification' in UI
- adds `slack.init()` to `server.index.js` to add event listener
- adds unit test for `slack/index`
no issue
- move the post lookup functionality into its own file
- handle the pre and post lookup checks to verify that we found the correct post
- checking that the URL matches handles checking that a post looked up with pagePermalink is a page
- all cases where there is no match throws a 404 directly, this could just call next() ?
refs #6644
- urls specified in config.js should be considered whitelisted/trusted
- this is not quite straightforward because config.js is not ready at the point the middleware is required
- tests have been updated to cover these new cases + use rewire to override the internal whitelist cache
refs #6589
- add internalAppsPath as a proper config path
- middleware/routes will be setup for any internal apps which have the function
- this should be refactored into some sort of proper hooks system as part of apps
- internal apps get permission to do anything the proxy allows
refs #6301, #4176
Add migration for:
- 5 new client permissions
- 15 relations between the admin, editor & author role and the 5 new permissions
- updates to tests to show that permissions get updated properly
refs #6301, #6399
- new scheduler client will be used by any web app that handles time and calls back to the scheduling API at the right time
- new scheduler client will need to be confidential, rather than public, hence the 'web' type instead of 'ua'
- adds validation to client type that it must have a type of 'ua', 'web', or 'native'
refs #6301
- move the temporary `fixClientSecret` function from migration.init into being a proper fixture migration task
- update the tests accordingly
refs #6301
- bump the default version & update corresponding test
- add empty task folders for 005 data & fixture migrations
- update tests to cover the new 005 upgrades
closes#5914, #6589
- moves all private-blogging related code & tests into /server/apps/private-blogging/
- rework Grunt to run private-blogging tests
- modify server apps code to have a place for internal apps
refs #6301, #4176
- always check existence of items before attempting to create them, in order to prevent duplicates
- provide stats on how many object creations are expected vs done
- split out and improve fixture utils tests (100% covers utils)
Refs #6644
- deps: cors@2.7.1; Add express cors package.
- Adds new middleware for proper CORS support.
- Handles CORS pre-flight checks.
- Separates request authentication/authorization from
CORS.
Refs #6642
- Do not send CORS headers on an invalid "origin"
header, but otherwise allow the response to
proceed normally. This enforces CORS for the browser
but does not blow up non-CORS requests.
refs #6301
- The order of model fixtures matters, so they should be in an array
- By splitting out the population code into a utils file, it'll be possible to use this to do updates as well
- This should make it much easier to do permissions updates in future
refs #6621, #6622
- remove unneeded `return new Promise.resolve()` lines
- reduce code in tests
- improve quality of tests checking that all task functions are executed
- add missing test coverage
- Simplify the `init` method in `models/index.js` so that it no longer
returns a promise. Easier to use.
- Eliminates the `deleteAllContent` method from `models/index.js` as it
can all be handled at the API layer in a single spot.
- Optimize `destroyAllContent` in `api/db.js`. Eliminates
double-fetching every post from the database and converting it to
JSON. Also only fetches ids from the database instead of the entire
model.
- Eliminates the custom static method `destroy` in the Post model in
favor of handling detaching tag relations in a single place (the
`destroying` event). This also eliminates a big source of unneeded
database round trips--needing to get post ids to feed into
`Post.destroy()` which then re-fetches the post again.
refs #6301
- fix messages that joined with comma and therefore missed outputting version no
- change `logInfo` to `logger` that has both an info and a warn method
- add new warn method to errors
- add a warn message everytime a migration (data or fixture) gets skipped over
- update logger everywhere, including tests
- update tests to check logger.warn gets called
refs #6301
- Make sure that every fixture operation has a check to ensure that it hasn't already run
- E.g. The update of sort_order on posts_tags should only happen if there are no values which aren't a zero
- This makes sure that we don't accidentally overwrite data on FORCE_MIGRATE
- No need to try to set settings types if they are already correct
- Only update the admin client if it needs it, else we're regenerating secrets each time
refs #5091, #6612
- fixes meta data so it won't output 'null' as a JSON-LD schema
- added test coverage for this if/else
- this case cannot happen within the existing system, it only happens with custom channels after #6612
refs #5091, #6612
- unify getNextUrl & getPrevUrl into getPaginatedUrl
- ensure that it can generate a prev, next or exact page no url
- ensure that it can figure out the base url
- use the same code from the page_url helper
- refactor the tests to ensure there's 100% coverage
Following on from #6612, this ensures that pagination always works regardless of whether the channel is default or custom
refs #5091
- makes post context explicit - data.post must be set, rather than post being the default
- uses channelConfig to determine the context for a channel (the channel name) rather than basing it off of the URL
- updates tests to setup the contexts more clearly, the outcome has not changed
Since #6469 req has channelConfig attached to it.
We can use req.channelConfig to determine what the context should be for a channel (the channel name)
This allows us to remove the hardcoded URLs, and means that custom channels will automatically get their own context.
Coupled with removing 'post' from being a default/fallthrough, to being explicitly set, this will reduce
potential context errors, as we start to extend the frontend capabilities
closes#6604
* Default for `to` was always `(from-1) + limit`. This caused a problem
where the `to` value could be higher than the length of the number of
blog posts, causing `@last` to never be called/reached
* Now sets `to` to have a default of `length` and if a limit was sent
through and not higher than `length`, to then set `to` to that value
* Added some extra tests for `@last` and `@first` use cases
* Added some inline commenting
refs #6301
- Replace builder & automated database upgrade with a set of explicit tasks
- Ensure the tasks can only happen if they need to
- Remove some duplicate code between fixture & db upgrades (more to do)
- Add tests
refs #6301
- changes createTable to use createTableIfNotExists, this is consistent with deletion
- splits out backup, reset, update and populate functions from migration/index into their own files
- moves the wrapped function for populatingDefaultSettings to fixtures.ensureDefaultSettings
- moves `modelOptions` down to the fixture files that actually use it
- adds test coverage for backup, reset and populate, but not for update as that needs refactoring
refs #6301
- `currentVersion` was leftover from before the first public release of Ghost!
- simplified the code for `getDatabaseVersion`
- improved & made consistent how errors are handled in `getDatabaseVersion`
- migration error handling updated to reflect the changes in `getDatabaseVersion`
- added tests for both `getDatabaseVersion` and `setDatabaseVersion`
no issue
- removes should-sinon dependency from package.json
- rewrites all usages of should-sinon to use normal should assertions
Unfortunately, should-sinon has very minimal documentation and therefore it is hard to discern what is considered a correctly-written assertion:
- in some cases, refactoring to use should-sinon causes false positives
- in other cases, assertions that work written in the normal way fail when written using should-sinon (e.g. getters, combos with rewire)
The additional overhead created by these issues outweigh any benefit from the easier-to-read assertions