diff --git a/frontend/config/webpack.config.dev.js b/frontend/config/webpack.config.dev.js
index 4bedf6e617..6edf02c10b 100644
--- a/frontend/config/webpack.config.dev.js
+++ b/frontend/config/webpack.config.dev.js
@@ -70,9 +70,14 @@ module.exports = {
paths.appSrc + "/file-history.js",
],
app: [
- require.resolve('./polyfills'),
- require.resolve('react-dev-utils/webpackHotDevClient'),
- paths.appSrc + "/app.js",
+ require.resolve('./polyfills'),
+ require.resolve('react-dev-utils/webpackHotDevClient'),
+ paths.appSrc + "/app.js",
+ ],
+ draftReview: [
+ require.resolve('./polyfills'),
+ require.resolve('react-dev-utils/webpackHotDevClient'),
+ paths.appSrc + "/draft-review.js",
]
},
diff --git a/frontend/config/webpack.config.prod.js b/frontend/config/webpack.config.prod.js
index f0f72c2616..9cd16b9fe2 100644
--- a/frontend/config/webpack.config.prod.js
+++ b/frontend/config/webpack.config.prod.js
@@ -63,6 +63,7 @@ module.exports = {
repoview: [require.resolve('./polyfills'), paths.appSrc + "/repo-wiki-mode.js"],
fileHistory: [require.resolve('./polyfills'), paths.appSrc + "/file-history.js"],
app: [require.resolve('./polyfills'), paths.appSrc + "/app.js"],
+ draftReview: [require.resolve('./polyfills'), paths.appSrc + "/draft-review.js"],
},
output: {
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index ae3bf8672c..12c43ddc81 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -17,9 +17,9 @@
}
},
"@seafile/seafile-editor": {
- "version": "0.1.26",
- "resolved": "https://registry.npmjs.org/@seafile/seafile-editor/-/seafile-editor-0.1.26.tgz",
- "integrity": "sha512-Vt9bbIFygXGqKc5tNjQwal7dqdskDUALsMZnRLlqjCJh4x1P+939yQ9c1fDYeqrh+cs0k9peTVoCIk5U53deVg==",
+ "version": "0.1.29",
+ "resolved": "https://registry.npmjs.org/@seafile/seafile-editor/-/seafile-editor-0.1.29.tgz",
+ "integrity": "sha512-kelTnU3uR6fQPAaTsnuMQDRcYdc2vrgRoZjgvOJPTBgqVj+fTtjZxogdwWm7iNiRJ4lZmQbKpWZXMEHSU/C2NQ==",
"requires": {
"@seafile/slate-react": "0.1.2",
"autoprefixer": "7.1.6",
@@ -56,7 +56,7 @@
"prop-types": "^15.6.1",
"raf": "3.4.0",
"react": "^16.2.0",
- "react-dev-utils": "^5.0.0",
+ "react-dev-utils": "^5.0.2",
"react-dom": "^16.2.0",
"react-s-alert": "^1.4.1",
"reactstrap": "^5.0.0-beta.3",
@@ -70,16 +70,16 @@
"remark-slug": "^5.0.0",
"seafile-js": "^0.2.17",
"seafile-ui": "^0.1.10",
- "slate": "^0.40.2",
- "slate-hotkeys": "^0.2.3",
- "slate-html-serializer": "^0.7.2",
- "slate-schema-violations": "^0.1.39",
+ "slate": "0.40.2",
+ "slate-hotkeys": "0.2.3",
+ "slate-html-serializer": "0.7.2",
+ "slate-schema-violations": "0.1.39",
"socket.io-client": "^2.1.1",
"style-loader": "0.19.0",
"toggle-selection": "^1.0.6",
"unified": "^6.1.6",
"url-loader": "0.6.2",
- "url-parse": "^1.4.0",
+ "url-parse": "^1.4.3",
"valid-url": "^1.0.9",
"whatwg-fetch": "2.0.3",
"xtend": "^4.0.1"
@@ -87,7 +87,7 @@
"dependencies": {
"reactstrap": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-5.0.0.tgz",
+ "resolved": "http://registry.npmjs.org/reactstrap/-/reactstrap-5.0.0.tgz",
"integrity": "sha512-y0eju/LAK7gbEaTFfq2iW92MF7/5Qh0tc1LgYr2mg92IX8NodGc03a+I+cp7bJ0VXHAiLy0bFL9UP89oSm4cBg==",
"requires": {
"classnames": "^2.2.3",
@@ -123,9 +123,9 @@
},
"dependencies": {
"debug": {
- "version": "3.2.5",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz",
- "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==",
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"requires": {
"ms": "^2.1.1"
}
@@ -138,9 +138,9 @@
}
},
"@types/node": {
- "version": "10.0.4",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-10.0.4.tgz",
- "integrity": "sha512-RisaZmcmCLjRipAY7nVi3fmkIk4Z0JMn8YHdGF6qYMsIDpD0dfzz+3yy2dL5Q5aHWOnqPx51IRxkA44myknJvw=="
+ "version": "10.11.7",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.11.7.tgz",
+ "integrity": "sha512-yOxFfkN9xUFLyvWaeYj90mlqTJ41CsQzWKS3gXdOMOyPVacUsymejKxJ4/pMW7exouubuEeZLJawGgcNGYlTeg=="
},
"abab": {
"version": "1.0.4",
@@ -501,7 +501,7 @@
},
"axios": {
"version": "0.18.0",
- "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
"integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
"requires": {
"follow-redirects": "^1.3.0",
@@ -1675,9 +1675,9 @@
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"bail": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.2.tgz",
- "integrity": "sha1-99bBcxYwqfnw1NNe0fli4gdKF2Q="
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.3.tgz",
+ "integrity": "sha512-1X8CnjFVQ+a+KW36uBNMTU5s8+v5FzeqrP7hTG5aTb4aPreSbZJlhwPon9VKMuEVgV++JM+SQrALY3kr7eswdg=="
},
"balanced-match": {
"version": "1.0.0",
@@ -2083,9 +2083,9 @@
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"ccount": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.2.tgz",
- "integrity": "sha1-U7ai+BW7d7nChx97mnLDol8djok="
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.3.tgz",
+ "integrity": "sha512-Jt9tIBkRc9POUof7QA/VwWd+58fKkEEfI+/t1/eOlxKM7ZhrczNzMFefge7Ai+39y1pR/pP6cI19guHy3FSLmw=="
},
"center-align": {
"version": "0.1.3",
@@ -2121,24 +2121,24 @@
}
},
"character-entities": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.1.tgz",
- "integrity": "sha1-92hxvl72bdt/j440eOzDdMJ9bco="
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.2.tgz",
+ "integrity": "sha512-sMoHX6/nBiy3KKfC78dnEalnpn0Az0oSNvqUWYTtYrhRI5iUIYsROU48G+E+kMFQzqXaJ8kHJZ85n7y6/PHgwQ=="
},
"character-entities-html4": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.1.tgz",
- "integrity": "sha1-NZoqSg9+KdPcKsmb2+Ie45Q46lA="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.2.tgz",
+ "integrity": "sha512-sIrXwyna2+5b0eB9W149izTPJk/KkJTg6mEzDGibwBUkyH1SbDa+nf515Ppdi3MaH35lW0JFJDWeq9Luzes1Iw=="
},
"character-entities-legacy": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.1.tgz",
- "integrity": "sha1-9Ad53xoQGHK7UQo9KV4fzPFHIC8="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz",
+ "integrity": "sha512-9NB2VbXtXYWdXzqrvAHykE/f0QJxzaKIpZ5QzNZrrgQ7Iyxr2vnfS8fCBNVW9nUEZE0lo57nxKRqnzY/dKrwlA=="
},
"character-reference-invalid": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.1.tgz",
- "integrity": "sha1-lCg191Dk7GGjCOYMLvjMEBEgLvw="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz",
+ "integrity": "sha512-7I/xceXfKyUJmSAn/jw8ve/9DyOP7XxufNYLI9Px7CmsKgEUaZLUTax6nZxGQtaoiZCjpu6cHPj20xC/vqRReQ=="
},
"chardet": {
"version": "0.4.2",
@@ -2192,9 +2192,9 @@
}
},
"classnames": {
- "version": "2.2.5",
- "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz",
- "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0="
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
+ "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
},
"clean-css": {
"version": "4.1.9",
@@ -2907,9 +2907,9 @@
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
},
"deepmerge": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.1.0.tgz",
- "integrity": "sha512-Q89Z26KAfA3lpPGhbF6XMfYAm3jIV3avViy6KOJ2JLzFbeWHOvPQUu5aSJIWXap3gDZC2y1eF5HXEPI2wGqgvw=="
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
+ "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
},
"default-require-extensions": {
"version": "1.0.0",
@@ -3959,9 +3959,9 @@
}
},
"extend": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
- "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"external-editor": {
"version": "2.1.0",
@@ -5215,9 +5215,9 @@
}
},
"hast-util-embedded": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-1.0.0.tgz",
- "integrity": "sha1-SdYRS0CTOp0L1wijsBI3jyzW6Gw=",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-1.0.1.tgz",
+ "integrity": "sha512-VreBCMxmSRCT7OjZihrth3ByA9GtLKbRyoDJafP/1vxAxx+VNdhHKKADCSIyMGvrvTExVRqU/BnVq51dxMLAeQ==",
"requires": {
"hast-util-is-element": "^1.0.0"
}
@@ -5255,19 +5255,19 @@
}
},
"hast-util-is-element": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.0.0.tgz",
- "integrity": "sha1-P3IWl4sq4U2YdJh4eCZ18zvjzgA="
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.0.1.tgz",
+ "integrity": "sha512-s/ggaNehYVqmLgTXEv12Lbb72bsOD2r5DhAqPgtDdaI/YFNXVzz0zHFVJnhjIjn7Nak8GbL4nzT2q0RA5div+A=="
},
"hast-util-parse-selector": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.1.0.tgz",
- "integrity": "sha1-tVwPS7e7IEDIicMl74erKcOBArQ="
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.0.tgz",
+ "integrity": "sha512-trw0pqZN7+sH9k7hPWCJNZUbWW2KroSIM/XpIy3G5ZMtx9LSabCyoSp4skJZ4q/eZ5UOBPtvWh4W9c+RE3HRoQ=="
},
"hast-util-phrasing": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-1.0.0.tgz",
- "integrity": "sha1-sFH5kbyWWVwPdWMEpHjtpIZ0xEg=",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-1.0.1.tgz",
+ "integrity": "sha512-PS7eg0yzYyVsTEf82CyrY0nMevcjofTcs7ygi3m6ykggveKqmNc39+/1JWT478piWt3hq5gOsXbMoCxjRQi+rA==",
"requires": {
"hast-util-embedded": "^1.0.0",
"hast-util-has-property": "^1.0.0",
@@ -5300,9 +5300,9 @@
}
},
"hast-util-sanitize": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-1.1.2.tgz",
- "integrity": "sha1-0QvWdXoh5ZwTq8iuNTDdO219Z54=",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-1.2.0.tgz",
+ "integrity": "sha512-VwCTqjt6fbMGacxGB1FKV5sBJaVVkyCGVMDwb4nnqvCW2lkqscA2GEpOyBx4ZWRXty1eAZF58MHBrllEoQEoBg==",
"requires": {
"xtend": "^4.0.1"
}
@@ -5338,9 +5338,9 @@
}
},
"hast-util-whitespace": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.0.tgz",
- "integrity": "sha1-vQlpGWJdKTbh/xe8Tff9cn8X7Ok="
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.1.tgz",
+ "integrity": "sha512-Mfx2ZnmVMTAopZ8as42nKrNt650tCZYhy/MPeO1Imdg/cmCWK6GUSnFrrE3ezGjVifn7x5zMfu8jrjwIGyImSw=="
},
"hastscript": {
"version": "3.1.0",
@@ -5837,9 +5837,9 @@
"integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY="
},
"is-alphabetical": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.1.tgz",
- "integrity": "sha1-x3B5zJHU76x3W+EDS/LSQ/lebwg="
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.2.tgz",
+ "integrity": "sha512-V0xN4BYezDHcBSKb1QHUFMlR4as/XEuCZBzMJUU4n7+Cbt33SmUnSol+pnXFvLxSHNq2CemUXNdaXV6Flg7+xg=="
},
"is-alphanumeric": {
"version": "1.0.0",
@@ -5847,9 +5847,9 @@
"integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ="
},
"is-alphanumerical": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.1.tgz",
- "integrity": "sha1-37SqTRCF4zvbYcLe6cgOnGwZ9Ts=",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz",
+ "integrity": "sha512-pyfU/0kHdISIgslFfZN9nfY1Gk3MquQgUm1mJTjdkEPpkAKNWuBTSqFwewOpR7N351VkErCiyV71zX7mlQQqsg==",
"requires": {
"is-alphabetical": "^1.0.0",
"is-decimal": "^1.0.0"
@@ -5911,9 +5911,9 @@
"dev": true
},
"is-decimal": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.1.tgz",
- "integrity": "sha1-9ftqlJlq2ejjdh+/vQkfH8qMToI="
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.2.tgz",
+ "integrity": "sha512-TRzl7mOCchnhchN+f3ICUCzYvL9ul7R+TYOsZ8xia++knyZAJfv/uA1FvQXsAnYIl1T3B2X5E/J7Wb1QXiIBXg=="
},
"is-directory": {
"version": "0.3.1",
@@ -5965,9 +5965,9 @@
}
},
"is-hexadecimal": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz",
- "integrity": "sha1-bghLvJIGH7sJcexYts5tQE4k2mk="
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz",
+ "integrity": "sha512-but/G3sapV3MNyqiDBLrOi4x8uCIw0RY3o/Vb5GT0sMFHrVV7731wFSVy41T5FO1og7G0gXLJh0MkgPRouko/A=="
},
"is-hotkey": {
"version": "0.1.3",
@@ -6143,9 +6143,9 @@
"integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
},
"is-whitespace-character": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.1.tgz",
- "integrity": "sha1-muAXbzKCtlRXoZks2whPil+DPjs="
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz",
+ "integrity": "sha512-SzM+T5GKUCtLhlHFKt2SDAX2RFzfS6joT91F2/WSi9LxgFdsnhfPK/UIA+JhRR2xuyLdrCys2PiFDrtn1fU5hQ=="
},
"is-window": {
"version": "1.0.2",
@@ -6158,9 +6158,9 @@
"integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
},
"is-word-character": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.1.tgz",
- "integrity": "sha1-WgP6HqkazopusMfNdw64bWXIvvs="
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.2.tgz",
+ "integrity": "sha512-T3FlsX8rCHAH8e7RE7PfOPZVFQlcV3XRF9eOOBQ1uf70OxO7CjjSOjeImMPCADBdYWcStAbVbYvJ1m2D3tb+EA=="
},
"is-wsl": {
"version": "1.1.0",
@@ -7023,14 +7023,14 @@
}
},
"markdown-escapes": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.1.tgz",
- "integrity": "sha1-GZTfLTr0gR3lmmcUk0wrIpJzRRg="
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.2.tgz",
+ "integrity": "sha512-lbRZ2mE3Q9RtLjxZBZ9+IMl68DKIXaVAhwvwn9pmjnPLS0h/6kyBMgNhqi1xFJ/2yv6cSyv0jbiZavZv93JkkA=="
},
"markdown-table": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.1.tgz",
- "integrity": "sha1-Sz3ToTPRUYuO8NvHCb8qG0gkvIw="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.2.tgz",
+ "integrity": "sha512-NcWuJFHDA8V3wkDgR/j4+gZx+YQwstPgfQDV8ndUeWWzta3dnDTBxpVzqS9lkmJAuV5YX35lmyojl6HO5JXAgw=="
},
"math-expression-evaluator": {
"version": "1.2.17",
@@ -7065,26 +7065,25 @@
}
},
"mdast-util-compact": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz",
- "integrity": "sha1-zbX4TitqLTEU3zO9BdnLMuPECDo=",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.2.tgz",
+ "integrity": "sha512-d2WS98JSDVbpSsBfVvD9TaDMlqPRz7ohM/11G0rp5jOBb5q96RJ6YLszQ/09AAixyzh23FeIpCGqfaamEADtWg==",
"requires": {
- "unist-util-modify-children": "^1.0.0",
"unist-util-visit": "^1.1.0"
}
},
"mdast-util-definitions": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-1.2.2.tgz",
- "integrity": "sha512-9NloPSwaB9f1PKcGqaScfqRf6zKOEjTIXVIbPOmgWI/JKxznlgVXC5C+8qgl3AjYg2vJBRgLYfLICaNiac89iA==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-1.2.3.tgz",
+ "integrity": "sha512-P6wpRO8YVQ1iv30maMc93NLh7COvufglBE8/ldcOyYmk5EbfF0YeqlLgtqP/FOBU501Kqar1x5wYWwB3Nga74g==",
"requires": {
"unist-util-visit": "^1.0.0"
}
},
"mdast-util-to-hast": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-3.0.0.tgz",
- "integrity": "sha512-zvEXH2AUevWfKuBqtEPNcDUPI8UC6yIVvgEgNi1v1dLnzb5Pfm+PZjnZn0FhW1WmHcrGMX059MAoqicEauzjcw==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-3.0.2.tgz",
+ "integrity": "sha512-YI8Ea3TFWEZrS31+6Q/d8ZYTOSDKM06IPc3l2+OMFX1o3JTG2mrztlmzDsUMwIXLWofEdTVl/WXBgRG6ddlU/A==",
"requires": {
"collapse-white-space": "^1.0.0",
"detab": "^2.0.0",
@@ -7100,9 +7099,9 @@
}
},
"mdast-util-to-string": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.0.4.tgz",
- "integrity": "sha1-XEVch4yTVfDB5/PotxnPWDaRrPs="
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.0.5.tgz",
+ "integrity": "sha512-2qLt/DEOo5F6nc2VFScQiHPzQ0XXcabquRJxKMhKte8nt42o08HUxNDPk7tt0YPxnWjAT11I1SYi0X0iPnfI5A=="
},
"mdurl": {
"version": "1.0.1",
@@ -7685,9 +7684,9 @@
}
},
"parse-entities": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.1.1.tgz",
- "integrity": "sha1-gRLYhHExnyerrk1klksSL+ThuJA=",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.0.tgz",
+ "integrity": "sha512-XXtDdOPLSB0sHecbEapQi6/58U/ODj/KWfIXmmMCJF/eRn8laX6LZbOyioMoETOOJoWRW8/qTSl5VQkUIfKM5g==",
"requires": {
"character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0",
@@ -9712,15 +9711,23 @@
}
},
"rehype-format": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-2.2.0.tgz",
- "integrity": "sha1-dRuD0O8+b/enRMdoh5FOBVqhaHM=",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-2.3.0.tgz",
+ "integrity": "sha512-uv09q8BdbeRIo5LLrt8cMnONX6HUvpzvcwxjLiC5XLIeYb6KA7AS2inSJXQJ1Wu2B5rHK8CQgcoiig4Jr8krGA==",
"requires": {
+ "hast-util-embedded": "^1.0.1",
"hast-util-phrasing": "^1.0.0",
"html-whitespace-sensitive-tag-names": "^1.0.0",
"rehype-minify-whitespace": "^2.0.0",
"repeat-string": "^1.5.4",
"unist-util-visit-parents": "^1.0.0"
+ },
+ "dependencies": {
+ "unist-util-visit-parents": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz",
+ "integrity": "sha512-yvo+MMLjEwdc3RhhPYSximset7rwjMrdt9E41Smmvg25UQIenzrN83cRnF1JMzoMi9zZOQeYXHSDf7p+IQkW3Q=="
+ }
}
},
"rehype-minify-whitespace": {
@@ -9771,9 +9778,9 @@
}
},
"remark-breaks": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-1.0.0.tgz",
- "integrity": "sha512-1JaM/mhBLUfa/YZkf+qwzeAYxKdD1dZcruRb8jNBESd2+UQIZM9Oq9tIAJnKpFlOtdcIbqWfVTjt+UUnD/8+Dw=="
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-1.0.1.tgz",
+ "integrity": "sha512-Z3FuhGrHZ0KLzvI4KHSzBA7vey1PjmseDTaA4Bfe7Fd1XhqlmHr1Br9BiNTtcpNpHBd00KGLuifDE3kF9UW8ig=="
},
"remark-parse": {
"version": "5.0.0",
@@ -9798,17 +9805,17 @@
}
},
"remark-rehype": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-3.0.0.tgz",
- "integrity": "sha512-WUinfb6vi34f4VYs2XS4HvuYNd0tCu68HOlG4aMp1dfFyVuVfL3aiL9WPw+Q6W99xTTHyxwr7BGO94jF0psoEA==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-3.0.1.tgz",
+ "integrity": "sha512-A9oIvjlUwY2qLNrgoH7MxQb6EEs7kgdOXLtY/5CYCnvsupor7e7gTGmfkzccBkqJ/6nkbEdiX3hfY11FAvYGHg==",
"requires": {
"mdast-util-to-hast": "^3.0.0"
}
},
"remark-slug": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-5.0.0.tgz",
- "integrity": "sha512-bRFK90ia6iooqC5KH6e9nEIL3OwRbTPU6ed2fm/fa66uofKdmRcsmRVMwND3pXLbvH2F022cETYlE7YlVs7LNQ==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-5.1.0.tgz",
+ "integrity": "sha512-FW/V7b3ekfDL1eyPDyzfq0qz5HFPKPNWVC2eqFDie45r774FLGoymOS1oU7LVQfdFNEvNLZ6oBJT/oIxAyBISg==",
"requires": {
"github-slugger": "^1.0.0",
"mdast-util-to-string": "^1.0.0",
@@ -10356,9 +10363,9 @@
"integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU="
},
"slate": {
- "version": "0.40.4",
- "resolved": "https://registry.npmjs.org/slate/-/slate-0.40.4.tgz",
- "integrity": "sha512-8TvIe4POpZk7BBts6iMg2X6ecy2ux/TLakaMR0XvCQQ2bByMACKn8DTZmpBZPuNvZGDaD/JNxpRa7cbZs61h+A==",
+ "version": "0.40.2",
+ "resolved": "https://registry.npmjs.org/slate/-/slate-0.40.2.tgz",
+ "integrity": "sha512-STxC+TGQMinfFoRZuT1/cdExkqCKwgYpdDNEK6tAxS8F/A6lYzgH0J3rtWQH1ePPtZSEIPcqsroFWXVUTAZG6w==",
"requires": {
"debug": "^3.1.0",
"direction": "^0.1.5",
@@ -10370,9 +10377,9 @@
},
"dependencies": {
"debug": {
- "version": "3.2.5",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz",
- "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==",
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"requires": {
"ms": "^2.1.1"
}
@@ -10385,9 +10392,9 @@
}
},
"slate-base64-serializer": {
- "version": "0.2.68",
- "resolved": "https://registry.npmjs.org/slate-base64-serializer/-/slate-base64-serializer-0.2.68.tgz",
- "integrity": "sha512-bYgsKyDW852Yn301BEL0pCMAbR22rTFw4VGj1i7Utv4CuH2ViZZZE7BOW8eUTlamQtANsGQKf1m3DXHl/QQKxA==",
+ "version": "0.2.72",
+ "resolved": "https://registry.npmjs.org/slate-base64-serializer/-/slate-base64-serializer-0.2.72.tgz",
+ "integrity": "sha512-Bra4elmvDIfsFwh1BjW1E7O3g+/BOkfSAij10mjraveHG5MC386t/FJ1itZt2ydv+EAlep5iXkbKzMp8u+RalQ==",
"requires": {
"isomorphic-base64": "^1.0.2"
}
@@ -10406,31 +10413,31 @@
"integrity": "sha512-QdXa+qmOG46VrTfnzn2gUVzs1WiO3Q+zCv3XomzMNGdgAJjCgHBs3jaeQD845h15loS3OJ181gCNAkB3dby6Hw=="
},
"slate-hotkeys": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/slate-hotkeys/-/slate-hotkeys-0.2.5.tgz",
- "integrity": "sha512-b1KnfQTcw6HwgKNUQCdxl1SUu6Sm26DaAqYjxM+ftd/6sUSmDN0YTdMfK+UbNND7WRCkN2Acqq36zQJfEczekg==",
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/slate-hotkeys/-/slate-hotkeys-0.2.3.tgz",
+ "integrity": "sha512-2Ptajlcbc0m+Pt5i/cWp0uKVSjj0VYbKcS+60Dmx5r7HunYhFBi92g3qwQ162uvb6rWenOFVHHYTrf8SiExoZg==",
"requires": {
"is-hotkey": "^0.1.3",
"slate-dev-environment": "^0.2.0"
}
},
"slate-html-serializer": {
- "version": "0.7.7",
- "resolved": "https://registry.npmjs.org/slate-html-serializer/-/slate-html-serializer-0.7.7.tgz",
- "integrity": "sha512-h15NN+lzuDNSLu1NvZ0lJdwEYVA2ec0T0jjAI5lOX+lmOLPIw7JbQwpVeDtYvsw7STkGidr12tGG27fKmOHo8g==",
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/slate-html-serializer/-/slate-html-serializer-0.7.2.tgz",
+ "integrity": "sha512-UpF3tnEEzdvQKQWvRPqnkKftoBG7efDkNCjaoa7p4yCPav/RstCAm9AwhdINGsKDcYgPt9RXvhpe6r7iu/KQQQ==",
"requires": {
"type-of": "^2.0.1"
}
},
"slate-plain-serializer": {
- "version": "0.6.7",
- "resolved": "https://registry.npmjs.org/slate-plain-serializer/-/slate-plain-serializer-0.6.7.tgz",
- "integrity": "sha512-iS5zS1A1EMsl//FK7Cio3+ktEa5K8mCwpH2ub2FadsSoE4lmFy3MMrggvf2zEi/xHTLgwLBx6CDPECW428EzDQ=="
+ "version": "0.6.11",
+ "resolved": "https://registry.npmjs.org/slate-plain-serializer/-/slate-plain-serializer-0.6.11.tgz",
+ "integrity": "sha512-BPFi76EDyb8sPLQjUKFDFFtee/4rMmQceIgD6L6abv7ABndfNrBkK+uXTRQ42H4H60r1sXKvMnPYDsEuLwG+MA=="
},
"slate-prop-types": {
- "version": "0.4.66",
- "resolved": "https://registry.npmjs.org/slate-prop-types/-/slate-prop-types-0.4.66.tgz",
- "integrity": "sha512-5Y2MdsLVc6YWQK5Z3vablXEs5VLkpRAVsJ4PuELgrAzcGEvHpI8UlpR0zcMGF9SsJVWMaIY8fF6+9nh3pAg0JQ=="
+ "version": "0.4.67",
+ "resolved": "https://registry.npmjs.org/slate-prop-types/-/slate-prop-types-0.4.67.tgz",
+ "integrity": "sha512-FmdwitAw1Y69JHm326dfwP6Zd6R99jz1Im8jvKcnG2hytk72I1vIv6ct2CkNGwc3sg90+OIO/Rf18frYxxoTzw=="
},
"slate-schema-violations": {
"version": "0.1.39",
@@ -10688,9 +10695,9 @@
"dev": true
},
"state-toggle": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.0.tgz",
- "integrity": "sha1-0g+aYWu08MO5i5GSLSW2QKorxCU="
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.1.tgz",
+ "integrity": "sha512-Qe8QntFrrpWTnHwvwj2FZTgv+PKIsp0B9VxLzLLbSpPXWOgRgc5LVj/aTiSfK1RqIeF9jeC1UeOH8Q8y60A7og=="
},
"statuses": {
"version": "1.3.1",
@@ -10768,9 +10775,9 @@
}
},
"stringify-entities": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.1.tgz",
- "integrity": "sha1-sVDsLXKsTBtfMktR+2soyc3/BYw=",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz",
+ "integrity": "sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A==",
"requires": {
"character-entities-html4": "^1.0.0",
"character-entities-legacy": "^1.0.0",
@@ -11068,14 +11075,14 @@
"integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM="
},
"trim-trailing-lines": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.0.tgz",
- "integrity": "sha1-eu+7eAjfnWafbaLkOMrIxGradoQ="
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz",
+ "integrity": "sha512-bWLv9BbWbbd7mlqqs2oQYnLD/U/ZqeJeJwbO0FG2zA1aTq+HTvxfHNKFa/HGCVyJpDiioUYaBhfiT6rgk+l4mg=="
},
"trough": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.2.tgz",
- "integrity": "sha512-FHkoUZvG6Egrv9XZAyYGKEyb1JMsFphgPjoczkZC2y6W93U1jswcVURB8MUvtsahEPEVACyxD47JAL63vF4JsQ=="
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.3.tgz",
+ "integrity": "sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw=="
},
"tty-browserify": {
"version": "0.0.0",
@@ -11194,25 +11201,24 @@
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
},
"unherit": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.0.tgz",
- "integrity": "sha1-a5qu379z3xdWrZ4xbdmBiFhAzX0=",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.1.tgz",
+ "integrity": "sha512-+XZuV691Cn4zHsK0vkKYwBEwB74T3IZIcxrgn2E4rKwTfFyI1zCh7X7grwh9Re08fdPlarIdyWgI8aVB3F5A5g==",
"requires": {
"inherits": "^2.0.1",
"xtend": "^4.0.1"
}
},
"unified": {
- "version": "6.1.6",
- "resolved": "https://registry.npmjs.org/unified/-/unified-6.1.6.tgz",
- "integrity": "sha512-pW2f82bCIo2ifuIGYcV12fL96kMMYgw7JKVEgh7ODlrM9rj6vXSY3BV+H6lCcv1ksxynFf582hwWLnA1qRFy4w==",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz",
+ "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==",
"requires": {
"bail": "^1.0.0",
"extend": "^3.0.0",
"is-plain-obj": "^1.1.0",
"trough": "^1.0.0",
"vfile": "^2.0.0",
- "x-is-function": "^1.0.4",
"x-is-string": "^0.1.0"
}
},
@@ -11243,66 +11249,69 @@
}
},
"unist-builder": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-1.0.2.tgz",
- "integrity": "sha1-jDuZA+9kvPsRfdfPal2Y/Bs7J7Y=",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-1.0.3.tgz",
+ "integrity": "sha512-/KB8GEaoeHRyIqClL+Kam+Y5NWJ6yEiPsAfv1M+O1p+aKGgjR89WwoEHKTyOj17L6kAlqtKpAgv2nWvdbQDEig==",
"requires": {
"object-assign": "^4.1.0"
}
},
"unist-util-generated": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.1.tgz",
- "integrity": "sha1-mfFseJWayFTe58YVwpGSTIv03n8="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.2.tgz",
+ "integrity": "sha512-1HcwiEO62dr0XWGT+abVK4f0aAm8Ik8N08c5nAYVmuSxfvpA9rCcNyX/le8xXj1pJK5nBrGlZefeWB6bN8Pstw=="
},
"unist-util-is": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.1.tgz",
- "integrity": "sha1-DDEmKeP5YMZukx6BLT2A53AQlHs="
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.2.tgz",
+ "integrity": "sha512-YkXBK/H9raAmG7KXck+UUpnKiNmUdB+aBGrknfQ4EreE1banuzrKABx3jP6Z5Z3fMSPMQQmeXBlKpCbMwBkxVw=="
},
"unist-util-modify-children": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-1.1.1.tgz",
- "integrity": "sha1-ZtfmpEnm9nIguXarPLi166w55R0=",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-1.1.2.tgz",
+ "integrity": "sha512-GRi04yhng1WqBf5RBzPkOtWAadcZS2gvuOgNn/cyJBYNxtTuyYqTKN0eg4rC1YJwGnzrqfRB3dSKm8cNCjNirg==",
"requires": {
"array-iterate": "^1.0.0"
}
},
"unist-util-position": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.0.0.tgz",
- "integrity": "sha1-5uHgPu64HF4a/lU+jUrfvXwNj4I="
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.0.1.tgz",
+ "integrity": "sha512-05QfJDPI7PE1BIUtAxeSV+cDx21xP7+tUZgSval5CA7tr0pHBwybF7OnEa1dOFqg6BfYH/qiMUnWwWj+Frhlww=="
},
"unist-util-remove-position": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.1.tgz",
- "integrity": "sha1-WoXBVV/BugwQG4ZwfRXlD6TIcbs=",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz",
+ "integrity": "sha512-XxoNOBvq1WXRKXxgnSYbtCF76TJrRoe5++pD4cCBsssSiWSnPEktyFrFLE8LTk3JW5mt9hB0Sk5zn4x/JeWY7Q==",
"requires": {
"unist-util-visit": "^1.1.0"
}
},
"unist-util-stringify-position": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.1.tgz",
- "integrity": "sha1-PMvcU2ee7W7PN3fdf14yKcG2qjw="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz",
+ "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ=="
},
"unist-util-visit": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.3.0.tgz",
- "integrity": "sha512-9ntYcxPFtl44gnwXrQKZ5bMqXMY0ZHzUpqMFiU4zcc8mmf/jzYm8GhYgezuUlX4cJIM1zIDYaO6fG/fI+L6iiQ==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.0.tgz",
+ "integrity": "sha512-FiGu34ziNsZA3ZUteZxSFaczIjGmksfSgdKqBfOejrrfzyUy5b7YrlzT1Bcvi+djkYDituJDy2XB7tGTeBieKw==",
"requires": {
- "unist-util-is": "^2.1.1"
+ "unist-util-visit-parents": "^2.0.0"
}
},
"unist-util-visit-parents": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz",
- "integrity": "sha512-yvo+MMLjEwdc3RhhPYSximset7rwjMrdt9E41Smmvg25UQIenzrN83cRnF1JMzoMi9zZOQeYXHSDf7p+IQkW3Q=="
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.0.1.tgz",
+ "integrity": "sha512-6B0UTiMfdWql4cQ03gDTCSns+64Zkfo2OCbK31Ov0uMizEz+CJeAp0cgZVb5Fhmcd7Bct2iRNywejT0orpbqUA==",
+ "requires": {
+ "unist-util-is": "^2.1.2"
+ }
},
"universalify": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
- "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
},
"unpipe": {
"version": "1.0.0",
@@ -11501,14 +11510,14 @@
}
},
"vfile-location": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.2.tgz",
- "integrity": "sha1-02dcWch3SY5JK0dW/2Xkrxp1IlU="
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.3.tgz",
+ "integrity": "sha512-zM5/l4lfw1CBoPx3Jimxoc5RNDAHHpk6AM6LM0pTIkm5SUSsx8ZekZ0PVdf0WEZ7kjlhSt7ZlqbRL6Cd6dBs6A=="
},
"vfile-message": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.0.0.tgz",
- "integrity": "sha512-HPREhzTOB/sNDc9/Mxf8w0FmHnThg5CRSJdR9VRFkD2riqYWs+fuXlj5z8mIpv2LrD7uU41+oPWFOL4Mjlf+dw==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.0.1.tgz",
+ "integrity": "sha512-vSGCkhNvJzO6VcWC6AlJW4NtYOVtS+RgCaqFIYUjoGIlHnFL+i0LbtYvonDWOMcB97uTPT4PRsyYY7REWC9vug==",
"requires": {
"unist-util-stringify-position": "^1.1.1"
}
@@ -11785,13 +11794,13 @@
"dependencies": {
"ansi-regex": {
"version": "1.1.1",
- "resolved": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz",
"integrity": "sha1-QchHGUZGN15qGl0Qw8oFTvn8mA0=",
"dev": true
},
"strip-ansi": {
"version": "2.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz",
"integrity": "sha1-32LBqpTtLxFOHQ8h/R1QSCt5pg4=",
"dev": true,
"requires": {
@@ -12181,11 +12190,6 @@
"resolved": "https://registry.npmjs.org/x-is-array/-/x-is-array-0.1.0.tgz",
"integrity": "sha1-3lIBcdR7P0FvVYfWKbidJrEtwp0="
},
- "x-is-function": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/x-is-function/-/x-is-function-1.0.4.tgz",
- "integrity": "sha1-XSlNw9Joy90GJYDgxd93o5HR+h4="
- },
"x-is-string": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index a669e49fe4..960b7dda2e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -4,8 +4,9 @@
"private": true,
"dependencies": {
"@reach/router": "^1.2.0",
- "@seafile/seafile-editor": "^0.1.26",
+ "@seafile/seafile-editor": "^0.1.29",
"autoprefixer": "7.1.6",
+ "classnames": "^2.2.6",
"css-loader": "0.28.7",
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",
diff --git a/frontend/src/app.js b/frontend/src/app.js
index ed5524cf30..86064ac4bf 100644
--- a/frontend/src/app.js
+++ b/frontend/src/app.js
@@ -5,6 +5,8 @@ import { siteRoot } from './utils/constants';
import SidePanel from './components/side-panel';
import MainPanel from './components/main-panel';
import DraftsView from './pages/drafts/drafts-view';
+import DraftContent from './pages/drafts/draft-content';
+import ReviewContent from './pages/drafts/review-content';
import FilesActivities from './pages/dashboard/files-activities';
import Starred from './pages/starred/starred';
@@ -49,7 +51,10 @@ class App extends Component {
-
+
+
+
+
diff --git a/frontend/src/components/draft-list-view/draft-list-item.js b/frontend/src/components/draft-list-view/draft-list-item.js
index e58b7d301d..b2dc606c9f 100644
--- a/frontend/src/components/draft-list-view/draft-list-item.js
+++ b/frontend/src/components/draft-list-view/draft-list-item.js
@@ -47,8 +47,22 @@ class DraftListItem extends React.Component {
onDraftEditClick = () => {
let draft = this.props.draft;
let filePath = draft.draft_file_path;
- let repoID = draft.draft_repo_id;
- window.location.href= siteRoot + 'lib/' + repoID + '/file' + filePath + '?mode=edit';
+ let repoID = draft.origin_repo_id;
+ let url = siteRoot + 'lib/' + repoID + '/file' + filePath + '?mode=edit&draft_id=' + draft.id;
+ window.open(url)
+ }
+
+ onLibraryClick = () => {
+ let draft = this.props.draft;
+ let repoID = draft.origin_repo_id;
+ let url = siteRoot + '#common/lib/' + repoID;
+ window.open(url)
+ }
+
+ onReviewClick = () => {
+ let draft = this.props.draft;
+ let url = siteRoot + 'drafts/review/' + draft.review_id + '/';
+ window.open(url);
}
getFileName(filePath) {
@@ -65,13 +79,19 @@ class DraftListItem extends React.Component {
{fileName}
- {draft.owner}
+ {draft.repo_name}
+
+ { draft.review_id && draft.review_status === 'open' ? #{draft.review_id} : -- }
+
{localTime}
-
+ {
+ this.props.draft.review_status !== 'open' &&
+
+ }
);
diff --git a/frontend/src/components/draft-list-view/draft-list-menu.js b/frontend/src/components/draft-list-view/draft-list-menu.js
index dfda814c09..a9e0d6a63e 100644
--- a/frontend/src/components/draft-list-view/draft-list-menu.js
+++ b/frontend/src/components/draft-list-view/draft-list-menu.js
@@ -13,18 +13,28 @@ class DraftListMenu extends React.Component {
render() {
let style = {};
- let {isMenuShow, menuPosition} = this.props;
+ let {isMenuShow, menuPosition, currentDraft} = this.props;
if (isMenuShow) {
style = {position: 'fixed', top: menuPosition.top, left: menuPosition.left, display: 'block'};
}
- return (
-
+ if (currentDraft.review_status === null) {
+ return (
{gettext('Delete')}
{gettext('Publish')}
+ {gettext('Ask for review')}
-
- );
+ );
+ }
+
+ if (currentDraft.review_status === 'closed' ) {
+ return (
+
+ {gettext('Delete')}
+ {gettext('Ask for review')}
+
+ );
+ }
}
}
diff --git a/frontend/src/components/draft-list-view/draft-list-view.js b/frontend/src/components/draft-list-view/draft-list-view.js
index d2d2dcd7a5..6fff8df734 100644
--- a/frontend/src/components/draft-list-view/draft-list-view.js
+++ b/frontend/src/components/draft-list-view/draft-list-view.js
@@ -20,8 +20,9 @@ class DraftListView extends React.Component {
{/*img*/}
{gettext('Name')}
- {gettext('Owner')}
- {gettext('Last Update')}
+ {gettext('Library')}
+ {gettext('Review')}
+ {gettext('Last Update')}
diff --git a/frontend/src/components/main-side-nav.js b/frontend/src/components/main-side-nav.js
index 28703d0208..0207b2a61e 100644
--- a/frontend/src/components/main-side-nav.js
+++ b/frontend/src/components/main-side-nav.js
@@ -147,13 +147,13 @@ class MainSideNav extends React.Component {
Tools
- this.tabItemClick('favorites')}>
+ this.tabItemClick('favorites')}>
{gettext('Favorites')}
-
+
- this.tabItemClick('dashboard')}>
+ this.tabItemClick('dashboard')}>
{gettext('Acitivities')}
@@ -165,7 +165,7 @@ class MainSideNav extends React.Component {
this.tabItemClick('drafts')}>
-
+
{gettext('Drafts')}
diff --git a/frontend/src/components/markdown-viewer.js b/frontend/src/components/markdown-viewer.js
index 2a5f5458a7..d75d1a96ea 100644
--- a/frontend/src/components/markdown-viewer.js
+++ b/frontend/src/components/markdown-viewer.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { processor, processorGetAST } from '@seafile/seafile-editor/src/lib/seafile-markdown2html';
+import { processor, processorGetAST } from '@seafile/seafile-editor/dist/utils/seafile-markdown2html';
import Prism from 'prismjs';
import WikiOutline from './wiki-outline';
@@ -7,7 +7,7 @@ var URL = require('url-parse');
const gettext = window.gettext;
-require('@seafile/seafile-editor/src/lib/code-hight-package');
+require('@seafile/seafile-editor/dist/editor/code-hight-package');
const contentClass = "wiki-md-viewer-rendered-content";
diff --git a/frontend/src/components/review-list-view/review-list-item.js b/frontend/src/components/review-list-view/review-list-item.js
new file mode 100644
index 0000000000..df9963ec4a
--- /dev/null
+++ b/frontend/src/components/review-list-view/review-list-item.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { siteRoot, lang } from '../../utils/constants';
+import moment from 'moment';
+
+moment.locale(lang);
+const propTypes = {
+ isItemFreezed: PropTypes.bool.isRequired,
+}
+class ReviewListItem extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ highlight: '',
+ };
+ }
+
+ onMouseEnter = () => {
+ if (!this.props.isItemFreezed) {
+ this.setState({
+ highlight: 'tr-highlight'
+ });
+ }
+ }
+
+ onMouseLeave = () => {
+ if (!this.props.isItemFreezed) {
+ this.setState({
+ highlight: ''
+ });
+ }
+ }
+
+ onReviewsClick = () => {
+ let item = this.props.item;
+ let filePath = item.draft_file_path;
+ let itemID = item.id;
+ window.open(siteRoot + 'drafts/review/' + itemID);
+ }
+
+ getFileName(filePath) {
+ let lastIndex = filePath.lastIndexOf("/");
+ return filePath.slice(lastIndex+1);
+ }
+
+ render() {
+ let item = this.props.item;
+ let fileName = this.getFileName(item.draft_file_path);
+ let localTime = moment.utc(item.updated_at).toDate();
+ localTime = moment(localTime).fromNow();
+
+ return (
+
+
+ {fileName}
+ {item.draft_origin_repo_name}
+ {item.status}
+ {localTime}
+
+
+ );
+ }
+}
+
+ReviewListItem.propTypes = propTypes;
+
+export default ReviewListItem;
diff --git a/frontend/src/components/review-list-view/review-list-view.js b/frontend/src/components/review-list-view/review-list-view.js
new file mode 100644
index 0000000000..354f7d272f
--- /dev/null
+++ b/frontend/src/components/review-list-view/review-list-view.js
@@ -0,0 +1,47 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { gettext } from '../../utils/constants';
+import ReviewListItem from './review-list-item';
+
+const propTypes = {
+ isItemFreezed: PropTypes.bool.isRequired,
+ itemsList: PropTypes.array.isRequired,
+};
+
+class ReviewListView extends React.Component {
+
+ render() {
+ let items = this.props.itemsList;
+ return (
+
+
+
+
+ {/*img*/}
+ {gettext('Name')}
+ {gettext('Library')}
+ {gettext('Status')}
+ {gettext('Last Update')}
+
+
+
+
+ { items && items.map((item) => {
+ return (
+
+ );
+ })}
+
+
+
+ );
+ }
+}
+
+ReviewListView.propTypes = propTypes;
+
+export default ReviewListView;
diff --git a/frontend/src/css/initial-style.css b/frontend/src/css/initial-style.css
index da701f6cca..6d47bf5b06 100644
--- a/frontend/src/css/initial-style.css
+++ b/frontend/src/css/initial-style.css
@@ -142,3 +142,14 @@
.article hr.active {
border-top: 1px solid #eb8205;
}
+
+.content-container {
+ background-color: #fafaf9;
+ border-radius: 10px;
+}
+
+.markdown-viewer-render-content {
+ margin: 20px 40px;
+ background-color: #fff;
+ border: 1px solid #e6e6dd;
+}
diff --git a/frontend/src/css/layout.css b/frontend/src/css/layout.css
index 45536bfae8..8ae639105f 100644
--- a/frontend/src/css/layout.css
+++ b/frontend/src/css/layout.css
@@ -1,4 +1,4 @@
-#wrapper {
+#wrapper, .wrapper {
width: 100%;
height: 100%;
display: flex;
@@ -7,7 +7,6 @@
/* for top bottom layout*/
#header {
- height: 49px;
display: flex;
}
@@ -112,4 +111,82 @@
[role=group] {
display: flex;
flex: 1;
-}
\ No newline at end of file
+}
+
+.header {
+ padding: 0.625rem;
+ display: flex;
+ flex-shrink:0;
+ align-items: center;
+ justify-content: space-between;
+ background-color: #fff;
+ border-bottom: 1px solid #e5e5e5;
+ box-shadow: 0 3px 2px -2px rgba(200,200,200,.15);
+}
+
+.header .cur-file-info {
+ display: flex;
+ margin-left: 0.5rem;
+}
+
+.header .info-item {
+ display: flex;
+ font-size: 1.2rem;
+ font-weight: bold;
+ margin-right: 0.5rem;
+ align-items: center;
+ justify-content: center;
+}
+
+.header .file-copywriting {
+ margin-left: 0.5rem;
+ font-size: 1rem;
+ font-weight: normal;
+ color: #999;
+}
+
+.header .file-feature {
+ width: 2.9375rem;
+ height: 2.9375rem;
+ font-size: 1.8rem;
+ background-color: #fbcb09;
+ color: #fff;
+}
+
+.header .file-operation-btn {
+ margin-right: 0.25rem;
+}
+
+.review {
+ padding: 0;
+}
+
+.review .cur-file-info {
+ margin: 0;
+}
+
+.review .file-feature {
+ width: 4.1875rem;
+ height: 4.1875rem;
+ font-size: 2rem;
+}
+
+.review-state {
+ position: relative;
+ padding: 0.75rem 1.25rem;
+ margin: auto 0.5rem;
+ border: 1px solid transparent;
+ border-radius: 3px;
+}
+
+.review-state-finished {
+ color: #316100;
+ background-color: #dff1cc;
+ border-color: #d2ecb8;
+}
+
+.review-state-closed {
+ color: #6b1110;
+ background-color: #f5d2d2;
+ border-color: #f1c1c0;
+}
diff --git a/frontend/src/draft-review.js b/frontend/src/draft-review.js
new file mode 100644
index 0000000000..c693efba9f
--- /dev/null
+++ b/frontend/src/draft-review.js
@@ -0,0 +1,133 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Prism from 'prismjs';
+import { siteRoot, gettext, draftID, reviewID, draftOriginFilePath, draftFilePath, draftOriginRepoID, draftFileName, opStatus, publishFileVersion, originFileVersion } from './utils/constants';
+import { seafileAPI } from './utils/seafile-api';
+import axios from 'axios';
+import DiffViewer from '@seafile/seafile-editor/dist/viewer/diff-viewer';
+import Loading from './components/loading';
+import Toast from './components/toast';
+
+import 'seafile-ui';
+import './assets/css/fa-solid.css';
+import './assets/css/fa-regular.css';
+import './assets/css/fontawesome.css';
+import './css/layout.css';
+import './css/initial-style.css';
+import './css/toolbar.css';
+
+require('@seafile/seafile-editor/dist/editor/code-hight-package');
+
+class DraftReview extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ draftContent: '',
+ draftOriginContent: '',
+ reviewStatus: opStatus,
+ isLoading: true,
+ };
+ }
+
+ componentDidMount() {
+ if (publishFileVersion == 'None') {
+ axios.all([
+ seafileAPI.getFileDownloadLink(draftOriginRepoID, draftFilePath),
+ seafileAPI.getFileDownloadLink(draftOriginRepoID, draftOriginFilePath)
+ ]).then(axios.spread((res1, res2) => {
+ axios.all([
+ seafileAPI.getFileContent(res1.data),
+ seafileAPI.getFileContent(res2.data)
+ ]).then(axios.spread((draftContent, draftOriginContent) => {
+ this.setState({
+ draftContent: draftContent.data,
+ draftOriginContent: draftOriginContent.data,
+ isLoading: false
+ });
+ }));
+ }));
+ } else {
+ let dl0 = siteRoot + 'repo/' + draftOriginRepoID + '/' + publishFileVersion + '/download?' + 'p=' + draftOriginFilePath;
+ let dl = siteRoot + 'repo/' + draftOriginRepoID + '/' + originFileVersion + '/download?' + 'p=' + draftOriginFilePath;
+ axios.all([
+ seafileAPI.getFileContent(dl0),
+ seafileAPI.getFileContent(dl)
+ ]).then(axios.spread((draftContent, draftOriginContent) => {
+ this.setState({
+ draftContent: draftContent.data,
+ draftOriginContent: draftOriginContent.data,
+ isLoading: false,
+ });
+ }));
+ }
+ }
+
+ onCloseReview = () => {
+ seafileAPI.updateReviewStatus(reviewID, 'closed').then(res => {
+ this.setState({reviewStatus: 'closed'});
+ Toast.success('Review close succeeded.')
+ }).catch(() => {
+ Toast.error('Review close failed.')
+ });
+ }
+
+ onPublishReview = () => {
+ seafileAPI.updateReviewStatus(reviewID, 'finished').then(res => {
+ this.setState({reviewStatus: 'finished'});
+ Toast.success('Review publish succeeded.')
+ }).catch(() => {
+ Toast.error('Review publish failed.')
+ });
+ }
+
+ render() {
+ return(
+
+
+
+
+
+
+ {
+ this.state.isLoading ?
+ :
+
+ }
+
+
+
+
+
+ );
+ }
+}
+
+ReactDOM.render (
+ ,
+ document.getElementById('wrapper')
+);
diff --git a/frontend/src/markdown-editor.js b/frontend/src/markdown-editor.js
index 4fd7d28a60..d241bfe469 100644
--- a/frontend/src/markdown-editor.js
+++ b/frontend/src/markdown-editor.js
@@ -10,6 +10,7 @@ let siteRoot = window.app.config.siteRoot;
let domain = window.app.pageOptions.domain;
let protocol = window.app.pageOptions.protocol;
let mode = window.app.pageOptions.mode;
+let draftID = window.app.pageOptions.draftID;
let dirPath = '/';
const serviceUrl = window.app.config.serviceUrl;
@@ -25,6 +26,7 @@ function getImageFileNameWithTimestamp() {
return 'image-' + d.toString() + '.png';
}
+
class EditorUtilities {
constructor () {
@@ -170,6 +172,14 @@ class EditorUtilities {
getFileHistoryVersion(commitID) {
return seafileAPI.getFileRevision(repoID, commitID, filePath);
}
+
+ createDraftReview() {
+ return seafileAPI.createDraftReview(draftID)
+ .then(res => {
+ let url = serviceUrl + '/drafts/review/' + res.data.id;
+ return url;
+ })
+ }
}
@@ -243,6 +253,7 @@ class MarkdownEditor extends React.Component {
collabServer={this.state.collabServer}
showFileHistory={true}
mode={mode}
+ draftID={draftID}
/>
);
}
diff --git a/frontend/src/pages/drafts/draft-content.js b/frontend/src/pages/drafts/draft-content.js
new file mode 100644
index 0000000000..b807ce3048
--- /dev/null
+++ b/frontend/src/pages/drafts/draft-content.js
@@ -0,0 +1,136 @@
+import React from 'react';
+import { siteRoot, gettext } from '../../utils/constants';
+import editUtilties from '../../utils/editor-utilties';
+import Toast from '../../components/toast';
+import Loading from '../../components/loading';
+import DraftListView from '../../components/draft-list-view/draft-list-view';
+import DraftListMenu from '../../components/draft-list-view/draft-list-menu';
+
+
+class DraftContent extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ draftList: [],
+ isLoadingDraft: true,
+ isMenuShow: false,
+ menuPosition: {top:'', left: ''},
+ currentDraft: null,
+ isItemFreezed: false,
+ };
+ }
+
+ componentDidMount() {
+ this.initDraftList();
+ document.addEventListener('click', this.onHideContextMenu);
+ }
+
+ componentWillUnmount() {
+ document.removeEventListener('click', this.onHideContextMenu);
+ }
+
+ initDraftList() {
+ this.setState({isLoadingDraft: true});
+ editUtilties.listDrafts().then(res => {
+ this.setState({
+ draftList: res.data.data,
+ isLoadingDraft: false,
+ });
+ });
+ }
+
+ onDeleteHandler = () => {
+ let draft = this.state.currentDraft;
+ editUtilties.deleteDraft(draft.id).then(res => {
+ this.initDraftList();
+ Toast.success('Delete draft succeeded.');
+ }).catch(() => {
+ Toast.error('Delete draft failed.');
+ });
+ }
+
+ onPublishHandler = () => {
+ let draft = this.state.currentDraft;
+ editUtilties.publishDraft(draft.id).then(res => {
+ this.initDraftList();
+ Toast.success('Publish draft succeeded.');
+ }).catch(() => {
+ Toast.error('Publish draft failed.');
+ });
+ }
+
+ onReviewHandler = () => {
+ let draft = this.state.currentDraft;
+
+ editUtilties.createDraftReview(draft.id).then(res => {
+ window.open(siteRoot + 'drafts/review/' + res.data.id);
+ })
+ .catch((error) => {
+ if (error.response.status == '409') {
+ Toast.error("The draft review is existing.");
+ }
+ });
+ }
+
+ onMenuToggleClick = (e, draft) => {
+ if (this.state.isMenuShow) {
+ this.onHideContextMenu();
+ } else {
+ this.onShowContextMenu(e, draft);
+ }
+ }
+
+ onShowContextMenu = (e, draft) => {
+ let left = e.clientX - 8*16;
+ let top = e.clientY + 10;
+ let position = {top: top, left: left};
+ this.setState({
+ isMenuShow: true,
+ menuPosition: position,
+ currentDraft: draft,
+ isItemFreezed: true
+ });
+ }
+
+ onHideContextMenu = () => {
+ this.setState({
+ isMenuShow: false,
+ currentDraft: null,
+ isItemFreezed: false
+ });
+ }
+
+ render() {
+ return (
+
+ {this.state.isLoadingDraft &&
}
+ {(!this.state.isLoadingDraft && this.state.draftList.length !==0) &&
+
+ }
+ {(!this.state.isLoadingDraft && this.state.draftList.length === 0) &&
+
+
{gettext('No draft yet')}
+
{gettext('Draft is a way to let you collaborate with others on files. You can create a draft from a file, edit the draft and then ask for a review. The original file will be updated only after the draft be reviewed.')}
+
+ }
+ {this.state.isMenuShow &&
+
+ }
+
+ );
+ }
+}
+
+export default DraftContent;
diff --git a/frontend/src/pages/drafts/drafts-view.js b/frontend/src/pages/drafts/drafts-view.js
index 81fe2da2e9..21fb4ef344 100644
--- a/frontend/src/pages/drafts/drafts-view.js
+++ b/frontend/src/pages/drafts/drafts-view.js
@@ -1,124 +1,43 @@
import React from 'react';
-import { gettext } from '../../utils/constants';
-import editUtilties from '../../utils/editor-utilties';
-import Toast from '../../components/toast/';
-import Loading from '../../components/loading';
-import DraftListView from '../../components/draft-list-view/draft-list-view';
-import DraftListMenu from '../../components/draft-list-view/draft-list-menu';
+import { siteRoot, gettext } from '../../utils/constants';
+import { Link } from '@reach/router';
class DraftsView extends React.Component {
constructor(props) {
super(props);
this.state = {
- draftList: [],
- isLoadingDraft: true,
- isMenuShow: false,
- menuPosition: {top:'', left: ''},
- currentDraft: null,
- isItemFreezed: false,
+ currentTab: this.props.currentTab
};
}
- componentDidMount() {
- this.initDraftList();
- document.addEventListener('click', this.onHideContextMenu);
- }
-
- componentWillUnmount() {
- document.removeEventListener('click', this.onHideContextMenu);
- }
-
- initDraftList() {
- this.setState({isLoadingDraft: true});
- editUtilties.listDrafts().then(res => {
- this.setState({
- draftList: res.data.data,
- isLoadingDraft: false,
- });
- });
- }
-
- onDeleteHandler = () => {
- let draft = this.state.currentDraft;
- editUtilties.deleteDraft(draft.id).then(res => {
- this.initDraftList();
- Toast.success(gettext('Delete draft succeeded.'));
- }).catch(() => {
- Toast.error(gettext('Delete draft failed.'));
- });
- }
-
- onPublishHandler = () => {
- let draft = this.state.currentDraft;
- editUtilties.publishDraft(draft.id).then(res => {
- this.initDraftList();
- Toast.success(gettext('Publish draft succeeded.'));
- }).catch(() => {
- Toast.error(gettext('Publish draft failed.'));
- });
- }
-
- onMenuToggleClick = (e, draft) => {
- if (this.state.isMenuShow) {
- this.onHideContextMenu();
- } else {
- this.onShowContextMenu(e, draft);
- }
- }
-
- onShowContextMenu = (e, draft) => {
- let left = e.clientX - 8*16;
- let top = e.clientY + 10;
- let position = {top: top, left: left};
+ tabItemClick = (param) => {
this.setState({
- isMenuShow: true,
- menuPosition: position,
- currentDraft: draft,
- isItemFreezed: true
- });
- }
-
- onHideContextMenu = () => {
- this.setState({
- isMenuShow: false,
- currentDraft: null,
- isItemFreezed: false
- });
+ currentTab: param
+ })
}
render() {
return (
-
{gettext('Drafts')}
-
- {this.state.isLoadingDraft &&
}
- {(!this.state.isLoadingDraft && this.state.draftList.length !==0) &&
-
- }
- {(!this.state.isLoadingDraft && this.state.draftList.length === 0) &&
-
-
{gettext('No draft yet')}
-
{gettext('Draft is a way to let you collaborate with others on files. You can create a draft from a file, edit the draft and then ask for a review. The original file will be updated only after the draft be reviewed.')}
-
- }
- {this.state.isMenuShow &&
-
- }
+
+
+ this.tabItemClick('drafts')}>
+
+ {gettext('Drafts')}
+
+
+ this.tabItemClick('reviews')}>
+
+ {gettext('Reviews')}
+
+
+
+ {this.props.children}
);
}
}
-export default DraftsView;
\ No newline at end of file
+export default DraftsView;
diff --git a/frontend/src/pages/drafts/review-content.js b/frontend/src/pages/drafts/review-content.js
new file mode 100644
index 0000000000..9364e49c89
--- /dev/null
+++ b/frontend/src/pages/drafts/review-content.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import { gettext } from '../../utils/constants';
+import editUtilties from '../../utils/editor-utilties';
+import Loading from '../../components/loading';
+import ReviewListView from '../../components/review-list-view/review-list-view';
+
+class ReviewContent extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ reviewsList: [],
+ isLoadingReviews: true,
+ isItemFreezed: false,
+ };
+ }
+
+ componentDidMount() {
+ this.initReviewList();
+ }
+
+ initReviewList() {
+ this.setState({isLoadingReviews: true});
+ editUtilties.listReviews().then(res => {
+ this.setState({
+ reviewsList: res.data.data,
+ isLoadingReviews: false,
+ });
+ });
+ }
+
+ render() {
+ return (
+
+ {this.state.isLoadingReviews &&
}
+ {(!this.state.isLoadingReviews && this.state.reviewsList.length !==0) &&
+
+ }
+ {(!this.state.isLoadingReviews && this.state.reviewsList.length === 0) &&
+
+
{gettext('There is no Review file existing')}
+
+ }
+
+ );
+ }
+}
+
+export default ReviewContent;
diff --git a/frontend/src/pages/file-history/main-panel.js b/frontend/src/pages/file-history/main-panel.js
index b0e63c5509..fbce83a083 100644
--- a/frontend/src/pages/file-history/main-panel.js
+++ b/frontend/src/pages/file-history/main-panel.js
@@ -3,10 +3,10 @@ import PropTypes from 'prop-types';
import Prism from 'prismjs';
import Loading from '../../components/loading';
import CommonToolbar from '../../components/toolbar/common-toolbar';
-import DiffViewer from '@seafile/seafile-editor/dist/diff-viewer/diff-viewer';
+import DiffViewer from '@seafile/seafile-editor/dist/viewer/diff-viewer';
import '../../css/initial-style.css';
-require('@seafile/seafile-editor/src/lib/code-hight-package');
+require('@seafile/seafile-editor/dist/editor/code-hight-package');
const contentClass = 'markdown-viewer-render-content';
const propTypes = {
diff --git a/frontend/src/pages/repo-wiki-mode/main-panel.js b/frontend/src/pages/repo-wiki-mode/main-panel.js
index 355c5b42f5..54e0dec99a 100644
--- a/frontend/src/pages/repo-wiki-mode/main-panel.js
+++ b/frontend/src/pages/repo-wiki-mode/main-panel.js
@@ -115,7 +115,7 @@ class MainPanel extends Component {
-
+
diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js
index d90305df4e..9636706125 100644
--- a/frontend/src/utils/constants.js
+++ b/frontend/src/utils/constants.js
@@ -26,3 +26,13 @@ export const historyRepoID = window.fileHistory ? window.fileHistory.pageOptions
export const repoName = window.fileHistory ? window.fileHistory.pageOptions.repoName : '';
export const filePath = window.fileHistory ? window.fileHistory.pageOptions.filePath : '';
export const fileName = window.fileHistory ? window.fileHistory.pageOptions.fileName : '';
+
+// Draft review
+export const draftFilePath = window.draftReview ? window.draftReview.config.draftFilePath: '';
+export const draftOriginFilePath = window.draftReview ? window.draftReview.config.draftOriginFilePath: '';
+export const draftOriginRepoID = window.draftReview ? window.draftReview.config.draftOriginRepoID: '';
+export const draftFileName = window.draftReview ? window.draftReview.config.draftFileName: '';
+export const reviewID = window.draftReview ? window.draftReview.config.reviewID : '';
+export const opStatus = window.draftReview ? window.draftReview.config.opStatus : '';
+export const publishFileVersion = window.draftReview ? window.draftReview.config.publishFileVersion : '';
+export const originFileVersion = window.draftReview ? window.draftReview.config.originFileVersion : '';
diff --git a/frontend/src/utils/editor-utilties.js b/frontend/src/utils/editor-utilties.js
index d485fad809..ec86d17f3b 100644
--- a/frontend/src/utils/editor-utilties.js
+++ b/frontend/src/utils/editor-utilties.js
@@ -119,6 +119,14 @@ class EditorUtilities {
return seafileAPI.cancelZipTask(zip_token);
}
+ createDraftReview(id) {
+ return seafileAPI.createDraftReview(id);
+ }
+
+ listReviews() {
+ return seafileAPI.listReviews();
+ }
+
}
const editorUtilities = new EditorUtilities();
diff --git a/media/css/seahub.css b/media/css/seahub.css
index e659cebfe6..39d0b779e4 100644
--- a/media/css/seahub.css
+++ b/media/css/seahub.css
@@ -200,6 +200,7 @@
.sf2-icon-plus2:before { content: "\e033"; }
.sf2-icon-upload:before { content: "\e034"; }
.sf2-icon-x3:before { content: "\e035"; }
+.sf2-icon-two-columns:before { content:"\e036"; }
/******* tags **********/
html, body {
@@ -4184,7 +4185,7 @@ img.thumbnail {
margin-left:15px;
font-size: 0;
}
-.wiki-view-icon-btn,
+.two-columns-view-icon-btn,
.grid-view-icon-btn,
.list-view-icon-btn {
display:inline-block;
@@ -4203,7 +4204,7 @@ img.thumbnail {
margin-left: 0;
}
-.wiki-view-icon-btn.active,
+.two-columns-view-icon-btn.active,
.grid-view-icon-btn.active,
.list-view-icon-btn.active {
color:#fff;
diff --git a/media/css/seahub_react.css b/media/css/seahub_react.css
index 264ae04a3c..6ba3b89e91 100644
--- a/media/css/seahub_react.css
+++ b/media/css/seahub_react.css
@@ -27,7 +27,7 @@
* empty-tip
* more-btn
* list-operation
- *
+ * prompt-message
*/
/****** sf2-icon-xx ********/
@font-face {
@@ -73,6 +73,7 @@
.sf2-icon-download:before { content:"\e008"; }
.sf2-icon-delete:before { content:"\e006"; }
.sf2-icon-caret-down:before { content:"\e01a"; }
+.sf2-icon-two-columns:before { content:"\e036"; }
/* common class and element style*/
a { color:#eb8205; }
@@ -234,9 +235,11 @@ justify-content: flex-end;
color:#d6d6d6;
cursor:pointer;
}
+
#account .avatar {
vertical-align:middle;
border-radius:1000px;
+ margin-right: 0.25rem;
}
@media (max-width:767px) {
@@ -881,3 +884,32 @@ a.op-icon:focus {
text-decoration: none;
}
/* end operaton menu */
+
+.cur-view-path .tab-tabs-nav {
+ font-size:15px;
+ list-style: none;
+}
+.tab-tabs-nav .tab {
+ float:left;
+ border:0;
+ background:none;
+}
+.tab-tabs-nav .tab .a {
+ color:#8A948F;
+ font-weight:normal;
+ padding:.3em 0;
+ margin-right:0.6em;
+}
+.tab-tabs-nav .ui-state-active .a {
+ color:#DD4B39;
+ border-bottom:2px solid #DD4B39;
+}
+.tab-tabs-nav .tab .a:hover {
+ color:#DD4B39;
+}
+.cur-view-path .tab-tabs-nav .ui-state-active .a,
+.cur-view-path .tab-tabs-nav .a:hover {
+ color:#eb8205;
+ text-decoration:none;
+ border-bottom-color:#eb8205;
+}
diff --git a/media/css/sf_font2/seafile-font2.eot b/media/css/sf_font2/seafile-font2.eot
index 766a7e180a..9657d0f92e 100644
Binary files a/media/css/sf_font2/seafile-font2.eot and b/media/css/sf_font2/seafile-font2.eot differ
diff --git a/media/css/sf_font2/seafile-font2.svg b/media/css/sf_font2/seafile-font2.svg
index cbf6166c02..cb94b247c5 100644
--- a/media/css/sf_font2/seafile-font2.svg
+++ b/media/css/sf_font2/seafile-font2.svg
@@ -13,33 +13,35 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
@@ -48,17 +50,16 @@
-
+
-
-
+
diff --git a/media/css/sf_font2/seafile-font2.ttf b/media/css/sf_font2/seafile-font2.ttf
index c0e5748d7f..c1e543a211 100644
Binary files a/media/css/sf_font2/seafile-font2.ttf and b/media/css/sf_font2/seafile-font2.ttf differ
diff --git a/media/css/sf_font2/seafile-font2.woff b/media/css/sf_font2/seafile-font2.woff
index ba90fc93de..de0cd1871c 100644
Binary files a/media/css/sf_font2/seafile-font2.woff and b/media/css/sf_font2/seafile-font2.woff differ
diff --git a/seahub/api2/endpoints/draft_reviews.py b/seahub/api2/endpoints/draft_reviews.py
new file mode 100644
index 0000000000..9b1b35adbe
--- /dev/null
+++ b/seahub/api2/endpoints/draft_reviews.py
@@ -0,0 +1,99 @@
+# Copyright (c) 2012-2016 Seafile Ltd.
+from rest_framework import status
+from rest_framework.authentication import SessionAuthentication
+from rest_framework.permissions import IsAuthenticated
+from rest_framework.response import Response
+from rest_framework.views import APIView
+
+from django.db import IntegrityError
+
+from seaserv import seafile_api
+from seahub.api2.authentication import TokenAuthentication
+from seahub.api2.throttling import UserRateThrottle
+from seahub.api2.utils import api_error
+
+from seahub.drafts.models import Draft, DraftReview, DraftReviewExist, \
+ DraftFileConflict
+
+
+class DraftReviewsView(APIView):
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ permission_classes = (IsAuthenticated, )
+ throttle_classes = (UserRateThrottle, )
+
+ def get(self, request, format=None):
+ """List all user draft review
+ """
+ username = request.user.username
+ data = [x.to_dict() for x in DraftReview.objects.filter(creator=username)]
+
+ return Response({'data': data})
+
+
+ def post(self, request, format=None):
+ """Create a draft review
+ """
+ draft_id = request.data.get('draft_id', '')
+ try:
+ d = Draft.objects.get(pk=draft_id)
+ except Draft.DoesNotExist:
+ return api_error(status.HTTP_404_NOT_FOUND,
+ 'Draft %s not found.' % draft_id)
+
+ # perm check
+ if d.username != request.user.username:
+ return api_error(status.HTTP_403_FORBIDDEN,
+ 'Permission denied.')
+ try:
+ d_r = DraftReview.objects.add(creator=d.username, draft=d)
+ except (DraftReviewExist):
+ return api_error(status.HTTP_409_CONFLICT, 'Draft review already exists.')
+
+ return Response(d_r.to_dict())
+
+
+class DraftReviewView(APIView):
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ permission_classes = (IsAuthenticated, )
+ throttle_classes = (UserRateThrottle, )
+
+ def put(self, request, pk, format=None):
+ """update review status
+ """
+
+ st = request.data.get('status', '')
+ if not st:
+ return api_error(status.HTTP_400_BAD_REQUEST,
+ 'Status %s invalid.')
+
+ try:
+ r = DraftReview.objects.get(pk=pk)
+ except DraftReview.DoesNotExist:
+ return api_error(status.HTTP_404_NOT_FOUND,
+ 'Review %s not found' % pk)
+
+ r.status = st
+ r.save()
+
+ if st == 'finished':
+
+ try:
+ d = Draft.objects.get(pk=r.draft_id_id)
+ except Draft.DoesNotExist:
+ return api_error(status.HTTP_404_NOT_FOUND,
+ 'Draft %s not found.' % pk)
+
+ try:
+ d.publish()
+ except (DraftFileConflict, IntegrityError):
+ return api_error(status.HTTP_409_CONFLICT,
+ 'There is a conflict between the draft and the original file')
+
+ file_id = seafile_api.get_file_id_by_path(r.origin_repo_id, r.origin_file_path)
+ r.publish_file_version = file_id
+ r.save()
+ d.delete()
+
+ result = r.to_dict()
+
+ return Response(result)
diff --git a/seahub/api2/endpoints/drafts.py b/seahub/api2/endpoints/drafts.py
index 01ab0a044d..38139baa0a 100644
--- a/seahub/api2/endpoints/drafts.py
+++ b/seahub/api2/endpoints/drafts.py
@@ -1,6 +1,8 @@
# Copyright (c) 2012-2016 Seafile Ltd.
+import os
import json
import logging
+import posixpath
from rest_framework import status
from rest_framework.authentication import SessionAuthentication
@@ -22,9 +24,12 @@ from seahub.api2.utils import api_error
from seahub.constants import PERMISSION_READ_WRITE
from seahub.drafts.models import Draft, DraftFileExist, DraftFileConflict
from seahub.views import check_folder_permission
+from seahub.utils import gen_file_get_url
logger = logging.getLogger(__name__)
+HTTP_520_OPERATION_FAILED = 520
+
class DraftsView(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
@@ -65,7 +70,7 @@ class DraftsView(APIView):
username = request.user.username
try:
- d = Draft.objects.add(username, repo, file_path, file_id, org_id=org_id)
+ d = Draft.objects.add(username, repo, file_path, file_id)
return Response(d.to_dict())
except (DraftFileExist, IntegrityError):
@@ -98,6 +103,7 @@ class DraftView(APIView):
try:
d.publish()
+ d.delete()
return Response(status.HTTP_200_OK)
except (DraftFileConflict, IntegrityError):
return api_error(status.HTTP_409_CONFLICT,
diff --git a/seahub/drafts/migrations/0001_initial.py b/seahub/drafts/migrations/0001_initial.py
index c659e3d712..ec36561533 100644
--- a/seahub/drafts/migrations/0001_initial.py
+++ b/seahub/drafts/migrations/0001_initial.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Generated by Django 1.11.11 on 2018-09-03 08:34
+# Generated by Django 1.11.15 on 2018-10-11 06:12
from __future__ import unicode_literals
from django.db import migrations, models
@@ -25,13 +25,32 @@ class Migration(migrations.Migration):
('username', seahub.base.fields.LowerCaseCharField(db_index=True, max_length=255)),
('origin_repo_id', models.CharField(max_length=36)),
('origin_file_version', models.CharField(max_length=100)),
- ('draft_repo_id', models.CharField(max_length=36)),
- ('draft_file_path', models.CharField(max_length=2048)),
+ ('draft_file_path', models.CharField(max_length=1024)),
('origin_file_uuid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tags.FileUUIDMap')),
],
+ options={
+ 'ordering': ['-created_at', '-updated_at'],
+ 'abstract': False,
+ },
),
- migrations.AlterUniqueTogether(
- name='draft',
- unique_together=set([('username', 'draft_repo_id')]),
+ migrations.CreateModel(
+ name='DraftReview',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
+ ('updated_at', models.DateTimeField(auto_now=True, db_index=True)),
+ ('creator', seahub.base.fields.LowerCaseCharField(db_index=True, max_length=255)),
+ ('status', models.CharField(max_length=20)),
+ ('origin_repo_id', models.CharField(max_length=36)),
+ ('origin_file_path', models.CharField(max_length=1024)),
+ ('draft_file_path', models.CharField(max_length=1024)),
+ ('origin_file_version', models.CharField(max_length=100)),
+ ('publish_file_version', models.CharField(max_length=100, null=True)),
+ ('draft_id', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='drafts.Draft')),
+ ],
+ options={
+ 'ordering': ['-created_at', '-updated_at'],
+ 'abstract': False,
+ },
),
]
diff --git a/seahub/drafts/migrations/0002_auto_20180915_0612.py b/seahub/drafts/migrations/0002_auto_20180915_0612.py
deleted file mode 100644
index 748c7e0e9f..0000000000
--- a/seahub/drafts/migrations/0002_auto_20180915_0612.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11.11 on 2018-09-15 06:12
-from __future__ import unicode_literals
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('drafts', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='draft',
- options={'ordering': ['-created_at', '-updated_at']},
- ),
- migrations.AlterUniqueTogether(
- name='draft',
- unique_together=set([]),
- ),
- ]
diff --git a/seahub/drafts/migrations/0002_auto_20181011_1207.py b/seahub/drafts/migrations/0002_auto_20181011_1207.py
new file mode 100644
index 0000000000..3e76c372f9
--- /dev/null
+++ b/seahub/drafts/migrations/0002_auto_20181011_1207.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.15 on 2018-10-11 12:07
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('drafts', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='draftreview',
+ name='draft_id',
+ field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='drafts.Draft'),
+ ),
+ ]
diff --git a/seahub/drafts/models.py b/seahub/drafts/models.py
index 7a1273c67d..c8a70f5d99 100644
--- a/seahub/drafts/models.py
+++ b/seahub/drafts/models.py
@@ -41,27 +41,24 @@ class DraftManager(models.Manager):
if file_id is None:
file_id = seafile_api.get_file_id_by_path(repo.id, file_path)
- # create draft repo if any
- draft_repo_id = self.get_user_draft_repo_id(username)
- if draft_repo_id is None:
- draft_repo_id = create_user_draft_repo(username)
+ # create drafts dir if any
+ draft_dir_id = seafile_api.get_dir_id_by_path(repo.id, '/Drafts')
+ if draft_dir_id is None:
+ seafile_api.post_dir(repo.id, '/', 'Drafts', username)
# check draft file does not exists and copy origin file content to
# draft file
draft_file_name = get_draft_file_name(repo.id, file_path)
- draft_file_path = '/' + draft_file_name
-
- if seafile_api.get_file_id_by_path(draft_repo_id, draft_file_path):
- raise DraftFileExist
+ draft_file_path = '/Drafts/' + draft_file_name
+ # copy file to draft dir
seafile_api.copy_file(repo.id, file_uuid.parent_path, file_uuid.filename,
- draft_repo_id, '/', draft_file_name,
+ repo.id, '/Drafts', draft_file_name,
username=username, need_progress=0, synchronous=1)
draft = self.model(username=username,
origin_repo_id=repo.id, origin_file_uuid=file_uuid,
origin_file_version=file_id,
- draft_repo_id=draft_repo_id,
draft_file_path=draft_file_path)
draft.save(using=self._db)
return draft
@@ -74,8 +71,7 @@ class Draft(TimestampedModel):
origin_repo_id = models.CharField(max_length=36)
origin_file_uuid = models.ForeignKey(FileUUIDMap, on_delete=models.CASCADE)
origin_file_version = models.CharField(max_length=100)
- draft_repo_id = models.CharField(max_length=36)
- draft_file_path = models.CharField(max_length=2048)
+ draft_file_path = models.CharField(max_length=1024)
objects = DraftManager()
@@ -83,8 +79,9 @@ class Draft(TimestampedModel):
# unique_together = (('username', 'draft_repo_id'), )
def delete(self):
- seafile_api.del_file(self.draft_repo_id, '/',
- self.draft_file_path.lstrip('/'), self.username)
+ draft_file_name = os.path.basename(self.draft_file_path)
+ seafile_api.del_file(self.origin_repo_id, '/Drafts/',
+ draft_file_name, self.username)
super(Draft, self).delete()
@@ -94,7 +91,11 @@ class Draft(TimestampedModel):
if not r_repo:
raise DraftFileConflict
- origin_file_path = self.origin_file_uuid.parent_path + self.origin_file_uuid.filename
+ if self.origin_file_uuid.parent_path == '/':
+ origin_file_path = self.origin_file_uuid.parent_path + self.origin_file_uuid.filename
+ else:
+ origin_file_path = self.origin_file_uuid.parent_path + '/' + self.origin_file_uuid.filename
+
file_id = seafile_api.get_file_id_by_path(self.origin_repo_id,
origin_file_path)
if not file_id:
@@ -103,28 +104,97 @@ class Draft(TimestampedModel):
if file_id != self.origin_file_version:
raise DraftFileConflict
+ draft_file_name = os.path.basename(self.draft_file_path)
# move draft file to origin file
seafile_api.move_file(
- self.draft_repo_id, '/', self.draft_file_path.lstrip('/'),
+ self.origin_repo_id, '/Drafts', draft_file_name,
self.origin_repo_id, self.origin_file_uuid.parent_path,
self.origin_file_uuid.filename, replace=1,
username=self.username, need_progress=0, synchronous=1
)
- self.delete()
def to_dict(self):
uuid = self.origin_file_uuid
file_path = posixpath.join(uuid.parent_path, uuid.filename) # TODO: refactor uuid
+ repo = seafile_api.get_repo(self.origin_repo_id)
+
+ review_id = None
+ review_status = None
+ if hasattr(self, 'draftreview'):
+ review_id = self.draftreview.id
+ review_status = self.draftreview.status
+
return {
'id': self.pk,
+ 'review_id': review_id,
+ 'review_status': review_status,
'owner': self.username,
+ 'repo_name': repo.name,
'owner_nickname': email2nickname(self.username),
'origin_repo_id': self.origin_repo_id,
'origin_file_path': file_path,
'origin_file_version': self.origin_file_version,
- 'draft_repo_id': self.draft_repo_id,
+ 'draft_file_path': self.draft_file_path,
+ 'created_at': datetime_to_isoformat_timestr(self.created_at),
+ 'updated_at': datetime_to_isoformat_timestr(self.updated_at),
+ }
+
+
+class DraftReviewExist(Exception):
+ pass
+
+
+class DraftReviewManager(models.Manager):
+ def add(self, creator, draft):
+
+ has_review = hasattr(draft, 'draftreview')
+ if has_review:
+ raise DraftReviewExist
+
+ uuid = draft.origin_file_uuid
+ file_path = posixpath.join(uuid.parent_path, uuid.filename)
+
+ draft_review = self.model(creator=creator,
+ status='open',
+ draft_id=draft,
+ origin_repo_id=draft.origin_repo_id,
+ origin_file_path=file_path,
+ draft_file_path=draft.draft_file_path,
+ origin_file_version=draft.origin_file_version)
+ draft_review.save(using=self._db)
+
+ return draft_review
+
+
+class DraftReview(TimestampedModel):
+ creator = LowerCaseCharField(max_length=255, db_index=True)
+ status = models.CharField(max_length=20)
+ origin_repo_id = models.CharField(max_length=36)
+ origin_file_path = models.CharField(max_length=1024)
+ draft_file_path = models.CharField(max_length=1024)
+ origin_file_version = models.CharField(max_length=100)
+ publish_file_version = models.CharField(max_length=100, null=True)
+ draft_id = models.OneToOneField(Draft, null=True, on_delete=models.SET_NULL)
+
+ objects = DraftReviewManager()
+
+ def to_dict(self):
+ r_repo = seafile_api.get_repo(self.origin_repo_id)
+ if not r_repo:
+ raise DraftFileConflict
+
+ return {
+ 'id': self.pk,
+ 'creator': self.creator,
+ 'status': self.status,
+ 'creator_name': email2nickname(self.creator),
+ 'draft_origin_repo_id': self.origin_repo_id,
+ 'draft_origin_repo_name': r_repo.name,
+ 'draft_origin_file_path': self.origin_file_path,
+ 'draft_origin_file_version': self.origin_file_version,
+ 'draft_publish_file_version': self.publish_file_version,
'draft_file_path': self.draft_file_path,
'created_at': datetime_to_isoformat_timestr(self.created_at),
'updated_at': datetime_to_isoformat_timestr(self.updated_at),
diff --git a/seahub/drafts/urls.py b/seahub/drafts/urls.py
new file mode 100644
index 0000000000..4f3218b1eb
--- /dev/null
+++ b/seahub/drafts/urls.py
@@ -0,0 +1,10 @@
+# Copyright (c) 2012-2016 Seafile Ltd.
+from django.conf.urls import url
+
+from .views import review, drafts, reviews
+
+urlpatterns = [
+ url(r'^$', drafts, name='drafts'),
+ url(r'^reviews/$', reviews, name='reviews'),
+ url(r'^review/(?P\d+)/$', review, name='review'),
+]
diff --git a/seahub/drafts/utils.py b/seahub/drafts/utils.py
index cfe5973281..13109b3d3c 100644
--- a/seahub/drafts/utils.py
+++ b/seahub/drafts/utils.py
@@ -3,7 +3,7 @@ import os
from seaserv import seafile_api
-from seahub.utils import normalize_file_path
+from seahub.utils import normalize_file_path, check_filename_with_rename
def create_user_draft_repo(username, org_id=-1):
repo_name = 'Drafts'
@@ -18,6 +18,9 @@ def create_user_draft_repo(username, org_id=-1):
def get_draft_file_name(repo_id, file_path):
file_path = normalize_file_path(file_path)
file_name, file_ext = os.path.splitext(os.path.basename(file_path))
- md5 = hashlib.md5((repo_id + file_path).encode('utf-8')).hexdigest()[:10]
+ # md5 = hashlib.md5((repo_id + file_path).encode('utf-8')).hexdigest()[:10]
- return "%s-%s%s" % (file_name, md5, file_ext)
+ draft_file_name = "%s%s%s" % (file_name, '(draft)', file_ext)
+ new_file_name = check_filename_with_rename(repo_id, '/Drafts', draft_file_name)
+
+ return new_file_name
diff --git a/seahub/drafts/views.py b/seahub/drafts/views.py
new file mode 100644
index 0000000000..9420e89143
--- /dev/null
+++ b/seahub/drafts/views.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+import os
+
+from django.shortcuts import render, get_object_or_404
+from django.utils.translation import ugettext as _
+
+from seahub.auth.decorators import login_required
+from seahub.views import check_folder_permission
+from seahub.utils import render_permission_error
+from seahub.drafts.models import Draft, DraftReview
+
+@login_required
+def drafts(request):
+ return render(request, "react_app.html")
+
+
+@login_required
+def reviews(request):
+ return render(request, "react_app.html")
+
+
+@login_required
+def review(request, pk):
+ d_r = get_object_or_404(DraftReview, pk=pk)
+
+ # check perm
+ file_path = d_r.origin_file_path
+ origin_repo_id = d_r.origin_repo_id
+
+ if request.user.username:
+ permission = check_folder_permission(request, origin_repo_id, file_path)
+
+ if permission is None:
+ return render_permission_error(request, _(u'Permission denied.'))
+
+ draft_file_name = os.path.basename(d_r.draft_file_path)
+
+ return render(request, "draft_review.html", {
+ "review_id": pk,
+ "draft_repo_id": d_r.origin_repo_id,
+ "draft_origin_repo_id": d_r.origin_repo_id,
+ "draft_origin_file_path": d_r.origin_file_path,
+ "draft_file_path": d_r.draft_file_path,
+ "draft_file_name": draft_file_name,
+ "origin_file_version": d_r.origin_file_version,
+ "publish_file_version": d_r.publish_file_version,
+ "status": d_r.status
+ })
diff --git a/seahub/templates/draft_review.html b/seahub/templates/draft_review.html
new file mode 100644
index 0000000000..5c31e76eee
--- /dev/null
+++ b/seahub/templates/draft_review.html
@@ -0,0 +1,23 @@
+{% extends "base_for_react.html" %}
+{% load render_bundle from webpack_loader %}
+
+{% block extra_script %}
+
+
+{% render_bundle 'draftReview' %}
+{% endblock %}
diff --git a/seahub/templates/js/templates.html b/seahub/templates/js/templates.html
index c8132a0fb1..ca56ec196a 100644
--- a/seahub/templates/js/templates.html
+++ b/seahub/templates/js/templates.html
@@ -498,7 +498,7 @@
<% if (app.pageOptions.enable_repo_wiki_mode) { %>
-
+
<% } %>
diff --git a/seahub/templates/view_file_markdown.html b/seahub/templates/view_file_markdown.html
index 3e8ae22b9e..4606722f88 100644
--- a/seahub/templates/view_file_markdown.html
+++ b/seahub/templates/view_file_markdown.html
@@ -26,6 +26,7 @@
protocol: '{{ protocol }}',
lang: '{{ language_code }}',
mode: '{{ mode }}',
+ draftID: '{{ draft_id }}',
},
userInfo: {
username: '{{ user.username }}',
diff --git a/seahub/urls.py b/seahub/urls.py
index 45c7367f1e..515ceabd2f 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -67,6 +67,7 @@ from seahub.api2.endpoints.repo_file_uploaded_bytes import RepoFileUploadedBytes
from seahub.api2.endpoints.user_avatar import UserAvatarView
from seahub.api2.endpoints.wikis import WikisView, WikiView
from seahub.api2.endpoints.drafts import DraftsView, DraftView
+from seahub.api2.endpoints.draft_reviews import DraftReviewsView, DraftReviewView
from seahub.api2.endpoints.activities import ActivitiesView
from seahub.api2.endpoints.wiki_pages import WikiPageView, WikiPagesView, WikiPagesDirView, WikiPageContentView
from seahub.api2.endpoints.revision_tag import TaggedItemsView, TagNamesView
@@ -185,7 +186,6 @@ urlpatterns = [
### React ###
url(r'^dashboard/$', TemplateView.as_view(template_name="react_app.html"), name="dashboard"),
- url(r'^drafts/$', TemplateView.as_view(template_name="react_app.html"), name="drafts"),
url(r'^starred/$', TemplateView.as_view(template_name="react_app.html"), name="starred"),
### Ajax ###
@@ -326,6 +326,11 @@ urlpatterns = [
url(r'^api/v2.1/drafts/$', DraftsView.as_view(), name='api-v2.1-drafts'),
url(r'^api/v2.1/drafts/(?P\d+)/$', DraftView.as_view(), name='api-v2.1-draft'),
+
+ ## user::reviews
+ url(r'^api/v2.1/reviews/$', DraftReviewsView.as_view(), name='api-v2.1-draft-reviews'),
+ url(r'^api/v2.1/review/(?P\d+)/$', DraftReviewView.as_view(), name='api-v2.1-draft-review'),
+
## user::activities
url(r'^api/v2.1/activities/$', ActivitiesView.as_view(), name='api-v2.1-acitvity'),
@@ -438,6 +443,7 @@ urlpatterns = [
url(r'^invite/', include('seahub.invitations.urls', app_name='invitations', namespace='invitations')),
url(r'^terms/', include('termsandconditions.urls')),
url(r'^wikis/', include('seahub.wiki.urls', app_name='wiki', namespace='wiki')),
+ url(r'^drafts/', include('seahub.drafts.urls', app_name='drafts', namespace='drafts')),
## admin::address book
url(r'^api/v2.1/admin/address-book/groups/$', AdminAddressBookGroups.as_view(), name='api-v2.1-admin-address-book-groups'),
diff --git a/seahub/views/file.py b/seahub/views/file.py
index 1a15961716..d1945e5b0a 100644
--- a/seahub/views/file.py
+++ b/seahub/views/file.py
@@ -606,6 +606,7 @@ def view_lib_file(request, repo_id, path):
return_dict['file_encoding_list'] = file_encoding_list
mode = request.GET.get('mode', '')
+ draft_id = request.GET.get('draft_id', '')
if filetype == MARKDOWN:
return_dict['protocol'] = request.is_secure() and 'https' or 'http'
@@ -615,6 +616,7 @@ def view_lib_file(request, repo_id, path):
return_dict['language_code'] = get_language()
return_dict['seafile_collab_server'] = SEAFILE_COLLAB_SERVER
return_dict['mode'] = 'edit' if mode else 'viewer'
+ return_dict['draft_id'] = draft_id
else:
return_dict['file_content'] = file_content
diff --git a/static/scripts/app/views/dirent.js b/static/scripts/app/views/dirent.js
index 6578b784ac..0b8c5f83bd 100644
--- a/static/scripts/app/views/dirent.js
+++ b/static/scripts/app/views/dirent.js
@@ -623,9 +623,10 @@ define([
beforeSend: Common.prepareCSRFToken,
success: function(res) {
var siteRoot = window.app.config.siteRoot;
- var repoID = res.draft_repo_id;
+ var repoID = res.origin_repo_id;
var filePath = res.draft_file_path;
- window.location.href= siteRoot + 'lib/' + repoID + '/file' + filePath + '?mode=edit';
+ var draftID = res.id;
+ window.location.href= siteRoot + 'lib/' + repoID + '/file' + filePath + '?mode=edit&draft_id=' + draftID;
},
error: function() {
var err_msg = gettext("The draft already exists.");
diff --git a/tests/api/endpoints/test_drafts.py b/tests/api/endpoints/test_drafts.py
index fcf7718d64..4e4b1cfd79 100644
--- a/tests/api/endpoints/test_drafts.py
+++ b/tests/api/endpoints/test_drafts.py
@@ -45,7 +45,7 @@ class DraftsViewTest(BaseTestCase):
'file_path': self.file,
})
- self.assertEqual(409, resp.status_code)
+ self.assertEqual(200, resp.status_code)
class DraftViewTest(BaseTestCase):
diff --git a/tests/seahub/drafts/test_models.py b/tests/seahub/drafts/test_models.py
index 462ce1bf7c..8023632aba 100644
--- a/tests/seahub/drafts/test_models.py
+++ b/tests/seahub/drafts/test_models.py
@@ -15,7 +15,6 @@ class DraftManagerTest(BaseTestCase):
d = draft.to_dict()
assert d['origin_repo_id'] == self.repo.id
assert d['origin_file_path'] == self.file
- assert len(d['draft_repo_id']) == 36
assert len(d['draft_file_path']) > 0
def test_add_another_file(self):
@@ -23,29 +22,15 @@ class DraftManagerTest(BaseTestCase):
parent_dir='/',
filename='test2.txt',
username=self.user.username)
- assert Draft.objects.get_user_draft_repo_id(self.user.username) is None
assert len(Draft.objects.all()) == 0
draft = Draft.objects.add(self.user.username, self.repo, self.file)
assert draft is not None
assert len(Draft.objects.all()) == 1
- draft_repo = Draft.objects.get_user_draft_repo_id(self.user.username)
- assert draft_repo is not None
draft2 = Draft.objects.add(self.user.username, self.repo, file2)
assert draft2 is not None
assert len(Draft.objects.all()) == 2
- assert draft_repo == Draft.objects.get_user_draft_repo_id(self.user.username)
-
- def test_add_same_file(self):
- draft = Draft.objects.add(self.user.username, self.repo, self.file)
- assert draft is not None
-
- try:
- Draft.objects.add(self.user.username, self.repo, self.file)
- assert False
- except DraftFileExist:
- assert True
class DraftTest(BaseTestCase):
@@ -55,27 +40,23 @@ class DraftTest(BaseTestCase):
assert d is not None
assert len(Draft.objects.all()) == 1
- assert seafile_api.get_file_id_by_path(d.draft_repo_id, d.draft_file_path) is not None
+ assert seafile_api.get_file_id_by_path(d.origin_repo_id, d.draft_file_path) is not None
d = Draft.objects.all()[0]
d.delete()
assert len(Draft.objects.all()) == 0
- assert seafile_api.get_file_id_by_path(d.draft_repo_id, d.draft_file_path) is None
+ assert seafile_api.get_file_id_by_path(d.origin_repo_id, d.draft_file_path) is None
def test_publish(self):
assert len(Draft.objects.all()) == 0
d = Draft.objects.add(self.user.username, self.repo, self.file)
assert d is not None
assert len(Draft.objects.all()) == 1
- assert seafile_api.get_file_id_by_path(d.draft_repo_id, d.draft_file_path) is not None
- assert len(seafile_api.list_dir_by_path(self.repo.id, '/')) == 1
+ assert seafile_api.get_file_id_by_path(d.origin_repo_id, d.draft_file_path) is not None
+ assert len(seafile_api.list_dir_by_path(self.repo.id, '/Drafts')) == 1
d.publish()
# file is updated in origin repo
- assert len(seafile_api.list_dir_by_path(self.repo.id, '/')) == 1
-
- # draft file is delete deleted
- assert len(Draft.objects.all()) == 0
- assert seafile_api.get_file_id_by_path(d.draft_repo_id, d.draft_file_path) is None
+ assert len(seafile_api.list_dir_by_path(self.repo.id, '/')) == 2