diff --git a/development.ini b/development.ini index 21b96c1..5370edd 100644 --- a/development.ini +++ b/development.ini @@ -34,8 +34,8 @@ beaker.session.timeout = 1209600 # SQLAlchemy database URL sqlalchemy.url = sqlite:///%(here)s/scribeengine.sqlite -# Images directory -paths.images = %(here)s/images +# Media upload directory +paths.media = %(here)s/scribeengine/public/files # Themes directory paths.themes = %(here)s/themes @@ -53,6 +53,11 @@ mail.smtp.password = mymailpassword # Server-related settings server.timezone = 'Africa/Johannesburg' +# Setup options +setup.create_tables = true +setup.create_user = false +setup.blog_details = false + # WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* # Debug mode will enable the interactive debugging tool, allowing ANYONE to # execute malicious code after an exception is raised. diff --git a/scribeengine/config/deployment.ini_tmpl b/scribeengine/config/deployment.ini_tmpl index bbb4277..4e25484 100644 --- a/scribeengine/config/deployment.ini_tmpl +++ b/scribeengine/config/deployment.ini_tmpl @@ -34,8 +34,8 @@ app_instance_uuid = ${app_instance_uuid} # SQLAlchemy database URL sqlalchemy.url = sqlite:///production.db -# Images directory -paths.images = %(here)s/images +# Media upload directory +paths.media = %(here)s/media # Themes directory paths.themes = %(here)s/themes @@ -53,6 +53,11 @@ mail.smtp.password = mymailpassword # Server-related settings server.timezone = 'Africa/Johannesburg' +# Setup options (for when you run "paster setup-app config.ini") +setup.create_tables = true +setup.create_user = false +setup.blog_details = false + # WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* # Debug mode will enable the interactive debugging tool, allowing ANYONE to # execute malicious code after an exception is raised. diff --git a/scribeengine/controllers/media.py b/scribeengine/controllers/media.py new file mode 100644 index 0000000..d98b80c --- /dev/null +++ b/scribeengine/controllers/media.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# ScribeEngine - Open Source Blog Software # +# --------------------------------------------------------------------------- # +# Copyright (c) 2010 Raoul Snyman # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### + +import os +import logging +from pprint import pprint, pformat + +from pylons.decorators import jsonify + +from scribeengine.lib.base import * +from scribeengine.lib import utils +from scribeengine.model import MediaType, File +from scribeengine.model.meta import Session + +try: + import json +except ImportError: + import simplejson as json + +log = logging.getLogger(__name__) + +class MediaController(BaseController): + + def __before__(self): + BaseController.__before__(self) + self._add_javascript(u'tinymce/tiny_mce_popup.js') + self._add_javascript(u'ScribeEngine.Media.js') + + def _get_directories(self, parent=None, tree=[]): + old_root = parent + dirname = os.path.split(parent)[1] + node = {u'data': dirname, u'state': u'open'} + for root, dirs, files in os.walk(parent): + if root != old_root: + break + if dirs: + node[u'children'] = [] + for dirpath in dirs: + full_dirpath = os.path.join(root, dirpath) + self._get_directories(full_dirpath, node[u'children']) + tree.append(node) + + def _get_files(self, dirpath): + for root, dirs, files in os.walk(os.path.abspath(dirpath)): + filelist = [] + for filename in files: + if os.path.splitext(filename)[1] in [u'.png', u'.jpg', u'.gif']: + filelist.append({u'name': filename, u'type': u'/images/file-image.png'}) + else: + filelist.append({u'name': filename, u'type': u'/images/file-unknown.png'}) + return filelist + break + + def index(self): + self._add_javascript(u'jstree/jquery.jstree.js') + directories = [] + path = os.path.join(config[u'paths.media'], u'user%s' % c.current_user.id) + if not os.path.exists(path): + os.makedirs(path) + self._get_directories(path, directories) + c.directories = json.dumps(directories) + c.files = self._get_files(path) + c.page_title = u'Media Browser' + return render(u'/media/index.mako') + + @jsonify + def get_files(self): + path = request.GET.get(u'path', u'').split(u',') + dirpath = os.path.join(config[u'paths.media'], *path) + log.debug("Path: %s", path) + return {u'results': self._get_files(dirpath)} + + @jsonify + def create_directory(self): + """ + Create a directory, called via AJAX. + """ + dirname = request.GET.get(u'directory') + parent = request.GET.get(u'parent') + if dirname and parent: + parent = os.path.abspath(parent) + log.debug(parent) + #os.makedirs(os.path.join(parent, dirname)) + return u'1' + else: + return u'0' + diff --git a/scribeengine/lib/base.py b/scribeengine/lib/base.py index 0071e79..af41c28 100644 --- a/scribeengine/lib/base.py +++ b/scribeengine/lib/base.py @@ -77,6 +77,7 @@ class BaseController(WSGIController): c.month_posts[post.created.day] = [] c.month_posts[post.created.day].append(post) self._add_javascript(u'jquery.js') + self._add_javascript(u'jquery.metadata.js') if c.jsvalidation: self._add_javascript(u'jquery.validate.js') self._add_javascript(u'ScribeEngine.js') @@ -243,7 +244,7 @@ def authenticate(permission=None): session[u'redirect_url'] = request.environ[u'PATH_INFO'] session.save() h.flash.set_message(u'You need to be logged in to do that.', u'error') - h.redirect_to('/admin/login') + h.redirect_to('/account/login') return decorator(validate) diff --git a/scribeengine/model/__init__.py b/scribeengine/model/__init__.py index 6af5860..466a984 100644 --- a/scribeengine/model/__init__.py +++ b/scribeengine/model/__init__.py @@ -28,11 +28,12 @@ from sqlalchemy.orm import mapper, relation, backref from scribeengine.model import meta from scribeengine.model.tables import categories_table, comments_table, \ - pages_table, permissions_table, posts_table, roles_table, tags_table, \ - users_table, variables_table, categories_posts_table, \ - permissions_roles_table, posts_tags_table, roles_users_table -from scribeengine.model.classes import Category, Comment, Page, Permission, \ - Post, Role, Tag, User, Variable + files_table, media_types_table, pages_table, permissions_table, \ + posts_table, roles_table, tags_table, users_table, variables_table, \ + categories_posts_table, permissions_roles_table, posts_tags_table, \ + roles_users_table +from scribeengine.model.classes import Category, Comment, File, MediaType, \ + Page, Permission, Post, Role, Tag, User, Variable def init_model(engine): """Call me before using any of the tables or classes in the model""" @@ -41,25 +42,36 @@ def init_model(engine): mapper(Category, categories_table) mapper(Comment, comments_table) +mapper(File, files_table) +mapper(MediaType, media_types_table, + properties={ + u'files': relation(File, backref=u'media_type') + } +) mapper(Page, pages_table) mapper(Permission, permissions_table) mapper(Post, posts_table, properties={ - u'categories': relation(Category, backref='posts', secondary=categories_posts_table), - u'comments': relation(Comment, backref=u'post', order_by=Comment.created.asc()), - u'tags': relation(Tag, backref=backref(u'posts', order_by='posts.created DESC'), secondary=posts_tags_table) + u'categories': relation(Category, backref=u'posts', + secondary=categories_posts_table), + u'comments': relation(Comment, backref=u'post', + order_by=Comment.created.asc()), + u'tags': relation(Tag, + backref=backref(u'posts', order_by='posts.created DESC'), + secondary=posts_tags_table) } ) mapper(Role, roles_table, properties={ - u'permissions': relation(Permission, backref=u'roles', secondary=permissions_roles_table) + u'permissions': relation(Permission, backref=u'roles', + secondary=permissions_roles_table) } ) mapper(Tag, tags_table) mapper(User, users_table, properties={ u'comments': relation(Comment, backref=u'user'), - #u'pages': relation(Page, backref=u'user'), + u'files': relation(File, backref=u'user'), u'posts': relation(Post, backref=u'user'), u'roles': relation(Role, backref=u'users', secondary=roles_users_table) } diff --git a/scribeengine/model/classes.py b/scribeengine/model/classes.py index 3c2bef0..489b89a 100644 --- a/scribeengine/model/classes.py +++ b/scribeengine/model/classes.py @@ -55,6 +55,20 @@ class Comment(BaseModel): pass +class File(BaseModel): + """ + A file in the media library. + """ + pass + + +class MediaType(BaseModel): + """ + Distinguishes between different types of media. + """ + pass + + class Page(BaseModel): """ A page on the blog. This is separate from a blog entry, for things like diff --git a/scribeengine/model/tables.py b/scribeengine/model/tables.py index c8819ad..159810d 100644 --- a/scribeengine/model/tables.py +++ b/scribeengine/model/tables.py @@ -50,6 +50,21 @@ comments_table = Table(u'comments', metadata, Column(u'modified', DateTime, default=datetime.now()) ) +files_table = Table(u'files', metadata, + Column(u'id', Integer, primary_key=True), + Column(u'user_id', Integer, ForeignKey(u'users.id'), nullable=False), + Column(u'media_type_id', Integer, ForeignKey(u'media_types.id'), nullable=False), + Column(u'filename', Unicode(255), nullable=False, index=True), + Column(u'mimetype', Unicode(255)), + Column(u'path', Unicode(255)), + Column(u'size', Integer, default=0) +) + +media_types_table = Table(u'media_types', metadata, + Column(u'id', Integer, primary_key=True), + Column(u'title', Unicode(255), nullable=False, index=True) +) + # Definition of the "pages" table pages_table = Table(u'pages', metadata, Column(u'id', Integer, primary_key=True), diff --git a/scribeengine/public/images/file-image.png b/scribeengine/public/images/file-image.png new file mode 100644 index 0000000..a56f9af Binary files /dev/null and b/scribeengine/public/images/file-image.png differ diff --git a/scribeengine/public/images/file-unknown.png b/scribeengine/public/images/file-unknown.png new file mode 100644 index 0000000..dc28b5a Binary files /dev/null and b/scribeengine/public/images/file-unknown.png differ diff --git a/scribeengine/public/images/media-button.png b/scribeengine/public/images/media-button.png new file mode 100644 index 0000000..a0fcdeb Binary files /dev/null and b/scribeengine/public/images/media-button.png differ diff --git a/scribeengine/public/scripts/ScribeEngine.Media.js b/scribeengine/public/scripts/ScribeEngine.Media.js new file mode 100644 index 0000000..6a97e8c --- /dev/null +++ b/scribeengine/public/scripts/ScribeEngine.Media.js @@ -0,0 +1,98 @@ +/***************************************************************************** + * ScribeEngine - Open Source Blog Software * + * ------------------------------------------------------------------------- * + * Copyright (c) 2010 Raoul Snyman * + * ------------------------------------------------------------------------- * + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; version 2 of the License. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * + * Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + *****************************************************************************/ + +ScribeEngine.Namespace.create("ScribeEngine.Media", { + init: function () + { + // do nothing for the moment. in future, pre-select the file. + }, + closeWindow: function () + { + window.close(); + }, + newDirectory: function () + { + var dirname = prompt("New Directory:", "directory"); + }, + getFiles: function (event) + { + var tree = jQuery.jstree._focused(); + var path = tree.get_path(ScribeEngine.Events.getElement(event)); + $("#selected-path").val(path); + $.getJSON( + '/media/get-files', + {"path": path.toString()}, + function (data, textStatus) { + $("#file-list > ul").html(""); + $.each(data.results, function () { + var file = this; + if (file.name.length > 15) + { + file.display_name = file.name.substr(0, 12) + "..."; + } + else + { + file.display_name = file.name; + } + $("#file-list > ul").append( + $("