├── core
├── server
│ ├── web
│ │ ├── admin
│ │ │ ├── views
│ │ │ │ └── .gitkeep
│ │ │ ├── index.js
│ │ │ ├── middleware.js
│ │ │ ├── serviceworker.js
│ │ │ └── controller.js
│ │ ├── site
│ │ │ ├── index.js
│ │ │ └── routes.js
│ │ ├── index.js
│ │ ├── shared
│ │ │ ├── index.js
│ │ │ └── middlewares
│ │ │ │ ├── image
│ │ │ │ └── index.js
│ │ │ │ ├── api
│ │ │ │ └── index.js
│ │ │ │ ├── validation
│ │ │ │ ├── index.js
│ │ │ │ └── profile-image.js
│ │ │ │ ├── update-user-last-seen.js
│ │ │ │ ├── emit-events.js
│ │ │ │ ├── labs.js
│ │ │ │ ├── maintenance.js
│ │ │ │ ├── pretty-urls.js
│ │ │ │ ├── ghost-locals.js
│ │ │ │ ├── admin-redirects.js
│ │ │ │ ├── frontend-client.js
│ │ │ │ └── log-request.js
│ │ └── api
│ │ │ └── v2
│ │ │ └── content
│ │ │ └── middleware.js
│ ├── apps
│ │ ├── private-blogging
│ │ │ ├── robots.txt
│ │ │ └── lib
│ │ │ │ └── helpers
│ │ │ │ └── index.js
│ │ ├── amp
│ │ │ ├── lib
│ │ │ │ └── helpers
│ │ │ │ │ └── index.js
│ │ │ └── index.js
│ │ └── subscribers
│ │ │ ├── index.js
│ │ │ └── lib
│ │ │ └── helpers
│ │ │ └── index.js
│ ├── api
│ │ ├── shared
│ │ │ ├── serializers
│ │ │ │ ├── output
│ │ │ │ │ └── index.js
│ │ │ │ ├── input
│ │ │ │ │ └── index.js
│ │ │ │ └── index.js
│ │ │ ├── utils
│ │ │ │ ├── index.js
│ │ │ │ └── options.js
│ │ │ ├── validators
│ │ │ │ ├── input
│ │ │ │ │ └── index.js
│ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── v2
│ │ │ ├── utils
│ │ │ │ ├── validators
│ │ │ │ │ ├── output
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── input
│ │ │ │ │ │ ├── schemas
│ │ │ │ │ │ │ ├── images-upload.json
│ │ │ │ │ │ │ ├── tags-edit.json
│ │ │ │ │ │ │ ├── pages-add.json
│ │ │ │ │ │ │ ├── posts-add.json
│ │ │ │ │ │ │ ├── pages-edit.json
│ │ │ │ │ │ │ ├── posts-edit.json
│ │ │ │ │ │ │ ├── tags-add.json
│ │ │ │ │ │ │ └── images.json
│ │ │ │ │ │ ├── tags.js
│ │ │ │ │ │ ├── pages.js
│ │ │ │ │ │ ├── posts.js
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── users.js
│ │ │ │ │ │ └── invites.js
│ │ │ │ │ └── utils
│ │ │ │ │ │ └── strip-keyword.js
│ │ │ │ ├── serializers
│ │ │ │ │ ├── output
│ │ │ │ │ │ ├── redirects.js
│ │ │ │ │ │ ├── preview.js
│ │ │ │ │ │ ├── oembed.js
│ │ │ │ │ │ ├── site.js
│ │ │ │ │ │ ├── config.js
│ │ │ │ │ │ ├── slugs.js
│ │ │ │ │ │ ├── images.js
│ │ │ │ │ │ ├── actions.js
│ │ │ │ │ │ ├── webhooks.js
│ │ │ │ │ │ ├── utils
│ │ │ │ │ │ │ └── date.js
│ │ │ │ │ │ ├── mail.js
│ │ │ │ │ │ ├── themes.js
│ │ │ │ │ │ ├── invites.js
│ │ │ │ │ │ ├── members.js
│ │ │ │ │ │ ├── authors.js
│ │ │ │ │ │ ├── tags.js
│ │ │ │ │ │ ├── all.js
│ │ │ │ │ │ ├── notifications.js
│ │ │ │ │ │ ├── pages.js
│ │ │ │ │ │ └── posts.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── input
│ │ │ │ │ │ ├── integrations.js
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── settings.js
│ │ │ │ │ │ ├── authors.js
│ │ │ │ │ │ ├── db.js
│ │ │ │ │ │ ├── users.js
│ │ │ │ │ │ └── tags.js
│ │ │ │ └── index.js
│ │ │ ├── slack.js
│ │ │ ├── roles.js
│ │ │ ├── settings-public.js
│ │ │ ├── images.js
│ │ │ ├── site.js
│ │ │ ├── members.js
│ │ │ └── config.js
│ │ ├── index.js
│ │ └── v0.1
│ │ │ └── slack.js
│ ├── services
│ │ ├── rss
│ │ │ ├── index.js
│ │ │ ├── renderer.js
│ │ │ └── cache.js
│ │ ├── themes
│ │ │ ├── config
│ │ │ │ ├── defaults.json
│ │ │ │ └── index.js
│ │ │ ├── engines
│ │ │ │ ├── defaults.json
│ │ │ │ └── index.js
│ │ │ ├── engine.js
│ │ │ ├── list.js
│ │ │ └── loader.js
│ │ ├── mail
│ │ │ └── index.js
│ │ ├── webhooks
│ │ │ ├── index.js
│ │ │ └── payload.js
│ │ ├── routing
│ │ │ ├── middlewares
│ │ │ │ └── index.js
│ │ │ ├── helpers
│ │ │ │ ├── error.js
│ │ │ │ ├── secure.js
│ │ │ │ ├── render-entries.js
│ │ │ │ ├── render-entry.js
│ │ │ │ ├── renderer.js
│ │ │ │ └── index.js
│ │ │ ├── index.js
│ │ │ ├── controllers
│ │ │ │ └── index.js
│ │ │ └── PreviewRouter.js
│ │ ├── url
│ │ │ └── index.js
│ │ ├── auth
│ │ │ ├── api-key
│ │ │ │ └── index.js
│ │ │ ├── session
│ │ │ │ └── index.js
│ │ │ ├── passport.js
│ │ │ └── index.js
│ │ ├── settings
│ │ │ ├── default-routes.yaml
│ │ │ └── public.js
│ │ ├── members
│ │ │ └── index.js
│ │ └── permissions
│ │ │ └── index.js
│ ├── lib
│ │ ├── members
│ │ │ ├── static
│ │ │ │ ├── gateway
│ │ │ │ │ └── index.html
│ │ │ │ └── auth
│ │ │ │ │ ├── .gitignore
│ │ │ │ │ ├── styles
│ │ │ │ │ └── members.css
│ │ │ │ │ ├── components
│ │ │ │ │ ├── FormSubmit.js
│ │ │ │ │ ├── FormHeaderCTA.js
│ │ │ │ │ ├── FormFooter.js
│ │ │ │ │ ├── FormHeader.js
│ │ │ │ │ ├── NameInput.js
│ │ │ │ │ ├── EmailInput.js
│ │ │ │ │ ├── PasswordInput.js
│ │ │ │ │ ├── CheckoutForm.js
│ │ │ │ │ └── FormInput.js
│ │ │ │ │ ├── postcss.config.js
│ │ │ │ │ ├── assets
│ │ │ │ │ └── images
│ │ │ │ │ │ ├── icon-right-arrow.svg
│ │ │ │ │ │ ├── icon-email.svg
│ │ │ │ │ │ ├── icon-lock.svg
│ │ │ │ │ │ ├── ghost-logo.svg
│ │ │ │ │ │ └── icon-name.svg
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── pages
│ │ │ │ │ ├── PasswordResetSentPage.js
│ │ │ │ │ ├── RequestPasswordResetPage.js
│ │ │ │ │ └── ResetPasswordPage.js
│ │ │ │ │ └── package.json
│ │ │ └── util.js
│ │ ├── mobiledoc
│ │ │ ├── atoms
│ │ │ │ ├── index.js
│ │ │ │ └── soft-return.js
│ │ │ ├── cards
│ │ │ │ ├── hr.js
│ │ │ │ ├── index.js
│ │ │ │ ├── card-markdown.js
│ │ │ │ ├── html.js
│ │ │ │ ├── code.js
│ │ │ │ └── embed.js
│ │ │ ├── index.js
│ │ │ └── create-card.js
│ │ ├── social
│ │ │ ├── index.js
│ │ │ └── urls.js
│ │ ├── fs
│ │ │ ├── index.js
│ │ │ ├── package-json
│ │ │ │ └── index.js
│ │ │ └── zip-folder.js
│ │ ├── common
│ │ │ ├── index.js
│ │ │ ├── logging.js
│ │ │ └── events.js
│ │ ├── constants.js
│ │ ├── security
│ │ │ ├── index.js
│ │ │ ├── url.js
│ │ │ ├── password.js
│ │ │ └── identifier.js
│ │ ├── image
│ │ │ └── index.js
│ │ ├── promise
│ │ │ └── sequence.js
│ │ ├── request.js
│ │ └── ghost-version.js
│ ├── public
│ │ ├── favicon.ico
│ │ ├── 404-ghost.png
│ │ ├── 404-ghost@2x.png
│ │ ├── robots.txt
│ │ └── ghost-sdk.min.js
│ ├── data
│ │ ├── schema
│ │ │ ├── fixtures
│ │ │ │ └── index.js
│ │ │ ├── clients
│ │ │ │ └── index.js
│ │ │ ├── index.js
│ │ │ └── checks.js
│ │ ├── migrations
│ │ │ ├── hooks
│ │ │ │ ├── init
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── before.js
│ │ │ │ │ └── shutdown.js
│ │ │ │ └── migrate
│ │ │ │ │ ├── afterEach.js
│ │ │ │ │ ├── beforeEach.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── before.js
│ │ │ │ │ └── shutdown.js
│ │ │ ├── versions
│ │ │ │ ├── 2.13
│ │ │ │ │ └── 1-remove-empty-strings.js
│ │ │ │ └── 2.0
│ │ │ │ │ └── 6-replace-fixture-posts.js
│ │ │ └── init
│ │ │ │ └── 2-create-fixtures.js
│ │ ├── db
│ │ │ └── index.js
│ │ ├── meta
│ │ │ ├── rss_url.js
│ │ │ ├── og_type.js
│ │ │ ├── published_date.js
│ │ │ ├── keywords.js
│ │ │ ├── canonical_url.js
│ │ │ ├── amp_url.js
│ │ │ ├── modified_date.js
│ │ │ ├── author_image.js
│ │ │ ├── creator_url.js
│ │ │ ├── author_fb_url.js
│ │ │ ├── author_url.js
│ │ │ ├── excerpt.js
│ │ │ ├── context_object.js
│ │ │ ├── blog_logo.js
│ │ │ ├── og_image.js
│ │ │ ├── cover_image.js
│ │ │ └── twitter_image.js
│ │ ├── xml
│ │ │ └── sitemap
│ │ │ │ ├── tag-generator.js
│ │ │ │ ├── page-generator.js
│ │ │ │ ├── post-generator.js
│ │ │ │ ├── utils.js
│ │ │ │ └── user-generator.js
│ │ └── importer
│ │ │ └── importers
│ │ │ └── data
│ │ │ ├── subscribers.js
│ │ │ └── roles.js
│ ├── models
│ │ ├── relations
│ │ │ └── index.js
│ │ ├── plugins
│ │ │ ├── index.js
│ │ │ └── transaction-events.js
│ │ ├── author.js
│ │ ├── tag-public.js
│ │ ├── refreshtoken.js
│ │ ├── app-field.js
│ │ ├── app-setting.js
│ │ ├── client-trusted-domain.js
│ │ └── accesstoken.js
│ ├── helpers
│ │ ├── tpl
│ │ │ ├── navigation.hbs
│ │ │ ├── pagination.hbs
│ │ │ └── subscribe_form.hbs
│ │ ├── encode.js
│ │ ├── title.js
│ │ ├── meta_title.js
│ │ ├── meta_description.js
│ │ ├── page_url.js
│ │ ├── asset.js
│ │ ├── lang.js
│ │ ├── url.js
│ │ ├── twitter_url.js
│ │ ├── facebook_url.js
│ │ └── is.js
│ ├── adapters
│ │ └── scheduling
│ │ │ ├── SchedulingBase.js
│ │ │ └── index.js
│ ├── config
│ │ └── env
│ │ │ ├── config.production.json
│ │ │ └── config.development.json
│ └── analytics-events.js
├── test
│ ├── utils
│ │ ├── fixtures
│ │ │ ├── settings
│ │ │ │ ├── test.yml
│ │ │ │ ├── notyaml.md
│ │ │ │ ├── badroutes.yaml
│ │ │ │ ├── goodroutes.yaml
│ │ │ │ ├── routes.yaml
│ │ │ │ └── newroutes.yaml
│ │ │ ├── test.hbs
│ │ │ ├── themes
│ │ │ │ ├── test-theme
│ │ │ │ │ ├── post.hbs
│ │ │ │ │ ├── default.hbs
│ │ │ │ │ ├── something.hbs
│ │ │ │ │ ├── index.hbs
│ │ │ │ │ ├── home.hbs
│ │ │ │ │ ├── assets
│ │ │ │ │ │ ├── screenshot-desktop.jpg
│ │ │ │ │ │ └── screenshot-mobile.jpg
│ │ │ │ │ └── podcast
│ │ │ │ │ │ └── rss.hbs
│ │ │ │ ├── test-theme-channels
│ │ │ │ │ ├── default.hbs
│ │ │ │ │ ├── channel2.hbs
│ │ │ │ │ └── channel3.hbs
│ │ │ │ ├── casper
│ │ │ │ │ ├── locales
│ │ │ │ │ │ ├── de.json
│ │ │ │ │ │ └── en.json
│ │ │ │ │ ├── assets
│ │ │ │ │ │ ├── screenshot-desktop.jpg
│ │ │ │ │ │ └── screenshot-mobile.jpg
│ │ │ │ │ └── partials
│ │ │ │ │ │ ├── icons
│ │ │ │ │ │ ├── facebook.hbs
│ │ │ │ │ │ ├── infinity.hbs
│ │ │ │ │ │ ├── rss.hbs
│ │ │ │ │ │ ├── avatar.hbs
│ │ │ │ │ │ ├── point.hbs
│ │ │ │ │ │ ├── location.hbs
│ │ │ │ │ │ ├── website.hbs
│ │ │ │ │ │ └── twitter.hbs
│ │ │ │ │ │ └── byline-single.hbs
│ │ │ │ ├── broken-theme
│ │ │ │ │ └── package.json
│ │ │ │ ├── casper.zip
│ │ │ │ ├── invalid.zip
│ │ │ │ ├── valid.zip
│ │ │ │ ├── warnings.zip
│ │ │ │ └── casper-1.4
│ │ │ │ │ ├── README.md
│ │ │ │ │ └── partials
│ │ │ │ │ └── navigation.hbs
│ │ │ ├── import
│ │ │ │ ├── zips
│ │ │ │ │ ├── zip-image-dir
│ │ │ │ │ │ └── images
│ │ │ │ │ │ │ └── image.jpg
│ │ │ │ │ ├── zip-without-base-dir
│ │ │ │ │ │ └── test.json
│ │ │ │ │ ├── zip-with-base-dir
│ │ │ │ │ │ └── basedir
│ │ │ │ │ │ │ └── test.json
│ │ │ │ │ ├── zip-old-roon-export
│ │ │ │ │ │ └── Roon-Export
│ │ │ │ │ │ │ └── published
│ │ │ │ │ │ │ └── test.md
│ │ │ │ │ └── zip-with-double-base-dir
│ │ │ │ │ │ └── basedir
│ │ │ │ │ │ └── basedir
│ │ │ │ │ │ └── test.json
│ │ │ │ ├── deleted-2014-12-19-test-1.md
│ │ │ │ ├── draft-2014-12-19-test-1.md
│ │ │ │ ├── draft-2014-12-19-test-2.md
│ │ │ │ ├── published-2014-12-19-test-1.md
│ │ │ │ └── draft-2014-12-19-test-3.md
│ │ │ ├── example.js
│ │ │ ├── csv
│ │ │ │ ├── single-column-with-header.csv
│ │ │ │ ├── two-columns-with-header.csv
│ │ │ │ └── two-columns-obscure-header.csv
│ │ │ ├── app
│ │ │ │ ├── badlib.js
│ │ │ │ ├── goodlib.js
│ │ │ │ ├── nested
│ │ │ │ │ └── goodnested.js
│ │ │ │ ├── badoutside.js
│ │ │ │ ├── badtop.js
│ │ │ │ ├── badinstall.js
│ │ │ │ ├── badrequire.js
│ │ │ │ └── good.js
│ │ │ ├── images
│ │ │ │ ├── favicon.ico
│ │ │ │ ├── favicon.png
│ │ │ │ ├── myicon.ico
│ │ │ │ ├── ghosticon.jpg
│ │ │ │ ├── ghost-logo.png
│ │ │ │ ├── ghost-logo.pngx
│ │ │ │ ├── loadingcat.gif
│ │ │ │ ├── favicon_too_large.png
│ │ │ │ ├── favicon_too_small.png
│ │ │ │ ├── loadingcat_square.gif
│ │ │ │ ├── favicon_16x_single.ico
│ │ │ │ ├── favicon_64x_single.ico
│ │ │ │ ├── favicon_multi_sizes.ico
│ │ │ │ ├── favicon_not_square.png
│ │ │ │ └── favicon_size_too_large.png
│ │ │ ├── config
│ │ │ │ ├── overrides.json
│ │ │ │ ├── env
│ │ │ │ │ ├── config.testing.json
│ │ │ │ │ └── config.testing-mysql.json
│ │ │ │ ├── defaults.json
│ │ │ │ ├── config.testing.json
│ │ │ │ └── config.testing-mysql.json
│ │ │ └── export
│ │ │ │ └── broken.json
│ │ ├── mocks
│ │ │ ├── index.js
│ │ │ └── utils.js
│ │ └── assertions.js
│ ├── .eslintignore
│ ├── unit
│ │ ├── web
│ │ │ ├── middleware
│ │ │ │ └── theme-handler_spec.js
│ │ │ ├── shared
│ │ │ │ └── middleware
│ │ │ │ │ └── api
│ │ │ │ │ └── spam-prevention_spec.js
│ │ │ └── api
│ │ │ │ └── v2
│ │ │ │ └── content
│ │ │ │ └── middleware_spec.js
│ │ ├── helpers
│ │ │ ├── test_tpl
│ │ │ │ ├── pagination.hbs
│ │ │ │ └── navigation.hbs
│ │ │ ├── encode_spec.js
│ │ │ ├── template_spec.js
│ │ │ ├── lang_spec.js
│ │ │ └── post_class_spec.js
│ │ ├── lib
│ │ │ ├── mobiledoc
│ │ │ │ ├── atoms
│ │ │ │ │ └── soft-return_spec.js
│ │ │ │ ├── cards
│ │ │ │ │ └── hr_spec.js
│ │ │ │ └── converters
│ │ │ │ │ └── converters_spec.js
│ │ │ ├── security
│ │ │ │ └── password_spec.js
│ │ │ └── promise
│ │ │ │ └── sequence_spec.js
│ │ ├── services
│ │ │ └── auth
│ │ │ │ └── passport_spec.js
│ │ ├── models
│ │ │ └── permission_spec.js
│ │ ├── data
│ │ │ └── meta
│ │ │ │ └── rss_url_spec.js
│ │ └── api
│ │ │ ├── v2
│ │ │ └── utils
│ │ │ │ └── index_spec.js
│ │ │ └── shared
│ │ │ └── util
│ │ │ └── options_spec.js
│ ├── regression
│ │ └── README.md
│ ├── .jshintrc
│ └── acceptance
│ │ └── README.md
└── index.js
├── .eslintignore
├── content
├── apps
│ └── README.md
├── logs
│ └── README.md
├── images
│ └── README.md
├── data
│ └── README.md
├── settings
│ └── README.md
└── adapters
│ └── README.md
├── .gitattributes
├── .gitmodules
├── .eslintrc.json
├── .github
├── PULL_REQUEST_TEMPLATE.md
├── stale.yml
└── ISSUE_TEMPLATE
│ ├── --anything-else.md
│ └── ---bug-report.md
├── .editorconfig
├── MigratorConfig.js
├── SECURITY.md
└── .npmignore
/core/server/web/admin/views/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/settings/test.yml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/settings/notyaml.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/test/.eslintignore:
--------------------------------------------------------------------------------
1 | core/test/coverage/**
2 |
--------------------------------------------------------------------------------
/core/test/unit/web/middleware/theme-handler_spec.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/test.hbs:
--------------------------------------------------------------------------------
1 |
HelloWorld
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/test-theme/post.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/test-theme/default.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/test-theme/something.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/test-theme-channels/default.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/server/web/site/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./app');
2 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/import/zips/zip-image-dir/images/image.jpg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/import/zips/zip-without-base-dir/test.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/server/apps/private-blogging/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
--------------------------------------------------------------------------------
/core/server/web/admin/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./app');
2 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/import/zips/zip-with-base-dir/basedir/test.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/test-theme-channels/channel2.hbs:
--------------------------------------------------------------------------------
1 | channel2
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/test-theme-channels/channel3.hbs:
--------------------------------------------------------------------------------
1 | channel3
--------------------------------------------------------------------------------
/core/server/api/shared/serializers/output/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/output/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/core/server/services/rss/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./renderer');
2 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/import/deleted-2014-12-19-test-1.md:
--------------------------------------------------------------------------------
1 | You're live! Nice.
--------------------------------------------------------------------------------
/core/test/utils/fixtures/import/draft-2014-12-19-test-1.md:
--------------------------------------------------------------------------------
1 | You're live! Nice.
--------------------------------------------------------------------------------
/core/server/lib/members/static/gateway/index.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/server/services/themes/config/defaults.json:
--------------------------------------------------------------------------------
1 | {
2 | "posts_per_page": 5
3 | }
4 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/import/zips/zip-old-roon-export/Roon-Export/published/test.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/import/zips/zip-with-double-base-dir/basedir/basedir/test.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/test-theme/index.hbs:
--------------------------------------------------------------------------------
1 | {{tag.slug}}
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | core/server/public/**/*.js
2 | core/server/lib/members/static/auth/**/*.js
3 |
--------------------------------------------------------------------------------
/content/apps/README.md:
--------------------------------------------------------------------------------
1 | # Content / Apps
2 |
3 | Coming soon, Ghost apps will appear here.
--------------------------------------------------------------------------------
/core/server/services/themes/engines/defaults.json:
--------------------------------------------------------------------------------
1 | {
2 | "ghost-api": "v0.1"
3 | }
4 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/example.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = {
3 | answer: 42
4 | };
5 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | /dist
3 | /build
4 | /*.log
5 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/import/draft-2014-12-19-test-2.md:
--------------------------------------------------------------------------------
1 | # Welcome to Ghost
2 |
3 | You're live! Nice.
--------------------------------------------------------------------------------
/core/test/utils/fixtures/import/published-2014-12-19-test-1.md:
--------------------------------------------------------------------------------
1 | #Welcome to Ghost
2 |
3 | You're live! Nice.
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper/locales/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "Top left Button": "Oben Links."
3 | }
4 |
--------------------------------------------------------------------------------
/core/server/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/server/public/favicon.ico
--------------------------------------------------------------------------------
/core/test/utils/fixtures/csv/single-column-with-header.csv:
--------------------------------------------------------------------------------
1 | email
2 | jbloggs@example.com
3 | test@example.com
4 |
--------------------------------------------------------------------------------
/core/server/public/404-ghost.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/server/public/404-ghost.png
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper/locales/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "Top left Button": "Left Button on Top"
3 | }
4 |
--------------------------------------------------------------------------------
/core/test/utils/mocks/index.js:
--------------------------------------------------------------------------------
1 | exports.utils = require('./utils');
2 | exports.express = require('./express');
3 |
--------------------------------------------------------------------------------
/content/logs/README.md:
--------------------------------------------------------------------------------
1 | # Content / Logs
2 |
3 | This is the default log file location when Ghost runs in Production.
4 |
--------------------------------------------------------------------------------
/core/server/public/404-ghost@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/server/public/404-ghost@2x.png
--------------------------------------------------------------------------------
/core/test/utils/fixtures/csv/two-columns-with-header.csv:
--------------------------------------------------------------------------------
1 | id,email
2 | 1,"jbloggs@example.com"
3 | 1,test@example.com
4 |
--------------------------------------------------------------------------------
/core/server/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Sitemap: {{blog-url}}/sitemap.xml
3 | Disallow: /ghost/
4 | Disallow: /p/
5 |
--------------------------------------------------------------------------------
/core/server/services/mail/index.js:
--------------------------------------------------------------------------------
1 | exports.GhostMailer = require('./GhostMailer');
2 | exports.utils = require('./utils');
3 |
--------------------------------------------------------------------------------
/core/server/web/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get shared() {
3 | return require('./shared');
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/app/badlib.js:
--------------------------------------------------------------------------------
1 | var knex = require('knex');
2 |
3 | module.exports = {
4 | knex: knex
5 | };
6 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/broken-theme/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "broken-theme",
3 | "version": "0.0.0"
4 | }
5 |
--------------------------------------------------------------------------------
/content/images/README.md:
--------------------------------------------------------------------------------
1 | # Content / Images
2 |
3 | If using the standard file storage, Ghost will upload images to this directory.
--------------------------------------------------------------------------------
/core/server/data/schema/fixtures/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./fixtures');
2 | module.exports.utils = require('./utils');
3 |
--------------------------------------------------------------------------------
/core/server/lib/mobiledoc/atoms/index.js:
--------------------------------------------------------------------------------
1 | const softReturn = require('./soft-return');
2 |
3 | module.exports = [softReturn];
4 |
--------------------------------------------------------------------------------
/core/server/lib/social/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get urls() {
3 | return require('./urls');
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/core/test/unit/helpers/test_tpl/pagination.hbs:
--------------------------------------------------------------------------------
1 | {{@blog.title}}
2 | Page {{page}} of {{pages}}
3 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/app/goodlib.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | util: function () {
3 | return 42;
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/csv/two-columns-obscure-header.csv:
--------------------------------------------------------------------------------
1 | id,Email Address
2 | 1,"jbloggs@example.com"
3 | 2,test@example.com
4 |
--------------------------------------------------------------------------------
/core/server/data/migrations/hooks/init/index.js:
--------------------------------------------------------------------------------
1 | exports.shutdown = require('./shutdown');
2 | exports.before = require('./before');
3 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/favicon.ico
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/favicon.png
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/myicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/myicon.ico
--------------------------------------------------------------------------------
/core/test/utils/fixtures/import/draft-2014-12-19-test-3.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Welcome to Ghost
4 |
5 | You're live! Nice.
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/themes/casper.zip
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/invalid.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/themes/invalid.zip
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/valid.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/themes/valid.zip
--------------------------------------------------------------------------------
/core/server/api/shared/utils/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get options() {
3 | return require('./options');
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/core/server/models/relations/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get authors() {
3 | return require('./authors');
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/core/server/services/webhooks/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get listen() {
3 | return require('./listen');
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/app/nested/goodnested.js:
--------------------------------------------------------------------------------
1 | var lib = require('../goodlib.js');
2 |
3 | module.exports = {
4 | other: 42
5 | };
6 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/ghosticon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/ghosticon.jpg
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/warnings.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/themes/warnings.zip
--------------------------------------------------------------------------------
/core/server/api/shared/serializers/input/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get all() {
3 | return require('./all');
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/core/server/api/shared/validators/input/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get all() {
3 | return require('./all');
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/core/server/services/themes/engines/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get create() {
3 | return require('./create');
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/config/overrides.json:
--------------------------------------------------------------------------------
1 | {
2 | "paths": {
3 | "appRoot": ".",
4 | "corePath": "core/"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/ghost-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/ghost-logo.png
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/ghost-logo.pngx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/ghost-logo.pngx
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/loadingcat.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/loadingcat.gif
--------------------------------------------------------------------------------
/content/data/README.md:
--------------------------------------------------------------------------------
1 | # Content / Data
2 |
3 | This is the home of your Ghost database, do not overwrite this folder or any of the files inside of it.
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/test-theme/home.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ghost_head}}
4 |
5 |
6 | home.hbs
7 |
8 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # enforce unix style line endings
2 | *.js text eol=lf
3 | *.md text eol=lf
4 | *.json text eol=lf
5 | *.yml text eol=lf
6 | *.hbs text eol=lf
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/favicon_too_large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/favicon_too_large.png
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/favicon_too_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/favicon_too_small.png
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/loadingcat_square.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/loadingcat_square.gif
--------------------------------------------------------------------------------
/core/server/services/routing/middlewares/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get pageParam() {
3 | return require('./page-param');
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/favicon_16x_single.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/favicon_16x_single.ico
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/favicon_64x_single.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/favicon_64x_single.ico
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/favicon_multi_sizes.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/favicon_multi_sizes.ico
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/favicon_not_square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/favicon_not_square.png
--------------------------------------------------------------------------------
/core/test/utils/fixtures/images/favicon_size_too_large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/images/favicon_size_too_large.png
--------------------------------------------------------------------------------
/core/test/unit/helpers/test_tpl/navigation.hbs:
--------------------------------------------------------------------------------
1 | {{@blog.title}}
2 |
3 | {{#foreach navigation}}
4 | {{label}}
5 | {{/foreach}}
6 |
--------------------------------------------------------------------------------
/core/server/services/url/index.js:
--------------------------------------------------------------------------------
1 | const UrlService = require('./UrlService'),
2 | urlService = new UrlService();
3 |
4 | // Singleton
5 | module.exports = urlService;
6 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/redirects.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | download(response, apiConfig, frame) {
3 | frame.response = response;
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/core/server/data/migrations/hooks/migrate/afterEach.js:
--------------------------------------------------------------------------------
1 | var Promise = require('bluebird');
2 |
3 | module.exports = function afterEach() {
4 | return Promise.resolve();
5 | };
6 |
--------------------------------------------------------------------------------
/core/server/data/migrations/hooks/migrate/beforeEach.js:
--------------------------------------------------------------------------------
1 | var Promise = require('bluebird');
2 |
3 | module.exports = function beforeEach() {
4 | return Promise.resolve();
5 | };
6 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper/assets/screenshot-desktop.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/themes/casper/assets/screenshot-desktop.jpg
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper/assets/screenshot-mobile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/themes/casper/assets/screenshot-mobile.jpg
--------------------------------------------------------------------------------
/core/server/apps/private-blogging/lib/helpers/index.js:
--------------------------------------------------------------------------------
1 | module.exports = function registerHelpers(ghost) {
2 | ghost.helpers.register('input_password', require('./input_password'));
3 | };
4 |
--------------------------------------------------------------------------------
/core/server/data/schema/clients/index.js:
--------------------------------------------------------------------------------
1 | var sqlite3 = require('./sqlite3'),
2 | mysql = require('./mysql');
3 |
4 | module.exports = {
5 | sqlite3: sqlite3,
6 | mysql: mysql
7 | };
8 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/styles/members.css:
--------------------------------------------------------------------------------
1 | @import './normalize.css';
2 | @import './utils.css';
3 | @import './variables.css';
4 | @import './components.css';
5 | @import './screen.css';
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/test-theme/assets/screenshot-desktop.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/themes/test-theme/assets/screenshot-desktop.jpg
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/test-theme/assets/screenshot-mobile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grangier/Ghost/master/core/test/utils/fixtures/themes/test-theme/assets/screenshot-mobile.jpg
--------------------------------------------------------------------------------
/core/server/api/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./v0.1');
2 | module.exports['v0.1'] = require('./v0.1');
3 | module.exports.v2 = require('./v2');
4 | module.exports.shared = require('./shared');
5 |
--------------------------------------------------------------------------------
/core/server/web/site/routes.js:
--------------------------------------------------------------------------------
1 | const routing = require('../../services/routing');
2 |
3 | module.exports = function siteRoutes(options = {}) {
4 | return routing.bootstrap.init(options);
5 | };
6 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/config/env/config.testing.json:
--------------------------------------------------------------------------------
1 | {
2 | "url": "http://localhost:2368",
3 | "logging": {
4 | "level": "info",
5 | "transports": ["stdout"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/core/server/lib/mobiledoc/atoms/soft-return.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'soft-return',
3 | type: 'dom',
4 | render(opts) {
5 | return opts.env.dom.createElement('br');
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/config/env/config.testing-mysql.json:
--------------------------------------------------------------------------------
1 | {
2 | "url": "http://localhost:2368",
3 | "logging": {
4 | "level": "info",
5 | "transports": ["stdout"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "content/themes/casper"]
2 | path = content/themes/casper
3 | url = ../../TryGhost/Casper.git
4 | [submodule "core/client"]
5 | path = core/client
6 | url = ../../TryGhost/Ghost-Admin.git
7 |
--------------------------------------------------------------------------------
/core/server/data/migrations/versions/2.13/1-remove-empty-strings.js:
--------------------------------------------------------------------------------
1 | const Promise = require('bluebird');
2 |
3 | module.exports.up = () => Promise.resolve();
4 |
5 | module.exports.down = () => Promise.resolve();
6 |
--------------------------------------------------------------------------------
/core/server/services/auth/api-key/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get admin() {
3 | return require('./admin');
4 | },
5 | get content() {
6 | return require('./content');
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper/partials/icons/facebook.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true,
4 | "node": true
5 | },
6 | "plugins": [
7 | "ghost"
8 | ],
9 | "extends": [
10 | "plugin:ghost/node"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/core/server/api/shared/validators/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get handle() {
3 | return require('./handle');
4 | },
5 |
6 | get input() {
7 | return require('./input');
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/core/server/lib/fs/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get readCSV() {
3 | return require('./read-csv');
4 | },
5 |
6 | get zipFolder() {
7 | return require('./zip-folder');
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/core/server/services/settings/default-routes.yaml:
--------------------------------------------------------------------------------
1 | routes:
2 |
3 | collections:
4 | /:
5 | permalink: /{slug}/
6 | template: index
7 |
8 | taxonomies:
9 | tag: /tag/{slug}/
10 | author: /author/{slug}/
11 |
--------------------------------------------------------------------------------
/core/server/web/shared/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get middlewares() {
3 | return require('./middlewares');
4 | },
5 |
6 | get utils() {
7 | return require('./utils');
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/export/broken.json:
--------------------------------------------------------------------------------
1 | {"asdadas":[{
2 | "meta": {
3 | "exported_on": 1388318311015,
4 | "version": "003"
5 | },
6 | "data": {
7 | "posts": []
8 | }
9 | }]}
10 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get input() {
3 | return require('./input');
4 | },
5 |
6 | get output() {
7 | return require('./output');
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/preview.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | all(model, apiConfig, frame) {
3 | frame.response = {
4 | preview: [model.toJSON(frame.options)]
5 | };
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get input() {
3 | return require('./input');
4 | },
5 |
6 | get output() {
7 | return require('./output');
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/core/server/data/migrations/hooks/migrate/index.js:
--------------------------------------------------------------------------------
1 | exports.before = require('./before');
2 | exports.beforeEach = require('./beforeEach');
3 | exports.afterEach = require('./afterEach');
4 | exports.shutdown = require('./shutdown');
5 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/config/defaults.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "client": "sqlite3",
4 | "connection": {
5 | "filename": "/test.db"
6 | },
7 | "debug": false
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/settings/badroutes.yaml:
--------------------------------------------------------------------------------
1 | routes:
2 |
3 | collections:
4 | /
5 | permalink: /{slug}/
6 | template:
7 | - index
8 |
9 | taxonomies:
10 | tag: /tag/{slug}/
11 | author: /author/{slug}/
12 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/settings/goodroutes.yaml:
--------------------------------------------------------------------------------
1 | routes:
2 |
3 | collections:
4 | /:
5 | permalink: /{slug}/
6 | template:
7 | - index
8 |
9 | taxonomies:
10 | tag: /tag/{slug}/
11 | author: /author/{slug}/
12 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/settings/routes.yaml:
--------------------------------------------------------------------------------
1 | routes:
2 |
3 | collections:
4 | /:
5 | permalink: /{slug}/
6 | template:
7 | - index
8 |
9 | taxonomies:
10 | tag: /tag/{slug}/
11 | author: /author/{slug}/
12 |
--------------------------------------------------------------------------------
/core/server/data/migrations/hooks/init/before.js:
--------------------------------------------------------------------------------
1 | var Promise = require('bluebird'),
2 | models = require('../../../../models');
3 |
4 | module.exports = function before() {
5 | models.init();
6 | return Promise.resolve();
7 | };
8 |
--------------------------------------------------------------------------------
/core/server/data/migrations/hooks/migrate/before.js:
--------------------------------------------------------------------------------
1 | var backup = require('../../../db/backup'),
2 | models = require('../../../../models');
3 |
4 | module.exports = function before() {
5 | models.init();
6 | return backup();
7 | };
8 |
--------------------------------------------------------------------------------
/core/server/data/schema/index.js:
--------------------------------------------------------------------------------
1 | module.exports.tables = require('./schema');
2 | module.exports.checks = require('./checks');
3 | module.exports.commands = require('./commands');
4 | module.exports.defaultSettings = require('./default-settings');
5 |
--------------------------------------------------------------------------------
/core/server/web/shared/middlewares/image/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get normalize() {
3 | return require('./normalize');
4 | },
5 | get handleImageSizes() {
6 | return require('./handle-image-sizes');
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/settings/newroutes.yaml:
--------------------------------------------------------------------------------
1 | routes:
2 |
3 | collections:
4 | /blog/:
5 | permalink: /blog/{year}/{slug}/
6 | template:
7 | - index
8 |
9 | taxonomies:
10 | tag: /category/{slug}/
11 | author: /author/{slug}/
12 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper/partials/icons/infinity.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/server/helpers/tpl/navigation.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#foreach navigation}}
3 | - {{label}}
4 | {{/foreach}}
5 |
6 |
--------------------------------------------------------------------------------
/core/server/lib/mobiledoc/cards/hr.js:
--------------------------------------------------------------------------------
1 | const createCard = require('../create-card');
2 |
3 | module.exports = createCard({
4 | name: 'hr',
5 | type: 'dom',
6 | render(opts) {
7 | return opts.env.dom.createElement('hr');
8 | }
9 | });
10 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/components/FormSubmit.js:
--------------------------------------------------------------------------------
1 | export default ({onClick, label}) => (
2 |
3 |
6 |
7 | );
8 |
--------------------------------------------------------------------------------
/core/server/adapters/scheduling/SchedulingBase.js:
--------------------------------------------------------------------------------
1 | function SchedulingBase() {
2 | Object.defineProperty(this, 'requiredFns', {
3 | value: ['schedule', 'unschedule', 'reschedule', 'run'],
4 | writable: false
5 | });
6 | }
7 |
8 | module.exports = SchedulingBase;
9 |
--------------------------------------------------------------------------------
/core/server/api/v2/slack.js:
--------------------------------------------------------------------------------
1 | const common = require('../../lib/common');
2 |
3 | module.exports = {
4 | docName: 'slack',
5 | sendTest: {
6 | permissions: false,
7 | query() {
8 | common.events.emit('slack.test');
9 | }
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/schemas/images-upload.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "$schema": "http://json-schema.org/draft-07/schema#",
4 | "$id": "images.upload",
5 | "title": "images.upload",
6 | "description": "Schema for images.upload",
7 | "$ref": "images#/definitions/image"
8 | }
9 |
--------------------------------------------------------------------------------
/core/server/data/db/index.js:
--------------------------------------------------------------------------------
1 | var connection;
2 |
3 | Object.defineProperty(exports, 'knex', {
4 | enumerable: true,
5 | configurable: true,
6 | get: function get() {
7 | connection = connection || require('./connection');
8 | return connection;
9 | }
10 | });
11 |
--------------------------------------------------------------------------------
/core/server/lib/mobiledoc/cards/index.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | require('./card-markdown'),
3 | require('./code'),
4 | require('./embed'),
5 | require('./hr'),
6 | require('./html'),
7 | require('./image'),
8 | require('./markdown'),
9 | require('./gallery')
10 | ];
11 |
--------------------------------------------------------------------------------
/core/server/api/shared/serializers/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get handle() {
3 | return require('./handle');
4 | },
5 |
6 | get input() {
7 | return require('./input');
8 | },
9 |
10 | get output() {
11 | return require('./output');
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/core/server/data/meta/rss_url.js:
--------------------------------------------------------------------------------
1 | const routingService = require('../../services/routing');
2 |
3 | function getRssUrl(data, absolute) {
4 | return routingService.registry.getRssUrl({
5 | secure: data.secure,
6 | absolute: absolute
7 | });
8 | }
9 |
10 | module.exports = getRssUrl;
11 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper/partials/icons/rss.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/oembed.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:oembed');
2 |
3 | module.exports = {
4 | all(res, apiConfig, frame) {
5 | debug('all');
6 | frame.response = res;
7 | debug(frame.response);
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/components/FormHeaderCTA.js:
--------------------------------------------------------------------------------
1 | export default ({title, label, icon, hash}) => (
2 |
9 | );
10 |
--------------------------------------------------------------------------------
/core/test/regression/README.md:
--------------------------------------------------------------------------------
1 | ## Regression Tests
2 |
3 | This folder should contain packages which we test in a cron job once per day.
4 | These tests should ensure that we don't break Ghost.
5 |
6 | The goal is that most of these packages use Ghost's API's to test behaviours, otherwise transform the tests into unit tests.
7 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/app/badoutside.js:
--------------------------------------------------------------------------------
1 | var lib = require('../example');
2 |
3 | function BadApp(app) {
4 | this.app = app;
5 | }
6 |
7 | BadApp.prototype.install = function () {
8 | return lib.answer;
9 | };
10 |
11 | BadApp.prototype.activate = function () {
12 | };
13 |
14 | module.exports = BadApp;
15 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/site.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:site');
2 |
3 | module.exports = {
4 | read(data, apiConfig, frame) {
5 | debug('read');
6 |
7 | frame.response = {
8 | site: data
9 | };
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/core/server/data/migrations/versions/2.0/6-replace-fixture-posts.js:
--------------------------------------------------------------------------------
1 | const Promise = require('bluebird');
2 |
3 | // @NOTE: the logic of this migration script was removed.
4 | module.exports.up = () => {
5 | return Promise.resolve();
6 | };
7 |
8 | module.exports.down = () => {
9 | return Promise.resolve();
10 | };
11 |
--------------------------------------------------------------------------------
/core/server/models/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | filter: require('./filter'),
3 | includeCount: require('./include-count'),
4 | pagination: require('./pagination'),
5 | collision: require('./collision'),
6 | transactionEvents: require('./transaction-events'),
7 | hasPosts: require('./has-posts')
8 | };
9 |
--------------------------------------------------------------------------------
/core/server/web/shared/middlewares/api/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get cors() {
3 | return require('./cors');
4 | },
5 |
6 | get spamPrevention() {
7 | return require('./spam-prevention');
8 | },
9 |
10 | get versionMatch() {
11 | return require('./version-match');
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/core/server/web/shared/middlewares/validation/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get upload() {
3 | return require('./upload');
4 | },
5 |
6 | get blogIcon() {
7 | return require('./blog-icon');
8 | },
9 |
10 | get profileImage() {
11 | return require('./profile-image');
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/app/badtop.js:
--------------------------------------------------------------------------------
1 | var knex = require('knex');
2 |
3 | function BadApp(app) {
4 | this.app = app;
5 | }
6 |
7 | BadApp.prototype.install = function () {
8 | return knex.dropTableIfExists('users');
9 | };
10 |
11 | BadApp.prototype.activate = function () {
12 | };
13 |
14 | module.exports = BadApp;
15 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/app/badinstall.js:
--------------------------------------------------------------------------------
1 | function BadApp(app) {
2 | this.app = app;
3 | }
4 |
5 | BadApp.prototype.install = function () {
6 | var knex = require('knex');
7 |
8 | return knex.dropTableIfExists('users');
9 | };
10 |
11 | BadApp.prototype.activate = function () {
12 | };
13 |
14 | module.exports = BadApp;
15 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/config/config.testing.json:
--------------------------------------------------------------------------------
1 | {
2 | "paths": {
3 | "corePath": "try-to-override"
4 | },
5 | "database": {
6 | "connection": {
7 | "filename": "/hehe.db"
8 | },
9 | "debug": true
10 | },
11 | "logging": {
12 | "level": "error"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/test-theme/podcast/rss.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{@blog.title}}
4 | {{#get "posts" filter="featured:true" limit="20"}}
5 | {{#foreach posts}}
6 | {{url}}
7 | {{/foreach}}
8 | {{/get}}
9 |
10 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/config.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:config');
2 |
3 | module.exports = {
4 | all(data, apiConfig, frame) {
5 | frame.response = {
6 | config: data
7 | };
8 |
9 | debug(frame.response);
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/app/badrequire.js:
--------------------------------------------------------------------------------
1 | var lib = require('./badlib');
2 |
3 | function BadApp(app) {
4 | this.app = app;
5 | }
6 |
7 | BadApp.prototype.install = function () {
8 | return lib.knex.dropTableIfExists('users');
9 | };
10 |
11 | BadApp.prototype.activate = function () {
12 | };
13 |
14 | module.exports = BadApp;
15 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/config/config.testing-mysql.json:
--------------------------------------------------------------------------------
1 | {
2 | "paths": {
3 | "corePath": "try-to-override"
4 | },
5 | "database": {
6 | "connection": {
7 | "filename": "/hehe.db"
8 | },
9 | "debug": true
10 | },
11 | "logging": {
12 | "level": "error"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/core/server/adapters/scheduling/index.js:
--------------------------------------------------------------------------------
1 | const postScheduling = require(__dirname + '/post-scheduling');
2 |
3 | /**
4 | * scheduling modules:
5 | * - post scheduling: publish posts/pages when scheduled
6 | */
7 | exports.init = function init(options) {
8 | options = options || {};
9 |
10 | return postScheduling.init(options);
11 | };
12 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/components/FormFooter.js:
--------------------------------------------------------------------------------
1 | export default ({title, hash, label}) => (
2 |
10 | );
11 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('postcss-import'),
4 | require('autoprefixer'),
5 | require('postcss-css-variables'),
6 | require('postcss-color-mod-function'),
7 | require('cssnano'),
8 | require('postcss-custom-properties')
9 | ]
10 | };
11 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper/partials/icons/avatar.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/server/data/xml/sitemap/tag-generator.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash'),
2 | BaseMapGenerator = require('./base-generator');
3 |
4 | class TagsMapGenerator extends BaseMapGenerator {
5 | constructor(opts) {
6 | super();
7 |
8 | this.name = 'tags';
9 | _.extend(this, opts);
10 | }
11 | }
12 |
13 | module.exports = TagsMapGenerator;
14 |
--------------------------------------------------------------------------------
/core/server/helpers/encode.js:
--------------------------------------------------------------------------------
1 | // # Encode Helper
2 | //
3 | // Usage: `{{encode uri}}`
4 | //
5 | // Returns URI encoded string
6 |
7 | var proxy = require('./proxy'),
8 | SafeString = proxy.SafeString;
9 |
10 | module.exports = function encode(string, options) {
11 | var uri = string || options;
12 | return new SafeString(encodeURIComponent(uri));
13 | };
14 |
--------------------------------------------------------------------------------
/core/server/lib/common/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get i18n() {
3 | return require('./i18n');
4 | },
5 |
6 | get events() {
7 | return require('./events');
8 | },
9 |
10 | get errors() {
11 | return require('./errors');
12 | },
13 |
14 | get logging() {
15 | return require('./logging');
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/core/server/lib/mobiledoc/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | activate() {
3 | // needed by ghost
4 | },
5 |
6 | get cards() {
7 | return require('./cards');
8 | },
9 |
10 | get atoms() {
11 | return require('./atoms');
12 | },
13 |
14 | get converters() {
15 | return require('./converters');
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper/partials/icons/point.hbs:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/slugs.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:slugs');
2 |
3 | module.exports = {
4 | all(slug, apiConfig, frame) {
5 | debug('all');
6 |
7 | frame.response = {
8 | slugs: [{slug}]
9 | };
10 |
11 | debug(frame.response);
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/core/server/data/xml/sitemap/page-generator.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash'),
2 | BaseMapGenerator = require('./base-generator');
3 |
4 | class PageMapGenerator extends BaseMapGenerator {
5 | constructor(opts) {
6 | super();
7 |
8 | this.name = 'pages';
9 |
10 | _.extend(this, opts);
11 | }
12 | }
13 |
14 | module.exports = PageMapGenerator;
15 |
--------------------------------------------------------------------------------
/core/server/data/xml/sitemap/post-generator.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash'),
2 | BaseMapGenerator = require('./base-generator');
3 |
4 | class PostMapGenerator extends BaseMapGenerator {
5 | constructor(opts) {
6 | super();
7 |
8 | this.name = 'posts';
9 |
10 | _.extend(this, opts);
11 | }
12 | }
13 |
14 | module.exports = PostMapGenerator;
15 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper/partials/icons/location.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/server/lib/mobiledoc/cards/card-markdown.js:
--------------------------------------------------------------------------------
1 | // this card is just an alias of the `markdown` card which is necessary because
2 | // our markdown-only editor was using the `card-markdown` card name
3 | const markdownCard = require('./markdown');
4 | const createCard = require('../create-card');
5 |
6 | module.exports = createCard(
7 | Object.assign({}, markdownCard, {name: 'card-markdown'})
8 | );
9 |
--------------------------------------------------------------------------------
/core/server/services/routing/helpers/error.js:
--------------------------------------------------------------------------------
1 | function handleError(next) {
2 | return function handleError(err) {
3 | // If we've thrown an error message of type: 'NotFound' then we found no path match.
4 | if (err.errorType === 'NotFoundError') {
5 | return next();
6 | }
7 |
8 | return next(err);
9 | };
10 | }
11 |
12 | module.exports = handleError;
13 |
--------------------------------------------------------------------------------
/core/server/api/shared/utils/options.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 |
3 | const trimAndLowerCase = (params) => {
4 | params = params || '';
5 |
6 | if (_.isString(params)) {
7 | params = params.split(',');
8 | }
9 |
10 | return params.map((item) => {
11 | return item.trim().toLowerCase();
12 | });
13 | };
14 |
15 | module.exports.trimAndLowerCase = trimAndLowerCase;
16 |
--------------------------------------------------------------------------------
/core/server/data/meta/og_type.js:
--------------------------------------------------------------------------------
1 | function getOgType(data) {
2 | var context = data.context ? data.context[0] : null;
3 |
4 | context = context === 'amp' ? 'post' : context;
5 |
6 | if (context === 'author') {
7 | return 'profile';
8 | }
9 | if (context === 'post') {
10 | return 'article';
11 | }
12 | return 'website';
13 | }
14 |
15 | module.exports = getOgType;
16 |
--------------------------------------------------------------------------------
/core/server/data/meta/published_date.js:
--------------------------------------------------------------------------------
1 | function getPublishedDate(data) {
2 | var context = data.context ? data.context[0] : null;
3 |
4 | context = context === 'amp' ? 'post' : context;
5 |
6 | if (data[context] && data[context].published_at) {
7 | return new Date(data[context].published_at).toISOString();
8 | }
9 | return null;
10 | }
11 |
12 | module.exports = getPublishedDate;
13 |
--------------------------------------------------------------------------------
/core/server/services/routing/helpers/secure.js:
--------------------------------------------------------------------------------
1 | // TODO: figure out how to remove the need for this
2 | // Add Request context parameter to the data object
3 | // to be passed down to the templates
4 | function setRequestIsSecure(req, data) {
5 | (Array.isArray(data) ? data : [data]).forEach(function forEach(d) {
6 | d.secure = req.secure;
7 | });
8 | }
9 |
10 | module.exports = setRequestIsSecure;
11 |
--------------------------------------------------------------------------------
/core/server/data/migrations/hooks/migrate/shutdown.js:
--------------------------------------------------------------------------------
1 | const database = require('../../../db');
2 |
3 | module.exports = function shutdown(options = {}) {
4 | /**
5 | * We have to close Ghost's db connection if knex-migrator was used in the shell.
6 | * Otherwise the process doesn't exit.
7 | */
8 | if (options.executedFromShell === true) {
9 | return database.knex.destroy();
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/core/server/lib/constants.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | ONE_HOUR_S: 3600,
3 | ONE_DAY_S: 86400,
4 | ONE_MONTH_S: 2628000,
5 | SIX_MONTH_S: 15768000,
6 | ONE_YEAR_S: 31536000,
7 | FIVE_MINUTES_MS: 300000,
8 | ONE_HOUR_MS: 3600000,
9 | ONE_DAY_MS: 86400000,
10 | ONE_WEEK_MS: 604800000,
11 | ONE_MONTH_MS: 2628000000,
12 | SIX_MONTH_MS: 15768000000,
13 | ONE_YEAR_MS: 31536000000
14 | };
15 |
--------------------------------------------------------------------------------
/content/settings/README.md:
--------------------------------------------------------------------------------
1 | # Content / Settings
2 |
3 | ### routes.yaml
4 |
5 |
6 |
7 | This is how the default `routes.yaml` file looks like:
8 |
9 | ```yaml
10 | routes:
11 |
12 | collections:
13 | /:
14 | permalink: '/{slug}/'
15 | template:
16 | - index
17 |
18 | taxonomies:
19 | tag: /tag/{slug}/
20 | author: /author/{slug}/
21 | ```
22 |
--------------------------------------------------------------------------------
/core/server/data/meta/keywords.js:
--------------------------------------------------------------------------------
1 | const models = require('../../models');
2 |
3 | function getKeywords(data) {
4 | if (data.post && data.post.tags && data.post.tags.length > 0) {
5 | return models.Base.Model.filterByVisibility(data.post.tags, ['public'], false, function processItem(item) {
6 | return item.name;
7 | });
8 | }
9 | return null;
10 | }
11 |
12 | module.exports = getKeywords;
13 |
--------------------------------------------------------------------------------
/core/server/helpers/title.js:
--------------------------------------------------------------------------------
1 | // # Title Helper
2 | // Usage: `{{title}}`
3 | //
4 | // Overrides the standard behaviour of `{[title}}` to ensure the content is correctly escaped
5 |
6 | var proxy = require('./proxy'),
7 | SafeString = proxy.SafeString,
8 | escapeExpression = proxy.escapeExpression;
9 |
10 | module.exports = function title() {
11 | return new SafeString(escapeExpression(this.title || ''));
12 | };
13 |
--------------------------------------------------------------------------------
/core/server/lib/social/urls.js:
--------------------------------------------------------------------------------
1 | module.exports.twitter = function twitter(username) {
2 | // Creates the canonical twitter URL without the '@'
3 | return 'https://twitter.com/' + username.replace(/^@/, '');
4 | };
5 |
6 | module.exports.facebook = function facebook(username) {
7 | // Handles a starting slash, this shouldn't happen, but just in case
8 | return 'https://www.facebook.com/' + username.replace(/^\//, '');
9 | };
10 |
--------------------------------------------------------------------------------
/core/server/services/auth/session/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // @TODO: expose files/units and not functions of units
3 | get createSession() {
4 | return require('./middleware').createSession;
5 | },
6 |
7 | get destroySession() {
8 | return require('./middleware').destroySession;
9 | },
10 |
11 | get authenticate() {
12 | return require('./middleware').authenticate;
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Got some code for us? Awesome 🎊!
2 |
3 | Please include a description of your change & check your PR against this list, thanks!
4 |
5 | - [ ] There's a clear use-case for this code change
6 | - [ ] Commit message has a short title & references relevant issues
7 | - [ ] The build will pass (run `yarn test` and `yarn lint`)
8 |
9 | More info can be found by clicking the "guidelines for contributing" link above.
10 |
--------------------------------------------------------------------------------
/core/server/data/meta/canonical_url.js:
--------------------------------------------------------------------------------
1 | var urlService = require('../../services/url'),
2 | getUrl = require('./url');
3 |
4 | function getCanonicalUrl(data) {
5 | var url = urlService.utils.urlJoin(urlService.utils.urlFor('home', true), getUrl(data, false));
6 |
7 | if (url.indexOf('/amp/')) {
8 | url = url.replace(/\/amp\/$/i, '/');
9 | }
10 |
11 | return url;
12 | }
13 |
14 | module.exports = getCanonicalUrl;
15 |
--------------------------------------------------------------------------------
/core/server/helpers/tpl/pagination.hbs:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/core/server/lib/security/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get url() {
3 | return require('./url');
4 | },
5 |
6 | get tokens() {
7 | return require('./tokens');
8 | },
9 |
10 | get string() {
11 | return require('./string');
12 | },
13 |
14 | get identifier() {
15 | return require('./identifier');
16 | },
17 |
18 | get password() {
19 | return require('./password');
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper/partials/icons/website.hbs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 4
9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.hbs]
14 | insert_final_newline = false
15 |
16 | [{package}.json]
17 | indent_size = 2
18 |
19 | [*.md]
20 | trim_trailing_whitespace = false
21 |
22 | [*.yml]
23 | indent_size = 2
24 |
25 | [Makefile]
26 | indent_style = tab
27 |
--------------------------------------------------------------------------------
/core/server/helpers/meta_title.js:
--------------------------------------------------------------------------------
1 | // # Meta Title Helper
2 | // Usage: `{{meta_title}}`
3 | //
4 | // Page title used for sharing and SEO
5 | var proxy = require('./proxy'),
6 | getMetaDataTitle = proxy.metaData.getMetaDataTitle;
7 |
8 | // We use the name meta_title to match the helper for consistency:
9 | module.exports = function meta_title(options) { // eslint-disable-line camelcase
10 | return getMetaDataTitle(this, options.data.root, options);
11 | };
12 |
--------------------------------------------------------------------------------
/core/server/web/shared/middlewares/update-user-last-seen.js:
--------------------------------------------------------------------------------
1 | const constants = require('../../../lib/constants');
2 |
3 | module.exports = function updateUserLastSeenMiddleware(req, res, next) {
4 | if (!req.user) {
5 | return next();
6 | }
7 |
8 | if (Date.now() - req.user.get('last_seen') < constants.ONE_HOUR_MS) {
9 | return next();
10 | }
11 |
12 | req.user.updateLastSeen().then(() => {
13 | next();
14 | }, next);
15 | };
16 |
--------------------------------------------------------------------------------
/core/server/web/shared/middlewares/emit-events.js:
--------------------------------------------------------------------------------
1 | const common = require('../../../lib/common');
2 | const INVALIDATE_ALL = '/*';
3 |
4 | module.exports = function emitEvents(req, res, next) {
5 | res.on('finish', function triggerEvents() {
6 | if (res.get('X-Cache-Invalidate') === INVALIDATE_ALL) {
7 | common.events.emit('site.changed');
8 | }
9 |
10 | res.removeListener('finish', triggerEvents);
11 | });
12 | next();
13 | };
14 |
--------------------------------------------------------------------------------
/core/server/services/webhooks/payload.js:
--------------------------------------------------------------------------------
1 | const serialize = require('./serialize');
2 | const Promise = require('bluebird');
3 |
4 | module.exports = (event, model) => {
5 | const payload = {};
6 |
7 | if (model) {
8 | return serialize(event, model)
9 | .then((result) => {
10 | Object.assign(payload, result);
11 |
12 | return payload;
13 | });
14 | }
15 |
16 | return Promise.resolve(payload);
17 | };
18 |
--------------------------------------------------------------------------------
/core/server/services/themes/config/index.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash'),
2 | defaultConfig = require('./defaults'),
3 | allowedKeys = ['posts_per_page', 'image_sizes'];
4 |
5 | module.exports.create = function configLoader(packageJson) {
6 | var config = _.cloneDeep(defaultConfig);
7 |
8 | if (packageJson && packageJson.hasOwnProperty('config')) {
9 | config = _.assign(config, _.pick(packageJson.config, allowedKeys));
10 | }
11 |
12 | return config;
13 | };
14 |
--------------------------------------------------------------------------------
/core/server/api/v2/roles.js:
--------------------------------------------------------------------------------
1 | const models = require('../../models');
2 |
3 | module.exports = {
4 | docName: 'roles',
5 | browse: {
6 | options: [
7 | 'permissions'
8 | ],
9 | validation: {
10 | options: {
11 | permissions: ['assign']
12 | }
13 | },
14 | permissions: true,
15 | query(frame) {
16 | return models.Role.findAll(frame.options);
17 | }
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/images.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:images');
2 | const mapper = require('./utils/mapper');
3 |
4 | module.exports = {
5 | upload(path, apiConfig, frame) {
6 | debug('upload');
7 |
8 | return frame.response = {
9 | images: [{
10 | url: mapper.mapImage(path),
11 | ref: frame.data.ref || null
12 | }]
13 | };
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/core/server/data/meta/amp_url.js:
--------------------------------------------------------------------------------
1 | var urlService = require('../../services/url'),
2 | getUrl = require('./url'),
3 | _ = require('lodash');
4 |
5 | function getAmplUrl(data) {
6 | var context = data.context ? data.context : null;
7 |
8 | if (_.includes(context, 'post') && !_.includes(context, 'amp')) {
9 | return urlService.utils.urlJoin(urlService.utils.urlFor('home', true), getUrl(data, false), 'amp/');
10 | }
11 | return null;
12 | }
13 |
14 | module.exports = getAmplUrl;
15 |
--------------------------------------------------------------------------------
/core/server/data/xml/sitemap/utils.js:
--------------------------------------------------------------------------------
1 | var urlService = require('../../../services/url'),
2 | sitemapsUtils;
3 |
4 | sitemapsUtils = {
5 | getDeclarations: function () {
6 | var baseUrl = urlService.utils.urlFor('sitemap_xsl', true);
7 | baseUrl = baseUrl.replace(/^(http:|https:)/, '');
8 | return '' +
9 | '';
10 | }
11 | };
12 |
13 | module.exports = sitemapsUtils;
14 |
--------------------------------------------------------------------------------
/core/server/lib/image/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get blogIcon() {
3 | return require('./blog-icon');
4 | },
5 |
6 | get imageSize() {
7 | return require('./image-size');
8 | },
9 |
10 | get gravatar() {
11 | return require('./gravatar');
12 | },
13 |
14 | get imageSizeCache() {
15 | return require('./cached-image-size-from-url');
16 | },
17 |
18 | get manipulator() {
19 | return require('./manipulator');
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/core/server/services/routing/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get bootstrap() {
3 | return require('./bootstrap');
4 | },
5 |
6 | get registry() {
7 | return require('./registry');
8 | },
9 |
10 | get helpers() {
11 | return require('./helpers');
12 | },
13 |
14 | get CollectionRouter() {
15 | return require('./CollectionRouter');
16 | },
17 |
18 | get TaxonomyRouter() {
19 | return require('./TaxonomyRouter');
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/assets/images/icon-right-arrow.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/server/models/author.js:
--------------------------------------------------------------------------------
1 | const ghostBookshelf = require('./base');
2 | const user = require('./user');
3 |
4 | const Author = user.User.extend({
5 | shouldHavePosts: {
6 | joinTo: 'author_id',
7 | joinTable: 'posts_authors'
8 | }
9 | });
10 |
11 | const Authors = ghostBookshelf.Collection.extend({
12 | model: Author
13 | });
14 |
15 | module.exports = {
16 | Author: ghostBookshelf.model('Author', Author),
17 | Authors: ghostBookshelf.collection('Authors', Authors)
18 | };
19 |
--------------------------------------------------------------------------------
/core/server/api/v2/settings-public.js:
--------------------------------------------------------------------------------
1 | const settingsCache = require('../../services/settings/cache');
2 |
3 | module.exports = {
4 | docName: 'settings',
5 |
6 | browse: {
7 | permissions: true,
8 | query() {
9 | // @TODO: decouple settings cache from API knowledge
10 | // The controller fetches models (or cached models) and the API frame for the target API version formats the response.
11 | return settingsCache.getPublic();
12 | }
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/actions.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:actions');
2 | const mapper = require('./utils/mapper');
3 |
4 | module.exports = {
5 | browse(models, apiConfig, frame) {
6 | debug('browse');
7 |
8 | frame.response = {
9 | actions: models.data.map(model => mapper.mapAction(model, frame)),
10 | meta: models.meta
11 | };
12 |
13 | debug(frame.response);
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/MigratorConfig.js:
--------------------------------------------------------------------------------
1 | var config = require('./core/server/config'),
2 | ghostVersion = require('./core/server/lib/ghost-version');
3 |
4 | /**
5 | * knex-migrator can be used via CLI or within the application
6 | * when using the CLI, we need to ensure that our global overrides are triggered
7 | */
8 | require('./core/server/overrides');
9 |
10 | module.exports = {
11 | currentVersion: ghostVersion.safe,
12 | database: config.get('database'),
13 | migrationPath: config.get('paths:migrationPath')
14 | };
15 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/input/integrations.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:input:integrations');
3 |
4 | module.exports = {
5 | add(apiConfig, frame) {
6 | debug('add');
7 |
8 | frame.data = _.pick(frame.data.integrations[0], apiConfig.data);
9 | },
10 | edit(apiConfig, frame) {
11 | debug('edit');
12 |
13 | frame.data = _.pick(frame.data.integrations[0], apiConfig.data);
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/webhooks.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:webhooks');
2 |
3 | module.exports = {
4 | all(models, apiConfig, frame) {
5 | debug('all');
6 | // CASE: e.g. destroy returns null
7 | if (!models) {
8 | return;
9 | }
10 |
11 | frame.response = {
12 | webhooks: [models.toJSON(frame.options)]
13 | };
14 |
15 | debug(frame.response);
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/core/server/lib/mobiledoc/cards/html.js:
--------------------------------------------------------------------------------
1 | const createCard = require('../create-card');
2 |
3 | module.exports = createCard({
4 | name: 'html',
5 | type: 'dom',
6 | render(opts) {
7 | if (!opts.payload.html) {
8 | return '';
9 | }
10 |
11 | // use the SimpleDOM document to create a raw HTML section.
12 | // avoids parsing/rendering of potentially broken or unsupported HTML
13 | return opts.env.dom.createRawHTMLSection(opts.payload.html);
14 | }
15 | });
16 |
--------------------------------------------------------------------------------
/core/server/data/meta/modified_date.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 |
3 | function getModifiedDate(data) {
4 | var context = data.context ? data.context : null,
5 | modDate;
6 |
7 | context = _.includes(context, 'amp') ? 'post' : context;
8 |
9 | if (data[context]) {
10 | modDate = data[context].updated_at || null;
11 | if (modDate) {
12 | return new Date(modDate).toISOString();
13 | }
14 | }
15 | return null;
16 | }
17 |
18 | module.exports = getModifiedDate;
19 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/components/FormHeader.js:
--------------------------------------------------------------------------------
1 | import { IconError } from './icons';
2 |
3 | export default ({title, error, errorText, children}) => (
4 |
5 |
6 | { title ? (
{title}
) : "" }
7 | { children }
8 |
9 | {(error ?
10 |
{ IconError }
11 | {errorText}
12 |
: "")
13 | }
14 |
15 | );
16 |
--------------------------------------------------------------------------------
/core/server/services/routing/helpers/render-entries.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('services:routing:helpers:render-entries'),
2 | formatResponse = require('./format-response'),
3 | renderer = require('./renderer');
4 |
5 | module.exports = function renderEntries(req, res) {
6 | debug('renderEntries called');
7 | return function renderEntries(result) {
8 | // Format data 2
9 | // Render
10 | return renderer(req, res, formatResponse.entries(result));
11 | };
12 | };
13 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Reporting Security Vulnerabilities
2 |
3 | Potential security vulnerabilities can be reported directly us at `security@ghost.org`. The Ghost Security Team communicates privately and works in a secured, isolated repository for tracking, testing, and resolving security-related issues.
4 |
5 | The full, up-to-date details of our security policy and procedure can always be found in our documentation:
6 |
7 | https://docs.ghost.org/security/
8 |
9 | Please refer to this before emailing us. Thanks for helping make Ghost safe for everyone 🙏.
--------------------------------------------------------------------------------
/core/server/models/tag-public.js:
--------------------------------------------------------------------------------
1 | const ghostBookshelf = require('./base');
2 | const tag = require('./tag');
3 |
4 | const TagPublic = tag.Tag.extend({
5 | shouldHavePosts: {
6 | joinTo: 'tag_id',
7 | joinTable: 'posts_tags'
8 | }
9 | });
10 |
11 | const TagsPublic = ghostBookshelf.Collection.extend({
12 | model: TagPublic
13 | });
14 |
15 | module.exports = {
16 | TagPublic: ghostBookshelf.model('TagPublic', TagPublic),
17 | TagsPublic: ghostBookshelf.collection('TagsPublic', TagsPublic)
18 | };
19 |
--------------------------------------------------------------------------------
/core/server/lib/common/logging.js:
--------------------------------------------------------------------------------
1 | const config = require('../../config'),
2 | {logging} = require('ghost-ignition');
3 |
4 | module.exports = logging({
5 | env: config.get('env'),
6 | path: config.get('logging:path') || config.getContentPath('logs'),
7 | domain: config.get('url'),
8 | mode: config.get('logging:mode'),
9 | level: config.get('logging:level'),
10 | transports: config.get('logging:transports'),
11 | loggly: config.get('logging:loggly'),
12 | rotation: config.get('logging:rotation')
13 | });
14 |
--------------------------------------------------------------------------------
/core/server/models/refreshtoken.js:
--------------------------------------------------------------------------------
1 | var ghostBookshelf = require('./base'),
2 | Basetoken = require('./base/token'),
3 |
4 | Refreshtoken,
5 | Refreshtokens;
6 |
7 | Refreshtoken = Basetoken.extend({
8 | tableName: 'refreshtokens'
9 | });
10 |
11 | Refreshtokens = ghostBookshelf.Collection.extend({
12 | model: Refreshtoken
13 | });
14 |
15 | module.exports = {
16 | Refreshtoken: ghostBookshelf.model('Refreshtoken', Refreshtoken),
17 | Refreshtokens: ghostBookshelf.collection('Refreshtokens', Refreshtokens)
18 | };
19 |
--------------------------------------------------------------------------------
/core/server/api/shared/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get headers() {
3 | return require('./headers');
4 | },
5 |
6 | get http() {
7 | return require('./http');
8 | },
9 |
10 | get Frame() {
11 | return require('./frame');
12 | },
13 |
14 | get pipeline() {
15 | return require('./pipeline');
16 | },
17 |
18 | get validators() {
19 | return require('./validators');
20 | },
21 |
22 | get serializers() {
23 | return require('./serializers');
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/core/server/apps/amp/lib/helpers/index.js:
--------------------------------------------------------------------------------
1 | // Dirty require!
2 | const ghostHead = require('../../../../helpers/ghost_head');
3 |
4 | function registerAmpHelpers(ghost) {
5 | ghost.helpers.registerAsync('amp_content', require('./amp_content'));
6 |
7 | ghost.helpers.register('amp_components', require('./amp_components'));
8 |
9 | // we use the {{ghost_head}} helper, but call it {{amp_ghost_head}}, so it's consistent
10 | ghost.helpers.registerAsync('amp_ghost_head', ghostHead);
11 | }
12 |
13 | module.exports = registerAmpHelpers;
14 |
--------------------------------------------------------------------------------
/core/test/unit/helpers/encode_spec.js:
--------------------------------------------------------------------------------
1 | var should = require('should'),
2 |
3 | // Stuff we are testing
4 | helpers = require('../../../server/helpers');
5 |
6 | describe('{{encode}} helper', function () {
7 | it('can escape URI', function () {
8 | var uri = '$pecial!Charact3r(De[iver]y)Foo #Bar',
9 | expected = '%24pecial!Charact3r(De%5Biver%5Dy)Foo%20%23Bar',
10 | escaped = helpers.encode(uri);
11 |
12 | should.exist(escaped);
13 | String(escaped).should.equal(expected);
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/core/server/helpers/meta_description.js:
--------------------------------------------------------------------------------
1 | // # Meta Description Helper
2 | // Usage: `{{meta_description}}`
3 | //
4 | // Page description used for sharing and SEO
5 | var proxy = require('./proxy'),
6 | getMetaDataDescription = proxy.metaData.getMetaDataDescription;
7 |
8 | // We use the name meta_description to match the helper for consistency:
9 | module.exports = function meta_description(options) { // eslint-disable-line camelcase
10 | options = options || {};
11 |
12 | return getMetaDataDescription(this, options.data.root) || '';
13 | };
14 |
--------------------------------------------------------------------------------
/core/server/services/routing/controllers/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get entry() {
3 | return require('./entry');
4 | },
5 |
6 | get collection() {
7 | return require('./collection');
8 | },
9 |
10 | get rss() {
11 | return require('./rss');
12 | },
13 |
14 | get preview() {
15 | return require('./preview');
16 | },
17 |
18 | get channel() {
19 | return require('./channel');
20 | },
21 |
22 | get static() {
23 | return require('./static');
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/core/server/config/env/config.production.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "client": "mysql",
4 | "connection": {
5 | "host" : "127.0.0.1",
6 | "user" : "root",
7 | "password" : "",
8 | "database" : "ghost"
9 | }
10 | },
11 | "paths": {
12 | "contentPath": "content/"
13 | },
14 | "logging": {
15 | "level": "info",
16 | "rotation": {
17 | "enabled": true
18 | },
19 | "transports": ["file", "stdout"]
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/server/models/app-field.js:
--------------------------------------------------------------------------------
1 | var ghostBookshelf = require('./base'),
2 | AppField,
3 | AppFields;
4 |
5 | AppField = ghostBookshelf.Model.extend({
6 | tableName: 'app_fields',
7 |
8 | post: function post() {
9 | return this.morphOne('Post', 'relatable');
10 | }
11 | });
12 |
13 | AppFields = ghostBookshelf.Collection.extend({
14 | model: AppField
15 | });
16 |
17 | module.exports = {
18 | AppField: ghostBookshelf.model('AppField', AppField),
19 | AppFields: ghostBookshelf.collection('AppFields', AppFields)
20 | };
21 |
--------------------------------------------------------------------------------
/core/server/services/rss/renderer.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash'),
2 | rssCache = require('./cache');
3 |
4 | module.exports.render = function render(res, baseUrl, data) {
5 | // Format data - this is the same as what Express does
6 | var rssData = _.merge({}, res.locals, data);
7 |
8 | // Fetch RSS from the cache
9 | return rssCache
10 | .getXML(baseUrl, rssData)
11 | .then(function then(feedXml) {
12 | res.set('Content-Type', 'text/xml; charset=UTF-8');
13 | res.send(feedXml);
14 | });
15 | };
16 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/components/NameInput.js:
--------------------------------------------------------------------------------
1 | import FormInput from './FormInput';
2 | import { IconName } from './icons';
3 |
4 | export default ({value, error, children, onInput, className}) => (
5 |
16 | {children}
17 |
18 | );
19 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/schemas/tags-edit.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "$schema": "http://json-schema.org/draft-07/schema#",
4 | "$id": "tags.edit",
5 | "title": "tags.edit",
6 | "description": "Schema for tags.edit",
7 | "type": "object",
8 | "additionalProperties": false,
9 | "properties": {
10 | "tags": {
11 | "type": "array",
12 | "minItems": 1,
13 | "maxItems": 1,
14 | "items": {"$ref": "tags#/definitions/tag"}
15 | }
16 | },
17 | "required": [ "tags" ]
18 | }
19 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/components/EmailInput.js:
--------------------------------------------------------------------------------
1 | import FormInput from './FormInput';
2 | import { IconEmail } from './icons';
3 |
4 | export default ({value, error, children, onInput, className}) => (
5 |
16 | {children}
17 |
18 | );
19 |
--------------------------------------------------------------------------------
/core/server/models/app-setting.js:
--------------------------------------------------------------------------------
1 | var ghostBookshelf = require('./base'),
2 | AppSetting,
3 | AppSettings;
4 |
5 | AppSetting = ghostBookshelf.Model.extend({
6 | tableName: 'app_settings',
7 |
8 | app: function app() {
9 | return this.belongsTo('App');
10 | }
11 | });
12 |
13 | AppSettings = ghostBookshelf.Collection.extend({
14 | model: AppSetting
15 | });
16 |
17 | module.exports = {
18 | AppSetting: ghostBookshelf.model('AppSetting', AppSetting),
19 | AppSettings: ghostBookshelf.collection('AppSettings', AppSettings)
20 | };
21 |
--------------------------------------------------------------------------------
/core/server/web/admin/middleware.js:
--------------------------------------------------------------------------------
1 | const urlService = require('../../services/url');
2 |
3 | function redirectAdminUrls(req, res, next) {
4 | const subdir = urlService.utils.getSubdir(),
5 | ghostPathRegex = new RegExp(`^${subdir}/ghost/(.+)`),
6 | ghostPathMatch = req.originalUrl.match(ghostPathRegex);
7 |
8 | if (ghostPathMatch) {
9 | return res.redirect(urlService.utils.urlJoin(urlService.utils.urlFor('admin'), '#', ghostPathMatch[1]));
10 | }
11 |
12 | next();
13 | }
14 |
15 | module.exports = [
16 | redirectAdminUrls
17 | ];
18 |
--------------------------------------------------------------------------------
/core/server/api/v2/images.js:
--------------------------------------------------------------------------------
1 | const storage = require('../../adapters/storage');
2 |
3 | module.exports = {
4 | docName: 'images',
5 | upload: {
6 | statusCode: 201,
7 | permissions: false,
8 | query(frame) {
9 | const store = storage.getStorage();
10 |
11 | if (frame.files) {
12 | return Promise
13 | .map(frame.files, file => store.save(file))
14 | .then(paths => paths[0]);
15 | }
16 | return store.save(frame.file);
17 | }
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/core/server/data/xml/sitemap/user-generator.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash'),
2 | validator = require('validator'),
3 | BaseMapGenerator = require('./base-generator');
4 |
5 | class UserMapGenerator extends BaseMapGenerator {
6 | constructor(opts) {
7 | super();
8 |
9 | this.name = 'authors';
10 | _.extend(this, opts);
11 | }
12 |
13 | validateImageUrl(imageUrl) {
14 | return imageUrl && validator.isURL(imageUrl, {protocols: ['http', 'https'], require_protocol: true});
15 | }
16 | }
17 |
18 | module.exports = UserMapGenerator;
19 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/components/PasswordInput.js:
--------------------------------------------------------------------------------
1 | import FormInput from './FormInput';
2 | import { IconLock } from './icons';
3 |
4 | export default ({value, error, children, onInput, className}) => (
5 |
16 | { children }
17 |
18 | );
19 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/app/good.js:
--------------------------------------------------------------------------------
1 | var path = require('path'),
2 | util = require('./goodlib.js'),
3 | nested = require('./nested/goodnested');
4 |
5 | function GoodApp(app) {
6 | this.app = app;
7 | }
8 |
9 | GoodApp.prototype.install = function () {
10 | // Goes through app to do data
11 | this.app.something = 42;
12 | this.app.util = util;
13 | this.app.nested = nested;
14 | this.app.path = path.join(__dirname, 'good.js');
15 |
16 | return true;
17 | };
18 |
19 | GoodApp.prototype.activate = function () {
20 | };
21 |
22 | module.exports = GoodApp;
23 |
--------------------------------------------------------------------------------
/core/server/helpers/page_url.js:
--------------------------------------------------------------------------------
1 | // ### Page URL Helper
2 | //
3 | // *Usage example:*
4 | // `{{page_url 2}}`
5 | //
6 | // Returns the URL for the page specified in the current object context.
7 | var proxy = require('./proxy'),
8 | getPaginatedUrl = proxy.metaData.getPaginatedUrl;
9 |
10 | // We use the name page_url to match the helper for consistency:
11 | module.exports = function page_url(page, options) { // eslint-disable-line camelcase
12 | if (!options) {
13 | options = page;
14 | page = 1;
15 | }
16 | return getPaginatedUrl(page, options.data.root);
17 | };
18 |
--------------------------------------------------------------------------------
/core/server/api/v2/site.js:
--------------------------------------------------------------------------------
1 | const ghostVersion = require('../../lib/ghost-version');
2 | const settingsCache = require('../../services/settings/cache');
3 | const urlService = require('../../services/url');
4 |
5 | const site = {
6 | docName: 'site',
7 |
8 | read: {
9 | permissions: false,
10 | query() {
11 | return {
12 | title: settingsCache.get('title'),
13 | url: urlService.utils.urlFor('home', true),
14 | version: ghostVersion.safe
15 | };
16 | }
17 | }
18 | };
19 |
20 | module.exports = site;
21 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/tags.js:
--------------------------------------------------------------------------------
1 | const jsonSchema = require('../utils/json-schema');
2 |
3 | module.exports = {
4 | add(apiConfig, frame) {
5 | const schema = require('./schemas/tags-add');
6 | const definitions = require('./schemas/tags');
7 | return jsonSchema.validate(schema, definitions, frame.data);
8 | },
9 |
10 | edit(apiConfig, frame) {
11 | const schema = require('./schemas/tags-edit');
12 | const definitions = require('./schemas/tags');
13 | return jsonSchema.validate(schema, definitions, frame.data);
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/core/server/models/client-trusted-domain.js:
--------------------------------------------------------------------------------
1 | var ghostBookshelf = require('./base'),
2 |
3 | ClientTrustedDomain,
4 | ClientTrustedDomains;
5 |
6 | ClientTrustedDomain = ghostBookshelf.Model.extend({
7 | tableName: 'client_trusted_domains'
8 | });
9 |
10 | ClientTrustedDomains = ghostBookshelf.Collection.extend({
11 | model: ClientTrustedDomain
12 | });
13 |
14 | module.exports = {
15 | ClientTrustedDomain: ghostBookshelf.model('ClientTrustedDomain', ClientTrustedDomain),
16 | ClientTrustedDomains: ghostBookshelf.collection('ClientTrustedDomains', ClientTrustedDomains)
17 | };
18 |
--------------------------------------------------------------------------------
/core/server/services/rss/cache.js:
--------------------------------------------------------------------------------
1 | var crypto = require('crypto'),
2 | generateFeed = require('./generate-feed'),
3 | feedCache = {};
4 |
5 | module.exports.getXML = function getFeedXml(baseUrl, data) {
6 | var dataHash = crypto.createHash('md5').update(JSON.stringify(data)).digest('hex');
7 | if (!feedCache[baseUrl] || feedCache[baseUrl].hash !== dataHash) {
8 | // We need to regenerate
9 | feedCache[baseUrl] = {
10 | hash: dataHash,
11 | xml: generateFeed(baseUrl, data)
12 | };
13 | }
14 |
15 | return feedCache[baseUrl].xml;
16 | };
17 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/pages.js:
--------------------------------------------------------------------------------
1 | const jsonSchema = require('../utils/json-schema');
2 |
3 | module.exports = {
4 | add(apiConfig, frame) {
5 | const schema = require(`./schemas/pages-add`);
6 | const definitions = require('./schemas/pages');
7 | return jsonSchema.validate(schema, definitions, frame.data);
8 | },
9 |
10 | edit(apiConfig, frame) {
11 | const schema = require(`./schemas/pages-edit`);
12 | const definitions = require('./schemas/pages');
13 | return jsonSchema.validate(schema, definitions, frame.data);
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/posts.js:
--------------------------------------------------------------------------------
1 | const jsonSchema = require('../utils/json-schema');
2 |
3 | module.exports = {
4 | add(apiConfig, frame) {
5 | const schema = require(`./schemas/posts-add`);
6 | const definitions = require('./schemas/posts');
7 | return jsonSchema.validate(schema, definitions, frame.data);
8 | },
9 |
10 | edit(apiConfig, frame) {
11 | const schema = require(`./schemas/posts-edit`);
12 | const definitions = require('./schemas/posts');
13 | return jsonSchema.validate(schema, definitions, frame.data);
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/assets/images/icon-email.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/server/services/auth/passport.js:
--------------------------------------------------------------------------------
1 | var ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy,
2 | BearerStrategy = require('passport-http-bearer').Strategy,
3 | passport = require('passport'),
4 | authStrategies = require('./auth-strategies');
5 |
6 | /**
7 | * auth types:
8 | * - password: local login
9 | */
10 | exports.init = function initPassport() {
11 | passport.use(new ClientPasswordStrategy(authStrategies.clientPasswordStrategy));
12 | passport.use(new BearerStrategy(authStrategies.bearerStrategy));
13 |
14 | return passport.initialize();
15 | };
16 |
--------------------------------------------------------------------------------
/core/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "strict": false,
5 | "sub": true,
6 | "eqeqeq": true,
7 | "laxbreak": true,
8 | "bitwise": true,
9 | "curly": true,
10 | "forin": true,
11 | "immed": true,
12 | "latedef": true,
13 | "newcap": true,
14 | "noarg": true,
15 | "noempty": true,
16 | "nonew": true,
17 | "plusplus": true,
18 | "regexp": true,
19 | "undef": true,
20 | "unused": true,
21 | "indent": 4,
22 | "quotmark": "single",
23 | "predef": [ "-Promise" ]
24 | }
25 |
--------------------------------------------------------------------------------
/core/test/unit/helpers/template_spec.js:
--------------------------------------------------------------------------------
1 | var should = require('should'),
2 | hbs = require.main.require('core/server/services/themes/engine'),
3 | template = require.main.require('core/server/helpers/template');
4 |
5 | describe('Helpers Template', function () {
6 | it('can execute a template', function () {
7 | hbs.registerPartial('test', 'Hello {{name}}
');
8 |
9 | var safeString = template.execute('test', {name: 'world'});
10 |
11 | should.exist(safeString);
12 | safeString.should.have.property('string').and.equal('Hello world
');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/core/server/lib/common/events.js:
--------------------------------------------------------------------------------
1 | const events = require('events'),
2 | util = require('util');
3 | let EventRegistry,
4 | EventRegistryInstance;
5 |
6 | EventRegistry = function () {
7 | events.EventEmitter.call(this);
8 | };
9 |
10 | util.inherits(EventRegistry, events.EventEmitter);
11 |
12 | EventRegistry.prototype.onMany = function (arr, onEvent) {
13 | arr.forEach((eventName) => {
14 | this.on(eventName, onEvent);
15 | });
16 | };
17 |
18 | EventRegistryInstance = new EventRegistry();
19 | EventRegistryInstance.setMaxListeners(100);
20 |
21 | module.exports = EventRegistryInstance;
22 |
--------------------------------------------------------------------------------
/core/test/unit/lib/mobiledoc/atoms/soft-return_spec.js:
--------------------------------------------------------------------------------
1 | const should = require('should');
2 | const atom = require('../../../../../server/lib/mobiledoc/atoms/soft-return');
3 | const SimpleDom = require('simple-dom');
4 | const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap);
5 |
6 | describe('Soft return atom', function () {
7 | it('generates a `br` tag', function () {
8 | let opts = {
9 | env: {
10 | dom: new SimpleDom.Document()
11 | }
12 | };
13 |
14 | serializer.serialize(atom.render(opts)).should.match('
');
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/core/server/helpers/asset.js:
--------------------------------------------------------------------------------
1 | // # Asset helper
2 | // Usage: `{{asset "css/screen.css"}}`, `{{asset "css/screen.css" ghost="true"}}`
3 | //
4 | // Returns the path to the specified asset. The ghost flag outputs the asset path for the Ghost admin
5 | const proxy = require('./proxy'),
6 | get = require('lodash/get'),
7 | getAssetUrl = proxy.metaData.getAssetUrl,
8 | SafeString = proxy.SafeString;
9 |
10 | module.exports = function asset(path, options) {
11 | const hasMinFile = get(options, 'hash.hasMinFile');
12 |
13 | return new SafeString(
14 | getAssetUrl(path, hasMinFile)
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/core/server/web/admin/serviceworker.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('web:admin:serviceworker');
2 | const path = require('path');
3 |
4 | // Route: index
5 | // Path: /ghost/sw.js|sw-registration.js
6 | // Method: GET
7 | module.exports = function adminController(req, res) {
8 | debug('serviceworker called');
9 |
10 | const sw = path.join(__dirname, '..', '..', '..', 'built', 'assets', 'sw.js'),
11 | swr = path.join(__dirname, '..', '..', '..', 'built', 'assets', 'sw-registration.js'),
12 | fileToSend = req.url === '/sw.js' ? sw : swr;
13 |
14 | res.sendFile(fileToSend);
15 | };
16 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get posts() {
3 | return require('./posts');
4 | },
5 |
6 | get pages() {
7 | return require('./pages');
8 | },
9 |
10 | get invites() {
11 | return require('./invites');
12 | },
13 |
14 | get settings() {
15 | return require('./settings');
16 | },
17 |
18 | get tags() {
19 | return require('./tags');
20 | },
21 |
22 | get users() {
23 | return require('./users');
24 | },
25 |
26 | get images() {
27 | return require('./images');
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/input/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get db() {
3 | return require('./db');
4 | },
5 |
6 | get integrations() {
7 | return require('./integrations');
8 | },
9 |
10 | get pages() {
11 | return require('./pages');
12 | },
13 |
14 | get posts() {
15 | return require('./posts');
16 | },
17 |
18 | get settings() {
19 | return require('./settings');
20 | },
21 |
22 | get users() {
23 | return require('./users');
24 | },
25 |
26 | get tags() {
27 | return require('./tags');
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/core/server/helpers/lang.js:
--------------------------------------------------------------------------------
1 | // # lang helper
2 | // {{lang}} gives the current language tag
3 | // Usage example:
4 | //
5 | // Examples of language tags from RFC 5646:
6 | // de (German)
7 | // fr (French)
8 | // ja (Japanese)
9 | // en-US (English as used in the United States)
10 | //
11 | // Standard:
12 | // Language tags in HTML and XML
13 | // https://www.w3.org/International/articles/language-tags/
14 |
15 | var proxy = require('./proxy'),
16 | i18n = proxy.i18n,
17 | SafeString = proxy.SafeString;
18 |
19 | module.exports = function lang() {
20 | return new SafeString(i18n.locale());
21 | };
22 |
--------------------------------------------------------------------------------
/core/test/unit/lib/mobiledoc/cards/hr_spec.js:
--------------------------------------------------------------------------------
1 | const should = require('should');
2 | const card = require('../../../../../server/lib/mobiledoc/cards/hr');
3 | const SimpleDom = require('simple-dom');
4 | const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap);
5 |
6 | describe('HR card', function () {
7 | it('generates a horizontal rule', function () {
8 | let opts = {
9 | env: {
10 | dom: new SimpleDom.Document()
11 | }
12 | };
13 |
14 | serializer.serialize(card.render(opts)).should.match('
');
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/core/server/api/v0.1/slack.js:
--------------------------------------------------------------------------------
1 | // # Slack API
2 | // API for sending Test Notifications to Slack
3 | const Promise = require('bluebird'),
4 | common = require('../../lib/common');
5 |
6 | let slack;
7 |
8 | /**
9 | * ## Slack API Method
10 | *
11 | * **See:** [API Methods](constants.js.html#api%20methods)
12 | * @typedef Slack
13 | * @param slack
14 | */
15 | slack = {
16 | /**
17 | * ### SendTest
18 | * Send a test notification
19 | *
20 | * @public
21 | */
22 | sendTest() {
23 | common.events.emit('slack.test');
24 | return Promise.resolve();
25 | }
26 | };
27 |
28 | module.exports = slack;
29 |
--------------------------------------------------------------------------------
/core/server/config/env/config.development.json:
--------------------------------------------------------------------------------
1 | {
2 | "url": "http://localhost:2368",
3 | "database": {
4 | "client": "sqlite3",
5 | "connection": {
6 | "filename": "content/data/ghost-dev.db"
7 | },
8 | "debug": false
9 | },
10 | "paths": {
11 | "contentPath": "content/"
12 | },
13 | "privacy": {
14 | "useRpcPing": false,
15 | "useUpdateCheck": true
16 | },
17 | "useMinFiles": false,
18 | "caching": {
19 | "theme": {
20 | "maxAge": 0
21 | },
22 | "admin": {
23 | "maxAge": 0
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/core/server/services/auth/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get authorize() {
3 | return require('./authorize');
4 | },
5 |
6 | get authenticate() {
7 | return require('./authenticate');
8 | },
9 |
10 | get session() {
11 | return require('./session');
12 | },
13 | /*
14 | * TODO: Get rid of these when v0.1 is gone
15 | */
16 | get init() {
17 | return (options) => {
18 | require('./oauth').init(options);
19 | return require('./passport').init(options);
20 | };
21 | },
22 | get oauth() {
23 | return require('./oauth');
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper-1.4/README.md:
--------------------------------------------------------------------------------
1 | # Casper
2 |
3 | The default theme for [Ghost](http://github.com/tryghost/ghost/). Casper 1.4 is the last release of Casper's original design, created to be compatible **ONLY with Ghost 1.0** and above. If you are running an earlier version of Ghost, you will need [Casper 1.3.7](https://github.com/TryGhost/Casper/releases/tag/1.3.7). For all other versions of Ghost, we recommend switching to the [latest release](https://github.com/TryGhost/Casper/releases) of Casper, with an updated design.
4 |
5 | ## Copyright & License
6 |
7 | Copyright (c) 2013-2019 Ghost Foundation - Released under the [MIT license](LICENSE).
8 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | get permissions() {
3 | return require('./permissions');
4 | },
5 |
6 | get serializers() {
7 | return require('./serializers');
8 | },
9 |
10 | get validators() {
11 | return require('./validators');
12 | },
13 |
14 | isContentAPI: (frame) => {
15 | return frame.apiType === 'content';
16 | },
17 |
18 | isAdminAPIKey: (frame) => {
19 | return frame.options.context && Object.keys(frame.options.context).length !== 0 && frame.options.context.api_key &&
20 | frame.options.context.api_key.type === 'admin';
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/input/settings.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 |
3 | module.exports = {
4 | edit(apiConfig, frame) {
5 | // CASE: allow shorthand syntax where a single key and value are passed to edit instead of object and options
6 | if (_.isString(frame.data)) {
7 | frame.data = {settings: [{key: frame.data, value: frame.options}]};
8 | }
9 |
10 | // prepare data
11 | frame.data.settings.forEach((setting) => {
12 | if (!_.isString(setting.value)) {
13 | setting.value = JSON.stringify(setting.value);
14 | }
15 | });
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/core/server/services/members/index.js:
--------------------------------------------------------------------------------
1 | const config = require('../../config/index.js');
2 | const common = require('../../lib/common');
3 |
4 | module.exports = {
5 | get api() {
6 | if (!config.get('enableDeveloperExperiments')) {
7 | return {
8 | apiRouter: function (req, res, next) {
9 | return next(new common.errors.NotFoundError());
10 | },
11 | staticRouter: function (req, res, next) {
12 | return next(new common.errors.NotFoundError());
13 | }
14 | };
15 | }
16 | return require('./api');
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/users.js:
--------------------------------------------------------------------------------
1 | const Promise = require('bluebird');
2 | const debug = require('ghost-ignition').debug('api:v2:utils:validators:input:users');
3 | const common = require('../../../../../lib/common');
4 |
5 | module.exports = {
6 | changePassword(apiConfig, frame) {
7 | debug('changePassword');
8 |
9 | const data = frame.data.password[0];
10 |
11 | if (data.newPassword !== data.ne2Password) {
12 | return Promise.reject(new common.errors.ValidationError({
13 | message: common.i18n.t('errors.models.user.newPasswordsDoNotMatch')
14 | }));
15 | }
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/core/test/acceptance/README.md:
--------------------------------------------------------------------------------
1 | ## Acceptance Tests
2 |
3 | This folder should only contain a set of basic API use cases.
4 |
5 | We are currently refactoring the test env. The "old" folder currently contains all API tests for the
6 | stable API version (v2). The goal is:
7 |
8 | - either keep a test if it's a basic use case e.g. upload an image, schedule a post, download a theme
9 | - otherwise move the test to regression api v2 tests
10 |
11 | We probably need a differentiation for the acceptance tests for session and api_key authentication.
12 |
13 | Before we move tests:
14 |
15 | - we have to re-work how are test utility is structured
16 | - we have to reduce tests
17 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/utils/date.js:
--------------------------------------------------------------------------------
1 | const moment = require('moment-timezone');
2 | const settingsCache = require('../../../../../../services/settings/cache');
3 |
4 | const format = (date) => {
5 | return moment(date)
6 | .tz(settingsCache.get('active_timezone'))
7 | .toISOString(true);
8 | };
9 |
10 | const forPost = (attrs) => {
11 | ['created_at', 'updated_at', 'published_at'].forEach((field) => {
12 | if (attrs[field]) {
13 | attrs[field] = format(attrs[field]);
14 | }
15 | });
16 |
17 | return attrs;
18 | };
19 |
20 | module.exports.format = format;
21 | module.exports.forPost = forPost;
22 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/invites.js:
--------------------------------------------------------------------------------
1 | const Promise = require('bluebird');
2 | const common = require('../../../../../lib/common');
3 | const models = require('../../../../../models');
4 |
5 | module.exports = {
6 | add(apiConfig, frame) {
7 | return models.User.findOne({email: frame.data.invites[0].email}, frame.options)
8 | .then((user) => {
9 | if (user) {
10 | return Promise.reject(new common.errors.ValidationError({
11 | message: common.i18n.t('errors.api.users.userAlreadyRegistered')
12 | }));
13 | }
14 | });
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/core/server/web/shared/middlewares/labs.js:
--------------------------------------------------------------------------------
1 | const labsUtil = require('../../../services/labs');
2 | const common = require('../../../lib/common');
3 |
4 | const labs = {
5 | subscribers(req, res, next) {
6 | if (labsUtil.isSet('subscribers') === true) {
7 | return next();
8 | } else {
9 | return next(new common.errors.NotFoundError());
10 | }
11 | },
12 | members(req, res, next) {
13 | if (labsUtil.isSet('members') === true) {
14 | return next();
15 | } else {
16 | return next(new common.errors.NotFoundError());
17 | }
18 | }
19 | };
20 |
21 | module.exports = labs;
22 |
--------------------------------------------------------------------------------
/core/server/helpers/url.js:
--------------------------------------------------------------------------------
1 | // # URL helper
2 | // Usage: `{{url}}`, `{{url absolute="true"}}`
3 | //
4 | // Returns the URL for the current object scope i.e. If inside a post scope will return post permalink
5 | // `absolute` flag outputs absolute URL, else URL is relative
6 |
7 | var proxy = require('./proxy'),
8 | SafeString = proxy.SafeString,
9 | getMetaDataUrl = proxy.metaData.getMetaDataUrl;
10 |
11 | module.exports = function url(options) {
12 | var absolute = options && options.hash.absolute,
13 | outputUrl = getMetaDataUrl(this, absolute);
14 |
15 | outputUrl = encodeURI(decodeURI(outputUrl));
16 |
17 | return new SafeString(outputUrl);
18 | };
19 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/assets/images/icon-lock.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/server/lib/security/url.js:
--------------------------------------------------------------------------------
1 | // The token is encoded URL safe by replacing '+' with '-', '\' with '_' and removing '='
2 | // NOTE: the token is not encoded using valid base64 anymore
3 | module.exports.encodeBase64 = function encodeBase64(base64String) {
4 | return base64String.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
5 | };
6 |
7 | // Decode url safe base64 encoding and add padding ('=')
8 | module.exports.decodeBase64 = function decodeBase64(base64String) {
9 | base64String = base64String.replace(/-/g, '+').replace(/_/g, '/');
10 | while (base64String.length % 4) {
11 | base64String += '=';
12 | }
13 | return base64String;
14 | };
15 |
--------------------------------------------------------------------------------
/core/server/lib/security/password.js:
--------------------------------------------------------------------------------
1 | const Promise = require('bluebird');
2 |
3 | module.exports.hash = function hash(plainPassword) {
4 | const bcrypt = require('bcryptjs'),
5 | bcryptGenSalt = Promise.promisify(bcrypt.genSalt),
6 | bcryptHash = Promise.promisify(bcrypt.hash);
7 |
8 | return bcryptGenSalt().then(function (salt) {
9 | return bcryptHash(plainPassword, salt);
10 | });
11 | };
12 |
13 | module.exports.compare = function compare(plainPassword, hashedPassword) {
14 | const bcrypt = require('bcryptjs'),
15 | bcryptCompare = Promise.promisify(bcrypt.compare);
16 |
17 | return bcryptCompare(plainPassword, hashedPassword);
18 | };
19 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/mail.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:mail');
3 |
4 | module.exports = {
5 | all(response, apiConfig, frame) {
6 | const toReturn = _.cloneDeep(frame.data);
7 |
8 | delete toReturn.mail[0].options;
9 | // Sendmail returns extra details we don't need and that don't convert to JSON
10 | delete toReturn.mail[0].message.transport;
11 |
12 | toReturn.mail[0].status = {
13 | message: response.message
14 | };
15 |
16 | frame.response = toReturn;
17 |
18 | debug(frame.response);
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/core/server/data/meta/author_image.js:
--------------------------------------------------------------------------------
1 | var urlService = require('../../services/url'),
2 | getContextObject = require('./context_object.js'),
3 | _ = require('lodash');
4 |
5 | function getAuthorImage(data, absolute) {
6 | var context = data.context ? data.context : null,
7 | contextObject = getContextObject(data, context);
8 |
9 | if ((_.includes(context, 'post') || _.includes(context, 'page')) && contextObject.primary_author && contextObject.primary_author.profile_image) {
10 | return urlService.utils.urlFor('image', {image: contextObject.primary_author.profile_image}, absolute);
11 | }
12 | return null;
13 | }
14 |
15 | module.exports = getAuthorImage;
16 |
--------------------------------------------------------------------------------
/core/server/data/meta/creator_url.js:
--------------------------------------------------------------------------------
1 | var getContextObject = require('./context_object.js'),
2 | _ = require('lodash');
3 |
4 | function getCreatorTwitterUrl(data) {
5 | var context = data.context ? data.context : null,
6 | contextObject = getContextObject(data, context);
7 |
8 | if ((_.includes(context, 'post') || _.includes(context, 'page')) && contextObject.primary_author && contextObject.primary_author.twitter) {
9 | return contextObject.primary_author.twitter;
10 | } else if (_.includes(context, 'author') && contextObject.twitter) {
11 | return contextObject.twitter;
12 | }
13 | return null;
14 | }
15 |
16 | module.exports = getCreatorTwitterUrl;
17 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/schemas/pages-add.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "$schema": "http://json-schema.org/draft-07/schema#",
4 | "$id": "pages.add",
5 | "title": "pages.add",
6 | "description": "Schema for pages.add",
7 | "type": "object",
8 | "additionalProperties": false,
9 | "properties": {
10 | "pages": {
11 | "type": "array",
12 | "minItems": 1,
13 | "maxItems": 1,
14 | "items": {
15 | "type": "object",
16 | "allOf": [{"$ref": "pages#/definitions/page"}],
17 | "required": ["title"]
18 | }
19 | }
20 | },
21 | "required": ["pages"]
22 | }
23 |
--------------------------------------------------------------------------------
/core/server/helpers/tpl/subscribe_form.hbs:
--------------------------------------------------------------------------------
1 |
12 |
13 | {{#if error}}
14 | {{error.message}}
15 | {{/if}}
16 |
--------------------------------------------------------------------------------
/core/test/utils/mocks/utils.js:
--------------------------------------------------------------------------------
1 | var Module = require('module'),
2 | originalRequireFn = Module.prototype.require;
3 |
4 | /**
5 | * helper fn to mock non existent modules
6 | * mocks.utils.mockNotExistingModule(/pattern/, mockedModule)
7 | */
8 | exports.mockNotExistingModule = function mockNotExistingModule(modulePath, module) {
9 | Module.prototype.require = function (path) {
10 | if (path.match(modulePath)) {
11 | return module;
12 | }
13 |
14 | return originalRequireFn.apply(this, arguments);
15 | };
16 | };
17 |
18 | exports.unmockNotExistingModule = function unmockNotExistingModule() {
19 | Module.prototype.require = originalRequireFn;
20 | };
21 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/schemas/posts-add.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "$schema": "http://json-schema.org/draft-07/schema#",
4 | "$id": "posts.add",
5 | "title": "posts.add",
6 | "description": "Schema for posts.add",
7 | "type": "object",
8 | "additionalProperties": false,
9 | "properties": {
10 | "posts": {
11 | "type": "array",
12 | "minItems": 1,
13 | "maxItems": 1,
14 | "items": {
15 | "type": "object",
16 | "allOf": [{"$ref": "posts#/definitions/post"}],
17 | "required": ["title"]
18 | }
19 | }
20 | },
21 | "required": [ "posts" ]
22 | }
23 |
--------------------------------------------------------------------------------
/core/server/data/importer/importers/data/subscribers.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('importer:subscribers'),
2 | BaseImporter = require('./base');
3 |
4 | class SubscribersImporter extends BaseImporter {
5 | constructor(allDataFromFile) {
6 | super(allDataFromFile, {
7 | modelName: 'Subscriber',
8 | dataKeyToImport: 'subscribers'
9 | });
10 | }
11 |
12 | beforeImport() {
13 | debug('beforeImport');
14 | return super.beforeImport();
15 | }
16 |
17 | doImport(options, importOptions) {
18 | return super.doImport(options, importOptions);
19 | }
20 | }
21 |
22 | module.exports = SubscribersImporter;
23 |
--------------------------------------------------------------------------------
/core/server/data/meta/author_fb_url.js:
--------------------------------------------------------------------------------
1 | var getContextObject = require('./context_object.js'),
2 | _ = require('lodash');
3 |
4 | function getAuthorFacebookUrl(data) {
5 | var context = data.context ? data.context : null,
6 | contextObject = getContextObject(data, context);
7 |
8 | if ((_.includes(context, 'post') || _.includes(context, 'page')) && contextObject.primary_author && contextObject.primary_author.facebook) {
9 | return contextObject.primary_author.facebook;
10 | } else if (_.includes(context, 'author') && contextObject.facebook) {
11 | return contextObject.facebook;
12 | }
13 | return null;
14 | }
15 |
16 | module.exports = getAuthorFacebookUrl;
17 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/schemas/pages-edit.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "$schema": "http://json-schema.org/draft-07/schema#",
4 | "$id": "pages.edit",
5 | "title": "pages.edit",
6 | "description": "Schema for pages.edit",
7 | "type": "object",
8 | "additionalProperties": false,
9 | "properties": {
10 | "pages": {
11 | "type": "array",
12 | "minItems": 1,
13 | "maxItems": 1,
14 | "items": {
15 | "type": "object",
16 | "allOf": [{"$ref": "pages#/definitions/page"}],
17 | "required": ["updated_at"]
18 | }
19 | }
20 | },
21 | "required": ["pages"]
22 | }
23 |
--------------------------------------------------------------------------------
/core/server/web/shared/middlewares/maintenance.js:
--------------------------------------------------------------------------------
1 | const config = require('../../../config');
2 | const common = require('../../../lib/common');
3 | const urlService = require('../../../services/url');
4 |
5 | module.exports = function maintenance(req, res, next) {
6 | if (config.get('maintenance').enabled) {
7 | return next(new common.errors.MaintenanceError({
8 | message: common.i18n.t('errors.general.maintenance')
9 | }));
10 | }
11 |
12 | if (!urlService.hasFinished()) {
13 | return next(new common.errors.MaintenanceError({
14 | message: common.i18n.t('errors.general.maintenanceUrlService')
15 | }));
16 | }
17 |
18 | next();
19 | };
20 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/schemas/posts-edit.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "$schema": "http://json-schema.org/draft-07/schema#",
4 | "$id": "posts.edit",
5 | "title": "posts.edit",
6 | "description": "Schema for posts.edit",
7 | "type": "object",
8 | "additionalProperties": false,
9 | "properties": {
10 | "posts": {
11 | "type": "array",
12 | "minItems": 1,
13 | "maxItems": 1,
14 | "items": {
15 | "type": "object",
16 | "allOf": [{"$ref": "posts#/definitions/post"}],
17 | "required": ["updated_at"]
18 | }
19 | }
20 | },
21 | "required": [ "posts" ]
22 | }
23 |
--------------------------------------------------------------------------------
/core/test/unit/lib/security/password_spec.js:
--------------------------------------------------------------------------------
1 | const should = require('should'),
2 | security = require('../../../../server/lib/security');
3 |
4 | describe('Lib: Security - Password', function () {
5 | it('hash plain password', function () {
6 | return security.password.hash('test')
7 | .then(function (hash) {
8 | hash.should.match(/^\$2[ayb]\$.{56}$/);
9 | });
10 | });
11 |
12 | it('compare password', function () {
13 | return security.password.compare('test', '$2a$10$we16f8rpbrFZ34xWj0/ZC.LTPUux8ler7bcdTs5qIleN6srRHhilG')
14 | .then(function (valid) {
15 | valid.should.be.true;
16 | });
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/core/test/utils/assertions.js:
--------------------------------------------------------------------------------
1 | var should = require('should'),
2 | errorProps = ['message', 'errorType'];
3 |
4 | should.Assertion.add('JSONErrorObject', function () {
5 | this.params = {operator: 'to be a valid JSON Error Object'};
6 | this.obj.should.be.an.Object();
7 | this.obj.should.have.properties(errorProps);
8 | });
9 |
10 | should.Assertion.add('JSONErrorResponse', function () {
11 | this.params = {operator: 'to be a valid JSON Error Response'};
12 |
13 | this.obj.should.have.property('errors').which.is.an.Array();
14 | this.obj.errors.length.should.be.above(0);
15 |
16 | this.obj.errors.forEach(function (err) {
17 | err.should.be.a.JSONErrorObject();
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 90
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | # exemptLabels:
7 | # Label to use when marking an issue as stale
8 | staleLabel: stale
9 | # Comment to post when marking an issue as stale. Set to `false` to disable
10 | markComment: >
11 | This issue has been automatically marked as stale because it has not had
12 | recent activity. It will be closed if no further activity occurs. Thank you
13 | for your contributions.
14 | # Comment to post when closing a stale issue. Set to `false` to disable
15 | closeComment: false
16 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/input/authors.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:input:authors');
2 | const utils = require('../../index');
3 |
4 | function setDefaultOrder(frame) {
5 | if (!frame.options.order) {
6 | frame.options.order = 'name asc';
7 | }
8 | }
9 |
10 | module.exports = {
11 | browse(apiConfig, frame) {
12 | debug('browse');
13 |
14 | if (utils.isContentAPI(frame)) {
15 | setDefaultOrder(frame);
16 | }
17 | },
18 |
19 | read(apiConfig, frame) {
20 | debug('read');
21 |
22 | if (utils.isContentAPI(frame)) {
23 | setDefaultOrder(frame);
24 | }
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/themes.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:themes');
2 |
3 | module.exports = {
4 | browse(themes, apiConfig, frame) {
5 | debug('browse');
6 |
7 | frame.response = themes;
8 |
9 | debug(frame.response);
10 | },
11 |
12 | upload() {
13 | debug('upload');
14 | this.browse(...arguments);
15 | },
16 |
17 | activate() {
18 | debug('activate');
19 | this.browse(...arguments);
20 | },
21 |
22 | download(fn, apiConfig, frame) {
23 | debug('download');
24 |
25 | frame.response = fn;
26 |
27 | debug(frame.response);
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/index.js:
--------------------------------------------------------------------------------
1 | import './styles/members.css';
2 | import { Component } from 'preact';
3 |
4 | import MembersProvider from './components/MembersProvider';
5 | import Modal from './components/Modal';
6 |
7 | export default class App extends Component {
8 | constructor() {
9 | super();
10 | const apiUrl = window.location.href.substring(0, window.location.href.indexOf('/members/auth'));
11 |
12 | this.state = {
13 | apiUrl
14 | };
15 | }
16 |
17 | render() {
18 | return (
19 |
20 |
21 |
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/core/test/utils/fixtures/themes/casper-1.4/partials/navigation.hbs:
--------------------------------------------------------------------------------
1 |
17 |
18 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/invites.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:invites');
2 |
3 | module.exports = {
4 | all(models, apiConfig, frame) {
5 | debug('all');
6 |
7 | if (!models) {
8 | return;
9 | }
10 |
11 | if (models.meta) {
12 | frame.response = {
13 | invites: models.data.map(model => model.toJSON(frame.options)),
14 | meta: models.meta
15 | };
16 |
17 | return;
18 | }
19 |
20 | frame.response = {
21 | invites: [models.toJSON(frame.options)]
22 | };
23 |
24 | debug(frame.response);
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/core/server/data/importer/importers/data/roles.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('importer:roles'),
2 | BaseImporter = require('./base');
3 |
4 | class RolesImporter extends BaseImporter {
5 | constructor(allDataFromFile) {
6 | super(allDataFromFile, {
7 | modelName: 'Role',
8 | dataKeyToImport: 'roles'
9 | });
10 |
11 | this.errorConfig.returnDuplicates = false;
12 | }
13 |
14 | beforeImport() {
15 | debug('beforeImport');
16 | return super.beforeImport();
17 | }
18 |
19 | doImport(options, importOptions) {
20 | return super.doImport(options, importOptions);
21 | }
22 | }
23 |
24 | module.exports = RolesImporter;
25 |
--------------------------------------------------------------------------------
/core/server/helpers/twitter_url.js:
--------------------------------------------------------------------------------
1 | // # Twitter URL Helper
2 | // Usage: `{{twitter_url}}` or `{{twitter_url author.twitter}}`
3 | //
4 | // Output a url for a twitter username
5 | var proxy = require('./proxy'),
6 | socialUrls = proxy.socialUrls,
7 | localUtils = proxy.localUtils;
8 |
9 | // We use the name twitter_url to match the helper for consistency:
10 | module.exports = function twitter_url(username, options) { // eslint-disable-line camelcase
11 | if (!options) {
12 | options = username;
13 | username = localUtils.findKey('twitter', this, options.data.blog);
14 | }
15 |
16 | if (username) {
17 | return socialUrls.twitter(username);
18 | }
19 |
20 | return null;
21 | };
22 |
--------------------------------------------------------------------------------
/core/server/lib/members/static/auth/assets/images/ghost-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/core/server/services/settings/public.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The settings with type "blog" were originally meant to be public
3 | * This has been misused - unsplash and slack are incorrectly stored there
4 | * https://github.com/TryGhost/Ghost/issues/10318
5 | *
6 | * This file acts as a new whitelist for "public" settings
7 | */
8 |
9 | module.exports = {
10 | title: 'title',
11 | description: 'description',
12 | logo: 'logo',
13 | icon: 'icon',
14 | cover_image: 'cover_image',
15 | facebook: 'facebook',
16 | twitter: 'twitter',
17 | default_locale: 'lang',
18 | active_timezone: 'timezone',
19 | ghost_head: 'ghost_head',
20 | ghost_foot: 'ghost_foot',
21 | navigation: 'navigation'
22 | };
23 |
--------------------------------------------------------------------------------
/core/server/lib/mobiledoc/cards/code.js:
--------------------------------------------------------------------------------
1 | const createCard = require('../create-card');
2 |
3 | module.exports = createCard({
4 | name: 'code',
5 | type: 'dom',
6 | render(opts) {
7 | let payload = opts.payload;
8 | let dom = opts.env.dom;
9 |
10 | if (!payload.code) {
11 | return '';
12 | }
13 |
14 | let pre = dom.createElement('pre');
15 | let code = dom.createElement('code');
16 |
17 | if (payload.language) {
18 | code.setAttribute('class', `language-${payload.language}`);
19 | }
20 |
21 | code.appendChild(dom.createTextNode(payload.code));
22 | pre.appendChild(code);
23 |
24 | return pre;
25 | }
26 | });
27 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/members.js:
--------------------------------------------------------------------------------
1 | const common = require('../../../../../lib/common');
2 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:members');
3 |
4 | module.exports = {
5 | browse(data, apiConfig, frame) {
6 | debug('browse');
7 |
8 | frame.response = data;
9 | },
10 |
11 | read(data, apiConfig, frame) {
12 | debug('read');
13 |
14 | if (!data) {
15 | return Promise.reject(new common.errors.NotFoundError({
16 | message: common.i18n.t('errors.api.members.memberNotFound')
17 | }));
18 | }
19 |
20 | frame.response = {
21 | members: [data]
22 | };
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/core/server/data/meta/author_url.js:
--------------------------------------------------------------------------------
1 | var urlService = require('../../services/url');
2 |
3 | function getAuthorUrl(data, absolute) {
4 | var context = data.context ? data.context[0] : null;
5 |
6 | context = context === 'amp' ? 'post' : context;
7 |
8 | if (data.author) {
9 | return urlService.getUrlByResourceId(data.author.id, {absolute: absolute, secure: data.author.secure, withSubdirectory: true});
10 | }
11 |
12 | if (data[context] && data[context].primary_author) {
13 | return urlService.getUrlByResourceId(data[context].primary_author.id, {absolute: absolute, secure: data[context].secure, withSubdirectory: true});
14 | }
15 |
16 | return null;
17 | }
18 |
19 | module.exports = getAuthorUrl;
20 |
--------------------------------------------------------------------------------
/core/server/helpers/facebook_url.js:
--------------------------------------------------------------------------------
1 | // # Facebook URL Helper
2 | // Usage: `{{facebook_url}}` or `{{facebook_url author.facebook}}`
3 | //
4 | // Output a url for a facebook username
5 | var proxy = require('./proxy'),
6 | socialUrls = proxy.socialUrls,
7 | localUtils = proxy.localUtils;
8 |
9 | // We use the name facebook_url to match the helper for consistency:
10 | module.exports = function facebook_url(username, options) { // eslint-disable-line camelcase
11 | if (!options) {
12 | options = username;
13 | username = localUtils.findKey('facebook', this, options.data.blog);
14 | }
15 |
16 | if (username) {
17 | return socialUrls.facebook(username);
18 | }
19 |
20 | return null;
21 | };
22 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/serializers/output/authors.js:
--------------------------------------------------------------------------------
1 | const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:authors');
2 | const mapper = require('./utils/mapper');
3 |
4 | module.exports = {
5 | browse(models, apiConfig, frame) {
6 | debug('browse');
7 |
8 | frame.response = {
9 | authors: models.data.map(model => mapper.mapUser(model, frame)),
10 | meta: models.meta
11 | };
12 |
13 | debug(frame.response);
14 | },
15 |
16 | read(model, apiConfig, frame) {
17 | debug('read');
18 |
19 | frame.response = {
20 | authors: [mapper.mapUser(model, frame)]
21 | };
22 |
23 | debug(frame.response);
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/core/server/api/v2/utils/validators/input/schemas/tags-add.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "$schema": "http://json-schema.org/draft-07/schema#",
4 | "$id": "tags.add",
5 | "title": "tags.add",
6 | "description": "Schema for tags.add",
7 | "type": "object",
8 | "additionalProperties": false,
9 | "properties": {
10 | "tags": {
11 | "type": "array",
12 | "minItems": 1,
13 | "maxItems": 1,
14 | "additionalProperties": false,
15 | "items": {
16 | "type": "object",
17 | "allOf": [{"$ref": "tags#/definitions/tag"}],
18 | "required": ["name"]
19 | }
20 | }
21 | },
22 | "required": [ "tags" ]
23 | }
24 |
--------------------------------------------------------------------------------
/core/server/web/shared/middlewares/pretty-urls.js:
--------------------------------------------------------------------------------
1 | // Pretty URL redirects
2 | //
3 | // These are two pieces of middleware that handle ensuring that
4 | // URLs get formatted correctly.
5 | // Slashes ensures that we get trailing slashes
6 | // Uncapitalise changes case to lowercase
7 | // @TODO optimise this to reduce the number of redirects required to get to a pretty URL
8 | // @TODO move this to being used by routers?
9 | const slashes = require('connect-slashes');
10 | const config = require('../../../config');
11 |
12 | module.exports = [
13 | slashes(true, {
14 | headers: {
15 | 'Cache-Control': `public, max-age=${config.get('caching:301:maxAge')}`
16 | }
17 | }),
18 | require('./uncapitalise')
19 | ];
20 |
--------------------------------------------------------------------------------
/core/server/data/meta/excerpt.js:
--------------------------------------------------------------------------------
1 | var downsize = require('downsize');
2 |
3 | function getExcerpt(html, truncateOptions) {
4 | truncateOptions = truncateOptions || {};
5 | // Strip inline and bottom footnotes
6 | var excerpt = html.replace(/.*?<\/a>/gi, '');
7 | excerpt = excerpt.replace(/