├── test_project ├── __init__.py ├── local_settings.py ├── manage.py ├── urls.py └── settings.py ├── mezzanine_agenda ├── templatetags │ ├── __init__.py │ └── event_tags.py ├── __init__.py ├── fixtures │ └── events_page.json ├── admin.py ├── urls.py ├── defaults.py ├── templates │ └── agenda │ │ ├── includes │ │ └── filter_panel.html │ │ ├── event_list.html │ │ └── event_detail.html ├── feeds.py ├── tests.py ├── migrations │ └── 0001_initial.py ├── views.py └── models.py ├── .gitignore ├── .travis.yml ├── setup.py └── README.mdown /test_project/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mezzanine_agenda/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info/* 3 | *.db 4 | .DS_Store 5 | setuptools_git* 6 | -------------------------------------------------------------------------------- /mezzanine_agenda/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Provides a agenda app with events, keywords, locations and comments. 3 | Events can be listed by month, keyword, location or author. 4 | """ 5 | from __future__ import unicode_literals 6 | 7 | __version__ = "0.1.2" 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: '2.7' 3 | env: 4 | - DJANGO_VERSION="django>=1.5,<1.5.99" 5 | - DJANGO_VERSION="django>=1.6,<1.6.99" 6 | install: 7 | - 'pip install $DJANGO_VERSION' 8 | - 'pip install -q -e . --use-mirrors' 9 | script: 10 | - 'cd test_project' 11 | - 'python manage.py test mezzanine_agenda' 12 | -------------------------------------------------------------------------------- /mezzanine_agenda/fixtures/events_page.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pk": 9, 4 | "model": "pages.page", 5 | "fields": { 6 | "status": 2, 7 | "_order": 1, 8 | "parent": null, 9 | "description": "Events", 10 | "title": "Events", 11 | "titles": "Events", 12 | "content_model": "richtextpage", 13 | "in_menus": [1, 2, 3], 14 | "slug": "events", 15 | "site": 1 16 | } 17 | }, 18 | { 19 | "pk": 9, 20 | "model": "pages.richtextpage", 21 | "fields": { 22 | "content": "
Events
" 23 | } 24 | } 25 | ] 26 | 27 | -------------------------------------------------------------------------------- /test_project/local_settings.py: -------------------------------------------------------------------------------- 1 | 2 | DEBUG = True 3 | 4 | # Make these unique, and don't share it with anybody. 5 | SECRET_KEY = "%(SECRET_KEY)s" 6 | NEVERCACHE_KEY = "%(NEVERCACHE_KEY)s" 7 | 8 | DATABASES = { 9 | "default": { 10 | # Ends with "postgresql_psycopg2", "mysql", "sqlite3" or "oracle". 11 | "ENGINE": "django.db.backends.sqlite3", 12 | # DB name or path to database file if using sqlite3. 13 | "NAME": "dev.db", 14 | # Not used with sqlite3. 15 | "USER": "", 16 | # Not used with sqlite3. 17 | "PASSWORD": "", 18 | # Set to empty string for localhost. Not used with sqlite3. 19 | "HOST": "", 20 | # Set to empty string for default. Not used with sqlite3. 21 | "PORT": "", 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test_project/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | import os 5 | import sys 6 | 7 | 8 | # Corrects some pathing issues in various contexts, such as cron jobs, 9 | # and the project layout still being in Django 1.3 format. 10 | from settings import PROJECT_ROOT, PROJECT_DIRNAME 11 | os.chdir(PROJECT_ROOT) 12 | sys.path.insert(0, os.path.abspath(os.path.join(PROJECT_ROOT, ".."))) 13 | 14 | 15 | # Add the site ID CLI arg to the environment, which allows for the site 16 | # used in any site related queries to be manually set for management 17 | # commands. 18 | for i, arg in enumerate(sys.argv): 19 | if arg.startswith("--site"): 20 | os.environ["MEZZANINE_SITE_ID"] = arg.split("=")[1] 21 | sys.argv.pop(i) 22 | 23 | 24 | # Run Django. 25 | if __name__ == "__main__": 26 | settings_module = "%s.settings" % PROJECT_DIRNAME 27 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module) 28 | from django.core.management import execute_from_command_line 29 | execute_from_command_line(sys.argv) 30 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | from mezzanine_agenda import __version__ 3 | import subprocess 4 | 5 | def get_long_desc(): 6 | """Use Pandoc to convert the readme to ReST for the PyPI.""" 7 | try: 8 | return subprocess.check_output(['pandoc', '-f', 'markdown', '-t', 'rst', 'README.mdown']) 9 | except: 10 | print "WARNING: The long readme wasn't converted properly" 11 | 12 | setup(name='mezzanine-agenda', 13 | version=__version__, 14 | description='Events for the Mezzanine CMS', 15 | long_description=get_long_desc(), 16 | author='James Pells', 17 | author_email='jimmy@jamespells.com', 18 | url='https://github.com/jpells/mezzanine-agenda', 19 | packages=find_packages(), 20 | include_package_data=True, 21 | setup_requires=[ 22 | 'setuptools_git>=0.3', 23 | ], 24 | install_requires=[ 25 | 'mezzanine', 26 | 'icalendar==3.0.1b2', 27 | 'geopy==0.95.1', 28 | 'pytz', 29 | ], 30 | classifiers = [ 31 | 'Development Status :: 4 - Beta', 32 | 'Environment :: Web Environment', 33 | 'Framework :: Django', 34 | 'License :: OSI Approved :: MIT License', 35 | 'Natural Language :: English', 36 | 'Operating System :: OS Independent', 37 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 38 | ], 39 | ) 40 | -------------------------------------------------------------------------------- /mezzanine_agenda/admin.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from copy import deepcopy 4 | 5 | from django.contrib import admin 6 | from django.utils.translation import ugettext_lazy as _ 7 | 8 | from mezzanine_agenda.models import Event, EventLocation 9 | from mezzanine.conf import settings 10 | from mezzanine.core.admin import DisplayableAdmin, OwnableAdmin 11 | 12 | 13 | event_fieldsets = deepcopy(DisplayableAdmin.fieldsets) 14 | event_fieldsets[0][1]["fields"].insert(1, ("start", "end")) 15 | event_fieldsets[0][1]["fields"].insert(2, "location") 16 | event_fieldsets[0][1]["fields"].insert(3, "facebook_event") 17 | event_fieldsets[0][1]["fields"].extend(["content", "allow_comments"]) 18 | event_list_display = ["title", "user", "status", "admin_link"] 19 | if settings.EVENT_USE_FEATURED_IMAGE: 20 | event_fieldsets[0][1]["fields"].insert(-2, "featured_image") 21 | event_list_display.insert(0, "admin_thumb") 22 | event_fieldsets = list(event_fieldsets) 23 | event_list_filter = deepcopy(DisplayableAdmin.list_filter) + ("location",) 24 | 25 | 26 | class EventAdmin(DisplayableAdmin, OwnableAdmin): 27 | """ 28 | Admin class for events. 29 | """ 30 | 31 | fieldsets = event_fieldsets 32 | list_display = event_list_display 33 | list_filter = event_list_filter 34 | 35 | def save_form(self, request, form, change): 36 | """ 37 | Super class ordering is important here - user must get saved first. 38 | """ 39 | OwnableAdmin.save_form(self, request, form, change) 40 | return DisplayableAdmin.save_form(self, request, form, change) 41 | 42 | 43 | class EventLocationAdmin(admin.ModelAdmin): 44 | """ 45 | Admin class for event locations. Hides itself from the admin menu 46 | unless explicitly specified. 47 | """ 48 | 49 | fieldsets = ((None, {"fields": ("title", "address", "mappable_location", "lat", "lon")}),) 50 | 51 | def in_menu(self): 52 | """ 53 | Hide from the admin menu unless explicitly set in ``ADMIN_MENU_ORDER``. 54 | """ 55 | for (name, items) in settings.ADMIN_MENU_ORDER: 56 | if "mezzanine_agenda.EventLocation" in items: 57 | return True 58 | return False 59 | 60 | 61 | admin.site.register(Event, EventAdmin) 62 | admin.site.register(EventLocation, EventLocationAdmin) 63 | -------------------------------------------------------------------------------- /mezzanine_agenda/urls.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.conf.urls import patterns, url 4 | 5 | from mezzanine.conf import settings 6 | 7 | 8 | # Trailing slash for urlpatterns based on setup. 9 | _slash = "/" if settings.APPEND_SLASH else "" 10 | 11 | # Agenda patterns. 12 | urlpatterns = patterns("mezzanine_agenda.views", 13 | url("^feeds/(?P48 | {% if tag %} 49 | {% trans "Viewing events tagged" %} {{ tag }} 50 | {% else %}{% if location %} 51 | {% trans "Viewing events for the location" %} {{ location }} 52 | {% else %}{% if year or month %} 53 | {% trans "Viewing events from" %} {% if month %}{{ month }}, {% endif %} 54 | {{ year }} 55 | {% else %}{% if author %} 56 | {% trans "Viewing events by" %} 57 | {{ author.get_full_name|default:author.username }} 58 | {% endif %}{% endif %}{% endif %}{% endif %} 59 | {% endblock %} 60 |
61 | {% else %} 62 | {% if page %} 63 | {% block event_list_pagecontent %} 64 | {% editable page.richtextpage.content %} 65 | {{ page.richtextpage.content|richtext_filters|safe }} 66 | {% endeditable %} 67 | {% endblock %} 68 | {% endif %} 69 | {% endif %} 70 | 71 | {% block event_calendar %} 72 |73 | Subscribe to all events in Google Calendar/Outlook/iCal 74 |
75 | {% endblock %} 76 | 77 | {% for event in events.object_list %} 78 | {% block event_list_event_title %} 79 | {% editable event.title %} 80 |128 | {% trans "read more" %} 129 | {% if event.allow_comments %} 130 | / 131 | {% if settings.COMMENTS_DISQUS_SHORTNAME %} 132 | 134 | {% trans "Comments" %} 135 | 136 | {% else %} 137 | 138 | {% blocktrans count comments_count=event.comments_count %}{{ comments_count }} comment{% plural %}{{ comments_count }} comments{% endblocktrans %} 139 | 140 | {% endif %} 141 | {% endif %} 142 |
143 |41 | {% if event.allow_comments %} 42 | {% if settings.COMMENTS_DISQUS_SHORTNAME %} 43 | ({% spaceless %} 45 | {% trans "Comments" %} 46 | {% endspaceless %}) 47 | {% else %}({% spaceless %} 48 | {% blocktrans count comments_count=event.comments_count %}{{ comments_count }} comment{% plural %}{{ comments_count }} comments{% endblocktrans %} 49 | {% endspaceless %}) 50 | {% endif %} 51 | {% endif %} 52 |
53 | {% endblock %} 54 | 55 | {% block event_detail_calendar %} 56 |57 | Add to Google Calendar 58 | 59 | 60 | Add to Outlook/iCal 61 |
62 | {% endblock %} 63 | 64 | {% block event_detail_featured_image %} 65 | {% if settings.EVENT_USE_FEATURED_IMAGE and event.featured_image %} 66 |
86 | {{ event.location.address|linebreaksbr }}
87 |
88 | Get Directions
89 |
90 |