mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-21 03:18:23 +00:00
merge master to 13.0
This commit is contained in:
79
frontend/package-lock.json
generated
79
frontend/package-lock.json
generated
@@ -5262,15 +5262,9 @@
|
||||
"@seafile/react-image-lightbox": "4.0.0",
|
||||
"@seafile/slate": "0.91.8",
|
||||
"@seafile/slate-history": "0.86.2",
|
||||
"@seafile/slate-hyperscript": "0.81.7",
|
||||
"@seafile/slate-react": "0.92.7",
|
||||
"axios": "^1.7.4",
|
||||
"classnames": "2.3.2",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"dayjs": "1.10.7",
|
||||
"deep-copy": "1.4.2",
|
||||
"dtable-ui-component": "^5.1.9",
|
||||
"is-hotkey": "0.2.0",
|
||||
"is-url": "^1.2.4",
|
||||
"lodash.isequal": "4.5.0",
|
||||
"lodash.throttle": "4.1.1",
|
||||
@@ -5292,7 +5286,7 @@
|
||||
"remark-rehype": "11.0.0",
|
||||
"remark-stringify": "11.0.0",
|
||||
"slugid": "3.2.0",
|
||||
"socket.io-client": "4.6.1",
|
||||
"socket.io-client": "4.8.1",
|
||||
"type-of": "2.0.1",
|
||||
"unified": "11.0.3",
|
||||
"url-join": "4.0.1",
|
||||
@@ -5316,9 +5310,8 @@
|
||||
},
|
||||
"node_modules/@seafile/sdoc-editor/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.7.tgz",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
@@ -5332,23 +5325,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@seafile/sdoc-editor/node_modules/engine.io-client": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/engine.io-client/-/engine.io-client-6.4.0.tgz",
|
||||
"integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==",
|
||||
"license": "MIT",
|
||||
"version": "6.6.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
|
||||
"integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.0.3",
|
||||
"ws": "~8.11.0",
|
||||
"xmlhttprequest-ssl": "~2.0.0"
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1",
|
||||
"xmlhttprequest-ssl": "~2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@seafile/sdoc-editor/node_modules/engine.io-parser": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/engine.io-parser/-/engine.io-parser-5.0.7.tgz",
|
||||
"integrity": "sha512-P+jDFbvK6lE3n1OL+q9KuzdOFWkkZ/cMV9gol/SbVfpyqfvrfrFTOFJ6fQm2VC3PZHlU3QPhVwmbsCnauHF2MQ==",
|
||||
"license": "MIT",
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
@@ -5366,15 +5357,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@seafile/sdoc-editor/node_modules/socket.io-client": {
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/socket.io-client/-/socket.io-client-4.6.1.tgz",
|
||||
"integrity": "sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==",
|
||||
"license": "MIT",
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
|
||||
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.4.0",
|
||||
"socket.io-parser": "~4.2.1"
|
||||
"engine.io-client": "~6.6.1",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
@@ -5382,9 +5372,8 @@
|
||||
},
|
||||
"node_modules/@seafile/sdoc-editor/node_modules/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
@@ -5423,16 +5412,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@seafile/sdoc-editor/node_modules/ws": {
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmmirror.com/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"license": "MIT",
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
@@ -5444,9 +5432,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@seafile/sdoc-editor/node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
||||
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
@@ -5499,6 +5487,20 @@
|
||||
"xtend": "4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@seafile/seafile-editor/node_modules/@seafile/react-image-lightbox": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@seafile/react-image-lightbox/-/react-image-lightbox-3.0.4.tgz",
|
||||
"integrity": "sha512-dhvFAyTT/pEANWq3iWmheze6IuIJyXAGcQHR1Jhy3Chq1hdmlPnGTEzeNaM2r7wCMn7tSDvev+8n4DiiliqRgg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.8.1",
|
||||
"react-modal": "^3.16.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.x || ^17.x",
|
||||
"react-dom": "^16.x || ^17.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@seafile/seafile-editor/node_modules/bail": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/bail/-/bail-2.0.2.tgz",
|
||||
@@ -5743,9 +5745,8 @@
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
||||
"license": "MIT"
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
|
||||
},
|
||||
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
||||
"version": "2.2.3",
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
||||
|
||||
import '../../css/single-dropdown-toolbar.css';
|
||||
|
||||
const propTypes = {
|
||||
withPlusIcon: PropTypes.bool,
|
||||
opList: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
@@ -37,8 +38,7 @@ class SingleDropdownToolbar extends React.Component {
|
||||
const { opList, withPlusIcon = false } = this.props;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Dropdown isOpen={this.state.isDropdownMenuOpen} toggle={this.toggleDropdownMenu}>
|
||||
<Dropdown isOpen={this.state.isDropdownMenuOpen} toggle={this.toggleDropdownMenu} direction="down">
|
||||
<DropdownToggle
|
||||
tag="span"
|
||||
role="button"
|
||||
@@ -47,12 +47,10 @@ class SingleDropdownToolbar extends React.Component {
|
||||
onKeyDown={this.onDropdownToggleKeyDown}
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
{withPlusIcon
|
||||
? (<><i className="sf3-font-new sf3-font main-icon"></i><i className="sf3-font-down sf3-font"></i></>)
|
||||
: <i className="sf3-font-down sf3-font"></i>
|
||||
}
|
||||
{withPlusIcon && <i className="sf3-font-new sf3-font main-icon"></i>}
|
||||
<i className="sf3-font-down sf3-font"></i>
|
||||
</DropdownToggle>
|
||||
<DropdownMenu>
|
||||
<DropdownMenu positionFixed={true}>
|
||||
{opList.map((item, index) => {
|
||||
if (item == 'Divider') {
|
||||
return <DropdownItem key={index} divider />;
|
||||
@@ -66,7 +64,6 @@ class SingleDropdownToolbar extends React.Component {
|
||||
})}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -334,12 +334,6 @@ img[src=""],img:not([src]) { /* for first loading img*/
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.path-container .add-wiki-dropdown .sf3-font-down {
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dir-tool>div {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
@@ -369,6 +369,14 @@ class MetadataManagerAPI {
|
||||
return this.req.post(url, params);
|
||||
};
|
||||
|
||||
setPeoplePhoto = (repoID, peopleId, recordId) => {
|
||||
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/people-cover-photo/' + peopleId + '/';
|
||||
const params = {
|
||||
record_id: recordId
|
||||
};
|
||||
return this.req.put(url, params);
|
||||
};
|
||||
|
||||
// ocr
|
||||
openOCR = (repoID) => {
|
||||
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/ocr/';
|
||||
|
@@ -179,6 +179,14 @@ class Context {
|
||||
return viewId === FACE_RECOGNITION_VIEW_ID;
|
||||
};
|
||||
|
||||
canSetPeoplePhoto = () => {
|
||||
const viewId = this.getSetting('viewID');
|
||||
if (this.permission === 'r' || viewId !== FACE_RECOGNITION_VIEW_ID) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
restoreRows = () => {
|
||||
// todo
|
||||
};
|
||||
@@ -282,6 +290,11 @@ class Context {
|
||||
return this.metadataAPI.removePeoplePhotos(repoID, recordId, photoIds);
|
||||
};
|
||||
|
||||
setPeoplePhoto = (recordId, photoId) => {
|
||||
const repoID = this.settings['repoID'];
|
||||
return this.metadataAPI.setPeoplePhoto(repoID, recordId, photoId);
|
||||
};
|
||||
|
||||
// file tag
|
||||
updateFileTags = (data) => {
|
||||
const repoID = this.settings['repoID'];
|
||||
|
@@ -622,6 +622,14 @@ class Store {
|
||||
this.applyOperation(operation);
|
||||
};
|
||||
|
||||
setPeoplePhoto = (peopleId, selectedPhoto) => {
|
||||
const type = OPERATION_TYPE.SET_PEOPLE_COVER_PHOTO;
|
||||
const operation = this.createOperation({
|
||||
type, repo_id: this.repoId, people_id: peopleId, selected_photo: selectedPhoto
|
||||
});
|
||||
this.applyOperation(operation);
|
||||
};
|
||||
|
||||
// tag
|
||||
updateFileTags = (data) => {
|
||||
const type = OPERATION_TYPE.UPDATE_FILE_TAGS;
|
||||
|
@@ -32,6 +32,7 @@ export const OPERATION_TYPE = {
|
||||
DELETE_PEOPLE_PHOTOS: 'delete_people_photos',
|
||||
REMOVE_PEOPLE_PHOTOS: 'remove_people_photos',
|
||||
ADD_PEOPLE_PHOTOS: 'add_people_photos',
|
||||
SET_PEOPLE_COVER_PHOTO: 'set_people_cover_photo',
|
||||
|
||||
// tag
|
||||
UPDATE_FILE_TAGS: 'update_file_tags',
|
||||
@@ -72,6 +73,7 @@ export const OPERATION_ATTRIBUTES = {
|
||||
[OPERATION_TYPE.DELETE_PEOPLE_PHOTOS]: ['repo_id', 'people_id', 'deleted_photos'],
|
||||
[OPERATION_TYPE.REMOVE_PEOPLE_PHOTOS]: ['repo_id', 'people_id', 'removed_photos', 'success_callback'],
|
||||
[OPERATION_TYPE.ADD_PEOPLE_PHOTOS]: ['repo_id', 'people_id', 'old_people_id', 'added_photos', 'success_callback'],
|
||||
[OPERATION_TYPE.SET_PEOPLE_COVER_PHOTO]: ['repo_id', 'people_id', 'selected_photo'],
|
||||
|
||||
[OPERATION_TYPE.MODIFY_SETTINGS]: ['repo_id', 'view_id', 'settings'],
|
||||
|
||||
|
@@ -234,6 +234,15 @@ class ServerOperator {
|
||||
});
|
||||
break;
|
||||
}
|
||||
case OPERATION_TYPE.SET_PEOPLE_COVER_PHOTO: {
|
||||
const { people_id, selected_photo } = operation;
|
||||
window.sfMetadataContext.setPeoplePhoto(people_id, selected_photo).then(res => {
|
||||
callback({ operation });
|
||||
}).catch(error => {
|
||||
callback({ error: gettext('Failed to set people cover photo') });
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// tags
|
||||
case OPERATION_TYPE.UPDATE_FILE_TAGS: {
|
||||
|
@@ -31,6 +31,10 @@ const FaceRecognition = () => {
|
||||
store.removePeoplePhotos(peopleId, peoplePhotos, { success_callback });
|
||||
}, [store]);
|
||||
|
||||
const onSetPeoplePhoto = useCallback((peopleId, peoplePhoto) => {
|
||||
store.setPeoplePhoto(peopleId, peoplePhoto);
|
||||
}, [store]);
|
||||
|
||||
const openPeople = useCallback((people) => {
|
||||
peopleRef.current = people;
|
||||
const name = people._is_someone ? (people._name || gettext('Person image')) : gettext('Unknown people');
|
||||
@@ -64,6 +68,7 @@ const FaceRecognition = () => {
|
||||
onAddPeoplePhotos={onAddPeoplePhotos}
|
||||
onRemovePeoplePhotos={onRemovePeoplePhotos}
|
||||
onDeletePeoplePhotos={onDeletePeoplePhotos}
|
||||
onSetPeoplePhoto={onSetPeoplePhoto}
|
||||
/>
|
||||
) : (
|
||||
<Peoples peoples={peoples} onRename={onRename} onOpenPeople={openPeople} />
|
||||
|
@@ -22,7 +22,7 @@ import '../../gallery/index.css';
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
const PeoplePhotos = ({ view, people, onClose, onDeletePeoplePhotos, onAddPeoplePhotos, onRemovePeoplePhotos }) => {
|
||||
const PeoplePhotos = ({ view, people, onClose, onDeletePeoplePhotos, onAddPeoplePhotos, onSetPeoplePhoto, onRemovePeoplePhotos }) => {
|
||||
const [isLoading, setLoading] = useState(true);
|
||||
const [isLoadingMore, setLoadingMore] = useState(false);
|
||||
const [metadata, setMetadata] = useState({ rows: [] });
|
||||
@@ -148,6 +148,11 @@ const PeoplePhotos = ({ view, people, onClose, onDeletePeoplePhotos, onAddPeople
|
||||
});
|
||||
}, [people, onAddPeoplePhotos, deletedByIds]);
|
||||
|
||||
const handleSetPeoplePhoto = useCallback((selectedImage) => {
|
||||
const { id } = selectedImage;
|
||||
onSetPeoplePhoto(people._id, id);
|
||||
}, [people, onSetPeoplePhoto]);
|
||||
|
||||
const loadData = useCallback((view) => {
|
||||
setLoading(true);
|
||||
metadataAPI.getPeoplePhotos(repoID, people._id, 0, PER_LOAD_NUMBER).then(res => {
|
||||
@@ -245,6 +250,7 @@ const PeoplePhotos = ({ view, people, onClose, onDeletePeoplePhotos, onAddPeople
|
||||
onDelete={handelDelete}
|
||||
onRemoveImage={people._is_someone ? handelRemove : null}
|
||||
onAddImage={!people._is_someone ? handelAdd : null}
|
||||
onSetPeoplePhoto={handleSetPeoplePhoto}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@@ -18,10 +18,11 @@ const CONTEXT_MENU_KEY = {
|
||||
DELETE: 'delete',
|
||||
DUPLICATE: 'duplicate',
|
||||
REMOVE: 'remove',
|
||||
SET_PEOPLE_PHOTO: 'set_people_photo',
|
||||
ADD_PHOTO_TO_GROUP: 'add_photo_to_group',
|
||||
};
|
||||
|
||||
const GalleryContextMenu = ({ metadata, selectedImages, onDelete, onDuplicate, addFolder, onRemoveImage, onAddImage }) => {
|
||||
const GalleryContextMenu = ({ metadata, selectedImages, onDelete, onDuplicate, addFolder, onRemoveImage, onAddImage, onSetPeoplePhoto }) => {
|
||||
const [isZipDialogOpen, setIsZipDialogOpen] = useState(false);
|
||||
const [isCopyDialogOpen, setIsCopyDialogOpen] = useState(false);
|
||||
const [isPeoplesDialogShow, setPeoplesDialogShow] = useState(false);
|
||||
@@ -31,6 +32,7 @@ const GalleryContextMenu = ({ metadata, selectedImages, onDelete, onDuplicate, a
|
||||
const canDuplicateRow = window.sfMetadataContext.canDuplicateRow();
|
||||
const canRemovePhotoFromPeople = window.sfMetadataContext.canRemovePhotoFromPeople();
|
||||
const canAddPhotoToPeople = window.sfMetadataContext.canAddPhotoToPeople();
|
||||
const canSetPeoplePhoto = window.sfMetadataContext.canSetPeoplePhoto();
|
||||
|
||||
const options = useMemo(() => {
|
||||
let validOptions = [{ value: CONTEXT_MENU_KEY.DOWNLOAD, label: gettext('Download') }];
|
||||
@@ -46,8 +48,11 @@ const GalleryContextMenu = ({ metadata, selectedImages, onDelete, onDuplicate, a
|
||||
if (onAddImage && canAddPhotoToPeople) {
|
||||
validOptions.push({ value: CONTEXT_MENU_KEY.ADD_PHOTO_TO_GROUP, label: gettext('Add to group') });
|
||||
}
|
||||
if (onSetPeoplePhoto && canSetPeoplePhoto) {
|
||||
validOptions.push({ value: CONTEXT_MENU_KEY.SET_PEOPLE_PHOTO, label: gettext('Set as cover photo') });
|
||||
}
|
||||
return validOptions;
|
||||
}, [checkCanDeleteRow, canDuplicateRow, canRemovePhotoFromPeople, canAddPhotoToPeople, selectedImages, onDuplicate, onDelete, onRemoveImage, onAddImage]);
|
||||
}, [checkCanDeleteRow, canDuplicateRow, canRemovePhotoFromPeople, canAddPhotoToPeople, selectedImages, onDuplicate, onDelete, onRemoveImage, onAddImage, canSetPeoplePhoto, onSetPeoplePhoto]);
|
||||
|
||||
const closeZipDialog = () => {
|
||||
setIsZipDialogOpen(false);
|
||||
@@ -104,8 +109,13 @@ const GalleryContextMenu = ({ metadata, selectedImages, onDelete, onDuplicate, a
|
||||
case CONTEXT_MENU_KEY.ADD_PHOTO_TO_GROUP:
|
||||
setPeoplesDialogShow(true);
|
||||
break;
|
||||
case CONTEXT_MENU_KEY.SET_PEOPLE_PHOTO:
|
||||
onSetPeoplePhoto(selectedImages[0]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, [handleDownload, onDelete, selectedImages, toggleCopyDialog, onRemoveImage]);
|
||||
}, [handleDownload, onDelete, selectedImages, toggleCopyDialog, onRemoveImage, onSetPeoplePhoto]);
|
||||
|
||||
const closePeoplesDialog = useCallback(() => {
|
||||
setPeoplesDialogShow(false);
|
||||
|
@@ -19,7 +19,7 @@ import './index.css';
|
||||
|
||||
const OVER_SCAN_ROWS = 20;
|
||||
|
||||
const Main = ({ isLoadingMore, metadata, onDelete, onLoadMore, duplicateRecord, onAddFolder, onRemoveImage, onAddImage }) => {
|
||||
const Main = ({ isLoadingMore, metadata, onDelete, onLoadMore, duplicateRecord, onAddFolder, onRemoveImage, onAddImage, onSetPeoplePhoto }) => {
|
||||
const [isFirstLoading, setFirstLoading] = useState(true);
|
||||
const [zoomGear, setZoomGear] = useState(0);
|
||||
const [containerWidth, setContainerWidth] = useState(0);
|
||||
@@ -237,6 +237,13 @@ const Main = ({ isLoadingMore, metadata, onDelete, onLoadMore, duplicateRecord,
|
||||
});
|
||||
}, [onRemoveImage, updateCurrentDirent]);
|
||||
|
||||
const handleMakeSelectedAsCoverPhoto = useCallback((selectedImage) => {
|
||||
onSetPeoplePhoto(selectedImage, () => {
|
||||
updateCurrentDirent();
|
||||
setSelectedImages([]);
|
||||
});
|
||||
}, [onSetPeoplePhoto, updateCurrentDirent]);
|
||||
|
||||
const handleClickOutside = useCallback((event) => {
|
||||
const className = getEventClassName(event);
|
||||
const isClickInsideImage = className.includes('metadata-gallery-image-item') || className.includes('metadata-gallery-grid-image');
|
||||
@@ -401,6 +408,7 @@ const Main = ({ isLoadingMore, metadata, onDelete, onLoadMore, duplicateRecord,
|
||||
addFolder={onAddFolder}
|
||||
onRemoveImage={onRemoveImage ? handelRemoveSelectedImages : null}
|
||||
onAddImage={onAddImage}
|
||||
onSetPeoplePhoto={handleMakeSelectedAsCoverPhoto}
|
||||
/>
|
||||
{isImagePopupOpen && (
|
||||
<ModalPortal>
|
||||
|
@@ -102,12 +102,12 @@ class GroupItem extends React.Component {
|
||||
<h4 className="sf-heading m-0 d-flex align-items-center">
|
||||
<span className={`${group.parent_group_id == 0 ? 'sf3-font-group' : 'sf3-font-department'} sf3-font nav-icon`} aria-hidden="true"></span>
|
||||
<a href={`${siteRoot}group/${group.id}/`} title={group.name} className="ellipsis">{group.name}</a>
|
||||
{isDeptAdmin && (
|
||||
{isDeptAdmin &&
|
||||
<SingleDropdownToolbar
|
||||
withPlusIcon={true}
|
||||
opList={[{ 'text': gettext('New Library'), 'onClick': this.toggleCreateRepoDialog }]}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
</h4>
|
||||
</div>
|
||||
{group.repos.length === 0 ?
|
||||
|
@@ -277,6 +277,7 @@ class InvitationsView extends React.Component {
|
||||
<div className="d-flex">
|
||||
<h3 className="sf-heading">{gettext('Invite Guest')}</h3>
|
||||
<SingleDropdownToolbar
|
||||
withPlusIcon={true}
|
||||
opList={[{ 'text': gettext('Invite Guest'), 'onClick': this.toggleInvitePeopleDialog }]}
|
||||
/>
|
||||
</div>
|
||||
|
@@ -80,7 +80,7 @@ class Content extends Component {
|
||||
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortByFileCount}>{gettext('Files')} {sortBy == 'file_count' ? sortIcon : initialSortIcon}</a>{' / '}
|
||||
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortBySize}>{gettext('Size')} {sortBy == 'size' ? sortIcon : initialSortIcon}</a>
|
||||
</Fragment> :
|
||||
gettext('Files') / gettext('Size')
|
||||
<>{gettext('Files')}{' / '}{gettext('Size')}</>
|
||||
}
|
||||
</th>
|
||||
<Fragment>
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
||||
import { gettext, canCreateWiki } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import toaster from '../../components/toast';
|
||||
@@ -12,6 +11,7 @@ import WikiCardView from '../../components/wiki-card-view/wiki-card-view';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { userAPI } from '../../utils/user-api';
|
||||
import WikiConvertStatusDialog from '../../components/dialog/wiki-convert-status-dialog';
|
||||
import SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar';
|
||||
|
||||
|
||||
const propTypes = {
|
||||
@@ -30,7 +30,6 @@ class Wikis extends Component {
|
||||
groupWikis: [],
|
||||
isShowAddWikiMenu: false,
|
||||
isShowAddDialog: false,
|
||||
isDropdownMenuShown: false,
|
||||
isShowConvertStatusDialog: false,
|
||||
};
|
||||
}
|
||||
@@ -308,13 +307,6 @@ class Wikis extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
toggleDropdownMenu = (e) => {
|
||||
e.stopPropagation();
|
||||
this.setState({
|
||||
isDropdownMenuShown: !this.state.isDropdownMenuShown
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Fragment>
|
||||
@@ -338,20 +330,10 @@ class Wikis extends Component {
|
||||
<div className="path-container">
|
||||
<h3 className="sf-heading m-0">{gettext('Wikis')}</h3>
|
||||
{canCreateWiki &&
|
||||
<Dropdown
|
||||
direction="down"
|
||||
className="add-wiki-dropdown"
|
||||
isOpen={this.state.isDropdownMenuShown}
|
||||
toggle={this.toggleDropdownMenu}
|
||||
onMouseMove={(e) => {e.stopPropagation();}}
|
||||
>
|
||||
<DropdownToggle tag="i" className="px-1">
|
||||
<span className="sf3-font sf3-font-down" aria-hidden="true"></span>
|
||||
</DropdownToggle>
|
||||
<DropdownMenu positionFixed={true}>
|
||||
<DropdownItem onClick={() => {this.toggleAddWikiDialog();}}>{gettext('Add Wiki')}</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
<SingleDropdownToolbar
|
||||
withPlusIcon={true}
|
||||
opList={[{ 'text': gettext('Add Wiki'), 'onClick': () => this.toggleAddWikiDialog() }]}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2,11 +2,13 @@ import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import SharedFileView from './components/shared-file-view/shared-file-view';
|
||||
import SharedFileViewTip from './components/shared-file-view/shared-file-view-tip';
|
||||
import { gettext } from './utils/constants';
|
||||
import { Utils } from './utils/utils';
|
||||
import { gettext, siteRoot } from './utils/constants';
|
||||
|
||||
import './css/image-file-view.css';
|
||||
|
||||
const { fileName, rawPath, err, prevImgPath, nextImgPath } = window.shared.pageOptions;
|
||||
const { fileName, rawPath, err, prevImgPath, nextImgPath, repoEncrypted, fileExt, filePath, sharedToken } = window.shared.pageOptions;
|
||||
const { thumbnailSizeForOriginal } = window.app.pageOptions;
|
||||
|
||||
const prevImgURL = `?p=${encodeURIComponent(prevImgPath)}`;
|
||||
const nextImgURL = `?p=${encodeURIComponent(nextImgPath)}`;
|
||||
@@ -35,6 +37,13 @@ class FileContent extends React.Component {
|
||||
return <SharedFileViewTip />;
|
||||
}
|
||||
|
||||
// some types of images, the browser can't preview them directly, so we need to get thumbnails for them
|
||||
let thumbnailURL = '';
|
||||
const fileExtList = ['tif', 'tiff', 'psd', 'heic'];
|
||||
if (!repoEncrypted && fileExtList.includes(fileExt)) {
|
||||
thumbnailURL = `${siteRoot}thumbnail/${sharedToken}/${thumbnailSizeForOriginal}${Utils.encodePath(filePath)}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="shared-file-view-body d-flex text-center">
|
||||
<div className="image-file-view flex-1">
|
||||
@@ -44,7 +53,7 @@ class FileContent extends React.Component {
|
||||
{nextImgPath && (
|
||||
<a href={nextImgURL} id="img-next" title={gettext('you can also press →')}><span className="sf3-font sf3-font-down rotate-270 d-inline-block"></span></a>
|
||||
)}
|
||||
<img src={rawPath} alt={fileName} id="image-view" />
|
||||
<img src={thumbnailURL || rawPath} alt={fileName} id="image-view" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@@ -19,7 +19,7 @@ const TagsTreeView = ({ currentPath }) => {
|
||||
const [keyTreeNodeExpandedMap, setKeyTreeNodeExpandedMap] = useState({});
|
||||
|
||||
const recordsTree = useMemo(() => {
|
||||
return tagsData.rows_tree || [];
|
||||
return (tagsData && tagsData.rows_tree) || [];
|
||||
}, [tagsData]);
|
||||
|
||||
const buildTree = useCallback((roots, tree) => {
|
||||
@@ -40,6 +40,7 @@ const TagsTreeView = ({ currentPath }) => {
|
||||
}, [recordsTree, buildTree]);
|
||||
|
||||
const getKeyTreeNodeExpandedMap = useCallback(() => {
|
||||
if (!window.sfTagsDataContext || !window.sfTagsDataContext.localStorage) return {};
|
||||
const strKeyTreeNodeExpandedMap = window.sfTagsDataContext.localStorage.getItem(LOCAL_KEY_TREE_NODE_EXPANDED);
|
||||
if (strKeyTreeNodeExpandedMap) {
|
||||
try {
|
||||
|
@@ -15,7 +15,7 @@ const TagsTableSearcher = () => {
|
||||
const [searchResult, setSearchResult] = useState(null);
|
||||
|
||||
const recordsTree = useMemo(() => {
|
||||
return tagsData.rows_tree || [];
|
||||
return (tagsData && tagsData.rows_tree) || [];
|
||||
}, [tagsData]);
|
||||
|
||||
const recordsCount = useMemo(() => {
|
||||
|
@@ -573,7 +573,7 @@
|
||||
"Five_column": "5 columns",
|
||||
"Full_width_mode": "Full width mode",
|
||||
"Video": "Video",
|
||||
"Upload_local_video": "Upload_local_video",
|
||||
"Upload_local_video": "Upload local video",
|
||||
"The_current_version_does_not_support_>5MB_video_file": "The_current_version_does_not_support_>5MB_video_file",
|
||||
"Token_expired_Please_refresh_the_page": "Token expired. Please refresh the page.",
|
||||
"Link_to_page": "Link to page",
|
||||
@@ -607,6 +607,12 @@
|
||||
"Enter_reply": "Enter reply",
|
||||
"Processing_content_cannot_be_empty": "Processing content cannot be empty",
|
||||
"AI_error_message": "Request error, please try again",
|
||||
"Add_video_link": "Add video link",
|
||||
"Link_Seafile_video_file": "Link Seafile video file",
|
||||
"Select_video_file": "Select video file",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload."
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
|
||||
"Image_copy_error": "Image copy error"
|
||||
|
||||
}
|
||||
|
@@ -573,7 +573,7 @@
|
||||
"Five_column": "5 Spalten",
|
||||
"Full_width_mode": "Full width mode",
|
||||
"Video": "Video",
|
||||
"Upload_local_video": "Upload_local_video",
|
||||
"Upload_local_video": "Lokales Video hochladen",
|
||||
"The_current_version_does_not_support_>5MB_video_file": "The_current_version_does_not_support_>5MB_video_file",
|
||||
"Token_expired_Please_refresh_the_page": "Der Token ist abgelaufen. Laden Sie die Seite neu.",
|
||||
"Link_to_page": "Link to page",
|
||||
@@ -607,6 +607,11 @@
|
||||
"Enter_reply": "Antwort eingeben",
|
||||
"Processing_content_cannot_be_empty": "Processing content cannot be empty",
|
||||
"AI_error_message": "Request error, please try again",
|
||||
"Add_video_link": "Videolink hinzufügen",
|
||||
"Link_Seafile_video_file": "Link Seafile video file",
|
||||
"Select_video_file": "Select video file",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload."
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
|
||||
"Image_copy_error": "Image copy error"
|
||||
}
|
||||
|
@@ -573,7 +573,7 @@
|
||||
"Five_column": "5 columns",
|
||||
"Full_width_mode": "Full width mode",
|
||||
"Video": "Video",
|
||||
"Upload_local_video": "Upload_local_video",
|
||||
"Upload_local_video": "Upload local video",
|
||||
"The_current_version_does_not_support_>5MB_video_file": "The_current_version_does_not_support_>5MB_video_file",
|
||||
"Token_expired_Please_refresh_the_page": "Token expired. Please refresh the page.",
|
||||
"Link_to_page": "Link to page",
|
||||
@@ -607,6 +607,11 @@
|
||||
"Enter_reply": "Enter reply",
|
||||
"Processing_content_cannot_be_empty": "Processing content cannot be empty",
|
||||
"AI_error_message": "Request error, please try again",
|
||||
"Add_video_link": "Add video link",
|
||||
"Link_Seafile_video_file": "Link Seafile video file",
|
||||
"Select_video_file": "Select video file",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload."
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
|
||||
"Image_copy_error": "Image copy error"
|
||||
}
|
||||
|
@@ -573,7 +573,7 @@
|
||||
"Five_column": "5 columns",
|
||||
"Full_width_mode": "Full width mode",
|
||||
"Video": "Video",
|
||||
"Upload_local_video": "Upload_local_video",
|
||||
"Upload_local_video": "Upload local video",
|
||||
"The_current_version_does_not_support_>5MB_video_file": "The_current_version_does_not_support_>5MB_video_file",
|
||||
"Token_expired_Please_refresh_the_page": "Token expired. Please refresh the page.",
|
||||
"Link_to_page": "Link to page",
|
||||
@@ -607,6 +607,12 @@
|
||||
"Enter_reply": "Enter reply",
|
||||
"Processing_content_cannot_be_empty": "Processing content cannot be empty",
|
||||
"AI_error_message": "Request error, please try again",
|
||||
"Add_video_link": "Add video link",
|
||||
"Link_Seafile_video_file": "Link Seafile video file",
|
||||
"Select_video_file": "Select video file",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload."
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
|
||||
"Image_copy_error": "Image copy error"
|
||||
|
||||
}
|
||||
|
@@ -573,7 +573,7 @@
|
||||
"Five_column": "5 columns",
|
||||
"Full_width_mode": "Full width mode",
|
||||
"Video": "Video",
|
||||
"Upload_local_video": "Upload_local_video",
|
||||
"Upload_local_video": "Upload local video",
|
||||
"The_current_version_does_not_support_>5MB_video_file": "The_current_version_does_not_support_>5MB_video_file",
|
||||
"Token_expired_Please_refresh_the_page": "Token expired. Please refresh the page.",
|
||||
"Link_to_page": "Link to page",
|
||||
@@ -607,6 +607,12 @@
|
||||
"Enter_reply": "Enter reply",
|
||||
"Processing_content_cannot_be_empty": "Processing content cannot be empty",
|
||||
"AI_error_message": "Request error, please try again",
|
||||
"Add_video_link": "Add video link",
|
||||
"Link_Seafile_video_file": "Link Seafile video file",
|
||||
"Select_video_file": "Select video file",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload."
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
|
||||
"Image_copy_error": "Image copy error"
|
||||
|
||||
}
|
||||
|
@@ -573,7 +573,7 @@
|
||||
"Five_column": "5 columns",
|
||||
"Full_width_mode": "Full width mode",
|
||||
"Video": "Video",
|
||||
"Upload_local_video": "Upload_local_video",
|
||||
"Upload_local_video": "Upload local video",
|
||||
"The_current_version_does_not_support_>5MB_video_file": "The_current_version_does_not_support_>5MB_video_file",
|
||||
"Token_expired_Please_refresh_the_page": "Token expired. Please refresh the page.",
|
||||
"Link_to_page": "Link to page",
|
||||
@@ -607,6 +607,12 @@
|
||||
"Enter_reply": "Enter reply",
|
||||
"Processing_content_cannot_be_empty": "Processing content cannot be empty",
|
||||
"AI_error_message": "Request error, please try again",
|
||||
"Add_video_link": "Add video link",
|
||||
"Link_Seafile_video_file": "Link Seafile video file",
|
||||
"Select_video_file": "Select video file",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload."
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
|
||||
"Image_copy_error": "Image copy error"
|
||||
|
||||
}
|
||||
|
@@ -573,7 +573,7 @@
|
||||
"Five_column": "5 columns",
|
||||
"Full_width_mode": "Full width mode",
|
||||
"Video": "Video",
|
||||
"Upload_local_video": "Upload_local_video",
|
||||
"Upload_local_video": "Upload local video",
|
||||
"The_current_version_does_not_support_>5MB_video_file": "The_current_version_does_not_support_>5MB_video_file",
|
||||
"Token_expired_Please_refresh_the_page": "Token expired. Please refresh the page.",
|
||||
"Link_to_page": "Link to page",
|
||||
@@ -607,6 +607,12 @@
|
||||
"Enter_reply": "Enter reply",
|
||||
"Processing_content_cannot_be_empty": "Processing content cannot be empty",
|
||||
"AI_error_message": "Request error, please try again",
|
||||
"Add_video_link": "Add video link",
|
||||
"Link_Seafile_video_file": "Link Seafile video file",
|
||||
"Select_video_file": "Select video file",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload."
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
|
||||
"Image_copy_error": "Image copy error"
|
||||
|
||||
}
|
||||
|
@@ -573,7 +573,7 @@
|
||||
"Five_column": "5 columns",
|
||||
"Full_width_mode": "Full width mode",
|
||||
"Video": "Video",
|
||||
"Upload_local_video": "Upload_local_video",
|
||||
"Upload_local_video": "Upload local video",
|
||||
"The_current_version_does_not_support_>5MB_video_file": "The_current_version_does_not_support_>5MB_video_file",
|
||||
"Token_expired_Please_refresh_the_page": "Token expired. Please refresh the page.",
|
||||
"Link_to_page": "Link to page",
|
||||
@@ -607,6 +607,12 @@
|
||||
"Enter_reply": "Enter reply",
|
||||
"Processing_content_cannot_be_empty": "Processing content cannot be empty",
|
||||
"AI_error_message": "Request error, please try again",
|
||||
"Add_video_link": "Add video link",
|
||||
"Link_Seafile_video_file": "Link Seafile video file",
|
||||
"Select_video_file": "Select video file",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload."
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
|
||||
"Image_copy_error": "Image copy error"
|
||||
|
||||
}
|
||||
|
@@ -573,7 +573,7 @@
|
||||
"Five_column": "5 columns",
|
||||
"Full_width_mode": "Full width mode",
|
||||
"Video": "Vidéo",
|
||||
"Upload_local_video": "Upload_local_video",
|
||||
"Upload_local_video": "Upload local video",
|
||||
"The_current_version_does_not_support_>5MB_video_file": "The_current_version_does_not_support_>5MB_video_file",
|
||||
"Token_expired_Please_refresh_the_page": "Token expired. Please refresh the page.",
|
||||
"Link_to_page": "Link to page",
|
||||
@@ -607,6 +607,12 @@
|
||||
"Enter_reply": "Enter reply",
|
||||
"Processing_content_cannot_be_empty": "Processing content cannot be empty",
|
||||
"AI_error_message": "Request error, please try again",
|
||||
"Add_video_link": "Add video link",
|
||||
"Link_Seafile_video_file": "Link Seafile video file",
|
||||
"Select_video_file": "Select video file",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload."
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
|
||||
"Image_copy_error": "Image copy error"
|
||||
|
||||
}
|
||||
|
@@ -573,7 +573,7 @@
|
||||
"Five_column": "5 columns",
|
||||
"Full_width_mode": "Full width mode",
|
||||
"Video": "Video",
|
||||
"Upload_local_video": "Upload_local_video",
|
||||
"Upload_local_video": "Upload local video",
|
||||
"The_current_version_does_not_support_>5MB_video_file": "The_current_version_does_not_support_>5MB_video_file",
|
||||
"Token_expired_Please_refresh_the_page": "Token expired. Please refresh the page.",
|
||||
"Link_to_page": "Link to page",
|
||||
@@ -607,6 +607,12 @@
|
||||
"Enter_reply": "Enter reply",
|
||||
"Processing_content_cannot_be_empty": "Processing content cannot be empty",
|
||||
"AI_error_message": "Request error, please try again",
|
||||
"Add_video_link": "Add video link",
|
||||
"Link_Seafile_video_file": "Link Seafile video file",
|
||||
"Select_video_file": "Select video file",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload."
|
||||
"And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
|
||||
"Image_copy_error": "Image copy error"
|
||||
|
||||
}
|
||||
|
@@ -573,7 +573,7 @@
|
||||
"Five_column": "5 столбцов",
|
||||
"Full_width_mode": "Режим полной ширины",
|
||||
"Video": "Видео",
|
||||
"Upload_local_video": "Загрузка_локального_видео",
|
||||
"Upload_local_video": "Загрузить локального видео",
|
||||
"The_current_version_does_not_support_>5MB_video_file": "Текущая_версия_не_поддерживает_видео_файл_>5_МБ",
|
||||
"Token_expired_Please_refresh_the_page": "Срок действия токена истек. Обновите страницу.",
|
||||
"Link_to_page": "Ссылка на страницу",
|
||||
@@ -607,6 +607,11 @@
|
||||
"Enter_reply": "Введите ответ",
|
||||
"Processing_content_cannot_be_empty": "Содержимое обработки не может быть пустым",
|
||||
"AI_error_message": "Ошибка запроса, попробуйте еще раз",
|
||||
"Add_video_link": "Добавить ссылку на видео",
|
||||
"Link_Seafile_video_file": "Ссылка на видео файл Seafile",
|
||||
"Select_video_file": "Выбрать видео файл",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "Поддержка Youtube, Tencent, Bilibili и других",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "Изображение не может быть скопировано. Скачайте исходное изображение,",
|
||||
"And_select_insert_-_image_to_upload": "и выбрать 「вставить」 - 「изображение」 для загрузки."
|
||||
"And_select_insert_-_image_to_upload": "и выбрать 「вставить」 - 「изображение」 для загрузки.",
|
||||
"Image_copy_error": "Ошибка копирования изображения"
|
||||
}
|
||||
|
@@ -607,6 +607,11 @@
|
||||
"Enter_reply": "输入回复",
|
||||
"Processing_content_cannot_be_empty": "处理内容不能为空",
|
||||
"AI_error_message": "请求错误,请重试",
|
||||
"Add_video_link": "添加视频链接",
|
||||
"Link_Seafile_video_file": "链接Seafile视频文件",
|
||||
"Select_video_file": "选择视频文件",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "支持Youtube,腾讯视频,B站及其他平台",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "此照片不支持复制,请下载原图",
|
||||
"And_select_insert_-_image_to_upload": "后点击工具栏「插入」- 「照片」上传"
|
||||
"And_select_insert_-_image_to_upload": "后点击工具栏「插入」- 「照片」上传",
|
||||
"Image_copy_error": "图片复制错误"
|
||||
}
|
||||
|
@@ -607,6 +607,11 @@
|
||||
"Enter_reply": "输入回复",
|
||||
"Processing_content_cannot_be_empty": "处理内容不能为空",
|
||||
"AI_error_message": "请求错误,请重试",
|
||||
"Add_video_link": "添加视频链接",
|
||||
"Link_Seafile_video_file": "链接Seafile视频文件",
|
||||
"Select_video_file": "选择视频文件",
|
||||
"Support_Youtube_Tencent_Bilibili_and_more": "支持Youtube,腾讯视频,B站及其他平台",
|
||||
"Image_cannot_be_copied_Please_download_the_source_image": "此照片不支持复制,请下载原图",
|
||||
"And_select_insert_-_image_to_upload": "后点击工具栏「插入」- 「照片」上传"
|
||||
"And_select_insert_-_image_to_upload": "后点击工具栏「插入」- 「照片」上传",
|
||||
"Image_copy_error": "图片复制错误"
|
||||
}
|
||||
|
@@ -62,6 +62,8 @@ class Saml2Backend(ModelBackend):
|
||||
saml_user = SocialAuthUser.objects.get_by_provider_and_uid(SAML_PROVIDER_IDENTIFIER, name_id)
|
||||
if not saml_user and SSO_LDAP_USE_SAME_UID:
|
||||
saml_user = SocialAuthUser.objects.get_by_provider_and_uid(LDAP_PROVIDER, name_id)
|
||||
if saml_user:
|
||||
SocialAuthUser.objects.add(saml_user.username, SAML_PROVIDER_IDENTIFIER, name_id)
|
||||
if saml_user:
|
||||
user = self.get_user(saml_user.username)
|
||||
if not user:
|
||||
|
@@ -181,6 +181,8 @@ def oauth_callback(request):
|
||||
oauth_user = SocialAuthUser.objects.get_by_provider_and_uid(OAUTH_PROVIDER, uid)
|
||||
if not oauth_user and SSO_LDAP_USE_SAME_UID:
|
||||
oauth_user = SocialAuthUser.objects.get_by_provider_and_uid(LDAP_PROVIDER, uid)
|
||||
if oauth_user:
|
||||
SocialAuthUser.objects.add(oauth_user.username, OAUTH_PROVIDER, uid)
|
||||
if oauth_user:
|
||||
email = oauth_user.username
|
||||
is_new_user = False
|
||||
|
@@ -17,7 +17,7 @@ from seahub.repo_metadata.utils import add_init_metadata_task, gen_unique_id, in
|
||||
get_unmodifiable_columns, can_read_metadata, init_faces, \
|
||||
extract_file_details, get_table_by_name, remove_faces_table, FACES_SAVE_PATH, \
|
||||
init_tags, init_tag_self_link_columns, remove_tags_table, add_init_face_recognition_task, init_ocr, \
|
||||
remove_ocr_column, get_update_record
|
||||
remove_ocr_column, get_update_record, update_people_cover_photo
|
||||
from seahub.repo_metadata.metadata_server_api import MetadataServerAPI, list_metadata_view_records
|
||||
from seahub.utils.repo import is_repo_admin
|
||||
from seaserv import seafile_api
|
||||
@@ -2633,3 +2633,68 @@ class MetadataMergeTags(APIView):
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal Server Error')
|
||||
|
||||
return Response({'success': True})
|
||||
|
||||
|
||||
class PeopleCoverPhoto(APIView):
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAuthenticated,)
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def put(self, request, repo_id, people_id):
|
||||
record_id = request.data.get('record_id')
|
||||
if not record_id:
|
||||
error_msg = 'record_id invalid'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
metadata = RepoMetadata.objects.filter(repo_id=repo_id).first()
|
||||
if (
|
||||
not metadata
|
||||
or not metadata.enabled
|
||||
or not metadata.face_recognition_enabled
|
||||
):
|
||||
error_msg = f'The face recognition is disabled for repo {repo_id}.'
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
# resource check
|
||||
repo = seafile_api.get_repo(repo_id)
|
||||
if not repo:
|
||||
error_msg = 'Library %s not found.' % repo_id
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
if not is_repo_admin(request.user.username, repo_id):
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
metadata_server_api = MetadataServerAPI(repo_id, request.user.username)
|
||||
|
||||
from seafevents.repo_metadata.constants import METADATA_TABLE
|
||||
|
||||
sql = f'SELECT {METADATA_TABLE.columns.obj_id.name} FROM `{METADATA_TABLE.name}` WHERE `{METADATA_TABLE.columns.id.name}` = "{record_id}"'
|
||||
|
||||
try:
|
||||
query_result = metadata_server_api.query_rows(sql)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
error_msg = 'Internal Server Error'
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||
|
||||
obj_id = query_result.get('results', [dict()])[0].get(
|
||||
METADATA_TABLE.columns.obj_id.name, ''
|
||||
)
|
||||
if not obj_id:
|
||||
return api_error(status.HTTP_404_NOT_FOUND, 'obj_id not found')
|
||||
|
||||
params = {
|
||||
'repo_id': repo_id,
|
||||
'obj_id': obj_id,
|
||||
'people_id': people_id,
|
||||
}
|
||||
|
||||
try:
|
||||
update_people_cover_photo(params)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
error_msg = 'Internal Server Error'
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||
|
||||
return Response({'success': True})
|
||||
|
@@ -3,7 +3,7 @@ from .apis import MetadataRecords, MetadataManage, MetadataColumns, MetadataReco
|
||||
MetadataFolders, MetadataViews, MetadataViewsMoveView, MetadataViewsDetailView, MetadataViewsDuplicateView, FacesRecords, \
|
||||
FaceRecognitionManage, FacesRecord, MetadataExtractFileDetails, PeoplePhotos, MetadataTagsStatusManage, MetadataTags, \
|
||||
MetadataTagsLinks, MetadataFileTags, MetadataTagFiles, MetadataMergeTags, MetadataTagsFiles, MetadataDetailsSettingsView, \
|
||||
MetadataOCRManageView
|
||||
MetadataOCRManageView, PeopleCoverPhoto
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^$', MetadataManage.as_view(), name='api-v2.1-metadata'),
|
||||
@@ -23,6 +23,7 @@ urlpatterns = [
|
||||
re_path(r'^face-records/$', FacesRecords.as_view(), name='api-v2.1-metadata-face-records'),
|
||||
re_path(r'^people-photos/(?P<people_id>.+)/$', PeoplePhotos.as_view(), name='api-v2.1-metadata-people-photos'),
|
||||
re_path(r'^face-recognition/$', FaceRecognitionManage.as_view(), name='api-v2.1-metadata-face-recognition'),
|
||||
re_path(r'^people-cover-photo/(?P<people_id>.+)/$', PeopleCoverPhoto.as_view(), name='api-v2.1-metadata-people-cover-photo'),
|
||||
|
||||
re_path(r'^extract-file-details/$', MetadataExtractFileDetails.as_view(), name='api-v2.1-metadata-extract-file-details'),
|
||||
|
||||
|
@@ -41,6 +41,13 @@ def extract_file_details(params):
|
||||
resp = requests.post(url, json=params, headers=headers, timeout=30)
|
||||
return json.loads(resp.content)['details']
|
||||
|
||||
def update_people_cover_photo(params):
|
||||
payload = {'exp': int(time.time()) + 300, }
|
||||
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
|
||||
headers = {"Authorization": "Token %s" % token}
|
||||
url = urljoin(SEAFEVENTS_SERVER_URL, '/update-people-cover-photo')
|
||||
resp = requests.post(url, json=params, headers=headers, timeout=30)
|
||||
return json.loads(resp.content)
|
||||
|
||||
def generator_base64_code(length=4):
|
||||
possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz0123456789'
|
||||
|
@@ -62,6 +62,7 @@ body {
|
||||
window.shared = {
|
||||
pageOptions: {
|
||||
repoID: '{{ repo.id }}',
|
||||
repoEncrypted: {% if repo.encrypted %}true{% else %}false{% endif %},
|
||||
filePath: '{{ path|escapejs }}',
|
||||
sharedToken: '{{ shared_token }}',
|
||||
trafficOverLimit: {% if traffic_over_limit %}true{% else %}false{% endif %},
|
||||
|
@@ -7,7 +7,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_data %}
|
||||
actionURL: '{{action_url}}',
|
||||
actionURL: '{{action_url|safe}}',
|
||||
accessToken: '{{access_token}}',
|
||||
accessTokenTtl: '{{access_token_ttl}}',
|
||||
{% endblock %}
|
||||
|
Reference in New Issue
Block a user