mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-04 08:28:11 +00:00
request reviewer api (#2472)
This commit is contained in:
258
frontend/package-lock.json
generated
258
frontend/package-lock.json
generated
@@ -4,6 +4,92 @@
|
|||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/helper-module-imports": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/types": "^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@babel/types": {
|
||||||
|
"version": "7.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.3.tgz",
|
||||||
|
"integrity": "sha512-RpPOVfK+yatXyn8n4PB1NW6k9qjinrXrRR8ugBN8fD6hCy5RXI6PSbVqpOJBO9oSaY7Nom4ohj35feb0UR9hSA==",
|
||||||
|
"requires": {
|
||||||
|
"esutils": "^2.0.2",
|
||||||
|
"lodash": "^4.17.10",
|
||||||
|
"to-fast-properties": "^2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": {
|
||||||
|
"version": "4.17.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||||
|
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
|
||||||
|
},
|
||||||
|
"to-fast-properties": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@emotion/babel-utils": {
|
||||||
|
"version": "0.6.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/babel-utils/-/babel-utils-0.6.10.tgz",
|
||||||
|
"integrity": "sha512-/fnkM/LTEp3jKe++T0KyTszVGWNKPNOUJfjNKLO17BzQ6QPxgbg3whayom1Qr2oLFH3V92tDymU+dT5q676uow==",
|
||||||
|
"requires": {
|
||||||
|
"@emotion/hash": "^0.6.6",
|
||||||
|
"@emotion/memoize": "^0.6.6",
|
||||||
|
"@emotion/serialize": "^0.9.1",
|
||||||
|
"convert-source-map": "^1.5.1",
|
||||||
|
"find-root": "^1.1.0",
|
||||||
|
"source-map": "^0.7.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||||
|
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@emotion/hash": {
|
||||||
|
"version": "0.6.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.6.6.tgz",
|
||||||
|
"integrity": "sha512-ojhgxzUHZ7am3D2jHkMzPpsBAiB005GF5YU4ea+8DNPybMk01JJUM9V9YRlF/GE95tcOm8DxQvWA2jq19bGalQ=="
|
||||||
|
},
|
||||||
|
"@emotion/memoize": {
|
||||||
|
"version": "0.6.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.6.tgz",
|
||||||
|
"integrity": "sha512-h4t4jFjtm1YV7UirAFuSuFGyLa+NNxjdkq6DpFLANNQY5rHueFZHVY+8Cu1HYVP6DrheB0kv4m5xPjo7eKT7yQ=="
|
||||||
|
},
|
||||||
|
"@emotion/serialize": {
|
||||||
|
"version": "0.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.9.1.tgz",
|
||||||
|
"integrity": "sha512-zTuAFtyPvCctHBEL8KZ5lJuwBanGSutFEncqLn/m9T1a6a93smBStK+bZzcNPgj4QS8Rkw9VTwJGhRIUVO8zsQ==",
|
||||||
|
"requires": {
|
||||||
|
"@emotion/hash": "^0.6.6",
|
||||||
|
"@emotion/memoize": "^0.6.6",
|
||||||
|
"@emotion/unitless": "^0.6.7",
|
||||||
|
"@emotion/utils": "^0.8.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@emotion/stylis": {
|
||||||
|
"version": "0.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.7.1.tgz",
|
||||||
|
"integrity": "sha512-/SLmSIkN13M//53TtNxgxo57mcJk/UJIDFRKwOiLIBEyBHEcipgR6hNMQ/59Sl4VjCJ0Z/3zeAZyvnSLPG/1HQ=="
|
||||||
|
},
|
||||||
|
"@emotion/unitless": {
|
||||||
|
"version": "0.6.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.6.7.tgz",
|
||||||
|
"integrity": "sha512-Arj1hncvEVqQ2p7Ega08uHLr1JuRYBuO5cIvcA+WWEQ5+VmkOE3ZXzl04NbQxeQpWX78G7u6MqxKuNX3wvYZxg=="
|
||||||
|
},
|
||||||
|
"@emotion/utils": {
|
||||||
|
"version": "0.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.8.2.tgz",
|
||||||
|
"integrity": "sha512-rLu3wcBWH4P5q1CGoSSH/i9hrXs7SlbRLkoq9IGuoPYNGQvDJ3pt/wmOM+XgYjIDRMVIdkUWt0RsfzF50JfnCw=="
|
||||||
|
},
|
||||||
"@reach/router": {
|
"@reach/router": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@reach/router/-/router-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@reach/router/-/router-1.2.1.tgz",
|
||||||
@@ -172,6 +258,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
|
||||||
"integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4="
|
"integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4="
|
||||||
},
|
},
|
||||||
|
"abbrev": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||||
|
},
|
||||||
"accepts": {
|
"accepts": {
|
||||||
"version": "1.3.4",
|
"version": "1.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz",
|
||||||
@@ -869,6 +960,32 @@
|
|||||||
"babel-types": "^6.26.0"
|
"babel-types": "^6.26.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"babel-plugin-emotion": {
|
||||||
|
"version": "9.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-9.2.11.tgz",
|
||||||
|
"integrity": "sha512-dgCImifnOPPSeXod2znAmgc64NhaaOjGEHROR/M+lmStb3841yK1sgaDYAYMnlvWNz8GnpwIPN0VmNpbWYZ+VQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/helper-module-imports": "^7.0.0",
|
||||||
|
"@emotion/babel-utils": "^0.6.4",
|
||||||
|
"@emotion/hash": "^0.6.2",
|
||||||
|
"@emotion/memoize": "^0.6.1",
|
||||||
|
"@emotion/stylis": "^0.7.0",
|
||||||
|
"babel-plugin-macros": "^2.0.0",
|
||||||
|
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||||
|
"convert-source-map": "^1.5.0",
|
||||||
|
"find-root": "^1.1.0",
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
|
"source-map": "^0.5.7",
|
||||||
|
"touch": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.5.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
|
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"babel-plugin-istanbul": {
|
"babel-plugin-istanbul": {
|
||||||
"version": "4.1.5",
|
"version": "4.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.5.tgz",
|
||||||
@@ -884,6 +1001,58 @@
|
|||||||
"resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz",
|
||||||
"integrity": "sha1-r+3IU70/jcNUjqZx++adA8wsF2c="
|
"integrity": "sha1-r+3IU70/jcNUjqZx++adA8wsF2c="
|
||||||
},
|
},
|
||||||
|
"babel-plugin-macros": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-NBVpEWN4OQ/bHnu1fyDaAaTPAjnhXCEPqr1RwqxrU7b6tZ2hypp+zX4hlNfmVGfClD5c3Sl6Hfj5TJNF5VG5aA==",
|
||||||
|
"requires": {
|
||||||
|
"cosmiconfig": "^5.0.5",
|
||||||
|
"resolve": "^1.8.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cosmiconfig": {
|
||||||
|
"version": "5.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.6.tgz",
|
||||||
|
"integrity": "sha512-6DWfizHriCrFWURP1/qyhsiFvYdlJzbCzmtFWh744+KyWsJo5+kPzUZZaMRSSItoYc0pxFX7gEO7ZC1/gN/7AQ==",
|
||||||
|
"requires": {
|
||||||
|
"is-directory": "^0.3.1",
|
||||||
|
"js-yaml": "^3.9.0",
|
||||||
|
"parse-json": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"esprima": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||||
|
},
|
||||||
|
"js-yaml": {
|
||||||
|
"version": "3.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
|
||||||
|
"integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
|
||||||
|
"requires": {
|
||||||
|
"argparse": "^1.0.7",
|
||||||
|
"esprima": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parse-json": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
|
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
|
||||||
|
"requires": {
|
||||||
|
"error-ex": "^1.3.1",
|
||||||
|
"json-parse-better-errors": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resolve": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
|
||||||
|
"requires": {
|
||||||
|
"path-parse": "^1.0.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"babel-plugin-react-transform": {
|
"babel-plugin-react-transform": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-react-transform/-/babel-plugin-react-transform-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/babel-plugin-react-transform/-/babel-plugin-react-transform-2.0.2.tgz",
|
||||||
@@ -962,8 +1131,7 @@
|
|||||||
"babel-plugin-syntax-jsx": {
|
"babel-plugin-syntax-jsx": {
|
||||||
"version": "6.18.0",
|
"version": "6.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
|
||||||
"integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=",
|
"integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"babel-plugin-syntax-object-rest-spread": {
|
"babel-plugin-syntax-object-rest-spread": {
|
||||||
"version": "6.13.0",
|
"version": "6.13.0",
|
||||||
@@ -2557,6 +2725,20 @@
|
|||||||
"elliptic": "^6.0.0"
|
"elliptic": "^6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"create-emotion": {
|
||||||
|
"version": "9.2.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/create-emotion/-/create-emotion-9.2.12.tgz",
|
||||||
|
"integrity": "sha512-P57uOF9NL2y98Xrbl2OuiDQUZ30GVmASsv5fbsjF4Hlraip2kyAvMm+2PoYUvFFw03Fhgtxk3RqZSm2/qHL9hA==",
|
||||||
|
"requires": {
|
||||||
|
"@emotion/hash": "^0.6.2",
|
||||||
|
"@emotion/memoize": "^0.6.1",
|
||||||
|
"@emotion/stylis": "^0.7.0",
|
||||||
|
"@emotion/unitless": "^0.6.2",
|
||||||
|
"csstype": "^2.5.2",
|
||||||
|
"stylis": "^3.5.0",
|
||||||
|
"stylis-rule-sheet": "^0.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"create-error-class": {
|
"create-error-class": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
|
||||||
@@ -2860,6 +3042,11 @@
|
|||||||
"cssom": "0.3.x"
|
"cssom": "0.3.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"csstype": {
|
||||||
|
"version": "2.5.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.5.7.tgz",
|
||||||
|
"integrity": "sha512-Nt5VDyOTIIV4/nRFswoCKps1R5CD1hkiyjBE9/thNaNZILLEviVw9yWQw15+O+CpNjQKB/uvdcxFFOrSflY3Yw=="
|
||||||
|
},
|
||||||
"currently-unhandled": {
|
"currently-unhandled": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
|
||||||
@@ -3240,6 +3427,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
|
||||||
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k="
|
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k="
|
||||||
},
|
},
|
||||||
|
"emotion": {
|
||||||
|
"version": "9.2.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/emotion/-/emotion-9.2.12.tgz",
|
||||||
|
"integrity": "sha512-hcx7jppaI8VoXxIWEhxpDW7I+B4kq9RNzQLmsrF6LY8BGKqe2N+gFAQr0EfuFucFlPs2A9HM4+xNj4NeqEWIOQ==",
|
||||||
|
"requires": {
|
||||||
|
"babel-plugin-emotion": "^9.2.11",
|
||||||
|
"create-emotion": "^9.2.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"encodeurl": {
|
"encodeurl": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
@@ -4185,6 +4381,11 @@
|
|||||||
"pkg-dir": "^2.0.0"
|
"pkg-dir": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"find-root": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
|
||||||
|
},
|
||||||
"find-up": {
|
"find-up": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
|
||||||
@@ -6672,6 +6873,11 @@
|
|||||||
"integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==",
|
"integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"json-parse-better-errors": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
|
||||||
|
},
|
||||||
"json-schema": {
|
"json-schema": {
|
||||||
"version": "0.2.3",
|
"version": "0.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
|
||||||
@@ -7426,6 +7632,14 @@
|
|||||||
"which": "^1.3.0"
|
"which": "^1.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nopt": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
|
||||||
|
"integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
|
||||||
|
"requires": {
|
||||||
|
"abbrev": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"normalize-package-data": {
|
"normalize-package-data": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
||||||
@@ -9400,6 +9614,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz",
|
||||||
"integrity": "sha1-Aj1vObsVyXwHHp5g0A0TbqxfoLQ="
|
"integrity": "sha1-Aj1vObsVyXwHHp5g0A0TbqxfoLQ="
|
||||||
},
|
},
|
||||||
|
"react-input-autosize": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-3+K4CD13iE4lQQ2WlF8PuV5htfmTRLH6MDnfndHM6LuBRszuXnuyIfE7nhSKt8AzRBZ50bu0sAhkNMeS5pxQQA==",
|
||||||
|
"requires": {
|
||||||
|
"prop-types": "^15.5.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-lifecycles-compat": {
|
"react-lifecycles-compat": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||||
@@ -9437,6 +9659,20 @@
|
|||||||
"babel-runtime": "^6.23.0"
|
"babel-runtime": "^6.23.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-select": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-select/-/react-select-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-ukie2LJStNfJEJ7wtqA+crAfzYpkpPr86urvmJGisECwsWJob9boCM4zjmKCi5QR7G8uY9+v7ZoliJpeCz/4xw==",
|
||||||
|
"requires": {
|
||||||
|
"classnames": "^2.2.5",
|
||||||
|
"emotion": "^9.1.2",
|
||||||
|
"memoize-one": "^4.0.0",
|
||||||
|
"prop-types": "^15.6.0",
|
||||||
|
"raf": "^3.4.0",
|
||||||
|
"react-input-autosize": "^2.2.1",
|
||||||
|
"react-transition-group": "^2.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-transform-catch-errors": {
|
"react-transform-catch-errors": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-transform-catch-errors/-/react-transform-catch-errors-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-transform-catch-errors/-/react-transform-catch-errors-1.0.2.tgz",
|
||||||
@@ -10879,6 +11115,16 @@
|
|||||||
"schema-utils": "^0.3.0"
|
"schema-utils": "^0.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"stylis": {
|
||||||
|
"version": "3.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.3.tgz",
|
||||||
|
"integrity": "sha512-TxU0aAscJghF9I3V9q601xcK3Uw1JbXvpsBGj/HULqexKOKlOEzzlIpLFRbKkCK990ccuxfXUqmPbIIo7Fq/cQ=="
|
||||||
|
},
|
||||||
|
"stylis-rule-sheet": {
|
||||||
|
"version": "0.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
|
||||||
|
"integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
|
||||||
|
},
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz",
|
||||||
@@ -11092,6 +11338,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.6.tgz",
|
||||||
"integrity": "sha1-wxdI5V0hDv/AD9zcfW5o19e7nOw="
|
"integrity": "sha1-wxdI5V0hDv/AD9zcfW5o19e7nOw="
|
||||||
},
|
},
|
||||||
|
"touch": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/touch/-/touch-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-qjNtvsFXTRq7IuMLweVgFxmEuQ6gLbRs2jQxL80TtZ31dEKWYIxRXquij6w6VimyDek5hD3PytljHmEtAs2u0A==",
|
||||||
|
"requires": {
|
||||||
|
"nopt": "~1.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tough-cookie": {
|
"tough-cookie": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
"react-cookies": "^0.1.0",
|
"react-cookies": "^0.1.0",
|
||||||
"react-dom": "^16.5.2",
|
"react-dom": "^16.5.2",
|
||||||
"react-moment": "^0.7.9",
|
"react-moment": "^0.7.9",
|
||||||
|
"react-select": "^2.1.1",
|
||||||
"reactstrap": "^6.4.0",
|
"reactstrap": "^6.4.0",
|
||||||
"seafile-js": "^0.2.31",
|
"seafile-js": "^0.2.31",
|
||||||
"seafile-ui": "^0.1.10",
|
"seafile-ui": "^0.1.10",
|
||||||
|
138
frontend/src/components/dialog/add-reviewer-dialog.js
Normal file
138
frontend/src/components/dialog/add-reviewer-dialog.js
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import AsyncSelect from 'react-select/lib/Async';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { gettext } from '../../utils/constants';
|
||||||
|
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||||
|
import { seafileAPI } from '../../utils/seafile-api.js';
|
||||||
|
import '../../css/add-reviewer-dialog.css';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
showReviewerDialog: PropTypes.bool.isRequired,
|
||||||
|
reviewID: PropTypes.string.isRequired,
|
||||||
|
toggleAddReviewerDialog: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
class AddReviewerDialog extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
reviewers: [],
|
||||||
|
selectedOption: null,
|
||||||
|
errorMsg: [],
|
||||||
|
};
|
||||||
|
this.Options = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
seafileAPI.listReviewers(this.props.reviewID).then((res) => {
|
||||||
|
this.setState({
|
||||||
|
reviewers: res.data.reviewers
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSelectChange = (option) => {
|
||||||
|
this.setState({
|
||||||
|
selectedOption: option,
|
||||||
|
});
|
||||||
|
this.Options = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
loadOptions = (value, callback) => {
|
||||||
|
if (value.trim().length > 0) {
|
||||||
|
this.Options = [];
|
||||||
|
let that = this;
|
||||||
|
seafileAPI.searchUsers(value.trim()).then((res) => {
|
||||||
|
for (let i = 0 ; i < res.data.users.length; i++) {
|
||||||
|
let obj = {};
|
||||||
|
obj.value = res.data.users[i].name;
|
||||||
|
obj.email = res.data.users[i].email;
|
||||||
|
obj.label =
|
||||||
|
<div>
|
||||||
|
<img src={res.data.users[i].avatar_url} className="avatar reviewer-select-avatar" alt=""/>
|
||||||
|
<span className='reviewer-select-name'>{res.data.users[i].name}</span>
|
||||||
|
</div>;
|
||||||
|
that.Options.push(obj);
|
||||||
|
}
|
||||||
|
callback(this.Options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addReviewers = () => {
|
||||||
|
if (this.state.selectedOption.length > 0 ) {
|
||||||
|
let reviewers = [];
|
||||||
|
for (let i = 0; i < this.state.selectedOption.length; i ++) {
|
||||||
|
reviewers[i] = this.state.selectedOption[i].email;
|
||||||
|
}
|
||||||
|
seafileAPI.addReviewers(this.props.reviewID, reviewers).then((res) => {
|
||||||
|
if (res.data.failed.length > 0) {
|
||||||
|
let errorMsg = [];
|
||||||
|
for (let i = 0 ; i < res.data.failed.length ; i++) {
|
||||||
|
errorMsg[i] = res.data.failed[i];
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
errorMsg: errorMsg
|
||||||
|
});
|
||||||
|
let that = this;
|
||||||
|
setTimeout(() => {
|
||||||
|
that.setState({
|
||||||
|
errorMsg: []
|
||||||
|
});
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
selectedOption: null
|
||||||
|
});
|
||||||
|
if (res.data.success.length > 0) {
|
||||||
|
this.listReviewers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Modal isOpen={this.props.showReviewerDialog}>
|
||||||
|
<ModalHeader>{gettext('Request a review')}</ModalHeader>
|
||||||
|
<ModalBody >
|
||||||
|
<p>{gettext('Add new reviewer')}</p>
|
||||||
|
<AsyncSelect
|
||||||
|
className='reviewer-select' isMulti isFocused
|
||||||
|
loadOptions={this.loadOptions}
|
||||||
|
placeholder={gettext('Please enter 1 or more character')}
|
||||||
|
onChange={this.handleSelectChange}
|
||||||
|
/>
|
||||||
|
{this.state.errorMsg.length > 0 &&
|
||||||
|
this.state.errorMsg.map((item, index = 0, arr) => {
|
||||||
|
return (
|
||||||
|
<p className="error" key={index}>{this.state.errorMsg[index].email}
|
||||||
|
{':'}{this.state.errorMsg[index].error_msg}</p>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{ this.state.reviewers.length > 0 &&
|
||||||
|
this.state.reviewers.map((item, index = 0, arr) => {
|
||||||
|
return (
|
||||||
|
<div className="reviewer-select-info" key={index}>
|
||||||
|
<img className="avatar reviewer-select-avatar" src={item.avatar_url} alt=""/>
|
||||||
|
<span className="reviewer-select-name">{item.user_name}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button color="primary" onClick={this.addReviewers}>{gettext('Submit')}</Button>
|
||||||
|
<Button color="secondary" onClick={this.props.toggleAddReviewerDialog}>
|
||||||
|
{gettext('Cancel')}</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddReviewerDialog.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default AddReviewerDialog;
|
13
frontend/src/css/add-reviewer-dialog.css
Normal file
13
frontend/src/css/add-reviewer-dialog.css
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
.reviewer-select-info {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.reviewer-select-avatar {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.reviewer-select-name {
|
||||||
|
height: 2em;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
|
.reviewer-select>div>div:nth-child(2) {
|
||||||
|
display: none;
|
||||||
|
}
|
@@ -2,7 +2,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header .common-list-btn {
|
.header .common-list-btn, .header .add-reviewer-btn {
|
||||||
margin-right: .25em;
|
margin-right: .25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@ import Loading from './components/loading';
|
|||||||
import Toast from './components/toast';
|
import Toast from './components/toast';
|
||||||
import ReviewComments from './components/review-list-view/review-comments';
|
import ReviewComments from './components/review-list-view/review-comments';
|
||||||
import { Button, Tooltip } from 'reactstrap';
|
import { Button, Tooltip } from 'reactstrap';
|
||||||
|
import AddReviewerDialog from './components/dialog/add-reviewer-dialog.js';
|
||||||
|
|
||||||
import 'seafile-ui';
|
import 'seafile-ui';
|
||||||
import './assets/css/fa-solid.css';
|
import './assets/css/fa-solid.css';
|
||||||
@@ -37,6 +38,7 @@ class DraftReview extends React.Component {
|
|||||||
commentWidth: 30,
|
commentWidth: 30,
|
||||||
isShowDiff: true,
|
isShowDiff: true,
|
||||||
showDiffTip: false,
|
showDiffTip: false,
|
||||||
|
showReviewerDialog: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +155,12 @@ class DraftReview extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleAddReviewerDialog = () => {
|
||||||
|
this.setState({
|
||||||
|
showReviewerDialog: !this.state.showReviewerDialog
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.getCommentsNumber();
|
this.getCommentsNumber();
|
||||||
}
|
}
|
||||||
@@ -187,6 +195,8 @@ class DraftReview extends React.Component {
|
|||||||
target="toggle-diff" toggle={this.toggleDiffTip}>
|
target="toggle-diff" toggle={this.toggleDiffTip}>
|
||||||
{gettext('View diff')}</Tooltip>
|
{gettext('View diff')}</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
<button className="btn btn-primary add-reviewer-btn" onClick={this.toggleAddReviewerDialog}>
|
||||||
|
{gettext('Add reviewer')}</button>
|
||||||
<button className="btn btn-icon btn-secondary btn-active common-list-btn"
|
<button className="btn btn-icon btn-secondary btn-active common-list-btn"
|
||||||
id="commentsNumber" type="button" data-active="false"
|
id="commentsNumber" type="button" data-active="false"
|
||||||
onMouseDown={this.toggleCommentList}>
|
onMouseDown={this.toggleCommentList}>
|
||||||
@@ -244,6 +254,13 @@ class DraftReview extends React.Component {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{ this.state.showReviewerDialog &&
|
||||||
|
<AddReviewerDialog
|
||||||
|
showReviewerDialog={this.state.showReviewerDialog}
|
||||||
|
toggleAddReviewerDialog={this.toggleAddReviewerDialog}
|
||||||
|
reviewID={reviewID}
|
||||||
|
/>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -41,5 +41,6 @@ export const draftFileName = window.draftReview ? window.draftReview.config.draf
|
|||||||
export const reviewID = window.draftReview ? window.draftReview.config.reviewID : '';
|
export const reviewID = window.draftReview ? window.draftReview.config.reviewID : '';
|
||||||
export const draftID = window.draftReview ? window.draftReview.config.draftID : '';
|
export const draftID = window.draftReview ? window.draftReview.config.draftID : '';
|
||||||
export const opStatus = window.draftReview ? window.draftReview.config.opStatus : '';
|
export const opStatus = window.draftReview ? window.draftReview.config.opStatus : '';
|
||||||
|
export const reviewPerm = window.draftReview ? window.draftReview.config.perm : '';
|
||||||
export const publishFileVersion = window.draftReview ? window.draftReview.config.publishFileVersion : '';
|
export const publishFileVersion = window.draftReview ? window.draftReview.config.publishFileVersion : '';
|
||||||
export const originFileVersion = window.draftReview ? window.draftReview.config.originFileVersion : '';
|
export const originFileVersion = window.draftReview ? window.draftReview.config.originFileVersion : '';
|
||||||
|
118
seahub/api2/endpoints/draft_review_reviewer.py
Normal file
118
seahub/api2/endpoints/draft_review_reviewer.py
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
# 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.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from seaserv import seafile_api
|
||||||
|
from seahub.api2.authentication import TokenAuthentication
|
||||||
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
|
from seahub.api2.utils import api_error, user_to_dict
|
||||||
|
|
||||||
|
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||||
|
from seahub.base.accounts import User
|
||||||
|
from seahub.views import check_folder_permission
|
||||||
|
from seahub.utils import is_valid_username
|
||||||
|
from seahub.drafts.models import DraftReview, ReviewReviewer
|
||||||
|
from seahub.drafts.signals import request_reviewer_successful
|
||||||
|
|
||||||
|
|
||||||
|
class DraftReviewReviewerView(APIView):
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAuthenticated, )
|
||||||
|
throttle_classes = (UserRateThrottle, )
|
||||||
|
|
||||||
|
def get(self, request, pk, format=None):
|
||||||
|
try:
|
||||||
|
r = DraftReview.objects.get(pk=pk)
|
||||||
|
except DraftReview.DoesNotExist:
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND,
|
||||||
|
'Review %s not found' % pk)
|
||||||
|
|
||||||
|
# format user result
|
||||||
|
try:
|
||||||
|
avatar_size = int(request.GET.get('avatar_size', 32))
|
||||||
|
except ValueError:
|
||||||
|
avatar_size = 32
|
||||||
|
|
||||||
|
# get reviewer list
|
||||||
|
reviewers = []
|
||||||
|
for x in r.reviewreviewer_set.all():
|
||||||
|
reviewer = user_to_dict(x.reviewer, request=request, avatar_size=avatar_size)
|
||||||
|
reviewers.append(reviewer)
|
||||||
|
|
||||||
|
return Response({'reviewers': reviewers})
|
||||||
|
|
||||||
|
def post(self, request, pk, format=None):
|
||||||
|
"""Create a draft review
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
r = DraftReview.objects.get(pk=pk)
|
||||||
|
except DraftReview.DoesNotExist:
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND,
|
||||||
|
'Review %s not found' % pk)
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
result['failed'] = []
|
||||||
|
result['success'] = []
|
||||||
|
|
||||||
|
reviewers = request.data.getlist('reviewer')
|
||||||
|
for reviewer in reviewers:
|
||||||
|
if not is_valid_username(reviewer):
|
||||||
|
result['failed'].append({
|
||||||
|
'email': reviewer,
|
||||||
|
'error_msg': _(u'username invalid.')
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
User.objects.get(email=reviewer)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
result['failed'].append({
|
||||||
|
'email': reviewer,
|
||||||
|
'error_msg': _(u'User %s not found.') % reviewer
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
|
||||||
|
# can't share to owner
|
||||||
|
if reviewer == r.creator:
|
||||||
|
error_msg = _(u'Draft review can not be asked owner to review.')
|
||||||
|
result['failed'].append({
|
||||||
|
'email': reviewer,
|
||||||
|
'error_msg': error_msg
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
|
||||||
|
# check perm
|
||||||
|
if seafile_api.check_permission_by_path(r.origin_repo_id, r.origin_file_path, reviewer) != 'rw':
|
||||||
|
error_msg = _(u'Permission denied.')
|
||||||
|
result['failed'].append({
|
||||||
|
'email': reviewer,
|
||||||
|
'error_msg': error_msg
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ReviewReviewer.objects.filter(review_id=r, reviewer=reviewer):
|
||||||
|
error_msg = _(u'Reviewer %s has existed.') % reviewer
|
||||||
|
result['failed'].append({
|
||||||
|
'email': reviewer,
|
||||||
|
'error_msg': error_msg
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
|
||||||
|
result['success'].append({
|
||||||
|
"user_info": {
|
||||||
|
"name": reviewer,
|
||||||
|
"nickname": email2nickname(reviewer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
ReviewReviewer.objects.add(reviewer, r)
|
||||||
|
|
||||||
|
request_reviewer_successful.send(sender=None, from_user=r.creator,
|
||||||
|
to_user=reviewer, review_id=r.id)
|
||||||
|
|
||||||
|
return Response(result)
|
@@ -13,9 +13,12 @@ from seaserv import seafile_api
|
|||||||
from seahub.api2.authentication import TokenAuthentication
|
from seahub.api2.authentication import TokenAuthentication
|
||||||
from seahub.api2.throttling import UserRateThrottle
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
from seahub.api2.utils import api_error
|
from seahub.api2.utils import api_error
|
||||||
|
from seahub.constants import PERMISSION_READ_WRITE
|
||||||
|
from seahub.views import check_folder_permission
|
||||||
|
|
||||||
from seahub.drafts.models import Draft, DraftReview, DraftReviewExist, \
|
from seahub.drafts.models import Draft, DraftReview, DraftReviewExist, \
|
||||||
DraftFileConflict
|
DraftFileConflict, ReviewReviewer
|
||||||
|
from seahub.drafts.signals import update_review_successful
|
||||||
|
|
||||||
|
|
||||||
class DraftReviewsView(APIView):
|
class DraftReviewsView(APIView):
|
||||||
@@ -28,10 +31,10 @@ class DraftReviewsView(APIView):
|
|||||||
"""
|
"""
|
||||||
username = request.user.username
|
username = request.user.username
|
||||||
data = [x.to_dict() for x in DraftReview.objects.filter(creator=username)]
|
data = [x.to_dict() for x in DraftReview.objects.filter(creator=username)]
|
||||||
|
data += [x.review_id.to_dict() for x in ReviewReviewer.objects.filter(reviewer=username)]
|
||||||
|
|
||||||
return Response({'data': data})
|
return Response({'data': data})
|
||||||
|
|
||||||
|
|
||||||
def post(self, request, format=None):
|
def post(self, request, format=None):
|
||||||
"""Create a draft review
|
"""Create a draft review
|
||||||
"""
|
"""
|
||||||
@@ -74,10 +77,22 @@ class DraftReviewView(APIView):
|
|||||||
return api_error(status.HTTP_404_NOT_FOUND,
|
return api_error(status.HTTP_404_NOT_FOUND,
|
||||||
'Review %s not found' % pk)
|
'Review %s not found' % pk)
|
||||||
|
|
||||||
|
perm = check_folder_permission(request, r.origin_repo_id, r.origin_file_path)
|
||||||
|
|
||||||
|
# Review owner and 'rw' perm on the original file to close review
|
||||||
|
if st == 'closed':
|
||||||
|
if perm != PERMISSION_READ_WRITE or request.user.username != r.creator:
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
r.status = st
|
r.status = st
|
||||||
r.save()
|
r.save()
|
||||||
|
|
||||||
|
# Only 'rw' perm on original file can publish review
|
||||||
if st == 'finished':
|
if st == 'finished':
|
||||||
|
if perm != PERMISSION_READ_WRITE:
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
d = Draft.objects.get(pk=r.draft_id_id)
|
d = Draft.objects.get(pk=r.draft_id_id)
|
||||||
@@ -119,9 +134,26 @@ class DraftReviewView(APIView):
|
|||||||
# get draft published version
|
# get draft published version
|
||||||
file_id = seafile_api.get_file_id_by_path(r.origin_repo_id, origin_file_path)
|
file_id = seafile_api.get_file_id_by_path(r.origin_repo_id, origin_file_path)
|
||||||
r.publish_file_version = file_id
|
r.publish_file_version = file_id
|
||||||
|
r.status = st
|
||||||
r.save()
|
r.save()
|
||||||
d.delete()
|
d.delete()
|
||||||
|
|
||||||
|
reviewers = ReviewReviewer.objects.filter(review_id=r)
|
||||||
|
# send notice to other reviewers if has
|
||||||
|
if reviewers:
|
||||||
|
for i in reviewers:
|
||||||
|
# If it is a reviewer operation, exclude it.
|
||||||
|
if i.reviewer == request.user.username:
|
||||||
|
continue
|
||||||
|
|
||||||
|
update_review_successful.send(sender=None, from_user=request.user.username,
|
||||||
|
to_user=i.reviewer, review_id=r.id, status=st)
|
||||||
|
|
||||||
|
# send notice to review owner
|
||||||
|
if request.user.username != r.creator:
|
||||||
|
update_review_successful.send(sender=None, from_user=request.user.username,
|
||||||
|
to_user=r.creator, review_id=r.id, status=st)
|
||||||
|
|
||||||
result = r.to_dict()
|
result = r.to_dict()
|
||||||
|
|
||||||
return Response(result)
|
return Response(result)
|
||||||
|
@@ -21,7 +21,6 @@ from seahub.api2.authentication import TokenAuthentication
|
|||||||
from seahub.api2.endpoints.utils import add_org_context
|
from seahub.api2.endpoints.utils import add_org_context
|
||||||
from seahub.api2.throttling import UserRateThrottle
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
from seahub.api2.utils import api_error
|
from seahub.api2.utils import api_error
|
||||||
from seahub.constants import PERMISSION_READ_WRITE
|
|
||||||
from seahub.drafts.models import Draft, DraftFileExist, DraftFileConflict
|
from seahub.drafts.models import Draft, DraftFileExist, DraftFileConflict
|
||||||
from seahub.views import check_folder_permission
|
from seahub.views import check_folder_permission
|
||||||
from seahub.utils import gen_file_get_url
|
from seahub.utils import gen_file_get_url
|
||||||
@@ -63,7 +62,7 @@ class DraftsView(APIView):
|
|||||||
|
|
||||||
# perm check
|
# perm check
|
||||||
perm = check_folder_permission(request, repo.id, file_path)
|
perm = check_folder_permission(request, repo.id, file_path)
|
||||||
if perm != PERMISSION_READ_WRITE:
|
if perm is None:
|
||||||
error_msg = 'Permission denied.'
|
error_msg = 'Permission denied.'
|
||||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
31
seahub/drafts/migrations/0004_reviewreviewer.py
Normal file
31
seahub/drafts/migrations/0004_reviewreviewer.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.15 on 2018-10-23 08:58
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import seahub.base.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('drafts', '0003_reviewcomment'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ReviewReviewer',
|
||||||
|
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)),
|
||||||
|
('reviewer', seahub.base.fields.LowerCaseCharField(db_index=True, max_length=255)),
|
||||||
|
('review_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='drafts.DraftReview')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['-created_at', '-updated_at'],
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
27
seahub/drafts/migrations/0005_auto_20181024_1009.py
Normal file
27
seahub/drafts/migrations/0005_auto_20181024_1009.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.15 on 2018-10-24 10:09
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('drafts', '0004_reviewreviewer'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='reviewreviewer',
|
||||||
|
options={},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='reviewreviewer',
|
||||||
|
name='created_at',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='reviewreviewer',
|
||||||
|
name='updated_at',
|
||||||
|
),
|
||||||
|
]
|
@@ -243,3 +243,27 @@ class ReviewComment(TimestampedModel):
|
|||||||
'resolved': self.resolved,
|
'resolved': self.resolved,
|
||||||
'detail': self.detail,
|
'detail': self.detail,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ReviewReviewerManager(models.Manager):
|
||||||
|
def add(self, reviewer, review_id):
|
||||||
|
review_reviewer = self.model(reviewer=reviewer, review_id=review_id)
|
||||||
|
review_reviewer.save(using=self._db)
|
||||||
|
|
||||||
|
return review_reviewer
|
||||||
|
|
||||||
|
|
||||||
|
class ReviewReviewer(models.Model):
|
||||||
|
"""
|
||||||
|
Model used to record review reviewer.
|
||||||
|
"""
|
||||||
|
reviewer = LowerCaseCharField(max_length=255, db_index=True)
|
||||||
|
review_id = models.ForeignKey('DraftReview', on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
objects = ReviewReviewerManager()
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
'nickname': email2nickname(self.reviewer),
|
||||||
|
'name': self.reviewer,
|
||||||
|
}
|
||||||
|
@@ -2,3 +2,5 @@
|
|||||||
import django.dispatch
|
import django.dispatch
|
||||||
|
|
||||||
comment_review_successful = django.dispatch.Signal(providing_args=["review", "comment", "author"])
|
comment_review_successful = django.dispatch.Signal(providing_args=["review", "comment", "author"])
|
||||||
|
request_reviewer_successful = django.dispatch.Signal(providing_args=["from_user", "to_user", "review_id"])
|
||||||
|
update_review_successful = django.dispatch.Signal(providing_args=["from_user", "to_user", "review_id", "status"])
|
||||||
|
@@ -45,5 +45,6 @@ def review(request, pk):
|
|||||||
"draft_file_name": draft_file_name,
|
"draft_file_name": draft_file_name,
|
||||||
"origin_file_version": d_r.origin_file_version,
|
"origin_file_version": d_r.origin_file_version,
|
||||||
"publish_file_version": d_r.publish_file_version,
|
"publish_file_version": d_r.publish_file_version,
|
||||||
"status": d_r.status
|
"status": d_r.status,
|
||||||
|
"permission": permission
|
||||||
})
|
})
|
||||||
|
@@ -54,6 +54,8 @@ MSG_TYPE_REPO_SHARE_TO_GROUP = 'repo_share_to_group'
|
|||||||
MSG_TYPE_USER_MESSAGE = 'user_message'
|
MSG_TYPE_USER_MESSAGE = 'user_message'
|
||||||
MSG_TYPE_FILE_COMMENT = 'file_comment'
|
MSG_TYPE_FILE_COMMENT = 'file_comment'
|
||||||
MSG_TYPE_REVIEW_COMMENT = 'review_comment'
|
MSG_TYPE_REVIEW_COMMENT = 'review_comment'
|
||||||
|
MSG_TYPE_UPDATE_REVIEW = 'update_review'
|
||||||
|
MSG_TYPE_REQUEST_REVIEWER = 'request_reviewer'
|
||||||
MSG_TYPE_GUEST_INVITATION_ACCEPTED = 'guest_invitation_accepted'
|
MSG_TYPE_GUEST_INVITATION_ACCEPTED = 'guest_invitation_accepted'
|
||||||
|
|
||||||
USER_NOTIFICATION_COUNT_CACHE_PREFIX = 'USER_NOTIFICATION_COUNT_'
|
USER_NOTIFICATION_COUNT_CACHE_PREFIX = 'USER_NOTIFICATION_COUNT_'
|
||||||
@@ -98,6 +100,17 @@ def review_comment_msg_to_json(review_id, author, comment):
|
|||||||
'author': author,
|
'author': author,
|
||||||
'comment': comment})
|
'comment': comment})
|
||||||
|
|
||||||
|
def request_reviewer_msg_to_json(review_id, from_user, to_user):
|
||||||
|
return json.dumps({'review_id': review_id,
|
||||||
|
'from_user': from_user,
|
||||||
|
'to_user': to_user})
|
||||||
|
|
||||||
|
def update_review_msg_to_json(review_id, from_user, to_user, status):
|
||||||
|
return json.dumps({'review_id': review_id,
|
||||||
|
'from_user': from_user,
|
||||||
|
'to_user': to_user,
|
||||||
|
'status': status})
|
||||||
|
|
||||||
def guest_invitation_accepted_msg_to_json(invitation_id):
|
def guest_invitation_accepted_msg_to_json(invitation_id):
|
||||||
return json.dumps({'invitation_id': invitation_id})
|
return json.dumps({'invitation_id': invitation_id})
|
||||||
|
|
||||||
@@ -298,6 +311,16 @@ class UserNotificationManager(models.Manager):
|
|||||||
"""
|
"""
|
||||||
return self._add_user_notification(to_user, MSG_TYPE_REVIEW_COMMENT, detail)
|
return self._add_user_notification(to_user, MSG_TYPE_REVIEW_COMMENT, detail)
|
||||||
|
|
||||||
|
def add_request_reviewer_msg(self, to_user, detail):
|
||||||
|
"""Notify ``to_user`` that reviewer
|
||||||
|
"""
|
||||||
|
return self._add_user_notification(to_user, MSG_TYPE_REQUEST_REVIEWER, detail)
|
||||||
|
|
||||||
|
def add_update_review_msg(self, to_user, detail):
|
||||||
|
"""Notify ``to_user`` that reviewer and owner
|
||||||
|
"""
|
||||||
|
return self._add_user_notification(to_user, MSG_TYPE_UPDATE_REVIEW, detail)
|
||||||
|
|
||||||
def add_guest_invitation_accepted_msg(self, to_user, detail):
|
def add_guest_invitation_accepted_msg(self, to_user, detail):
|
||||||
"""Nofity ``to_user`` that a guest has accpeted an invitation.
|
"""Nofity ``to_user`` that a guest has accpeted an invitation.
|
||||||
"""
|
"""
|
||||||
@@ -399,6 +422,12 @@ class UserNotification(models.Model):
|
|||||||
def is_review_comment_msg(self):
|
def is_review_comment_msg(self):
|
||||||
return self.msg_type == MSG_TYPE_REVIEW_COMMENT
|
return self.msg_type == MSG_TYPE_REVIEW_COMMENT
|
||||||
|
|
||||||
|
def is_request_reviewer_msg(self):
|
||||||
|
return self.msg_type == MSG_TYPE_REQUEST_REVIEWER
|
||||||
|
|
||||||
|
def is_update_review_msg(self):
|
||||||
|
return self.msg_type == MSG_TYPE_UPDATE_REVIEW
|
||||||
|
|
||||||
def is_guest_invitation_accepted_msg(self):
|
def is_guest_invitation_accepted_msg(self):
|
||||||
return self.msg_type == MSG_TYPE_GUEST_INVITATION_ACCEPTED
|
return self.msg_type == MSG_TYPE_GUEST_INVITATION_ACCEPTED
|
||||||
|
|
||||||
@@ -760,6 +789,49 @@ class UserNotification(models.Model):
|
|||||||
}
|
}
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
def format_request_reviewer_msg(self):
|
||||||
|
try:
|
||||||
|
d = json.loads(self.detail)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
return _(u"Internal error")
|
||||||
|
|
||||||
|
review_id = d['review_id']
|
||||||
|
from_user = d['from_user']
|
||||||
|
|
||||||
|
msg = _("%(from_user)s has sent you a request for review <a href='%(file_url)s'>%(review_id)s</a>") % {
|
||||||
|
'review_id': review_id,
|
||||||
|
'file_url': reverse('drafts:review', args=[review_id]),
|
||||||
|
'from_user': escape(email2nickname(from_user))
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
|
||||||
|
def format_update_review_msg(self):
|
||||||
|
try:
|
||||||
|
d = json.loads(self.detail)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
return _(u"Internal error")
|
||||||
|
|
||||||
|
review_id = d['review_id']
|
||||||
|
from_user = d['from_user']
|
||||||
|
status = d['status']
|
||||||
|
|
||||||
|
if status == 'closed':
|
||||||
|
msg = _("%(from_user)s has closed review <a href='%(file_url)s'>%(review_id)s</a>") % {
|
||||||
|
'review_id': review_id,
|
||||||
|
'file_url': reverse('drafts:review', args=[review_id]),
|
||||||
|
'from_user': escape(email2nickname(from_user))
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == 'finished':
|
||||||
|
msg = _("%(from_user)s has published review <a href='%(file_url)s'>%(review_id)s</a>") % {
|
||||||
|
'review_id': review_id,
|
||||||
|
'file_url': reverse('drafts:review', args=[review_id]),
|
||||||
|
'from_user': escape(email2nickname(from_user))
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
|
||||||
def format_guest_invitation_accepted_msg(self):
|
def format_guest_invitation_accepted_msg(self):
|
||||||
try:
|
try:
|
||||||
d = json.loads(self.detail)
|
d = json.loads(self.detail)
|
||||||
@@ -794,7 +866,8 @@ from seahub.group.signals import grpmsg_added, group_join_request, add_user_to_g
|
|||||||
from seahub.share.signals import share_repo_to_user_successful, \
|
from seahub.share.signals import share_repo_to_user_successful, \
|
||||||
share_repo_to_group_successful
|
share_repo_to_group_successful
|
||||||
from seahub.invitations.signals import accept_guest_invitation_successful
|
from seahub.invitations.signals import accept_guest_invitation_successful
|
||||||
from seahub.drafts.signals import comment_review_successful
|
from seahub.drafts.signals import comment_review_successful, \
|
||||||
|
request_reviewer_successful, update_review_successful
|
||||||
|
|
||||||
@receiver(upload_file_successful)
|
@receiver(upload_file_successful)
|
||||||
def add_upload_file_msg_cb(sender, **kwargs):
|
def add_upload_file_msg_cb(sender, **kwargs):
|
||||||
@@ -908,6 +981,26 @@ def comment_review_successful_cb(sender, **kwargs):
|
|||||||
detail = review_comment_msg_to_json(review.id, author, comment)
|
detail = review_comment_msg_to_json(review.id, author, comment)
|
||||||
UserNotification.objects.add_review_comment_msg(review.creator, detail)
|
UserNotification.objects.add_review_comment_msg(review.creator, detail)
|
||||||
|
|
||||||
|
@receiver(request_reviewer_successful)
|
||||||
|
def requeset_reviewer_successful_cb(sender, **kwargs):
|
||||||
|
from_user = kwargs['from_user']
|
||||||
|
review_id = kwargs['review_id']
|
||||||
|
to_user = kwargs['to_user']
|
||||||
|
|
||||||
|
detail = request_reviewer_msg_to_json(review_id, from_user, to_user)
|
||||||
|
|
||||||
|
UserNotification.objects.add_request_reviewer_msg(to_user, detail)
|
||||||
|
|
||||||
|
@receiver(update_review_successful)
|
||||||
|
def update_review_successful_cb(sender, **kwargs):
|
||||||
|
from_user = kwargs['from_user']
|
||||||
|
review_id = kwargs['review_id']
|
||||||
|
to_user = kwargs['to_user']
|
||||||
|
status = kwargs['status']
|
||||||
|
|
||||||
|
detail = update_review_msg_to_json(review_id, from_user, to_user, status)
|
||||||
|
|
||||||
|
UserNotification.objects.add_update_review_msg(to_user, detail)
|
||||||
|
|
||||||
@receiver(accept_guest_invitation_successful)
|
@receiver(accept_guest_invitation_successful)
|
||||||
def accept_guest_invitation_successful_cb(sender, **kwargs):
|
def accept_guest_invitation_successful_cb(sender, **kwargs):
|
||||||
|
@@ -38,6 +38,12 @@
|
|||||||
{% elif notice.is_review_comment_msg %}
|
{% elif notice.is_review_comment_msg %}
|
||||||
<p class="brief">{{ notice.format_review_comment_msg|safe }}</p>
|
<p class="brief">{{ notice.format_review_comment_msg|safe }}</p>
|
||||||
|
|
||||||
|
{% elif notice.is_update_review_msg %}
|
||||||
|
<p class="brief">{{ notice.format_update_review_msg|safe }}</p>
|
||||||
|
|
||||||
|
{% elif notice.is_request_reviewer_msg %}
|
||||||
|
<p class="brief">{{ notice.format_request_reviewer_msg|safe }}</p>
|
||||||
|
|
||||||
{% elif notice.is_guest_invitation_accepted_msg %}
|
{% elif notice.is_guest_invitation_accepted_msg %}
|
||||||
<p class="brief">{{ notice.format_guest_invitation_accepted_msg|safe }}</p>
|
<p class="brief">{{ notice.format_guest_invitation_accepted_msg|safe }}</p>
|
||||||
|
|
||||||
|
@@ -192,5 +192,18 @@ def add_notice_from_info(notices):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
|
|
||||||
|
elif notice.is_request_reviewer_msg():
|
||||||
|
try:
|
||||||
|
d = json.loads(notice.detail)
|
||||||
|
notice.msg_from = d['from_user']
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
|
||||||
|
elif notice.is_update_review_msg():
|
||||||
|
try:
|
||||||
|
d = json.loads(notice.detail)
|
||||||
|
notice.msg_from = d['from_user']
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
|
||||||
return notices
|
return notices
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
draftOriginRepoID: '{{ draft_origin_repo_id }}',
|
draftOriginRepoID: '{{ draft_origin_repo_id }}',
|
||||||
draftFileName: '{{ draft_file_name }}',
|
draftFileName: '{{ draft_file_name }}',
|
||||||
opStatus: '{{ status }}',
|
opStatus: '{{ status }}',
|
||||||
|
perm: '{{ permission }}',
|
||||||
publishFileVersion: '{{ publish_file_version }}',
|
publishFileVersion: '{{ publish_file_version }}',
|
||||||
originFileVersion: '{{ origin_file_version }}',
|
originFileVersion: '{{ origin_file_version }}',
|
||||||
}
|
}
|
||||||
|
@@ -40,6 +40,12 @@
|
|||||||
{% elif notice.is_review_comment_msg %}
|
{% elif notice.is_review_comment_msg %}
|
||||||
<p class="brief">{{ notice.format_review_comment_msg|safe }}</p>
|
<p class="brief">{{ notice.format_review_comment_msg|safe }}</p>
|
||||||
|
|
||||||
|
{% elif notice.is_update_review_msg %}
|
||||||
|
<p class="brief">{{ notice.format_update_review_msg|safe }}</p>
|
||||||
|
|
||||||
|
{% elif notice.is_request_reviewer_msg %}
|
||||||
|
<p class="brief">{{ notice.format_request_reviewer_msg|safe }}</p>
|
||||||
|
|
||||||
{% elif notice.is_guest_invitation_accepted_msg %}
|
{% elif notice.is_guest_invitation_accepted_msg %}
|
||||||
<p class="brief">{{ notice.format_guest_invitation_accepted_msg|safe }}</p>
|
<p class="brief">{{ notice.format_guest_invitation_accepted_msg|safe }}</p>
|
||||||
|
|
||||||
|
@@ -68,6 +68,7 @@ from seahub.api2.endpoints.user_avatar import UserAvatarView
|
|||||||
from seahub.api2.endpoints.wikis import WikisView, WikiView
|
from seahub.api2.endpoints.wikis import WikisView, WikiView
|
||||||
from seahub.api2.endpoints.drafts import DraftsView, DraftView
|
from seahub.api2.endpoints.drafts import DraftsView, DraftView
|
||||||
from seahub.api2.endpoints.draft_reviews import DraftReviewsView, DraftReviewView
|
from seahub.api2.endpoints.draft_reviews import DraftReviewsView, DraftReviewView
|
||||||
|
from seahub.api2.endpoints.draft_review_reviewer import DraftReviewReviewerView
|
||||||
from seahub.api2.endpoints.activities import ActivitiesView
|
from seahub.api2.endpoints.activities import ActivitiesView
|
||||||
from seahub.api2.endpoints.wiki_pages import WikiPageView, WikiPagesView, WikiPagesDirView, WikiPageContentView
|
from seahub.api2.endpoints.wiki_pages import WikiPageView, WikiPagesView, WikiPagesDirView, WikiPageContentView
|
||||||
from seahub.api2.endpoints.revision_tag import TaggedItemsView, TagNamesView
|
from seahub.api2.endpoints.revision_tag import TaggedItemsView, TagNamesView
|
||||||
@@ -340,6 +341,7 @@ urlpatterns = [
|
|||||||
## user::reviews
|
## user::reviews
|
||||||
url(r'^api/v2.1/reviews/$', DraftReviewsView.as_view(), name='api-v2.1-draft-reviews'),
|
url(r'^api/v2.1/reviews/$', DraftReviewsView.as_view(), name='api-v2.1-draft-reviews'),
|
||||||
url(r'^api/v2.1/review/(?P<pk>\d+)/$', DraftReviewView.as_view(), name='api-v2.1-draft-review'),
|
url(r'^api/v2.1/review/(?P<pk>\d+)/$', DraftReviewView.as_view(), name='api-v2.1-draft-review'),
|
||||||
|
url(r'^api/v2.1/review/(?P<pk>\d+)/reviewer/$', DraftReviewReviewerView.as_view(), name='api-v2.1-draft-review-reviewer'),
|
||||||
|
|
||||||
## user::activities
|
## user::activities
|
||||||
url(r'^api/v2.1/activities/$', ActivitiesView.as_view(), name='api-v2.1-acitvity'),
|
url(r'^api/v2.1/activities/$', ActivitiesView.as_view(), name='api-v2.1-acitvity'),
|
||||||
|
Reference in New Issue
Block a user