models.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847
  1. from datetime import datetime, timedelta
  2. from taggit.managers import TaggableManager
  3. from taggit.models import Tag
  4. from django.db import models
  5. from django.utils.translation import ugettext_lazy as _
  6. from django.core.urlresolvers import reverse
  7. from django.contrib.contenttypes import generic
  8. from django.contrib.contenttypes.models import ContentType
  9. from django.contrib.sites.models import Site
  10. from django.contrib.sites.managers import CurrentSiteManager
  11. from django.contrib.auth.models import User, Group
  12. from django.conf import settings
  13. from newsroom.managers import *
  14. from django.utils.safestring import mark_safe
  15. from django.contrib.markup.templatetags import markup
  16. from django_inlines import inlines
  17. from darkroom.inlines import PhotoInline, GraphicInline
  18. inlines.registry.register('photo', PhotoInline)
  19. inlines.registry.register('graphic', GraphicInline)
  20. from darkroom.models import Gallery, Photo, Graphic, Movie, Slideshow
  21. from adzone.models import BannerAd, AdCategory
  22. from directory.models import Town, Place, Person
  23. from documents.models import Document, PDFDocument
  24. from remember.models import Announcement, InMemoriam, Service, Remembrance
  25. #from historical.models import HistoricArticle
  26. from django_extensions.db.models import TimeStampedModel
  27. from django_extensions.db.fields import AutoSlugField
  28. # Get relative media path
  29. try:
  30. NEWSROOM_DIR = getattr(settings, 'NEWSROOM_DIR', 'newsroom/')
  31. except:
  32. NEWSROOM_DIR = 'newsroom/'
  33. MARKUP_HTML = 'h'
  34. MARKUP_MARKDOWN = 'm'
  35. MARKUP_REST = 'r'
  36. MARKUP_TEXTILE = 't'
  37. MARKUP_OPTIONS = getattr(settings, 'ARTICLE_MARKUP_OPTIONS', (
  38. (MARKUP_MARKDOWN, _('Markdown')),
  39. (MARKUP_HTML, _('HTML/Plain Text')),
  40. (MARKUP_REST, _('ReStructured Text')),
  41. (MARKUP_TEXTILE, _('Textile'))
  42. ))
  43. MARKUP_DEFAULT = getattr(settings, 'COPY_MARKUP_DEFAULT', MARKUP_MARKDOWN)
  44. AUTO_TAG=getattr(settings, 'NEWSROOM_AUTO_TAG', True)
  45. class Paper(TimeStampedModel):
  46. name=models.CharField(_('name'), max_length=100)
  47. towns=models.ManyToManyField(Town)
  48. slug=AutoSlugField(_('slug'), populate_from='name', editable=True)
  49. description=models.TextField(_('description'), blank=True, null=True)
  50. class Meta:
  51. verbose_name = _('paper')
  52. verbose_name_plural = _('papers')
  53. ordering = ('name',)
  54. def __unicode__(self):
  55. return self.name
  56. class Link(TimeStampedModel):
  57. url = models.URLField('URL')
  58. title = models.CharField(_('title'), max_length=255)
  59. description=models.CharField(_('description'), max_length=200, blank=True, null=True)
  60. published=models.BooleanField(_('published'), default=False)
  61. published_on=models.DateTimeField(_('published on'))
  62. class Meta:
  63. verbose_name=_('link')
  64. verbose_name_plural=_('links')
  65. ordering=('created',)
  66. get_latest_by='published_on'
  67. def __unicode__(self):
  68. return self.title
  69. @models.permalink
  70. def get_absolute_url(self):
  71. return ('link_detail', (), {'slug': self.slug})
  72. class AuthorType(models.Model):
  73. title=models.CharField(_('name'), max_length=100)
  74. slug=AutoSlugField(_('slug'), populate_from='title', editable=True)
  75. description=models.TextField(_('description'), blank=True, null=True)
  76. class Meta:
  77. ordering=('title',)
  78. def __unicode__(self):
  79. return self.title
  80. @models.permalink
  81. def get_absolute_url(self):
  82. return ('author_type_detail', (), {'slug': self.slug})
  83. class Author(TimeStampedModel):
  84. '''Author model.'''
  85. name=models.CharField(_('name'), max_length=100)
  86. slug=AutoSlugField(_('slug'), populate_from='name', editable=True)
  87. fixed_initials=models.CharField(_('initials'), blank=True, null=True, max_length=5)
  88. type=models.ForeignKey(AuthorType)
  89. title=models.CharField(_('Title'), blank=True, null=True, max_length=100)
  90. organization=models.ForeignKey(Place, blank=True, null=True)
  91. about=models.TextField(_('about'), blank=True, null=True)
  92. user=models.ForeignKey(User, blank=True, null=True)
  93. def initials(self):
  94. if self.fixed_initials:
  95. return self.fixed_initials
  96. else:
  97. initials=''
  98. for i in self.name.split(" "):
  99. initials += i[0]
  100. return initials
  101. def __unicode__(self):
  102. return self.name
  103. @models.permalink
  104. def get_absolute_url(self):
  105. return ('nr-author-detail', (), {'type_slug': self.type.slug, 'slug': self.slug})
  106. class Meta:
  107. verbose_name=_('author')
  108. verbose_name_plural=_('authors')
  109. ordering=('name',)
  110. get_latest_by='created'
  111. class AttachedLink(models.Model):
  112. content_type = models.ForeignKey(ContentType)
  113. object_id = models.IntegerField(db_index=True)
  114. content_object = generic.GenericForeignKey()
  115. link=models.ForeignKey(Link)
  116. def __unicode__(self):
  117. return self.link.title
  118. class AttachedGraphic(models.Model):
  119. content_type = models.ForeignKey(ContentType)
  120. object_id = models.IntegerField(db_index=True)
  121. content_object = generic.GenericForeignKey()
  122. graphic = models.ForeignKey(Graphic)
  123. def __unicode__(self):
  124. return self.graphic.title
  125. class AttachedMovie(models.Model):
  126. content_type = models.ForeignKey(ContentType)
  127. object_id = models.IntegerField(db_index=True)
  128. content_object = generic.GenericForeignKey()
  129. movie=models.ForeignKey(Movie)
  130. def __unicode__(self):
  131. return self.movie.title
  132. class AttachedSlideshow(models.Model):
  133. content_type = models.ForeignKey(ContentType)
  134. object_id = models.IntegerField(db_index=True)
  135. content_object = generic.GenericForeignKey()
  136. slideshow=models.ForeignKey(Slideshow)
  137. def __unicode__(self):
  138. return self.slideshow.title
  139. class AttachedDocument(TimeStampedModel):
  140. content_type = models.ForeignKey(ContentType)
  141. object_id = models.IntegerField(db_index=True)
  142. content_object = generic.GenericForeignKey()
  143. document=models.ForeignKey(Document)
  144. class Meta:
  145. ordering = ['-created']
  146. get_latest_by = 'created'
  147. def __unicode__(self):
  148. return self.document.title
  149. def get_absolute_url(self):
  150. return self.document.get_absolute_url()
  151. def doc_dir(self):
  152. return os.path.dirname(self.get_file_filename())
  153. def remove_dirs(self):
  154. if os.path.isdir(self.doc_dir()):
  155. if os.listdir(self.doc_dir()) == []:
  156. os.removedirs(self.doc_dir())
  157. def delete(self):
  158. super(Document, self).delete()
  159. self.remove_dirs()
  160. class AttachedGallery(models.Model):
  161. content_type = models.ForeignKey(ContentType)
  162. object_id = models.IntegerField(db_index=True)
  163. content_object = generic.GenericForeignKey()
  164. gallery = models.ForeignKey(Gallery)
  165. def __unicode__(self):
  166. return self.gallery.title
  167. class Dateline(TimeStampedModel):
  168. '''Dateline model.
  169. A dateline specifies where a story's action takes place.
  170. The reason for the separate model is so that the string displayed
  171. can be custom, while the towns the dateline refers to are predetermined
  172. in the database. Thus a date line of: One Town and Anotehr Town or
  173. Our Community, can have specific locations related to them, while
  174. being able to be displayed per editorial spec.
  175. '''
  176. title=models.CharField(_('title'), max_length=100)
  177. slug=AutoSlugField(_('slug'), populate_from='title', editable=True)
  178. towns=models.ManyToManyField(Town)
  179. class Meta:
  180. ordering=('title',)
  181. def __unicode__(self):
  182. return self.title
  183. @models.permalink
  184. def get_absolute_url(self):
  185. return ('dateline_detail', (), {'slug':self.slug})
  186. MARKUP_HELP = _("""Select the type of markup you are using in this article.
  187. <ul>
  188. <li><a href="http://daringfireball.net/projects/markdown/basics" target="_blank">Markdown Guide</a></li>
  189. <li><a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html" target="_blank">ReStructured Text Guide</a></li>
  190. <li><a href="http://thresholdstate.com/articles/4312/the-textile-reference-manual" target="_blank">Textile Guide</a></li>
  191. </ul>""")
  192. class StandingHed(TimeStampedModel):
  193. '''Standing Hed model.
  194. In lieu of a proper column or blog application, this is a hack to get some
  195. stories to always be associated with one author or column.
  196. '''
  197. title=models.CharField(_('Title'), max_length=100)
  198. slug=AutoSlugField(_('Slug'), populate_from='title', editable=True)
  199. def __unicode__(self):
  200. return self.title
  201. class Copy(TimeStampedModel):
  202. '''Copy model.
  203. An abstract model for anything that can be copy.
  204. By default, we have two choices for heds.
  205. If a different web_hed isn't specified, we use the one from print.
  206. '''
  207. print_hed=models.CharField(_('print headline'), max_length=255)
  208. web_hed=models.CharField(_('web headline'), max_length=255)
  209. standing_hed=models.ForeignKey(StandingHed, blank=True, null=True)
  210. slug=AutoSlugField(_('slug'), populate_from='web_hed', editable=True)
  211. kicker=models.CharField(_('kicker'), max_length=255, blank=True, null=True)
  212. subhed=models.CharField(_('sub-headline'), max_length=255, blank=True, null=True)
  213. body=models.TextField(_('body'))
  214. summary=models.TextField(_('summary'), blank=True)
  215. markup = models.CharField(max_length=1, choices=MARKUP_OPTIONS, default=MARKUP_DEFAULT, help_text=MARKUP_HELP)
  216. published=models.BooleanField(_('publish'), default=False)
  217. published_on=models.DateTimeField(_('publish date'), default=datetime.now())
  218. paper_editions = models.ManyToManyField('PaperEdition', blank=True, null=True)
  219. sites=models.ManyToManyField(Site)
  220. view_count = models.PositiveIntegerField(default=0, editable=False)
  221. created_by=models.ForeignKey(User, editable=False)
  222. auto_tag=models.BooleanField(_('auto-tag'), default=True)
  223. tags = TaggableManager(blank=True)
  224. class Meta:
  225. abstract=True
  226. ordering=('-published_on', 'print_hed')
  227. get_latest_by='published_on'
  228. @property
  229. def hed(self):
  230. if self.web_hed:
  231. return self.web_hed
  232. else:
  233. return self.print_hed
  234. @property
  235. def paragraphs(self):
  236. """
  237. Return the paragraphs as a list
  238. """
  239. import re
  240. if self.markup=='m':
  241. import markdown
  242. html=markdown.markdown(self.body)
  243. elif self.markup=='t':
  244. import textile
  245. html=textile.textile(self.body)
  246. elif self.markup=='r':
  247. html='Cant figure out how to implement RST yet...'
  248. else:
  249. html= self.body
  250. return re.findall("(<p.+?>.+?</p>)", html, re.I | re.S)
  251. def do_auto_tag(self):
  252. """
  253. Performs the auto-tagging work if necessary.
  254. Returns True if an additional save is required, False otherwise.
  255. """
  256. found = False
  257. if self.auto_tag:
  258. import re
  259. # don't clobber any existing tags!
  260. existing_ids = [t.id for t in self.tags.all()]
  261. unused = Tag.objects.all()
  262. unused = unused.exclude(id__in=existing_ids)
  263. for tag in unused:
  264. regex = re.compile(r'\b%s\b' % tag.name, re.I)
  265. if regex.search(self.body) or regex.search(self.web_hed) or regex.search(self.print_hed)\
  266. or regex.search(self.subhed) or regex.search(self.kicker):
  267. self.tags.add(tag)
  268. found = True
  269. return found
  270. def do_scrub_rich_text(self):
  271. scrubbed=False
  272. if self.markup == 'h':
  273. import re
  274. from django.template.defaultfilters import removetags
  275. self.body = re.sub('\n', ' ', re.sub('style=".*?"', '', re.sub('align=".*?"', '', removetags(self.body, "font br"))))
  276. scrubbed=True
  277. return scrubbed
  278. def do_set_summary(self):
  279. summarized=False
  280. if (len(self.paragraphs[0].split(' ')) > 100) or (len(self.paragraphs) == 1):
  281. self.summary = self.paragraphs[0]
  282. summarized=True
  283. elif len(self.paragraphs)==0:
  284. summarized=False
  285. else:
  286. self.summary = self.paragraphs[0] + self.paragraphs[1]
  287. summarized=True
  288. return summarized
  289. def save(self, *args, **kwargs):
  290. self.do_set_summary()
  291. super(TimeStampedModel, self).save(*args, **kwargs)
  292. self.do_auto_tag()
  293. self.do_scrub_rich_text()
  294. super(TimeStampedModel, self).save(*args, **kwargs)
  295. class Story(Copy):
  296. '''Story model.
  297. A story is one of our primary types of content.
  298. '''
  299. STATUS_CHOICES=( (0, 'None'), (1, 'Primary'), (2, 'Secondary'), )
  300. dateline=models.ForeignKey(Dateline)
  301. authors=models.ManyToManyField(Author, blank=True, null=True)
  302. lead_photo=models.ForeignKey(Photo, blank=True, null=True)
  303. cropped_photo=models.ImageField(_('cropped photo'), blank=True, null=True,upload_to='newsroom/cropped_photos/')
  304. status=models.IntegerField(_('status'), choices=STATUS_CHOICES, default=0)
  305. towns=models.ManyToManyField(Town)
  306. weight=models.IntegerField(_('Weight'), null=True, blank=True, default=0,
  307. help_text='Stories weighted higher appear before those with lower weights.')
  308. press_release=models.BooleanField(_('press release'), default=False)
  309. source=models.ForeignKey(Place, blank=True, null=True, help_text="Source of original copy if is a press release.")
  310. attached_documents=generic.GenericRelation(AttachedDocument)
  311. attached_gallery=generic.GenericRelation(AttachedGallery)
  312. attached_graphic=generic.GenericRelation(AttachedGraphic)
  313. attached_movie=generic.GenericRelation(AttachedMovie)
  314. attached_slideshow=generic.GenericRelation(AttachedSlideshow)
  315. # Custom story manager (see manager.py)
  316. objects = StoryManager()
  317. on_site = CurrentSiteManager('sites')
  318. class Meta:
  319. verbose_name_plural=('stories')
  320. ordering=('-published_on', 'web_hed', 'print_hed')
  321. get_latest_by='published_on'
  322. def prod_slug(self):
  323. authors=''
  324. for a in self.authors.all():
  325. authors = a.initials()
  326. if authors:
  327. return u'%s-%s' % (authors, self.slug)
  328. else:
  329. return u'PR-%s' % (self.slug)
  330. def __unicode__(self):
  331. return self.hed
  332. def editions(self):
  333. list = ""
  334. for e in self.edition_set.all():
  335. list += e.paper.name +" " + e.published_on.strftime("%m-%d-%y") + "; "
  336. return list
  337. @property
  338. def author(self):
  339. #link='<a href="%s" title="%s">%s</a>'
  340. string='%s'
  341. #authors=[link % (a.get_absolute_url(), a, a.name) for a in self.authors.all()]
  342. authors = [string % (a.name) for a in self.authors.all()]
  343. if len(authors) > 1:
  344. author_string="%s and %s" % (", ".join(authors[:-1]), authors[-1])
  345. if len(authors) == 1:
  346. author_string=authors[0]
  347. else:
  348. author_string = ''
  349. return mark_safe(author_string)
  350. @models.permalink
  351. def get_absolute_url(self, site=''):
  352. return ('nr-story-detail', (), {
  353. 'year': self.published_on.year,
  354. 'month': self.published_on.strftime('%b').lower(),
  355. 'day': self.published_on.day,
  356. 'slug': self.slug})
  357. def get_admin_url(self):
  358. return u'admin/newsroom/story/%s/' % (str(self.id))
  359. def get_relative_url(self):
  360. return u'news/%s/%s/%s/%s' % (self.published_on.year, self.published_on.strftime('%b').lower(), self.published_on.day, self.slug)
  361. class EditorialType(models.Model):
  362. '''Editorial Type model.
  363. A type of editorial...
  364. '''
  365. name= models.CharField(_('name'), max_length=100)
  366. slug=AutoSlugField(_('slug'), populate_from='name', editable=True)
  367. description=models.TextField(_('description'), blank=True, null=True)
  368. def __unicode__(self):
  369. return self.name
  370. @models.permalink
  371. def get_absolute_url(self):
  372. return ('editorial_type_detail', (), {'slug':self.slug})
  373. class Editorial(Copy):
  374. '''Editorial model.
  375. Manages editorial content: letter to the editor, editor's notes, corrections.
  376. '''
  377. dateline=models.ForeignKey(Dateline, blank=True, null=True)
  378. type = models.ForeignKey(EditorialType)
  379. author=models.ForeignKey(Author, blank=True, null=True)
  380. citizen_authors=models.ManyToManyField(Person, blank=True, null=True)
  381. towns=models.ManyToManyField(Town)
  382. # Custom story manager (see manager.py)
  383. objects = EditorialManager()
  384. on_site = CurrentSiteManager('sites')
  385. class Meta:
  386. verbose_name_plural=('editorials')
  387. ordering=('-published_on', 'web_hed', 'print_hed')
  388. get_latest_by='published_on'
  389. def __unicode__(self):
  390. return self.web_hed
  391. def editions(self):
  392. list = ""
  393. for e in self.edition_set.all():
  394. list += e.paper.name +" " + e.published_on.strftime("%m-%d-%y") + "; "
  395. return list
  396. @models.permalink
  397. def get_absolute_url(self, site=''):
  398. return ('nr-editorial-detail', (), {
  399. 'year': self.published_on.year,
  400. 'month': self.published_on.strftime('%b').lower(),
  401. 'day': self.published_on.day,
  402. 'slug': self.slug})
  403. def get_relative_url(self):
  404. return u'%s/%s/%s/%s' % (self.published_on.year, self.published_on.strftime('%b').lower(), self.published_on.day, self.slug)
  405. class PaperEdition(TimeStampedModel):
  406. paper=models.ForeignKey(Paper)
  407. published_on=models.DateField(_('publish on'))
  408. published=models.BooleanField(_('published'), default=False)
  409. site=models.ManyToManyField(Site)
  410. class Meta:
  411. verbose_name=_('Paper edition')
  412. verbose_name_plural=_('Paper editions')
  413. ordering=('-published_on', 'paper')
  414. get_latest_by='published_on'
  415. def __unicode__(self):
  416. return u'%s published %s' % (self.paper, self.published_on)
  417. class WebEdition(TimeStampedModel):
  418. paper = models.ForeignKey(Paper)
  419. published_on = models.DateField(_('publish on'))
  420. published = models.BooleanField(_('published'), default=False)
  421. site = models.ForeignKey(Site)
  422. featured_photo = models.ForeignKey(Photo, null=True, blank=True, related_name="featured_photo")
  423. featured_story = models.ForeignKey(Story, related_name="featured_story", null=True, blank=True)
  424. stories = models.ManyToManyField(
  425. Story,
  426. blank=True,
  427. null=True,
  428. help_text="Include the featured story in this list of all stories in this edition.",
  429. limit_choices_to={'published_on__gte': datetime.today()-timedelta(days = 45)},
  430. )
  431. photos = models.ManyToManyField(
  432. Photo,
  433. blank=True,
  434. null=True,
  435. related_name="photos",
  436. limit_choices_to={'published_on__gte': datetime.today()-timedelta(days = 45)},
  437. )
  438. galleries = models.ManyToManyField(
  439. Gallery,
  440. blank=True,
  441. null=True,
  442. limit_choices_to={'published_on__gte': datetime.today()-timedelta(days = 45)},
  443. )
  444. movies = models.ManyToManyField(Movie, blank=True, null=True)
  445. slideshows = models.ManyToManyField(Slideshow, blank=True, null=True)
  446. editorials = models.ManyToManyField(Editorial, blank=True, null=True)
  447. pdfdocuments = models.ManyToManyField(PDFDocument, blank=True, null=True)
  448. remembrances = models.ManyToManyField(
  449. Remembrance,
  450. blank=True,
  451. null=True,
  452. limit_choices_to={'published_on__gte': datetime.today()-timedelta(days = 45)},
  453. )
  454. death_announcements = models.ManyToManyField(
  455. Announcement,
  456. blank=True,
  457. null=True,
  458. limit_choices_to={'published_on__gte': datetime.today()-timedelta(days = 45)},
  459. )
  460. memorial_servicesl = models.ManyToManyField(
  461. Service,
  462. blank=True,
  463. null=True,
  464. limit_choices_to={'published_on__gte': datetime.today()-timedelta(days = 45)},
  465. )
  466. inmemoriams = models.ManyToManyField(
  467. InMemoriam,
  468. blank=True,
  469. null=True,
  470. limit_choices_to={'published_on__gte': datetime.today()-timedelta(days = 45)},
  471. )
  472. objects=EditionManager()
  473. on_site = CurrentSiteManager('site')
  474. # per-paper managers
  475. patriot=PatriotEditionManager()
  476. advantages=AdvantagesEditionManager()
  477. packet=PacketEditionManager()
  478. compass=CompassEditionManager()
  479. class Meta:
  480. verbose_name=_('Web edition')
  481. verbose_name_plural=_('Web editions')
  482. ordering=('-published_on', 'paper')
  483. get_latest_by='published_on'
  484. def __unicode__(self):
  485. return u'Web edition of %s published on %s' % (self.paper, self.published_on)
  486. '''
  487. def save(self):
  488. if not self.id:
  489. super(Edition, self).save(*args, **kwargs)
  490. if self.published==True:
  491. self.stories.all().update(published=True)
  492. self.galleries.all().update(published=True)
  493. self.slideshows.all().update(published=True)
  494. self.movies.all().update(published=True)
  495. self.editorials.all().update(published=True)
  496. for p in self.photos.all():
  497. p.published=True
  498. p.save()
  499. for s in self.stories.all():
  500. if s.lead_photo:
  501. s.lead_photo.published=True
  502. s.lead_photo.save()
  503. s.photos.all().update(published=True)
  504. for g in s.galleries.all():
  505. for p in g.photos.all():
  506. p.published=True
  507. p.save()
  508. for g in self.galleries.all():
  509. for p in g.photos.all():
  510. p.published=True
  511. p.save()
  512. for a in self.death_announcements.all():
  513. a.published=True
  514. a.save()
  515. for s in self.memorial_services.all():
  516. s.published=True
  517. s.save()
  518. for i in self.inmemoriams.all():
  519. i.published=True
  520. i.save()
  521. for r in self.remembrances.all():
  522. r.published=True
  523. r.save()
  524. else:
  525. self.stories.all().update(published=False)
  526. self.galleries.all().update(published=False)
  527. self.photos.all().update(published=False)
  528. self.slideshows.all().update(published=False)
  529. self.movies.all().update(published=False)
  530. self.editorials.all().update(published=False)
  531. for s in self.stories.all():
  532. if s.lead_photo:
  533. s.lead_photo.published=False
  534. s.lead_photo.save()
  535. for g in self.galleries.all():
  536. for p in g.photos.all():
  537. p.published=False
  538. p.save()
  539. for a in self.death_announcements.all():
  540. a.published=False
  541. a.save()
  542. for s in self.memorial_services.all():
  543. s.published=False
  544. s.save()
  545. for i in self.inmemoriams.all():
  546. i.published=False
  547. i.save()
  548. for r in self.remembrances.all():
  549. r.published=False
  550. r.save()
  551. super(WebEdition, self).save(*args, **kwargs)
  552. '''
  553. class ArchiveItem(TimeStampedModel):
  554. '''Archive item model.
  555. A base class inherited by whatever content class we want to stuff in the archive.
  556. Items are associated with a particular paper, can expire and belong to "groups"
  557. The groups allow for very basic access control, so we could sell access to certain
  558. archive items.
  559. '''
  560. archived_on=models.DateTimeField(_('Archived on'), default=datetime.now())
  561. expires_on=models.DateTimeField(_('Expires on'), blank=True, null=True)
  562. groups=models.ManyToManyField(Group, blank=True, null=True)
  563. paper=models.ForeignKey(Paper)
  564. class ArchiveSection(ArchiveItem):
  565. '''Archive section model.
  566. This model is used to collect editioral content into a produced section
  567. The group can then be displayed on as an archive on a particular subject.
  568. It actually duplicates a lot of what's in a story because section objects
  569. don't exist in and of their own like stories or documents.
  570. '''
  571. GROUP_BY_CHOICES = (
  572. ('type', _('Type')),
  573. ('dline', _('Dateline')),
  574. ('both', _('Both')),
  575. )
  576. title=models.CharField(_('title'), max_length=200)
  577. slug=AutoSlugField(_('slug'), populate_from='title', editable=True)
  578. description=models.TextField(_('body'))
  579. published=models.BooleanField(_('published'), default=False)
  580. published_on=models.DateTimeField(_('published on'))
  581. thumbnail=models.ImageField(_('Thumbnail'), blank=True, null=True, upload_to="newsroom/archives/thumbnails/")
  582. lead_image=models.ImageField(_('Lead image'), blank=True, null=True, upload_to="newsroom/archives/lead_images/")
  583. dateline=models.ForeignKey(Dateline, blank=True, null=True)
  584. stories=models.ManyToManyField(Story, blank=True, null=True)
  585. editorials=models.ManyToManyField(Editorial, blank=True, null=True)
  586. pdf_documents=models.ManyToManyField(PDFDocument, blank=True, null=True)
  587. photos=models.ManyToManyField(Photo, blank=True, null=True)
  588. galleries=models.ManyToManyField(Gallery, blank=True, null=True)
  589. movies=models.ManyToManyField(Movie, blank=True, null=True)
  590. slideshows=models.ManyToManyField(Slideshow, blank=True, null=True)
  591. group_by=models.CharField(_('Group by'), choices=GROUP_BY_CHOICES,
  592. default='type', max_length=10)
  593. class Meta:
  594. verbose_name=_('archive section')
  595. verbose_name_plural=('archive sections')
  596. ordering=['-modified']
  597. def __unicode__(self):
  598. return u'%s' % self.title
  599. @models.permalink
  600. def get_absolute_url(self):
  601. return ('nr-archive-section-detail', (), {'year':self.published_on.year, 'slug':self.slug})
  602. def get_relative_url(self):
  603. return u'%s/%s/' % (self.published_on.year, self.slug)
  604. class ArchiveStory(ArchiveItem):
  605. '''Archive story model.
  606. A wrapper class for putting stories into our "archive" section.
  607. '''
  608. story=models.ForeignKey(Story)
  609. class Meta:
  610. verbose_name=_('Archived story')
  611. verbose_name_plural=_('Archived stories')
  612. def __unicode__(self):
  613. return u'%s' % self.story.hed()
  614. @models.permalink
  615. def get_absolute_url(self):
  616. return self.story.get_absolute_url
  617. class ArchiveMovie(ArchiveItem):
  618. '''Archive movie model.
  619. A wrapper class for putting movies into our "archive" section.
  620. '''
  621. movie=models.ForeignKey(Movie)
  622. class Meta:
  623. verbose_name=_('Archived movie')
  624. verbose_name_plural=_('Archived movies')
  625. def __unicode__(self):
  626. return u'%s' % self.movie.title()
  627. @models.permalink
  628. def get_absolute_url(self):
  629. return self.movie.get_absolute_url
  630. class ArchiveSlideshow(ArchiveItem):
  631. '''Archive slideshow model.
  632. A wrapper class for putting slideshows into our "archive" section.
  633. '''
  634. slideshow=models.ForeignKey(Slideshow)
  635. class Meta:
  636. verbose_name=_('Archived slideshow')
  637. verbose_name_plural=_('Archived slideshows')
  638. def __unicode__(self):
  639. return u'%s' % self.slideshow.title()
  640. @models.permalink
  641. def get_absolute_url(self):
  642. return self.slideshow.get_absolute_url
  643. class ArchiveDocument(ArchiveItem):
  644. '''Archive document model.
  645. We need a way to stick documents in the archive...
  646. '''
  647. document=models.ForeignKey(Document)
  648. class Meta:
  649. verbose_name=_('Archived document')
  650. verbose_name_plural=_('Archived documents')
  651. def __unicode__(self):
  652. return u'%s' % self.document.title
  653. @models.permalink
  654. def get_absolute_url(self):
  655. return self.document.get_absolute_url
  656. '''
  657. class PageType(models.Model):
  658. name = models.CharField(_('name'), max_length=150)
  659. slug=AutoSlugField(_('slug'), populate_from='name', editable=True)
  660. description = models.TextField(_('description'), blank=True, null=True)
  661. class Meta:
  662. verbose_name=_('page type')
  663. verbose_name_plural=('page types')
  664. def __unicode__(self):
  665. return u'%s' % (self.name)
  666. class Page(models.Model):
  667. number=models.IntegerField(_('number'), max_length=3)
  668. edition=models.ForeignKey(Edition)
  669. copy_inches=models.IntegerField(_('inches of copy'), max_length=3, blank=True, null=True, help_text='If left blank, will be calcuated by included copy.' )
  670. ad_inches=models.IntegerField(_('inches of ads'), max_length=3, blank=True, null=True, help_text='If left blank, will be calcuated by included ads.' )
  671. ad_group=models.ForeignKey(AdCategory, null=True, blank=True, help_text='Primary ad group for page.')
  672. ad_notes=models.TextField(_('ad notes'), null=True, blank=True)
  673. ad_spec_pos=models.TextField(_('ad special position'), null=True, blank=True)
  674. ads = models.ManyToManyField(BannerAd, blank=True, null=True)
  675. copy = models.ManyToManyField(Story, blank=True, null=True )
  676. photos = models.ManyToManyField(Photo, blank=True, null=True )
  677. type = models.ForeignKey(PageType, blank=True, null=True )
  678. class Meta:
  679. verbose_name=_('page')
  680. verbose_name_plural=('pages')
  681. unique_together = (("number", "edition"),)
  682. def __unicode__(self):
  683. return u'Page %s of %s' % (self.number, self.edition)
  684. '''