Josh VanderLinden avatar Josh VanderLinden committed 339933f Merge

Merge with article_attachments

Comments (0)

Files changed (8)

articles/__init__.py

 except:
     # the user probably doesn't have pygments installed
     pass
+

articles/admin.py

 from django.contrib.sites.models import Site
 from django.utils.translation import ugettext_lazy as _
 from forms import ArticleAdminForm
-from models import Tag, Article, ArticleStatus
+from models import Tag, Article, ArticleStatus, Attachment
+from models import Tag, Article, Attachment
 
 class ArticleStatusAdmin(admin.ModelAdmin):
     list_display = ('name', 'is_live')
     list_filter = ('is_live',)
     search_fields = ('name',)
 
+class AttachmentInline(admin.TabularInline):
+    model = Attachment
+    extra = 5
+    max_num = 15
+
 class ArticleAdmin(admin.ModelAdmin):
     list_display = ('title', 'status', 'author', 'publish_date', 'expiration_date', 'is_active')
     list_filter = ('author', 'status', 'is_active', 'publish_date', 'expiration_date', 'sites')
     search_fields = ('title', 'keywords', 'description', 'content')
     date_hierarchy = 'publish_date'
     form = ArticleAdminForm
+    inlines = [
+        AttachmentInline,
+    ]
 
     fieldsets = (
         (None, {'fields': ('title', 'content', 'tags', 'markup', 'status')}),

articles/management/commands/check_for_articles_from_email.py

+from base64 import b64decode
 from datetime import datetime
 from email.parser import FeedParser
 from email.utils import parseaddr, parsedate
 from django.core.management.base import BaseCommand
 from django.utils.translation import ugettext_lazy as _
 
-from articles.models import Article, MARKUP_HTML, MARKUP_MARKDOWN, MARKUP_REST, MARKUP_TEXTILE
+from articles.models import Article, Attachment, MARKUP_HTML, MARKUP_MARKDOWN, MARKUP_REST, MARKUP_TEXTILE
 
 MB_IMAP4 = 'IMAP4'
 MB_POP3 = 'POP3'
         if email.is_multipart():
             self.log('Extracting email contents from multipart message')
             for pl in email.get_payload():
-                if pl.get_content_type() in ('text/plain', 'text/html'):
+                if pl.get_content_type() in ('text/plain', 'text/html') and pl.get_filename() is None:
                     return pl.get_payload()
         else:
             return email.get_payload()
         created = []
         site = Site.objects.get_current()
 
-        # make sure we have a valid default markup
         ack = self.config.get('acknowledge', False)
         autopost = self.config.get('autopost', False)
+
+        # make sure we have a valid default markup
         markup = self.config.get('markup', MARKUP_HTML)
         if markup not in (MARKUP_HTML, MARKUP_MARKDOWN, MARKUP_REST, MARKUP_TEXTILE):
             markup = MARKUP_HTML
                 # try to grab the timestamp from the email message
                 publish_date = datetime.fromtimestamp(time.mktime(parsedate(email['Date'])))
             except StandardError, err:
-                self.log("An error occured when I tried to convert the email's timestamp into a datetime object: %s" % (err,))
+                self.log("An error occurred when I tried to convert the email's timestamp into a datetime object: %s" % (err,))
                 publish_date = datetime.now()
 
             # post the article
                 self.log('Error creating article: %s' % (err,), 0)
                 continue
             else:
+
+                # handle attachments
+                if email.is_multipart():
+                    files = [pl for pl in email.get_payload() if pl.get_filename() is not None]
+                    for att in files:
+                        obj = Attachment(
+                            article=article,
+                            caption=att.get_filename(),
+                        )
+                        obj.attachment.save(obj.caption, ChunkyString(att.get_payload()))
+                        obj.save()
+
                 created.append(num)
 
             if ack:
 
         return created
 
+class ChunkyString(str):
+    """Makes is possible to easily chunk attachments"""
+
+    def chunks(self):
+        i = 0
+        decoded = b64decode(self)
+        while True:
+            l = i
+            i += 1024
+            yield decoded[l:i]
+
+            if i > len(decoded):
+                raise StopIteration
+

articles/models.py

 from django.utils.translation import ugettext_lazy as _
 from datetime import datetime
 from base64 import encodestring
+import mimetypes
 import re
 import urllib
 
     class Meta:
         ordering = ('-publish_date', 'title')
 
+class Attachment(models.Model):
+    upload_to = lambda inst, fn: 'attach/%s/%s/%s' % (datetime.now().year, inst.article.slug, fn)
+
+    article = models.ForeignKey(Article, related_name='attachments')
+    attachment = models.FileField(upload_to=upload_to)
+    caption = models.CharField(max_length=255, blank=True)
+
+    @property
+    def filename(self):
+        return self.attachment.name.split('/')[-1]
+
+    @property
+    def content_type_class(self):
+        mt = mimetypes.guess_type(self.attachment.path)[0]
+        if mt:
+            content_type = mt.replace('/', '_')
+        else:
+            # assume everything else is text/plain
+            content_type = 'text_plain'
+
+        return content_type
+

articles/templates/articles/_article_content.html

     {{ article.rendered_content|safe }}
 </div>
 
+{% for att in article.attachments.all %}
+{% if forloop.first %}<div id="article-attachments">
+    <h3>Attachments</h3>
+    <ul>{% endif %}
+        <li id="attachment-{{ forloop.counter }}" class="attachment ct_{{ att.content_type_class }}">
+            <a href="{{ att.attachment.url }}" class="attachment-link">{{ att.filename }}</a>
+            <span class="attachment-size">Size: {{ att.attachment.size|filesizeformat }}</span>
+        </li>
+    {% if forloop.last %}</ul>
+</div>{% endif %}
+{% endfor %}
+

articles/templates/articles/_comments.html

 {% if disqus_forum %}
 <div id="disqus_thread"></div>
-<script type="text/javascript">
+<script type="application/javascript">
 var disqus_identifier = {{ article.id }};
 </script>
-<script type="text/javascript" src="http://disqus.com/forums/{{ disqus_forum }}/embed.js"></script>
+<script type="application/javascript" src="http://disqus.com/forums/{{ disqus_forum }}/embed.js"></script>
 <noscript><a href="http://disqus.com/forums/{{ disqus_forum }}/?url=ref">View the discussion thread.</a></noscript>
 <a href="http://disqus.com" class="dsq-brlink">Comments powered by <span class="logo-disqus">Disqus</span></a>
 {% endif %}

articles/templates/articles/_meta.html

     {% if article.use_addthis_button and article.addthis_username %}
     <!-- AddThis Button BEGIN -->
     <div>
-        <script type="text/javascript">var addthis_pub="{{ article.addthis_username }}";</script>
+        <script type="application/javascript">var addthis_pub="{{ article.addthis_username }}";</script>
         <a href="http://www.addthis.com/bookmark.php?v=20" onmouseover="return addthis_open(this, '', '[URL]', '[TITLE]')" onmouseout="addthis_close()" onclick="return addthis_sendto()"><img src="http://s7.addthis.com/static/btn/lg-share-en.gif" width="125" height="16" alt="Bookmark and Share" style="border:0"/></a>
-        <script type="text/javascript" src="http://s7.addthis.com/js/200/addthis_widget.js"></script>
+        <script type="application/javascript" src="http://s7.addthis.com/js/200/addthis_widget.js"></script>
     </div>
     <!-- AddThis Button END -->
     {% endif %}
 
-    <script type="text/javascript" src="http://tweetmeme.com/i/scripts/button.js"></script>
+    <script type="application/javascript" src="http://tweetmeme.com/i/scripts/button.js"></script>
 
     <h4>{% trans 'Tags' %}</h4>
     <p>{% if article.tags.count %}{% for tag in article.tags.all %}<a href="{{ tag.get_absolute_url }}">{{ tag.name }}</a> {% endfor %}{% else %}None{% endif %}</p>

articles/templates/articles/base.html

 {% block footer %}
 {{ block.super }}
 
-<script type="text/javascript">
+<script type="application/javascript">
 //<![CDATA[
 (function() {
     var links = document.getElementsByTagName('a');
         query += 'url' + i + '=' + encodeURIComponent(links[i].href) + '&';
     }
     }
-    document.write('<script charset="utf-8" type="text/javascript" src="http://disqus.com/forums/{{ disqus_forum }}/get_num_replies.js' + query + '"></' + 'script>');
+    document.write('<script charset="utf-8" type="application/javascript" src="http://disqus.com/forums/{{ disqus_forum }}/get_num_replies.js' + query + '"></' + 'script>');
 })();
 //]]>
 </script>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.