diff --git a/.bootstraprc b/.bootstraprc index c3cf2650279e29d294291b6cb860a8e75d49474a..924360a9818097b167d91384b0effefbc2d47a14 100644 --- a/.bootstraprc +++ b/.bootstraprc @@ -14,6 +14,7 @@ # First tables, due to self dependencies "tables": true, "alert": true, + "badge": true, "button-group": true, "buttons": true, "breadcrumb": true, @@ -40,6 +41,7 @@ "alert": true, "collapse": true, "modal": true, + "scrollspy": true, "tab": true, "tooltip": true, "util": true, diff --git a/SciPost_v1/settings/base.py b/SciPost_v1/settings/base.py index 3abb27c0d8eaba1c8e5ac09122a2a0e7d62aab09..70d622cb4954383f85fdbc07915b83ad3770e5c8 100644 --- a/SciPost_v1/settings/base.py +++ b/SciPost_v1/settings/base.py @@ -98,13 +98,18 @@ INSTALLED_APPS = ( 'webpack_loader', ) + HAYSTACK_CONNECTIONS = { 'default': { 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', 'PATH': 'local_files/haystack/', }, + 'base_search': { + 'ENGINE': 'haystack.backends.simple_backend.SimpleEngine', + } } + SPHINXDOC_BASE_TEMPLATE = 'scipost/base.html' SPHINXDOC_PROTECTED_PROJECTS = { 'scipost': ['scipost.can_view_docs_scipost'], diff --git a/commentaries/search_indexes.py b/commentaries/search_indexes.py new file mode 100644 index 0000000000000000000000000000000000000000..603ee07d4031536bf6948c0d9a17ee41b6b8f529 --- /dev/null +++ b/commentaries/search_indexes.py @@ -0,0 +1,19 @@ +# import datetime + +from haystack import indexes + +from .models import Commentary + + +class CommentaryIndex(indexes.SearchIndex, indexes.Indexable): + text = indexes.CharField(document=True, model_attr='title', use_template=True) + authors = indexes.CharField(model_attr='author_list') + date = indexes.DateTimeField(model_attr='pub_date') + abstract = indexes.CharField(model_attr='pub_abstract') + + def get_model(self): + return Commentary + + def index_queryset(self, using=None): + """Used when the entire index for model is updated.""" + return self.get_model().objects.vetted() diff --git a/commentaries/templates/partials/commentaries/search_card.html b/commentaries/templates/partials/commentaries/search_card.html new file mode 100644 index 0000000000000000000000000000000000000000..70d7531590e671d4798380a62c83a5edfb17d86b --- /dev/null +++ b/commentaries/templates/partials/commentaries/search_card.html @@ -0,0 +1 @@ +{% extends 'commentaries/_commentary_card_content.html' %} diff --git a/comments/search_indexes.py b/comments/search_indexes.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccccda8f8bf33b3eb77de14c759c08ef86b33ef --- /dev/null +++ b/comments/search_indexes.py @@ -0,0 +1,17 @@ +# import datetime + +from haystack import indexes + +from .models import Comment + + +class CommentIndex(indexes.SearchIndex, indexes.Indexable): + text = indexes.CharField(document=True, model_attr='comment_text', use_template=True) + date = indexes.DateTimeField(model_attr='date_submitted') + + def get_model(self): + return Comment + + def index_queryset(self, using=None): + """Used when the entire index for model is updated.""" + return self.get_model().objects.vetted() diff --git a/comments/templates/partials/comments/search_card.html b/comments/templates/partials/comments/search_card.html new file mode 100644 index 0000000000000000000000000000000000000000..ccda3bf319b2060a8847f407370d0afddcb6351f --- /dev/null +++ b/comments/templates/partials/comments/search_card.html @@ -0,0 +1 @@ +{% extends 'comments/_comment_card_content.html' %} diff --git a/common/forms.py b/common/forms.py new file mode 100644 index 0000000000000000000000000000000000000000..b4f7d0c5b629af00de549c04b029117e7edc2ba4 --- /dev/null +++ b/common/forms.py @@ -0,0 +1,93 @@ +import datetime +import re + +from django.forms.widgets import Widget, Select +from django.utils.dates import MONTHS +from django.utils.safestring import mark_safe + +__all__ = ('MonthYearWidget',) + +RE_DATE = re.compile(r'(\d{4})-(\d\d?)-(\d\d?)$') + + +class MonthYearWidget(Widget): + """ + A Widget that splits date input into two <select> boxes for month and year, + with 'day' defaulting to the first of the month. + + Based on SelectDateWidget, in + + django/trunk/django/forms/extras/widgets.py + + + """ + none_value = (0, '---') + month_field = '%s_month' + year_field = '%s_year' + + def __init__(self, attrs=None, years=True, months=True, required=False): + self.attrs = attrs or {} + self.required = required + self.today = datetime.date.today() + if years: + this_year = self.today.year + self.year_choices = [(i, i) for i in range(this_year - 4, this_year + 1)] + if not self.required: + self.year_choices.insert(0, self.none_value) + if months: + self.month_choices = dict(MONTHS.items()) + if not self.required: + self.month_choices[self.none_value[0]] = self.none_value[1] + self.month_choices = sorted(self.month_choices.items()) + + def render(self, name, value, attrs=None): + try: + year_val, month_val = value.year, value.month + except AttributeError: + year_val = month_val = None + if isinstance(value, (str, bytes)): + match = RE_DATE.match(value) + if match: + year_val, month_val, day_val = [int(v) for v in match.groups()] + + output = [] + + if 'id' in self.attrs: + id_ = self.attrs['id'] + else: + id_ = 'id_%s' % name + + if hasattr(self, 'month_choices'): + local_attrs = self.build_attrs(id=self.month_field % id_) + s = Select(choices=self.month_choices, attrs={'class': 'form-control'}) + select_html = s.render(self.month_field % name, month_val, local_attrs) + output.append(select_html) + + if hasattr(self, 'year_choices'): + local_attrs = self.build_attrs(id=self.year_field % id_) + s = Select(choices=self.year_choices, attrs={'class': 'form-control'}) + select_html = s.render(self.year_field % name, year_val, local_attrs) + output.append(select_html) + + return mark_safe(u'\n'.join(output)) + + @classmethod + def id_for_label(self, id_): + return '%s_month' % id_ + + def value_from_datadict(self, data, files, name): + if hasattr(self, 'year_choices'): + y = data.get(self.year_field % name) + else: + y = self.today.year + + if hasattr(self, 'month_choices'): + m = data.get(self.month_field % name) + else: + m = self.today.month + + if y == "0" or m == "0": + return None + if y and m: + return '%s-%s-%s' % (y, m, 1) + return data.get(name, None) diff --git a/journals/search_indexes.py b/journals/search_indexes.py new file mode 100644 index 0000000000000000000000000000000000000000..df45d5f971e0ebf0361753687d0253983479dcb2 --- /dev/null +++ b/journals/search_indexes.py @@ -0,0 +1,20 @@ +# import datetime + +from haystack import indexes + +from .models import Publication + + +class PublicationIndex(indexes.SearchIndex, indexes.Indexable): + text = indexes.CharField(document=True, model_attr='title', use_template=True) + authors = indexes.CharField(model_attr='author_list') + date = indexes.DateTimeField(model_attr='publication_date') + abstract = indexes.CharField() + doi_label = indexes.CharField() + + def get_model(self): + return Publication + + def index_queryset(self, using=None): + """Used when the entire index for model is updated.""" + return self.get_model().objects.published() diff --git a/journals/templates/partials/journals/search_card.html b/journals/templates/partials/journals/search_card.html new file mode 100644 index 0000000000000000000000000000000000000000..ff17dfb329eb875d2b3ec2b01965688d2305f34d --- /dev/null +++ b/journals/templates/partials/journals/search_card.html @@ -0,0 +1 @@ +{% extends 'journals/_publication_card_content.html' %} diff --git a/scipost/forms.py b/scipost/forms.py index ef56c9cf1c29ff7832d30553c6bb9139206f18a8..8b79ae454317970e1a17bb7ea866e02551c73cbd 100644 --- a/scipost/forms.py +++ b/scipost/forms.py @@ -1,3 +1,5 @@ +import datetime + from django import forms from django.contrib.auth import authenticate from django.contrib.auth.models import User, Group @@ -5,6 +7,7 @@ from django.contrib.auth.password_validation import validate_password from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse_lazy from django.utils import timezone +from django.utils.dates import MONTHS from django.utils.http import is_safe_url from django_countries import countries @@ -13,12 +16,18 @@ from django_countries.fields import LazyTypedChoiceField from captcha.fields import ReCaptchaField from ajax_select.fields import AutoCompleteSelectField +from haystack.forms import ModelSearchForm as HayStackSearchForm from .constants import SCIPOST_DISCIPLINES, TITLE_CHOICES, SCIPOST_FROM_ADDRESSES from .decorators import has_contributor from .models import Contributor, DraftInvitation, RegistrationInvitation,\ UnavailabilityPeriod, PrecookedEmail +from commentaries.models import Commentary +from comments.models import Comment +from submissions.models import Submission +from theses.models import ThesisLink +from common.forms import MonthYearWidget from partners.decorators import has_contact from journals.models import Publication # from mailing_lists.models import MailchimpList, MailchimpSubscription @@ -425,6 +434,31 @@ class SearchForm(forms.Form): q = forms.CharField(max_length=100) +def get_date_filter_choices(): + today = datetime.date.today() + empty = [(0, '---')] + months = empty + list(MONTHS.items()) + years = empty + [(i, i) for i in range(today.year - 4, today.year + 1)] + return months, years + + +class Search2Form(HayStackSearchForm): + # The date filters doesn't function well... + # start_1 = forms.DateField(widget=MonthYearWidget(years=False), required=False) # Month + # start_2 = forms.DateField(widget=MonthYearWidget(months=False), required=False) # Year + # end_1 = forms.DateField(widget=MonthYearWidget(years=False), required=False) # Month + # end_2 = forms.DateField(widget=MonthYearWidget(months=False), required=False) # Years + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + models = self.fields['models'].choices + models = filter(lambda x: x[0] != 'sphinxdoc.document', models) + self.fields['models'].choices = models + + def search(self): + return super().search().using('base_search') + + class EmailGroupMembersForm(forms.Form): group = forms.ModelChoiceField(queryset=Group.objects.all()) email_subject = forms.CharField(widget=forms.Textarea(), label='') diff --git a/scipost/static/scipost/SciPost.css b/scipost/static/scipost/SciPost.css index b4248ef268dc319dae35282531fdc524dc3133d1..59a65ec9ace010ea561c03571b2d1dc1cf80645e 100644 --- a/scipost/static/scipost/SciPost.css +++ b/scipost/static/scipost/SciPost.css @@ -4,39 +4,6 @@ General style sheet for SciPost @import url(//fonts.googleapis.com/css?family=Merriweather+Sans); -* { - box-sizing: border-box; -} - -.row:after { - content: ""; - clear: both; - display: block; -} - -[class*="col-"] { - /*float: left; - padding: 5px;*/ -} -/* For desktop: */ -.col-1 {width: 8.33%;} -.col-2 {width: 16.66%;} -.col-3 {width: 25%;} -.col-4 {width: 33.33%;} -.col-5 {width: 41.66%;} -.col-6 {width: 50%;} -.col-7 {width: 58.33%;} -.col-8 {width: 66.66%;} -.col-9 {width: 75%;} -.col-10 {width: 83.33%;} -.col-11 {width: 91.66%;} -.col-12 {width: 100%;} -@media only screen and (max-width: 768px) { - /* For mobile phones: */ - [class*="col-"] { - width: 100%; - } -} hr.hr6 { height: 6px; @@ -307,16 +274,6 @@ ul.personalTabMenu li a.inactive { padding: 0px 4px; } -body { -/* font-family: Merriweather, sans-serif; */ - font-family: 'Merriweather Sans'; - font-weight: lighter; - font-size: 0.8rem; - margin: 0px; - padding: 0px; - background-color: #ffffff; -} - body section div.sectionbox { width: 300px; background-color: #f4f4f4; @@ -326,16 +283,6 @@ body section div.sectionbox { vertical-align: top; } -a { - color: #eeeeee; -/* text-decoration: underline; */ - target: _parent; -} -a:hover { - color: #ffffff; - text-decoration: underline; -} - .submitButton { background-color: #002B49; color: #ffffff; @@ -372,19 +319,6 @@ header p { margin: 25px; } -nav { - background-color: #f0f0f0; - /*color: #002B49; - margin:0px; - padding:0px; - float:center; - clear:both;*/ - /*font-size: 12px;*/ - /*font-weight: 500;*/ -} - - - ol { list-style-type: none; counter-reset: item; diff --git a/scipost/static/scipost/assets/config/preconfig.scss b/scipost/static/scipost/assets/config/preconfig.scss index a67c0d9ceadf3b07734d0e93cd64ee84064e6654..b0e0d444387739b04fc179363c0dd821e376019c 100644 --- a/scipost/static/scipost/assets/config/preconfig.scss +++ b/scipost/static/scipost/assets/config/preconfig.scss @@ -27,6 +27,7 @@ $container-max-widths: ( // $scipost-light: #C3D7EE; $scipost-lightblue: #6885c3; +$scipost-lightestblue: #d3e3f6; $scipost-darkblue: #002b49; $scipost-orange: #f6a11a; $scipost-white: #f9f9f9; @@ -38,7 +39,7 @@ $body-color: $scipost-darkblue; $white: #fff; $blue: $scipost-lightblue; // Primary $green: #6ebb6e; -$cyan: $scipost-lightblue; +$cyan: $scipost-lightestblue; $yellow: $scipost-orange; $gray-600: #ccc; $text-muted: #636c72; @@ -71,12 +72,16 @@ $breadcrumb-divider-color: $scipost-orange; // Forms // -$input-btn-padding-x: .5rem; -$input-btn-padding-y: .25rem; -$input-btn-padding-y-sm: .15rem; +$input-btn-padding-y-sm: 0.1rem; +$input-btn-padding-x: 0.5rem; +$input-btn-padding-y: 0.25rem; +$input-btn-padding-y-lg: 0.25rem; +$input-btn-padding-x-lg: 0.75rem; +$input-btn-line-height-lg: 1.4; $input-border-radius-sm: $base-border-radius; $input-border-radius: $base-border-radius; $input-border-radius-lg: $base-border-radius; +$enable-rounded: true !important; $btn-transition: none; $input-height: calc(1.5rem + 2px); diff --git a/scipost/static/scipost/assets/css/_buttons.scss b/scipost/static/scipost/assets/css/_buttons.scss index aa1038e1dec480fc51e8b4dc2388f272a6157730..b499a56eed8565f667bb083f58ff642e775aca51 100644 --- a/scipost/static/scipost/assets/css/_buttons.scss +++ b/scipost/static/scipost/assets/css/_buttons.scss @@ -31,6 +31,10 @@ } } +.btn-info { + color: $scipost-darkblue; +} + .category-group, .voting-group { border-radius: $card-border-radius; diff --git a/scipost/static/scipost/assets/css/_form.scss b/scipost/static/scipost/assets/css/_form.scss index 7ed22071c9a78ef80474c04b7e6ef3a21784a5a6..25694c4652bcedaceed0f28c56a540a518ed0b14 100644 --- a/scipost/static/scipost/assets/css/_form.scss +++ b/scipost/static/scipost/assets/css/_form.scss @@ -4,6 +4,7 @@ */ .form-control { font-family: inherit; + border-radius: $base-border-radius; } .has-error .form-control { @@ -53,6 +54,8 @@ input[type="file"] { } .search-nav-form { + min-width: 260px; + input { margin-right: 0 !important; outline: 0; @@ -61,12 +64,14 @@ input[type="file"] { input[name="q"] { border-right: 0; - min-width: 200px; + width: 200px; border-radius: $base-border-radius 0 0 $base-border-radius; } input[type="submit"] { background: #f4f4f4; + width: 60px; + text-align: center; border-radius: 0 $base-border-radius $base-border-radius 0; &:hover { @@ -75,6 +80,24 @@ input[type="file"] { } } +.form-group.checkboxes { + [type="checkbox"] { + display: none; + } + + [type="checkbox"]:checked + .btn-info { + color: $white; + background-color: $scipost-lightblue; + border-color: $scipost-lightblue; + + } + + [type="checkbox"]:checked + .btn-primary { + background-color: $scipost-darkblue; + border-color: $scipost-darkblue; + } +} + // Formset // .delete-form-group { diff --git a/scipost/static/scipost/assets/css/_grid.scss b/scipost/static/scipost/assets/css/_grid.scss index e7ef90aaf393c8ec59f20056ba1c6ec87fa6fe0a..7b5c41d378921df7532013bb2938e9b4ce3d96eb 100644 --- a/scipost/static/scipost/assets/css/_grid.scss +++ b/scipost/static/scipost/assets/css/_grid.scss @@ -22,6 +22,12 @@ img { display: none; } +// footer.footer { +// position: fixed; +// width: 100%; +// bottom: 0; +// } + footer.secondary { color: $scipost-darkblue; background: transparent; diff --git a/scipost/static/scipost/assets/css/_nav.scss b/scipost/static/scipost/assets/css/_nav.scss index 4ff137786c65dd73d06efbb4dea0415f80e395cb..b904d97c8a7021ce05661e2d6b7be56f9ffdd3b9 100644 --- a/scipost/static/scipost/assets/css/_nav.scss +++ b/scipost/static/scipost/assets/css/_nav.scss @@ -1,3 +1,8 @@ +.nav, +nav { + background-color: #f0f0f0; +} + .nav.btn-group .nav-item { padding: 0 !important; } diff --git a/scipost/static/scipost/assets/js/scripts.js b/scipost/static/scipost/assets/js/scripts.js index 6dfe09de3b42b5601b25d5bc9dba53bae26f456f..0c8bf6045322d54e9beb70dd1c83fe138d8b5153 100644 --- a/scipost/static/scipost/assets/js/scripts.js +++ b/scipost/static/scipost/assets/js/scripts.js @@ -39,4 +39,8 @@ $(function(){ var tab_name = e.target.hash.substring(1) window.history.replaceState({}, null, '?tab=' + tab_name); }); + + // $('footer').each(function () { + var $spy = $('body').scrollspy({ target: '.footer' }) + // }) }); diff --git a/scipost/templates/scipost/footer.html b/scipost/templates/scipost/footer.html index a69ef2bf8bd2ab871184b3708c9a2ca4e0620da1..f792aebbd718746f0af4c4081b2c4e0cb3a68c1b 100644 --- a/scipost/templates/scipost/footer.html +++ b/scipost/templates/scipost/footer.html @@ -1,26 +1,28 @@ {% load staticfiles %} -<footer class="container-fluid py-2"> - <div class="row mt-2"> +<footer class="footer"> + <div class="container-fluid py-1"> + <div class="row my-3"> - <div class="col-md-4"> - Copyright © <a href="{% url 'scipost:foundation' %}" target="_">SciPost Foundation</a>. - <br/> - Contact the <a href="mailto:admin@scipost.org">administrators</a> or <a href="mailto:techsupport@scipost.org">tech support</a> - <br/> - <a href="{% url 'scipost:terms_and_conditions' %}">Terms and conditions.</a> - </div> - <div class="col-md-3"> - Follow us:<br/> - <table> - <tr> - <td><a href="//www.facebook.com/scipost" target="_blank" title="Facebook"><img src="{% static 'scipost/images/FB-f-Logo__white_29.png' %}" width="20" alt="Facebook"/></a></td> - <td><a href="//twitter.com/scipost_dot_org" target="_blank" title="Twitter"><img src="{% static 'scipost/images/Twitter_Logo_Blue.png' %}" width="32" alt="Twitter"/></a></td> - <td><a style="float: right;" href="{% url 'scipost:feeds' %}"><img src="{% static 'scipost/images/feed-icon-28x28.png' %}" alt="Feed logo" width="20"></a></td> - </tr> - </table> - </div> - <div class="col-md-5"> - <a rel="license" href="//creativecommons.org/licenses/by/4.0/" target="_blank"><img alt="Creative Commons License" style="border-width:0" src="//i.creativecommons.org/l/by/4.0/80x15.png" /></a><br />Except where otherwise noted, all content on SciPost is licensed under a <a rel="license" href="//creativecommons.org/licenses/by/4.0/" target="_blank">Creative Commons Attribution 4.0 International License</a>. - </div> + <div class="col-md-4 mb-3 mb-md-0"> + Copyright © <a href="{% url 'scipost:foundation' %}" target="_">SciPost Foundation</a> + <br/> + Contact the <a href="mailto:admin@scipost.org">administrators</a> or <a href="mailto:techsupport@scipost.org">tech support</a> + <br/> + <a href="{% url 'scipost:terms_and_conditions' %}">Terms and conditions</a> + </div> + <div class="col-md-3 mb-3 mb-md-0"> + Follow us:<br/> + <table> + <tr> + <td><a href="//www.facebook.com/scipost" target="_blank" title="Facebook"><img src="{% static 'scipost/images/FB-f-Logo__white_29.png' %}" width="20" alt="Facebook"/></a></td> + <td><a href="//twitter.com/scipost_dot_org" target="_blank" title="Twitter"><img src="{% static 'scipost/images/Twitter_Logo_Blue.png' %}" width="32" alt="Twitter"/></a></td> + <td><a style="float: right;" href="{% url 'scipost:feeds' %}"><img src="{% static 'scipost/images/feed-icon-28x28.png' %}" alt="Feed logo" width="20"></a></td> + </tr> + </table> + </div> + <div class="col-md-5"> + <a rel="license" href="//creativecommons.org/licenses/by/4.0/" target="_blank"><img alt="Creative Commons License" style="border-width:0" src="//i.creativecommons.org/l/by/4.0/80x15.png" /></a><br />Except where otherwise noted, all content on SciPost is licensed under a <a rel="license" href="//creativecommons.org/licenses/by/4.0/" target="_blank">Creative Commons Attribution 4.0 International License</a>. + </div> + </div> </div> </footer> diff --git a/scipost/templates/scipost/header.html b/scipost/templates/scipost/header.html index 2f9ddf2da8de3355fb651ceb26017797df7015ca..c85dd99638939ffefad2995eac8fb76b81e747a0 100644 --- a/scipost/templates/scipost/header.html +++ b/scipost/templates/scipost/header.html @@ -5,7 +5,7 @@ <div class="flex-logobox"> <a href="{% url 'scipost:index' %}"><img src="{% static 'scipost/images/logo_scipost_RGB_HTML_groot.png' %}" alt="SciPost logo" width="240" /></a> </div> - <div class="flex-blurbbox hidden-sm-down"> + <div class="flex-blurbbox d-none d-md-block"> <p> <i>The complete scientific publication portal</i><br /> <i>Managed by professional scientists</i><br /> diff --git a/scipost/templates/scipost/navbar.html b/scipost/templates/scipost/navbar.html index f38d7da1d7a3e6c095107237e4274612bbf2e32e..ed33013d14f50075d450eba4b2c9916846f1a638 100644 --- a/scipost/templates/scipost/navbar.html +++ b/scipost/templates/scipost/navbar.html @@ -50,7 +50,7 @@ </ul> <form action="{% url 'scipost:search' %}" method="get" class="form-inline search-nav-form"> - <input class="form-control" id="id_q" maxlength="100" name="q" type="text" required="required" value="{{search_query}}"> + <input class="form-control" id="id_q" maxlength="100" name="q" type="text" required="required" value="{{search_query|default:''}}"> <input class="btn btn-secondary" type="submit" value="Search"> </form> </div> diff --git a/scipost/templates/scipost/search.html b/scipost/templates/scipost/search.html index dd9d6d630935dfd9ee83abfe95198de6019530f2..f80596b41adeda4aabb6b9599c47f67bee1eb7f5 100644 --- a/scipost/templates/scipost/search.html +++ b/scipost/templates/scipost/search.html @@ -6,6 +6,7 @@ <div class="row"> <div class="col-12"> + <h1>Search results: <span class="text-muted">{{ search_term|default:'' }}</span></h1> <h1>Search results{% if search_term %}: <small><i>{{ search_term }}</i></small>{% endif %}</h1> {% if not publication_search_list and not commentary_search_list and not submission_search_list and not thesislink_search_list and not comment_search_link and not comment_search_list %} <p>Your search query did not return any result.</p> diff --git a/scipost/templates/scipost/search2.html b/scipost/templates/scipost/search2.html new file mode 100644 index 0000000000000000000000000000000000000000..5453a7c2e737f6870d1067af4648bd4c94c211cb --- /dev/null +++ b/scipost/templates/scipost/search2.html @@ -0,0 +1,182 @@ +{% extends 'scipost/base.html' %} + +{% load bootstrap %} + +{% block pagetitle %}: list{% endblock pagetitle %} + +{% block content %} + + +<h1>Search results</h1> +<form method="get" class="my-2"> + <div class="form-group row"> + <div class="col-md-5 col-lg-3 mb-2 mb-md-0 pr-md-1"> + <input class="form-control" id="{{form.q.auto_id}}" maxlength="100" name="{{form.q.name}}" type="text" value="{{form.q.value}}" required="required"> + </div> + <div class="col-md-7 col-lg-9 pl-md-1"> + <input class="btn btn-primary" type="submit" value="Search"> + </div> + </div> +</form> + + +{% if not publication_search_list and not commentary_search_list and not submission_search_list and not thesislink_search_list and not comment_search_link and not comment_search_list %} + <p>Your search query did not return any result.</p> +{% endif %} + +{% if publication_search_list %} +<hr> +<div class="row"> + <div class="col-12"> + <h2 class="highlight">Publications ({{publication_search_list|length}})</h2> + </div> +</div> + +<div class="row"> + <div class="col-12"> + {% for publication in publication_search_list %} + <div class="card card-publication"> + {% include 'journals/_publication_card_content.html' with publication=publication %} + </div> + {% endfor %} + </div> +</div> + +<div class="row"> + <div class="col-12"> + <div class="pagination"> + <span class="step-links"> + {% if publication_search_list.has_previous %} + <a href="?publication_search_list_page={{ publication_search_list.previous_page_number }}">previous</a> + {% endif %} + <span class="current"> + Page {{ publication_search_list.number }} of {{ publication_search_list.paginator.num_pages }}. + </span> + {% if publication_search_list.has_next %} + <a href="?publication_search_list_page={{ publication_search_list.next_page_number }}">next</a> + {% endif %} + </span> + </div> + </div> +</div> +{% endif %} + + +{% if commentary_search_list %} +<hr> +<div class="row"> + <div class="col-12"> + <h2 class="highlight">Commentaries ({{commentary_search_list|length}})</h2> + </div> +</div> +<div class="row"> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for commentary in commentary_search_list %} + <li class="list-group-item"> + {% include 'commentaries/_commentary_card_content.html' with commentary=commentary %} + </li> + {% endfor %} + </ul> + </div> +</div> + +<div class="row"> + <div class="col-12"> + <div class="pagination"> + <span class="step-links"> + {% if commentary_search_list.has_previous %} + <a href="?commentary_search_list_page={{ commentary_search_list.previous_page_number }}">previous</a> + {% endif %} + <span class="current"> + Page {{ commentary_search_list.number }} of {{ commentary_search_list.paginator.num_pages }}. + </span> + {% if commentary_search_list.has_next %} + <a href="?commentary_search_list_page={{ commentary_search_list.next_page_number }}">next</a> + {% endif %} + </span> + </div> + </div> +</div> +{% endif %} + + +{% if submission_search_list %} +<hr> +<div class="row"> + <div class="col-12"> + <h2 class="highlight">Submissions ({{submission_search_list|length}})</h2> + </div> +</div> +<div class="row"> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for submission in submission_search_list %} + <li class="list-group-item"> + {% include 'submissions/_submission_card_content.html' with submission=submission %} + </li> + {% endfor %} + </ul> + </div> +</div> + +<div class="row"> + <div class="col-12"> + <div class="pagination"> + <span class="step-links"> + {% if submission_search_list.has_previous %} + <a href="?submission_search_list_page={{ submission_search_list.previous_page_number }}">previous</a> + {% endif %} + <span class="current"> + Page {{ submission_search_list.number }} of {{ submission_search_list.paginator.num_pages }} + </span> + {% if submission_search_list.has_next %} + <a href="?submission_search_list_page={{ submission_search_list.next_page_number }}">next</a> + {% endif %} + </span> + </div> + </div> +</div> +{% endif %} + +{% if thesislink_search_list %} +<hr> +<div class="row"> + <div class="col-12"> + <h2 class="highlight">Theses ({{thesislink_search_list|length}})</h2> + </div> +</div> +<div class="row"> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for thesislink in thesislink_search_list %} + <li class="list-group-item"> + {% include 'theses/_thesislink_card_content.html' with thesislink=thesislink %} + </li> + {% endfor %} + </ul> + </div> +</div> +{% endif %} + +{% if comment_search_list %} +<hr> +<div class="row"> + <div class="col-12"> + <h2 class="highlight">Comments ({{comment_search_list|length}})</h2> + </div> +</div> +<div class="row"> + <div class="col-12"> + <ul class="list-group list-group-flush"> + {% for comment in comment_search_list %} + <li class="list-group-item"> + {% include 'comments/_comment_card_extended_content.html' with comment=comment %} + </li> + {% endfor %} + </ul> + </div> +</div> +{% endif %} + +{% endblock content %} diff --git a/scipost/urls.py b/scipost/urls.py index 71d914230408fe0c8b94779cb33f014f3b98219a..709d8ddfeb5f8c8c1ad044f38dfc1ed1f08c0a9c 100644 --- a/scipost/urls.py +++ b/scipost/urls.py @@ -63,6 +63,8 @@ urlpatterns = [ # Search url(r'^search$', views.search, name='search'), + url(r'^search2', views.SearchView.as_view(), name='search2'), + url(r'^search3', include('haystack.urls')), ################ # Contributors: diff --git a/scipost/views.py b/scipost/views.py index 58aeea2e4f475c25d64d285fa6cac7b412856ac7..d55d4472621a5101da659a0fb4aec6b408b387ed 100644 --- a/scipost/views.py +++ b/scipost/views.py @@ -11,16 +11,15 @@ from django.core import mail from django.core.mail import EmailMessage, EmailMultiAlternatives from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.urlresolvers import reverse -from django.db.models import Q +from django.db.models import Q, Prefetch from django.shortcuts import redirect from django.template import Context, Template from django.views.decorators.http import require_POST from django.views.generic.list import ListView -from django.db.models import Prefetch - from guardian.decorators import permission_required from guardian.shortcuts import assign_perm, get_objects_for_user +from haystack.generic_views import SearchView from .constants import SCIPOST_SUBJECT_AREAS, subject_areas_raw_dict, SciPost_from_addresses_dict from .decorators import has_contributor @@ -31,7 +30,8 @@ from .forms import AuthenticationForm, DraftInvitationForm, UnavailabilityPeriod RegistrationForm, RegistrationInvitationForm, AuthorshipClaimForm,\ ModifyPersonalMessageForm, SearchForm, VetRegistrationForm, reg_ref_dict,\ UpdatePersonalDataForm, UpdateUserDataForm, PasswordChangeForm,\ - EmailGroupMembersForm, EmailParticularForm, SendPrecookedEmailForm + EmailGroupMembersForm, EmailParticularForm, SendPrecookedEmailForm,\ + Search2Form from .utils import Utils, EMAIL_FOOTER, SCIPOST_SUMMARY_FOOTER, SCIPOST_SUMMARY_FOOTER_HTML from commentaries.models import Commentary @@ -115,6 +115,21 @@ def documentsSearchResults(query): return context +class SearchView(SearchView): + template_name = 'search/search.html' + form_class = Search2Form + + def get_context_data(self, *args, **kwargs): + ctx = super().get_context_data(*args, **kwargs) + # Add context variable to fill navbar form + ctx['suggestion'] = kwargs['object_list'].spelling_suggestion() + ctx['search_query'] = self.request.GET.get('q') + ctx['stats_results'] = kwargs['object_list'].stats_results() + ctx['results_count'] = kwargs['object_list'].count() + # raise + return ctx + + def search(request): """ For the global search form in navbar """ form = SearchForm(request.GET or None) diff --git a/submissions/search_indexes.py b/submissions/search_indexes.py new file mode 100644 index 0000000000000000000000000000000000000000..8e602429e186a8cf4f286fb90e310effbfc9f7f0 --- /dev/null +++ b/submissions/search_indexes.py @@ -0,0 +1,19 @@ +# import datetime + +from haystack import indexes + +from .models import Submission + + +class SubmissionIndex(indexes.SearchIndex, indexes.Indexable): + text = indexes.CharField(document=True, model_attr='title', use_template=True) + authors = indexes.CharField(model_attr='authors') + date = indexes.DateTimeField(model_attr='submission_date') + abstract = indexes.CharField(model_attr='abstract') + + def get_model(self): + return Submission + + def index_queryset(self, using=None): + """Used when the entire index for model is updated.""" + return self.get_model().objects.public() diff --git a/submissions/templates/partials/submissions/search_card.html b/submissions/templates/partials/submissions/search_card.html new file mode 100644 index 0000000000000000000000000000000000000000..537ed070cd91eafa4186ada284e3c42247bf5b91 --- /dev/null +++ b/submissions/templates/partials/submissions/search_card.html @@ -0,0 +1 @@ +{% extends 'submissions/_submission_card_content.html' %} diff --git a/templates/search/search.html b/templates/search/search.html new file mode 100644 index 0000000000000000000000000000000000000000..ade8ef6ae0ad3cba24fd933207fd1118c9fa2751 --- /dev/null +++ b/templates/search/search.html @@ -0,0 +1,108 @@ +{% extends 'scipost/base.html' %} + +{% load bootstrap %} +{# {% load highlight %}#} + +{% block pagetitle %}: list{% endblock pagetitle %} + +{% block content %} + + +<div class="row"> + <div class="col-md-4"> + <h1>Filter</h1> + <form method="get"> + <div class="form-group"> + <label for="{{form.q.auto_id}}">{{form.q.label}}</label> + <input type="text" name="{{form.q.name}}" class="form-control form-control-lg" id="{{form.q.auto_id}}" aria-describedby="search_help" placeholder="Search term" value="{{form.q.value}}" required="required"> + <small id="search_help" class="form-text text-muted">{{form.q.help_text}}</small> + </div> + + <div class="form-group"> + <label>Type</label> + </div> + <div class="form-group checkboxes"> + {% for field in form.models %} + <input type="checkbox" name="{{field.name}}" id="{{field.id_for_label}}" value="{{field.choice_value|stringformat:'s'}}" {% if field.choice_value in form.models.value %}checked="checked"{% endif %}> + <label class="btn btn-info" for="{{field.id_for_label}}"> + {{field.choice_label}} + </label> + + {% endfor %} + </div> + + {% comment %} + {# DISABLED DATE SEARCH: DOES NOT WORK OK #} + <label>Date from</label> + <div class="form-row"> + <div class="form-group col-md-6"> + {{form.start_1}} + </div> + <div class="form-group col-md-6"> + {{form.start_2}} + </div> + </div> + + <label>Date until</label> + <div class="form-row"> + <div class="form-group col-md-6"> + {{form.end_1}} + </div> + <div class="form-group col-md-6"> + {{form.end_2}} + </div> + </div> + {% endcomment %} + + <div class="form-group"> + {# <input type="reset" class="btn btn-danger" value="Reset">#} + <input type="submit" class="btn btn-primary" value="Search"> + </div> + </form> + </div> + <div class="col-md-8"> + + {% if query %} + <h1 class="mb-3">Search results</h1> + <ul class="list-group list-group-flush"> + {% for result in object_list %} + <li class="list-group-item{% if result.content_type == 'journals.publication' %} border-0{% endif %}"> + {% if result.content_type == 'submissions.submission' %} + {% include 'partials/submissions/search_card.html' with submission=result.object %} + {% elif result.content_type == 'commentaries.commentary' %} + {% include 'partials/commentaries/search_card.html' with commentary=result.object %} + {% elif result.content_type == 'theses.thesislink' %} + {% include 'partials/theses/search_card.html' with thesislink=result.object %} + {% elif result.content_type == 'comments.comment' %} + <div class="py-2"> + {% include 'partials/comments/search_card.html' with comment=result.object %} + </div> + {% elif result.content_type == 'journals.publication' %} + <div class="card card-publication"> + {% include 'partials/journals/search_card.html' with publication=result.object %} + </div> + {% else %} + <a href="{{ result.object.get_absolute_url }}">{{ result.object.title }}</a> + {% endif %} + </li> + {% empty %} + <p>Your search query did not return any result.</p> + {% endfor %} + + {% if is_paginated %} + <p> + {% if page_obj.has_previous %} + <a href="?q={{ query }}&page={{ page_obj.previous_page_number }}">Previous</a> + {% endif %} + Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}. + {% if page_obj.has_next %} + <a href="?q={{ query }}&page={{ page_obj.next_page_number }}">Next</a> + {% endif %} + </p> + {% endif %} + </ul> + {% endif %} + </div> +</div> + +{% endblock content %} diff --git a/theses/search_indexes.py b/theses/search_indexes.py new file mode 100644 index 0000000000000000000000000000000000000000..b75bb610a75b91667a6e5d765e0f4543b9483e1d --- /dev/null +++ b/theses/search_indexes.py @@ -0,0 +1,20 @@ +# import datetime + +from haystack import indexes + +from .models import ThesisLink + + +class ThesisIndex(indexes.SearchIndex, indexes.Indexable): + text = indexes.CharField(document=True, model_attr='title', use_template=True) + authors = indexes.CharField(model_attr='author_list') + supervisor = indexes.CharField() + date = indexes.DateTimeField(model_attr='defense_date') + abstract = indexes.CharField(model_attr='abstract') + + def get_model(self): + return ThesisLink + + def index_queryset(self, using=None): + """Used when the entire index for model is updated.""" + return self.get_model().objects.vetted() diff --git a/theses/templates/partials/theses/search_card.html b/theses/templates/partials/theses/search_card.html new file mode 100644 index 0000000000000000000000000000000000000000..dc78efd39c67be7c11c563e13ef3ac586ce0d609 --- /dev/null +++ b/theses/templates/partials/theses/search_card.html @@ -0,0 +1 @@ +{% extends 'theses/_thesislink_card_content.html' %}