1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-02 15:38:15 +00:00
* star repo

* file_icon_url -> item_icon_url

* add UserStarredFiles.objects.get_starred_repos_by_user()
This commit is contained in:
lian
2019-02-26 14:14:00 +08:00
committed by Daniel Pan
parent ac989da25a
commit d22e600442
12 changed files with 141 additions and 35 deletions

View File

@@ -8,6 +8,7 @@ import { gettext, siteRoot, isPro, username, folderPermEnabled, isSystemStaff }
import ModalPotal from '../../components/modal-portal'; import ModalPotal from '../../components/modal-portal';
import ShareDialog from '../../components/dialog/share-dialog'; import ShareDialog from '../../components/dialog/share-dialog';
import Rename from '../rename'; import Rename from '../rename';
import { seafileAPI } from '../../utils/seafile-api';
const propTypes = { const propTypes = {
currentGroup: PropTypes.object, currentGroup: PropTypes.object,
@@ -31,6 +32,7 @@ class SharedRepoListItem extends React.Component {
isItemMenuShow: false, isItemMenuShow: false,
isShowSharedDialog: false, isShowSharedDialog: false,
isRenaming: false, isRenaming: false,
isStarred: this.props.repo.starred,
}; };
this.isDeparementOnwerGroupMember = false; this.isDeparementOnwerGroupMember = false;
} }
@@ -323,6 +325,18 @@ class SharedRepoListItem extends React.Component {
return null; return null;
} }
onStarRepo = () => {
if (this.state.isStarred) {
seafileAPI.unStarItem(this.props.repo.repo_id, '/').then(() => {
this.setState({isStarred: !this.state.isStarred});
});
} else {
seafileAPI.starItem(this.props.repo.repo_id, '/').then(() => {
this.setState({isStarred: !this.state.isStarred});
})
}
}
renderPCUI = () => { renderPCUI = () => {
let { iconUrl, iconTitle, libPath } = this.getRepoComputeParams(); let { iconUrl, iconTitle, libPath } = this.getRepoComputeParams();
let { repo } = this.props; let { repo } = this.props;
@@ -332,6 +346,10 @@ class SharedRepoListItem extends React.Component {
return ( return (
<Fragment> <Fragment>
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseOver={this.onMouseOver} onMouseLeave={this.onMouseLeave}> <tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseOver={this.onMouseOver} onMouseLeave={this.onMouseLeave}>
<td className="text-center">
{!this.state.isStarred && <i className="far fa-star star-empty cursor-pointer" onClick={this.onStarRepo}></i>}
{this.state.isStarred && <i className="fas fa-star cursor-pointer" onClick={this.onStarRepo}></i>}
</td>
<td><img src={iconUrl} title={repo.iconTitle} alt={iconTitle} width="24" /></td> <td><img src={iconUrl} title={repo.iconTitle} alt={iconTitle} width="24" /></td>
<td> <td>
{this.state.isRenaming ? {this.state.isRenaming ?

View File

@@ -104,8 +104,9 @@ class SharedRepoListView extends React.Component {
<table className={isShowTableThread ? '' : 'table-thead-hidden'}> <table className={isShowTableThread ? '' : 'table-thead-hidden'}>
<thead> <thead>
<tr> <tr>
<th width="4%"></th>
<th width="4%"><span className="sr-only">{gettext('Library Type')}</span></th> <th width="4%"><span className="sr-only">{gettext('Library Type')}</span></th>
<th width="40%"><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {sortByName && sortIcon}</a></th> <th width="36%"><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {sortByName && sortIcon}</a></th>
<th width="12%"><span className="sr-only">{gettext('Actions')}</span></th> <th width="12%"><span className="sr-only">{gettext('Actions')}</span></th>
<th width={'14%'}>{gettext('Size')}</th> <th width={'14%'}>{gettext('Size')}</th>
<th width={'14%'}><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {sortByTime && sortIcon}</a></th> <th width={'14%'}><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {sortByTime && sortIcon}</a></th>

View File

@@ -15,6 +15,7 @@ class Repo {
this.modifier_email = object.modifier_email; this.modifier_email = object.modifier_email;
this.modifier_name = object.modifier_name; this.modifier_name = object.modifier_name;
this.type = object.type; this.type = object.type;
this.starred = object.starred;
if (object.is_admin != undefined) { if (object.is_admin != undefined) {
this.is_admin = object.is_admin; this.is_admin = object.is_admin;
} }

View File

@@ -34,6 +34,7 @@ class MylibRepoListItem extends React.Component {
super(props); super(props);
this.state = { this.state = {
isOpIconShow: false, isOpIconShow: false,
isStarred: this.props.repo.starred,
isRenaming: false, isRenaming: false,
isShareDialogShow: false, isShareDialogShow: false,
isDeleteDialogShow: false, isDeleteDialogShow: false,
@@ -103,6 +104,18 @@ class MylibRepoListItem extends React.Component {
this.props.onRepoClick(this.props.repo); this.props.onRepoClick(this.props.repo);
} }
onStarRepo = () => {
if (this.state.isStarred) {
seafileAPI.unStarItem(this.props.repo.repo_id, '/').then(() => {
this.setState({isStarred: !this.state.isStarred});
});
} else {
seafileAPI.starItem(this.props.repo.repo_id, '/').then(() => {
this.setState({isStarred: !this.state.isStarred});
})
}
}
onShareToggle = () => { onShareToggle = () => {
this.setState({isShareDialogShow: !this.state.isShareDialogShow}); this.setState({isShareDialogShow: !this.state.isShareDialogShow});
} }
@@ -185,6 +198,10 @@ class MylibRepoListItem extends React.Component {
let repoURL = `${siteRoot}library/${repo.repo_id}/${Utils.encodePath(repo.repo_name)}/`; let repoURL = `${siteRoot}library/${repo.repo_id}/${Utils.encodePath(repo.repo_name)}/`;
return ( return (
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onClick={this.onRepoClick}> <tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onClick={this.onRepoClick}>
<td className="text-center">
{!this.state.isStarred && <i className="far fa-star star-empty cursor-pointer" onClick={this.onStarRepo}></i>}
{this.state.isStarred && <i className="fas fa-star cursor-pointer" onClick={this.onStarRepo}></i>}
</td>
<td><img src={iconUrl} title={iconTitle} alt={iconTitle} width="24" /></td> <td><img src={iconUrl} title={iconTitle} alt={iconTitle} width="24" /></td>
<td> <td>
{this.state.isRenaming && ( {this.state.isRenaming && (

View File

@@ -77,8 +77,9 @@ class MylibRepoListView extends React.Component {
<table> <table>
<thead> <thead>
<tr> <tr>
<th width="4%"></th>
<th width="4%"><span className="sr-only">{gettext('Library Type')}</span></th> <th width="4%"><span className="sr-only">{gettext('Library Type')}</span></th>
<th width="42%"><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {this.props.sortBy === 'name' && sortIcon}</a></th> <th width="38%"><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {this.props.sortBy === 'name' && sortIcon}</a></th>
<th width="14%"><span className="sr-only">{gettext('Actions')}</span></th> <th width="14%"><span className="sr-only">{gettext('Actions')}</span></th>
<th width={showStorageBackend ? '15%' : '20%'}>{gettext('Size')}</th> <th width={showStorageBackend ? '15%' : '20%'}>{gettext('Size')}</th>
{showStorageBackend ? <th width="10%">{gettext('Storage backend')}</th> : null} {showStorageBackend ? <th width="10%">{gettext('Storage backend')}</th> : null}

View File

@@ -49,8 +49,9 @@ class Content extends Component {
const desktopThead = ( const desktopThead = (
<thead> <thead>
<tr> <tr>
<th width="4%"></th>
<th width="4%"><span className="sr-only">{gettext('Library Type')}</span></th> <th width="4%"><span className="sr-only">{gettext('Library Type')}</span></th>
<th width="38%"><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {sortByName && sortIcon}</a></th> <th width="34%"><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {sortByName && sortIcon}</a></th>
<th width="10%"><span className="sr-only">{gettext('Actions')}</span></th> <th width="10%"><span className="sr-only">{gettext('Actions')}</span></th>
<th width="14%">{gettext('Size')}</th> <th width="14%">{gettext('Size')}</th>
<th width="18%"><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {sortByTime && sortIcon}</a></th> <th width="18%"><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {sortByTime && sortIcon}</a></th>
@@ -120,6 +121,7 @@ class Item extends Component {
showOpIcon: false, showOpIcon: false,
unshared: false, unshared: false,
isShowSharedDialog: false, isShowSharedDialog: false,
isStarred: this.props.data.starred,
}; };
this.handleMouseOver = this.handleMouseOver.bind(this); this.handleMouseOver = this.handleMouseOver.bind(this);
@@ -176,6 +178,18 @@ class Item extends Component {
this.setState({isShowSharedDialog: false}); this.setState({isShowSharedDialog: false});
} }
onStarRepo = () => {
if (this.state.isStarred) {
seafileAPI.unStarItem(this.props.data.repo_id, '/').then(() => {
this.setState({isStarred: !this.state.isStarred});
});
} else {
seafileAPI.starItem(this.props.data.repo_id, '/').then(() => {
this.setState({isStarred: !this.state.isStarred});
})
}
}
render() { render() {
if (this.state.unshared) { if (this.state.unshared) {
return null; return null;
@@ -194,6 +208,10 @@ class Item extends Component {
const desktopItem = ( const desktopItem = (
<Fragment> <Fragment>
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}> <tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
<td className="text-center">
{!this.state.isStarred && <i className="far fa-star star-empty cursor-pointer" onClick={this.onStarRepo}></i>}
{this.state.isStarred && <i className="fas fa-star cursor-pointer" onClick={this.onStarRepo}></i>}
</td>
<td><img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="24" /></td> <td><img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="24" /></td>
<td><Link to={`${siteRoot}library/${data.repo_id}/${data.repo_name}/`}>{data.repo_name}</Link></td> <td><Link to={`${siteRoot}library/${data.repo_id}/${data.repo_name}/`}>{data.repo_name}</Link></td>
<td> <td>

View File

@@ -94,7 +94,12 @@ class TableBody extends Component {
let listFilesActivities = this.state.items.map(function(item, index) { let listFilesActivities = this.state.items.map(function(item, index) {
item.file_icon_url = item.is_dir ? Utils.getFolderIconUrl(false) : Utils.getFileIconUrl(item.obj_name); if (item.path === '/') {
item.item_icon_url = Utils.getDefaultLibIconUrl(false);
} else {
item.item_icon_url = item.is_dir ? Utils.getFolderIconUrl(false) : Utils.getFileIconUrl(item.obj_name);
}
item.encoded_path = Utils.encodePath(item.path); item.encoded_path = Utils.encodePath(item.path);
item.thumbnail_url = item.encoded_thumbnail_src ? `${siteRoot}${item.encoded_thumbnail_src}` : ''; item.thumbnail_url = item.encoded_thumbnail_src ? `${siteRoot}${item.encoded_thumbnail_src}` : '';
@@ -165,7 +170,7 @@ class Item extends Component {
{ {
data.thumbnail_url ? data.thumbnail_url ?
<img className="thumbnail" src={data.thumbnail_url} alt="" /> : <img className="thumbnail" src={data.thumbnail_url} alt="" /> :
<img src={data.file_icon_url} alt={gettext('icon')} width="24" /> <img src={data.item_icon_url} alt={gettext('icon')} width="24" />
} }
</td> </td>
<td> <td>
@@ -188,7 +193,7 @@ class Item extends Component {
{ {
data.thumbnail_url ? data.thumbnail_url ?
<img className="thumbnail" src={data.thumbnail_url} alt="" /> : <img className="thumbnail" src={data.thumbnail_url} alt="" /> :
<img src={data.file_icon_url} alt={gettext('icon')} width="24" /> <img src={data.item_icon_url} alt={gettext('icon')} width="24" />
} }
</td> </td>
<td> <td>

View File

@@ -229,6 +229,13 @@ export const Utils = {
navigator.userAgent.indexOf('Chrome') > -1; navigator.userAgent.indexOf('Chrome') > -1;
}, },
getDefaultLibIconUrl: function(isBig) {
let size = Utils.isHiDPI() ? 48 : 24;
size = isBig ? 256 : size;
let icon_name = 'lib.png';
return mediaUrl + 'img/lib/' + size + '/' + icon_name;
},
getLibIconUrl: function(repo, isBig) { getLibIconUrl: function(repo, isBig) {
let permission = repo.permission || repo.share_permission; //Compatible with regular repo and repo shared let permission = repo.permission || repo.share_permission; //Compatible with regular repo and repo shared
let size = Utils.isHiDPI() ? 48 : 24; let size = Utils.isHiDPI() ? 48 : 24;

View File

@@ -29,6 +29,7 @@ from seahub.share.signals import share_repo_to_group_successful
from seahub.share.utils import is_repo_admin, check_group_share_in_permission, \ from seahub.share.utils import is_repo_admin, check_group_share_in_permission, \
share_dir_to_group share_dir_to_group
from seahub.constants import PERMISSION_READ from seahub.constants import PERMISSION_READ
from seahub.base.models import UserStarredFiles
from seahub.base.templatetags.seahub_tags import email2nickname, \ from seahub.base.templatetags.seahub_tags import email2nickname, \
email2contact_email email2contact_email
@@ -69,6 +70,7 @@ class GroupLibraries(APIView):
""" """
# only group member can get group libraries # only group member can get group libraries
username = request.user.username
if not is_group_member(group_id, request.user.username): if not is_group_member(group_id, request.user.username):
error_msg = 'Permission denied.' error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg) return api_error(status.HTTP_403_FORBIDDEN, error_msg)
@@ -113,6 +115,13 @@ class GroupLibraries(APIView):
else: else:
contact_email_dict[email] = email2contact_email(email) contact_email_dict[email] = email2contact_email(email)
try:
starred_repos = UserStarredFiles.objects.get_starred_repos_by_user(username)
starred_repo_id_list = [item.repo_id for item in starred_repos]
except Exception as e:
logger.error(e)
starred_repo_id_list = []
result = [] result = []
for group_repo in group_repos: for group_repo in group_repos:
group_repo_info = get_group_repo_info(request, group_repo) group_repo_info = get_group_repo_info(request, group_repo)
@@ -127,6 +136,8 @@ class GroupLibraries(APIView):
group_repo_info['modifier_name'] = name_dict.get(modifier, '') group_repo_info['modifier_name'] = name_dict.get(modifier, '')
group_repo_info['modifier_contact_email'] = contact_email_dict.get(modifier, '') group_repo_info['modifier_contact_email'] = contact_email_dict.get(modifier, '')
group_repo_info['starred'] = group_repo.id in starred_repo_id_list
result.append(group_repo_info) result.append(group_repo_info)
return Response(result) return Response(result)

View File

@@ -28,6 +28,7 @@ from seahub.group.utils import validate_group_name, check_group_name_conflict, \
is_group_member, is_group_admin, is_group_owner, is_group_admin_or_owner, \ is_group_member, is_group_admin, is_group_owner, is_group_admin_or_owner, \
group_id_to_name group_id_to_name
from seahub.group.views import remove_group_common from seahub.group.views import remove_group_common
from seahub.base.models import UserStarredFiles
from seahub.base.templatetags.seahub_tags import email2nickname, \ from seahub.base.templatetags.seahub_tags import email2nickname, \
translate_seahub_time, email2contact_email translate_seahub_time, email2contact_email
from seahub.views.modules import is_wiki_mod_enabled_for_group, \ from seahub.views.modules import is_wiki_mod_enabled_for_group, \
@@ -111,6 +112,13 @@ class Groups(APIView):
gids = [g.id for g in user_groups] gids = [g.id for g in user_groups]
admin_info = ExtraGroupsSharePermission.objects.batch_get_repos_with_admin_permission(gids) admin_info = ExtraGroupsSharePermission.objects.batch_get_repos_with_admin_permission(gids)
try:
starred_repos = UserStarredFiles.objects.get_starred_repos_by_user(username)
starred_repo_id_list = [item.repo_id for item in starred_repos]
except Exception as e:
logger.error(e)
starred_repo_id_list = []
for g in user_groups: for g in user_groups:
group_info = get_group_info(request, g.id, avatar_size) group_info = get_group_info(request, g.id, avatar_size)
@@ -170,7 +178,8 @@ class Groups(APIView):
"owner_email": repo_owner, "owner_email": repo_owner,
"owner_name": name_dict.get(repo_owner, ''), "owner_name": name_dict.get(repo_owner, ''),
"owner_contact_email": contact_email_dict.get(repo_owner, ''), "owner_contact_email": contact_email_dict.get(repo_owner, ''),
"is_admin": (r.id, g.id) in admin_info "is_admin": (r.id, g.id) in admin_info,
"starred": r.repo_id in starred_repo_id_list,
} }
repos.append(repo) repos.append(repo)

View File

@@ -14,6 +14,7 @@ from seahub.api2.utils import api_error
from seahub.api2.endpoints.group_owned_libraries import get_group_id_by_repo_owner from seahub.api2.endpoints.group_owned_libraries import get_group_id_by_repo_owner
from seahub.base.models import UserStarredFiles
from seahub.base.templatetags.seahub_tags import email2nickname, \ from seahub.base.templatetags.seahub_tags import email2nickname, \
email2contact_email email2contact_email
from seahub.signals import repo_deleted from seahub.signals import repo_deleted
@@ -71,8 +72,16 @@ class ReposView(APIView):
if is_org_context(request): if is_org_context(request):
org_id = request.user.org.org_id org_id = request.user.org.org_id
try:
starred_repos = UserStarredFiles.objects.get_starred_repos_by_user(email)
starred_repo_id_list = [item.repo_id for item in starred_repos]
except Exception as e:
logger.error(e)
starred_repo_id_list = []
repo_info_list = [] repo_info_list = []
if filter_by['mine']: if filter_by['mine']:
if org_id: if org_id:
owned_repos = seafile_api.get_org_owned_repo_list(org_id, owned_repos = seafile_api.get_org_owned_repo_list(org_id,
email, ret_corrupted=True) email, ret_corrupted=True)
@@ -109,6 +118,7 @@ class ReposView(APIView):
"size": r.size, "size": r.size,
"encrypted": r.encrypted, "encrypted": r.encrypted,
"permission": 'rw', # Always have read-write permission to owned repo "permission": 'rw', # Always have read-write permission to owned repo
"starred": r.repo_id in starred_repo_id_list,
} }
if is_pro_version() and ENABLE_STORAGE_CLASSES: if is_pro_version() and ENABLE_STORAGE_CLASSES:
@@ -169,6 +179,7 @@ class ReposView(APIView):
"size": r.size, "size": r.size,
"encrypted": r.encrypted, "encrypted": r.encrypted,
"permission": r.permission, "permission": r.permission,
"starred": r.repo_id in starred_repo_id_list,
} }
if r.repo_id in repos_with_admin_share_to: if r.repo_id in repos_with_admin_share_to:
@@ -210,6 +221,7 @@ class ReposView(APIView):
"size": r.size, "size": r.size,
"encrypted": r.encrypted, "encrypted": r.encrypted,
"permission": r.permission, "permission": r.permission,
"starred": r.repo_id in starred_repo_id_list,
} }
repo_info_list.append(repo_info) repo_info_list.append(repo_info)
@@ -252,6 +264,7 @@ class ReposView(APIView):
"size": r.size, "size": r.size,
"encrypted": r.encrypted, "encrypted": r.encrypted,
"permission": r.permission, "permission": r.permission,
"starred": r.repo_id in starred_repo_id_list,
} }
repo_info_list.append(repo_info) repo_info_list.append(repo_info)

View File

@@ -149,6 +149,11 @@ class StarredFile(object):
class UserStarredFilesManager(models.Manager): class UserStarredFilesManager(models.Manager):
def get_starred_repos_by_user(self, email):
starred_repos = UserStarredFiles.objects.filter(email=email, path='/')
return starred_repos
def get_starred_item(self, email, repo_id, path): def get_starred_item(self, email, repo_id, path):
path_list = [normalize_file_path(path), normalize_dir_path(path)] path_list = [normalize_file_path(path), normalize_dir_path(path)]