Merge branch 'master' into schedule-admin
Conflicts: symposion/schedule/admin.py
This commit is contained in:
commit
8a95b0861c
18 changed files with 1285 additions and 432 deletions
|
@ -1,3 +1,4 @@
|
|||
include README LICENSE
|
||||
recursive-include symposion/templates *.html *.txt
|
||||
recursive-include symposion/static *
|
||||
recursive-include symposion/locale *
|
||||
|
|
35
README.rst
35
README.rst
|
@ -1,6 +1,9 @@
|
|||
Symposion
|
||||
---------
|
||||
|
||||
.. image:: http://slack.pinaxproject.com/badge.svg
|
||||
:target: http://slack.pinaxproject.com/
|
||||
|
||||
.. image:: https://img.shields.io/travis/pinax/symposion.svg
|
||||
:target: https://travis-ci.org/pinax/symposion
|
||||
|
||||
|
@ -17,12 +20,18 @@ Symposion
|
|||
:target: https://pypi.python.org/pypi/symposion/
|
||||
|
||||
|
||||
Pinax
|
||||
------
|
||||
|
||||
A conference management solution from Eldarion.
|
||||
Pinax is an open-source platform built on the Django Web Framework. It is an ecosystem of reusable Django apps, themes, and starter project templates.
|
||||
This collection can be found at http://pinaxproject.com.
|
||||
|
||||
Built with the generous support of the Python Software Foundation.
|
||||
|
||||
See http://eldarion.com/symposion/ for commercial support, customization and hosting
|
||||
symposion
|
||||
----------
|
||||
|
||||
symposion is a conference management solution from Eldarion. It was built with the generous support of the Python Software Foundation. See http://eldarion.com/symposion/ for commercial support, customization and hosting.
|
||||
|
||||
|
||||
Quickstart
|
||||
==========
|
||||
|
@ -36,3 +45,23 @@ customize and manage your Symposion installation. We have built a [basic
|
|||
Django startproject template that includes Symposion][1].
|
||||
|
||||
[1]: https://github.com/pinax/pinax-project-symposion
|
||||
|
||||
|
||||
Documentation
|
||||
---------------
|
||||
|
||||
The Pinax documentation is available at http://pinaxproject.com/pinax/.
|
||||
|
||||
|
||||
Code of Conduct
|
||||
----------------
|
||||
|
||||
In order to foster a kind, inclusive, and harassment-free community, the Pinax Project has a code of conduct, which can be found here http://pinaxproject.com/pinax/code_of_conduct/.
|
||||
|
||||
|
||||
Pinax Project Blog and Twitter
|
||||
-------------------------------
|
||||
|
||||
For updates and news regarding the Pinax Project, please follow us on Twitter at @pinaxproject and check out our blog http://blog.pinaxproject.com.
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,18 @@ from django.contrib import admin
|
|||
from symposion.conference.models import Conference, Section
|
||||
|
||||
|
||||
admin.site.register(Conference, list_display=("title", "start_date", "end_date"))
|
||||
class SectionInline(admin.TabularInline):
|
||||
model = Section
|
||||
prepopulated_fields = {"slug": ("name",)}
|
||||
extra = 1
|
||||
|
||||
|
||||
class ConferenceAdmin(admin.ModelAdmin):
|
||||
list_display = ("title", "start_date", "end_date")
|
||||
inlines = [SectionInline, ]
|
||||
|
||||
|
||||
admin.site.register(Conference, ConferenceAdmin)
|
||||
admin.site.register(
|
||||
Section,
|
||||
prepopulated_fields={"slug": ("name",)},
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
OrderedDict = None
|
||||
|
||||
from django import forms
|
||||
|
||||
import account.forms
|
||||
|
@ -11,8 +16,7 @@ class SignupForm(account.forms.SignupForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SignupForm, self).__init__(*args, **kwargs)
|
||||
del self.fields["username"]
|
||||
self.fields.keyOrder = [
|
||||
key_order = [
|
||||
"email",
|
||||
"email_confirm",
|
||||
"first_name",
|
||||
|
@ -20,6 +24,7 @@ class SignupForm(account.forms.SignupForm):
|
|||
"password",
|
||||
"password_confirm"
|
||||
]
|
||||
self.fields = reorder_fields(self.fields, key_order)
|
||||
|
||||
def clean_email_confirm(self):
|
||||
email = self.cleaned_data.get("email")
|
||||
|
@ -29,3 +34,24 @@ class SignupForm(account.forms.SignupForm):
|
|||
raise forms.ValidationError(
|
||||
"Email address must match previously typed email address")
|
||||
return email_confirm
|
||||
|
||||
|
||||
def reorder_fields(fields, order):
|
||||
"""Reorder form fields by order, removing items not in order.
|
||||
|
||||
>>> reorder_fields(
|
||||
... OrderedDict([('a', 1), ('b', 2), ('c', 3)]),
|
||||
... ['b', 'c', 'a'])
|
||||
OrderedDict([('b', 2), ('c', 3), ('a', 1)])
|
||||
"""
|
||||
for key, v in fields.items():
|
||||
if key not in order:
|
||||
del fields[key]
|
||||
|
||||
if not OrderedDict or hasattr(fields, "keyOrder"):
|
||||
# fields is SortedDict
|
||||
fields.keyOrder.sort(key=lambda k: order.index(k[0]))
|
||||
return fields
|
||||
else:
|
||||
# fields is OrderedDict
|
||||
return OrderedDict(sorted(fields.items(), key=lambda k: order.index(k[0])))
|
||||
|
|
BIN
symposion/locale/ja/LC_MESSAGES/django.mo
Normal file
BIN
symposion/locale/ja/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
573
symposion/locale/ja/LC_MESSAGES/django.po
Normal file
573
symposion/locale/ja/LC_MESSAGES/django.po
Normal file
|
@ -0,0 +1,573 @@
|
|||
# Symposion japanese translation.
|
||||
# Copyright (C) 2014-2015, pinax team
|
||||
# This file is distributed under the same license as the symposion package.
|
||||
# Hiroshi Miura <miurahr@linux.com>, 2015.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: symposion\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-07-31 14:47-0600\n"
|
||||
"PO-Revision-Date: 2015-06-18 10:04+0900\n"
|
||||
"Last-Translator: Hiroshi Miura <miurahr@linux.com>\n"
|
||||
"Language-Team: Japanese translation team <https://www.transifex.com/projects/p/symposion/language/ja/>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 1.5.4\n"
|
||||
"Language: ja\n"
|
||||
|
||||
#: cms/models.py:23
|
||||
msgid "Draft"
|
||||
msgstr "草稿"
|
||||
|
||||
#: cms/models.py:24
|
||||
msgid "Public"
|
||||
msgstr "公開"
|
||||
|
||||
#: cms/models.py:57
|
||||
msgid "Path can only contain letters, numbers and hyphens and end with /"
|
||||
msgstr ""
|
||||
"パス名には、英数字、ハイフン(-)のみが使えます。また末尾は'/'の必要がありま"
|
||||
"す。"
|
||||
|
||||
#: conference/models.py:15
|
||||
msgid "title"
|
||||
msgstr "タイトル"
|
||||
|
||||
#: conference/models.py:18 conference/models.py:58
|
||||
msgid "start date"
|
||||
msgstr "開始日"
|
||||
|
||||
#: conference/models.py:19 conference/models.py:59
|
||||
msgid "end date"
|
||||
msgstr "終了日"
|
||||
|
||||
#: conference/models.py:22
|
||||
msgid "timezone"
|
||||
msgstr "タイムゾーン"
|
||||
|
||||
#: conference/models.py:41 conference/models.py:52 sponsorship/models.py:18
|
||||
msgid "conference"
|
||||
msgstr "カンファレンス"
|
||||
|
||||
#: conference/models.py:42
|
||||
msgid "conferences"
|
||||
msgstr "カンファレンス"
|
||||
|
||||
#: conference/models.py:54 sponsorship/models.py:19 sponsorship/models.py:155
|
||||
msgid "name"
|
||||
msgstr "名前"
|
||||
|
||||
#: conference/models.py:65
|
||||
msgid "section"
|
||||
msgstr "セクション"
|
||||
|
||||
#: conference/models.py:66
|
||||
msgid "sections"
|
||||
msgstr "セクション"
|
||||
|
||||
#: proposals/models.py:71 templates/conference/user_list.html:60
|
||||
msgid "Name"
|
||||
msgstr "名前"
|
||||
|
||||
#: proposals/models.py:86
|
||||
msgid "Brief Description"
|
||||
msgstr "概要"
|
||||
|
||||
#: proposals/models.py:88
|
||||
msgid ""
|
||||
"If your proposal is accepted this will be made public and printed in the "
|
||||
"program. Should be one paragraph, maximum 400 characters."
|
||||
msgstr ""
|
||||
"もし提案が受諾されたら、これは公開され、配布されるプログラムに印刷されます。"
|
||||
"段落は一つのみで、400文字以内で記載してください。"
|
||||
|
||||
#: proposals/models.py:92
|
||||
msgid "Detailed Abstract"
|
||||
msgstr "提案の詳細"
|
||||
|
||||
#: proposals/models.py:93
|
||||
msgid ""
|
||||
"Detailed outline. Will be made public if your proposal is accepted. Edit "
|
||||
"using <a href='http://daringfireball.net/projects/markdown/basics' "
|
||||
"target='_blank'>Markdown</a>."
|
||||
msgstr ""
|
||||
"講演詳細:提案が受諾された場合に公開されます。<a href=\"http://www.markdown."
|
||||
"jp/what-is-markdown/\" target='_blank'>Markdown</a>を用いて記述してください。"
|
||||
|
||||
#: proposals/models.py:99
|
||||
msgid ""
|
||||
"Anything else you'd like the program committee to know when making their "
|
||||
"selection: your past experience, etc. This is not made public. Edit using <a "
|
||||
"href='http://daringfireball.net/projects/markdown/basics' "
|
||||
"target='_blank'>Markdown</a>."
|
||||
msgstr ""
|
||||
"プログラム委員に、講演者の過去の経験など、とくに伝えたいことがあれば、記述し"
|
||||
"てください。これは公開されることはありません。<a href=\"http://www.markdown."
|
||||
"jp/what-is-markdown/\" target='_blank'>Markdown</a>を用いて記述してください。"
|
||||
|
||||
#: proposals/models.py:153
|
||||
msgid "Pending"
|
||||
msgstr "ペンディング"
|
||||
|
||||
#: proposals/models.py:154 templates/proposals/_pending_proposal_row.html:16
|
||||
msgid "Accepted"
|
||||
msgstr "アクセプト"
|
||||
|
||||
#: proposals/models.py:155
|
||||
msgid "Declined"
|
||||
msgstr "リジェクト"
|
||||
|
||||
#: sponsorship/models.py:20
|
||||
msgid "order"
|
||||
msgstr "順序"
|
||||
|
||||
#: sponsorship/models.py:21
|
||||
msgid "cost"
|
||||
msgstr "経費"
|
||||
|
||||
#: sponsorship/models.py:22 sponsorship/models.py:156
|
||||
msgid "description"
|
||||
msgstr "記述"
|
||||
|
||||
#: sponsorship/models.py:22
|
||||
msgid "This is private."
|
||||
msgstr "これは非公開です。"
|
||||
|
||||
#: sponsorship/models.py:26
|
||||
msgid "sponsor level"
|
||||
msgstr "スポンサーのグレード"
|
||||
|
||||
#: sponsorship/models.py:27
|
||||
msgid "sponsor levels"
|
||||
msgstr "スポンサーのグレード"
|
||||
|
||||
#: sponsorship/models.py:38
|
||||
msgid "applicant"
|
||||
msgstr "応募者"
|
||||
|
||||
#: sponsorship/models.py:41
|
||||
msgid "Sponsor Name"
|
||||
msgstr "スポンサー名"
|
||||
|
||||
#: sponsorship/models.py:42
|
||||
msgid "external URL"
|
||||
msgstr "外部URL"
|
||||
|
||||
#: sponsorship/models.py:43
|
||||
msgid "annotation"
|
||||
msgstr ""
|
||||
|
||||
#: sponsorship/models.py:44
|
||||
msgid "Contact Name"
|
||||
msgstr "連絡先の名前"
|
||||
|
||||
#: sponsorship/models.py:45
|
||||
msgid "Contact Email"
|
||||
msgstr "連絡先のEmail"
|
||||
|
||||
#: sponsorship/models.py:46 sponsorship/models.py:167
|
||||
msgid "level"
|
||||
msgstr "グレード"
|
||||
|
||||
#: sponsorship/models.py:47
|
||||
msgid "added"
|
||||
msgstr ""
|
||||
|
||||
#: sponsorship/models.py:48
|
||||
msgid "active"
|
||||
msgstr "有効"
|
||||
|
||||
#: sponsorship/models.py:60 sponsorship/models.py:182
|
||||
msgid "sponsor"
|
||||
msgstr "スポンサー"
|
||||
|
||||
#: sponsorship/models.py:61
|
||||
msgid "sponsors"
|
||||
msgstr "スポンサー"
|
||||
|
||||
#: sponsorship/models.py:157
|
||||
msgid "type"
|
||||
msgstr "タイプ"
|
||||
|
||||
#: sponsorship/models.py:166 sponsorship/models.py:183
|
||||
msgid "benefit"
|
||||
msgstr ""
|
||||
|
||||
#: sponsorship/models.py:170 sponsorship/models.py:187
|
||||
msgid "max words"
|
||||
msgstr ""
|
||||
|
||||
#: sponsorship/models.py:171 sponsorship/models.py:188
|
||||
msgid "other limits"
|
||||
msgstr "他の制限"
|
||||
|
||||
#: sponsorship/models.py:192
|
||||
msgid "text"
|
||||
msgstr "テキスト"
|
||||
|
||||
#: sponsorship/models.py:193
|
||||
msgid "file"
|
||||
msgstr "ファイル"
|
||||
|
||||
#: templates/dashboard.html:16
|
||||
msgid "Speaking"
|
||||
msgstr "講演"
|
||||
|
||||
#: templates/dashboard.html:92 templates/sponsorship/detail.html:8
|
||||
msgid "Sponsorship"
|
||||
msgstr "スポンサー"
|
||||
|
||||
#: templates/dashboard.html:132 templates/reviews/review_detail.html:75
|
||||
msgid "Reviews"
|
||||
msgstr "レビュー"
|
||||
|
||||
#: templates/dashboard.html:177
|
||||
msgid "Teams"
|
||||
msgstr "チーム"
|
||||
|
||||
#: templates/boxes/box.html:9
|
||||
msgid "Editing content:"
|
||||
msgstr "コンテンツ編集:"
|
||||
|
||||
#: templates/cms/page_edit.html:11
|
||||
msgid "Edit page at:"
|
||||
msgstr ""
|
||||
|
||||
#: templates/conference/user_list.html:59
|
||||
msgid "Email"
|
||||
msgstr "電子メール"
|
||||
|
||||
#: templates/conference/user_list.html:61
|
||||
msgid "Speaker Profile?"
|
||||
msgstr "講演者のプロフィール"
|
||||
|
||||
#: templates/emails/teams_user_applied/message.html:3
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" <p>\n"
|
||||
" User \"%(username)s\" has applied to join <b>%(team_name)s</b> on "
|
||||
"%(site_name)s.\n"
|
||||
" </p>\n"
|
||||
"\n"
|
||||
" <p>\n"
|
||||
" To accept this application and see any other pending applications, "
|
||||
"visit the following url:\n"
|
||||
" <a href=\"http://%(site_url)s%(team_url)s\">http://%(site_url)s"
|
||||
"%(team_url)s</a>\n"
|
||||
" </p>\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
" <p>\n"
|
||||
" ユーザ \"%(username)s\" は、%(site_name)s の<b>%(team_name)s</b> に"
|
||||
"応募しました。 \n"
|
||||
" </p>\n"
|
||||
"\n"
|
||||
" <p>\n"
|
||||
" この応募を受諾したり、他の保留されている応募者を確認するには、つぎの"
|
||||
"URLを参照してください:\n"
|
||||
" <a href=\"http://%(site_url)s%(team_url)s\">http://%(site_url)s"
|
||||
"%(team_url)s</a>\n"
|
||||
" </p>\n"
|
||||
|
||||
#: templates/emails/teams_user_applied/subject.txt:1
|
||||
#, python-format
|
||||
msgid "%(username)s has applied to to join \"%(team)s\""
|
||||
msgstr "%(username)s は、 \"%(team)s\"に応募しました。"
|
||||
|
||||
#: templates/emails/teams_user_invited/message.html:3
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" <p>\n"
|
||||
" You have been invited to join <b>%(team_name)s</b> on "
|
||||
"%(site_name)s.\n"
|
||||
" </p>\n"
|
||||
"\n"
|
||||
" <p>\n"
|
||||
" To accept this invitation, visit the following url:\n"
|
||||
" <a href=\"http://%(site_url)s%(team_url)s\">http://%(site_url)s"
|
||||
"%(team_url)s</a>\n"
|
||||
" </p>\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
" <p>\n"
|
||||
" %(site_name)sの<b>%(team_name)s</b>に参加するよう招待されました。\n"
|
||||
" </p>\n"
|
||||
"\n"
|
||||
" <p>\n"
|
||||
" 招待を受諾するには、つぎのURLをクリックしてください:\n"
|
||||
" <a href=\"http://%(site_url)s%(team_url)s\">http://%(site_url)s"
|
||||
"%(team_url)s</a>\n"
|
||||
" </p>\n"
|
||||
|
||||
#: templates/emails/teams_user_invited/subject.txt:1
|
||||
#, python-format
|
||||
msgid "You have been invited to join \"%(team)s\""
|
||||
msgstr " \"%(team)s\"に参加するよう招待されました。"
|
||||
|
||||
#: templates/proposals/_pending_proposal_row.html:12
|
||||
msgid "Cancelled"
|
||||
msgstr "キャンセル済み"
|
||||
|
||||
#: templates/proposals/_pending_proposal_row.html:18
|
||||
msgid "Submitted"
|
||||
msgstr "投稿済み"
|
||||
|
||||
#: templates/proposals/_pending_proposal_row.html:21
|
||||
msgid "Invited"
|
||||
msgstr "招待された"
|
||||
|
||||
#: templates/proposals/_pending_proposal_row.html:30
|
||||
msgid "Choose Response"
|
||||
msgstr "応答の選択"
|
||||
|
||||
#: templates/proposals/_pending_proposal_row.html:35
|
||||
msgid "Accept invitation"
|
||||
msgstr "招待を受諾"
|
||||
|
||||
#: templates/proposals/_pending_proposal_row.html:37
|
||||
msgid "Decline invitation"
|
||||
msgstr "招待を拒否"
|
||||
|
||||
#: templates/proposals/_proposal_fields.html:4
|
||||
msgid "Submitted by"
|
||||
msgstr ""
|
||||
|
||||
#: templates/proposals/_proposal_fields.html:7
|
||||
msgid "Track"
|
||||
msgstr "トラック"
|
||||
|
||||
#: templates/proposals/_proposal_fields.html:10
|
||||
msgid "Audience Level"
|
||||
msgstr "受講者のレベル"
|
||||
|
||||
#: templates/proposals/_proposal_fields.html:14
|
||||
msgid "Additional Speakers"
|
||||
msgstr "追加の講演者"
|
||||
|
||||
#: templates/proposals/_proposal_fields.html:21
|
||||
msgid "Invitation Sent"
|
||||
msgstr "送信された招待"
|
||||
|
||||
#: templates/proposals/_proposal_fields.html:28
|
||||
msgid "Description"
|
||||
msgstr "記述"
|
||||
|
||||
#: templates/proposals/_proposal_fields.html:31
|
||||
msgid "Abstract"
|
||||
msgstr "講演概要"
|
||||
|
||||
#: templates/proposals/_proposal_fields.html:34
|
||||
msgid "Notes"
|
||||
msgstr "備考"
|
||||
|
||||
#: templates/proposals/_proposal_fields.html:37
|
||||
msgid "Speaker Bio"
|
||||
msgstr "講演者略歴"
|
||||
|
||||
#: templates/proposals/_proposal_fields.html:40
|
||||
msgid "Documents"
|
||||
msgstr "資料"
|
||||
|
||||
#: templates/proposals/proposal_cancel.html:7
|
||||
msgid "Cancel Proposal"
|
||||
msgstr "講演提案のキャンセル"
|
||||
|
||||
#: templates/proposals/proposal_cancel.html:16
|
||||
msgid "No, keep it for now"
|
||||
msgstr "いいえ、このままにします。"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:14
|
||||
msgid "Edit this proposal"
|
||||
msgstr "この提案を編集"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:17
|
||||
msgid "Cancel this proposal"
|
||||
msgstr "この提案をキャンセル"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:21
|
||||
msgid "Remove me from this proposal"
|
||||
msgstr "この提案から自分を削除する。"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:33
|
||||
#: templates/reviews/review_detail.html:74
|
||||
msgid "Proposal Details"
|
||||
msgstr "提案の詳細"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:35
|
||||
#: templates/proposals/proposal_detail.html:47
|
||||
msgid "Supporting Documents"
|
||||
msgstr "補足資料"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:38
|
||||
msgid "Reviewer Feedback"
|
||||
msgstr "レビュアーからのフィードバック"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:57
|
||||
msgid "delete"
|
||||
msgstr "削除"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:64
|
||||
msgid "No supporting documents attached to this proposal."
|
||||
msgstr "この提案に補足資料は添付されていません。"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:66
|
||||
msgid "Add Document"
|
||||
msgstr "資料の追加"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:73
|
||||
msgid "Conversation with Reviewers"
|
||||
msgstr "レビュアーとの会話記録"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:83
|
||||
msgid "Leave a Message"
|
||||
msgstr "メッセージを残す"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:85
|
||||
msgid "You can leave a message for the reviewers here."
|
||||
msgstr "メッセージをレビューアに残すことができます。"
|
||||
|
||||
#: templates/proposals/proposal_detail.html:94
|
||||
msgid "Submit"
|
||||
msgstr "投稿する"
|
||||
|
||||
#: templates/proposals/proposal_speaker_manage.html:7
|
||||
msgid "Proposal:"
|
||||
msgstr "講演提案:"
|
||||
|
||||
#: templates/proposals/proposal_speaker_manage.html:10
|
||||
msgid "Edit proposal"
|
||||
msgstr "講演提案の編集"
|
||||
|
||||
#: templates/proposals/proposal_speaker_manage.html:14
|
||||
msgid "Current Speakers"
|
||||
msgstr "現在登録されている講演者"
|
||||
|
||||
#: templates/proposals/proposal_speaker_manage.html:20
|
||||
msgid "pending invitation"
|
||||
msgstr "保留されている招待"
|
||||
|
||||
#: templates/proposals/proposal_speaker_manage.html:24
|
||||
msgid "Add another speaker"
|
||||
msgstr "他の講演者を追加"
|
||||
|
||||
#: templates/proposals/proposal_submit.html:6
|
||||
msgid "Submit A Proposal"
|
||||
msgstr "提案を投稿"
|
||||
|
||||
#: templates/reviews/_review_table.html:6
|
||||
#: templates/reviews/result_notification.html:45
|
||||
msgid "Speaker / Title"
|
||||
msgstr "講演者/タイトル"
|
||||
|
||||
#: templates/reviews/_review_table.html:7
|
||||
#: templates/reviews/result_notification.html:46
|
||||
msgid "Category"
|
||||
msgstr "カテゴリ"
|
||||
|
||||
#: templates/reviews/_review_table.html:9
|
||||
msgid "+1"
|
||||
msgstr ""
|
||||
|
||||
#: templates/reviews/_review_table.html:10
|
||||
msgid "+0"
|
||||
msgstr ""
|
||||
|
||||
#: templates/reviews/_review_table.html:11
|
||||
msgid "-0"
|
||||
msgstr ""
|
||||
|
||||
#: templates/reviews/_review_table.html:12
|
||||
msgid "-1"
|
||||
msgstr ""
|
||||
|
||||
#: templates/reviews/_review_table.html:13
|
||||
msgid "Your Rating"
|
||||
msgstr "あなたの投票"
|
||||
|
||||
#: templates/reviews/base.html:64
|
||||
msgid "All Reviews"
|
||||
msgstr "全てのレビュアー"
|
||||
|
||||
#: templates/reviews/base.html:77
|
||||
msgid "Voting Status"
|
||||
msgstr "投票の状態"
|
||||
|
||||
#: templates/reviews/result_notification.html:47
|
||||
msgid "Status"
|
||||
msgstr "状態"
|
||||
|
||||
#: templates/reviews/result_notification.html:48
|
||||
msgid "Notified?"
|
||||
msgstr "連絡済み?"
|
||||
|
||||
#: templates/reviews/review_detail.html:76
|
||||
msgid "Speaker Feedback"
|
||||
msgstr "講演者へのフィードバック"
|
||||
|
||||
#: templates/reviews/review_detail.html:84
|
||||
msgid "Current Results"
|
||||
msgstr "現在のところの結果"
|
||||
|
||||
#: templates/reviews/review_detail.html:91
|
||||
msgid "Total Responses"
|
||||
msgstr "全応答数"
|
||||
|
||||
#: templates/reviews/review_detail.html:108
|
||||
msgid "Submit Review"
|
||||
msgstr "レビューの投稿"
|
||||
|
||||
#: templates/reviews/review_detail.html:148
|
||||
msgid "Conversation with the submitter"
|
||||
msgstr "投稿者への連絡"
|
||||
|
||||
#: templates/reviews/review_detail.html:162
|
||||
msgid "Send a message"
|
||||
msgstr "メッセージ送信"
|
||||
|
||||
#: templates/reviews/review_detail.html:164
|
||||
msgid ""
|
||||
"\n"
|
||||
" If you'd like to communicate with the submitter, "
|
||||
"use the following form and he or she will be\n"
|
||||
" notified and given the opportunity to respond.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" もし、投稿者と連絡したい場合は、次の投稿フォームを"
|
||||
"用いて知らせることができます。\n"
|
||||
" そして、回答の機会を与えることができます。\n"
|
||||
" "
|
||||
|
||||
#: templates/schedule/_slot_edit.html:5
|
||||
msgid "Edit Slot"
|
||||
msgstr "スロットの編集"
|
||||
|
||||
#: templates/speakers/speaker_create.html:7
|
||||
#: templates/speakers/speaker_create.html:14
|
||||
msgid "Create Speaker Profile"
|
||||
msgstr "講演者プロフィールの作成"
|
||||
|
||||
#: templates/speakers/speaker_edit.html:7
|
||||
#: templates/speakers/speaker_edit.html:14
|
||||
msgid "Edit Speaker Profile"
|
||||
msgstr "講演者プロフィールの編集"
|
||||
|
||||
#: templates/sponsorship/add.html:7 templates/sponsorship/add.html.py:14
|
||||
msgid "Add a Sponsor"
|
||||
msgstr "スポンサーの追加"
|
||||
|
||||
#: templates/sponsorship/apply.html:7
|
||||
msgid "Apply to be a Sponsor"
|
||||
msgstr "スポンサーに応募する"
|
||||
|
||||
#: templates/sponsorship/apply.html:17
|
||||
msgid "Apply to Be a Sponsor"
|
||||
msgstr "スポンサーに応募する"
|
||||
|
||||
#: templates/sponsorship/list.html:7 templates/sponsorship/list.html.py:14
|
||||
msgid "About Our Sponsors"
|
||||
msgstr "スポンサーについて"
|
|
@ -9,6 +9,7 @@ from django.utils.timezone import now
|
|||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
import reversion
|
||||
|
||||
|
@ -106,8 +107,15 @@ class ProposalBase(models.Model):
|
|||
editable=False,
|
||||
)
|
||||
speaker = models.ForeignKey(Speaker, related_name="proposals")
|
||||
|
||||
def additional_speaker_validator(self, a_speaker):
|
||||
if a_speaker.speaker.email == self.speaker.email:
|
||||
raise ValidationError(_("%s is same as primary speaker.") % a_speaker.speaker.email)
|
||||
if a_speaker in [self.additional_speakers]:
|
||||
raise ValidationError(_("%s has already been in speakers.") % a_speaker.speaker.email)
|
||||
|
||||
additional_speakers = models.ManyToManyField(Speaker, through="AdditionalSpeaker",
|
||||
blank=True)
|
||||
blank=True, validators=[additional_speaker_validator])
|
||||
cancelled = models.BooleanField(default=False)
|
||||
|
||||
def can_edit(self):
|
||||
|
@ -147,7 +155,6 @@ class ProposalBase(models.Model):
|
|||
"kind": self.kind.name,
|
||||
}
|
||||
|
||||
|
||||
reversion.register(ProposalBase)
|
||||
|
||||
|
||||
|
@ -170,6 +177,14 @@ class AdditionalSpeaker(models.Model):
|
|||
class Meta:
|
||||
unique_together = ("speaker", "proposalbase")
|
||||
|
||||
def __unicode__(self):
|
||||
if self.status is self.SPEAKING_STATUS_PENDING:
|
||||
return _(u"pending speaker (%s)") % self.speaker.email
|
||||
elif self.status is self.SPEAKING_STATUS_DECLINED:
|
||||
return _(u"declined speaker (%s)") % self.speaker.email
|
||||
else:
|
||||
return self.speaker.name
|
||||
|
||||
|
||||
def uuid_filename(instance, filename):
|
||||
ext = filename.split(".")[-1]
|
||||
|
|
|
@ -3,28 +3,53 @@ from django.contrib import admin
|
|||
from symposion.schedule.models import Schedule, Day, Room, SlotKind, Slot, SlotRoom, Presentation, Session, SessionRole
|
||||
|
||||
|
||||
admin.site.register(Schedule)
|
||||
class DayInline(admin.StackedInline):
|
||||
model = Day
|
||||
extra = 2
|
||||
|
||||
|
||||
class SlotKindInline(admin.StackedInline):
|
||||
model = SlotKind
|
||||
|
||||
|
||||
class ScheduleAdmin(admin.ModelAdmin):
|
||||
model = Schedule
|
||||
inlines = [DayInline, SlotKindInline, ]
|
||||
|
||||
|
||||
class SlotRoomInline(admin.TabularInline):
|
||||
model = SlotRoom
|
||||
extra = 1
|
||||
|
||||
|
||||
class SlotAdmin(admin.ModelAdmin):
|
||||
list_filter = ("day", "kind")
|
||||
list_display = ("day", "start", "end", "kind", "content")
|
||||
inlines = [SlotRoomInline]
|
||||
|
||||
class RoomAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "order", "schedule"]
|
||||
list_filter = ["schedule"]
|
||||
inlines = [SlotRoomInline]
|
||||
|
||||
|
||||
class PresentationAdmin(admin.ModelAdmin):
|
||||
model = Presentation
|
||||
list_filter = ("section", "cancelled", "slot")
|
||||
|
||||
|
||||
admin.site.register(Day)
|
||||
admin.site.register(
|
||||
Room,
|
||||
list_display=("name", "order", "schedule"),
|
||||
list_filter=("schedule",)
|
||||
)
|
||||
admin.site.register(
|
||||
SlotKind,
|
||||
list_display=("label", "schedule"),
|
||||
)
|
||||
admin.site.register(
|
||||
Slot,
|
||||
list_display=("day", "start", "end", "kind", "content")
|
||||
list_display=["label", "schedule"],
|
||||
)
|
||||
admin.site.register(
|
||||
SlotRoom,
|
||||
list_display=("slot", "room")
|
||||
list_display=["slot", "room"]
|
||||
)
|
||||
admin.site.register(Schedule, ScheduleAdmin)
|
||||
admin.site.register(Room, RoomAdmin)
|
||||
admin.site.register(Slot, SlotAdmin)
|
||||
admin.site.register(Session)
|
||||
admin.site.register(SessionRole)
|
||||
admin.site.register(
|
||||
Presentation,
|
||||
list_filter=("section", "cancelled", "slot")
|
||||
)
|
||||
admin.site.register(Presentation, PresentationAdmin)
|
||||
|
|
|
@ -125,7 +125,8 @@ class Slot(models.Model):
|
|||
return Room.objects.filter(pk__in=self.slotroom_set.values("room"))
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s %s (%s - %s)" % (self.day, self.kind, self.start, self.end)
|
||||
roomlist = ' '.join(map(lambda r: r.__unicode__(), self.rooms))
|
||||
return u"%s %s (%s - %s) %s" % (self.day, self.kind, self.start, self.end, roomlist)
|
||||
|
||||
class Meta:
|
||||
ordering = ["day", "start", "end"]
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
from django.contrib import admin
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from symposion.sponsorship.models import SponsorLevel, Sponsor, Benefit, BenefitLevel, \
|
||||
SponsorBenefit
|
||||
|
@ -43,7 +46,16 @@ class SponsorAdmin(admin.ModelAdmin):
|
|||
})
|
||||
]
|
||||
inlines = [SponsorBenefitInline]
|
||||
list_display = ["name", "external_url", "level", "active"]
|
||||
list_display = ["name", "external_url", "level", "active", "contact", "applicant_field"]
|
||||
|
||||
def contact(self, sponsor):
|
||||
return mark_safe('<a href="mailto:%s">%s</a>' % (escape(sponsor.contact_email), escape(sponsor.contact_name)))
|
||||
|
||||
def applicant_field(self, sponsor):
|
||||
name = sponsor.applicant.get_full_name()
|
||||
email = sponsor.applicant.email
|
||||
return mark_safe('<a href="mailto:%s">%s</a>' % (escape(email), escape(name)))
|
||||
applicant_field.short_description = _(u"Applicant")
|
||||
|
||||
def get_form(self, *args, **kwargs):
|
||||
# @@@ kinda ugly but using choices= on NullBooleanField is broken
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import csv
|
||||
import os
|
||||
import shutil
|
||||
import zipfile
|
||||
|
||||
from contextlib import closing
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.template.defaultfilters import slugify
|
||||
|
||||
from sotmjp.sponsorship.models import Sponsor
|
||||
|
||||
|
||||
def zipdir(basedir, archivename):
|
||||
assert os.path.isdir(basedir)
|
||||
with closing(zipfile.ZipFile(archivename, "w", zipfile.ZIP_DEFLATED)) as z:
|
||||
for root, dirs, files in os.walk(basedir):
|
||||
#NOTE: ignore empty directories
|
||||
for fn in files:
|
||||
absfn = os.path.join(root, fn)
|
||||
zfn = absfn[len(basedir) + len(os.sep):] # XXX: relative path
|
||||
z.write(absfn, zfn)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
||||
def handle(self, *args, **options):
|
||||
try:
|
||||
os.makedirs(os.path.join(os.getcwd(), "build"))
|
||||
except:
|
||||
pass
|
||||
|
||||
csv_file = csv.writer(
|
||||
open(os.path.join(os.getcwd(), "build", "sponsors.csv"), "wb")
|
||||
)
|
||||
csv_file.writerow(["Name", "URL", "Level", "Description"])
|
||||
|
||||
for sponsor in Sponsor.objects.all():
|
||||
path = os.path.join(os.getcwd(), "build", slugify(sponsor.name))
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except:
|
||||
pass
|
||||
|
||||
data = {
|
||||
"name": sponsor.name,
|
||||
"url": sponsor.external_url,
|
||||
"level": sponsor.level.name,
|
||||
"description": "",
|
||||
}
|
||||
for sponsor_benefit in sponsor.sponsor_benefits.all():
|
||||
if sponsor_benefit.benefit_id == 2:
|
||||
data["description"] = sponsor_benefit.text
|
||||
if sponsor_benefit.benefit_id == 1:
|
||||
if sponsor_benefit.upload:
|
||||
data["ad"] = sponsor_benefit.upload.path
|
||||
if sponsor_benefit.benefit_id == 7:
|
||||
if sponsor_benefit.upload:
|
||||
data["logo"] = sponsor_benefit.upload.path
|
||||
|
||||
if "ad" in data:
|
||||
ad_path = data.pop("ad")
|
||||
shutil.copy(ad_path, path)
|
||||
if "logo" in data:
|
||||
logo_path = data.pop("logo")
|
||||
shutil.copy(logo_path, path)
|
||||
|
||||
csv_file.writerow([
|
||||
data["name"].encode("utf-8"),
|
||||
data["url"].encode("utf-8"),
|
||||
data["level"].encode("utf-8"),
|
||||
data["description"].encode("utf-8")
|
||||
])
|
||||
|
||||
zipdir(
|
||||
os.path.join(os.getcwd(), "build"),
|
||||
os.path.join(os.getcwd(), "sponsors.zip"))
|
|
@ -1,5 +1,6 @@
|
|||
import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
|
@ -39,6 +40,7 @@ class Sponsor(models.Model):
|
|||
null=True)
|
||||
|
||||
name = models.CharField(_("Sponsor Name"), max_length=100)
|
||||
display_url = models.URLField(_("display URL"), blank=True)
|
||||
external_url = models.URLField(_("external URL"))
|
||||
annotation = models.TextField(_("annotation"), blank=True)
|
||||
contact_name = models.CharField(_("Contact Name"), max_length=100)
|
||||
|
@ -65,6 +67,12 @@ class Sponsor(models.Model):
|
|||
return reverse("sponsor_detail", kwargs={"pk": self.pk})
|
||||
return reverse("sponsor_list")
|
||||
|
||||
def get_display_url(self):
|
||||
if self.display_url:
|
||||
return self.display_url
|
||||
else:
|
||||
return self.external_url
|
||||
|
||||
@property
|
||||
def website_logo(self):
|
||||
if self.sponsor_logo is None:
|
||||
|
@ -144,9 +152,17 @@ post_save.connect(_check_level_change, sender=Sponsor)
|
|||
|
||||
BENEFIT_TYPE_CHOICES = [
|
||||
("text", "Text"),
|
||||
("richtext", "Rich Text"),
|
||||
("file", "File"),
|
||||
("weblogo", "Web Logo"),
|
||||
("simple", "Simple")
|
||||
("simple", "Simple"),
|
||||
("option", "Option")
|
||||
]
|
||||
|
||||
CONTENT_TYPE_CHOICES = [
|
||||
("simple", "Simple"),
|
||||
] + [
|
||||
("listing_text_%s" % lang, "Listing Text (%s)" % label) for lang, label in settings.LANGUAGES
|
||||
]
|
||||
|
||||
|
||||
|
@ -154,8 +170,10 @@ class Benefit(models.Model):
|
|||
|
||||
name = models.CharField(_("name"), max_length=100)
|
||||
description = models.TextField(_("description"), blank=True)
|
||||
type = models.CharField(_("type"), choices=BENEFIT_TYPE_CHOICES, max_length=10,
|
||||
default="simple")
|
||||
type = models.CharField(_("type"), choices=BENEFIT_TYPE_CHOICES,
|
||||
max_length=10, default="simple")
|
||||
content_type = models.CharField(_("content type"), choices=CONTENT_TYPE_CHOICES,
|
||||
max_length=20, default="simple")
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from django import template
|
||||
from django.template.defaultfilters import linebreaks, urlize
|
||||
|
||||
from symposion.conference.models import current_conference
|
||||
from symposion.sponsorship.models import Sponsor, SponsorLevel
|
||||
|
@ -75,3 +76,45 @@ def sponsor_levels(parser, token):
|
|||
{% sponsor_levels as levels %}
|
||||
"""
|
||||
return SponsorLevelNode.handle_token(parser, token)
|
||||
|
||||
|
||||
class LocalizedTextNode(template.Node):
|
||||
|
||||
@classmethod
|
||||
def handle_token(cls, parser, token):
|
||||
bits = token.split_contents()
|
||||
if len(bits) == 3:
|
||||
return cls(bits[2], bits[1][1:-1])
|
||||
elif len(bits) == 5 and bits[-2] == "as":
|
||||
return cls(bits[2], bits[1][1:-1], bits[4])
|
||||
else:
|
||||
raise template.TemplateSyntaxError("%r takes 'as var'" % bits[0])
|
||||
|
||||
def __init__(self, sponsor, content_type, context_var=None):
|
||||
self.sponsor_var = template.Variable(sponsor)
|
||||
self.content_type = content_type
|
||||
self.content_var = context_var
|
||||
|
||||
def render(self, context):
|
||||
s = ''
|
||||
try:
|
||||
sponsor = self.sponsor_var.resolve(context)
|
||||
content_type = '%s_%s' % (self.content_type, context['request'].LANGUAGE_CODE)
|
||||
texts = sponsor.sponsor_benefits.filter(benefit__content_type=content_type)
|
||||
if texts.count() > 0:
|
||||
s = linebreaks(urlize(texts[0].text, autoescape=True))
|
||||
if self.content_var:
|
||||
context[self.content_var] = s
|
||||
s = ''
|
||||
except:
|
||||
pass
|
||||
return s
|
||||
|
||||
|
||||
@register.tag
|
||||
def localized_text(parser, token):
|
||||
"""
|
||||
{% localized_text "content_type" sponsor %}
|
||||
{% localized_text "content_type" sponsor as localized_text %}
|
||||
"""
|
||||
return LocalizedTextNode.handle_token(parser, token)
|
||||
|
|
307
symposion/sponsorship/tests.py
Normal file
307
symposion/sponsorship/tests.py
Normal file
|
@ -0,0 +1,307 @@
|
|||
from cStringIO import StringIO
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from zipfile import ZipFile
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from pycon.sponsorship.models import Benefit, Sponsor, SponsorBenefit,\
|
||||
SponsorLevel
|
||||
from symposion.conference.models import current_conference
|
||||
|
||||
|
||||
class TestSponsorZipDownload(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(username='joe',
|
||||
email='joe@example.com',
|
||||
password='joe')
|
||||
self.user.is_staff = True
|
||||
self.user.save()
|
||||
self.url = reverse("sponsor_zip_logos")
|
||||
self.assertTrue(self.client.login(username='joe@example.com',
|
||||
password='joe'))
|
||||
|
||||
# we need a sponsor
|
||||
conference = current_conference()
|
||||
self.sponsor_level = SponsorLevel.objects.create(
|
||||
conference=conference, name="Lead", cost=1)
|
||||
self.sponsor = Sponsor.objects.create(
|
||||
name="Big Daddy",
|
||||
level=self.sponsor_level,
|
||||
active=True,
|
||||
)
|
||||
|
||||
# Create our benefits, of various types
|
||||
self.text_benefit = Benefit.objects.create(name="text", type="text")
|
||||
self.file_benefit = Benefit.objects.create(name="file", type="file")
|
||||
# These names must be spelled exactly this way:
|
||||
self.weblogo_benefit = Benefit.objects.create(name="Web logo", type="weblogo")
|
||||
self.printlogo_benefit = Benefit.objects.create(name="Print logo", type="file")
|
||||
self.advertisement_benefit = Benefit.objects.create(name="Advertisement", type="file")
|
||||
|
||||
def validate_response(self, rsp, names_and_sizes):
|
||||
# Ensure a response from the view looks right, contains a valid
|
||||
# zip archive, has files with the right names and sizes.
|
||||
self.assertEqual("application/zip", rsp['Content-type'])
|
||||
prefix = settings.CONFERENCE_URL_PREFIXES[settings.CONFERENCE_ID]
|
||||
|
||||
self.assertEqual(
|
||||
'attachment; filename="pycon_%s_sponsorlogos.zip"' % prefix,
|
||||
rsp['Content-Disposition'])
|
||||
zipfile = ZipFile(StringIO(rsp.content), "r")
|
||||
# Check out the zip - testzip() returns None if no errors found
|
||||
self.assertIsNone(zipfile.testzip())
|
||||
# Compare contents to what is expected
|
||||
infolist = zipfile.infolist()
|
||||
self.assertEqual(len(names_and_sizes), len(infolist))
|
||||
for info, name_and_size in zip(infolist, names_and_sizes):
|
||||
name, size = name_and_size
|
||||
self.assertEqual(name, info.filename)
|
||||
self.assertEqual(size, info.file_size)
|
||||
|
||||
def make_temp_file(self, name, size=0):
|
||||
# Create a temp file with the given name and size under self.temp_dir
|
||||
path = os.path.join(self.temp_dir, name)
|
||||
with open(path, "wb") as f:
|
||||
f.write(size * "x")
|
||||
|
||||
def test_must_be_logged_in(self):
|
||||
# Must be logged in to use the view
|
||||
# If not logged in, doesn't redirect, just serves up a login view
|
||||
self.client.logout()
|
||||
rsp = self.client.get(self.url)
|
||||
self.assertEqual(200, rsp.status_code)
|
||||
self.assertIn("""<body class="login">""", rsp.content)
|
||||
|
||||
def test_must_be_staff(self):
|
||||
# Only staff can use the view
|
||||
# If not staff, doesn't show error, just serves up a login view
|
||||
# Also, the dashboard doesn't show the download button
|
||||
self.user.is_staff = False
|
||||
self.user.save()
|
||||
rsp = self.client.get(self.url)
|
||||
self.assertEqual(200, rsp.status_code)
|
||||
self.assertIn("""<body class="login">""", rsp.content)
|
||||
rsp = self.client.get(reverse('dashboard'))
|
||||
self.assertNotIn(self.url, rsp.content)
|
||||
|
||||
def test_no_files(self):
|
||||
# If there are no sponsor files, we still work
|
||||
# And the dashboard shows our download button
|
||||
rsp = self.client.get(self.url)
|
||||
self.validate_response(rsp, [])
|
||||
rsp = self.client.get(reverse('dashboard'))
|
||||
self.assertIn(self.url, rsp.content)
|
||||
|
||||
def test_different_benefit_types(self):
|
||||
# We only get files from the benefits named "Print logo" and "Web logo"
|
||||
# And we ignore any non-existent files
|
||||
try:
|
||||
# Create a temp dir for media files
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
with override_settings(MEDIA_ROOT=self.temp_dir):
|
||||
|
||||
# Give our sponsor some benefits
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.text_benefit,
|
||||
text="Foo!"
|
||||
)
|
||||
|
||||
self.make_temp_file("file1", 10)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.file_benefit,
|
||||
upload="file1"
|
||||
)
|
||||
|
||||
self.make_temp_file("file2", 20)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.weblogo_benefit,
|
||||
upload="file2"
|
||||
)
|
||||
|
||||
# Benefit whose file is missing from the disk
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.weblogo_benefit,
|
||||
upload="file3"
|
||||
)
|
||||
|
||||
# print logo benefit
|
||||
self.make_temp_file("file4", 40)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.printlogo_benefit,
|
||||
upload="file4"
|
||||
)
|
||||
|
||||
self.make_temp_file("file5", 50)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.advertisement_benefit,
|
||||
upload="file5"
|
||||
)
|
||||
|
||||
rsp = self.client.get(self.url)
|
||||
expected = [
|
||||
('web_logos/lead/big_daddy/file2', 20),
|
||||
('print_logos/lead/big_daddy/file4', 40),
|
||||
('advertisement/lead/big_daddy/file5', 50)
|
||||
]
|
||||
self.validate_response(rsp, expected)
|
||||
finally:
|
||||
if hasattr(self, 'temp_dir'):
|
||||
# Clean up any temp media files
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
def test_file_org(self):
|
||||
# The zip file is organized into directories:
|
||||
# {print_logos,web_logos,advertisement}/<sponsor_level>/<sponsor_name>/<filename>
|
||||
|
||||
# Add another sponsor at a different sponsor level
|
||||
conference = current_conference()
|
||||
self.sponsor_level2 = SponsorLevel.objects.create(
|
||||
conference=conference, name="Silly putty", cost=1)
|
||||
self.sponsor2 = Sponsor.objects.create(
|
||||
name="Big Mama",
|
||||
level=self.sponsor_level2,
|
||||
active=True,
|
||||
)
|
||||
#
|
||||
try:
|
||||
# Create a temp dir for media files
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
with override_settings(MEDIA_ROOT=self.temp_dir):
|
||||
|
||||
# Give our sponsors some benefits
|
||||
self.make_temp_file("file1", 10)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.weblogo_benefit,
|
||||
upload="file1"
|
||||
)
|
||||
# print logo benefit
|
||||
self.make_temp_file("file2", 20)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor,
|
||||
benefit=self.printlogo_benefit,
|
||||
upload="file2"
|
||||
)
|
||||
# Sponsor 2
|
||||
self.make_temp_file("file3", 30)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor2,
|
||||
benefit=self.weblogo_benefit,
|
||||
upload="file3"
|
||||
)
|
||||
# print logo benefit
|
||||
self.make_temp_file("file4", 42)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor2,
|
||||
benefit=self.printlogo_benefit,
|
||||
upload="file4"
|
||||
)
|
||||
# ad benefit
|
||||
self.make_temp_file("file5", 55)
|
||||
SponsorBenefit.objects.create(
|
||||
sponsor=self.sponsor2,
|
||||
benefit=self.advertisement_benefit,
|
||||
upload="file5"
|
||||
)
|
||||
|
||||
rsp = self.client.get(self.url)
|
||||
expected = [
|
||||
('web_logos/lead/big_daddy/file1', 10),
|
||||
('web_logos/silly_putty/big_mama/file3', 30),
|
||||
('print_logos/lead/big_daddy/file2', 20),
|
||||
('print_logos/silly_putty/big_mama/file4', 42),
|
||||
('advertisement/silly_putty/big_mama/file5', 55),
|
||||
]
|
||||
self.validate_response(rsp, expected)
|
||||
finally:
|
||||
if hasattr(self, 'temp_dir'):
|
||||
# Clean up any temp media files
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
|
||||
class TestBenefitValidation(TestCase):
|
||||
"""
|
||||
It should not be possible to save a SponsorBenefit if it has the
|
||||
wrong kind of data in it - e.g. a text-type benefit cannot have
|
||||
an uploaded file, and vice-versa.
|
||||
"""
|
||||
def setUp(self):
|
||||
# we need a sponsor
|
||||
conference = current_conference()
|
||||
self.sponsor_level = SponsorLevel.objects.create(
|
||||
conference=conference, name="Lead", cost=1)
|
||||
self.sponsor = Sponsor.objects.create(
|
||||
name="Big Daddy",
|
||||
level=self.sponsor_level,
|
||||
)
|
||||
|
||||
# Create our benefit types
|
||||
self.text_type = Benefit.objects.create(name="text", type="text")
|
||||
self.file_type = Benefit.objects.create(name="file", type="file")
|
||||
self.weblogo_type = Benefit.objects.create(name="log", type="weblogo")
|
||||
self.simple_type = Benefit.objects.create(name="simple", type="simple")
|
||||
|
||||
def validate(self, should_work, benefit_type, upload, text):
|
||||
obj = SponsorBenefit(
|
||||
benefit=benefit_type,
|
||||
sponsor=self.sponsor,
|
||||
upload=upload,
|
||||
text=text
|
||||
)
|
||||
if should_work:
|
||||
obj.save()
|
||||
else:
|
||||
with self.assertRaises(ValidationError):
|
||||
obj.save()
|
||||
|
||||
def test_text_has_text(self):
|
||||
self.validate(True, self.text_type, upload=None, text="Some text")
|
||||
|
||||
def test_text_has_upload(self):
|
||||
self.validate(False, self.text_type, upload="filename", text='')
|
||||
|
||||
def test_text_has_both(self):
|
||||
self.validate(False, self.text_type, upload="filename", text="Text")
|
||||
|
||||
def test_file_has_text(self):
|
||||
self.validate(False, self.file_type, upload=None, text="Some text")
|
||||
|
||||
def test_file_has_upload(self):
|
||||
self.validate(True, self.file_type, upload="filename", text='')
|
||||
|
||||
def test_file_has_both(self):
|
||||
self.validate(False, self.file_type, upload="filename", text="Text")
|
||||
|
||||
def test_weblogo_has_text(self):
|
||||
self.validate(False, self.weblogo_type, upload=None, text="Some text")
|
||||
|
||||
def test_weblogo_has_upload(self):
|
||||
self.validate(True, self.weblogo_type, upload="filename", text='')
|
||||
|
||||
def test_weblogo_has_both(self):
|
||||
self.validate(False, self.weblogo_type, upload="filename", text="Text")
|
||||
|
||||
def test_simple_has_neither(self):
|
||||
self.validate(True, self.simple_type, upload=None, text='')
|
||||
|
||||
def test_simple_has_text(self):
|
||||
self.validate(True, self.simple_type, upload=None, text="Some text")
|
||||
|
||||
def test_simple_has_upload(self):
|
||||
self.validate(False, self.simple_type, upload="filename", text='')
|
||||
|
||||
def test_simple_has_both(self):
|
||||
self.validate(False, self.simple_type, upload="filename", text="Text")
|
|
@ -7,5 +7,6 @@ urlpatterns = patterns(
|
|||
url(r"^$", TemplateView.as_view(template_name="sponsorship/list.html"), name="sponsor_list"),
|
||||
url(r"^apply/$", "sponsor_apply", name="sponsor_apply"),
|
||||
url(r"^add/$", "sponsor_add", name="sponsor_add"),
|
||||
url(r"^ziplogos/$", "sponsor_zip_logo_files", name="sponsor_zip_logos"),
|
||||
url(r"^(?P<pk>\d+)/$", "sponsor_detail", name="sponsor_detail"),
|
||||
)
|
||||
|
|
|
@ -1,13 +1,28 @@
|
|||
from django.http import Http404
|
||||
from cStringIO import StringIO
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from zipfile import ZipFile, ZipInfo
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.shortcuts import render_to_response, redirect, get_object_or_404
|
||||
from django.template import RequestContext
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
from symposion.sponsorship.forms import SponsorApplicationForm, SponsorDetailsForm, \
|
||||
SponsorBenefitsFormSet
|
||||
from symposion.sponsorship.models import Sponsor, SponsorBenefit
|
||||
from symposion.sponsorship.forms import SponsorApplicationForm, \
|
||||
SponsorDetailsForm, SponsorBenefitsFormSet
|
||||
from symposion.sponsorship.models import Benefit, Sponsor, SponsorBenefit, \
|
||||
SponsorLevel
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@login_required
|
||||
|
@ -18,13 +33,13 @@ def sponsor_apply(request):
|
|||
sponsor = form.save()
|
||||
if sponsor.sponsor_benefits.all():
|
||||
# Redirect user to sponsor_detail to give extra information.
|
||||
messages.success(request, "Thank you for your sponsorship "
|
||||
"application. Please update your "
|
||||
"benefit details below.")
|
||||
messages.success(request, _("Thank you for your sponsorship "
|
||||
"application. Please update your "
|
||||
"benefit details below."))
|
||||
return redirect("sponsor_detail", pk=sponsor.pk)
|
||||
else:
|
||||
messages.success(request, "Thank you for your sponsorship "
|
||||
"application.")
|
||||
messages.success(request, _("Thank you for your sponsorship "
|
||||
"application."))
|
||||
return redirect("dashboard")
|
||||
else:
|
||||
form = SponsorApplicationForm(user=request.user)
|
||||
|
@ -87,3 +102,96 @@ def sponsor_detail(request, pk):
|
|||
"form": form,
|
||||
"formset": formset,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@staff_member_required
|
||||
def sponsor_export_data(request):
|
||||
sponsors = []
|
||||
data = ""
|
||||
|
||||
for sponsor in Sponsor.objects.order_by("added"):
|
||||
d = {
|
||||
"name": sponsor.name,
|
||||
"url": sponsor.external_url,
|
||||
"level": (sponsor.level.order, sponsor.level.name),
|
||||
"description": "",
|
||||
}
|
||||
for sponsor_benefit in sponsor.sponsor_benefits.all():
|
||||
if sponsor_benefit.benefit_id == 2:
|
||||
d["description"] = sponsor_benefit.text
|
||||
sponsors.append(d)
|
||||
|
||||
def izip_longest(*args):
|
||||
fv = None
|
||||
|
||||
def sentinel(counter=([fv] * (len(args) - 1)).pop):
|
||||
yield counter()
|
||||
iters = [itertools.chain(it, sentinel(), itertools.repeat(fv)) for it in args]
|
||||
try:
|
||||
for tup in itertools.izip(*iters):
|
||||
yield tup
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
def pairwise(iterable):
|
||||
a, b = itertools.tee(iterable)
|
||||
b.next()
|
||||
return izip_longest(a, b)
|
||||
|
||||
def level_key(s):
|
||||
return s["level"]
|
||||
|
||||
for level, level_sponsors in itertools.groupby(sorted(sponsors, key=level_key), level_key):
|
||||
data += "%s\n" % ("-" * (len(level[1]) + 4))
|
||||
data += "| %s |\n" % level[1]
|
||||
data += "%s\n\n" % ("-" * (len(level[1]) + 4))
|
||||
for sponsor, next in pairwise(level_sponsors):
|
||||
description = sponsor["description"].strip()
|
||||
description = description if description else "-- NO DESCRIPTION FOR THIS SPONSOR --"
|
||||
data += "%s\n\n%s" % (sponsor["name"], description)
|
||||
if next is not None:
|
||||
data += "\n\n%s\n\n" % ("-" * 80)
|
||||
else:
|
||||
data += "\n\n"
|
||||
|
||||
return HttpResponse(data, content_type="text/plain;charset=utf-8")
|
||||
|
||||
|
||||
@staff_member_required
|
||||
def sponsor_zip_logo_files(request):
|
||||
"""Return a zip file of sponsor web and print logos"""
|
||||
|
||||
zip_stringio = StringIO()
|
||||
zipfile = ZipFile(zip_stringio, "w")
|
||||
try:
|
||||
benefits = Benefit.objects.all()
|
||||
for benefit in benefits:
|
||||
dir_name = benefit.name.lower().replace(" ", "_").replace('/', '_')
|
||||
for level in SponsorLevel.objects.all():
|
||||
level_name = level.name.lower().replace(" ", "_").replace('/', '_')
|
||||
for sponsor in Sponsor.objects.filter(level=level, active=True):
|
||||
sponsor_name = sponsor.name.lower().replace(" ", "_").replace('/', '_')
|
||||
full_dir = "/".join([dir_name, level_name, sponsor_name])
|
||||
for sponsor_benefit in SponsorBenefit.objects.filter(
|
||||
benefit=benefit,
|
||||
sponsor=sponsor,
|
||||
active=True,
|
||||
).exclude(upload=''):
|
||||
if os.path.exists(sponsor_benefit.upload.path):
|
||||
modtime = time.gmtime(os.stat(sponsor_benefit.upload.path).st_mtime)
|
||||
with open(sponsor_benefit.upload.path, "rb") as f:
|
||||
fname = os.path.split(sponsor_benefit.upload.name)[-1]
|
||||
zipinfo = ZipInfo(filename=full_dir + "/" + fname,
|
||||
date_time=modtime)
|
||||
zipfile.writestr(zipinfo, f.read())
|
||||
else:
|
||||
log.debug("No such sponsor file: %s" % sponsor_benefit.upload.path)
|
||||
finally:
|
||||
zipfile.close()
|
||||
|
||||
response = HttpResponse(zip_stringio.getvalue(),
|
||||
content_type="application/zip")
|
||||
prefix = settings.CONFERENCE_URL_PREFIXES[settings.CONFERENCE_ID]
|
||||
response['Content-Disposition'] = \
|
||||
'attachment; filename="%s_sponsorlogos.zip"' % prefix
|
||||
return response
|
||||
|
|
|
@ -1,384 +0,0 @@
|
|||
/* @group Base */
|
||||
.chzn-container {
|
||||
font-size: 13px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.chzn-container .chzn-drop {
|
||||
background: #fff;
|
||||
border: 1px solid #aaa;
|
||||
border-top: 0;
|
||||
position: absolute;
|
||||
top: 29px;
|
||||
left: 0;
|
||||
-webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15);
|
||||
-moz-box-shadow : 0 4px 5px rgba(0,0,0,.15);
|
||||
box-shadow : 0 4px 5px rgba(0,0,0,.15);
|
||||
z-index: 1010;
|
||||
}
|
||||
/* @end */
|
||||
|
||||
/* @group Single Chosen */
|
||||
.chzn-container-single .chzn-single {
|
||||
background-color: #ffffff;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0 );
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4));
|
||||
background-image: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
|
||||
background-image: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
|
||||
background-image: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
|
||||
background-image: linear-gradient(#ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius : 5px;
|
||||
border-radius : 5px;
|
||||
-moz-background-clip : padding;
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip : padding-box;
|
||||
border: 1px solid #aaaaaa;
|
||||
-webkit-box-shadow: 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
|
||||
-moz-box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
|
||||
box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
height: 23px;
|
||||
line-height: 24px;
|
||||
padding: 0 0 0 8px;
|
||||
color: #444444;
|
||||
text-decoration: none;
|
||||
}
|
||||
.chzn-container-single .chzn-default {
|
||||
color: #999;
|
||||
}
|
||||
.chzn-container-single .chzn-single span {
|
||||
margin-right: 26px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
-o-text-overflow: ellipsis;
|
||||
-ms-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.chzn-container-single .chzn-single abbr {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 26px;
|
||||
top: 6px;
|
||||
width: 12px;
|
||||
height: 13px;
|
||||
font-size: 1px;
|
||||
background: url('chosen-sprite.png') right top no-repeat;
|
||||
}
|
||||
.chzn-container-single .chzn-single abbr:hover {
|
||||
background-position: right -11px;
|
||||
}
|
||||
.chzn-container-single.chzn-disabled .chzn-single abbr:hover {
|
||||
background-position: right top;
|
||||
}
|
||||
.chzn-container-single .chzn-single div {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 18px;
|
||||
}
|
||||
.chzn-container-single .chzn-single div b {
|
||||
background: url('chosen-sprite.png') no-repeat 0 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.chzn-container-single .chzn-search {
|
||||
padding: 3px 4px;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
z-index: 1010;
|
||||
}
|
||||
.chzn-container-single .chzn-search input {
|
||||
background: #fff url('chosen-sprite.png') no-repeat 100% -22px;
|
||||
background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
|
||||
background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
||||
background: url('chosen-sprite.png') no-repeat 100% -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
||||
background: url('chosen-sprite.png') no-repeat 100% -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
||||
background: url('chosen-sprite.png') no-repeat 100% -22px, linear-gradient(#eeeeee 1%, #ffffff 15%);
|
||||
margin: 1px 0;
|
||||
padding: 4px 20px 4px 5px;
|
||||
outline: 0;
|
||||
border: 1px solid #aaa;
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
.chzn-container-single .chzn-drop {
|
||||
-webkit-border-radius: 0 0 4px 4px;
|
||||
-moz-border-radius : 0 0 4px 4px;
|
||||
border-radius : 0 0 4px 4px;
|
||||
-moz-background-clip : padding;
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip : padding-box;
|
||||
}
|
||||
/* @end */
|
||||
|
||||
.chzn-container-single-nosearch .chzn-search input {
|
||||
position: absolute;
|
||||
left: -9000px;
|
||||
}
|
||||
|
||||
/* @group Multi Chosen */
|
||||
.chzn-container-multi .chzn-choices {
|
||||
background-color: #fff;
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
|
||||
background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
||||
background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
||||
background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
||||
background-image: linear-gradient(#eeeeee 1%, #ffffff 15%);
|
||||
border: 1px solid #aaa;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
cursor: text;
|
||||
overflow: hidden;
|
||||
height: auto !important;
|
||||
height: 1%;
|
||||
position: relative;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices li {
|
||||
float: left;
|
||||
list-style: none;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-field {
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-field input {
|
||||
color: #666;
|
||||
background: transparent !important;
|
||||
border: 0 !important;
|
||||
font-family: sans-serif;
|
||||
font-size: 100%;
|
||||
height: 15px;
|
||||
padding: 5px;
|
||||
margin: 1px 0;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow : none;
|
||||
box-shadow : none;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-field .default {
|
||||
color: #999;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-choice {
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius : 3px;
|
||||
border-radius : 3px;
|
||||
-moz-background-clip : padding;
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip : padding-box;
|
||||
background-color: #e4e4e4;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
|
||||
background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||
background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||
background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||
background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
||||
-webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
|
||||
-moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
|
||||
box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
|
||||
color: #333;
|
||||
border: 1px solid #aaaaaa;
|
||||
line-height: 13px;
|
||||
padding: 3px 20px 3px 5px;
|
||||
margin: 3px 0 3px 5px;
|
||||
position: relative;
|
||||
cursor: default;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-choice-focus {
|
||||
background: #d4d4d4;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-choice .search-choice-close {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
top: 4px;
|
||||
width: 12px;
|
||||
height: 13px;
|
||||
font-size: 1px;
|
||||
background: url('chosen-sprite.png') right top no-repeat;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover {
|
||||
background-position: right -11px;
|
||||
}
|
||||
.chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close {
|
||||
background-position: right -11px;
|
||||
}
|
||||
/* @end */
|
||||
|
||||
/* @group Results */
|
||||
.chzn-container .chzn-results {
|
||||
margin: 0 4px 4px 0;
|
||||
max-height: 240px;
|
||||
padding: 0 0 0 4px;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.chzn-container-multi .chzn-results {
|
||||
margin: -1px 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
.chzn-container .chzn-results li {
|
||||
display: none;
|
||||
line-height: 15px;
|
||||
padding: 5px 6px;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.chzn-container .chzn-results .active-result {
|
||||
cursor: pointer;
|
||||
display: list-item;
|
||||
}
|
||||
.chzn-container .chzn-results .highlighted {
|
||||
background-color: #3875d7;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3875d7', endColorstr='#2a62bc', GradientType=0 );
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc));
|
||||
background-image: -webkit-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
|
||||
background-image: -moz-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
|
||||
background-image: -o-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
|
||||
background-image: linear-gradient(#3875d7 20%, #2a62bc 90%);
|
||||
color: #fff;
|
||||
}
|
||||
.chzn-container .chzn-results li em {
|
||||
background: #feffde;
|
||||
font-style: normal;
|
||||
}
|
||||
.chzn-container .chzn-results .highlighted em {
|
||||
background: transparent;
|
||||
}
|
||||
.chzn-container .chzn-results .no-results {
|
||||
background: #f4f4f4;
|
||||
display: list-item;
|
||||
}
|
||||
.chzn-container .chzn-results .group-result {
|
||||
cursor: default;
|
||||
color: #999;
|
||||
font-weight: bold;
|
||||
}
|
||||
.chzn-container .chzn-results .group-option {
|
||||
padding-left: 15px;
|
||||
}
|
||||
.chzn-container-multi .chzn-drop .result-selected {
|
||||
display: none;
|
||||
}
|
||||
.chzn-container .chzn-results-scroll {
|
||||
background: white;
|
||||
margin: 0 4px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 321px; /* This should by dynamic with js */
|
||||
z-index: 1;
|
||||
}
|
||||
.chzn-container .chzn-results-scroll span {
|
||||
display: inline-block;
|
||||
height: 17px;
|
||||
text-indent: -5000px;
|
||||
width: 9px;
|
||||
}
|
||||
.chzn-container .chzn-results-scroll-down {
|
||||
bottom: 0;
|
||||
}
|
||||
.chzn-container .chzn-results-scroll-down span {
|
||||
background: url('chosen-sprite.png') no-repeat -4px -3px;
|
||||
}
|
||||
.chzn-container .chzn-results-scroll-up span {
|
||||
background: url('chosen-sprite.png') no-repeat -22px -3px;
|
||||
}
|
||||
/* @end */
|
||||
|
||||
/* @group Active */
|
||||
.chzn-container-active .chzn-single {
|
||||
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
|
||||
-moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
|
||||
box-shadow : 0 0 5px rgba(0,0,0,.3);
|
||||
border: 1px solid #5897fb;
|
||||
}
|
||||
.chzn-container-active .chzn-single-with-drop {
|
||||
border: 1px solid #aaa;
|
||||
-webkit-box-shadow: 0 1px 0 #fff inset;
|
||||
-moz-box-shadow : 0 1px 0 #fff inset;
|
||||
box-shadow : 0 1px 0 #fff inset;
|
||||
background-color: #eee;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0 );
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff));
|
||||
background-image: -webkit-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
|
||||
background-image: -moz-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
|
||||
background-image: -o-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
|
||||
background-image: linear-gradient(#eeeeee 20%, #ffffff 80%);
|
||||
-webkit-border-bottom-left-radius : 0;
|
||||
-webkit-border-bottom-right-radius: 0;
|
||||
-moz-border-radius-bottomleft : 0;
|
||||
-moz-border-radius-bottomright: 0;
|
||||
border-bottom-left-radius : 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
.chzn-container-active .chzn-single-with-drop div {
|
||||
background: transparent;
|
||||
border-left: none;
|
||||
}
|
||||
.chzn-container-active .chzn-single-with-drop div b {
|
||||
background-position: -18px 1px;
|
||||
}
|
||||
.chzn-container-active .chzn-choices {
|
||||
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
|
||||
-moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
|
||||
box-shadow : 0 0 5px rgba(0,0,0,.3);
|
||||
border: 1px solid #5897fb;
|
||||
}
|
||||
.chzn-container-active .chzn-choices .search-field input {
|
||||
color: #111 !important;
|
||||
}
|
||||
/* @end */
|
||||
|
||||
/* @group Disabled Support */
|
||||
.chzn-disabled {
|
||||
cursor: default;
|
||||
opacity:0.5 !important;
|
||||
}
|
||||
.chzn-disabled .chzn-single {
|
||||
cursor: default;
|
||||
}
|
||||
.chzn-disabled .chzn-choices .search-choice .search-choice-close {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* @group Right to Left */
|
||||
.chzn-rtl { text-align: right; }
|
||||
.chzn-rtl .chzn-single { padding: 0 8px 0 0; overflow: visible; }
|
||||
.chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; direction: rtl; }
|
||||
|
||||
.chzn-rtl .chzn-single div { left: 3px; right: auto; }
|
||||
.chzn-rtl .chzn-single abbr {
|
||||
left: 26px;
|
||||
right: auto;
|
||||
}
|
||||
.chzn-rtl .chzn-choices .search-field input { direction: rtl; }
|
||||
.chzn-rtl .chzn-choices li { float: right; }
|
||||
.chzn-rtl .chzn-choices .search-choice { padding: 3px 5px 3px 19px; margin: 3px 5px 3px 0; }
|
||||
.chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 4px; right: auto; background-position: right top;}
|
||||
.chzn-rtl.chzn-container-single .chzn-results { margin: 0 0 4px 4px; padding: 0 4px 0 0; }
|
||||
.chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 15px; }
|
||||
.chzn-rtl.chzn-container-active .chzn-single-with-drop div { border-right: none; }
|
||||
.chzn-rtl .chzn-search input {
|
||||
background: #fff url('chosen-sprite.png') no-repeat -38px -22px;
|
||||
background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
|
||||
background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
||||
background: url('chosen-sprite.png') no-repeat -38px -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
||||
background: url('chosen-sprite.png') no-repeat -38px -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
||||
background: url('chosen-sprite.png') no-repeat -38px -22px, linear-gradient(#eeeeee 1%, #ffffff 15%);
|
||||
padding: 4px 5px 4px 20px;
|
||||
direction: rtl;
|
||||
}
|
||||
/* @end */
|
10
symposion/static/chosen/chosen.jquery.min.js
vendored
10
symposion/static/chosen/chosen.jquery.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue