1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-12 13:24:52 +00:00

sysadmin reconstruct terms and conditions pages (#4231)

* sysadmin reconstruct terms and conditions pages

* update seafile-js version

* add term content dialog

* optimized code struct

* fix redirect logic after add term

* optimized code

* optimized code

* update seafile-editor version
This commit is contained in:
Leo
2019-11-12 21:59:55 +08:00
committed by Daniel Pan
parent d1dca7d13e
commit 803c15730d
15 changed files with 956 additions and 95 deletions

View File

@@ -137,9 +137,9 @@
} }
}, },
"@seafile/seafile-editor": { "@seafile/seafile-editor": {
"version": "0.2.72", "version": "0.2.76",
"resolved": "https://registry.npmjs.org/@seafile/seafile-editor/-/seafile-editor-0.2.72.tgz", "resolved": "https://registry.npmjs.org/@seafile/seafile-editor/-/seafile-editor-0.2.76.tgz",
"integrity": "sha512-joCJtRfxRyxEMnIuLxv3Z2UaHOcaLqZ8zzhfKrpI0FCIFqdSvWxzHGLIGLKfD0q7kC+AG+Ut9ZJSLzTY0HAVcA==", "integrity": "sha512-UDaVOtSeFMdDvCfHgjxXc1zoMf4dNOu3/z8BLLpYi0i1O4gz+BWT5cv8LkodJLg4QnDuGRH8/VyF3Ed6ns6J6g==",
"requires": { "requires": {
"@seafile/seafile-calendar": "^0.0.6", "@seafile/seafile-calendar": "^0.0.6",
"@seafile/slate-react": "^0.1.8", "@seafile/slate-react": "^0.1.8",
@@ -204,7 +204,7 @@
"slate": "0.44.13", "slate": "0.44.13",
"slate-base64-serializer": "^0.2.72", "slate-base64-serializer": "^0.2.72",
"slate-hotkeys": "0.2.3", "slate-hotkeys": "0.2.3",
"slate-html-serializer": "0.7.2", "slate-html-serializer": "^0.7.2",
"slate-schema-violations": "0.1.39", "slate-schema-violations": "0.1.39",
"socket.io-client": "^2.1.1", "socket.io-client": "^2.1.1",
"style-loader": "0.19.0", "style-loader": "0.19.0",
@@ -328,9 +328,9 @@
} }
}, },
"@types/node": { "@types/node": {
"version": "12.7.12", "version": "12.12.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.12.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.7.tgz",
"integrity": "sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ==" "integrity": "sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w=="
}, },
"@videojs/http-streaming": { "@videojs/http-streaming": {
"version": "1.5.1", "version": "1.5.1",
@@ -5470,7 +5470,7 @@
}, },
"git-up": { "git-up": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "http://registry.npmjs.org/git-up/-/git-up-1.2.1.tgz", "resolved": "https://registry.npmjs.org/git-up/-/git-up-1.2.1.tgz",
"integrity": "sha1-JkSAoAax2EJhrB/gmjpRacV+oZ0=", "integrity": "sha1-JkSAoAax2EJhrB/gmjpRacV+oZ0=",
"requires": { "requires": {
"is-ssh": "^1.0.0", "is-ssh": "^1.0.0",
@@ -5479,7 +5479,7 @@
}, },
"git-url-parse": { "git-url-parse": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "http://registry.npmjs.org/git-url-parse/-/git-url-parse-5.0.1.tgz", "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-5.0.1.tgz",
"integrity": "sha1-/j15xnRq4FBIz6UIyB553du6OEM=", "integrity": "sha1-/j15xnRq4FBIz6UIyB553du6OEM=",
"requires": { "requires": {
"git-up": "^1.0.0" "git-up": "^1.0.0"
@@ -5862,9 +5862,9 @@
"integrity": "sha512-C62CVn7jbjp89yOhhy7vrkSaB7Vk906Gtcw/Ihd+Iufnq+2pwOZjdPmpzpKLWJXPJBMDX3wXg4FqmdOayPcewA==" "integrity": "sha512-C62CVn7jbjp89yOhhy7vrkSaB7Vk906Gtcw/Ihd+Iufnq+2pwOZjdPmpzpKLWJXPJBMDX3wXg4FqmdOayPcewA=="
}, },
"hast-util-parse-selector": { "hast-util-parse-selector": {
"version": "2.2.2", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.2.tgz", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.3.tgz",
"integrity": "sha512-jIMtnzrLTjzqgVEQqPEmwEZV+ea4zHRFTP8Z2Utw0I5HuBOXHzUPPQWr6ouJdJqDKLbFU/OEiYwZ79LalZkmmw==" "integrity": "sha512-nxbeqjQNxsvo/uYYAw9kij6td05YVUlf1qti09rVfbWSLT5H6wo3c+USIwX6nzXWk5kFZzXnEqO82856r0aM2Q=="
}, },
"hast-util-phrasing": { "hast-util-phrasing": {
"version": "1.0.4", "version": "1.0.4",
@@ -7614,11 +7614,6 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
}, },
"lodash._getnative": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
"integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
},
"lodash._reinterpolate": { "lodash._reinterpolate": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
@@ -7650,16 +7645,6 @@
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
}, },
"lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo="
},
"lodash.isarray": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U="
},
"lodash.isequal": { "lodash.isequal": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
@@ -7675,16 +7660,6 @@
"resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz",
"integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=" "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0="
}, },
"lodash.keys": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
"integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
"requires": {
"lodash._getnative": "^3.0.0",
"lodash.isarguments": "^3.0.0",
"lodash.isarray": "^3.0.0"
}
},
"lodash.memoize": { "lodash.memoize": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -7866,17 +7841,17 @@
} }
}, },
"mdast-util-compact": { "mdast-util-compact": {
"version": "1.0.3", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.3.tgz", "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.4.tgz",
"integrity": "sha512-nRiU5GpNy62rZppDKbLwhhtw5DXoFMqw9UNZFmlPsNaQCZ//WLjGKUwWMdJrUH+Se7UvtO2gXtAMe0g/N+eI5w==", "integrity": "sha512-3YDMQHI5vRiS2uygEFYaqckibpJtKq5Sj2c8JioeOQBU6INpKbdWzfyLqFFnDwEcEnRFIdMsguzs5pC1Jp4Isg==",
"requires": { "requires": {
"unist-util-visit": "^1.1.0" "unist-util-visit": "^1.1.0"
} }
}, },
"mdast-util-definitions": { "mdast-util-definitions": {
"version": "1.2.4", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-1.2.4.tgz", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-1.2.5.tgz",
"integrity": "sha512-HfUArPog1j4Z78Xlzy9Q4aHLnrF/7fb57cooTHypyGoe2XFNbcx/kWZDoOz+ra8CkUzvg3+VHV434yqEd1DRmA==", "integrity": "sha512-CJXEdoLfiISCDc2JB6QLb79pYfI6+GcIH+W2ox9nMc7od0Pz+bovcHsiq29xAQY6ayqe/9CsK2VzkSJdg1pFYA==",
"requires": { "requires": {
"unist-util-visit": "^1.0.0" "unist-util-visit": "^1.0.0"
} }
@@ -7900,9 +7875,9 @@
} }
}, },
"mdast-util-to-string": { "mdast-util-to-string": {
"version": "1.0.6", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.0.6.tgz", "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.0.7.tgz",
"integrity": "sha512-868pp48gUPmZIhfKrLbaDneuzGiw3OTDjHc5M1kAepR2CWBJ+HpEsm252K4aXdiP5coVZaJPOqGtVU6Po8xnXg==" "integrity": "sha512-P+gdtssCoHOX+eJUrrC30Sixqao86ZPlVjR5NEAoy0U79Pfxb1Y0Gntei0+GrnQD4T04X9xA8tcugp90cSmNow=="
}, },
"mdurl": { "mdurl": {
"version": "1.0.1", "version": "1.0.1",
@@ -8217,7 +8192,7 @@
}, },
"node-status-codes": { "node-status-codes": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "http://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz",
"integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=" "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8="
}, },
"noop6": { "noop6": {
@@ -8524,7 +8499,7 @@
}, },
"package.json": { "package.json": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "http://registry.npmjs.org/package.json/-/package.json-2.0.1.tgz", "resolved": "https://registry.npmjs.org/package.json/-/package.json-2.0.1.tgz",
"integrity": "sha1-+IYFnSpJ7QduZIg2ldc7K0bSHW0=", "integrity": "sha1-+IYFnSpJ7QduZIg2ldc7K0bSHW0=",
"requires": { "requires": {
"git-package-json": "^1.4.0", "git-package-json": "^1.4.0",
@@ -8534,7 +8509,7 @@
"dependencies": { "dependencies": {
"got": { "got": {
"version": "5.7.1", "version": "5.7.1",
"resolved": "http://registry.npmjs.org/got/-/got-5.7.1.tgz", "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz",
"integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=",
"requires": { "requires": {
"create-error-class": "^3.0.1", "create-error-class": "^3.0.1",
@@ -8556,7 +8531,7 @@
}, },
"package-json": { "package-json": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "http://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz", "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz",
"integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=", "integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=",
"requires": { "requires": {
"got": "^5.0.0", "got": "^5.0.0",
@@ -10236,16 +10211,16 @@
} }
}, },
"rc-animate": { "rc-animate": {
"version": "2.10.1", "version": "2.10.2",
"resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-2.10.1.tgz", "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-2.10.2.tgz",
"integrity": "sha512-yfP3g5fNf8wB5eh85nim2IGrqNu5u7TKrrSh710+1vlUqZvnI2R5YHK99IBCQNgkLCAWjT0sHtkcYdynjly39w==", "integrity": "sha512-cE/A7piAzoWFSgUD69NmmMraqCeqVBa51UErod8NS3LUEqWfppSVagHfa0qHAlwPVPiIBg3emRONyny3eiH0Dg==",
"requires": { "requires": {
"babel-runtime": "6.x", "babel-runtime": "6.x",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"css-animation": "^1.3.2", "css-animation": "^1.3.2",
"prop-types": "15.x", "prop-types": "15.x",
"raf": "^3.4.0", "raf": "^3.4.0",
"rc-util": "^4.8.0", "rc-util": "^4.15.3",
"react-lifecycles-compat": "^3.0.4" "react-lifecycles-compat": "^3.0.4"
} }
}, },
@@ -10264,15 +10239,15 @@
} }
}, },
"rc-util": { "rc-util": {
"version": "4.13.0", "version": "4.15.4",
"resolved": "https://registry.npmjs.org/rc-util/-/rc-util-4.13.0.tgz", "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-4.15.4.tgz",
"integrity": "sha512-rjfPy+afc2n40APHp6GYScXfgwHuUnYLz/4SCEWRaF8CHXKR8xw598LtPA36J3fEXENuMm6liO/CoKBoSrYCDw==", "integrity": "sha512-jy2Ej+5N0u9iRoZ/n9gYhVlZkgPV5h8Fu4zNBUsDQNQRCPJMfE6aI8h7KFZEeEZQfFq5abxBgyQyq7TGYyvARQ==",
"requires": { "requires": {
"add-dom-event-listener": "^1.1.0", "add-dom-event-listener": "^1.1.0",
"babel-runtime": "6.x", "babel-runtime": "6.x",
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"react-lifecycles-compat": "^3.0.4", "react-lifecycles-compat": "^3.0.4",
"shallowequal": "^0.2.2" "shallowequal": "^1.1.0"
} }
}, },
"react": { "react": {
@@ -11386,9 +11361,9 @@
} }
}, },
"seafile-js": { "seafile-js": {
"version": "0.2.138", "version": "0.2.139",
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.138.tgz", "resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.139.tgz",
"integrity": "sha512-glPINgDUw2a5q1qBX3hJPKlT2iNltvt3hQXkC6f2pQqLRdh3s8faNHP0ftD+7QnmEmXZpr1LX/6RjtdwP6U57g==", "integrity": "sha512-/KNI7N59iOh6zyEW4lMvSiHE0uHgEmm9hdnthp0hVvEfIVKX8K4dxQBj1zqDxuGIQnF8FXxiR/6Pzs21jlkhZg==",
"requires": { "requires": {
"axios": "^0.18.0", "axios": "^0.18.0",
"form-data": "^2.3.2", "form-data": "^2.3.2",
@@ -11535,12 +11510,9 @@
} }
}, },
"shallowequal": { "shallowequal": {
"version": "0.2.2", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-0.2.2.tgz", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha1-HjL9W8q2rWiKSBLLDMBO/HXHAU4=", "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
"requires": {
"lodash.keys": "^3.1.2"
}
}, },
"shebang-command": { "shebang-command": {
"version": "1.2.0", "version": "1.2.0",
@@ -11612,9 +11584,9 @@
} }
}, },
"slate-base64-serializer": { "slate-base64-serializer": {
"version": "0.2.111", "version": "0.2.112",
"resolved": "https://registry.npmjs.org/slate-base64-serializer/-/slate-base64-serializer-0.2.111.tgz", "resolved": "https://registry.npmjs.org/slate-base64-serializer/-/slate-base64-serializer-0.2.112.tgz",
"integrity": "sha512-pEsbxz4msVSCCCkn7rX+lHXxUj/oddcR4VsIYwWeQQLm9Uw7Ovxja4rQ/hVFcQqoU2DIjITRwBR9pv3RyS+PZQ==", "integrity": "sha512-Vo94bkCq8cbFj7Lutdh2RaM9S4WlLxnnMqZPKGUyefklUN4q2EzM/WUH7s9CIlLUH1qRfC/b0V25VJZr5XXTzA==",
"requires": { "requires": {
"isomorphic-base64": "^1.0.2" "isomorphic-base64": "^1.0.2"
} }
@@ -11637,9 +11609,9 @@
} }
}, },
"slate-html-serializer": { "slate-html-serializer": {
"version": "0.7.2", "version": "0.7.39",
"resolved": "https://registry.npmjs.org/slate-html-serializer/-/slate-html-serializer-0.7.2.tgz", "resolved": "https://registry.npmjs.org/slate-html-serializer/-/slate-html-serializer-0.7.39.tgz",
"integrity": "sha512-UpF3tnEEzdvQKQWvRPqnkKftoBG7efDkNCjaoa7p4yCPav/RstCAm9AwhdINGsKDcYgPt9RXvhpe6r7iu/KQQQ==", "integrity": "sha512-ZNyVjqCasSa0M80+4W1bFXMhW4KJM7EE6dkyBOhQmIcuRX529xOahcz+6ZYK7fQn0Cyrye899OtX26LulAh/Ew==",
"requires": { "requires": {
"type-of": "^2.0.1" "type-of": "^2.0.1"
} }
@@ -11650,9 +11622,9 @@
"integrity": "sha512-EGl+Y+9Fw9IULtPg8sttydaeiAoaibJolMXNfqI79+5GWTQwJFIbg24keKvsTw+3f2RieaPu8fcrKyujKtZ7ZQ==" "integrity": "sha512-EGl+Y+9Fw9IULtPg8sttydaeiAoaibJolMXNfqI79+5GWTQwJFIbg24keKvsTw+3f2RieaPu8fcrKyujKtZ7ZQ=="
}, },
"slate-prop-types": { "slate-prop-types": {
"version": "0.5.41", "version": "0.5.42",
"resolved": "https://registry.npmjs.org/slate-prop-types/-/slate-prop-types-0.5.41.tgz", "resolved": "https://registry.npmjs.org/slate-prop-types/-/slate-prop-types-0.5.42.tgz",
"integrity": "sha512-fLcXlugO9btF5b/by+dA+n8fn2mET75VGWltqFNxGdl6ncyBtrGspWA7mLVRFSqQWOS/Ig4A3URCRumOBBCUfQ==" "integrity": "sha512-3n3556FDs9/cyhRdDMryVB1PJvWeu+p3dx9TvHtONybud4tfulWk4r175JoVWcFZCUFGFQK7IbObUbz1MWNKCg=="
}, },
"slate-react-placeholder": { "slate-react-placeholder": {
"version": "0.1.20", "version": "0.1.20",
@@ -12562,9 +12534,9 @@
} }
}, },
"unist-util-generated": { "unist-util-generated": {
"version": "1.1.4", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.4.tgz", "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.5.tgz",
"integrity": "sha512-SA7Sys3h3X4AlVnxHdvN/qYdr4R38HzihoEVY2Q2BZu8NHWDnw5OGcC/tXWjQfd4iG+M6qRFNIRGqJmp2ez4Ww==" "integrity": "sha512-1TC+NxQa4N9pNdayCYA1EGUOCAO0Le3fVp7Jzns6lnua/mYgwHo0tz5WUAfrdpNch1RZLHc61VZ1SDgrtNXLSw=="
}, },
"unist-util-is": { "unist-util-is": {
"version": "3.0.0", "version": "3.0.0",
@@ -12572,22 +12544,22 @@
"integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==" "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A=="
}, },
"unist-util-modify-children": { "unist-util-modify-children": {
"version": "1.1.4", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-1.1.4.tgz", "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-1.1.5.tgz",
"integrity": "sha512-8iey9wkoB62C7Vi/8zcRUmi4b1f5AYKTwMkyEgLduo2D8+OY65RoSvbn6k9tVNri6qumXxAwXDVlXWQi0sENTw==", "integrity": "sha512-XeL5qqyoS3TEueCKEzHusWXE9JBDJPE4rl6LmcLOwlzv0RIZrcMNqKx02GSK3Ms4v45ldu+ltPxG42FBMVdPZw==",
"requires": { "requires": {
"array-iterate": "^1.0.0" "array-iterate": "^1.0.0"
} }
}, },
"unist-util-position": { "unist-util-position": {
"version": "3.0.3", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.0.3.tgz", "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.0.4.tgz",
"integrity": "sha512-28EpCBYFvnMeq9y/4w6pbnFmCUfzlsc41NJui5c51hOFjBA1fejcwc+5W4z2+0ECVbScG3dURS3JTVqwenzqZw==" "integrity": "sha512-tWvIbV8goayTjobxDIr4zVTyG+Q7ragMSMeKC3xnPl9xzIc0+she8mxXLM3JVNDDsfARPbCd3XdzkyLdo7fF3g=="
}, },
"unist-util-remove-position": { "unist-util-remove-position": {
"version": "1.1.3", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.3.tgz", "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz",
"integrity": "sha512-CtszTlOjP2sBGYc2zcKA/CvNdTdEs3ozbiJ63IPBxh8iZg42SCCb8m04f8z2+V1aSk5a7BxbZKEdoDjadmBkWA==", "integrity": "sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==",
"requires": { "requires": {
"unist-util-visit": "^1.1.0" "unist-util-visit": "^1.1.0"
} }
@@ -12826,9 +12798,9 @@
} }
}, },
"vfile-location": { "vfile-location": {
"version": "2.0.5", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.5.tgz", "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz",
"integrity": "sha512-Pa1ey0OzYBkLPxPZI3d9E+S4BmvfVwNAAXrrqGbwTVXWaX2p9kM1zZ+n35UtVM06shmWKH4RPRN8KI80qE3wNQ==" "integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA=="
}, },
"vfile-message": { "vfile-message": {
"version": "1.0.1", "version": "1.0.1",

View File

@@ -5,7 +5,7 @@
"dependencies": { "dependencies": {
"@reach/router": "^1.2.0", "@reach/router": "^1.2.0",
"@seafile/resumablejs": "^1.1.15", "@seafile/resumablejs": "^1.1.15",
"@seafile/seafile-editor": "^0.2.72", "@seafile/seafile-editor": "^0.2.76",
"MD5": "^1.3.0", "MD5": "^1.3.0",
"autoprefixer": "7.1.6", "autoprefixer": "7.1.6",
"classnames": "^2.2.6", "classnames": "^2.2.6",
@@ -41,7 +41,7 @@
"react-responsive": "^6.1.2", "react-responsive": "^6.1.2",
"react-select": "^2.4.1", "react-select": "^2.4.1",
"reactstrap": "^6.4.0", "reactstrap": "^6.4.0",
"seafile-js": "^0.2.138", "seafile-js": "^0.2.139",
"socket.io-client": "^2.2.0", "socket.io-client": "^2.2.0",
"sw-precache-webpack-plugin": "0.11.4", "sw-precache-webpack-plugin": "0.11.4",
"unified": "^7.0.0", "unified": "^7.0.0",

View File

@@ -0,0 +1,156 @@
import React, {Fragment} from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input, Alert, FormGroup, Label } from 'reactstrap';
import { gettext } from '../../../utils/constants';
import TermsPreviewWidget from '../terms-preview-widget';
import TermsEditorDialog from '../terms-editor-dialog';
import '../../../css/terms-conditions-editor.css';
const propTypes = {
isUpdate: PropTypes.bool,
oldTermObj: PropTypes.object,
addTerm: PropTypes.func,
updateTerm: PropTypes.func,
toggle: PropTypes.func.isRequired,
};
class AddOrUpdateTermDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
versionNumber: '',
text: {text: '', perview: ''},
isActive: true,
errorMsg: '',
isConditionsEditorDialogShow: false,
};
}
componentDidMount() {
let oldTermObj = this.props.oldTermObj;
if (oldTermObj) {
this.setState({
name: oldTermObj.name,
versionNumber: oldTermObj.version_number,
text: JSON.parse(oldTermObj.text),
isActive: !(oldTermObj.activate_time === ''),
});
}
}
handleNameChange = (e) => {
this.setState({name: e.target.value.trim()});
}
handleVersionNumberChange = (e) => {
this.setState({versionNumber: e.target.value.trim()});
}
handleTextChange = (e) => {
this.setState({text: e.target.value.trim()});
}
setActive = () => {
this.setState({isActive: true});
}
setInActive = () => {
this.setState({isActive: false});
}
addTerm = () => {
let { name, versionNumber, text, isActive } = this.state;
if (name === '') {
this.setState({errMsg: gettext('Name is required.')});
return;
}
if (versionNumber === '') {
this.setState({errMsg: gettext('Version Number is required.')});
return;
}
if (isNaN(versionNumber)) {
this.setState({errMsg: gettext('Version Number must be a number.')});
return;
}
text = JSON.stringify(text);
if (text === '') {
this.setState({errMsg: gettext('Text is required.')});
return;
}
if (this.props.isUpdate) {
this.props.updateTerm(name, versionNumber, text, isActive);
} else {
this.props.addTerm(name, versionNumber, text, isActive);
}
}
onContentClick = () => {
this.setState({isConditionsEditorDialogShow: !this.state.isConditionsEditorDialogShow});
}
onCloseEditorDialog = () => {
this.setState({isConditionsEditorDialogShow: false});
}
onUpdateContent = (content) => {
this.setState({ text: content });
}
render() {
let title = this.props.isUpdate ? gettext('Update Terms and Conditions') : gettext('Add Terms and Conditions');
return (
<Fragment>
<Modal isOpen={true} toggle={this.props.toggle}>
<ModalHeader toggle={this.props.toggle}>{title}</ModalHeader>
<ModalBody>
<FormGroup>
<Label for="name">{gettext('Name')}</Label>
<Input id="name" value={this.state.name} onChange={this.handleNameChange}/>
</FormGroup>
<FormGroup>
<Label>{gettext('Version Number')}</Label>
<Input value={this.state.versionNumber} onChange={this.handleVersionNumberChange}/>
</FormGroup>
<FormGroup className="form-content">
<Label>{gettext('Text')}</Label>
<TermsPreviewWidget content={this.state.text} onContentClick={this.onContentClick}/>
</FormGroup>
<FormGroup tag="fieldset">
<Label>{gettext('Activated')}</Label>
<FormGroup check>
<Label check>
<Input type="radio" checked={this.state.isActive} onChange={this.setActive} />
{' '}{gettext('On')}
</Label>
</FormGroup>
<FormGroup check>
<Label check>
<Input type="radio" checked={!this.state.isActive} onChange={this.setInActive} />
{' '}{gettext('Off')}
</Label>
</FormGroup>
</FormGroup>
{this.state.errMsg && <Alert color="danger">{this.state.errMsg}</Alert>}
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.addTerm}>{gettext('Submit')}</Button>
</ModalFooter>
</Modal>
{this.state.isConditionsEditorDialogShow && (
<TermsEditorDialog
content={this.state.text}
onCommit={this.onUpdateContent}
onCloseEditorDialog={this.onCloseEditorDialog}
/>
)}
</Fragment>
);
}
}
AddOrUpdateTermDialog.propTypes = propTypes;
export default AddOrUpdateTermDialog;

View File

@@ -0,0 +1,76 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
import getPreviewContent from '../../utils/markdown-utils';
import { SimpleEditor } from '@seafile/seafile-editor';
import { gettext } from '../../utils/constants';
const propTypes = {
title: PropTypes.string,
content: PropTypes.object,
onCommit: PropTypes.func.isRequired,
onCloseEditorDialog: PropTypes.func.isRequired,
};
class TermsEditorDialog extends React.Component {
static defaultProps = {
title: gettext('Terms'),
content: {text: '', preview: ''}
}
onKeyDown = (event) => {
event.stopPropagation();
}
toggle = () => {
if (this.isContentChanged()) {
let currentContent = this.getCurrentContent();
this.props.onCommit(currentContent);
}
this.props.onCloseEditorDialog();
}
isContentChanged = () => {
return this.simpleEditor.hasContentChange();
}
getCurrentContent = () => {
let markdownContent = this.simpleEditor.getMarkdown();
let { previewText , images, links } = getPreviewContent(markdownContent);
let content = Object.assign({}, this.value, { text: markdownContent, preview: previewText, images: images, links: links });
return content;
}
setSimpleEditorRef = (editor) => {
this.simpleEditor = editor;
}
render() {
let { content, title } = this.props;
return (
<Modal
isOpen={true}
toggle={this.toggle}
onKeyDown={this.onKeyDown}
wrapClassName={'conditions-editor-dialog-wrapper'}
className={'conditions-editor-dialog'}
contentClassName={'conditions-editor-dialog-content'}
size={'lg'}
style={{width: 770}}
>
<ModalHeader className="conditions-editor-dialog-title" toggle={this.toggle}>{title}</ModalHeader>
<ModalBody className={'conditions-editor-dialog-main'}>
<SimpleEditor
onRef={this.setSimpleEditorRef.bind(this)}
value={content.text || ''}
/>
</ModalBody>
</Modal>
);
}
}
TermsEditorDialog.propTypes = propTypes;
export default TermsEditorDialog;

View File

@@ -0,0 +1,46 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
import ConditionsPreviewWidget from './terms-preview-widget';
import { gettext } from '../../utils/constants';
const propTypes = {
title: PropTypes.string,
content: PropTypes.string,
onClosePreviewDialog: PropTypes.func.isRequired,
};
class TermsPreviewDialog extends React.Component {
static defaultProps = {
title: gettext('Terms'),
content: {text: '', perview: ''}
}
toggle = () => {
this.props.onClosePreviewDialog();
}
render() {
let { title, content } = this.props;
return (
<Modal
isOpen={true}
size={'lg'}
style={{width: 600}}
wrapClassName={'conditions-perview-wrapper'}
toggle={this.toggle}
>
<ModalHeader toggle={this.toggle}>{title}</ModalHeader>
<ModalBody>
<ConditionsPreviewWidget content={content} />
</ModalBody>
</Modal>
);
}
}
TermsPreviewDialog.propTypes = propTypes;
export default TermsPreviewDialog;

View File

@@ -0,0 +1,69 @@
import React from 'react';
import PropTypes from 'prop-types';
import { processor } from '@seafile/seafile-editor/dist/utils/seafile-markdown2html';
import Loading from '../loading';
const propTypes = {
content: PropTypes.object,
onContentClick: PropTypes.func,
};
class TermsPreviewWidget extends React.Component {
static defaultProps = {
content: {text: '', perview: ''}
}
constructor(props) {
super(props);
this.state = {
innerHtml: null,
isFormatValue: true,
}
}
componentDidMount() {
let content = this.props.content;
let mdFile = content ? content.text : '';
if (mdFile) {
this.formatterLongTextValue(mdFile);
} else {
this.setState({
isFormatValue: false,
innerHtml: ''
});
}
}
componentWillReceiveProps(nextProps) {
let mdFile = nextProps.content.text;
this.formatterLongTextValue(mdFile);
}
formatterLongTextValue = (mdFile) => {
processor.process(mdFile).then((result) => {
let innerHtml = String(result);
this.setState({
isFormatValue: false,
innerHtml: innerHtml
});
});
}
render() {
if (this.state.isFormatValue) {
return <Loading />;
}
return (
<div className="conditions-preview-container" onClick={this.props.onContentClick}>
<div dangerouslySetInnerHTML={{__html: this.state.innerHtml}}></div>
</div>
);
}
}
TermsPreviewWidget.propTypes = propTypes;
export default TermsPreviewWidget;

View File

@@ -0,0 +1,26 @@
.form-content .conditions-preview-container {
padding: 10px;
min-height: 38px;
max-height: 300px;
overflow: auto;
background-color: #f5f5f5;
}
.conditions-preview-wrapper .conditions-preview-container {
padding: 10px;
min-height: 300px;
overflow: auto;
background-color: #f5f5f5;
}
.conditions-preview-container ol,
.conditions-preview-container ul {
padding-inline-start: 40px;
margin-bottom: 1em;
}
.conditions-editor-dialog-main {
padding: 0;
height: 600px;
overflow: hidden;
}

View File

@@ -59,6 +59,8 @@ import SharePermissionLogs from './logs-page/share-permission-logs';
import AdminOperationLogs from './admin-logs/operation-logs'; import AdminOperationLogs from './admin-logs/operation-logs';
import AdminLoginLogs from './admin-logs/login-logs'; import AdminLoginLogs from './admin-logs/login-logs';
import TermsAndConditions from './terms-and-conditions/terms-and-conditions';
import WebSettings from './web-settings/web-settings'; import WebSettings from './web-settings/web-settings';
import Notifications from './notifications/notifications'; import Notifications from './notifications/notifications';
import FileScanRecords from './file-scan-records'; import FileScanRecords from './file-scan-records';
@@ -197,6 +199,7 @@ class SysAdmin extends React.Component {
<UserGroups path={siteRoot + 'sys/users/:email/groups'} /> <UserGroups path={siteRoot + 'sys/users/:email/groups'} />
<Invitations path={siteRoot + 'sys/invitations'} /> <Invitations path={siteRoot + 'sys/invitations'} />
<TermsAndConditions path={siteRoot + 'sys/terms-and-conditions/'} />
<FileScanRecords <FileScanRecords
path={siteRoot + 'sys/file-scan-records'} path={siteRoot + 'sys/file-scan-records'}

View File

@@ -222,10 +222,14 @@ class SidePanel extends React.Component {
} }
{isDefaultAdmin && enableTermsAndConditions && {isDefaultAdmin && enableTermsAndConditions &&
<li className="nav-item"> <li className="nav-item">
<a className='nav-link ellipsis' href={siteRoot + 'sys/termsadmin/'}> <Link
className={`nav-link ellipsis ${this.getActiveClass('termsandconditions')}`}
to={siteRoot + 'sys/terms-and-conditions/'}
onClick={() => this.props.tabItemClick('termsandconditions')}
>
<span className="sf2-icon-wiki" aria-hidden="true"></span> <span className="sf2-icon-wiki" aria-hidden="true"></span>
<span className="nav-text">{gettext('Terms and Conditions')}</span> <span className="nav-text">{gettext('Terms and Conditions')}</span>
</a> </Link>
</li> </li>
} }
{isPro && canViewAdminLog && {isPro && canViewAdminLog &&

View File

@@ -0,0 +1,84 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../../utils/constants';
import EmptyTip from '../../../components/empty-tip';
import Loading from '../../../components/loading';
import Item from './item';
const propTypes = {
loading: PropTypes.bool.isRequired,
items: PropTypes.array.isRequired,
errorMsg: PropTypes.string,
deleteTerm: PropTypes.func.isRequired,
updateTerm: PropTypes.func.isRequired,
};
class Content extends Component {
constructor(props) {
super(props);
this.state = {
isItemFreezed: false
};
}
onFreezedItem = () => {
this.setState({isItemFreezed: true});
}
onUnfreezedItem = () => {
this.setState({isItemFreezed: false});
}
render() {
const { loading, errorMsg, items } = this.props;
if (loading) {
return <Loading />;
} else if (errorMsg) {
return <p className="error text-center">{errorMsg}</p>;
} else {
const emptyTip = (
<EmptyTip>
<h2>{gettext('No Terms and Conditions.')}</h2>
</EmptyTip>
);
const table = (
<Fragment>
<table className="table-hover">
<thead>
<tr>
<th width="20%">{gettext('Name')}</th>
<th width="10%">{gettext('Version')}</th>
<th width="25%">{gettext('Text')}</th>
<th width="20%">{gettext('Created')}</th>
<th width="20%">{gettext('Activated')}</th>
<th width="5%">{/* operation */}</th>
</tr>
</thead>
{items &&
<tbody>
{items.map((item, index) => {
return (<Item
key={index}
item={item}
isItemFreezed={this.state.isItemFreezed}
onFreezedItem={this.onFreezedItem}
onUnfreezedItem={this.onUnfreezedItem}
deleteTerm={this.props.deleteTerm}
updateTerm={this.props.updateTerm}
/>);
})}
</tbody>
}
</table>
</Fragment>
);
return items.length ? table : emptyTip;
}
}
}
Content.propTypes = propTypes;
export default Content;

View File

@@ -0,0 +1,153 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../../utils/constants';
import { Utils } from '../../../utils/utils';
import moment from 'moment';
import AddOrUpdateTermDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-or-update-term-dialog';
import TermsPerviewDialog from '../../../components/dialog/terms-preview-dialog';
import ModalPortal from '../../../components/modal-portal';
import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog';
import OpMenu from './op-menu';
const propsTypes = {
item: PropTypes.object.isRequired,
isItemFreezed: PropTypes.bool.isRequired,
onFreezedItem: PropTypes.func.isRequired,
onUnfreezedItem: PropTypes.func.isRequired,
deleteTerm: PropTypes.func.isRequired,
updateTerm: PropTypes.func.isRequired,
}
class Item extends Component {
constructor(props) {
super(props);
this.state = {
isOpIconShown: false,
isUpdateDialogOpen: false,
isDeleteDialogOpen: false,
isTermsPerviewDialogOpen: false,
};
}
handleMouseEnter = () => {
if (!this.props.isItemFreezed) {
this.setState({
isOpIconShown: true,
highlight: true
});
}
}
handleMouseLeave = () => {
if (!this.props.isItemFreezed) {
this.setState({
isOpIconShown: false,
highlight: false
});
}
}
toggleUpdateDialog = (e) => {
this.setState({isUpdateDialogOpen: !this.state.isUpdateDialogOpen});
}
toggleDeleteDialog = (e) => {
this.setState({isDeleteDialogOpen: !this.state.isDeleteDialogOpen});
}
toggleTermsContentDialog = (e) => {
this.setState({isTermsPerviewDialogOpen: !this.state.isTermsPerviewDialogOpen})
}
onMenuItemClick = (operation) => {
switch(operation) {
case 'Update':
this.toggleUpdateDialog();
break;
case 'Delete':
this.toggleDeleteDialog();
break;
}
}
onUnfreezedItem = () => {
this.setState({
highlight: false,
isOpIconShow: false
});
this.props.onUnfreezedItem();
}
deleteTerm = () => {
this.props.deleteTerm(this.props.item.id);
this.toggleDeleteDialog();
}
updateTerm = (name, versionNumber, text, isActive) => {
this.props.updateTerm(this.props.item.id, name, versionNumber, text, isActive);
this.toggleUpdateDialog();
}
render() {
let { item } = this.props;
let { isDeleteDialogOpen, isUpdateDialogOpen, isTermsPerviewDialogOpen } = this.state;
let termContent = item.text ? JSON.parse(item.text) : {};
let itemName = '<span class="op-target">' + Utils.HTMLescape(item.name) + '</span>';
let deleteDialogMsg = gettext('Are you sure you want to delete {placeholder} ?').replace('{placeholder}', itemName);
return (
<Fragment>
<tr onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
<td>{item.name}</td>
<td>{item.version_number}</td>
<td className="ellipsis"><a href='#' onClick={this.toggleTermsContentDialog}>{termContent.text}</a></td>
<td>{moment(item.ctime).fromNow()}</td>
<td>{item.activate_time ? moment(item.activate_time).fromNow() : '--'}</td>
<td>
{this.state.isOpIconShown &&
<OpMenu
item={item}
onMenuItemClick={this.onMenuItemClick}
onFreezedItem={this.props.onFreezedItem}
onUnfreezedItem={this.onUnfreezedItem}
/>
}
</td>
</tr>
{isDeleteDialogOpen &&
<ModalPortal>
<CommonOperationConfirmationDialog
title={gettext('Delete T&C')}
message={deleteDialogMsg}
toggleDialog={this.toggleDeleteDialog}
executeOperation={this.deleteTerm}
confirmBtnText={gettext('Delete')}
/>
</ModalPortal>
}
{isUpdateDialogOpen &&
<ModalPortal>
<AddOrUpdateTermDialog
updateTerm={this.updateTerm}
toggle={this.toggleUpdateDialog}
isUpdate={true}
oldTermObj={item}
/>
</ModalPortal>
}
{isTermsPerviewDialogOpen &&
<ModalPortal>
<TermsPerviewDialog
content={termContent}
onClosePreviewDialog={this.toggleTermsContentDialog}
/>
</ModalPortal>
}
</Fragment>
);
}
}
Item.propTypes = propsTypes;
export default Item;

View File

@@ -0,0 +1,85 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
import { gettext } from '../../../utils/constants';
import { Utils } from '../../../utils/utils';
const propTypes = {
item: PropTypes.object.isRequired,
onFreezedItem: PropTypes.func.isRequired,
onUnfreezedItem: PropTypes.func.isRequired,
onMenuItemClick: PropTypes.func.isRequired,
};
class OpMenu extends React.Component {
constructor(props) {
super(props);
this.state = {
isItemMenuShow: false
};
}
onMenuItemClick = (e) => {
let operation = Utils.getEventData(e, 'op');
this.props.onMenuItemClick(operation);
}
onDropdownToggleClick = (e) => {
this.toggleOperationMenu(e);
}
toggleOperationMenu = (e) => {
this.setState(
{isItemMenuShow: !this.state.isItemMenuShow},
() => {
if (this.state.isItemMenuShow) {
this.props.onFreezedItem();
} else {
this.props.onUnfreezedItem();
}
}
);
}
translateOperations = (item) => {
let translateResult = '';
switch(item) {
case 'Update':
translateResult = gettext('Update');
break;
case 'Delete':
translateResult = gettext('Delete');
break;
default:
break;
}
return translateResult;
}
render() {
let operations = ['Update', 'Delete'];
return (
<Dropdown isOpen={this.state.isItemMenuShow} toggle={this.toggleOperationMenu}>
<DropdownToggle
tag="i"
className="sf-dropdown-toggle fa fa-ellipsis-v"
title={gettext('More Operations')}
data-toggle="dropdown"
aria-expanded={this.state.isItemMenuShow}
/>
<DropdownMenu className="mt-2 mr-2">
{operations.map((item, index )=> {
return (<DropdownItem key={index} data-op={item} onClick={this.onMenuItemClick}>{this.translateOperations(item)}</DropdownItem>);
})}
</DropdownMenu>
</Dropdown>
);
}
}
OpMenu.propTypes = propTypes;
export default OpMenu;

View File

@@ -0,0 +1,132 @@
import React, { Component, Fragment } from 'react';
import { Button } from 'reactstrap';
import { Utils } from '../../../utils/utils';
import { seafileAPI } from '../../../utils/seafile-api';
import { gettext, loginUrl } from '../../../utils/constants';
import AddOrUpdateTermDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-or-update-term-dialog';
import ModalPortal from '../../../components/modal-portal';
import toaster from '../../../components/toast';
import MainPanelTopbar from '../main-panel-topbar';
import Content from './content';
class TermsAndConditions extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
errorMsg: '',
termList: [],
isAddTermDialogOpen: false,
};
}
toggleAddTermDialog = () => {
this.setState({isAddTermDialogOpen: !this.state.isAddTermDialogOpen});
}
componentDidMount () {
seafileAPI.sysAdminListTermsAndConditions().then((res) => {
this.setState({
termList: res.data.term_and_condition_list,
loading: false,
});
}).catch((error) => {
if (error.response) {
if (error.response.status == 403) {
this.setState({
loading: false,
errorMsg: gettext('Permission denied')
});
location.href = `${loginUrl}?next=${encodeURIComponent(location.href)}`;
} else {
this.setState({
loading: false,
errorMsg: gettext('Error')
});
}
} else {
this.setState({
loading: false,
errorMsg: gettext('Please check the network.')
});
}
});
}
addTerm = (name, versionNumber, text, isActive) => {
seafileAPI.sysAdminAddTermAndCondition(name, versionNumber, text, isActive).then(res => {
// After adding the terms, you need to refresh the page.
location.reload();
}).catch((error) => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
updateTerm = (termID, name, versionNumber, text, isActive) => {
seafileAPI.sysAdminUpdateTermAndCondition(termID, name, versionNumber, text, isActive).then(res => {
let termList = this.state.termList.map(item => {
if (item.id == termID) {
return res.data;
} else {
return item;
}
});
this.setState({termList: termList});
toaster.success(gettext('Successfully update.'));
}).catch((error) => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
deleteTerm = (termID) => {
seafileAPI.sysAdminDeleteTermAndCondition(termID).then(res => {
let termList = this.state.termList.filter(item => item.id != termID);
this.setState({termList: termList});
toaster.success(gettext('Successfully deleted.'));
}).catch((error) => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
render() {
let { termList, isAddTermDialogOpen } = this.state;
return (
<Fragment>
<MainPanelTopbar>
<Button className="btn btn-secondary operation-item" onClick={this.toggleAddTermDialog}>{gettext('Add')}</Button>
</MainPanelTopbar>
<div className="main-panel-center flex-row">
<div className="cur-view-container">
<div className="cur-view-path">
<h3 className="sf-heading">{gettext('Terms and Conditions')}</h3>
</div>
<div className="cur-view-content">
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={termList}
deleteTerm={this.deleteTerm}
updateTerm={this.updateTerm}
/>
</div>
</div>
</div>
{isAddTermDialogOpen &&
<ModalPortal>
<AddOrUpdateTermDialog
isUpdate={false}
addTerm={this.addTerm}
toggle={this.toggleAddTermDialog}
/>
</ModalPortal>
}
</Fragment>
);
}
}
export default TermsAndConditions;

View File

@@ -0,0 +1,54 @@
const hrefReg = /\[.+\]\(\S+\)|<img src=(\S+).+\/>|!\[\]\(\S+\)|<\S+>/g,
imageReg1 = /^<img src="(\S+)" .+\/>/,
imageReg2 = /^!\[\]\((\S+)\)/,
linkReg1 = /^\[.+\]\(\S+\)/,
linkReg2 = /^<\S+>$/;
const getLinks = (hrefs) => {
const hrefObj = {
links: [],
images: []
};
hrefs.forEach((href) => {
if (href.search(linkReg1) >= 0 || href.search(linkReg2) >= 0) {
hrefObj.links.push(href);
} else {
let imageSrcs = href.match(imageReg1);
let imageSrcs1 = href.match(imageReg2);
if (imageSrcs) {
hrefObj.images.push(imageSrcs[1]);
} else if (imageSrcs1) {
hrefObj.images.push(imageSrcs1[1]);
}
}
});
return hrefObj;
};
const getPreviewContent = (markdownContent) => {
let previewText = '';
let newMarkdownContent = markdownContent.replace(hrefReg, '');
for (let index = 0; index < newMarkdownContent.length; index++) {
if (newMarkdownContent[index] === '#') {
continue;
} else if (newMarkdownContent[index] === '\n') {
previewText += ' ';
} else {
previewText += newMarkdownContent[index];
}
if (previewText.length === 30) {
break;
}
}
const hrefs = markdownContent.match(hrefReg);
if (hrefs) {
const { images, links } = getLinks(hrefs);
return { previewText, images, links };
}
return { previewText, images: [], links: [] };
};
export default getPreviewContent;

View File

@@ -733,6 +733,7 @@ urlpatterns = [
url(r'^sys/institutions/(?P<inst_id>\d+)/info/$', sysadmin_react_fake_view, name="sys_institution_info"), url(r'^sys/institutions/(?P<inst_id>\d+)/info/$', sysadmin_react_fake_view, name="sys_institution_info"),
url(r'^sys/institutions/(?P<inst_id>\d+)/members/$', sysadmin_react_fake_view, name="sys_institution_members"), url(r'^sys/institutions/(?P<inst_id>\d+)/members/$', sysadmin_react_fake_view, name="sys_institution_members"),
url(r'^sys/institutions/(?P<inst_id>\d+)/admins/$', sysadmin_react_fake_view, name="sys_institution_admins"), url(r'^sys/institutions/(?P<inst_id>\d+)/admins/$', sysadmin_react_fake_view, name="sys_institution_admins"),
url(r'^sys/terms-and-conditions/$', sysadmin_react_fake_view, name="terms_and_conditions"),
url(r'^sys/share-links/$', sysadmin_react_fake_view, name="sys_share_links"), url(r'^sys/share-links/$', sysadmin_react_fake_view, name="sys_share_links"),
url(r'^sys/upload-links/$', sysadmin_react_fake_view, name="sys_upload_links"), url(r'^sys/upload-links/$', sysadmin_react_fake_view, name="sys_upload_links"),
url(r'^sys/work-weixin/$', sysadmin_react_fake_view, name="sys_work_weixin"), url(r'^sys/work-weixin/$', sysadmin_react_fake_view, name="sys_work_weixin"),