mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-03 07:55:36 +00:00
add search results highlight
This commit is contained in:
@@ -13,6 +13,11 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
BUSINESS_MODE = False
|
BUSINESS_MODE = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
from settings import ENABLE_FILE_SEARCH
|
||||||
|
except ImportError:
|
||||||
|
ENABLE_FILE_SEARCH = False
|
||||||
|
|
||||||
def base(request):
|
def base(request):
|
||||||
"""
|
"""
|
||||||
Add seahub base configure to the context.
|
Add seahub base configure to the context.
|
||||||
@@ -37,5 +42,6 @@ def base(request):
|
|||||||
'site_name': SITE_NAME,
|
'site_name': SITE_NAME,
|
||||||
'enable_signup': ENABLE_SIGNUP,
|
'enable_signup': ENABLE_SIGNUP,
|
||||||
'max_file_name': MAX_FILE_NAME,
|
'max_file_name': MAX_FILE_NAME,
|
||||||
|
'enable_file_search': ENABLE_FILE_SEARCH,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -181,4 +181,17 @@ class DirFilesLastModifiedInfo(models.Model):
|
|||||||
last_modified_info = models.TextField()
|
last_modified_info = models.TextField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('repo_id', 'parent_dir_hash')
|
unique_together = ('repo_id', 'parent_dir_hash')
|
||||||
|
|
||||||
|
class FileLastModifiedInfo(models.Model):
|
||||||
|
repo_id = models.CharField(max_length=36, db_index=True)
|
||||||
|
file_id = models.CharField(max_length=40)
|
||||||
|
|
||||||
|
file_path = models.TextField()
|
||||||
|
file_path_hash = models.CharField(max_length=12)
|
||||||
|
|
||||||
|
last_modified = models.BigIntegerField()
|
||||||
|
email = models.EmailField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ('repo_id', 'file_path_hash')
|
@@ -1971,6 +1971,41 @@ textarea:-moz-placeholder {/* for FF */
|
|||||||
background:#f7f7f8;
|
background:#f7f7f8;
|
||||||
border-radius:2px;
|
border-radius:2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search-results {
|
#search-results {
|
||||||
padding-top:15px;
|
padding-top:15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-results a {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-results b {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results-item {
|
||||||
|
margin-top: 20px;
|
||||||
|
clear: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results-item .file-icon {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results-item .title {
|
||||||
|
margin: 0 0 0 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results-item .title a,
|
||||||
|
.search-results-item .title img,
|
||||||
|
.search-results-item .title span
|
||||||
|
{
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results-item .content {
|
||||||
|
clear: left;
|
||||||
|
margin: 0 0 0 40px;
|
||||||
}
|
}
|
@@ -87,7 +87,7 @@
|
|||||||
</a>
|
</a>
|
||||||
{% block nav %}{% endblock %}
|
{% block nav %}{% endblock %}
|
||||||
|
|
||||||
{% if request.user.is_authenticated %}
|
{% if enable_file_search and request.user.is_authenticated %}
|
||||||
<form id="top-search-form" method="get" action="{% url 'search' %}" class="search-form fright">
|
<form id="top-search-form" method="get" action="{% url 'search' %}" class="search-form fright">
|
||||||
<input class="search-input" name="q" placeholder="{% trans 'Search Files' %}" value="{{ keyword }}" />
|
<input class="search-input" name="q" placeholder="{% trans 'Search Files' %}" value="{{ keyword }}" />
|
||||||
<input type="submit" value="" class="search-submit" />
|
<input type="submit" value="" class="search-submit" />
|
||||||
|
@@ -16,26 +16,39 @@
|
|||||||
<p>{% trans 'No result found' %}</p>
|
<p>{% trans 'No result found' %}</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p class="tip">{% blocktrans count counter=total %}{{ total }} result{% plural %}{{ total }} results{% endblocktrans%}</p>
|
<p class="tip">{% blocktrans count counter=total %}{{ total }} result{% plural %}{{ total }} results{% endblocktrans%}</p>
|
||||||
<table>
|
<ul id="search-results-list">
|
||||||
<tr>
|
{% for file in results %}
|
||||||
<th width="5%"></th>
|
<li class="search-results-item">
|
||||||
<th width="45%">{% trans 'Name' %}</th>
|
<img class="file-icon" src="{{ MEDIA_URL }}img/file/{{ file.name|file_icon_filter }}" alt="{% trans "File"%}" />
|
||||||
<th width="22%">{% trans 'Library' %}</th>
|
<div class="title">
|
||||||
<th width="28%">{% trans 'Owner' %}</th>
|
<a href="{% url 'repo' repo_id=file.repo.id %}" target="_blank">{{ file.repo.name }}</a>
|
||||||
</tr>
|
<span>–</span>
|
||||||
{% for file in results %}
|
<a class="filename" href="{% url 'repo_view_file' repo_id=file.repo.id %}?p={{ file.fullpath|urlencode }}" target="_blank" title="{{ file.fullpath|slice:'1:'}}" >
|
||||||
<tr>
|
{% if file.name_highlight %}
|
||||||
<td><img src="{{ MEDIA_URL }}img/file/{{ file.name|file_icon_filter }}" alt="{% trans "File"%}" /></td>
|
{{ file.name_highlight|safe }}
|
||||||
<td><a href="{% url 'repo_view_file' repo_id=file.repo.id %}?p={{ file.fullpath|urlencode }}" target="_blank">{{ file.name }}</a></td>
|
{% else %}
|
||||||
<td><a href="{% url 'repo' repo_id=file.repo.id %}" target="_blank">{{ file.repo.name }}</a></td>
|
{{ file.name }}
|
||||||
<td>
|
{% endif %}
|
||||||
{% avatar file.repo.owner 20 %}
|
</a>
|
||||||
<a class="name" href="{% url 'profile.views.user_profile' file.repo.owner %}" target="_blank">{{ file.repo.owner|email2nickname }}</a>
|
<p>
|
||||||
</td>
|
{% if file.last_modified_by %}
|
||||||
</tr>
|
{% avatar file.last_modified_by 20 %}
|
||||||
{% endfor %}
|
<a class="name" href="{% url 'profile.views.user_profile' file.last_modified_by %}">{{ file.last_modified_by|email2nickname }}</a>
|
||||||
</table>
|
{% endif %}
|
||||||
{% if total > 25 %}
|
{% if file.last_modified %}
|
||||||
|
{{ file.last_modified|translate_seahub_time }}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% if file.content_highlight %}
|
||||||
|
<div class="content">
|
||||||
|
{{ file.content_highlight|safe }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% if total > per_page %}
|
||||||
<div id="paginator">
|
<div id="paginator">
|
||||||
{% if current_page != 1 %}
|
{% if current_page != 1 %}
|
||||||
<a href="?q={{ keyword|urlencode }}&page={{ prev_page }}&per_page={{ per_page }}">{% trans "Previous"%}</a>
|
<a href="?q={{ keyword|urlencode }}&page={{ prev_page }}&per_page={{ per_page }}">{% trans "Previous"%}</a>
|
||||||
|
6
urls.py
6
urls.py
@@ -103,7 +103,6 @@ urlpatterns = patterns('',
|
|||||||
url(r'^sys/orgadmin/$', sys_org_admin, name='sys_org_admin'),
|
url(r'^sys/orgadmin/$', sys_org_admin, name='sys_org_admin'),
|
||||||
url(r'^sys/groupadmin/$', sys_group_admin, name='sys_group_admin'),
|
url(r'^sys/groupadmin/$', sys_group_admin, name='sys_group_admin'),
|
||||||
|
|
||||||
url(r'^search/$', search, name='search'),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if settings.SERVE_STATIC:
|
if settings.SERVE_STATIC:
|
||||||
@@ -128,3 +127,8 @@ else:
|
|||||||
url(r'^pubinfo/groups/$', pubgrp, name='pubgrp'),
|
url(r'^pubinfo/groups/$', pubgrp, name='pubgrp'),
|
||||||
url(r'^pubinfo/users/$', pubuser, name='pubuser'),
|
url(r'^pubinfo/users/$', pubuser, name='pubuser'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if getattr(settings, 'ENABLE_FILE_SEARCH', False):
|
||||||
|
urlpatterns += patterns('',
|
||||||
|
url(r'^search/$', search, name='search'),
|
||||||
|
)
|
@@ -15,7 +15,7 @@ from django.template import RequestContext
|
|||||||
from django.utils.hashcompat import sha_constructor, md5_constructor
|
from django.utils.hashcompat import sha_constructor, md5_constructor
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from base.models import FileContributors, UserStarredFiles, DirFilesLastModifiedInfo
|
from base.models import FileContributors, UserStarredFiles, DirFilesLastModifiedInfo, FileLastModifiedInfo
|
||||||
|
|
||||||
from htmldiff import HtmlDiff
|
from htmldiff import HtmlDiff
|
||||||
|
|
||||||
@@ -406,6 +406,54 @@ def check_and_get_org_by_group(group_id, user):
|
|||||||
|
|
||||||
return org, base_template
|
return org, base_template
|
||||||
|
|
||||||
|
def calc_file_last_modified(repo_id, file_path, file_path_hash, file_id):
|
||||||
|
try:
|
||||||
|
# get the lastest one file revision
|
||||||
|
commits = seafserv_threaded_rpc.list_file_revisions(repo_id, file_path, 1, -1)
|
||||||
|
except SearpcError, e:
|
||||||
|
return '', 0
|
||||||
|
|
||||||
|
if not commits:
|
||||||
|
return '', 0
|
||||||
|
|
||||||
|
email, last_modified = commits[0].creator_name, commits[0].ctime
|
||||||
|
|
||||||
|
info = FileLastModifiedInfo(repo_id=repo_id,
|
||||||
|
file_path=file_path,
|
||||||
|
file_path_hash=file_path_hash,
|
||||||
|
file_id=file_id,
|
||||||
|
last_modified=last_modified,
|
||||||
|
email=email)
|
||||||
|
try:
|
||||||
|
info.save()
|
||||||
|
except IntegrityError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return email, last_modified
|
||||||
|
|
||||||
|
def get_file_last_modified(repo_id, file_path):
|
||||||
|
email = ''
|
||||||
|
last_modified = 0
|
||||||
|
file_path_hash = calc_file_path_hash(file_path)
|
||||||
|
file_id = seafserv_threaded_rpc.get_file_id_by_path(repo_id, file_path)
|
||||||
|
try:
|
||||||
|
fc = FileLastModifiedInfo.objects.get(repo_id=repo_id,
|
||||||
|
file_path_hash=file_path_hash)
|
||||||
|
except FileLastModifiedInfo.DoesNotExist:
|
||||||
|
# has no cache yet
|
||||||
|
user, last_modified = calc_file_last_modified(repo_id, file_path, file_path_hash, file_id)
|
||||||
|
else:
|
||||||
|
# cache found
|
||||||
|
if fc.file_id != file_id:
|
||||||
|
# but cache is outdated
|
||||||
|
fc.delete()
|
||||||
|
user, last_modified = calc_file_last_modified(repo_id, file_path, file_path_hash, file_id)
|
||||||
|
else:
|
||||||
|
# cache is valid
|
||||||
|
user, last_modified = fc.email, fc.last_modified
|
||||||
|
|
||||||
|
return user, last_modified
|
||||||
|
|
||||||
def get_file_contributors_from_revisions(repo_id, file_path):
|
def get_file_contributors_from_revisions(repo_id, file_path):
|
||||||
"""Inspect the file history and get a list of users who have modified the
|
"""Inspect the file history and get a list of users who have modified the
|
||||||
it.
|
it.
|
||||||
@@ -413,7 +461,7 @@ def get_file_contributors_from_revisions(repo_id, file_path):
|
|||||||
"""
|
"""
|
||||||
commits = []
|
commits = []
|
||||||
try:
|
try:
|
||||||
commits = seafserv_threaded_rpc.list_file_revisions(repo_id, file_path, -1)
|
commits = seafserv_threaded_rpc.list_file_revisions(repo_id, file_path, -1, -1)
|
||||||
except SearpcError, e:
|
except SearpcError, e:
|
||||||
return [], 0, ''
|
return [], 0, ''
|
||||||
|
|
||||||
@@ -770,48 +818,58 @@ def is_textual_file(file_type):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if getattr(settings, 'ENABLE_FILE_SEARCH', False):
|
||||||
|
from seafes import es_get_conn, es_search
|
||||||
|
|
||||||
from seafes import es_get_conn, es_search
|
conn = es_get_conn()
|
||||||
|
def search_file_by_name(request, keyword, start, size):
|
||||||
|
owned_repos, shared_repos, groups_repos, pub_repo_list = get_user_repos(request.user)
|
||||||
|
|
||||||
conn = es_get_conn()
|
# unify the repo.owner property
|
||||||
|
for repo in owned_repos:
|
||||||
|
repo.owner = request.user.username
|
||||||
|
for repo in shared_repos:
|
||||||
|
repo.owner = repo.user
|
||||||
|
for repo in pub_repo_list:
|
||||||
|
repo.owner = repo.user
|
||||||
|
|
||||||
def search_file_by_name(request, keyword, start, size):
|
pubrepo_id_map = {}
|
||||||
owned_repos, shared_repos, groups_repos, pub_repo_list = get_user_repos(request.user)
|
for repo in pub_repo_list:
|
||||||
|
# fix pub repo obj attr name mismatch in seafile/lib/repo.vala
|
||||||
|
repo.id = repo.repo_id
|
||||||
|
repo.name = repo.repo_name
|
||||||
|
pubrepo_id_map[repo.id] = repo
|
||||||
|
|
||||||
# unify the repo.owner property
|
# get a list of pure non-pub repos
|
||||||
for repo in owned_repos:
|
nonpub_repo_list = []
|
||||||
repo.owner = request.user.username
|
for repo in owned_repos + shared_repos + groups_repos:
|
||||||
for repo in shared_repos:
|
if repo.id not in pubrepo_id_map:
|
||||||
repo.owner = repo.user
|
nonpub_repo_list.append(repo)
|
||||||
for repo in pub_repo_list:
|
|
||||||
repo.owner = repo.user
|
|
||||||
|
|
||||||
pubrepo_id_map = {}
|
nonpub_repo_ids = [ repo.id for repo in nonpub_repo_list]
|
||||||
for repo in pub_repo_list:
|
|
||||||
# fix pub repo obj attr name mismatch in seafile/lib/repo.vala
|
|
||||||
repo.id = repo.repo_id
|
|
||||||
repo.name = repo.repo_name
|
|
||||||
pubrepo_id_map[repo.id] = repo
|
|
||||||
|
|
||||||
# get a list of pure non-pub repos
|
files_found, total = es_search(conn, nonpub_repo_ids, keyword, start, size)
|
||||||
nonpub_repo_list = []
|
|
||||||
for repo in owned_repos + shared_repos + groups_repos:
|
|
||||||
if repo.id not in pubrepo_id_map:
|
|
||||||
nonpub_repo_list.append(repo)
|
|
||||||
|
|
||||||
nonpub_repo_ids = [ repo.id for repo in nonpub_repo_list]
|
if len(files_found) > 0:
|
||||||
|
# construt a (id, repo) hash table for fast lookup
|
||||||
|
repo_id_map = {}
|
||||||
|
for repo in nonpub_repo_list:
|
||||||
|
repo_id_map[repo.id] = repo
|
||||||
|
|
||||||
files_found, total = es_search(conn, nonpub_repo_ids, keyword, start, size)
|
repo_id_map.update(pubrepo_id_map)
|
||||||
|
|
||||||
if len(files_found) > 0:
|
for f in files_found:
|
||||||
# construt a (id, repo) hash table for fast lookup
|
repo = repo_id_map.get(f['repo_id'].encode('UTF-8'), None)
|
||||||
repo_id_map = {}
|
if repo:
|
||||||
for repo in nonpub_repo_list:
|
f['repo'] = repo
|
||||||
repo_id_map[repo.id] = repo
|
f['exists'] = True
|
||||||
|
f['last_modified_by'], f['last_modified'] = get_file_last_modified(f['repo_id'], f['fullpath'])
|
||||||
|
else:
|
||||||
|
f['exists'] = False
|
||||||
|
|
||||||
repo_id_map.update(pubrepo_id_map)
|
files_found = filter(lambda f: f['exists'], files_found)
|
||||||
|
|
||||||
for f in files_found:
|
return files_found, total
|
||||||
f['repo'] = repo_id_map[f['repo_id'].encode('UTF-8')]
|
else:
|
||||||
|
def search_file_by_name(*args):
|
||||||
return files_found, total
|
pass
|
||||||
|
Reference in New Issue
Block a user