diff --git a/media/css/seahub.css b/media/css/seahub.css index 872f33f416..ece9a0adfa 100644 --- a/media/css/seahub.css +++ b/media/css/seahub.css @@ -1915,6 +1915,7 @@ textarea:-moz-placeholder {/* for FF */ height: 30px; } + /* wiki page */ #wiki-area.article { padding: 0 0; @@ -1925,3 +1926,55 @@ textarea:-moz-placeholder {/* for FF */ .wiki-page-missing { color:red; } + +#search-form { + float: right; + margin-top: 5px; + width: 200px; + height: 24px; + border: 2px solid #CDCDCD; + border-color: #9A9A9A #CDCDCD #CDCDCD #9A9A9A; + border-radius: 3px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + box-shadow: 0 0 0 #000,inset 0px 3px 3px #eee; + -moz-box-shadow: 0 0 0 #000,inset 0px 3px 3px #eee; + -webkit-box-shadow: 0 0 0 #000,inset 0px 3px 3px #eee; +} + +#search-keyword { + margin: 0; + width: 180px; + height: 20px; + padding: 2px; + padding-left: 18px; + font-size: 12px; + line-height: 12px; + border: 0; + outline: 0; + background: url('../img/search.png') no-repeat scroll left center; +} + +.search-info { + font-size: 16px; + border-bottom: 1px solid #8A948F; + padding-bottom: 3px; +} + +.search-result-count { + font-size: 13px; + color: #999; + line-height: 35px; +} + +.search-info-keyword { + font-size: 18px; + font-weight: bold; +} + +.search-results { + margin-top: 10px; +} + diff --git a/media/img/search.png b/media/img/search.png new file mode 100644 index 0000000000..ad1c434937 Binary files /dev/null and b/media/img/search.png differ diff --git a/search.py b/search.py new file mode 100644 index 0000000000..8ad26a2f7a --- /dev/null +++ b/search.py @@ -0,0 +1,58 @@ +import settings +import os +import stat +import simplejson as json +import re +import tempfile +import sys +import urllib +import urllib2 +import logging +import chardet +from urllib import quote +from django.core.cache import cache +from django.core.urlresolvers import reverse +from django.core.mail import send_mail +from django.contrib import messages +from django.contrib.sites.models import Site, RequestSite +from django.db import IntegrityError +from django.db.models import F +from django.http import HttpResponse, HttpResponseBadRequest, Http404, \ + HttpResponseRedirect +from django.shortcuts import render_to_response, redirect +from django.template import Context, loader, RequestContext +from django.template.loader import render_to_string +from django.utils.hashcompat import md5_constructor +from django.utils.translation import ugettext as _ +from django.views.generic.base import TemplateView, TemplateResponseMixin +from django.views.generic.edit import BaseFormView, FormMixin + +from auth.decorators import login_required + +from seahub.utils import search_file_by_name + +@login_required +def search(request): + keyword = request.GET['keyword'] + current_page = int(request.GET.get('page', '1')) + per_page= int(request.GET.get('per_page', '25')) + + start = (current_page - 1) * per_page + size = per_page + results, total = search_file_by_name(request, keyword, start, size) + + if total > current_page * per_page: + has_more = True + else: + has_more = False + + return render_to_response('search_results.html', { + 'keyword': keyword, + 'results': results, + 'total': total, + 'has_more': has_more, + 'current_page': current_page, + 'prev_page': current_page - 1, + 'next_page': current_page + 1, + 'per_page': per_page, + }, context_instance=RequestContext(request)) \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 546583ff24..6138733c71 100644 --- a/templates/base.html +++ b/templates/base.html @@ -86,6 +86,12 @@ Seafile logo {% block nav %}{% endblock %} + + {% if request.user.is_authenticated %} +
+ +
+ {% endif %} @@ -162,16 +168,16 @@ $('#info-bar .close').click(function() { info_id_exist = false, new_info_id = $(this).attr('data') + '_'; date.setTime(date.getTime() + 14*24*60*60*1000); - new_info_id += '; expires=' + date.toGMTString() + '; path=' + '{{ SITE_ROOT }}'; + new_info_id += '; expires=' + date.toGMTString() + '; path=' + '{{ SITE_ROOT }}'; for (var i = 0, len = cookies.length; i < len; i++) { if (cookies[i].split('=')[0] == 'info_id') { info_id_exist = true; - document.cookie = 'info_id=' + cookies[i].split('=')[1] + new_info_id; + document.cookie = 'info_id=' + cookies[i].split('=')[1] + new_info_id; break; } } if (!info_id_exist) { - document.cookie = 'info_id=' + new_info_id; + document.cookie = 'info_id=' + new_info_id; } } }); @@ -197,6 +203,7 @@ $(document).click(function(e) { $('#account-context').attr('data', 'no-popup'); } }); + {% endif %} {% endif %} diff --git a/templates/search_results.html b/templates/search_results.html new file mode 100644 index 0000000000..55c8b97a9d --- /dev/null +++ b/templates/search_results.html @@ -0,0 +1,71 @@ +{% extends base_template %} + +{% load seahub_tags avatar_tags i18n %} +{% load url from future %} + +{% block right_panel %} +

+ {% trans 'Search results for' %} "{{ keyword }}" +

+{% if not results %} +

+ {% trans 'No search result found' %} +

+{% else %} +

{% trans 'Found' %} {{ total }} {% trans 'results' %}

+ + + + + + + + {% for file in results %} + + + + + + + {% endfor %} +
{% trans 'Name' %}{% trans 'Library' %}{% trans 'Owner' %}
+ {% trans + + {{ file.name }} + ({{ file.score }}) + + {{ file.repo.name }} + + {% avatar file.repo.owner 20 %} + {{ file.repo.owner|email2nickname }} +
+
+ {% if current_page != 1 %} + {% trans "Previous"%} + {% endif %} + {% if has_more %} + {% trans "Next"%} + {% endif %} +{% if current_page != 1 or has_more %} +| +{% endif %} + {% trans "Per page: "%} + {% if per_page == 25 %} + 25 + {% else %} + 25 + {% endif %} + {% if per_page == 50 %} + 50 + {% else %} + 50 + {% endif %} + {% if per_page == 100 %} + 100 + {% else %} + 100 + {% endif %} +
+ +{% endif %} +{% endblock %} diff --git a/urls.py b/urls.py index b240dc60c6..b8b2b47c97 100644 --- a/urls.py +++ b/urls.py @@ -5,6 +5,7 @@ from django.views.generic.simple import direct_to_template from seahub.views import * from seahub.views.file import view_file, view_history_file, view_trash_file,\ view_snapshot_file +from seahub.search import search from notifications.views import notification_list from group.views import group_list @@ -101,6 +102,8 @@ urlpatterns = patterns('', url(r'^sys/useradmin/$', sys_useradmin, name='sys_useradmin'), url(r'^sys/orgadmin/$', sys_org_admin, name='sys_org_admin'), url(r'^sys/groupadmin/$', sys_group_admin, name='sys_group_admin'), + + url(r'^search/$', search, name='search'), ) if settings.SERVE_STATIC: @@ -125,4 +128,3 @@ else: url(r'^pubinfo/groups/$', pubgrp, name='pubgrp'), url(r'^pubinfo/users/$', pubuser, name='pubuser'), ) - diff --git a/utils/__init__.py b/utils/__init__.py index b6784028dc..866a1af90d 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -770,4 +770,48 @@ def is_textual_file(file_type): else: return False - + +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) + + # 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 + + pubrepo_id_map = {} + 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 + 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] + + files_found, total = es_search(conn, nonpub_repo_ids, keyword, start, size) + + 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 + + repo_id_map.update(pubrepo_id_map) + + for f in files_found: + f['repo'] = repo_id_map[f['repo_id'].encode('UTF-8')] + + return files_found, total