Commits

Anonymous committed a18fbb3 Draft

fixed bug and add features

Comments (0)

Files changed (18)

     - pep8
         https://github.com/jcrocholl/pep8/
 
+
 ChangeLog:
 
-- Delete a fucking genericView
+- fix display or not rubrique on public page
+- fix display or not article on public page
+- fix author_ip for visitor (gethostname and gethostbyname bug for Django 1.3)
+- fix comment error display
+- add comment markdown rendered
+- add queryset_iterator ex: http://djangosnippets.org/snippets/1949/ (see yield and gc)
+- fix path on settings.py
+- add admin wysiwyg
+
+--
+
+- delete a fuc**** genericView
 - adjust template display
 - fix pep8
 - fix UTF-8
 - add css
 - add comment
 
+--
+
+Bug:
+
+Bug add comment into admin, author_ip is incorrect
+Not possible to manager required field into admin
+
+What different ?
+get_list_or_404 or get_objects_or_404
+https://docs.djangoproject.com/en/1.4/topics/http/shortcuts/
+
+
+Todo:
+
+function _lookup_template can be optimized no ?
 
 
 Documentation:
 Code queryset_iterator #USE or NOT USE ???
 http://djangosnippets.org/snippets/1949/
 
+yield
+http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained
+gc
+http://www.digi.com/wiki/developer/index.php/Python_Garbage_Collection
 
 
-TODO
 
-If publish article template article
-comments code
-queryset_iterator
-

articles/admin.py

         }),
     )
 
+    class Media:
+        js = (
+            'https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojo/dojo.xd.js',
+            '/static/js/textareas.js')
+        css = {
+            'all': ('/static/css/bootstrap/css/editor.css',), }
+
+
 admin.site.register(Article, ArticleAdmin)

articles/models.py

 from django.conf import settings
 from django.contrib.auth.models import User
 from django.template.defaultfilters import slugify
+from datetime import datetime
 from rubriques.models import Rubrique
 import os
 
     """
     rubrique = models.ForeignKey(Rubrique)
     title = models.CharField('Title', max_length=200)
-    slug = models.SlugField('Slug', max_length=200, blank=True, editable=False)
+    slug = models.SlugField(
+        'Slug',
+        max_length=200,
+        unique=True,
+        blank=True,
+        editable=False)
     author = models.ForeignKey(User)
     tags = models.CharField('Tags', max_length=500)
     content = models.TextField('Content')
     image = models.FileField(upload_to=os.path.join(
         settings.MEDIA_ROOT, 'static/attachments'))
     published = models.BooleanField('Published')
-    publish_date = models.DateTimeField('Publish date')
-    expiry_date = models.DateTimeField('Expire date')
+    publish_date = models.DateTimeField('Publish date', default=datetime.now)
+    expiry_date = models.DateTimeField('Expire date', default=datetime.now)
     comments_enabled = models.BooleanField('Comments')
     choices = (
         ('never', _('Never')),

articles/views.py

 from django.conf import settings
 from django.http import HttpResponseRedirect
 from django.shortcuts import render_to_response, get_object_or_404
+from django.views.decorators.csrf import csrf_protect
 from django.template import Context, RequestContext
 from datetime import datetime
+from minicms.utils import queryset_iterator
 from articles.models import Article
 from comments.models import Comment, markup
 
 def index(request):
     """ List article
     """
-    entries = Article.objects.order_by('publish_date')
+    entries = queryset_iterator(Article.objects.order_by('publish_date'))
     context = {
         'home_articles_list': entries,
         'title': _('Articles')
         context_instance=RequestContext(request))
 
 
+@csrf_protect
 def detail(request, article_id):
     """ Detail article
     """
     entry = get_object_or_404(Article, pk=article_id)
-    list_comments = Comment.objects.filter(article=article_id)
+    list_comments = queryset_iterator(
+        Comment.objects.filter(article=article_id))
 
     # Add comment
     form_comment = Comment.CommentForm
         if form.is_valid():
             data = Comment(**form.cleaned_data)
             data.article_id = article_id
-            # data.rendered_content = markup(data.content, escape=True,
-            #                         small_headings=True)
+            data.rendered_content = markup(
+                data.content,
+                escape=True,
+                small_headings=True)
             data.published = True
+            data.author_ip = request.META['REMOTE_ADDR']
             data.publish_date = datetime.utcnow()
             data.save()
             return HttpResponseRedirect('#comments')
         else:
-            return HttpResponseRedirect('#comments')
+            form_comment = form
 
-    # Check for comment expiry
-    # comments_expired = False
-    # if entry.comments_expiry_date:
-    #    if entry.comments_expiry_date < datetime.now():
-    #        comments_expired = True
+    # DoubleCheck for comment expiry
+    comments_expired = False
+    if entry.comments_enabled:
+            comments_expired = True
 
     context = {
         'entry': entry,
         'list_comments': list_comments,
         'form': form_comment,
-        # 'comments_expired': comments_expired
+        'comments_expired': comments_expired
     }
 
     return render_to_response(

comments/models.py

 from django.conf import settings
 from django.contrib.auth.models import User
 from django.template.defaultfilters import slugify
-from socket import gethostname, gethostbyname
 from articles.models import Article
 import re
 
     author_ip = models.IPAddressField('IP Author', blank=True, editable=False)
     email = models.EmailField('Email')
     content = models.TextField('Content')
+    rendered_content = models.TextField('Render content')
     published = models.BooleanField('Published')
     publish_date = models.DateTimeField('Publish date')
 
     def __unicode__(self):
         return '%s' % (self.author)
 
-    def save(self, *args, **kwargs):
-        self.author_ip = gethostbyname(gethostname())
-        super(Comment, self).save(*args, **kwargs)
-
     class CommentForm(forms.Form):
         """ Comment form
         """
             """ Detect error
             """
             data = self.cleaned_data
-
             if not self._errors:
                 if data['author'] is None:
-                    raise ValidationError('Add author for this comment')
+                    raise forms.ValidationError('Add author for this comment')
                 if data['email'] is None:
-                    raise ValidationError('Add email for this comment')
+                    raise forms.ValidationError('Add email for this comment')
                 if data['content'] is None:
-                    raise ValidationError('Add content for this comment')
+                    raise forms.ValidationError('Add content for this comment')
 
             return data

minicms/settings.py

 
 # Django settings for minicms project.
 from .version import __version__
-import os
+import os.path
 
 DEBUG = True
 TEMPLATE_DEBUG = DEBUG
 
 PROJECT_ROOT = os.path.dirname(os.path.realpath(__file__))
 PROJECT_NAME = os.path.basename(PROJECT_ROOT)
+STATIC_ABSPATH = os.path.abspath('static')
 
 # Absolute filesystem path to the directory that will hold user-uploaded files.
 # Example: "/home/media/media.lawrence.com/media/"
     # Put strings here, like "/home/html/static" or "C:/www/django/static".
     # Always use forward slashes, even on Windows.
     # Don't forget to use absolute paths, not relative paths.
+    # '/media/Public/web/python/django/minicms/minicms/static',
+    STATIC_ABSPATH,
 )
 
 TEMPLATE_CONTEXT_PROCESSORS = (
 WSGI_APPLICATION = 'minicms.wsgi.application'
 
 TEMPLATE_DIRS = (
-    'minicms/templates'
+    '%s/templates' % PROJECT_NAME
 )
 
 INSTALLED_APPS = (

minicms/templates/minicms/articles/detail.html

         <hr />&gt; <strong>{% trans "Tags" %}:</strong> {{ entry.tags }}
     <hr />
     <img src="/{{ entry.image }}" alt="{% trans "Image from:" %} {{ article.title }}" /> 
-    <p>{{ entry.content }}</p>
+    <p>{{ entry.content|safe }}</p>
 
     <hr />
     {% if entry.expiry_date %}
     {% for comment in list_comments %}
         <div class="comment-info">
         <strong>{{ comment.author }}</strong> <span class="comment-time">{{ comment.publish_date|timesince }} ago</span>
-            <p>{{ comment.content|safe }}</p>
+            <p>{{ comment.rendered_content|safe }}</p>
         </div>
         </li>
     {% endfor %}

minicms/templates/minicms/privacy.html

 
 <p>If you require any more information or have any questions about our privacy policy, please feel free to contact us.</p>
 
-<p>At zouzzzz.info, the privacy of visitors is important to us. This privacy policy attempts to outline the types of personal information is received, collected and used by curiousconcept.com.</p>
+<p>At example.com, the privacy of visitors is important to us. This privacy policy attempts to outline the types of personal information is received, collected and used by curiousconcept.com.</p>
 
 <h2>Log Files</h2>
 
-<p>Like most other sites, zouzzzz.info makes use of log files. The information inside the log files includes internet protocol (IP) addresses, type of browser, Internet Service Provider (ISP), date/time stamp, referring/exit pages.</p>
+<p>Like most other sites, example.com makes use of log files. The information inside the log files includes internet protocol (IP) addresses, type of browser, Internet Service Provider (ISP), date/time stamp, referring/exit pages.</p>
 
-<p>This information is used to assit in the building and maintenance of zouzzzz.info. For instance, taking the site down for maintenace, when the log shows that few people should be affected.</p>
+<p>This information is used to assit in the building and maintenance of example.com. For instance, taking the site down for maintenace, when the log shows that few people should be affected.</p>
 
 <p>IP addresses, and other such information are not linked to any information that is personally identifiable.</p>
 
 <h2>Cookies and Web Beacons</h2>
 
-<p>While zouzzzz.info does not set any cookies. Web Beacons is not used, no distribution of your data. </p>
+<p>While example.com does not set any cookies. Web Beacons is not used, no distribution of your data. </p>
 
 <p>If you wish to disable cookies, you may do so through your individual browser options. More detailed information about cookie management with specific web browsers can be found at the browsers respective websites.</p>
 
 <h2>Search IP information</h2>
 
-<p>While information submitted to the Search IP form is not collected by zouzzzz.info, Search IP URL's may be recorded in the logs.</p>
+<p>While information submitted to the Search IP form is not collected by example.com, Search IP URL's may be recorded in the logs.</p>
 <hr />
 <div class="row show-grid">
     <div class="span12">
         <strong>Publication:</strong> Vincent Legeard<br/>
         4, Bd du docteur Peyr&eacute;<br/>
         61140 Bagnoles de l'Orne - France<br/>
-        06.15.33.78.35.
         </address>
         </div>
 
         <div class="span4">
         <address>
             <strong>PythonAnyWhere</strong>
+            <br/>
+            Resolver Systems Ltd<br/>
+            17a Clerkenwell Road<br/>
+            London EC1M 5RD<br/>
+            United Kingdom<br/>
+
         </address>
         </div>
 

minicms/templates/minicms/rubriques/index.html

     <section>
     {% if home_rubriques_list %}
         {% for rubrique in home_rubriques_list %}
+        {% if rubrique.published %}
         <h1><small>{% trans "Rubrique" %}</small> {{ rubrique.title }}</h1>
         <span class="date"><strong>{% trans "Publish date:" %}</strong> {{ rubrique.publish_date|date:"DATE_FORMAT" }}</span>
         <span class="author"><strong>{% trans "by:" %}</strong> 
         <p>{{ rubrique.content }}</p>
         <a href="/rubriques/{{ rubrique.id }}/">{% trans "Access for this rubrique" %}</a>
         <hr />
+        {% endif %}
         {% endfor %}
 
     <article>
     {% else %}
         <p>{% trans "No section here." %}</p>
     {% endif %}
+
     </section>
 
 {% endblock %}
     Note that the implementation of the iterator does not support
     ordered query sets.
     '''
-    pk = 0
-    last_pk = queryset.order_by('-pk')[0].pk
-    queryset = queryset.order_by('pk')
-    while pk < last_pk:
-        for row in queryset.filter(pk__gt=pk)[:chunksize]:
-            pk = row.pk
-            yield row
-        gc.collect()
+    if queryset:
+        pk = 0
+        last_pk = queryset.order_by('-pk')[0].pk
+        queryset = queryset.order_by('pk')
+        while pk < last_pk:
+            for row in queryset.filter(pk__gt=pk)[:chunksize]:
+                pk = row.pk
+                yield row
+            gc.collect()

rubriques/admin.py

 
 from .models import Rubrique
 from django.contrib import admin
+from rubriques.models import Rubrique
 
 
 class RubriqueAdmin(admin.ModelAdmin):
                               'classes': ['collapse']}),
     )
 
+    class Media:
+        js = (
+            'https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojo/dojo.xd.js',
+            '/static/js/textareas.js')
+        css = {
+            'all': ('/static/css/bootstrap/css/editor.css',), }
 
 admin.site.register(Rubrique, RubriqueAdmin)

rubriques/models.py

 
 from django.db import models
 from django.conf import settings
+from datetime import datetime
 from django.contrib.auth.models import User
 from django.template.defaultfilters import slugify
 
     """ Rubrique model
     """
     title = models.CharField('Title', max_length=200)
-    slug = models.SlugField('Slug', max_length=200, blank=True, editable=False)
+    slug = models.SlugField(
+        'Slug',
+        max_length=200,
+        unique=True,
+        blank=True,
+        editable=False)
     author = models.ForeignKey(User)
     content = models.TextField('Content')
     published = models.BooleanField('Published')
-    publish_date = models.DateTimeField('Publish date')
+    publish_date = models.DateTimeField('Publish date', default=datetime.now)
 
     class Meta:
         ordering = ["-publish_date"]

rubriques/views.py

 def index(request):
     """ list rubrique
     """
-    entries = Rubrique.objects.order_by('publish_date')
+    entries = queryset_iterator(Rubrique.objects.order_by('publish_date'))
     context = {
         'home_rubriques_list': entries,
         'title': _('Rubriques')
     """ detail rubrique
     """
     entry = get_object_or_404(Rubrique, pk=rubrique_id)
-    list_articles = Article.objects.filter(
-        rubrique_id=rubrique_id).order_by('-publish_date')
+    list_articles = queryset_iterator(Article.objects.filter(
+        rubrique_id=rubrique_id).order_by('-publish_date'))
+
     context = {
         'entry': entry,
         'list_articles': list_articles

static/attachments/01062012753.jpg

Removed
Old image

static/attachments/steel-production_e8f1f9.png

Removed
Old image

static/css/bootstrap/css/editor.css

+/* Import standard Dojo CSS files */
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dijit/themes/claro/claro.css";
+
+/* Import custom style sheets for plugins */
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojox/editor/plugins/resources/css/FindReplace.css";
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojox/editor/plugins/resources/css/ShowBlockNodes.css";
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojox/editor/plugins/resources/css/InsertEntity.css";
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojox/editor/plugins/resources/css/CollapsibleToolbar.css";
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojox/editor/plugins/resources/css/Blockquote.css";
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojox/editor/plugins/resources/css/PasteFromWord.css";
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojox/editor/plugins/resources/css/InsertAnchor.css";
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojox/editor/plugins/resources/css/TextColor.css";
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojox/editor/plugins/resources/css/SpellCheck.css";
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojox/editor/plugins/resources/css/PasteFromWord.css";
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojox/editor/plugins/resources/css/PasteFromWord.css";
+@import "https://ajax.googleapis.com/ajax/libs/dojo/1.6.0/dojox/editor/plugins/resources/css/PasteFromWord.css";
+
+/* update CSS rules to cater for Django Admin */
+.dijitEditor {display: inline-block;}
+.dijitEditor .dijitToolbar .dijitTextBox {width: 20ex;}
+.dijitEditor .dijitToolbar label {display: inline; float: none; width: auto;}

static/css/bootstrap/css/reste.css

-.comment-info {
+label {
+    background: #eee;
+    -webkit-box-shadow: 1px 2px 2px rgba(204,204,204,0.8);
+    -moz-box-shadow: 1px 2px 2px rgba(204,204,204,0.8);
+    box-shadow: 1px 2px 2px rgba(204,204,204,0.8);
+    color: #333;
+    font-size: 13px;
+    padding: 4px 5px;
+    text-align: right;
+}
+
+ul.errorlist { padding: 0; margin: 0; }
+ul.errorlist li { color: #b94a48; list-style: none; }
+
+tr.required label { font-weight: bold; }
+tr.required label:after { content: ' *' }
+
+.comment-info {
     border-top: solid 1px #ccc;
     border-bottom: solid 1px #ccc;
 }

static/js/textareas.js

+dojo.require("dijit.Editor");
+
+// extra plugins
+dojo.require("dijit._editor.plugins.FontChoice");
+dojo.require("dojox.editor.plugins.TextColor");
+dojo.require("dojox.editor.plugins.Blockquote");
+dojo.require("dijit._editor.plugins.LinkDialog");
+dojo.require("dojox.editor.plugins.InsertAnchor");
+dojo.require("dojox.editor.plugins.FindReplace");
+dojo.require("dojox.editor.plugins.ShowBlockNodes");
+dojo.require("dojox.editor.plugins.PasteFromWord");
+dojo.require("dijit._editor.plugins.ViewSource");
+dojo.require("dijit._editor.plugins.FullScreen");
+dojo.require("dojox.editor.plugins.InsertEntity");
+
+// headless plugins
+dojo.require("dojox.editor.plugins.CollapsibleToolbar");
+dojo.require("dojox.editor.plugins.NormalizeIndentOutdent");
+dojo.require("dojox.editor.plugins.PrettyPrint");   // let's pretty-print our HTML
+dojo.require("dojox.editor.plugins.AutoUrlLink");
+dojo.require("dojox.editor.plugins.ToolbarLineBreak");
+
+dojo.ready(function(){
+  var textareas = dojo.query("textarea");
+  if(textareas && textareas.length){
+    dojo.addClass(dojo.body(), "claro");
+    textareas.instantiate(dijit.Editor, {
+      styleSheets: "/appmedia/style.css;/appmedia/blog/style.css",
+      plugins: [
+        "collapsibletoolbar",
+        "fullscreen", "viewsource", "|",
+        "undo", "redo", "|",
+        "cut", "copy", "paste", "|",
+        "bold", "italic", "underline", "strikethrough", "|",
+        "insertOrderedList", "insertUnorderedList", "indent", "outdent", "||",
+        "formatBlock", "fontName", "fontSize", "||",
+        "findreplace", "insertEntity", "blockquote", "|",
+        "createLink", "insertImage", "insertanchor", "|",
+        "foreColor", "hiliteColor", "|",
+        "showblocknodes", "pastefromword",
+        // headless plugins
+        "normalizeindentoutdent", "prettyprint",
+        "autourllink", "dijit._editor.plugins.EnterKeyHandling"
+      ]
+    });
+  }
+});