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…
	
	Add table
		
		Reference in a new issue
	
	 Patrick Altman
						Patrick Altman