diff --git a/frontend/src/components/dialog/org-logs-file-update-detail.js b/frontend/src/components/dialog/org-logs-file-update-detail.js new file mode 100644 index 0000000000..c7a8b988d0 --- /dev/null +++ b/frontend/src/components/dialog/org-logs-file-update-detail.js @@ -0,0 +1,130 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Modal, ModalHeader, ModalBody } from 'reactstrap'; +import { seafileAPI } from '../../utils/seafile-api'; +import { gettext } from '../../utils/constants'; + +const propTypes = { + repoID: PropTypes.string.isRequired, + commitID: PropTypes.string.isRequired, + toggleCancel: PropTypes.func.isRequired, +}; + +class FileUpdateDetailDialog extends React.Component { + + constructor(props) { + super(props); + this.state = { + time: '', + renamed: [], + deldir: [], + modified: [], + newdir: [], + newfile: [], + removed: [], + }; + } + + componentDidMount() { + seafileAPI.orgAdminGetFileUpdateDetail(this.props.repoID, this.props.commitID).then(res => { + this.setState({ + time: res.data.date_time, + renamed: this.state.renamed.concat(res.data.renamed), + deldir: this.state.deldir.concat(res.data.deldir), + modified: this.state.modified.concat(res.data.modified), + newdir: this.state.newdir.concat(res.data.newdir), + newfile: this.state.newfile.concat(res.data.new), + removed: this.state.removed.concat(res.data.removed), + }); + }); + } + + renderContentItem = (items) => { + let con = ''; + con += ''; + return {__html: con}; + } + + renderContent = () => { + if (this.state.newfile.length > 0) { + return ( +
+

{gettext('New files')}

+

{this.state.time}

+
+
+ ); + } + + if (this.state.removed.length > 0) { + return ( +
+

{gettext('Deleted files')}

+

{this.state.time}

+
+
+ ); + } + + if (this.state.renamed.length > 0) { + return ( +
+

{gettext('Renamed or Moved files')}

+

{this.state.time}

+
+
+ ); + } + + if (this.state.modified.length > 0) { + return ( +
+

{gettext('Modified files')}

+

{this.state.time}

+
+
+ ); + } + + if (this.state.newdir.length > 0) { + return ( +
+

{gettext('New directories')}

+

{this.state.time}

+
+
+ ); + } + + if (this.state.deldir.length > 0) { + return ( +
+

{gettext('Deleted directories')}

+

{this.state.time}

+
+
+ ); + } + } + + render() { + return ( + + + {gettext('Modification Details')} + + + {this.renderContent()} + + + ); + } +} + +FileUpdateDetailDialog.propTypes = propTypes; + +export default FileUpdateDetailDialog; diff --git a/frontend/src/css/org-logs.css b/frontend/src/css/org-logs.css new file mode 100644 index 0000000000..9e558b03fd --- /dev/null +++ b/frontend/src/css/org-logs.css @@ -0,0 +1,21 @@ +.audit-unselect-item { + display: inline-block; + border: 1px solid #ccc; + border-radius: 2px; + padding: 1px 8px; + background: #f2f2f2; + cursor: pointer; + font-size: 14px; + margin: 10px 5px 0; +} +.audit-unselect-item:hover { + background-color: #ddd; +} +.no-deco, +.no-deco:hover, +.no-deco:focus { + text-decoration: none; +} +.cur-view-path .nav .nav-item a { + padding: 7px 10px; +} \ No newline at end of file diff --git a/frontend/src/models/org-admin-repo.js b/frontend/src/models/org-admin-repo.js index 31d49b59c8..e5974d4654 100644 --- a/frontend/src/models/org-admin-repo.js +++ b/frontend/src/models/org-admin-repo.js @@ -1,5 +1,3 @@ -import { Utils } from '../utils/utils'; - class OrgAdminRepo { constructor(object) { this.repoID = object.repo_id; diff --git a/frontend/src/models/org-group.js b/frontend/src/models/org-group.js index ab55144f69..276154357b 100644 --- a/frontend/src/models/org-group.js +++ b/frontend/src/models/org-group.js @@ -1,4 +1,3 @@ -import { Utils } from '../utils/utils'; import { lang } from '../utils/constants'; import moment from 'moment'; diff --git a/frontend/src/models/org-logs-file-audit.js b/frontend/src/models/org-logs-file-audit.js new file mode 100644 index 0000000000..3449fc4391 --- /dev/null +++ b/frontend/src/models/org-logs-file-audit.js @@ -0,0 +1,22 @@ +import { lang } from '../utils/constants'; +import moment from 'moment'; + +moment.locale(lang); + +class OrgLogsFileAuditEvent { + constructor(object) { + this.ip = object.ip; + this.type = object.type; + this.device = object.device; + this.repo_id = object.repo_id; + this.repo_name = object.repo_name; + this.file_name = object.file_name; + this.file_path = object.file_path; + this.user_name = object.user_name; + this.user_email = object.user_email; + this.user_contact_email = object.user_contact_email; + this.time = moment(object.time).format('YYYY-MM-DD HH:mm:ss'); + } +} + +export default OrgLogsFileAuditEvent; diff --git a/frontend/src/models/org-logs-file-update.js b/frontend/src/models/org-logs-file-update.js new file mode 100644 index 0000000000..635ff80775 --- /dev/null +++ b/frontend/src/models/org-logs-file-update.js @@ -0,0 +1,20 @@ +import { lang } from '../utils/constants'; +import moment from 'moment'; + +moment.locale(lang); + +class OrgLogsFileUpdateEvent { + constructor(object) { + this.repo_id = object.repo_id; + this.description = object.description; + this.repo_name = object.repo_name; + this.user_name = object.user_name; + this.user_email = object.user_email; + this.repo_encrypted = object.repo_encrypted; + this.repo_commit_id = object.repo_commit_id; + this.user_contact_email = object.user_contact_email; + this.time = moment(object.time).format('YYYY-MM-DD HH:mm:ss'); + } +} + +export default OrgLogsFileUpdateEvent; diff --git a/frontend/src/models/org-logs-perm-audit.js b/frontend/src/models/org-logs-perm-audit.js new file mode 100644 index 0000000000..dc9a64d2d8 --- /dev/null +++ b/frontend/src/models/org-logs-perm-audit.js @@ -0,0 +1,26 @@ +import { lang } from '../utils/constants'; +import moment from 'moment'; + +moment.locale(lang); + +class OrgLogsPermAuditEvent { + constructor(object) { + this.from_user_name = object.from_user_name; + this.from_user_email = object.from_user_email; + this.from_user_contact_email = object.from_user_contact_email; + this.to_user_email = object.to_user_email; + this.to_user_name = object.to_user_name; + this.to_user_contact_email = object.to_user_contact_email; + this.to_group_name = object.to_group_name; + this.to_group_id = object.to_group_id; + this.type = object.type; + this.repo_id = object.repo_id; + this.repo_name = object.repo_name; + this.folder_name = object.folder_name; + this.folder_path = object.folder_path; + this.time = moment(object.time).format('YYYY-MM-DD HH:mm:ss'); + this.permission = object.permission; + } +} + +export default OrgLogsPermAuditEvent; diff --git a/frontend/src/pages/org-admin/index.js b/frontend/src/pages/org-admin/index.js index f2206efdf5..42d6bc4b00 100644 --- a/frontend/src/pages/org-admin/index.js +++ b/frontend/src/pages/org-admin/index.js @@ -15,6 +15,10 @@ import OrgLinks from './org-links'; import OrgDepartments from './org-departments'; import OrgDepartmentsList from './org-departments-list'; import OrgDepartmentItem from './org-department-item'; +import OrgLogs from './org-logs'; +import OrgLogsFileAudit from './org-logs-file-audit'; +import OrgLogsFileUpdate from './org-logs-file-update'; +import OrgLogsPermAudit from './org-logs-perm-audit'; import '../../assets/css/fa-solid.css'; import '../../assets/css/fa-regular.css'; @@ -97,6 +101,15 @@ class Org extends React.Component { + + + + +
diff --git a/frontend/src/pages/org-admin/org-admin-list.js b/frontend/src/pages/org-admin/org-admin-list.js index 495d7eeead..d10d51efd6 100644 --- a/frontend/src/pages/org-admin/org-admin-list.js +++ b/frontend/src/pages/org-admin/org-admin-list.js @@ -107,7 +107,8 @@ class OrgAdminList extends React.Component { onFreezedItem={this.onFreezedItem} onUnfreezedItem={this.onUnfreezedItem} /> - )})} + ); + })} {this.props.isShowAddOrgAdminDialog && ( diff --git a/frontend/src/pages/org-admin/org-info.js b/frontend/src/pages/org-admin/org-info.js index 1dc6daf4d2..131df26a15 100644 --- a/frontend/src/pages/org-admin/org-info.js +++ b/frontend/src/pages/org-admin/org-info.js @@ -1,7 +1,6 @@ -import React, { Fragment, Component } from 'react'; - +import React, { Component } from 'react'; import { seafileAPI } from '../../utils/seafile-api'; -import { gettext, orgID, orgMemberQuotaEnabled} from '../../utils/constants'; +import { gettext, orgMemberQuotaEnabled} from '../../utils/constants'; import { Utils } from '../../utils/utils'; class OrgInfo extends Component { diff --git a/frontend/src/pages/org-admin/org-logs-file-audit.js b/frontend/src/pages/org-admin/org-logs-file-audit.js new file mode 100644 index 0000000000..170abda18b --- /dev/null +++ b/frontend/src/pages/org-admin/org-logs-file-audit.js @@ -0,0 +1,250 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; +import { seafileAPI } from '../../utils/seafile-api'; +import { siteRoot, gettext } from '../../utils/constants'; +import OrgLogsFileAuditEvent from '../../models/org-logs-file-audit'; +import '../../css/org-logs.css'; + +class OrgLogsFileAudit extends React.Component { + + constructor(props) { + super(props); + this.state = { + page: 1, + pageNext: false, + eventList: [], + userSelected: '', + repoSelected: '', + isItemFreezed: false + }; + } + + componentDidMount() { + let page = this.state.page; + let email = this.state.userSelected; + let repoID = this.state.repoSelected; + this.initData(email, repoID, page); + } + + initData = (email, repoID, page) => { + seafileAPI.orgAdminListFileAudit(email, repoID, page).then(res => { + let eventList = res.data.log_list.map(item => { + return new OrgLogsFileAuditEvent(item); + }); + + this.setState({ + eventList: eventList, + pageNext: res.data.page_next, + page: res.data.page, + userSelected: res.data.user_selected, + repoSelected: res.data.repo_selected + }); + }); + } + + + onChangePageNum = (e, num) => { + e.preventDefault(); + let page = this.state.page; + + if (num == 1) { + page = page + 1; + } else { + page = page - 1; + } + this.initData(page); + } + + filterUser = (userSelected) => { + this.setState({ userSelected: userSelected }); + } + + filterRepo = (repoSelected) => { + this.setState({ repoSelected: repoSelected }); + } + + render() { + let eventList = this.state.eventList; + return ( +
+ { + (this.state.userSelected || this.state.repoSelected) && + + {this.state.userSelected && + + {this.state.userSelected}{' ✖'} + + } + {this.state.repoSelected && + + {this.state.repoSelected}{' ✖'} + + } + + } + + + + + + + + + + + + + {eventList.map((item, index) => { + return ( + + ); + })} + +
{gettext('User')}{gettext('Type')}{gettext('IP')}{gettext('Date')}{gettext('Library')}{gettext('File')}
+
+ {this.state.page != 1 && this.onChangePageNum(e, -1)}>{gettext('Previous')}} + {(this.state.page != 1 && this.state.pageNext) && | } + {this.state.pageNext && this.onChangePageNum(e, 1)}>{gettext('Next')}} +
+
+ ); + } +} + +const propTypes = { + filterUser: PropTypes.func.isRequired, + filterRepo: PropTypes.func.isRequired, + isItemFreezed: PropTypes.bool.isRequired, +}; + +class FileAuditItem extends React.Component { + + constructor(props) { + super(props); + this.state = { + highlight: false, + showMenu: false, + isItemMenuShow: false, + userDropdownOpen: false, + repoDropdownOpen: false, + }; + } + + onMouseEnter = () => { + if (!this.props.isItemFreezed) { + this.setState({ + showMenu: true, + highlight: true, + }); + } + } + + onMouseLeave = () => { + if (!this.props.isItemFreezed) { + this.setState({ + showMenu: false, + highlight: false + }); + } + } + + toggleUserDropdown = () => { + this.setState({ userDropdownOpen: !this.state.userDropdownOpen }); + } + + renderUser = (fileEvent) => { + if (!fileEvent.user_email) { + return gettext('Anonymous User'); + } + + return ( + + {fileEvent.user_name}{' '} + + + + + {gettext('Only Show')}{' '} + {fileEvent.user_name} + + + + + ); + + } + + renderType = (type) => { + if (type.indexOf('web') != -1) { + type = 'web'; + } + if (type.indexOf('api') != -1) { + type = 'api'; + } + if (type.indexOf('share-link') != -1) { + type = 'share link'; + } + return type; + } + + toggleRepoDropdown = () => { + this.setState({ repoDropdownOpen: !this.state.repoDropdownOpen }); + } + + renderRepo = (fileEvent) => { + let repoName = 'Deleted'; + if (fileEvent.repo_name) { + repoName = fileEvent.repo_name; + } + return ( + + {repoName} + { fileEvent.repo_name && + + + + + {gettext('Only Show')}{' '}{fileEvent.repo_name} + + + } + + ); + } + + render() { + let { fileEvent } = this.props; + if (this.props.userSelected && fileEvent.user_email !== this.props.userSelected ) { + return null; + } else if (this.props.repoSelected && fileEvent.repo_name !== this.props.repoSelected) { + return null; + } else { + return ( + + {this.renderUser(fileEvent)} + {this.renderType(fileEvent.type)} + {fileEvent.ip} + {fileEvent.time} + {this.renderRepo(fileEvent)} + {fileEvent.file_name} + + ); + } + } +} + +FileAuditItem.propTypes = propTypes; + +export default OrgLogsFileAudit; diff --git a/frontend/src/pages/org-admin/org-logs-file-update.js b/frontend/src/pages/org-admin/org-logs-file-update.js new file mode 100644 index 0000000000..d62236c4ad --- /dev/null +++ b/frontend/src/pages/org-admin/org-logs-file-update.js @@ -0,0 +1,278 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; +import { seafileAPI } from '../../utils/seafile-api'; +import { siteRoot, gettext } from '../../utils/constants'; +import OrgLogsFileUpdateEvent from '../../models/org-logs-file-update'; +import ModalPortal from '../../components/modal-portal'; +import FileUpdateDetailDialog from '../../components/dialog/org-logs-file-update-detail'; +import '../../css/org-logs.css'; + +class OrgLogsFileUpdate extends Component { + + constructor(props) { + super(props); + this.state = { + page: 1, + pageNext: false, + eventList: [], + userSelected: '', + repoSelected: '', + isItemFreezed: false, + showDetails: false, + repoID: '', + commitID: '' + }; + } + + componentDidMount() { + let page = this.state.page; + let email = this.state.userSelected; + let repoID = this.state.repoSelected; + this.initData(email, repoID, page); + } + + initData = (email, repoID, page) => { + seafileAPI.orgAdminListFileUpdate(email, repoID, page).then(res => { + let eventList = res.data.log_list.map(item => { + return new OrgLogsFileUpdateEvent(item); + }); + + this.setState({ + eventList: eventList, + pageNext: res.data.page_next, + page: res.data.page, + }); + + }); + } + + onChangePageNum = (e, num) => { + e.preventDefault(); + let page = this.state.page; + + if (num == 1) { + page = page + 1; + } else { + page = page - 1; + } + this.initData(page); + } + + toggleCancelDetail = () => { + this.setState({ + showDetails: !this.state.showDetails + }); + } + + onDetails = (e, fileEvent) => { + e.preventDefault(); + this.setState({ + showDetails: !this.state.showDetails, + repoID: fileEvent.repo_id, + commitID: fileEvent.repo_commit_id + }); + } + + filterUser = (userSelected) => { + this.setState({ userSelected: userSelected }); + } + + filterRepo = (repoSelected) => { + this.setState({ repoSelected: repoSelected }); + } + + render() { + let eventList = this.state.eventList; + return ( +
+ { + (this.state.userSelected || this.state.repoSelected) && + + {this.state.userSelected && + + {this.state.userSelected}{' ✖'} + + } + {this.state.repoSelected && + + {this.state.repoSelected}{' ✖'} + + } + + } + + + + + + + + + + + {eventList.map((item, index) => { + return ( + + ); + })} + +
{gettext('User')}{gettext('Date')}{gettext('Library')}{gettext('Action')}
+
+ {this.state.page != 1 && this.onChangePageNum(e, -1)}>{gettext('Previous')}} + {(this.state.page != 1 && this.state.pageNext) && | } + {this.state.pageNext && this.onChangePageNum(e, 1)}>{gettext('Next')}} +
+ {this.state.showDetails && + + + + } +
+ ); + } +} + +const propTypes = { + filterUser: PropTypes.func.isRequired, + filterRepo: PropTypes.func.isRequired, + onDetails: PropTypes.func.isRequired, + userSelected: PropTypes.string.isRequired, + repoSelected: PropTypes.string.isRequired, + isItemFreezed: PropTypes.bool.isRequired, +}; + + +class FileUpdateItem extends React.Component { + + constructor(props) { + super(props); + this.state = { + highlight: false, + showMenu: false, + isItemMenuShow: false, + userDropdownOpen: false, + repoDropdownOpen: false, + }; + } + + onMouseEnter = () => { + if (!this.props.isItemFreezed) { + this.setState({ + showMenu: true, + highlight: true, + }); + } + } + + onMouseLeave = () => { + if (!this.props.isItemFreezed) { + this.setState({ + showMenu: false, + highlight: false + }); + } + } + + toggleUserDropdown = () => { + this.setState({ userDropdownOpen: !this.state.userDropdownOpen }); + } + + renderUser = (fileEvent) => { + if (!fileEvent.user_email) { + return gettext('Anonymous User'); + } + + return ( + + {fileEvent.user_name}{' '} + + + + + {gettext('Only Show')}{' '}{fileEvent.user_name} + + + + + ); + } + + toggleRepoDropdown = () => { + this.setState({ repoDropdownOpen: !this.state.repoDropdownOpen }); + } + + renderRepo = (fileEvent) => { + let repoName = 'Deleted'; + if (fileEvent.repo_name) { + repoName = fileEvent.repo_name; + } + return ( + + {repoName} + { fileEvent.repo_name && + + + + + {gettext('Only Show')}{' '} + {fileEvent.repo_name} + + + + } + + ); + } + + renderAction = (fileEvent) => { + if (fileEvent.repo_encrypted || !fileEvent.repo_id) { + return {fileEvent.description}; + } + + return ( + {fileEvent.description} + this.props.onDetails(e, fileEvent)}>{gettext('Details')} + + ); + } + + render() { + let { fileEvent } = this.props; + if (this.props.userSelected && fileEvent.user_email !== this.props.userSelected ) { + return null; + } else if (this.props.repoSelected && fileEvent.repo_name !== this.props.repoSelected) { + return null; + } else { + return ( + + {this.renderUser(fileEvent)} + {fileEvent.time} + {this.renderRepo(fileEvent)} + {this.renderAction(fileEvent)} + + ); + } + } +} + +FileUpdateItem.propTypes = propTypes; + +export default OrgLogsFileUpdate; diff --git a/frontend/src/pages/org-admin/org-logs-perm-audit.js b/frontend/src/pages/org-admin/org-logs-perm-audit.js new file mode 100644 index 0000000000..b564f62daa --- /dev/null +++ b/frontend/src/pages/org-admin/org-logs-perm-audit.js @@ -0,0 +1,237 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; +import { Utils } from '../../utils/utils'; +import { seafileAPI } from '../../utils/seafile-api'; +import { siteRoot, gettext } from '../../utils/constants'; +import OrgLogsFilePermEvent from '../../models/org-logs-perm-audit'; +import '../../css/org-logs.css'; + +class OrgLogsFileUpdate extends Component { + + constructor(props) { + super(props); + this.state = { + page: 1, + pageNext: false, + eventList: [], + userSelected: '', + repoSelected: '', + isItemFreezed: false + }; + } + + componentDidMount() { + let page = this.state.page; + let email = this.state.userSelected; + let repoID = this.state.repoSelected; + this.initData(email, repoID, page); + } + + initData = (email, repoID, page) => { + seafileAPI.orgAdminListPermAudit(email, repoID, page).then(res => { + let eventList = res.data.log_list.map(item => { + return new OrgLogsFilePermEvent(item); + }); + + this.setState({ + eventList: eventList, + pageNext: res.data.page_next, + page: res.data.page, + }); + }); + } + + onChangePageNum = (e, num) => { + e.preventDefault(); + let page = this.state.page; + + if (num == 1) { + page = page + 1; + } else { + page = page - 1; + } + this.initData(page); + } + + filterUser = (userSelected) => { + this.setState({ userSelected: userSelected }); + } + + render() { + let eventList = this.state.eventList; + return ( +
+ {this.state.userSelected && + + {this.state.userSelected}{' ✖'} + + } + + + + + + + + + + + + + + {eventList.map((item, index) => { + return ( + + ); + })} + +
{gettext('Share From')}{gettext('Share To')}{gettext('Actions')}{gettext('Permission')}{gettext('Library')}{gettext('Folder')}{gettext('Date')}
+
+ {this.state.page != 1 && this.onChangePageNum(e, -1)}>{gettext('Previous')}} + {(this.state.page != 1 && this.state.pageNext) && | } + {this.state.pageNext && this.onChangePageNum(e, 1)}>{gettext('Next')}} +
+
+ ); + } +} + + +const propTypes = { + filterUser: PropTypes.func.isRequired, + isItemFreezed: PropTypes.bool.isRequired, + userSelected: PropTypes.string.isRequired, +}; + +class PermAuditItem extends React.Component { + + constructor(props) { + super(props); + this.state = { + highlight: false, + showMenu: false, + isItemMenuShow: false, + userDropdownOpen: false, + }; + } + + onMouseEnter = () => { + if (!this.props.isItemFreezed) { + this.setState({ + showMenu: true, + highlight: true, + }); + } + } + + onMouseLeave = () => { + if (!this.props.isItemFreezed) { + this.setState({ + showMenu: false, + highlight: false + }); + } + } + + renderFromUser = (permEvent) => { + if (!permEvent.from_user_email) { + return gettext('Anonymous User'); + } + return ( + + {permEvent.from_user_name}{' '} + + + + + {gettext('Only Show')}{' '} + {permEvent.from_user_name} + + + + + ); + } + + toggleUserDropdown = () => { + this.setState({ userDropdownOpen: !this.state.userDropdownOpen }); + } + + renderToUser = (permEvent) => { + if (permEvent.type.indexOf('public') != -1) { + return {gettext('Organization')}; + } + + if (permEvent.type.indexOf('group') != -1) { + if (permEvent.to_group_name) { + return {permEvent.to_group_name}; + } + return 'Deleted'; + } + + if (permEvent.type.indexOf('user') != -1) { + return {permEvent.to_user_name}; + } + + } + + renderType = (type) => { + if (type.indexOf('add') != -1) { + type = 'Add'; + } + if (type.indexOf('modify') != -1) { + type = 'Modify'; + } + if (type.indexOf('delete') != -1) { + type = 'Delete'; + } + return type; + } + + renderRepo = (permEvent) => { + let repoName = 'Deleted'; + if (permEvent.repo_name) { + repoName = permEvent.repo_name; + } + return repoName; + } + + renderFolder = (name) => { + let folderName = '/'; + if (name) { + folderName = name; + } + return folderName; + } + + render() { + let { permEvent } = this.props; + if (this.props.userSelected && permEvent.from_user_email !== this.props.userSelected ) { + return null; + } else { + return ( + + {this.renderFromUser(permEvent)} + {this.renderToUser(permEvent)} + {this.renderType(permEvent.type)} + {Utils.sharePerms(permEvent.permission)} + {this.renderRepo(permEvent)} + {this.renderFolder(permEvent.folder_name)} + {permEvent.time} + + ); + } + } +} + +PermAuditItem.propTypes = propTypes; + +export default OrgLogsFileUpdate; diff --git a/frontend/src/pages/org-admin/org-logs.js b/frontend/src/pages/org-admin/org-logs.js new file mode 100644 index 0000000000..7487f5a204 --- /dev/null +++ b/frontend/src/pages/org-admin/org-logs.js @@ -0,0 +1,39 @@ +import React, { Component } from 'react'; +import { Link } from '@reach/router'; +import { siteRoot, gettext } from '../../utils/constants'; + +class OrgLogs extends Component { + + constructor(props) { + super(props); + } + + tabItemClick = (param) => { + this.props.tabItemClick(param); + } + + render() { + return ( +
+
+
+
    +
  • this.tabItemClick('logadmin')}> + {gettext('File Access')} +
  • +
  • this.tabItemClick('file-update')}> + {gettext('File Update')} +
  • +
  • this.tabItemClick('perm-audit')}> + {gettext('Permission')} +
  • +
+
+ {this.props.children} +
+
+ ); + } +} + +export default OrgLogs; diff --git a/frontend/src/pages/org-admin/side-panel.js b/frontend/src/pages/org-admin/side-panel.js index 2559d8e6ed..3e0943b7cf 100644 --- a/frontend/src/pages/org-admin/side-panel.js +++ b/frontend/src/pages/org-admin/side-panel.js @@ -69,10 +69,10 @@ class SidePanel extends React.Component {
  • - + this.tabItemClick('logadmin')} > {gettext('Logs')} - +
  • diff --git a/seahub/api2/endpoints/utils.py b/seahub/api2/endpoints/utils.py index 0174c2be1c..da212a0b87 100644 --- a/seahub/api2/endpoints/utils.py +++ b/seahub/api2/endpoints/utils.py @@ -7,7 +7,7 @@ import logging from rest_framework import status -from seaserv import ccnet_api +from seaserv import ccnet_api, seafile_api from pysearpc import SearpcError from seahub.api2.utils import api_error @@ -93,6 +93,33 @@ def get_user_name_dict(email_list): return user_name_dict +def get_repo_dict(repo_id_list): + repo_id_list = set(repo_id_list) + repo_dict = {} + for repo_id in repo_id_list: + if not repo_dict.has_key(repo_id): + repo_dict[repo_id] = '' + repo = seafile_api.get_repo(repo_id) + if repo: + repo_dict[repo_id] = repo + + return repo_dict + + +def get_group_dict(group_id_list): + group_id_list = set(group_id_list) + group_dict = {} + for group_id in group_id_list: + if not group_dict.has_key(group_id): + group_dict[group_id] = '' + group = ccnet_api.get_group(int(group_id)) + print group + if group: + group_dict[group_id] = group + + return group_dict + + def check_time_period_valid(start, end): if not start or not end: return False