mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-06 17:33:18 +00:00
Add tag (#3281)
This commit is contained in:
@@ -12,6 +12,9 @@ import CreateFile from '../../components/dialog/create-file-dialog';
|
|||||||
import ImageDialog from '../../components/dialog/image-dialog';
|
import ImageDialog from '../../components/dialog/image-dialog';
|
||||||
import { siteRoot, thumbnailSizeForOriginal } from '../../utils/constants';
|
import { siteRoot, thumbnailSizeForOriginal } from '../../utils/constants';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
|
import EditFileTagDialog from '../dialog/edit-filetag-dialog';
|
||||||
|
import FileTag from '../../models/file-tag';
|
||||||
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
currentPath: PropTypes.string.isRequired,
|
currentPath: PropTypes.string.isRequired,
|
||||||
@@ -31,6 +34,7 @@ const propTypes = {
|
|||||||
inResizing: PropTypes.bool.isRequired,
|
inResizing: PropTypes.bool.isRequired,
|
||||||
currentRepoInfo: PropTypes.object.isRequired,
|
currentRepoInfo: PropTypes.object.isRequired,
|
||||||
selectedDirentList: PropTypes.array.isRequired,
|
selectedDirentList: PropTypes.array.isRequired,
|
||||||
|
onFileTagChanged: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirColumnNav extends React.Component {
|
class DirColumnNav extends React.Component {
|
||||||
@@ -49,12 +53,18 @@ class DirColumnNav extends React.Component {
|
|||||||
isCopyDialogShow: false,
|
isCopyDialogShow: false,
|
||||||
isMoveDialogShow: false,
|
isMoveDialogShow: false,
|
||||||
isMutipleOperation: false,
|
isMutipleOperation: false,
|
||||||
|
isEditFileTagShow: false,
|
||||||
|
fileTagList: [],
|
||||||
|
nodeDirent: '',
|
||||||
};
|
};
|
||||||
this.isNodeMenuShow = true;
|
this.isNodeMenuShow = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
this.setState({opNode: nextProps.currentNode});
|
this.setState({opNode: nextProps.currentNode});
|
||||||
|
if (this.state.nodeDirent.object) {
|
||||||
|
this.getTagFileList(this.state.nodeDirent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodeClick = (node) => {
|
onNodeClick = (node) => {
|
||||||
@@ -92,6 +102,9 @@ class DirColumnNav extends React.Component {
|
|||||||
case 'Copy':
|
case 'Copy':
|
||||||
this.onCopyToggle();
|
this.onCopyToggle();
|
||||||
break;
|
break;
|
||||||
|
case 'Tags':
|
||||||
|
this.onEditFileTagToggle(node);
|
||||||
|
break;
|
||||||
case 'Move':
|
case 'Move':
|
||||||
this.onMoveToggle();
|
this.onMoveToggle();
|
||||||
break;
|
break;
|
||||||
@@ -101,6 +114,51 @@ class DirColumnNav extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onEditFileTagToggle = (node) => {
|
||||||
|
this.setState({
|
||||||
|
isEditFileTagShow: !this.state.isEditFileTagShow,
|
||||||
|
nodeDirent: node,
|
||||||
|
});
|
||||||
|
if (node.object) {
|
||||||
|
this.getTagFileList(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileTagChanged = () => {
|
||||||
|
let currentPath;
|
||||||
|
|
||||||
|
if (this.props.currentPath.charAt(this.props.currentPath.length - 1) === '/') {
|
||||||
|
currentPath = Utils.joinPath(this.props.currentPath, this.state.nodeDirent.object.name);
|
||||||
|
} else {
|
||||||
|
currentPath = this.props.currentPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.currentPath !== '/') {
|
||||||
|
if (currentPath === this.state.nodeDirent.path) {
|
||||||
|
this.props.onToolbarFileTagChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.currentPath !== this.state.nodeDirent.parentNode.path) {
|
||||||
|
this.getTagFileList(this.state.nodeDirent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.onFileTagChanged(this.state.nodeDirent.object, this.state.nodeDirent.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTagFileList = (node) => {
|
||||||
|
let {repoID} = this.props;
|
||||||
|
seafileAPI.listFileTags(repoID, node.path).then(res => {
|
||||||
|
let fileTagList = [];
|
||||||
|
res.data.file_tags.forEach(item => {
|
||||||
|
let file_tag = new FileTag(item);
|
||||||
|
fileTagList.push(file_tag);
|
||||||
|
});
|
||||||
|
this.setState({fileTagList: fileTagList});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onAddFileToggle = (type) => {
|
onAddFileToggle = (type) => {
|
||||||
if (type === 'root') {
|
if (type === 'root') {
|
||||||
let root = this.props.treeData.root;
|
let root = this.props.treeData.root;
|
||||||
@@ -338,6 +396,15 @@ class DirColumnNav extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</ModalPortal>
|
</ModalPortal>
|
||||||
)}
|
)}
|
||||||
|
{this.state.isEditFileTagShow &&
|
||||||
|
<EditFileTagDialog
|
||||||
|
repoID={this.props.repoID}
|
||||||
|
fileTagList={this.state.fileTagList}
|
||||||
|
filePath={this.state.nodeDirent.path}
|
||||||
|
toggleCancel={this.onEditFileTagToggle}
|
||||||
|
onFileTagChanged={this.onFileTagChanged}
|
||||||
|
/>
|
||||||
|
}
|
||||||
{this.state.isNodeImagePopupOpen && (
|
{this.state.isNodeImagePopupOpen && (
|
||||||
<ModalPortal>
|
<ModalPortal>
|
||||||
<ImageDialog
|
<ImageDialog
|
||||||
|
@@ -68,6 +68,7 @@ const propTypes = {
|
|||||||
onItemsMove: PropTypes.func.isRequired,
|
onItemsMove: PropTypes.func.isRequired,
|
||||||
onItemsCopy: PropTypes.func.isRequired,
|
onItemsCopy: PropTypes.func.isRequired,
|
||||||
onItemsDelete: PropTypes.func.isRequired,
|
onItemsDelete: PropTypes.func.isRequired,
|
||||||
|
onFileTagChanged: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirColumnView extends React.Component {
|
class DirColumnView extends React.Component {
|
||||||
@@ -171,6 +172,8 @@ class DirColumnView extends React.Component {
|
|||||||
onItemMove={this.props.onItemMove}
|
onItemMove={this.props.onItemMove}
|
||||||
onItemCopy={this.props.onItemCopy}
|
onItemCopy={this.props.onItemCopy}
|
||||||
selectedDirentList={this.props.selectedDirentList}
|
selectedDirentList={this.props.selectedDirentList}
|
||||||
|
onFileTagChanged={this.props.onFileTagChanged}
|
||||||
|
onToolbarFileTagChanged={this.props.onToolbarFileTagChanged}
|
||||||
/>
|
/>
|
||||||
<div className="dir-content-resize" onMouseDown={this.onResizeMouseDown}></div>
|
<div className="dir-content-resize" onMouseDown={this.onResizeMouseDown}></div>
|
||||||
<div className="dir-content-main" style={{userSelect: select, flex: mainFlex}}>
|
<div className="dir-content-main" style={{userSelect: select, flex: mainFlex}}>
|
||||||
@@ -224,6 +227,7 @@ class DirColumnView extends React.Component {
|
|||||||
onItemsMove={this.props.onItemsMove}
|
onItemsMove={this.props.onItemsMove}
|
||||||
onItemsCopy={this.props.onItemsCopy}
|
onItemsCopy={this.props.onItemsCopy}
|
||||||
onItemsDelete={this.props.onItemsDelete}
|
onItemsDelete={this.props.onItemsDelete}
|
||||||
|
onFileTagChanged={this.props.onFileTagChanged}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -37,6 +37,7 @@ const propTypes = {
|
|||||||
onItemsMove: PropTypes.func.isRequired,
|
onItemsMove: PropTypes.func.isRequired,
|
||||||
onItemsCopy: PropTypes.func.isRequired,
|
onItemsCopy: PropTypes.func.isRequired,
|
||||||
onItemsDelete: PropTypes.func.isRequired,
|
onItemsDelete: PropTypes.func.isRequired,
|
||||||
|
onFileTagChanged: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirListView extends React.Component {
|
class DirListView extends React.Component {
|
||||||
@@ -94,6 +95,7 @@ class DirListView extends React.Component {
|
|||||||
onItemsDelete={this.props.onItemsDelete}
|
onItemsDelete={this.props.onItemsDelete}
|
||||||
onAddFile={this.props.onAddFile}
|
onAddFile={this.props.onAddFile}
|
||||||
onAddFolder={this.props.onAddFolder}
|
onAddFolder={this.props.onAddFolder}
|
||||||
|
onFileTagChanged={this.props.onFileTagChanged}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
@@ -13,6 +13,7 @@ import MoveDirentDialog from '../dialog/move-dirent-dialog';
|
|||||||
import CopyDirentDialog from '../dialog/copy-dirent-dialog';
|
import CopyDirentDialog from '../dialog/copy-dirent-dialog';
|
||||||
import ShareDialog from '../dialog/share-dialog';
|
import ShareDialog from '../dialog/share-dialog';
|
||||||
import ZipDownloadDialog from '../dialog/zip-download-dialog';
|
import ZipDownloadDialog from '../dialog/zip-download-dialog';
|
||||||
|
import EditFileTagDialog from '../dialog/edit-filetag-dialog';
|
||||||
|
|
||||||
import '../../css/dirent-list-item.css';
|
import '../../css/dirent-list-item.css';
|
||||||
|
|
||||||
@@ -60,6 +61,7 @@ class DirentListItem extends React.Component {
|
|||||||
isShowTagTooltip: false,
|
isShowTagTooltip: false,
|
||||||
isDragTipShow: false,
|
isDragTipShow: false,
|
||||||
isDropTipshow: false,
|
isDropTipshow: false,
|
||||||
|
isEditFileTagShow: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +192,9 @@ class DirentListItem extends React.Component {
|
|||||||
case 'Copy':
|
case 'Copy':
|
||||||
this.onItemCopyToggle();
|
this.onItemCopyToggle();
|
||||||
break;
|
break;
|
||||||
|
case 'Tags':
|
||||||
|
this.onEditFileTagToggle();
|
||||||
|
break;
|
||||||
case 'Permission':
|
case 'Permission':
|
||||||
this.onPermissionItem();
|
this.onPermissionItem();
|
||||||
break;
|
break;
|
||||||
@@ -216,6 +221,17 @@ class DirentListItem extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onEditFileTagToggle = () => {
|
||||||
|
this.setState({
|
||||||
|
isEditFileTagShow: !this.state.isEditFileTagShow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileTagChanged = () => {
|
||||||
|
let direntPath = this.getDirentPath(this.props.dirent);
|
||||||
|
this.props.onFileTagChanged(this.props.dirent, direntPath);
|
||||||
|
}
|
||||||
|
|
||||||
onItemRenameToggle = () => {
|
onItemRenameToggle = () => {
|
||||||
this.props.onItemRenameToggle(this.props.dirent);
|
this.props.onItemRenameToggle(this.props.dirent);
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -557,6 +573,15 @@ class DirentListItem extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</ModalPortal>
|
</ModalPortal>
|
||||||
}
|
}
|
||||||
|
{this.state.isEditFileTagShow &&
|
||||||
|
<EditFileTagDialog
|
||||||
|
repoID={this.props.repoID}
|
||||||
|
fileTagList={dirent.file_tags}
|
||||||
|
filePath={direntPath}
|
||||||
|
toggleCancel={this.onEditFileTagToggle}
|
||||||
|
onFileTagChanged={this.onFileTagChanged}
|
||||||
|
/>
|
||||||
|
}
|
||||||
{this.state.isZipDialogOpen &&
|
{this.state.isZipDialogOpen &&
|
||||||
<ModalPortal>
|
<ModalPortal>
|
||||||
<ZipDownloadDialog
|
<ZipDownloadDialog
|
||||||
|
@@ -44,6 +44,7 @@ const propTypes = {
|
|||||||
onItemsMove: PropTypes.func.isRequired,
|
onItemsMove: PropTypes.func.isRequired,
|
||||||
onItemsCopy: PropTypes.func.isRequired,
|
onItemsCopy: PropTypes.func.isRequired,
|
||||||
onItemsDelete: PropTypes.func.isRequired,
|
onItemsDelete: PropTypes.func.isRequired,
|
||||||
|
onFileTagChanged: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirentListView extends React.Component {
|
class DirentListView extends React.Component {
|
||||||
@@ -221,6 +222,7 @@ class DirentListView extends React.Component {
|
|||||||
this.setState({isCopyDialogShow: !this.state.isCopyDialogShow});
|
this.setState({isCopyDialogShow: !this.state.isCopyDialogShow});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onItemsDownload = () => {
|
onItemsDownload = () => {
|
||||||
let { path, repoID, selectedDirentList } = this.props;
|
let { path, repoID, selectedDirentList } = this.props;
|
||||||
if (selectedDirentList.length) {
|
if (selectedDirentList.length) {
|
||||||
@@ -454,7 +456,7 @@ class DirentListView extends React.Component {
|
|||||||
contextmenuList = this.props.showShareBtn ? [SHARE, DOWNLOAD, DELETE, 'Divider'] : [DOWNLOAD, DELETE, 'Divider'];
|
contextmenuList = this.props.showShareBtn ? [SHARE, DOWNLOAD, DELETE, 'Divider'] : [DOWNLOAD, DELETE, 'Divider'];
|
||||||
}
|
}
|
||||||
|
|
||||||
let { RENAME, MOVE, COPY, PERMISSION, OPEN_VIA_CLIENT, LOCK, UNLOCK, COMMENT, HISTORY, ACCESS_LOG } = TextTranslation;
|
let { RENAME, MOVE, COPY, PERMISSION, OPEN_VIA_CLIENT, LOCK, UNLOCK, COMMENT, HISTORY, ACCESS_LOG, TAGS } = TextTranslation;
|
||||||
if (type === 'dir' && permission === 'rw') {
|
if (type === 'dir' && permission === 'rw') {
|
||||||
if (can_set_folder_perm) {
|
if (can_set_folder_perm) {
|
||||||
menuList = [...contextmenuList, RENAME, MOVE, COPY, 'Divider', PERMISSION, 'Divider', OPEN_VIA_CLIENT];
|
menuList = [...contextmenuList, RENAME, MOVE, COPY, 'Divider', PERMISSION, 'Divider', OPEN_VIA_CLIENT];
|
||||||
@@ -476,6 +478,7 @@ class DirentListView extends React.Component {
|
|||||||
menuList.push(MOVE);
|
menuList.push(MOVE);
|
||||||
}
|
}
|
||||||
menuList.push(COPY);
|
menuList.push(COPY);
|
||||||
|
menuList.push(TAGS);
|
||||||
if (isPro) {
|
if (isPro) {
|
||||||
if (dirent.is_locked) {
|
if (dirent.is_locked) {
|
||||||
if (dirent.locked_by_me || (dirent.lock_owner === 'OnlineOffice' && permission === 'rw')) {
|
if (dirent.locked_by_me || (dirent.lock_owner === 'OnlineOffice' && permission === 'rw')) {
|
||||||
@@ -573,6 +576,7 @@ class DirentListView extends React.Component {
|
|||||||
onItemContextMenu={this.onItemContextMenu}
|
onItemContextMenu={this.onItemContextMenu}
|
||||||
selectedDirentList={this.props.selectedDirentList}
|
selectedDirentList={this.props.selectedDirentList}
|
||||||
activeDirent={this.state.activeDirent}
|
activeDirent={this.state.activeDirent}
|
||||||
|
onFileTagChanged={this.props.onFileTagChanged}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@@ -67,7 +67,8 @@ class TreeNodeView extends React.Component {
|
|||||||
this.props.onNodeClick(this.props.node);
|
this.props.onNodeClick(this.props.node);
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoadToggle = () => {
|
onLoadToggle = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
let { node } = this.props;
|
let { node } = this.props;
|
||||||
if (node.isExpanded) {
|
if (node.isExpanded) {
|
||||||
this.props.onNodeCollapse(node);
|
this.props.onNodeCollapse(node);
|
||||||
|
@@ -2,7 +2,7 @@ class TreeNode {
|
|||||||
|
|
||||||
constructor({ path, object, isLoaded, isPreload, isExpanded, parentNode }) {
|
constructor({ path, object, isLoaded, isPreload, isExpanded, parentNode }) {
|
||||||
this.path = path || object.name, // The default setting is the object name, which is set to a relative path when the father is set.
|
this.path = path || object.name, // The default setting is the object name, which is set to a relative path when the father is set.
|
||||||
this.object = object.clone();
|
this.object = object;
|
||||||
this.isLoaded = isLoaded || false;
|
this.isLoaded = isLoaded || false;
|
||||||
this.isPreload = isPreload || false;
|
this.isPreload = isPreload || false;
|
||||||
this.isExpanded = isExpanded || false;
|
this.isExpanded = isExpanded || false;
|
||||||
|
@@ -168,7 +168,7 @@ class TreeView extends React.Component {
|
|||||||
getMenuList = (node) => {
|
getMenuList = (node) => {
|
||||||
let menuList = [];
|
let menuList = [];
|
||||||
|
|
||||||
let { NEW_FOLDER, NEW_FILE, COPY, MOVE, RENAME, DELETE, OPEN_VIA_CLIENT} = TextTranslation;
|
let { NEW_FOLDER, NEW_FILE, COPY, MOVE, RENAME, DELETE, OPEN_VIA_CLIENT, TAGS} = TextTranslation;
|
||||||
|
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return [NEW_FOLDER, NEW_FILE];
|
return [NEW_FOLDER, NEW_FILE];
|
||||||
@@ -177,7 +177,7 @@ class TreeView extends React.Component {
|
|||||||
if (node.object.type === 'dir') {
|
if (node.object.type === 'dir') {
|
||||||
menuList = [NEW_FOLDER, NEW_FILE, COPY, MOVE, RENAME, DELETE];
|
menuList = [NEW_FOLDER, NEW_FILE, COPY, MOVE, RENAME, DELETE];
|
||||||
} else {
|
} else {
|
||||||
menuList = [RENAME, DELETE, COPY, MOVE, OPEN_VIA_CLIENT];
|
menuList = [RENAME, DELETE, COPY, MOVE, TAGS, OPEN_VIA_CLIENT];
|
||||||
}
|
}
|
||||||
|
|
||||||
return menuList;
|
return menuList;
|
||||||
|
@@ -187,6 +187,7 @@ class LibContentContainer extends React.Component {
|
|||||||
onItemsMove={this.props.onItemsMove}
|
onItemsMove={this.props.onItemsMove}
|
||||||
onItemsCopy={this.props.onItemsCopy}
|
onItemsCopy={this.props.onItemsCopy}
|
||||||
onItemsDelete={this.props.onItemsDelete}
|
onItemsDelete={this.props.onItemsDelete}
|
||||||
|
onFileTagChanged={this.props.onFileTagChanged}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{this.props.currentMode === 'grid' && (
|
{this.props.currentMode === 'grid' && (
|
||||||
@@ -250,6 +251,8 @@ class LibContentContainer extends React.Component {
|
|||||||
onItemsMove={this.props.onItemsMove}
|
onItemsMove={this.props.onItemsMove}
|
||||||
onItemsCopy={this.props.onItemsCopy}
|
onItemsCopy={this.props.onItemsCopy}
|
||||||
onItemsDelete={this.props.onItemsDelete}
|
onItemsDelete={this.props.onItemsDelete}
|
||||||
|
onFileTagChanged={this.props.onFileTagChanged}
|
||||||
|
onToolbarFileTagChanged={this.props.onToolbarFileTagChanged}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
@@ -1031,7 +1031,8 @@ class LibContentView extends React.Component {
|
|||||||
let results = {};
|
let results = {};
|
||||||
for (let i = 0; i < direntList.length; i++) {
|
for (let i = 0; i < direntList.length; i++) {
|
||||||
let object = direntList[i];
|
let object = direntList[i];
|
||||||
let key = object.parent_dir;
|
let parentDir = object.parent_dir;
|
||||||
|
let key = parentDir === '/' ? '/' : parentDir.slice(0, parentDir.length - 1);
|
||||||
if (!results[key]) {
|
if (!results[key]) {
|
||||||
results[key] = [];
|
results[key] = [];
|
||||||
}
|
}
|
||||||
@@ -1444,6 +1445,7 @@ class LibContentView extends React.Component {
|
|||||||
closeDirentDetail={this.closeDirentDetail}
|
closeDirentDetail={this.closeDirentDetail}
|
||||||
showDirentDetail={this.showDirentDetail}
|
showDirentDetail={this.showDirentDetail}
|
||||||
onDeleteRepoTag={this.onDeleteRepoTag}
|
onDeleteRepoTag={this.onDeleteRepoTag}
|
||||||
|
onToolbarFileTagChanged={this.onToolbarFileTagChanged}
|
||||||
/>
|
/>
|
||||||
{this.state.pathExist && !this.state.isViewFile && (
|
{this.state.pathExist && !this.state.isViewFile && (
|
||||||
<FileUploader
|
<FileUploader
|
||||||
|
@@ -296,6 +296,7 @@ class DirView(APIView):
|
|||||||
parent_dir_list.append(tmp_parent_dir)
|
parent_dir_list.append(tmp_parent_dir)
|
||||||
for folder_name in parent_dir.strip('/').split('/'):
|
for folder_name in parent_dir.strip('/').split('/'):
|
||||||
tmp_parent_dir = posixpath.join(tmp_parent_dir, folder_name)
|
tmp_parent_dir = posixpath.join(tmp_parent_dir, folder_name)
|
||||||
|
tmp_parent_dir = normalize_dir_path(tmp_parent_dir)
|
||||||
parent_dir_list.append(tmp_parent_dir)
|
parent_dir_list.append(tmp_parent_dir)
|
||||||
|
|
||||||
all_dir_info_list = []
|
all_dir_info_list = []
|
||||||
|
Reference in New Issue
Block a user