diff --git a/README.md b/README.md index cdfc0129..2fe74c6b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

An open, extensible, wiki for your team built using React and Node.js.
Try out Outline using our hosted version at www.getoutline.com.

- Outline + Outline

@@ -63,12 +63,13 @@ Outline is composed of separate backend and frontend application which are both ### Frontend -Outline's frontend is a React application compiled with [Webpack](https://webpack.js.org/). It uses [Mobx](https://mobx.js.org/) for state management and [Styled Components](https://www.styled-components.com/) for component styles. Unless global, state logic and styles are always co-located with React components together with their subcomponents to make the component tree easier to manage. The editor is driven by [Slate](https://github.com/ianstormtaylor/slate) with several plugins. +Outline's frontend is a React application compiled with [Webpack](https://webpack.js.org/). It uses [Mobx](https://mobx.js.org/) for state management and [Styled Components](https://www.styled-components.com/) for component styles. Unless global, state logic and styles are always co-located with React components together with their subcomponents to make the component tree easier to manage. + +The editor itself is built ontop of [Slate](https://github.com/ianstormtaylor/slate) and hosted in a separate repository to encourage reuse: [rich-markdown-editor](https://github.com/outline/rich-markdown-editor) - `app/` - Frontend React application - `app/scenes` - Full page views - `app/components` - Reusable React components -- `app/components/Editor` - Text editor and its plugins - `app/stores` - Global state stores - `app/models` - State models - `app/types` - Flow types for non-models diff --git a/app/components/Sidebar/Settings.js b/app/components/Sidebar/Settings.js index 2022e52c..db92804b 100644 --- a/app/components/Sidebar/Settings.js +++ b/app/components/Sidebar/Settings.js @@ -29,7 +29,7 @@ class SettingsSidebar extends React.Component { return ( (props.showDisclosure ? '-8px' : '0')}; + padding-right: 24px; font-weight: 600; color: ${color.text}; text-decoration: none; diff --git a/package.json b/package.json index e48d715f..45fa310d 100644 --- a/package.json +++ b/package.json @@ -155,21 +155,11 @@ "react-waypoint": "^7.3.1", "redis": "^2.6.2", "redis-lock": "^0.1.0", - "rich-markdown-editor": "^1.0.0", + "rich-markdown-editor": "^1.1.0", "safestart": "1.1.0", "sequelize": "4.28.6", "sequelize-cli": "^2.7.0", "sequelize-encrypted": "0.1.0", - "slate": "^0.32.5", - "slate-collapse-on-escape": "^0.6.0", - "slate-edit-code": "^0.14.0", - "slate-edit-list": "^0.11.2", - "slate-md-serializer": "3.0.3", - "slate-paste-linkify": "^0.5.0", - "slate-plain-serializer": "0.5.4", - "slate-prism": "^0.5.0", - "slate-react": "^0.12.3", - "slate-trailing-block": "^0.5.0", "slug": "0.9.1", "string-hash": "^1.1.0", "string-replace-to-array": "^1.0.3", diff --git a/server/api/__snapshots__/documents.test.js.snap b/server/api/__snapshots__/documents.test.js.snap index eaf3a72b..b2dbf607 100644 --- a/server/api/__snapshots__/documents.test.js.snap +++ b/server/api/__snapshots__/documents.test.js.snap @@ -8,6 +8,15 @@ Object { } `; +exports[`#documents.delete should require authentication 1`] = ` +Object { + "error": "authentication_required", + "message": "Authentication required", + "ok": false, + "status": 401, +} +`; + exports[`#documents.list should require authentication 1`] = ` Object { "error": "authentication_required", diff --git a/server/api/documents.js b/server/api/documents.js index 6219c36f..18d6d109 100644 --- a/server/api/documents.js +++ b/server/api/documents.js @@ -428,7 +428,7 @@ router.post('documents.delete', auth(), async ctx => { authorize(ctx.state.user, 'delete', document); const collection = document.collection; - if (collection.type === 'atlas') { + if (collection && collection.type === 'atlas') { // Delete document and all of its children await collection.removeDocument(document); } diff --git a/server/api/documents.test.js b/server/api/documents.test.js index ccc4f81a..ba5ef342 100644 --- a/server/api/documents.test.js +++ b/server/api/documents.test.js @@ -618,3 +618,41 @@ describe('#documents.update', async () => { expect(res.status).toEqual(403); }); }); + +describe('#documents.delete', async () => { + it('should allow deleting document', async () => { + const { user, document } = await seed(); + const res = await server.post('/api/documents.delete', { + body: { token: user.getJwtToken(), id: document.id }, + }); + const body = await res.json(); + + expect(res.status).toEqual(200); + expect(body.success).toEqual(true); + }); + + it('should allow deleting document without collection', async () => { + const { user, document, collection } = await seed(); + + // delete collection without hooks to trigger document deletion + await collection.destroy({ hooks: false }); + const res = await server.post('/api/documents.delete', { + body: { token: user.getJwtToken(), id: document.id }, + }); + const body = await res.json(); + + expect(res.status).toEqual(200); + expect(body.success).toEqual(true); + }); + + it('should require authentication', async () => { + const { document } = await seed(); + const res = await server.post('/api/documents.delete', { + body: { id: document.id }, + }); + const body = await res.json(); + + expect(res.status).toEqual(401); + expect(body).toMatchSnapshot(); + }); +}); diff --git a/shared/utils/naturalSort.js b/shared/utils/naturalSort.js index 3cfb0a25..5c564bb4 100644 --- a/shared/utils/naturalSort.js +++ b/shared/utils/naturalSort.js @@ -1,9 +1,8 @@ // @flow - import _ from 'lodash'; import naturalSort from 'natural-sort'; -export default (sortableArray: Object[], key: string) => { +export default (sortableArray: Object[] = [], key: string) => { let keys = sortableArray.map(object => object[key]); keys.sort(naturalSort()); return _.sortBy(sortableArray, object => keys.indexOf(object[key])); diff --git a/yarn.lock b/yarn.lock index b20e7002..589cbec4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8762,9 +8762,9 @@ retry-as-promised@^2.3.1: bluebird "^3.4.6" debug "^2.6.9" -rich-markdown-editor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-1.0.0.tgz#ae540474e8e4e86b41d339d20cee0572c9794d2b" +rich-markdown-editor@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/rich-markdown-editor/-/rich-markdown-editor-1.1.0.tgz#178307255bba4777c5b0f991202b522e5e0850a1" dependencies: "@tommoor/slate-drop-or-paste-images" "^0.8.1" boundless-arrow-key-navigation "^1.1.0" @@ -8778,11 +8778,11 @@ rich-markdown-editor@^1.0.0: react-keydown "^1.9.7" react-medium-image-zoom "^3.0.10" react-portal "^4.1.4" - slate "^0.32.5" + slate "^0.33.6" slate-collapse-on-escape "^0.6.0" slate-edit-code "^0.14.0" slate-edit-list "^0.11.2" - slate-md-serializer "3.0.4" + slate-md-serializer "3.1.0" slate-paste-linkify "^0.5.0" slate-plain-serializer "0.5.4" slate-prism "^0.5.0" @@ -9143,13 +9143,9 @@ slate-edit-list@^0.11.2: version "0.11.2" resolved "https://registry.yarnpkg.com/slate-edit-list/-/slate-edit-list-0.11.2.tgz#c67b961d98435f9f7747d20b870cbc51d25af7a8" -slate-md-serializer@3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/slate-md-serializer/-/slate-md-serializer-3.0.3.tgz#194aaf74b8c5158cdd45f8d2394a1a1574acaf83" - -slate-md-serializer@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/slate-md-serializer/-/slate-md-serializer-3.0.4.tgz#0ee04c0c2e44954b82f9fab1410b658bb6191aad" +slate-md-serializer@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/slate-md-serializer/-/slate-md-serializer-3.1.0.tgz#8e82899c45a607615b19e03d42cbd38cc7f64acf" slate-paste-linkify@^0.5.0: version "0.5.0" @@ -9202,26 +9198,26 @@ slate-react@^0.12.3: slate-plain-serializer "^0.5.9" slate-prop-types "^0.4.26" -slate-schema-violations@^0.1.3: - version "0.1.7" - resolved "https://registry.yarnpkg.com/slate-schema-violations/-/slate-schema-violations-0.1.7.tgz#cf2c6156eaf545f4d1985d3d1b94c50d6d273a08" +slate-schema-violations@^0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/slate-schema-violations/-/slate-schema-violations-0.1.10.tgz#165227c230ea6c1027e523b7171a73e860e73646" slate-trailing-block@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/slate-trailing-block/-/slate-trailing-block-0.5.0.tgz#cedb4f2975f1167e0fb9d259ce1252b82f4d74ff" -slate@^0.32.5: - version "0.32.5" - resolved "https://registry.yarnpkg.com/slate/-/slate-0.32.5.tgz#5386c75dec1e5b87648189c9fbb4294cec623ff0" +slate@^0.33.6: + version "0.33.6" + resolved "https://registry.yarnpkg.com/slate/-/slate-0.33.6.tgz#0c7cb193cc5adeecec5c81e2ec0c86ab23dd6755" dependencies: - debug "^2.3.2" + debug "^3.1.0" direction "^0.1.5" esrever "^0.2.0" is-empty "^1.0.0" is-plain-object "^2.0.4" lodash "^4.17.4" slate-dev-logger "^0.1.39" - slate-schema-violations "^0.1.3" + slate-schema-violations "^0.1.10" type-of "^2.0.1" slice-ansi@0.0.4: