Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'develop' into french-trans

  • Loading branch information...
commit 0e289cc45836d4b147d9f7d8dd9fb30828a284cb 2 parents 68c1377 + 5f3ff60
@eclan eclan authored
Showing with 22,862 additions and 7,223 deletions.
  1. +3 −1 .gitignore
  2. +1 −0  CHANGES.txt
  3. +2 −0  contributors.txt → CONTRIBUTORS.txt
  4. 0  LICENCE → LICENCE.txt
  5. +11 −0 MANIFEST.in
  6. +17 −13 README.rst → README.txt
  7. +10 −0 REQUIREMENTS.txt
  8. +14 −4 arkestra_image_plugin/cms_plugins.py
  9. +0 −1  arkestra_image_plugin/views.py
  10. +2 −0  arkestra_utilities/admin_mixins.py
  11. +5 −5 arkestra_utilities/context_processors.py
  12. +30 −21 arkestra_utilities/generic_models.py
  13. +1 −3 arkestra_utilities/managers.py
  14. +5 −6 arkestra_utilities/menu.py
  15. +0 −2  arkestra_utilities/models.py
  16. +49 −54 arkestra_utilities/settings.py
  17. +6 −6 arkestra_utilities/static/cms/wymeditor/lang/en.js
  18. BIN  arkestra_utilities/static/static.png
  19. +1 −1  arkestra_utilities/templates/arkestra.html
  20. +3 −50 arkestra_utilities/widgets/wym_editor.py
  21. +34 −11 contacts_and_people/admin.py
  22. +0 −1  contacts_and_people/cms_plugins.py
  23. +2 −2 contacts_and_people/link_schemas.py
  24. +103 −62 contacts_and_people/models.py
  25. +15 −8 contacts_and_people/templates/admin/contacts_and_people/entity/change_list.html
  26. +1 −1  contacts_and_people/templates/admin/contacts_and_people/entity/change_list_tree.html
  27. +1 −1  contacts_and_people/templates/admin/contacts_and_people/entity/change_list_tree_item.html
  28. +1 −3 contacts_and_people/templates/contacts_and_people/entity_contacts_and_people.html
  29. +2 −2 contacts_and_people/templates/contacts_and_people/person.html
  30. +2 −2 contacts_and_people/templates/includes/access_notes.html
  31. +5 −3 contacts_and_people/templates/includes/address.html
  32. +2 −2 contacts_and_people/templates/includes/precise_location.html
  33. +230 −23 contacts_and_people/tests.py
  34. +0 −1  contacts_and_people/urls.py
  35. +19 −16 contacts_and_people/views.py
  36. +234 −0 debian_installation.txt
  37. BIN  docs/_build/doctrees/environment.pickle
  38. BIN  docs/_build/doctrees/getting_started.doctree
  39. BIN  docs/_build/doctrees/index.doctree
  40. BIN  docs/_build/doctrees/installation.doctree
  41. +0 −4 docs/_build/html/.buildinfo
  42. +0 −15 docs/_build/html/_sources/getting_started.txt
  43. +0 −31 docs/_build/html/_sources/index.txt
  44. +0 −34 docs/_build/html/_sources/installation.txt
  45. +0 −528 docs/_build/html/_static/basic.css
  46. +0 −256 docs/_build/html/_static/default.css
  47. +0 −247 docs/_build/html/_static/doctools.js
  48. BIN  docs/_build/html/_static/file.png
  49. +0 −154 docs/_build/html/_static/jquery.js
  50. BIN  docs/_build/html/_static/minus.png
  51. BIN  docs/_build/html/_static/plus.png
  52. +0 −62 docs/_build/html/_static/pygments.css
  53. +0 −518 docs/_build/html/_static/searchtools.js
  54. +0 −148 docs/_build/html/_static/sidebar.js
  55. +0 −16 docs/_build/html/_static/underscore.js
  56. +0 −90 docs/_build/html/genindex.html
  57. +0 −108 docs/_build/html/getting_started.html
  58. +0 −129 docs/_build/html/index.html
  59. +0 −153 docs/_build/html/installation.html
  60. BIN  docs/_build/html/objects.inv
  61. +0 −96 docs/_build/html/search.html
  62. +0 −1  docs/_build/html/searchindex.js
  63. +1 −1  docs/conf.py
  64. +4 −4 docs/{ → getting_started}/getting_started.rst
  65. +64 −0 docs/getting_started/installation.rst
  66. +82 −0 docs/getting_started/installation_notes.rst
  67. +20 −11 docs/index.rst
  68. +0 −144 docs/installation.rst
  69. +0 −1  docs/{ → using_arkestra}/arkestra_generic_models.rst
  70. 0  docs/{ → using_arkestra}/image_sizing_system.rst
  71. 0  docs/{ → using_arkestra}/links_system.rst
  72. 0  docs/{ → using_arkestra}/migration.rst
  73. +1 −0  docs/{templates.rst.css → using_arkestra/templates.rst}
  74. 0  docs/{ → using_arkestra}/using_news_and_events.rst
  75. 0  docs/{ → using_arkestra}/video_system.rst
  76. +0 −17 example/arkestra_settings.py
  77. +10,137 −1 example/example_database.json
  78. +107 −87 example/settings.py
  79. +2 −5 example/urls.py
  80. 0  docs/displaying_the_titles_of_events.rst → example_14/example_14/__init__.py
  81. +58 −0 example_14/example_14/arkestra_settings.py
  82. +5 −0 example_14/example_14/deployment_settings.py
  83. +1 −0  example_14/example_14/example_database.json
  84. +8 −0 example_14/example_14/legacy_finders.py
  85. +144 −0 example_14/example_14/media/css/institute.css
  86. BIN  example_14/example_14/media/filer/2011/06/30/0020n040.jpg
  87. BIN  example_14/example_14/media/filer/2011/07/04/0020n040.jpg
  88. BIN  example_14/example_14/media/filer/2011/07/05/0020n040.jpg
  89. BIN  example_14/example_14/media/filer/2011/07/05/0020n040_1.jpg
  90. BIN  example_14/example_14/media/filer/2011/07/06/07.jpg
  91. BIN  example_14/example_14/media/filer/2011/07/06/335.jpg
  92. BIN  example_14/example_14/media/filer/2011/07/06/84_evil-eye.gif
  93. BIN  example_14/example_14/media/filer/2011/07/06/johncale.jpg
  94. BIN  example_14/example_14/media/filer/2011/07/06/jonathan-richman-101.jpg
  95. BIN  example_14/example_14/media/filer/2011/07/06/tooth.jpg
  96. BIN  example_14/example_14/media/filer/2011/07/14/haddock.jpg
  97. BIN  example_14/example_14/media/filer/2011/07/28/110.jpg
  98. BIN  example_14/example_14/media/filer/2011/07/28/15644525.jpg
  99. BIN  example_14/example_14/media/filer/2011/07/28/arrow-down.png
  100. BIN  example_14/example_14/media/filer/2011/07/28/arrow-down_1.png
  101. BIN  example_14/example_14/media/filer/2011/07/28/arrow-left.png
  102. BIN  example_14/example_14/media/filer/2011/07/28/arrow-left_1.png
  103. BIN  example_14/example_14/media/filer/2011/07/28/arrow-right.png
  104. BIN  example_14/example_14/media/filer/2011/07/28/arrow-right_1.png
  105. BIN  example_14/example_14/media/filer/2011/07/28/arrow-up.png
  106. BIN  example_14/example_14/media/filer/2011/07/28/arrow-up_1.png
  107. BIN  example_14/example_14/media/filer/2011/07/29/normal_carl_perkins.jpg
  108. BIN  example_14/example_14/media/media.png
  109. +15 −0 example_14/example_14/requirements.txt
  110. +351 −0 example_14/example_14/settings.py
  111. +12 −0 example_14/example_14/templates/institute.html
  112. +26 −0 example_14/example_14/urls.py
  113. +28 −0 example_14/example_14/wsgi.py
  114. +10,492 −0 example_14/example_database.json
  115. +10 −0 example_14/manage.py
  116. +0 −1  housekeeping/clean_plugins.py
  117. +0 −1  housekeeping/tidy_links.py
  118. +4 −3 links/link_schemas.py
  119. +2 −3 links/models.py
  120. +15 −8 links/templates/admin/links/externalsite/change_list.html
  121. +0 −4 links/templatetags/link_tags.py
  122. +38 −17 links/tests.py
  123. +1 −1  news_and_events/admin.py
  124. +1 −2  news_and_events/feeds.py
  125. +1 −3 news_and_events/managers.py
  126. +9 −14 news_and_events/models.py
  127. +74 −23 news_and_events/templates/admin/news_and_events/event/change_list.html
  128. +130 −0 news_and_events/tests.py
  129. +8 −14 news_and_events/urls.py
  130. +22 −15 news_and_events/views.py
  131. +25 −0 setup.py
  132. +0 −1,665 support_files/Arkestra icons.graffle
  133. +0 −1,222 support_files/Arkestra logotypes.graffle
  134. +0 −934 support_files/institutes.graffle
  135. +1 −2  vacancies_and_studentships/managers.py
  136. +2 −4 vacancies_and_studentships/models.py
  137. +10 −12 vacancies_and_studentships/views.py
  138. +28 −15 video/admin.py
  139. +86 −87 video/cms_plugins.py
  140. +26 −7 video/models.py
View
4 .gitignore
@@ -9,6 +9,7 @@
*.swp
*.vi
*.cache
+*.egg-info
*~
*#
@@ -35,4 +36,5 @@ _assets
_design
_content
_tmp
-dist
+dist
+build
View
1  CHANGES.txt
@@ -0,0 +1 @@
+v2.0pre1, 2012 06 17 -- First PyPI release
View
2  contributors.txt → CONTRIBUTORS.txt
@@ -8,5 +8,7 @@ Jonas Obrist
Bjarni Þórisson
Luke Crooks
Kristian Oellegaard
+Barry Rowlingson
+Hamish Downer
... and if I have neglected to mention someone, please let me know!
View
0  LICENCE → LICENCE.txt
File renamed without changes
View
11 MANIFEST.in
@@ -0,0 +1,11 @@
+# include *.txt
+# recursive-include docs *.txt
+graft arkestra_image_plugin
+graft arkestra_utilities
+graft arkestra_utilities
+graft contacts_and_people
+graft housekeeping
+graft links
+graft news_and_events
+graft vacancies_and_studentships
+graft video
View
30 README.rst → README.txt
@@ -1,3 +1,7 @@
+========
+Arkestra
+========
+
Arkestra is a semantic web publishing system for organisations, created in Django.
**Go straight to the develop branch**: https://github.com/evildmp/Arkestra/tree/develop
@@ -5,7 +9,7 @@ if you're a new user and don't need to worry about an existing installation. Als
documentation of the develop branch: https://github.com/evildmp/Arkestra/blob/develop/docs/index.rst
Documentation & support
-***********************
+=======================
Documentation is in progress; what exists can be found at http://readthedocs.org/docs/arkestra/.
@@ -14,7 +18,7 @@ Documentation is in progress; what exists can be found at http://readthedocs.org
* IRC: #arkestra on irc.freenode.net
Relationship with django CMS
-****************************
+============================
Arkestra works alongside django CMS https://github.com/divio/django-cms/.
@@ -23,7 +27,7 @@ If you're already familiar with django CMS you can regard Arkestra as a set of a
Alternatively, Arkestra is a powerful system that uses django CMS's frameworks to publish information about an institution, its organisation, internal structures and relations with other institutions, people, news, events, place, vacancies, studentships and more.
Arkestra is intelligent
-***********************
+=======================
Arkestra has been designed to make the web editor's job as easy as possible.
@@ -32,22 +36,22 @@ It is an intelligent system - it structures information and makes use of connect
It uses these connections, explicit and implicit, to automate as much as can be automated, so that the web editor does not need to do anything that the system should have worked out for itself.
Arkestra is a semantic CMS
-**************************
+==========================
It's also a semantic system - it doesn't simply store data, but manages information according to a model of the real world. This means that every item of information in the system (including for example information published in Django CMS pages) has meaning because it is associated with real-world objects, and placed in their context.
Arkestra in practice
-********************
+====================
A large project
-===============
+---------------
Arkestra currently publishes the website of Cardiff University School of Medicine: http://medicine.cf.ac.uk/; this includes information about:
-1700 people, in
-2700 different roles, in
-160 entities (i.e. parts of the organisation), as well as
-330 news articles and 570 events.
+* 1700 people, in
+* 2700 different roles, in
+* 160 entities (i.e. parts of the organisation), as well as
+* 330 news articles and 570 events.
It also publishes over 10000 pages, around 90% of which are published automatically by the system.
@@ -56,15 +60,15 @@ All this information is managed by a team of over 40 web editors.
In other words it is suited to the needs of large organisations; it's robust and performs well.
Smaller projects
-================
+----------------
-It also works well for much smaller projects, its core concepts scale up and down effectively. Two much smaller sites using Arkestra include:
+Arkestra also works well for much smaller projects, its core concepts scale up and down effectively. Two much smaller sites using Arkestra include:
* http://aikidocardiff.com/
* http://thelaugharneweekend.com/
Work to be done
-***************
+===============
There is much work to be done in Arkestra. It works extremely well, but its codebase needs to be improved to conform with good practice and to make it possible to develop it effectively; it requires:
View
10 REQUIREMENTS.txt
@@ -0,0 +1,10 @@
+-e hg+https://bitbucket.org/evildmp/semanticeditor#egg=semanticeditor
+-e git+https://github.com/evildmp/django-widgetry.git#egg=django-widgetry
+
+# for now, we need to pin filer to this version - 0.9 introduces backwards incompatible changes
+# http://django-filer.readthedocs.org/en/0.9/upgrading.html#upgrading
+# the example database will need to be upgraded before we can use the main branch
+# -e git+https://github.com/stefanfoulis/django-filer.git@develop#egg=django-filer
+
+-e git+https://github.com/stefanfoulis/django-filer.git@81c7304b8240279a5fe13bf769d7839fed72fba9#egg=django_filer-0.9a1-py2.6-feature/issue-213
+
View
18 arkestra_image_plugin/cms_plugins.py
@@ -5,7 +5,6 @@
from django.contrib import admin, messages
from django import forms
from django.db import models
-from django.conf import settings
from cms.plugin_pool import plugin_pool
from cms.plugin_base import CMSPluginBase
@@ -15,6 +14,7 @@
from widgetry.tabs.admin import ModelAdminWithTabs
from widgetry import fk_lookup
+from arkestra_utilities.settings import IMAGESET_ITEM_PADDING
from arkestra_utilities.output_libraries.plugin_widths import get_placeholder_width, calculate_container_width
from arkestra_utilities.admin_mixins import AutocompleteMixin, SupplyRequestMixin
@@ -22,8 +22,6 @@
from models import FilerImage, ImageSetItem, ImageSetPlugin
-IMAGESET_ITEM_PADDING = getattr(settings, "IMAGESET_ITEM_PADDING", 10)
-
# a dictionary to show how many items per row depending on the number of items
LIGHTBOX_COLUMNS = {1:1, 2:2, 3:3, 4:4, 5:5, 6:3, 7:4, 8:4, 9:3, 10:5, 11:4, 12:4, 13:5, 14:5, 15:5, 16:4, 17:6, 18:6, 19:5, 20:5, 21:6, 22:6, 23:6, 24:6, 25:5 }
@@ -339,6 +337,8 @@ class ImageSetItemEditor(SupplyRequestMixin, admin.StackedInline, AutocompleteMi
# related_search_fields = ['destination_content_type']
model=ImageSetItem
extra=1
+
+
fieldset_basic = ('', {'fields': (('image',),)})
fieldset_advanced = ('Caption', {
'fields': (( 'auto_image_title', 'manual_image_title'), ( 'auto_image_caption', 'manual_image_caption'),),
@@ -380,16 +380,26 @@ class ImageSetPublisher(SupplyRequestMixin, CMSPluginBase):
raw_id_fields = ('image',)
inlines = (ImageSetItemEditor,)
admin_preview = False
- fieldset_basic = ('Size & proportions', {'fields': (('kind', 'width', 'aspect_ratio',),)})
+ fieldset_basic = ('Size & proportions', {'fields': ('notes', ('kind', 'width', 'aspect_ratio',),)})
fieldset_advanced = ('Advanced', {'fields': (( 'float', 'height'),), 'classes': ('collapse',)})
fieldset_items_per_row = ('For Multiple and Lightbox plugins only', {'fields': ('items_per_row',), 'classes': ('collapse',)})
fieldsets = (fieldset_basic, fieldset_items_per_row, fieldset_advanced)
+ readonly_fields = ["notes", ]
def __init__(self, model = None, admin_site = None):
self.admin_preview = False
self.text_enabled = True
super(ImageSetPublisher, self).__init__(model, admin_site)
+ def notes(self,instance):
+ if not instance.imageset_item.count():
+ message = u"There are currently no items in this set."
+ elif instance.imageset_item.count() == 1:
+ message = u"There is currently only one item in this set."
+ else:
+ message = u"There are currently %s items in this set." % instance.imageset_item.count()
+ return message
+
def render(self, context, imageset, placeholder):
# don't do anything if there are no items in the imageset
View
1  arkestra_image_plugin/views.py
@@ -1,7 +1,6 @@
from __future__ import division
from django.shortcuts import render_to_response, get_object_or_404
-from django.conf import settings
from django.template import RequestContext
from easy_thumbnails.files import get_thumbnailer
View
2  arkestra_utilities/admin_mixins.py
@@ -61,6 +61,7 @@ def formfield_for_manytomany(self, db_field, request, **kwargs):
return super(AutocompleteMixin, self).formfield_for_manytomany(db_field, request, **kwargs)
+
class InputURLMixin(forms.ModelForm):
input_url = forms.CharField(max_length=255, required = False,
help_text=u"Enter the URL of an external item that you want <strong>automatically</strong> added to the database, but first check carefully using <strong>External URL</strong> (above) to make sure it's really not there.",
@@ -85,5 +86,6 @@ class InputURLMixin(forms.ModelForm):
'url': ('If this is an external item', {'fields': ('external_url', 'input_url',)}),
'slug': ('If this is an internal item', {'fields': ('slug',)}),
'location': ('', {'fields': ('precise_location', 'access_note',)}),
+ 'address_report': ('', {'fields': ('address_report',)}),
'email': ('', {'fields': ('email',)}),
}
View
10 arkestra_utilities/context_processors.py
@@ -1,12 +1,12 @@
-from django.conf import settings
+from arkestra_utilities.settings import PAGE_TITLE_HEADING_LEVEL, IN_BODY_HEADING_LEVEL, IN_BODY_HEADING_LEVEL, MULTIPLE_ENTITY_MODE
def arkestra_templates(request):
"""
Adds useful Arkestra information to the context.
"""
return {
- 'PAGE_TITLE_HEADING_LEVEL': settings.PAGE_TITLE_HEADING_LEVEL,
- 'IN_BODY_HEADING_LEVEL': settings.IN_BODY_HEADING_LEVEL,
- "SHOW_EVENT_TYPES": settings.SHOW_EVENT_TYPES,
- "MULTIPLE_ENTITY_MODE": settings.MULTIPLE_ENTITY_MODE,
+ 'PAGE_TITLE_HEADING_LEVEL': PAGE_TITLE_HEADING_LEVEL,
+ 'IN_BODY_HEADING_LEVEL': IN_BODY_HEADING_LEVEL,
+ "SHOW_EVENT_TYPES": IN_BODY_HEADING_LEVEL,
+ "MULTIPLE_ENTITY_MODE": MULTIPLE_ENTITY_MODE,
}
View
51 arkestra_utilities/generic_models.py
@@ -1,20 +1,22 @@
+from datetime import datetime
+
from django.utils.translation import ugettext_lazy as _
from django.db import models
-from django.conf import settings
from django.contrib.contenttypes.models import ContentType
-from datetime import datetime
+
+from django.conf import settings
+
from cms.models.fields import PlaceholderField
from filer.fields.image import FilerImageField
+from arkestra_utilities.settings import PLUGIN_HEADING_LEVELS, PLUGIN_HEADING_LEVEL_DEFAULT
+
from links.models import ObjectLink
-from contacts_and_people.models import Entity, Person, default_entity_id, default_entity
+from contacts_and_people.models import Entity, Person
from contacts_and_people.templatetags.entity_tags import work_out_entity
-PLUGIN_HEADING_LEVELS = settings.PLUGIN_HEADING_LEVELS
-PLUGIN_HEADING_LEVEL_DEFAULT = settings.PLUGIN_HEADING_LEVEL_DEFAULT
-
class ArkestraGenericModel(models.Model):
class Meta:
abstract = True
@@ -35,11 +37,15 @@ class Meta:
image = FilerImageField(null=True, blank=True)
# universal plugin fields
- hosted_by = models.ForeignKey(Entity, default=default_entity_id,
+ hosted_by = models.ForeignKey(Entity, default=Entity.objects.default_entity_id(),
related_name='%(class)s_hosted_events', null=True, blank=True,
help_text=u"The entity responsible for publishing this item")
- publish_to = models.ManyToManyField(Entity, null=True, blank=True, related_name="%(class)s_publish_to",
- help_text=u"Use these sensibly - don't send minor items to the home page, for example")
+ publish_to = models.ManyToManyField(
+ Entity, verbose_name="Also publish to",
+ null=True, blank=True,
+ related_name="%(class)s_publish_to",
+ help_text=u"Use these sensibly - don't send minor items to the home page, for example.",
+ )
please_contact = models.ManyToManyField(Person, related_name='%(class)s_person',
help_text=u"The person to whom enquiries about this should be directed",
null=True, blank=True)
@@ -66,28 +72,31 @@ def get_importance(self):
@property
def get_hosted_by(self):
- return self.hosted_by or default_entity
+ return self.hosted_by or Entity.objects.base_entity()
@property
def get_template(self):
- return self.get_hosted_by.get_template()
+ if self.get_hosted_by:
+ return self.get_hosted_by.get_template()
+ else:
+ return settings.CMS_TEMPLATES[0][0]
@property
def get_entity(self):
"""
Real-world information, can be None
"""
- return self.hosted_by or Entity.objects.get(id=default_entity_id)
+ return self.hosted_by or Entity.objects.get(id=Entity.objects.base_entity())
- @property
- def get_website(self):
- """
- for internal Arkestra purposes only
- """
- if self.get_entity:
- return self.get_entity.get_website
- else:
- return None
+ # @property
+ # def get_website(self):
+ # """
+ # for internal Arkestra purposes only
+ # """
+ # if self.get_entity:
+ # return self.get_entity.get_website
+ # else:
+ # return None
@property
def links(self):
View
4 arkestra_utilities/managers.py
@@ -4,9 +4,7 @@
from django.db import models
from django.db.models import Q
-from django.conf import settings
-
-MULTIPLE_ENTITY_MODE = settings.MULTIPLE_ENTITY_MODE
+from arkestra_utilities.settings import MULTIPLE_ENTITY_MODE
class ArkestraGenericModelManager(models.Manager):
def get_by_natural_key(self, slug):
View
11 arkestra_utilities/menu.py
@@ -11,11 +11,10 @@
from datetime import datetime
-main_news_events_page_list_length = settings.MAIN_NEWS_EVENTS_PAGE_LIST_LENGTH
-arkestra_menus = settings.ARKESTRA_MENUS
+from arkestra_utilities.settings import MAIN_NEWS_EVENTS_PAGE_LIST_LENGTH, ARKESTRA_MENUS
-for menu in arkestra_menus:
+for menu in ARKESTRA_MENUS:
if menu["cms_plugin_model_name"]:
plugin = __import__(menu["plugins_module"], globals(), locals(), [menu["cms_plugin_model_name"],], -1)
menu["cms_plugin_model"] = getattr(plugin, menu["cms_plugin_model_name"])
@@ -68,7 +67,7 @@ def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb):
self.request = request
- if arkestra_menus and not post_cut:
+ if ARKESTRA_MENUS and not post_cut:
key = "ArkestraPages.modify()" + request.path + "pre_cut"
cached_pre_cut_nodes = cache.get(key, None)
if cached_pre_cut_nodes:
@@ -84,7 +83,7 @@ def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb):
node.entity = False
else:
node.entity = page.entity.all()[0]
- for menu in arkestra_menus:
+ for menu in ARKESTRA_MENUS:
self.do_menu(node, menu, node.entity)
# self.create_new_node(
@@ -116,7 +115,7 @@ def do_menu(self, node, menu, entity):
# create an instance of the plugin class editor with appropriate attributes
instance = cms_plugin_model.model(
entity = entity,
- limit_to = main_news_events_page_list_length
+ limit_to = MAIN_NEWS_EVENTS_PAGE_LIST_LENGTH
)
instance.type = "menu"
instance.view = "current"
View
2  arkestra_utilities/models.py
@@ -35,8 +35,6 @@ def __unicode__(self):
# from contacts_and_people.models import Entity #, Person, default_entity_id
# from contacts_and_people.templatetags.entity_tags import work_out_entity
-# PLUGIN_HEADING_LEVELS = settings.PLUGIN_HEADING_LEVELS
-# PLUGIN_HEADING_LEVEL_DEFAULT = settings.PLUGIN_HEADING_LEVEL_DEFAULT
#
# class ArkestraGenericModel(models.Model):
# class Meta:
View
103 arkestra_utilities/settings.py
@@ -1,3 +1,5 @@
+from django.conf import settings
+
# *Don't* be tempted to change anything in this file.
# If you do need to change a setting, copy it to your project's settings
# file, and change it there; it'll override the setiing here.
@@ -11,7 +13,7 @@
# 2. make sure it's correct
# 3. very rarely, you might have to change it to keep it correct
-ARKESTRA_BASE_ENTITY = None
+ARKESTRA_BASE_ENTITY = getattr(settings, "ARKESTRA_BASE_ENTITY", None)
# MULTIPLE_ENTITY_MODE is for projects hosting the site of more than one entity
# This does not necessarily entail a site for complex organisation,
@@ -19,44 +21,60 @@
# news and events items to particular entities for example requires
# MULTIPLE_ENTITY_MODE to be True
-MULTIPLE_ENTITY_MODE = True
+MULTIPLE_ENTITY_MODE = getattr(settings, "MULTIPLE_ENTITY_MODE", True)
+
+DEFAULT_CONTACTS_PAGE_TITLE = getattr(settings, "DEFAULT_CONTACTS_PAGE_TITLE", "Contacts & people")
+DEFAULT_NEWS_PAGE_TITLE = getattr(settings, "DEFAULT_NEWS_PAGE_TITLE", "News & events")
+DEFAULT_VACANCIES_PAGE_TITLE = getattr(settings, "DEFAULT_VACANCIES_PAGE_TITLE", "Vacancies & studentships")
+DEFAULT_PUBLICATIONS_PAGE_TITLE = getattr(settings, "DEFAULT_PUBLICATIONS_PAGE_TITLE", "Publications")
# -------- News & Events ----------------------
# How many items should be displayed on main news & events pages,
# such as /news-and-events
-MAIN_NEWS_EVENTS_PAGE_LIST_LENGTH = 6
+MAIN_NEWS_EVENTS_PAGE_LIST_LENGTH = getattr(settings, "MAIN_NEWS_EVENTS_PAGE_LIST_LENGTH", 6)
# the age in days at which items can be considered to have expired and should be archived
-AGE_AT_WHICH_ITEMS_EXPIRE = 180
+AGE_AT_WHICH_ITEMS_EXPIRE = getattr(settings, "AGE_AT_WHICH_ITEMS_EXPIRE", 180)
# in All forthcoming events lists, gather top events together
-COLLECT_TOP_ALL_FORTHCOMING_EVENTS = True
+COLLECT_TOP_ALL_FORTHCOMING_EVENTS = getattr(settings, "COLLECT_TOP_ALL_FORTHCOMING_EVENTS", True)
# show event type (e.g. "Seminar")
-SHOW_EVENT_TYPES = False
+SHOW_EVENT_TYPES = getattr(settings, "SHOW_EVENT_TYPES", False)
+
+STANDARD_FEED_ENTRY_COUNT = getattr(settings,'STANDARD_FEED_ENTRY_COUNT', 5)
+
+NEWS_AND_EVENTS_LAYOUT = getattr(settings, "NEWS_AND_EVENTS_LAYOUT", "sidebyside")
# -------- Date formats ----------------------
-ARKESTRA_DATE_FORMAT={
+DATE_FORMAT = getattr(settings, "ARKESTRA_DATE_FORMATS", "jS F Y")
+
+ARKESTRA_DATE_FORMATS = getattr(settings, "ARKESTRA_DATE_FORMATS",
+ {
"date_groups": "F Y",
"not_this_year": "jS F Y",
"not_this_month": "jS F",
"this_month": "jS",
}
+ )
+# admin
+
+ENABLE_CONTACTS_AND_PEOPLE_AUTH_ADMIN_INTEGRATION = getattr(settings, "ENABLE_CONTACTS_AND_PEOPLE_AUTH_ADMIN_INTEGRATION", False)
# -------- Headings ----------------------
# global value for the heading level for page titles (e.g. entity names in entity pages)
-PAGE_TITLE_HEADING_LEVEL = 1
+PAGE_TITLE_HEADING_LEVEL = getattr(settings, "PAGE_TITLE_HEADING_LEVEL", 1)
# The default (typically, the next down from the PAGE_TITLE_HEADING_LEVEL)
-IN_BODY_HEADING_LEVEL = 2
-PLUGIN_HEADING_LEVEL_DEFAULT = 2
+IN_BODY_HEADING_LEVEL = getattr(settings, "IN_BODY_HEADING_LEVEL", 2)
+PLUGIN_HEADING_LEVEL_DEFAULT = getattr(settings, "PLUGIN_HEADING_LEVEL_DEFAULT", 2)
# The heading levels available to plugins
-PLUGIN_HEADING_LEVELS = (
+PLUGIN_HEADING_LEVELS = getattr(settings, "PLUGIN_HEADING_LEVELS", (
(0, u"No heading"),
# (1, u"Heading 1"), # assuming that your templates reserve <h1> for page titles, don't allow for plugins
(2, u"Heading 2"),
@@ -64,15 +82,27 @@
(4, u"Heading 4"),
(5, u"Heading 5"),
)
+ )
+# image processing
+
+IMAGESET_ITEM_PADDING = getattr(settings, "IMAGESET_ITEM_PADDING", 10) # should be relative to templates!
+
+PERMITTED_FILETYPES = getattr(settings, "PERMITTED_FILETYPES", ["pdf",])
+
+
+# links
+
+LINK_SCHEMA = getattr(settings, 'LINK_SCHEMA', {})
+
# The video processing system
-USE_CELERY_FOR_VIDEO_ENCODING = False
+USE_CELERY_FOR_VIDEO_ENCODING = getattr(settings, "USE_CELERY_FOR_VIDEO_ENCODING", False)
# -------- Django CMS ----------------------
-CMS_SEO_FIELDS = True
-CMS_MENU_TITLE_OVERWRITE = True
+CMS_SEO_FIELDS = getattr(settings, "CMS_SEO_FIELDS", True)
+CMS_MENU_TITLE_OVERWRITE = getattr(settings, "CMS_MENU_TITLE_OVERWRITE", True)
# -------- Menus ----------------------
@@ -84,53 +114,18 @@
from vacancies_and_studentships.menu import menu_dict as vacancies_and_studentships_menu
-ARKESTRA_MENUS = [
+ARKESTRA_MENUS = getattr(settings, "ARKESTRA_MENUS", [
news_and_events_menu,
contacts_and_people_menu,
vacancies_and_studentships_menu,
]
+ )
# Do you want all menu branches to expand?
-EXPAND_ALL_MENU_BRANCHES = False
-
-# -------- Semantic editor ----------------------
-
-# ensure that the highest_page_body_heading_level is made available below
-
-WYM_CONTAINERS = ",\n".join([
- "{'name': 'P', 'title': 'Paragraph', 'css': 'wym_containers_p'}",
-# "{'name': 'H1', 'title': 'Heading_1', 'css': 'wym_containers_h1'}", # I assume you reserve <h1> for your page templates
- "{'name': 'H2', 'title': 'Heading_2', 'css': 'wym_containers_h2'}",
- "{'name': 'H3', 'title': 'Heading_3', 'css': 'wym_containers_h3'}",
- "{'name': 'H4', 'title': 'Heading_4', 'css': 'wym_containers_h4'}",
- "{'name': 'H5', 'title': 'Heading_5', 'css': 'wym_containers_h5'}",
- "{'name': 'H6', 'title': 'Heading_6', 'css': 'wym_containers_h6'}",
-# "{'name': 'PRE', 'title': 'Preformatted', 'css': 'wym_containers_pre'}",
- "{'name': 'BLOCKQUOTE', 'title': 'Blockquote', 'css': 'wym_containers_blockquote'}",
- # "{'name': 'TH', 'title': 'Table_Header', 'css': 'wym_containers_th'}", # not ready for this yet
-])
-
-WYM_TOOLS = ",\n".join([
- "{'name': 'Italic', 'title': 'Emphasis', 'css': 'wym_tools_emphasis'}", # and not italic
- "{'name': 'Bold', 'title': 'Strong', 'css': 'wym_tools_strong'}", # not 'bold'
- "{'name': 'InsertUnorderedList', 'title': 'Unordered_List', 'css': 'wym_tools_unordered_list'}",
- "{'name': 'InsertOrderedList', 'title': 'Ordered_List', 'css': 'wym_tools_ordered_list'}",
- "{'name': 'Indent', 'title': 'Indent', 'css': 'wym_tools_indent'}", # should be 'nest'
- "{'name': 'Outdent', 'title': 'Outdent', 'css': 'wym_tools_outdent'}", # should be 'unnest'
- # "{'name': 'Superscript', 'title': 'Superscript', 'css': 'wym_tools_superscript'}",
- # "{'name': 'Subscript', 'title': 'Subscript', 'css': 'wym_tools_subscript'}",
- "{'name': 'Undo', 'title': 'Undo', 'css': 'wym_tools_undo'}",
- "{'name': 'Redo', 'title': 'Redo', 'css': 'wym_tools_redo'}",
- # "{'name': 'Paste', 'title': 'Paste_From_Word', 'css': 'wym_tools_paste'}",
- "{'name': 'ToggleHtml', 'title': 'HTML', 'css': 'wym_tools_html'}",
- #"{'name': 'CreateLink', 'title': 'Link', 'css': 'wym_tools_link'}",
- #"{'name': 'Unlink', 'title': 'Unlink', 'css': 'wym_tools_unlink'}",
- #"{'name': 'InsertImage', 'title': 'Image', 'css': 'wym_tools_image'}",
- # "{'name': 'InsertTable', 'title': 'Table', 'css': 'wym_tools_table'}", # not ready for this yet
- #"{'name': 'Preview', 'title': 'Preview', 'css': 'wym_tools_preview'}",
-])
+EXPAND_ALL_MENU_BRANCHES = getattr(settings, "EXPAND_ALL_MENU_BRANCHES", False)
+
# -------- Django ----------------------
-LOGIN_REDIRECT_URL = "/admin/" #what happens after login - why is this required?
+LOGIN_REDIRECT_URL = getattr(settings, "LOGIN_REDIRECT_URL", "/admin/") #what happens after login - why is this required?
View
12 arkestra_utilities/static/cms/wymeditor/lang/en.js
@@ -15,12 +15,12 @@ WYMeditor.STRINGS['en'] = {
Table: 'Table',
HTML: 'Scary HTML view',
Paragraph: 'Paragraph',
- Heading_1: 'h1',
- Heading_2: 'h2',
- Heading_3: 'h3',
- Heading_4: 'h4',
- Heading_5: 'h5',
- Heading_6: 'h6',
+ Heading_1: 'Heading 1',
+ Heading_2: 'Heading 2',
+ Heading_3: 'Heading 3',
+ Heading_4: 'Heading 4',
+ Heading_5: 'Heading 5',
+ Heading_6: 'Heading 6',
Preformatted: 'Preformatted',
Blockquote: 'Blockquote',
Table_Header: 'Table Header',
View
BIN  arkestra_utilities/static/static.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
2  arkestra_utilities/templates/arkestra.html
@@ -51,7 +51,7 @@
<body {% block body_attributes %}{% endblock body_attributes %}>
{% cms_toolbar %}
- {% with placeholder_width=960 generic_main_width=523 sidebar_image_size="294x196" entity_image_size="445x384" sidebar_map_size="296x100" person_image_size="384x384" lightbox_max_dimension=600 plugin_thumbnail_size="75x75" %}
+ {% with placeholder_width=960 generic_main_width=523 sidebar_image_size="294x196" entity_image_size="445x384" person_entity_map_size="445x100" sidebar_map_size="296x100" person_image_size="460x460" lightbox_max_dimension=600 plugin_thumbnail_size="75x75" place_image_size="490x380" %}
{% block body_content %}
<div id="outer-wrapper">
{% block outer_wrapper_top %}{% endblock %}
View
53 arkestra_utilities/widgets/wym_editor.py
@@ -1,57 +1,10 @@
from os.path import join
+
from django import forms
-from django.conf import settings
from django.utils.safestring import mark_safe
+from django.conf import settings
-WYM_TOOLS = ",\n".join([
- "{'name': 'Italic', 'title': 'Emphasis', 'css': 'wym_tools_emphasis'}",
- "{'name': 'Bold', 'title': 'Strong', 'css': 'wym_tools_strong'}",
- "{'name': 'Superscript', 'title': 'Superscript', 'css': 'wym_tools_superscript'}",
- "{'name': 'Subscript', 'title': 'Subscript', 'css': 'wym_tools_subscript'}",
- "{'name': 'InsertOrderedList', 'title': 'Ordered_List', 'css': 'wym_tools_ordered_list'}",
- "{'name': 'InsertUnorderedList', 'title': 'Unordered_List', 'css': 'wym_tools_unordered_list'}",
- "{'name': 'Indent', 'title': 'Indent', 'css': 'wym_tools_indent'}",
- "{'name': 'Outdent', 'title': 'Outdent', 'css': 'wym_tools_outdent'}",
- "{'name': 'Undo', 'title': 'Undo', 'css': 'wym_tools_undo'}",
- "{'name': 'Redo', 'title': 'Redo', 'css': 'wym_tools_redo'}",
- "{'name': 'Paste', 'title': 'Paste_From_Word', 'css': 'wym_tools_paste'}",
- "{'name': 'ToggleHtml', 'title': 'HTML', 'css': 'wym_tools_html'}",
- #"{'name': 'CreateLink', 'title': 'Link', 'css': 'wym_tools_link'}",
- #"{'name': 'Unlink', 'title': 'Unlink', 'css': 'wym_tools_unlink'}",
- #"{'name': 'InsertImage', 'title': 'Image', 'css': 'wym_tools_image'}",
- #"{'name': 'InsertTable', 'title': 'Table', 'css': 'wym_tools_table'}",
- #"{'name': 'Preview', 'title': 'Preview', 'css': 'wym_tools_preview'}",
-])
-
-WYM_TOOLS = getattr(settings, "WYM_TOOLS", WYM_TOOLS)
-
-WYM_CONTAINERS = ",\n".join([
- "{'name': 'P', 'title': 'Paragraph', 'css': 'wym_containers_p'}",
- "{'name': 'H1', 'title': 'Heading_1', 'css': 'wym_containers_h1'}",
- "{'name': 'H2', 'title': 'Heading_2', 'css': 'wym_containers_h2'}",
- "{'name': 'H3', 'title': 'Heading_3', 'css': 'wym_containers_h3'}",
- "{'name': 'H4', 'title': 'Heading_4', 'css': 'wym_containers_h4'}",
- "{'name': 'H5', 'title': 'Heading_5', 'css': 'wym_containers_h5'}",
- "{'name': 'H6', 'title': 'Heading_6', 'css': 'wym_containers_h6'}",
- "{'name': 'PRE', 'title': 'Preformatted', 'css': 'wym_containers_pre'}",
- "{'name': 'BLOCKQUOTE', 'title': 'Blockquote', 'css': 'wym_containers_blockquote'}",
- "{'name': 'TH', 'title': 'Table_Header', 'css': 'wym_containers_th'}",
-])
-
-WYM_CONTAINERS = getattr(settings, "WYM_CONTAINERS", WYM_CONTAINERS)
-
-WYM_CLASSES = ",\n".join([
- "{'name': 'date', 'title': 'PARA: Date', 'expr': 'p'}",
- "{'name': 'hidden-note', 'title': 'PARA: Hidden note', 'expr': 'p[@class!=\"important\"]'}",
-])
-
-WYM_STYLES = ",\n".join([
- "{'name': '.hidden-note', 'css': 'color: #999; border: 2px solid #ccc;'}",
- "{'name': '.date', 'css': 'background-color: #ff9; border: 2px solid #ee9;'}",
-])
-
-WYM_CLASSES = getattr(settings, "WYM_CLASSES", WYM_CLASSES)
-WYM_STYLES = getattr(settings, "WYM_STYLES", WYM_STYLES)
+from cms.plugins.text.settings import WYM_TOOLS, WYM_CONTAINERS, WYM_CLASSES, WYM_STYLES
class WYMEditor(forms.Textarea):
class Media:
View
45 contacts_and_people/admin.py
@@ -25,6 +25,7 @@
from cms.admin.placeholderadmin import PlaceholderAdmin
from arkestra_utilities.admin_mixins import AutocompleteMixin, SupplyRequestMixin, InputURLMixin, fieldsets
+from arkestra_utilities.settings import ENABLE_CONTACTS_AND_PEOPLE_AUTH_ADMIN_INTEGRATION
HAS_PUBLICATIONS = 'publications' in settings.INSTALLED_APPS
@@ -88,6 +89,7 @@ class PhoneContactAdmin(admin.ModelAdmin):
pass
class PersonAndEntityAdmin(SupplyRequestMixin, AutocompleteMixin, ModelAdminWithTabsAndCMSPlaceholder):
+
def _media(self):
return super(AutocompleteMixin, self).media + super(ModelAdminWithTabsAndCMSPlaceholder, self).media
media = property(_media)
@@ -181,19 +183,32 @@ def action(modeladmin,request,queryset):
return (name, (action, name,"Add selected Person to %s as 'Member'" % (entity,)))
-class PersonAdmin(PersonAndEntityAdmin):
+class PersonAdmin(PersonAndEntityAdmin):
search_fields = ['given_name','surname','institutional_username',]
-
-
form = PersonForm
list_display = ( 'surname', 'given_name', 'image', 'get_entity', 'slug')
# list_editable = ('user',)
filter_horizontal = ('entities',)
prepopulated_fields = {'slug': ('title', 'given_name', 'middle_names', 'surname',)}
-
+ readonly_fields = ['address_report',]
+
+ # def __init__(self, model, admin_site):
+ # print self.readonly_fields
+ # super(PersonAdmin, self).__init__(model, admin_site)
+ # return
+
+ def address_report(self, instance):
+ if instance.building and instance.get_full_address == instance.get_entity.get_full_address:
+ return "Warning: this Person has the Specify Building field set, probably unnecessarily."
+ else:
+ return "%s" % (", ".join(instance.get_full_address)) or "<span class='errors'>Warning: this person has no address.</span>"
+
+ address_report.short_description = "Address"
+ address_report.allow_tags = True
+
name_fieldset = ('Name', {'fields': ('title', 'given_name', 'middle_names', 'surname',),})
override_fieldset = ('Over-ride default output', {
- 'fields': ('please_contact', 'override_entity', 'building',),
+ 'fields': ('please_contact', 'building',),
'classes': ('collapse',)
})
advanced_fieldset = (
@@ -205,11 +220,10 @@ class PersonAdmin(PersonAndEntityAdmin):
'fields': ('description',),
'classes': ('plugin-holder', 'plugin-holder-nopage',)
})
-
tabs = [
('Personal details', {'fieldsets': (name_fieldset, fieldsets["image"])}),
('Contact information', {
- 'fieldsets': (fieldsets["email"], fieldsets["location"], override_fieldset),
+ 'fieldsets': (fieldsets["email"], fieldsets["address_report"], fieldsets["location"], override_fieldset),
'inlines': [PhoneContactInline,]
}),
('Description', {'fieldsets': (description_fieldset,)}),
@@ -310,6 +324,15 @@ class EntityAdmin(PersonAndEntityAdmin):
prepopulated_fields = {
'slug': ('name',)
}
+ readonly_fields = ['address_report']
+
+ def address_report(self, instance):
+ if not instance.abstract_entity:
+ return "%s" % (", ".join(instance.get_full_address)) or "Warning: this Entity has no address."
+ else:
+ return "This is an abstract entity and therefore has no address"
+
+ address_report.short_description = "Address"
name_fieldset = ('Name', {'fields': ('name', 'short_name')})
website_fieldset = ('', {'fields': ('website',)})
@@ -348,7 +371,7 @@ class EntityAdmin(PersonAndEntityAdmin):
tabs = [
('Basic information', {'fieldsets': (name_fieldset, fieldsets["image"], website_fieldset, entity_hierarchy_fieldset)}),
- ('Location', {'fieldsets': (building_fieldset, fieldsets["location"],)}),
+ ('Location', {'fieldsets': (fieldsets["address_report"], building_fieldset, fieldsets["location"],)}),
('Contact', {
'fieldsets': (fieldsets["email"],),
'inlines': (PhoneContactInline,)
@@ -483,8 +506,8 @@ class BuildingAdmin(ModelAdminWithTabsAndCMSPlaceholder):
)
try:
- admin.site.register(models.Person,PersonAdmin)
-except AlreadyRegistered:
+ admin.site.register(models.Person, PersonAdmin)
+except admin.sites.AlreadyRegistered:
pass
admin.site.register(models.Building,BuildingAdmin)
@@ -495,7 +518,7 @@ class BuildingAdmin(ModelAdminWithTabsAndCMSPlaceholder):
# admin.site.register(models.PhoneContact,PhoneContactAdmin)
# ------------------------- admin hacks -------------------------
-if getattr(settings,"ENABLE_CONTACTS_AND_PEOPLE_AUTH_ADMIN_INTEGRATION", False):
+if ENABLE_CONTACTS_AND_PEOPLE_AUTH_ADMIN_INTEGRATION:
admin.site.unregister(User)
from django import template
from django.conf import settings
View
1  contacts_and_people/cms_plugins.py
@@ -1,4 +1,3 @@
-from django.conf import settings
from django.http import HttpResponseRedirect, HttpResponse
from django.utils.translation import ugettext as _
View
4 contacts_and_people/link_schemas.py
@@ -19,7 +19,7 @@ def short_text(self):
def description(self):
data = []
- data.append(smart_unicode(self.obj.get_role()))
+ data.append(smart_unicode(self.obj.get_role))
data.append(smart_unicode(self.obj.get_entity))
return ', '.join(data)
@@ -72,7 +72,7 @@ class BuildingWrapper(LinkWrapper):
search_fields = admin.BuildingAdmin.search_fields
def description(self):
- return self.obj.get_postal_address()
+ return ", ".join(self.obj.get_postal_address[1:])
def heading(self):
return "Places"
View
165 contacts_and_people/models.py
@@ -22,13 +22,12 @@
# from news_and_events.cms_plugins import CMSNewsAndEventsPlugin
from arkestra_utilities.mixins import URLModelMixin
-
+from arkestra_utilities.settings import MULTIPLE_ENTITY_MODE, ARKESTRA_BASE_ENTITY, DEFAULT_NEWS_PAGE_TITLE, DEFAULT_CONTACTS_PAGE_TITLE, DEFAULT_VACANCIES_PAGE_TITLE, DEFAULT_PUBLICATIONS_PAGE_TITLE
from links.models import ExternalLink
import news_and_events
-MULTIPLE_ENTITY_MODE = settings.MULTIPLE_ENTITY_MODE
-base_entity_id = settings.ARKESTRA_BASE_ENTITY
+base_entity_id = ARKESTRA_BASE_ENTITY
# Page = models.get_model('cms', 'Page')
# CMSPlugin = models.get_model('cms', 'CMSPlugin')
@@ -64,7 +63,7 @@ class Building(models.Model):
additional_street_address = models.CharField(help_text=u"If required",
max_length=100, null=True, blank=True)
postcode = models.CharField(max_length=9, null=True, blank=True)
- site = models.ForeignKey(Site)
+ site = models.ForeignKey(Site, related_name="place")
slug = models.SlugField(blank=True, help_text=u"Please leave blank/amend only if required",
max_length=255, null=True, unique=True)
image = FilerImageField(null=True, blank=True)
@@ -119,6 +118,7 @@ def get_name(self):
building_identifier = unicode(self.site) + ": " + self.postcode
return building_identifier
+ @property
def get_postal_address(self):
"""
Assembles the postal (external) parts of an address
@@ -134,7 +134,7 @@ def get_postal_address(self):
if self.additional_street_address:
address.append(self.additional_street_address)
if self.site.post_town:
- address.append(self.site.post_town + " " + self.postcode)
+ address.append(" ".join((item for item in (self.site.post_town, self.postcode) if item)))
elif self.postcode:
address.append(self.postcode)
return address
@@ -215,7 +215,7 @@ class EntityManager(TreeManager):
def get_by_natural_key(self, slug):
return self.get(slug=slug)
- def base_entity(self):
+ def base_entity(self):
try:
# are Entities available at all?
list(Entity.objects.all())
@@ -228,6 +228,7 @@ def base_entity(self):
# we managed to get Entity.objects.all()
# we don't use default_entity (or default_entity_id) in MULTIPLE_ENTITY_MODE
try:
+ # print "trying"
entity = Entity.objects.get(id = base_entity_id)
# it can't be found, maybe because of a misconfiguation or because we haven't added any Entities yet
except (Entity.DoesNotExist, DatabaseError), e:
@@ -238,6 +239,13 @@ def base_entity(self):
# print "** I successfully found a default entity:", entity
return entity
+ def default_entity_id(self):
+ if self.base_entity and not MULTIPLE_ENTITY_MODE:
+ return base_entity_id
+
+ def some_thing(self):
+ print "*********"
+
class Entity(MPTTModel, EntityLite, CommonFields):
objects=EntityManager()
short_name = models.CharField(blank=True, help_text="e.g. Haematology",
@@ -260,7 +268,8 @@ class Entity(MPTTModel, EntityLite, CommonFields):
)
news_page_menu_title = models.CharField(u"Title",
max_length= 50,
- default=getattr(settings, "DEFAULT_NEWS_PAGE_TITLE", "News & events"))
+ default = DEFAULT_NEWS_PAGE_TITLE
+ )
news_page_intro = PlaceholderField('body',
related_name="news_page_intro",
)
@@ -269,7 +278,8 @@ class Entity(MPTTModel, EntityLite, CommonFields):
)
contacts_page_menu_title = models.CharField(u"Title",
max_length=50,
- default=getattr(settings, "DEFAULT_CONTACTS_PAGE_TITLE", "Contacts & people"))
+ default = DEFAULT_CONTACTS_PAGE_TITLE,
+ )
contacts_page_intro = PlaceholderField('body',
related_name="contacts_page_intro",
help_text = "Text for the Contacts & people page"
@@ -280,7 +290,8 @@ class Entity(MPTTModel, EntityLite, CommonFields):
)
vacancies_page_menu_title = models.CharField(u"Title",
max_length=50,
- default=getattr(settings, "DEFAULT_VACANCIES_PAGE_TITLE", "Vacancies & studentships"))
+ default = DEFAULT_VACANCIES_PAGE_TITLE,
+ )
vacancies_page_intro = PlaceholderField('body',
related_name="vacancies_page_intro",
)
@@ -290,7 +301,8 @@ class Entity(MPTTModel, EntityLite, CommonFields):
default=False)
publications_page_menu_title = models.CharField(u"Title",
max_length=50,
- default=getattr(settings, "DEFAULT_CONTACTS_PAGE_TITLE", "Publications"))
+ default = DEFAULT_PUBLICATIONS_PAGE_TITLE,
+ )
class Meta:
verbose_name_plural = "Entities"
@@ -310,7 +322,8 @@ def get_absolute_url(self):
else:
return "/contact/"
- def get_real_ancestor(self):
+ @property
+ def _get_real_ancestor(self):
"""
Find the nearest non-abstract Entity amongst this Entity's ancestors
"""
@@ -318,36 +331,54 @@ def get_real_ancestor(self):
if not ancestor.abstract_entity:
return ancestor
- return None
+ @property
+ def get_building(self):
+ """
+ Return the Building for this Entity (or its nearest parent)
+ """
+ if self.abstract_entity:
+ return
+ elif self.building:
+ return self.building
+ else:
+ try:
+ return self._get_real_ancestor.get_building
+ except AttributeError:
+ return None
+
+ @property
+ def _get_institutional_address(self):
+ """
+ Lists the parts of an address within the institution (Section of YYY, Department of XXX and YYY, School of ZZZ)
+ """
+ if self.abstract_entity:
+ return
+ else:
+ ancestors = []
+ showparent = self.display_parent
+ for entity in self.get_ancestors(ascending = True).exclude(abstract_entity = True):
+ if showparent:
+ ancestors.append(entity)
+ showparent = entity.display_parent
+ return ancestors
- def get_address(self):
+ @property
+ def get_full_address(self):
"""
Returns the full address of the entity
"""
- entity = self
- if entity.abstract_entity:
- entity = self.get_real_ancestor()
- if entity:
- address = entity.get_institutional_address()
- building = entity.get_building()
+ if self.abstract_entity:
+ return []
+ else:
+ address = self._get_institutional_address
+ building = self.get_building
if building:
- if entity.building_recapitulates_entity_name:
- address.extend(building.get_postal_address()[1:])
+ if self.building_recapitulates_entity_name:
+ address.extend(building.get_postal_address[1:])
else:
- address.extend(building.get_postal_address())
+ address.extend(building.get_postal_address)
return address
- def get_institutional_address(self):
- """
- Lists the parts of an address within the institution (Section of YYY, Department of XXX and YYY, School of ZZZ)
- """
- ancestors = []
- showparent = self.display_parent
- for entity in self.get_ancestors(ascending = True).exclude(abstract_entity = True):
- if showparent:
- ancestors.append(entity)
- showparent = entity.display_parent
- return ancestors
@property
def get_website(self):
@@ -374,7 +405,7 @@ def get_website_url(self):
# try
return self.parent.get_website_url()
else: # except
- return default_entity.get_website
+ return Entity.objects.base_entity().get_website
def get_related_info_page_url(self, kind):
"""
@@ -384,7 +415,7 @@ def get_related_info_page_url(self, kind):
"""
if self.external_url:
return ""
- elif self == default_entity:
+ elif self == Entity.objects.base_entity():
return "/%s/" % kind
else:
return "/%s/%s/" % (kind, self.slug)
@@ -396,19 +427,8 @@ def get_template(self):
if self.get_website:
return self.get_website.get_template()
else:
- return default_entity.get_website.get_template()
+ return settings.CMS_TEMPLATES[0][0]
- def get_building(self):
- """
- Return the Building for this Entity (or its nearest parent)
- """
- if self.building:
- return self.building
- else:
- try:
- return self.parent.get_building()
- except AttributeError:
- return None
def get_contacts(self):
"""
@@ -551,7 +571,7 @@ class Person(PersonLite, CommonFields):
entities = models.ManyToManyField(Entity, related_name='people',
through='Membership', blank=True, null=True)
building = models.ForeignKey(Building, verbose_name='Specify building',
- help_text=u"Only required if this Person <strong>Home entity</strong> has a different address",
+ help_text=u"<strong>Only</strong> required if this Person's <strong>Home entity</strong> has a different address",
blank=True, null=True, on_delete=models.SET_NULL)
override_entity = models.ForeignKey(Entity, verbose_name='Specify entity',
help_text=u"<strong>Temporarily specify</strong> an entity for contact information - over-rides entity and postal address",
@@ -578,6 +598,7 @@ def get_absolute_url(self):
else:
return "/person/%s/" % self.slug
+ @property
def get_role(self):
"""
Returns a Membership object.
@@ -588,7 +609,11 @@ def get_role(self):
If it can't find any role, it returns None.
"""
- memberships = Membership.objects.filter(person = self, entity__abstract_entity = False, importance_to_person__gte = 2).order_by('-importance_to_person')
+ memberships = Membership.objects.filter(
+ person = self,
+ entity__abstract_entity = False,
+ importance_to_person__gte = 2).order_by('-importance_to_person'
+ )
if memberships:
return memberships[0]
else: # the poor person had no memberships
@@ -603,21 +628,35 @@ def get_entity(self):
"""
if self.override_entity and not self.override_entity.abstract_entity:
return self.override_entity
- elif self.get_role():
- return self.get_role().entity
+ elif self.get_role:
+ return self.get_role.entity
return None
- def get_address(self):
+ @property
+ def get_building(self):
+ """
+ Returns a Person's Building, if possible
+ """
+ if self.building:
+ return self.building
+ elif self.get_entity:
+ return self.get_entity.get_building
+
+ @property
+ def get_full_address(self):
"""
Works out a person's address, based on their home/best entity or information that overrides this
"""
if self.get_entity: # needs an entity to work
if self.building:
- address = self.get_entity.get_institutional_address()
- address.extend(self.building.get_postal_address())
+ address = self.get_entity._get_institutional_address
+ address.extend(self.building.get_postal_address)
return address
else:
- return self.get_entity.get_address()
+ return self.get_entity.get_full_address
+ else:
+ return []
+
def get_please_contact(self):
"""
@@ -719,8 +758,10 @@ def save(self, *args, **kwargs):
# if there's just one membership, make it home; if this one is home, make home on all the others false
memberships = Membership.objects.filter(person = self.person)
if self.importance_to_person == 5:
- for membership in memberships:
- if membership.importance_to_person == 5:
+ for membership in memberships:
+
+ if membership.importance_to_person == 5:
+
membership.importance_to_person = 4
super(Membership, membership).save()
self.importance_to_person = 5
@@ -787,11 +828,11 @@ class EntityMembersPluginEditor(CMSPlugin):
# default_entity_id is used to autofill the default entity where required, when MULTIPLE_ENTITY_MODE = False
# default_entity is used throughout the system
# make default_entity and default_entity_id available
-default_entity = Entity.objects.base_entity() # get it from the Entity custom manager method
-if default_entity and not MULTIPLE_ENTITY_MODE:
- default_entity_id = base_entity_id
-else:
- default_entity_id = None
+# default_entity = Entity.objects.base_entity() # get it from the Entity custom manager method
+# if default_entity and not MULTIPLE_ENTITY_MODE:
+# default_entity_id = base_entity_id
+# else:
+# default_entity_id = None
# crazymaniac's wild monkeypatch#
View
23 contacts_and_people/templates/admin/contacts_and_people/entity/change_list.html
@@ -1,5 +1,5 @@
{% extends "admin/change_list.html" %}
-{% load adminmedia admin_list i18n cms_admin js %}
+{% load adminmedia admin_list i18n cms_admin cms_js_tags %}
{% block title %}List of entities{% endblock %}
{% block bodyclass %}change-list{% endblock %}
@@ -12,13 +12,12 @@
{% block coltype %}flex{% endblock %}
{% block extrahead %}
-
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}cms/css/pages.css"/>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}cms/jstree/tree_component.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}cms/css/jquery.dialog.css" />
-
-<script type="text/javascript" src="{% admin_media_prefix %}js/jquery.min.js"></script>
+{{ block.super }}
+<script type="text/javascript" src="{% admin_static_url %}js/jquery.min.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}cms/js/csrf.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}cms/js/libs/jquery.livequery.js"></script>
@@ -32,6 +31,9 @@
<script type="text/javascript" src="{{ STATIC_URL }}cms/jstree/tree_component.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}cms/js/libs/jquery.ui.dialog.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}cms/js/libs/jquery.functional.js"></script>
+<script type="text/javascript" src="{{ STATIC_URL }}cms/js/libs/classy.min.js"></script>
+<script type="text/javascript" src="{{ STATIC_URL }}cms/js/plugins/cms.setup.js"></script>
+<script type="text/javascript" src="{{ STATIC_URL }}cms/js/plugins/cms.base.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}cms/js/change_list.js"></script>
@@ -43,7 +45,7 @@
{% block content %}
<script type="text/javascript">
//<![CDATA[
-(function($) {
+(function namespacing($) {
$(document).ready(function() {
{% if not cl.is_filtered %}
initTree();
@@ -54,12 +56,17 @@
$("#changelist-filter").toggle();
}
moveSuccess = function(node){
- var msg = $('<span class="success">{% trans "Successfully moved" %}</span>');
+ var msg = $({% javascript_string %}<span class="success">{% trans "Successfully moved" %}</span>{% end_javascript_string %});
node.append(msg);
msg.fadeOut(3000);
}
- moveError = function(node){
- var msg = $('<span class="success">{% trans "An error occured. Please reload the page" %}</span>');
+ moveError = function(node,message){
+ if(message && message!="error") {
+ var msg = $({% javascript_string %}<span class="success">{% end_javascript_string %}+message+{% javascript_string %}</span>{% end_javascript_string %});
+ }
+ else {
+ var msg = $({% javascript_string %}<span class="success">{% trans "An error occured. Please reload the page" %}</span>{% end_javascript_string %});
+ }
node.append(msg);
}
View
2  contacts_and_people/templates/admin/contacts_and_people/entity/change_list_tree.html
@@ -1,4 +1,4 @@
-{% load i18n entity_admin_tags %}
+{% load i18n entity_admin_tags cms_admin %}
<div id="sitemap" style="display:none">
<ul class="header">
<li>
View
2  contacts_and_people/templates/admin/contacts_and_people/entity/change_list_tree_item.html
@@ -1,4 +1,4 @@
- {% load i18n adminmedia %}
+{% load i18n adminmedia %}
<div class="cont">
<div class="col1">
<a href="{{ url }}{{ entity.id }}" class="title" title="edit">{{ entity.name }}</a>
View
4 contacts_and_people/templates/contacts_and_people/entity_contacts_and_people.html
@@ -17,9 +17,7 @@
<h{{ IN_BODY_HEADING_LEVEL }}>Contact information</h{{ IN_BODY_HEADING_LEVEL }}>
<dl>
{% include "includes/key_contacts.html" %}
- {% with address=entity.get_address %}
- {% include "includes/address.html" %}
- {% endwith %}
+ {% include "includes/address.html" %}
{% include "includes/precise_location.html" %}
{% include "includes/access_notes.html" %}
{% include "includes/email.html" %}
View
4 contacts_and_people/templates/contacts_and_people/person.html
@@ -33,10 +33,10 @@
<div class="row columns2">
<div class="column firstcolumn">
<div>
- {% if address or location or access_note or email or phone.all %}<h{{ IN_BODY_HEADING_LEVEL }}>Contact information</h{{ IN_BODY_HEADING_LEVEL }}>
+ {% if building or address or location or access_note or email or phone.all %}<h{{ IN_BODY_HEADING_LEVEL }}>Contact information</h{{ IN_BODY_HEADING_LEVEL }}>
<dl>
{% include "includes/address.html" %}
- {% if address %}{% include "includes/precise_location.html" %}{% endif %}
+ {% if full_address %}{% include "includes/precise_location.html" %}{% endif %}
{% include "includes/access_notes.html" %}
{% include "includes/email.html" %}
{% include "includes/phone_contacts.html" %}
View
4 contacts_and_people/templates/includes/access_notes.html
@@ -1,4 +1,4 @@
-{% if entity.access_note %}
+{% if access_note %}
<dt>Please note</dt>
- <dd>{{ entity.access_note }}</dd>
+ <dd>{{ access_note }}</dd>
{% endif %}
View
8 contacts_and_people/templates/includes/address.html
@@ -1,9 +1,11 @@
-{% if address %}
+{% if full_address %}
<dt>Address</dt>
<dd>
<strong>{{ entity }}</strong>
<br />
- {{ address|join:"<br />" }}
- {% if entity.building.map %}<p><a href="{{entity.building.get_absolute_url}}">map &amp; directions</a></p>{% endif %}
+ {{ full_address|join:"<br />" }}
+ {% if building.map %}<p><a href="{{ building.get_absolute_url }}">map &amp; directions<br />
+ <img alt="map" src="http://maps.googleapis.com/maps/api/staticmap?center={{ building.latitude }},{{ building.longitude }}&zoom={{ building.zoom }}&size={{ person_entity_map_size }}&markers=color:red|{{ building.latitude }},{{ building.longitude }}&sensor=false" />
+ </a></p>{% endif %}
</dd>
{% endif %}
View
4 contacts_and_people/templates/includes/precise_location.html
@@ -1,4 +1,4 @@
-{% if location %}
+{% if precise_location %}
<dt>Location</dt>
- <dd>{{ location }}</dd>
+ <dd>{{ precise_location }}</dd>
{% endif %}
View
253 contacts_and_people/tests.py
@@ -1,32 +1,239 @@
-"""
-This file demonstrates two different styles of tests (one doctest and one
-unittest). These will both pass when you run "manage.py test".
-
-Replace these with more appropriate tests for your application.
-"""
from django.test import TestCase
-from django.utils import unittest
-class PersonTestCase(unittest.TestCase):
- def setUp(self):
- self.lion = Animal.objects.create(name="lion", sound="roar")
- self.cat = Animal.objects.create(name="cat", sound="meow")
+from contacts_and_people.models import Site, Person, Building, Entity, Membership
- def testSpeaking(self):
- self.assertEqual(self.lion.speak(), 'The lion says "roar"')
- self.assertEqual(self.cat.speak(), 'The cat says "meow"')
+class EntityManagerTests(TestCase):
+ def setUp(self):
+ pass
+ def test_base_entity_with_empty_database(self):
+ """
+ test EntityManager.base_entity
+ """
+ # no Entities, should be None
+ self.assertEquals(Entity.objects.base_entity(), None)
-class SimpleTest(TestCase):
- def test_basic_addition(self):
+ def test_base_entity_with_one_entity(self):
"""
- Tests that 1 + 1 always equals 2.
+ test EntityManager.base_entity
"""
- self.failUnlessEqual(1 + 1, 2)
+ # one Entities, should be that
+ self.school = Entity(
+ name="School of Medicine",
+ slug="medicine",
+ )
+ self.school.save()
+ self.assertEquals(Entity.objects.base_entity(), self.school)
+
+
+class EntityTestObjectsMixin(object):
+ """
+ Create a set of inter-related objects that we'll use in a series of tests
+ """
+
+ def setUp(self):
+ # a geographical Site
+ self.cardiff = Site(
+ site_name="Main site",
+ post_town="Cardiff",
+ country="UK",
+ )
+ self.cardiff.save()
+
+ # a couple of Buildings on the Site
+ self.main_building = Building(
+ name="Main Building",
+ street="St Mary's Street",
+ site=self.cardiff,
+ )
+ self.main_building.save()
+
+ self.heart_testing_centre = Building(
+ name="Heart Testing Centre",
+ street="Queen Street",
+ site=self.cardiff,
+ )
+ self.heart_testing_centre.save()
+
+ # create some Entities in a hierarchy
+
+ # School of Medicine
+ # Departments (an abstract entity)
+ # Department of Cardiology
+ # Section of Heart Research
+ # Heart Testing Centre
+ # Department of Cardiology Student Centre
+ # Web editors (an abstract entity)
+
+ self.school = Entity(
+ name="School of Medicine",
+ building=self.main_building,
+ slug="medicine",
+ )
+ self.school.save()
+
+ self.departments = Entity(
+ name="departments",
+ parent=self.school,
+ slug="departments",
+ abstract_entity=True,
+ building=self.heart_testing_centre, # this should be ignored by everything!
+ )
+ self.departments.save()
+
+ self.department = Entity(
+ name="Department of Cardiology",
+ parent=self.departments,
+ slug="cardiology",
+ )
+ self.department.save()
+
+ self.section = Entity(
+ name="Section of Heart Research",
+ parent=self.department,
+ slug="heart-research",
+ )
+ self.section.save()
+
+ self.testing_centre = Entity(
+ name="Testing Centre",
+ parent=self.department,
+ slug="testing-centre",
+ building_recapitulates_entity_name=True,
+ building=self.heart_testing_centre,
+ )
+ self.testing_centre.save()
+
+ self.student_centre = Entity(
+ name="Department of Cardiology Student Centre",
+ parent=self.department,
+ slug="student-centre",
+ display_parent=False,
+ )
+ self.student_centre.save()
+
+ self.web_editors = Entity(
+ name="Group of web editors",
+ parent=self.school,
+ slug="web-editors",
+ abstract_entity=True,
+ )
+ self.web_editors.save()
+
+ # set up a Person - we will add memberships later in the tests
+ self.smith = Person()
+ self.smith.save()
+
+
+class ModelTests(EntityTestObjectsMixin, TestCase):
+ def test_entity_get_building(self):
+ """
+ test Entity.get_building
+ check that Entities report the correct Building
+ """
+
+ # get_building works when building is assigned
+ self.assertEquals(self.school.get_building, self.main_building)
+ # an abstract entity has no building *even if one is assigned*
+ self.assertEquals(self.departments.get_building, None)
+ # the section has no Building assigned so should inherit from its parent
+ self.assertEquals(self.section.get_building, self.main_building)
+ # the department has no Building assigned so should inherit from its real ancestor
+ self.assertEquals(self.department.get_building, self.main_building)
+
+ def test_entity_get_institutional_address(self):
+ """
+ test Entity.get_institutional_address
+ check that Entities report the correct internal addresses
+ """
+
+ # for section, should be a list of its ancestors excluding abstract entities
+ self.assertEquals(self.section._get_institutional_address, [self.department, self.school])
+ # for student_centre, should exclude department
+ self.assertEquals(self.student_centre._get_institutional_address, [self.school,])
+
+ def test_entity_get_full_address(self):
+ """
+ test Entity.get_full_address
+ check that Entities report the correct full addresses
+ """
+
+ # an entity with a building
+ self.assertEquals(self.school.get_full_address, [u'Main Building', u"St Mary's Street", u'Cardiff'])
+ # an abstract entity has no address
+ self.assertEquals(self.departments.get_full_address, [])
+ # abstract entity is skipped in address
+ self.assertEquals(self.department.get_full_address, [self.school, u'Main Building', u"St Mary's Street", u'Cardiff'])
+ # an entity that doesn't display its parent in the address
+ self.assertEquals(self.student_centre.get_full_address, [self.school, u'Main Building', u"St Mary's Street", u'Cardiff'])
+ # an entity with building_recapitulates_entity_name flag shares
+ # its name with the building & drops the 1st line of postal address
+ self.assertEquals(self.testing_centre.get_full_address, [self.department, self.school, u"Queen Street", u'Cardiff'])
+
+ def test_person_methods(self):
+ """
+ test Person methods: get_role, get_entity, get_building, get_full_address
+ check that Person reports the correct information in different circumstances
+ """
+
+ # smith has no Memberships
+ self.assertEquals(self.smith.get_role, None)
+ self.assertEquals(self.smith.get_entity, None)
+ self.assertEquals(self.smith.get_building, None)
+ self.assertEquals(self.smith.get_full_address, [])
+
+ # smith is a web editor and only has a membership of an abstract entity
+ smith_web_editor_membership = Membership(
+ person=self.smith,
+ entity=self.web_editors,
+ importance_to_person=5,
+ importance_to_entity=4,
+ role="Lead web editor",
+ )
+ smith_web_editor_membership.save()
+
+ self.assertEquals(self.smith.get_role, None)
+ self.assertEquals(self.smith.get_entity, None)
+ self.assertEquals(self.smith.get_building, None)
+ self.assertEquals(self.smith.get_full_address, [])
+
+ # smith's best entity so far is technician in the department
+ smith_department_membership = Membership(
+ person=self.smith,
+ entity=self.department,
+ importance_to_person=2, # note that it's not as important his other one
+ importance_to_entity=4,
+ role="Technician",
+ )
+ smith_department_membership.save()
+
+ self.assertEquals(self.smith.get_role, smith_department_membership)
+ self.assertEquals(self.smith.get_entity, self.department)
+ self.assertEquals(self.smith.get_building, self.main_building)
+ self.assertEquals(self.smith.get_full_address, [self.school, u'Main Building', u"St Mary's Street", u'Cardiff'])
+
+ # now smith has a better entity: school
+ smith_school_membership = Membership(
+ person=self.smith,
+ entity=self.school,
+ importance_to_person=5,
+ importance_to_entity=5,
+ role="Dean",
+ )
+ smith_school_membership.save()
-__test__ = {"doctest": """
-Another way to test that 1 + 1 is equal to 2.
+ self.assertEquals(self.smith.get_role, smith_school_membership)
+ self.assertEquals(self.smith.get_entity, self.school)
+ self.assertEquals(self.smith.get_building, self.main_building)
+ self.assertEquals(self.smith.get_full_address, [u'Main Building', u"St Mary's Street", u'Cardiff'])
+
+ # now smith's best entity will be department
+ smith_department_membership.importance_to_person = 5
+ smith_department_membership.save()
->>> 1 + 1 == 2
-True
-"""}
+ self.assertEquals(self.smith.get_role, smith_department_membership)
+ self.assertEquals(self.smith.get_entity, self.department)
+ self.assertEquals(self.smith.get_building, self.main_building)
+ self.assertEquals(self.smith.get_full_address, [self.school, u'Main Building', u"St Mary's Street", u'Cardiff'])
+ # check that his membership of school has been downgraded by the save()
+ self.assertEquals(Membership.objects.get(pk=smith_school_membership.pk).importance_to_person, 4)
View
1  contacts_and_people/urls.py
@@ -1,4 +1,3 @@
-from django.conf import settings
from django.conf.urls.defaults import patterns, include, url
urlpatterns = patterns('',
View
35 contacts_and_people/views.py
@@ -2,7 +2,7 @@
import django.http as http
from django.template import RequestContext
from django.shortcuts import render_to_response, get_object_or_404
-from models import Person, Building, Membership, Entity, default_entity
+from models import Person, Building, Membership, Entity
from links.link_functions import object_links
from django.conf import settings
@@ -13,7 +13,7 @@
from publications.models import BibliographicRecord
from publications.models import Researcher # required for publications
-def contacts_and_people(request, slug=getattr(default_entity, "slug", None)):
+def contacts_and_people(request, slug=getattr(Entity.objects.base_entity(), "slug", None)):
# general values needed to set up and construct the page and menus
entity = Entity.objects.get(slug=slug)
# for the menu, because next we mess up the path
@@ -29,6 +29,7 @@ def contacts_and_people(request, slug=getattr(default_entity, "slug", None)):
}
people, initials = entity.get_people_and_initials()
+
# are there Key People to show?
if entity.get_key_people(): # if so we will show a list of people with key roles, then a list of other people
people_list_heading = _(u"Also")
@@ -51,7 +52,8 @@ def contacts_and_people(request, slug=getattr(default_entity, "slug", None)):
"location": entity.precise_location,
"intro_page_placeholder": entity.contacts_page_intro,
"phone": entity.phone_contacts.all(),
-
+ "full_address" : entity.get_full_address,
+ "building" : entity.get_building,
"people": people,
"people_list_heading": people_list_heading,
"initials_list": initials,
@@ -59,7 +61,7 @@ def contacts_and_people(request, slug=getattr(default_entity, "slug", None)):
RequestContext(request),
)
-def people(request, slug=getattr(default_entity, "slug", None), letter=None):
+def people(request, slug=getattr(Entity.objects.base_entity(), "slug", None), letter=None):
"""
Responsible for lists of people
"""
@@ -115,29 +117,29 @@ def person(request, slug, active_tab=""):
person = get_object_or_404(Person,slug=slug)
person.links = object_links(person)
# we have a home_role, but we should also provide a role, even where it's good enough to give us an address
- home_role = person.get_role()
+ home_role = person.get_role
if home_role:
entity = home_role.entity
entity = person.get_entity # don't rely on home_role.entity - could be None or overridden
- address = person.get_address()
+ building = person.get_building
contact = person.get_please_contact()
email = contact.email
phone = contact.phone_contacts.all()
- if person.override_entity or person.please_contact:
- location = None
+ if person.please_contact:
+ precise_location = None
else:
- location = person.precise_location
- access_note = person.access_note
+ precise_location = person.precise_location
+ access_note = person.access_note
if home_role:
description = ", ".join((home_role.__unicode__(), entity.__unicode__()))
request.current_page = entity.get_website
else:
- description = default_entity.__unicode__()
- request.current_page = default_entity.get_website
+ description = Entity.objects.base_entity().__unicode__()
+ request.current_page = Entity.objects.base_entity().get_website
meta = {
"description": ": ".join((person.__unicode__(), description))
@@ -147,7 +149,7 @@ def person(request, slug, active_tab=""):
template = entity.get_template()
else: # no memberships, no useful information
# print "no memberships, no useful information"
- template = default_entity.get_template()
+ template = Entity.objects.base_entity().get_template()
tabs_dict = { # information for each kind of person tab
"default": {
@@ -216,11 +218,12 @@ def person(request, slug, active_tab=""):
"home_role": home_role, # entity and position
"entity": entity,
"template": template, # from entity
- "address": address, # from entity
+ "building": building,
"email": email, # from person or please_contact
- "location": location, # from person, or None
+ "precise_location": precise_location, # from person, or None
"contact": contact, # from person or please_contact
"phone": phone,
+ "full_address" : person.get_full_address,
"access_note": access_note, # from person
"tabs": tabs,
"tab_object": person,
@@ -299,7 +302,7 @@ def place(request, slug, active_tab=""):
meta = {
"description": meta_description_content,
}
- page = default_entity.get_website
+ page = Entity.objects.base_entity().get_website
request.current_page = page
template = page.get_template()
View
234 debian_installation.txt
@@ -0,0 +1,234 @@
+##############################################
+Setting up an Arkestra web server from scratch
+##############################################
+
+************************
+Basic Debian Linux setup
+************************
+
+
+Obtain Debian Linux
+===================
+
+Get a Debian network installer from http://www.debian.org (I used debian-6.0.5-amd64-netinst.iso).
+
+Copy it onto a USB flash drive:
+
+diskutil unmountDisk /dev/disk1 # unmount the volume
+cat Desktop/debian-6.0.5-amd64-netinst.iso | dd of=/dev/disk1 # copy the disk image
+
+Boot up the machine from the flash drive, and let Debian use its suggested defaults.
+
+When it comes to ask what software to install, tell it to install only the basic system utilities.
+
+
+Configure the system
+====================
+
+You'll need to login as root.
+
+apt-get install openssh-server
+
+Now you can login as from another machine instead of from the console. So do that now.
+
+As root:
+
+apt-get install sudo # now you can use sudo
+adduser daniele sudo # add your user account to the sudoers file
+
+Login as daniele:
+
+sudo locale-gen en_GB.UTF-8 # set up locales
+sudo /usr/sbin/update-locale LANG=en_GB.UTF-8
+
+
+sudo apt-get update # shouldn't really be necessary, as this is a new install
+sudo aptitude safe-upgrade
+
+# installing handy things for compiling and building
+sudo aptitude install build-essential linux-headers-`uname -r`
+
+Make your life easier: having /sbin on the path provides access to commands otherwise reserved for root, and makes it possible to run stop-start-daemon in the fcgi script below.
+
+pico ~/.profile # edit the user's profile
+
+# add:
+PATH=$PATH:/new/path/one:/new/path/two
+export PATH
+
+
+***************************
+Install the server software
+***************************
+
+
+OpenSSH
+=======
+
+ssh-keygen # generate server keys
+
+Paste your own public key(s) into .ssh/authorized_keys.
+
+chmod 600 .ssh/authorized_keys
+
+sudo pico /etc/ssh/sshd_config # edit the sshd_config
+
+The following 3 lines
+
+* Port 22
+* PermitRootLogin yes
+* PasswordAuthentication yes
+
+Become
+
+* Port 8888
+* PermitRootLogin no
+* PasswordAuthentication no
+
+
+The firewall
+============
+
+sudo apt-get install ufw
+
+sudo ufw allow 8000/tcp # web dev
+sudo ufw allow 8001/tcp # web dev
+sudo ufw allow 8002/tcp # web dev
+sudo ufw allow 8003/tcp # web dev
+sudo ufw allow 8004/tcp # web dev
+sudo ufw allow 8005/tcp # web dev
+sudo ufw allow 8082/tcp # munin
+sudo ufw allow 8888/tcp # for ssh
+sudo ufw allow 80/tcp # standard http
+sudo ufw allow 22/tcp # ssh
+sudo ufw enable
+
+# restart sshd
+# make sure you do this as root, using sudo
+sudo /etc/init.d/ssh restart
+
+
+MySQL server
+============
+
+# install mysql server and python interface
+sudo apt-get install mysql-server python-mysqldb
+
+sudo pico /etc/mysql/my.cnf # edit system-wide MySQL conf
+
+add/change in the [mysqld] section:
+