1
0
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:
杨顺强
2025-02-17 10:16:35 +08:00
39 changed files with 343 additions and 138 deletions

View File

@@ -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",

View File

@@ -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>
);
}
}

View File

@@ -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;
}

View File

@@ -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/';

View File

@@ -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'];

View File

@@ -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;

View File

@@ -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'],

View File

@@ -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: {

View File

@@ -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} />

View File

@@ -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>
);

View File

@@ -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);

View File

@@ -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>

View File

@@ -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 ?

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>
);

View File

@@ -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 {

View File

@@ -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(() => {

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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": "Ошибка копирования изображения"
}

View File

@@ -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": "图片复制错误"
}

View File

@@ -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": "图片复制错误"
}

View File

@@ -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:

View File

@@ -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

View File

@@ -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})

View File

@@ -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'),

View File

@@ -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'

View File

@@ -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 %},

View File

@@ -7,7 +7,7 @@
{% endblock %}
{% block extra_data %}
actionURL: '{{action_url}}',
actionURL: '{{action_url|safe}}',
accessToken: '{{access_token}}',
accessTokenTtl: '{{access_token_ttl}}',
{% endblock %}