diff --git a/development.ini b/development.ini index f2e9cd5..5212c77 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/media # Themes directory paths.themes = %(here)s/themes diff --git a/scribeengine/config/deployment.ini_tmpl b/scribeengine/config/deployment.ini_tmpl index 0dd278d..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 diff --git a/scribeengine/controllers/media.py b/scribeengine/controllers/media.py index a4c1487..38a431a 100644 --- a/scribeengine/controllers/media.py +++ b/scribeengine/controllers/media.py @@ -1,16 +1,46 @@ +# -*- 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 pylons import request, response, session, tmpl_context as c -from pylons.controllers.util import abort, redirect_to - -from scribeengine.lib.base import BaseController, render +from scribeengine.lib.base import * +from scribeengine.lib import utils +from scribeengine.model import MediaType, File +from scribeengine.model.meta import Session log = logging.getLogger(__name__) class MediaController(BaseController): + def _get_directories(self, parent=None, tree={}): + if not parent: + parent = config[u'paths.media'] + for dirpath in os.listdir(parent): + if os.path.isdir(dirpath): + tree[os.path.abspath(dirpath)] = {u'path': dirpath, u'children': {}} + self._get_directories(os.path.abspath(dirpath), + tree[os.path.abspath(dirpath)][u'children']) + def index(self): - # Return a rendered template - #return render('/media.mako') - # or, return a response - return 'Hello World' + c.directories = self._get_directories() + return render(u'/media/index.html') diff --git a/scribeengine/lib/base.py b/scribeengine/lib/base.py index 0071e79..9f09ff2 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') diff --git a/scribeengine/public/scripts/jquery.metadata.js b/scribeengine/public/scripts/jquery.metadata.js new file mode 100644 index 0000000..ad8bfba --- /dev/null +++ b/scribeengine/public/scripts/jquery.metadata.js @@ -0,0 +1,122 @@ +/* + * Metadata - jQuery plugin for parsing metadata from elements + * + * Copyright (c) 2006 John Resig, Yehuda Katz, J�örn Zaefferer, Paul McLanahan + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Revision: $Id: jquery.metadata.js 4187 2007-12-16 17:15:27Z joern.zaefferer $ + * + */ + +/** + * Sets the type of metadata to use. Metadata is encoded in JSON, and each property + * in the JSON will become a property of the element itself. + * + * There are three supported types of metadata storage: + * + * attr: Inside an attribute. The name parameter indicates *which* attribute. + * + * class: Inside the class attribute, wrapped in curly braces: { } + * + * elem: Inside a child element (e.g. a script tag). The + * name parameter indicates *which* element. + * + * The metadata for an element is loaded the first time the element is accessed via jQuery. + * + * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements + * matched by expr, then redefine the metadata type and run another $(expr) for other elements. + * + * @name $.metadata.setType + * + * @example

This is a p

+ * @before $.metadata.setType("class") + * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" + * @desc Reads metadata from the class attribute + * + * @example

This is a p

+ * @before $.metadata.setType("attr", "data") + * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" + * @desc Reads metadata from a "data" attribute + * + * @example

This is a p

+ * @before $.metadata.setType("elem", "script") + * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" + * @desc Reads metadata from a nested script element + * + * @param String type The encoding type + * @param String name The name of the attribute to be used to get metadata (optional) + * @cat Plugins/Metadata + * @descr Sets the type of encoding to be used when loading metadata for the first time + * @type undefined + * @see metadata() + */ + +(function($) { + +$.extend({ + metadata : { + defaults : { + type: 'class', + name: 'metadata', + cre: /({.*})/, + single: 'metadata' + }, + setType: function( type, name ){ + this.defaults.type = type; + this.defaults.name = name; + }, + get: function( elem, opts ){ + var settings = $.extend({},this.defaults,opts); + // check for empty string in single property + if ( !settings.single.length ) settings.single = 'metadata'; + + var data = $.data(elem, settings.single); + // returned cached data if it already exists + if ( data ) return data; + + data = "{}"; + + if ( settings.type == "class" ) { + var m = settings.cre.exec( elem.className ); + if ( m ) + data = m[1]; + } else if ( settings.type == "elem" ) { + if( !elem.getElementsByTagName ) + return undefined; + var e = elem.getElementsByTagName(settings.name); + if ( e.length ) + data = $.trim(e[0].innerHTML); + } else if ( elem.getAttribute != undefined ) { + var attr = elem.getAttribute( settings.name ); + if ( attr ) + data = attr; + } + + if ( data.indexOf( '{' ) <0 ) + data = "{" + data + "}"; + + data = eval("(" + data + ")"); + + $.data( elem, settings.single, data ); + return data; + } + } +}); + +/** + * Returns the metadata object for the first member of the jQuery object. + * + * @name metadata + * @descr Returns element's metadata object + * @param Object opts An object contianing settings to override the defaults + * @type jQuery + * @cat Plugins/Metadata + */ +$.fn.metadata = function( opts ){ + return $.metadata.get( this[0], opts ); +}; + +})(jQuery); \ No newline at end of file diff --git a/scribeengine/public/scripts/jtree/jquery.tree.js b/scribeengine/public/scripts/jtree/jquery.tree.js new file mode 100644 index 0000000..077f4e5 --- /dev/null +++ b/scribeengine/public/scripts/jtree/jquery.tree.js @@ -0,0 +1,2064 @@ +/* + * jsTree 0.9.9a + * http://jstree.com/ + * + * Copyright (c) 2009 Ivan Bozhanov (vakata.com) + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Date: 2009-10-06 + * + */ + +(function($) { + // jQuery plugin + $.tree = { + datastores : { }, + plugins : { }, + defaults : { + data : { + async : false, // Are async requests used to load open_branch contents + type : "html", // One of included datastores + opts : { method: "GET", url: false } // Options passed to datastore + }, + selected : false, // FALSE or STRING or ARRAY + opened : [], // ARRAY OF INITIALLY OPENED NODES + languages : [], // ARRAY of string values (which will be used as CSS classes - so they must be valid) + ui : { + dots : true, // BOOL - dots or no dots + animation : 0, // INT - duration of open/close animations in miliseconds + scroll_spd : 4, + theme_path : false, // Path to the theme CSS file - if set to false and theme_name is not false - will lookup jstree-path-here/themes/theme-name-here/style.css + theme_name : "default",// if set to false no theme will be loaded + selected_parent_close : "select_parent", // false, "deselect", "select_parent" + selected_delete : "select_previous" // false, "select_previous" + }, + types : { + "default" : { + clickable : true, // can be function + renameable : true, // can be function + deletable : true, // can be function + creatable : true, // can be function + draggable : true, // can be function + max_children : -1, // -1 - not set, 0 - no children, 1 - one child, etc // can be function + max_depth : -1, // -1 - not set, 0 - no children, 1 - one level of children, etc // can be function + valid_children : "all", // all, none, array of values // can be function + icon : { + image : false, + position : false + } + } + }, + rules : { + multiple : false, // FALSE | CTRL | ON - multiple selection off/ with or without holding Ctrl + multitree : "none", // all, none, array of tree IDs to accept from + type_attr : "rel", // STRING attribute name (where is the type stored as string) + createat : "bottom", // STRING (top or bottom) new nodes get inserted at top or bottom + drag_copy : "ctrl", // FALSE | CTRL | ON - drag to copy off/ with or without holding Ctrl + drag_button : "left", // left, right or both + use_max_children : true, + use_max_depth : true, + + max_children: -1, + max_depth : -1, + valid_children : "all" + }, + lang : { + new_node : "New folder", + loading : "Loading ..." + }, + callback : { + beforechange: function(NODE,TREE_OBJ) { return true }, + beforeopen : function(NODE,TREE_OBJ) { return true }, + beforeclose : function(NODE,TREE_OBJ) { return true }, + beforemove : function(NODE,REF_NODE,TYPE,TREE_OBJ) { return true }, + beforecreate: function(NODE,REF_NODE,TYPE,TREE_OBJ) { return true }, + beforerename: function(NODE,LANG,TREE_OBJ) { return true }, + beforedelete: function(NODE,TREE_OBJ) { return true }, + beforedata : function(NODE,TREE_OBJ) { return { id : $(NODE).attr("id") || 0 } }, // PARAMETERS PASSED TO SERVER + ondata : function(DATA,TREE_OBJ) { return DATA; }, // modify data before parsing it + onparse : function(STR,TREE_OBJ) { return STR; }, // modify string before visualizing it + onhover : function(NODE,TREE_OBJ) { }, // node hovered + onselect : function(NODE,TREE_OBJ) { }, // node selected + ondeselect : function(NODE,TREE_OBJ) { }, // node deselected + onchange : function(NODE,TREE_OBJ) { }, // focus changed + onrename : function(NODE,TREE_OBJ,RB) { }, // node renamed + onmove : function(NODE,REF_NODE,TYPE,TREE_OBJ,RB) { }, // move completed + oncopy : function(NODE,REF_NODE,TYPE,TREE_OBJ,RB) { }, // copy completed + oncreate : function(NODE,REF_NODE,TYPE,TREE_OBJ,RB) { }, // node created + ondelete : function(NODE,TREE_OBJ,RB) { }, // node deleted + onopen : function(NODE,TREE_OBJ) { }, // node opened + onopen_all : function(TREE_OBJ) { }, // all nodes opened + onclose_all : function(TREE_OBJ) { }, // all nodes closed + onclose : function(NODE,TREE_OBJ) { }, // node closed + error : function(TEXT,TREE_OBJ) { }, // error occured + ondblclk : function(NODE,TREE_OBJ) { TREE_OBJ.toggle_branch.call(TREE_OBJ, NODE); TREE_OBJ.select_branch.call(TREE_OBJ, NODE); }, + onrgtclk : function(NODE,TREE_OBJ,EV) { }, // right click - to prevent use: EV.preventDefault(); EV.stopPropagation(); return false + onload : function(TREE_OBJ) { }, + oninit : function(TREE_OBJ) { }, + onfocus : function(TREE_OBJ) { }, + ondestroy : function(TREE_OBJ) { }, + onsearch : function(NODES, TREE_OBJ) { NODES.addClass("search"); }, + ondrop : function(NODE,REF_NODE,TYPE,TREE_OBJ) { }, + check : function(RULE,NODE,VALUE,TREE_OBJ) { return VALUE; }, + check_move : function(NODE,REF_NODE,TYPE,TREE_OBJ) { return true; } + }, + plugins : { } + }, + + create : function () { return new tree_component(); }, + focused : function () { return tree_component.inst[tree_component.focused]; }, + reference : function (obj) { + var o = $(obj); + if(!o.size()) o = $("#" + obj); + if(!o.size()) return null; + o = (o.is(".tree")) ? o.attr("id") : o.parents(".tree:eq(0)").attr("id"); + return tree_component.inst[o] || null; + }, + rollback : function (data) { + for(var i in data) { + if(!data.hasOwnProperty(i)) continue; + var tmp = tree_component.inst[i]; + var lock = !tmp.locked; + + // if not locked - lock the tree + if(lock) tmp.lock(true); + // Cancel ongoing rename + tmp.inp = false; + tmp.container.html(data[i].html).find(".dragged").removeClass("dragged").end().find(".hover").removeClass("hover"); + + if(data[i].selected) { + tmp.selected = $("#" + data[i].selected); + tmp.selected_arr = []; + tmp.container + .find("a.clicked").each( function () { + tmp.selected_arr.push(tmp.get_node(this)); + }); + } + // if this function set the lock - unlock + if(lock) tmp.lock(false); + + delete lock; + delete tmp; + } + }, + drop_mode : function (opts) { + opts = $.extend(opts, { show : false, type : "default", str : "Foreign node" }); + tree_component.drag_drop.foreign = true; + tree_component.drag_drop.isdown = true; + tree_component.drag_drop.moving = true; + tree_component.drag_drop.appended = false; + tree_component.drag_drop.f_type = opts.type; + tree_component.drag_drop.f_data = opts; + + + if(!opts.show) { + tree_component.drag_drop.drag_help = false; + tree_component.drag_drop.drag_node = false; + } + else { + tree_component.drag_drop.drag_help = $("
"); + tree_component.drag_drop.drag_node = tree_component.drag_drop.drag_help.find("li:eq(0)"); + } + if($.tree.drag_start !== false) $.tree.drag_start.call(null, false); + }, + drag_start : false, + drag : false, + drag_end : false + }; + $.fn.tree = function (opts) { + return this.each(function() { + var conf = $.extend({},opts); + if(tree_component.inst && tree_component.inst[$(this).attr('id')]) tree_component.inst[$(this).attr('id')].destroy(); + if(conf !== false) new tree_component().init(this, conf); + }); + }; + + // core + function tree_component () { + return { + cntr : ++tree_component.cntr, + settings : $.extend({},$.tree.defaults), + + init : function(elem, conf) { + var _this = this; + this.container = $(elem); + if(this.container.size == 0) return false; + tree_component.inst[this.cntr] = this; + if(!this.container.attr("id")) this.container.attr("id","jstree_" + this.cntr); + tree_component.inst[this.container.attr("id")] = tree_component.inst[this.cntr]; + tree_component.focused = this.cntr; + this.settings = $.extend(true, {}, this.settings, conf); + + // DEAL WITH LANGUAGE VERSIONS + if(this.settings.languages && this.settings.languages.length) { + this.current_lang = this.settings.languages[0]; + var st = false; + var id = "#" + this.container.attr("id"); + for(var ln = 0; ln < this.settings.languages.length; ln++) { + st = tree_component.add_css(id + " ." + this.settings.languages[ln]); + if(st !== false) st.style.display = (this.settings.languages[ln] == this.current_lang) ? "" : "none"; + } + } + else this.current_lang = false; + // THEMES + this.container.addClass("tree"); + if(this.settings.ui.theme_name !== false) { + if(this.settings.ui.theme_path === false) { + $("script").each(function () { + if(this.src.toString().match(/jquery\.tree.*?js$/)) { _this.settings.ui.theme_path = this.src.toString().replace(/jquery\.tree.*?js$/, "") + "themes/" + _this.settings.ui.theme_name + "/style.css"; return false; } + }); + } + if(this.settings.ui.theme_path != "" && $.inArray(this.settings.ui.theme_path, tree_component.themes) == -1) { + tree_component.add_sheet({ url : this.settings.ui.theme_path }); + tree_component.themes.push(this.settings.ui.theme_path); + } + this.container.addClass("tree-" + this.settings.ui.theme_name); + } + // TYPE ICONS + var type_icons = ""; + for(var t in this.settings.types) { + if(!this.settings.types.hasOwnProperty(t)) continue; + if(!this.settings.types[t].icon) continue; + if( this.settings.types[t].icon.image || this.settings.types[t].icon.position) { + if(t == "default") type_icons += "#" + this.container.attr("id") + " li > a ins { "; + else type_icons += "#" + this.container.attr("id") + " li[rel=" + t + "] > a ins { "; + if(this.settings.types[t].icon.image) type_icons += " background-image:url(" + this.settings.types[t].icon.image + "); "; + if(this.settings.types[t].icon.position) type_icons += " background-position:" + this.settings.types[t].icon.position + "; "; + type_icons += "} "; + } + } + if(type_icons != "") tree_component.add_sheet({ str : type_icons }); + + if(this.settings.rules.multiple) this.selected_arr = []; + this.offset = false; + this.hovered = false; + this.locked = false; + + if(tree_component.drag_drop.marker === false) tree_component.drag_drop.marker = $("
").attr({ id : "jstree-marker" }).hide().appendTo("body"); + this.callback("oninit", [this]); + this.refresh(); + this.attach_events(); + this.focus(); + }, + refresh : function (obj) { + if(this.locked) return this.error("LOCKED"); + var _this = this; + if(obj && !this.settings.data.async) obj = false; + this.is_partial_refresh = obj ? true : false; + + // SAVE OPENED + this.opened = Array(); + if(this.settings.opened != false) { + $.each(this.settings.opened, function (i, item) { + if(this.replace(/^#/,"").length > 0) { _this.opened.push("#" + this.replace(/^#/,"")); } + }); + this.settings.opened = false; + } + else { + this.container.find("li.open").each(function (i) { if(this.id) { _this.opened.push("#" + this.id); } }); + } + + // SAVE SELECTED + if(this.selected) { + this.settings.selected = Array(); + if(obj) { + $(obj).find("li:has(a.clicked)").each(function () { + if(this.id) _this.settings.selected.push("#" + this.id); + }); + } + else { + if(this.selected_arr) { + $.each(this.selected_arr, function () { + if(this.attr("id")) _this.settings.selected.push("#" + this.attr("id")); + }); + } + else { + if(this.selected.attr("id")) this.settings.selected.push("#" + this.selected.attr("id")); + } + } + } + else if(this.settings.selected !== false) { + var tmp = Array(); + if((typeof this.settings.selected).toLowerCase() == "object") { + $.each(this.settings.selected, function () { + if(this.replace(/^#/,"").length > 0) tmp.push("#" + this.replace(/^#/,"")); + }); + } + else { + if(this.settings.selected.replace(/^#/,"").length > 0) tmp.push("#" + this.settings.selected.replace(/^#/,"")); + } + this.settings.selected = tmp; + } + + if(obj && this.settings.data.async) { + this.opened = Array(); + obj = this.get_node(obj); + obj.find("li.open").each(function (i) { _this.opened.push("#" + this.id); }); + if(obj.hasClass("open")) obj.removeClass("open").addClass("closed"); + if(obj.hasClass("leaf")) obj.removeClass("leaf"); + obj.children("ul:eq(0)").html(""); + return this.open_branch(obj, true, function () { _this.reselect.apply(_this); }); + } + + var _this = this; + var _datastore = new $.tree.datastores[this.settings.data.type](); + if(this.container.children("ul").size() == 0) { + this.container.html(""); + } + _datastore.load(this.callback("beforedata",[false,this]),this,this.settings.data.opts,function(data) { + data = _this.callback("ondata",[data, _this]); + _datastore.parse(data,_this,_this.settings.data.opts,function(str) { + str = _this.callback("onparse", [str, _this]); + _this.container.empty().append($("