diff --git a/contrib/ban_words/main.php b/contrib/ban_words/main.php index aee98417..f64b1f69 100644 --- a/contrib/ban_words/main.php +++ b/contrib/ban_words/main.php @@ -2,6 +2,7 @@ /* * Name: Comment Word Ban * Author: Shish + * Link: http://code.shishnet.org/shimmie2/ * License: GPLv2 * Description: For stopping spam and other comment abuse * Documentation: diff --git a/contrib/bulk_add/main.php b/contrib/bulk_add/main.php index 0fa89029..c31d43fb 100644 --- a/contrib/bulk_add/main.php +++ b/contrib/bulk_add/main.php @@ -2,6 +2,7 @@ /* * Name: Bulk Add * Author: Shish + * Link: http://code.shishnet.org/shimmie2/ * License: GPLv2 * Description: Bulk add server-side images * Documentation: @@ -30,7 +31,9 @@ class BulkAdd extends SimpleExtension { $this->theme->display_admin_block(); } - + /** + * Generate the necessary DataUploadEvent for a given image and tags. + */ private function add_image($tmpname, $filename, $tags) { assert(file_exists($tmpname)); diff --git a/contrib/et/main.php b/contrib/et/main.php index dd6babe2..088d21f6 100644 --- a/contrib/et/main.php +++ b/contrib/et/main.php @@ -29,6 +29,9 @@ class ET extends SimpleExtension { } } + /** + * Collect the information and return it in a keyed array. + */ private function get_info() { global $config, $database; global $_event_listeners; // yay for using secret globals \o/ diff --git a/contrib/favorites/main.php b/contrib/favorites/main.php index ad37edd0..15f266c2 100644 --- a/contrib/favorites/main.php +++ b/contrib/favorites/main.php @@ -152,6 +152,17 @@ class Favorites extends SimpleExtension { "); $config->set_int("ext_favorites_version", 1); } + + if($config->get_int("ext_favorites_version") < 2) { + log_info("favorites", "Cleaning user favourites"); + $database->Execute("DELETE FROM user_favorites WHERE user_id NOT IN (SELECT id FROM users)"); + $database->Execute("DELETE FROM user_favorites WHERE image_id NOT IN (SELECT id FROM images)"); + + log_info("favorites", "Adding foreign keys to user favourites"); + $database->Execute("ALTER TABLE user_favorites ADD CONSTRAINT foreign_user_favorites_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;"); + $database->Execute("ALTER TABLE user_favorites ADD CONSTRAINT user_favorites_image_id FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE;"); + $config->set_int("ext_favorites_version", 2); + } } private function add_vote($image_id, $user_id, $do_set) { diff --git a/contrib/featured/main.php b/contrib/featured/main.php index 12f06b08..83ace62e 100644 --- a/contrib/featured/main.php +++ b/contrib/featured/main.php @@ -2,6 +2,7 @@ /* * Name: Featured Image * Author: Shish + * Link: http://code.shishnet.org/shimmie2/ * License: GPLv2 * Description: Bring a specific image to the users' attentions * Documentation: diff --git a/contrib/handle_flash/main.php b/contrib/handle_flash/main.php index 013528ac..21b19b30 100644 --- a/contrib/handle_flash/main.php +++ b/contrib/handle_flash/main.php @@ -2,7 +2,8 @@ /* * Name: Handle Flash * Author: Shish - * Description: Handle Flash files + * Link: http://code.shishnet.org/shimmie2/ + * Description: Handle Flash files. (No thumbnail is generated for flash files) */ class FlashFileHandler extends DataHandlerExtension { diff --git a/contrib/handle_svg/main.php b/contrib/handle_svg/main.php index e2d02a3f..bab0cf14 100644 --- a/contrib/handle_svg/main.php +++ b/contrib/handle_svg/main.php @@ -2,7 +2,8 @@ /* * Name: Handle SVG * Author: Shish - * Description: Handle SVG files + * Link: http://code.shishnet.org/shimmie2/ + * Description: Handle SVG files. (No thumbnail is generated for SVG files) */ class SVGFileHandler implements Extension { diff --git a/contrib/ipban/main.php b/contrib/ipban/main.php index 06e86b1d..3af43ead 100644 --- a/contrib/ipban/main.php +++ b/contrib/ipban/main.php @@ -2,6 +2,7 @@ /* * Name: IP Ban * Author: Shish + * Link: http://code.shishnet.org/shimmie2/ * License: GPLv2 * Description: Ban IP addresses * Documentation: @@ -92,7 +93,7 @@ class IPBan extends SimpleExtension { public function onRemoveIPBan($event) { global $database; $database->Execute("DELETE FROM bans WHERE id = :id", array("id"=>$event->id)); - $database->cache->delete("ip_bans"); + $database->cache->delete("ip_bans_sorted"); } // installer {{{ @@ -261,7 +262,7 @@ class IPBan extends SimpleExtension { global $database; $sql = "INSERT INTO bans (ip, reason, end_timestamp, banner_id) VALUES (:ip, :reason, :end, :admin_id)"; $database->Execute($sql, array("ip"=>$ip, "reason"=>$reason, "end"=>strtotime($end), "admin_id"=>$user->id)); - $database->cache->delete("ip_bans"); + $database->cache->delete("ip_bans_sorted"); log_info("ipban", "'$user->name' has banned '$ip' because '$reason' until '$end'"); } // }}} diff --git a/contrib/log_db/main.php b/contrib/log_db/main.php index 2e51ce24..da5a590c 100644 --- a/contrib/log_db/main.php +++ b/contrib/log_db/main.php @@ -1,8 +1,9 @@ + * Link: http://code.shishnet.org/shimmie2/ + * Description: Keep a record of SCore events (in the database). * Visibility: admin */ diff --git a/contrib/news/main.php b/contrib/news/main.php index 69c87255..bd3f8388 100644 --- a/contrib/news/main.php +++ b/contrib/news/main.php @@ -2,6 +2,7 @@ /* * Name: News * Author: Shish + * Link: http://code.shishnet.org/shimmie2/ * License: GPLv2 * Description: Show a short amount of text in a block on the post list * Documentation: diff --git a/contrib/notes/jquery.js b/contrib/notes/jquery.js deleted file mode 100644 index 2e43a823..00000000 --- a/contrib/notes/jquery.js +++ /dev/null @@ -1,3408 +0,0 @@ -(function(){ -/* - * jQuery 1.2.3 - New Wave Javascript - * - * Copyright (c) 2008 John Resig (jquery.com) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * $Date: 2008-02-06 00:21:25 -0500 (Wed, 06 Feb 2008) $ - * $Rev: 4663 $ - */ - -// Map over jQuery in case of overwrite -if ( window.jQuery ) - var _jQuery = window.jQuery; - -var jQuery = window.jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.prototype.init( selector, context ); -}; - -// Map over the $ in case of overwrite -if ( window.$ ) - var _$ = window.$; - -// Map the jQuery namespace to the '$' one -window.$ = jQuery; - -// A simple way to check for HTML strings or ID strings -// (both of which we optimize for) -var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/; - -// Is it a simple selector -var isSimple = /^.[^:#\[\.]*$/; - -jQuery.fn = jQuery.prototype = { - init: function( selector, context ) { - // Make sure that a selection was provided - selector = selector || document; - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this[0] = selector; - this.length = 1; - return this; - - // Handle HTML strings - } else if ( typeof selector == "string" ) { - // Are we dealing with HTML string or an ID? - var match = quickExpr.exec( selector ); - - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) - selector = jQuery.clean( [ match[1] ], context ); - - // HANDLE: $("#id") - else { - var elem = document.getElementById( match[3] ); - - // Make sure an element was located - if ( elem ) - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id != match[3] ) - return jQuery().find( selector ); - - // Otherwise, we inject the element directly into the jQuery object - else { - this[0] = elem; - this.length = 1; - return this; - } - - else - selector = []; - } - - // HANDLE: $(expr, [context]) - // (which is just equivalent to: $(content).find(expr) - } else - return new jQuery( context ).find( selector ); - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) - return new jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector ); - - return this.setArray( - // HANDLE: $(array) - selector.constructor == Array && selector || - - // HANDLE: $(arraylike) - // Watch for when an array-like object, contains DOM nodes, is passed in as the selector - (selector.jquery || selector.length && selector != window && !selector.nodeType && selector[0] != undefined && selector[0].nodeType) && jQuery.makeArray( selector ) || - - // HANDLE: $(*) - [ selector ] ); - }, - - // The current version of jQuery being used - jquery: "1.2.3", - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - // The number of elements contained in the matched element set - length: 0, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == undefined ? - - // Return a 'clean' array - jQuery.makeArray( this ) : - - // Return just the object - this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - // Build a new jQuery matched element set - var ret = jQuery( elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - // Return the newly-formed element set - return ret; - }, - - // Force the current matched set of elements to become - // the specified array of elements (destroying the stack in the process) - // You should use pushStack() in order to do this, but maintain the stack - setArray: function( elems ) { - // Resetting the length to 0, then using the native Array push - // is a super-fast way to populate an object with array-like properties - this.length = 0; - Array.prototype.push.apply( this, elems ); - - return this; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - var ret = -1; - - // Locate the position of the desired element - this.each(function(i){ - if ( this == elem ) - ret = i; - }); - - return ret; - }, - - attr: function( name, value, type ) { - var options = name; - - // Look for the case where we're accessing a style value - if ( name.constructor == String ) - if ( value == undefined ) - return this.length && jQuery[ type || "attr" ]( this[0], name ) || undefined; - - else { - options = {}; - options[ name ] = value; - } - - // Check to see if we're setting style values - return this.each(function(i){ - // Set all the styles - for ( name in options ) - jQuery.attr( - type ? - this.style : - this, - name, jQuery.prop( this, options[ name ], type, i, name ) - ); - }); - }, - - css: function( key, value ) { - // ignore negative width and height values - if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) - value = undefined; - return this.attr( key, value, "curCSS" ); - }, - - text: function( text ) { - if ( typeof text != "object" && text != null ) - return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); - - var ret = ""; - - jQuery.each( text || this, function(){ - jQuery.each( this.childNodes, function(){ - if ( this.nodeType != 8 ) - ret += this.nodeType != 1 ? - this.nodeValue : - jQuery.fn.text( [ this ] ); - }); - }); - - return ret; - }, - - wrapAll: function( html ) { - if ( this[0] ) - // The elements to wrap the target around - jQuery( html, this[0].ownerDocument ) - .clone() - .insertBefore( this[0] ) - .map(function(){ - var elem = this; - - while ( elem.firstChild ) - elem = elem.firstChild; - - return elem; - }) - .append(this); - - return this; - }, - - wrapInner: function( html ) { - return this.each(function(){ - jQuery( this ).contents().wrapAll( html ); - }); - }, - - wrap: function( html ) { - return this.each(function(){ - jQuery( this ).wrapAll( html ); - }); - }, - - append: function() { - return this.domManip(arguments, true, false, function(elem){ - if (this.nodeType == 1) - this.appendChild( elem ); - }); - }, - - prepend: function() { - return this.domManip(arguments, true, true, function(elem){ - if (this.nodeType == 1) - this.insertBefore( elem, this.firstChild ); - }); - }, - - before: function() { - return this.domManip(arguments, false, false, function(elem){ - this.parentNode.insertBefore( elem, this ); - }); - }, - - after: function() { - return this.domManip(arguments, false, true, function(elem){ - this.parentNode.insertBefore( elem, this.nextSibling ); - }); - }, - - end: function() { - return this.prevObject || jQuery( [] ); - }, - - find: function( selector ) { - var elems = jQuery.map(this, function(elem){ - return jQuery.find( selector, elem ); - }); - - return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ? - jQuery.unique( elems ) : - elems ); - }, - - clone: function( events ) { - // Do the clone - var ret = this.map(function(){ - if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) { - // IE copies events bound via attachEvent when - // using cloneNode. Calling detachEvent on the - // clone will also remove the events from the orignal - // In order to get around this, we use innerHTML. - // Unfortunately, this means some modifications to - // attributes in IE that are actually only stored - // as properties will not be copied (such as the - // the name attribute on an input). - var clone = this.cloneNode(true), - container = document.createElement("div"); - container.appendChild(clone); - return jQuery.clean([container.innerHTML])[0]; - } else - return this.cloneNode(true); - }); - - // Need to set the expando to null on the cloned set if it exists - // removeData doesn't work here, IE removes it from the original as well - // this is primarily for IE but the data expando shouldn't be copied over in any browser - var clone = ret.find("*").andSelf().each(function(){ - if ( this[ expando ] != undefined ) - this[ expando ] = null; - }); - - // Copy the events from the original to the clone - if ( events === true ) - this.find("*").andSelf().each(function(i){ - if (this.nodeType == 3) - return; - var events = jQuery.data( this, "events" ); - - for ( var type in events ) - for ( var handler in events[ type ] ) - jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data ); - }); - - // Return the cloned set - return ret; - }, - - filter: function( selector ) { - return this.pushStack( - jQuery.isFunction( selector ) && - jQuery.grep(this, function(elem, i){ - return selector.call( elem, i ); - }) || - - jQuery.multiFilter( selector, this ) ); - }, - - not: function( selector ) { - if ( selector.constructor == String ) - // test special case where just one selector is passed in - if ( isSimple.test( selector ) ) - return this.pushStack( jQuery.multiFilter( selector, this, true ) ); - else - selector = jQuery.multiFilter( selector, this ); - - var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; - return this.filter(function() { - return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; - }); - }, - - add: function( selector ) { - return !selector ? this : this.pushStack( jQuery.merge( - this.get(), - selector.constructor == String ? - jQuery( selector ).get() : - selector.length != undefined && (!selector.nodeName || jQuery.nodeName(selector, "form")) ? - selector : [selector] ) ); - }, - - is: function( selector ) { - return selector ? - jQuery.multiFilter( selector, this ).length > 0 : - false; - }, - - hasClass: function( selector ) { - return this.is( "." + selector ); - }, - - val: function( value ) { - if ( value == undefined ) { - - if ( this.length ) { - var elem = this[0]; - - // We need to handle select boxes special - if ( jQuery.nodeName( elem, "select" ) ) { - var index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type == "select-one"; - - // Nothing was selected - if ( index < 0 ) - return null; - - // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[ i ]; - - if ( option.selected ) { - // Get the specifc value for the option - value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value; - - // We don't need an array for one selects - if ( one ) - return value; - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - - // Everything else, we just grab the value - } else - return (this[0].value || "").replace(/\r/g, ""); - - } - - return undefined; - } - - return this.each(function(){ - if ( this.nodeType != 1 ) - return; - - if ( value.constructor == Array && /radio|checkbox/.test( this.type ) ) - this.checked = (jQuery.inArray(this.value, value) >= 0 || - jQuery.inArray(this.name, value) >= 0); - - else if ( jQuery.nodeName( this, "select" ) ) { - var values = value.constructor == Array ? - value : - [ value ]; - - jQuery( "option", this ).each(function(){ - this.selected = (jQuery.inArray( this.value, values ) >= 0 || - jQuery.inArray( this.text, values ) >= 0); - }); - - if ( !values.length ) - this.selectedIndex = -1; - - } else - this.value = value; - }); - }, - - html: function( value ) { - return value == undefined ? - (this.length ? - this[0].innerHTML : - null) : - this.empty().append( value ); - }, - - replaceWith: function( value ) { - return this.after( value ).remove(); - }, - - eq: function( i ) { - return this.slice( i, i + 1 ); - }, - - slice: function() { - return this.pushStack( Array.prototype.slice.apply( this, arguments ) ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function(elem, i){ - return callback.call( elem, i, elem ); - })); - }, - - andSelf: function() { - return this.add( this.prevObject ); - }, - - data: function( key, value ){ - var parts = key.split("."); - parts[1] = parts[1] ? "." + parts[1] : ""; - - if ( value == null ) { - var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); - - if ( data == undefined && this.length ) - data = jQuery.data( this[0], key ); - - return data == null && parts[1] ? - this.data( parts[0] ) : - data; - } else - return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){ - jQuery.data( this, key, value ); - }); - }, - - removeData: function( key ){ - return this.each(function(){ - jQuery.removeData( this, key ); - }); - }, - - domManip: function( args, table, reverse, callback ) { - var clone = this.length > 1, elems; - - return this.each(function(){ - if ( !elems ) { - elems = jQuery.clean( args, this.ownerDocument ); - - if ( reverse ) - elems.reverse(); - } - - var obj = this; - - if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) ) - obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") ); - - var scripts = jQuery( [] ); - - jQuery.each(elems, function(){ - var elem = clone ? - jQuery( this ).clone( true )[0] : - this; - - // execute all scripts after the elements have been injected - if ( jQuery.nodeName( elem, "script" ) ) { - scripts = scripts.add( elem ); - } else { - // Remove any inner scripts for later evaluation - if ( elem.nodeType == 1 ) - scripts = scripts.add( jQuery( "script", elem ).remove() ); - - // Inject the elements into the document - callback.call( obj, elem ); - } - }); - - scripts.each( evalScript ); - }); - } -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.prototype.init.prototype = jQuery.prototype; - -function evalScript( i, elem ) { - if ( elem.src ) - jQuery.ajax({ - url: elem.src, - async: false, - dataType: "script" - }); - - else - jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); - - if ( elem.parentNode ) - elem.parentNode.removeChild( elem ); -} - -jQuery.extend = jQuery.fn.extend = function() { - // copy reference to target object - var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; - - // Handle a deep copy situation - if ( target.constructor == Boolean ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target != "object" && typeof target != "function" ) - target = {}; - - // extend jQuery itself if only one argument is passed - if ( length == 1 ) { - target = this; - i = 0; - } - - for ( ; i < length; i++ ) - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) - // Extend the base object - for ( var name in options ) { - // Prevent never-ending loop - if ( target === options[ name ] ) - continue; - - // Recurse if we're merging object values - if ( deep && options[ name ] && typeof options[ name ] == "object" && target[ name ] && !options[ name ].nodeType ) - target[ name ] = jQuery.extend( target[ name ], options[ name ] ); - - // Don't bring in undefined values - else if ( options[ name ] != undefined ) - target[ name ] = options[ name ]; - - } - - // Return the modified object - return target; -}; - -var expando = "jQuery" + (new Date()).getTime(), uuid = 0, windowData = {}; - -// exclude the following css properties to add px -var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i; - -jQuery.extend({ - noConflict: function( deep ) { - window.$ = _$; - - if ( deep ) - window.jQuery = _jQuery; - - return jQuery; - }, - - // See test/unit/core.js for details concerning this function. - isFunction: function( fn ) { - return !!fn && typeof fn != "string" && !fn.nodeName && - fn.constructor != Array && /function/i.test( fn + "" ); - }, - - // check if an element is in a (or is an) XML document - isXMLDoc: function( elem ) { - return elem.documentElement && !elem.body || - elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; - }, - - // Evalulates a script in a global context - globalEval: function( data ) { - data = jQuery.trim( data ); - - if ( data ) { - // Inspired by code by Andrea Giammarchi - // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html - var head = document.getElementsByTagName("head")[0] || document.documentElement, - script = document.createElement("script"); - - script.type = "text/javascript"; - if ( jQuery.browser.msie ) - script.text = data; - else - script.appendChild( document.createTextNode( data ) ); - - head.appendChild( script ); - head.removeChild( script ); - } - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); - }, - - cache: {}, - - data: function( elem, name, data ) { - elem = elem == window ? - windowData : - elem; - - var id = elem[ expando ]; - - // Compute a unique ID for the element - if ( !id ) - id = elem[ expando ] = ++uuid; - - // Only generate the data cache if we're - // trying to access or manipulate it - if ( name && !jQuery.cache[ id ] ) - jQuery.cache[ id ] = {}; - - // Prevent overriding the named cache with undefined values - if ( data != undefined ) - jQuery.cache[ id ][ name ] = data; - - // Return the named cache data, or the ID for the element - return name ? - jQuery.cache[ id ][ name ] : - id; - }, - - removeData: function( elem, name ) { - elem = elem == window ? - windowData : - elem; - - var id = elem[ expando ]; - - // If we want to remove a specific section of the element's data - if ( name ) { - if ( jQuery.cache[ id ] ) { - // Remove the section of cache data - delete jQuery.cache[ id ][ name ]; - - // If we've removed all the data, remove the element's cache - name = ""; - - for ( name in jQuery.cache[ id ] ) - break; - - if ( !name ) - jQuery.removeData( elem ); - } - - // Otherwise, we want to remove all of the element's data - } else { - // Clean up the element expando - try { - delete elem[ expando ]; - } catch(e){ - // IE has trouble directly removing the expando - // but it's ok with using removeAttribute - if ( elem.removeAttribute ) - elem.removeAttribute( expando ); - } - - // Completely remove the data cache - delete jQuery.cache[ id ]; - } - }, - - // args is for internal usage only - each: function( object, callback, args ) { - if ( args ) { - if ( object.length == undefined ) { - for ( var name in object ) - if ( callback.apply( object[ name ], args ) === false ) - break; - } else - for ( var i = 0, length = object.length; i < length; i++ ) - if ( callback.apply( object[ i ], args ) === false ) - break; - - // A special, fast, case for the most common use of each - } else { - if ( object.length == undefined ) { - for ( var name in object ) - if ( callback.call( object[ name ], name, object[ name ] ) === false ) - break; - } else - for ( var i = 0, length = object.length, value = object[0]; - i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} - } - - return object; - }, - - prop: function( elem, value, type, i, name ) { - // Handle executable functions - if ( jQuery.isFunction( value ) ) - value = value.call( elem, i ); - - // Handle passing in a number to a CSS property - return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ? - value + "px" : - value; - }, - - className: { - // internal only, use addClass("class") - add: function( elem, classNames ) { - jQuery.each((classNames || "").split(/\s+/), function(i, className){ - if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) - elem.className += (elem.className ? " " : "") + className; - }); - }, - - // internal only, use removeClass("class") - remove: function( elem, classNames ) { - if (elem.nodeType == 1) - elem.className = classNames != undefined ? - jQuery.grep(elem.className.split(/\s+/), function(className){ - return !jQuery.className.has( classNames, className ); - }).join(" ") : - ""; - }, - - // internal only, use is(".class") - has: function( elem, className ) { - return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; - } - }, - - // A method for quickly swapping in/out CSS properties to get correct calculations - swap: function( elem, options, callback ) { - var old = {}; - // Remember the old values, and insert the new ones - for ( var name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - callback.call( elem ); - - // Revert the old values - for ( var name in options ) - elem.style[ name ] = old[ name ]; - }, - - css: function( elem, name, force ) { - if ( name == "width" || name == "height" ) { - var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; - - function getWH() { - val = name == "width" ? elem.offsetWidth : elem.offsetHeight; - var padding = 0, border = 0; - jQuery.each( which, function() { - padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; - border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; - }); - val -= Math.round(padding + border); - } - - if ( jQuery(elem).is(":visible") ) - getWH(); - else - jQuery.swap( elem, props, getWH ); - - return Math.max(0, val); - } - - return jQuery.curCSS( elem, name, force ); - }, - - curCSS: function( elem, name, force ) { - var ret; - - // A helper method for determining if an element's values are broken - function color( elem ) { - if ( !jQuery.browser.safari ) - return false; - - var ret = document.defaultView.getComputedStyle( elem, null ); - return !ret || ret.getPropertyValue("color") == ""; - } - - // We need to handle opacity special in IE - if ( name == "opacity" && jQuery.browser.msie ) { - ret = jQuery.attr( elem.style, "opacity" ); - - return ret == "" ? - "1" : - ret; - } - // Opera sometimes will give the wrong display answer, this fixes it, see #2037 - if ( jQuery.browser.opera && name == "display" ) { - var save = elem.style.outline; - elem.style.outline = "0 solid black"; - elem.style.outline = save; - } - - // Make sure we're using the right name for getting the float value - if ( name.match( /float/i ) ) - name = styleFloat; - - if ( !force && elem.style && elem.style[ name ] ) - ret = elem.style[ name ]; - - else if ( document.defaultView && document.defaultView.getComputedStyle ) { - - // Only "float" is needed here - if ( name.match( /float/i ) ) - name = "float"; - - name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); - - var getComputedStyle = document.defaultView.getComputedStyle( elem, null ); - - if ( getComputedStyle && !color( elem ) ) - ret = getComputedStyle.getPropertyValue( name ); - - // If the element isn't reporting its values properly in Safari - // then some display: none elements are involved - else { - var swap = [], stack = []; - - // Locate all of the parent display: none elements - for ( var a = elem; a && color(a); a = a.parentNode ) - stack.unshift(a); - - // Go through and make them visible, but in reverse - // (It would be better if we knew the exact display type that they had) - for ( var i = 0; i < stack.length; i++ ) - if ( color( stack[ i ] ) ) { - swap[ i ] = stack[ i ].style.display; - stack[ i ].style.display = "block"; - } - - // Since we flip the display style, we have to handle that - // one special, otherwise get the value - ret = name == "display" && swap[ stack.length - 1 ] != null ? - "none" : - ( getComputedStyle && getComputedStyle.getPropertyValue( name ) ) || ""; - - // Finally, revert the display styles back - for ( var i = 0; i < swap.length; i++ ) - if ( swap[ i ] != null ) - stack[ i ].style.display = swap[ i ]; - } - - // We should always get a number back from opacity - if ( name == "opacity" && ret == "" ) - ret = "1"; - - } else if ( elem.currentStyle ) { - var camelCase = name.replace(/\-(\w)/g, function(all, letter){ - return letter.toUpperCase(); - }); - - ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { - // Remember the original values - var style = elem.style.left, runtimeStyle = elem.runtimeStyle.left; - - // Put in the new values to get a computed value out - elem.runtimeStyle.left = elem.currentStyle.left; - elem.style.left = ret || 0; - ret = elem.style.pixelLeft + "px"; - - // Revert the changed values - elem.style.left = style; - elem.runtimeStyle.left = runtimeStyle; - } - } - - return ret; - }, - - clean: function( elems, context ) { - var ret = []; - context = context || document; - // !context.createElement fails in IE with an error but returns typeof 'object' - if (typeof context.createElement == 'undefined') - context = context.ownerDocument || context[0] && context[0].ownerDocument || document; - - jQuery.each(elems, function(i, elem){ - if ( !elem ) - return; - - if ( elem.constructor == Number ) - elem = elem.toString(); - - // Convert html string into DOM nodes - if ( typeof elem == "string" ) { - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ - return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? - all : - front + ">"; - }); - - // Trim whitespace, otherwise indexOf won't work as expected - var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div"); - - var wrap = - // option or optgroup - !tags.indexOf("", "" ] || - - !tags.indexOf("", "" ] || - - tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && - [ 1, "", "
" ] || - - !tags.indexOf("", "" ] || - - // matched above - (!tags.indexOf("", "" ] || - - !tags.indexOf("", "" ] || - - // IE can't serialize and "); /* Attempt to cache the CSS & JavaScript files */ if ($this->add_cached_auto_html_headers() === FALSE) { diff --git a/core/user.class.php b/core/user.class.php index 85d90df5..843d6c9d 100644 --- a/core/user.class.php +++ b/core/user.class.php @@ -4,6 +4,7 @@ function _new_user($row) { return new User($row); } + /** * An object representing a row in the "users" table. * @@ -90,6 +91,77 @@ class User { /* * useful user object functions start here */ + public function can($ability) { + global $config; + + // TODO: make this into an editable database table + $user_classes = array( + "anonymous" => array( + "change_setting" => False, # web-level settings, eg the config table + "override_config" => False, # sys-level config, eg config.php + "big_search" => False, # more than 3 tags (speed mode only) + "lock_image" => False, + "view_ip" => False, # view IP addresses associated with things + "change_password" => False, + "change_user_info" => False, + "delete_user" => False, + "delete_image" => False, + "delete_comment" => False, + "replace_image" => False, + "manage_extension_list" => False, + "manage_alias_list" => False, + "edit_tag" => $config->get_bool("tag_edit_anon"), + "edit_source" => $config->get_bool("source_edit_anon"), + "mass_tag_edit" => False, + ), + "user" => array( + "change_setting" => False, + "override_config" => False, + "big_search" => True, + "lock_image" => False, + "view_ip" => False, + "change_password" => False, + "change_user_info" => False, + "delete_user" => False, + "delete_image" => False, + "delete_comment" => False, + "replace_image" => False, + "manage_extension_list" => False, + "manage_alias_list" => False, + "edit_tag" => True, + "edit_source" => True, + "mass_tag_edit" => False, + ), + "admin" => array( + "change_setting" => True, + "override_config" => True, + "big_search" => True, + "lock_image" => True, + "view_ip" => True, + "change_password" => True, + "change_user_info" => True, + "delete_user" => True, + "delete_image" => True, + "delete_comment" => True, + "replace_image" => True, + "manage_extension_list" => True, + "manage_alias_list" => True, + "edit_tag" => True, + "edit_source" => True, + "mass_tag_edit" => True, + ), + ); + + return $user_classes[$this->get_class()][$ability]; + } + + // FIXME: this should be a column in the users table + public function get_class() { + if($this->is_admin()) return "admin"; + else if($this->is_logged_in()) return "user"; + else return"anonymous"; + } + /** * Test if this user is anonymous (not logged in) @@ -144,6 +216,7 @@ class User { /** * Get a snippet of HTML which will render the user's avatar, be that * a local file, a remote file, a gravatar, a something else, etc + * @retval String of HTML */ public function get_avatar_html() { // FIXME: configurable @@ -170,6 +243,8 @@ class User { * authtok = md5(sesskey, salt), presented to the user in web forms, to make sure that * the form was generated within the session. Salted and re-hashed so that * reading a web page from the user's cache doesn't give access to the session key + * + * @retval String containing auth token (MD5sum) */ public function get_auth_token() { global $config; diff --git a/core/util.inc.php b/core/util.inc.php index 882c8560..8ddb2fb3 100644 --- a/core/util.inc.php +++ b/core/util.inc.php @@ -190,12 +190,26 @@ function undb_bool($val) { if($val === false || $val == 'N' || $val == 'n' || $val == 'F' || $val == 'f' || $val === 0) return false; } -function startsWith($haystack, $needle) { +/** + * Checks if a given string contains another at the beginning. + * + * @param $haystack String to examine. + * @param $needle String to look for. + * @retval bool + */ +function startsWith(/*string*/ $haystack, /*string*/ $needle) { $length = strlen($needle); return (substr($haystack, 0, $length) === $needle); } -function endsWith($haystack, $needle) { +/** + * Checks if a given string contains another at the end. + * + * @param $haystack String to examine. + * @param $needle String to look for. + * @retval bool + */ +function endsWith(/*string*/ $haystack, /*string*/ $needle) { $length = strlen($needle); $start = $length * -1; //negative return (substr($haystack, $start) === $needle); @@ -621,6 +635,7 @@ function log_msg($section, $priority, $message) { send_event(new LogEvent($section, $priority, $message)); } +// More shorthand ways of logging function log_debug($section, $message) {log_msg($section, SCORE_LOG_DEBUG, $message);} function log_info($section, $message) {log_msg($section, SCORE_LOG_INFO, $message);} function log_warning($section, $message) {log_msg($section, SCORE_LOG_WARNING, $message);} @@ -847,6 +862,13 @@ function send_event(Event $event) { // string representation of a number, it's two numbers separated by a space. // What the fuck were the PHP developers smoking. $_load_start = microtime(true); + +/** + * Collects some debug information (execution time, memory usage, queries, etc) + * and formats it to stick in the footer of the page. + * + * @retval String of debug info to add to the page. + */ function get_debug_info() { global $config, $_event_count, $database, $_execs, $_load_start; @@ -879,7 +901,7 @@ function get_debug_info() { // print_obj ($object, $title, $return) function print_obj($object,$title="Object Information", $return=false) { global $user; - if(DEBUG && isset($_GET['debug']) && $user->is_admin()) { + if(DEBUG && isset($_GET['DEBUG']) && $user->can("override_config")) { $pr = print_r($object,true); $count = substr_count($pr,"\n")<=25?substr_count($pr,"\n"):25; $pr = ""; @@ -1051,6 +1073,9 @@ function _load_extensions() { ctx_log_endok(); } +/** + * Used to display fatal errors to the web user. + */ function _fatal_error(Exception $e) { $version = VERSION; $message = $e->getMessage(); diff --git a/ext/alias_editor/main.php b/ext/alias_editor/main.php index 2942809d..7a246144 100755 --- a/ext/alias_editor/main.php +++ b/ext/alias_editor/main.php @@ -28,7 +28,7 @@ class AliasEditor extends SimpleExtension { if($event->page_matches("alias")) { if($event->get_arg(0) == "add") { - if($user->is_admin()) { + if($user->can("manage_alias_list")) { if(isset($_POST['oldtag']) && isset($_POST['newtag'])) { try { $aae = new AddAliasEvent($_POST['oldtag'], $_POST['newtag']); @@ -43,7 +43,7 @@ class AliasEditor extends SimpleExtension { } } else if($event->get_arg(0) == "remove") { - if($user->is_admin()) { + if($user->can("manage_alias_list")) { if(isset($_POST['oldtag'])) { $database->execute("DELETE FROM aliases WHERE oldtag=:oldtag", array("oldtag" => $_POST['oldtag'])); log_info("alias_editor", "Deleted alias for ".$_POST['oldtag']); @@ -74,7 +74,7 @@ class AliasEditor extends SimpleExtension { $total_pages = ceil($database->get_one("SELECT COUNT(*) FROM aliases") / $alias_per_page); - $this->theme->display_aliases($page, $alias, $user->is_admin(), $page_number + 1, $total_pages); + $this->theme->display_aliases($alias, $page_number + 1, $total_pages); } else if($event->get_arg(0) == "export") { $page->set_mode("data"); @@ -82,7 +82,7 @@ class AliasEditor extends SimpleExtension { $page->set_data($this->get_alias_csv($database)); } else if($event->get_arg(0) == "import") { - if($user->is_admin()) { + if($user->can("manage_alias_list")) { if(count($_FILES) > 0) { $tmp = $_FILES['alias_file']['tmp_name']; $contents = file_get_contents($tmp); @@ -115,7 +115,7 @@ class AliasEditor extends SimpleExtension { public function onUserBlockBuilding(UserBlockBuildingEvent $event) { global $user; - if($user->is_admin()) { + if($user->can("manage_alias_list")) { $event->add_link("Alias Editor", make_link("alias/list")); } } diff --git a/ext/alias_editor/theme.php b/ext/alias_editor/theme.php index cec35af0..7b6bfb49 100644 --- a/ext/alias_editor/theme.php +++ b/ext/alias_editor/theme.php @@ -5,10 +5,13 @@ class AliasEditorTheme extends Themelet { * Show a page of aliases: * * $aliases = an array of ($old_tag => $new_tag) - * $is_admin = whether things like "add new alias" should be shown + * $can_manage = whether things like "add new alias" should be shown */ - public function display_aliases(Page $page, $aliases, $is_admin, $pageNumber, $totalPages) { - if($is_admin) { + public function display_aliases($aliases, $pageNumber, $totalPages) { + global $page, $user; + + $can_manage = $user->can("manage_alias_list"); + if($can_manage) { $action = "Action"; $add = " @@ -33,7 +36,7 @@ class AliasEditorTheme extends Themelet { $oe = ($n++ % 2 == 0) ? "even" : "odd"; $h_aliases .= "$h_old$h_new"; - if($is_admin) { + if($can_manage) { $h_aliases .= " ".make_form(make_link("alias/remove"))." @@ -70,7 +73,7 @@ class AliasEditorTheme extends Themelet { $page->set_heading("Alias List"); $page->add_block(new NavBlock()); $page->add_block(new Block("Aliases", $html)); - if($is_admin) { + if($can_manage) { $page->add_block(new Block("Bulk Upload", $bulk_html, "main", 51)); } diff --git a/ext/bbcode/main.php b/ext/bbcode/main.php index 239f06c2..420b5033 100644 --- a/ext/bbcode/main.php +++ b/ext/bbcode/main.php @@ -38,7 +38,8 @@ class BBCode extends FormatterExtension { $text = preg_replace("/\[i\](.*?)\[\/i\]/s", "\\1", $text); $text = preg_replace("/\[u\](.*?)\[\/u\]/s", "\\1", $text); $text = preg_replace("/\[s\](.*?)\[\/s\]/s", "\\1", $text); - $text = preg_replace("/>>(\d+)/s", ">>\\1", $text); + $text = preg_replace("/>>(\d+)(#\d+)?/s", ">>\\1\\2", $text); + $text = preg_replace("/(^|\s)#(\d+)/s", "\\1#\\2", $text); $text = preg_replace("/>>([^\d].+)/", "
\\1
", $text); $text = preg_replace("/\[url=((?:https?|ftp|irc|mailto):\/\/.*?)\](.*?)\[\/url\]/s", "\\2", $text); $text = preg_replace("/\[url\]((?:https?|ftp|irc|mailto):\/\/.*?)\[\/url\]/s", "\\1", $text); diff --git a/ext/comment/main.php b/ext/comment/main.php index f260ace2..b64a8984 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -128,7 +128,7 @@ class CommentList extends SimpleExtension { } } else if($event->get_arg(0) === "delete") { - if($user->is_admin()) { + if($user->can("delete_comment")) { // FIXME: post, not args if($event->count_args() === 3) { send_event(new CommentDeletionEvent($event->get_arg(1))); @@ -173,7 +173,6 @@ class CommentList extends SimpleExtension { $h_comment_rate = sprintf("%.1f", ($i_comment_count / $i_days_old)); $event->add_stats("Comments made: $i_comment_count, $h_comment_rate per day"); - global $user; $recent = $this->get_user_recent_comments($event->display_user->id, 10); $this->theme->display_user_comments($recent); } diff --git a/ext/comment/theme.php b/ext/comment/theme.php index 15911c47..472d2daa 100644 --- a/ext/comment/theme.php +++ b/ext/comment/theme.php @@ -143,46 +143,53 @@ class CommentListTheme extends Themelet { $h_name = html_escape($comment->owner_name); $h_poster_ip = html_escape($comment->poster_ip); $h_timestamp = autodate($comment->posted); - $h_comment = ($trim ? substr($tfe->stripped, 0, 50)."..." : $tfe->formatted); + $h_comment = ($trim ? substr($tfe->stripped, 0, 50) . (strlen($tfe->stripped) > 50 ? "..." : "") : $tfe->formatted); $i_comment_id = int_escape($comment->comment_id); $i_image_id = int_escape($comment->image_id); - $anoncode = ""; - if($h_name == "Anonymous" && $this->anon_id >= 0) { - $anoncode = ''.$this->anon_id.''; - $this->anon_id++; + if($h_name == "Anonymous") { + $anoncode = ""; + if($this->anon_id >= 0) { + $anoncode = ''.$this->anon_id.''; + $this->anon_id++; + } + $h_userlink = $h_name . $anoncode; + } + else { + $h_userlink = ''.$h_name.''; } - $h_userlink = ''.$h_name.''.$anoncode; $stripped_nonl = str_replace("\n", "\\n", substr($tfe->stripped, 0, 50)); $stripped_nonl = str_replace("\r", "\\r", $stripped_nonl); - $h_dellink = $user->is_admin() ? - '
('.$h_poster_ip.', '.$h_timestamp.', Del)' : ''; if($trim) { return ' '.$h_userlink.': '.$h_comment.' >>> - '.$h_dellink.' '; } else { - //$avatar = ""; - //if(!empty($comment->owner->email)) { - // $hash = md5(strtolower($comment->owner->email)); - // $avatar = "
"; - //} - $oe = ($this->comments_shown++ % 2 == 0) ? "even" : "odd"; + $avatar = ""; + if(!empty($comment->owner_email)) { + $hash = md5(strtolower($comment->owner_email)); + $avatar = "
"; + } + $h_reply = " - Reply"; + $h_ip = $user->can("view_ip") ? "
$h_poster_ip" : ""; + $h_del = $user->can("delete_comment") ? + ' - Del' : ''; return ' -
- - '.$h_userlink.': '.$h_comment.' - '.$h_dellink.' +
+
+ '.$avatar.' + '.$h_timestamp.$h_reply.$h_ip.$h_del.' +
+ '.$h_userlink.': '.$h_comment.'
'; } + return ""; } protected function build_postbox($image_id) { @@ -196,7 +203,7 @@ class CommentListTheme extends Themelet { '.make_form(make_link("comment/add")).' - + '.$captcha.'
diff --git a/ext/ext_manager/main.php b/ext/ext_manager/main.php index 1b90ef0a..3500401c 100644 --- a/ext/ext_manager/main.php +++ b/ext/ext_manager/main.php @@ -91,7 +91,7 @@ class ExtManager extends SimpleExtension { public function onPageRequest(PageRequestEvent $event) { global $page, $user; if($event->page_matches("ext_manager")) { - if($user->is_admin()) { + if($user->can("manage_extension_list")) { if($event->get_arg(0) == "set" && $user->check_auth_token()) { if(is_writable("ext")) { $this->set_things($_POST); @@ -130,7 +130,7 @@ class ExtManager extends SimpleExtension { public function onUserBlockBuilding(UserBlockBuildingEvent $event) { global $user; - if($user->is_admin()) { + if($user->can("manage_extension_list")) { $event->add_link("Extension Manager", make_link("ext_manager")); } else { diff --git a/ext/image/main.php b/ext/image/main.php index 0fde1e24..d0288131 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -162,7 +162,7 @@ class ImageIO extends SimpleExtension { } if($event->page_matches("image_admin/delete")) { global $page, $user; - if($user->is_admin() && isset($_POST['image_id']) && $user->check_auth_token()) { + if($user->can("delete_image") && isset($_POST['image_id']) && $user->check_auth_token()) { $image = Image::by_id($_POST['image_id']); if($image) { send_event(new ImageDeletionEvent($image)); @@ -173,7 +173,7 @@ class ImageIO extends SimpleExtension { } if($event->page_matches("image_admin/replace")) { global $page, $user; - if($user->is_admin() && isset($_POST['image_id']) && $user->check_auth_token()) { + if($user->can("replace_image") && isset($_POST['image_id']) && $user->check_auth_token()) { $image = Image::by_id($_POST['image_id']); if($image) { $page->set_mode("redirect"); @@ -190,11 +190,11 @@ class ImageIO extends SimpleExtension { global $user; global $config; - if($user->is_admin()) { + if($user->can("delete_image")) { $event->add_part($this->theme->get_deleter_html($event->image->id)); } /* In the future, could perhaps allow users to replace images that they own as well... */ - if ($user->is_admin() && $config->get_bool("upload_replace")) { + if ($user->can("replace_image") && $config->get_bool("upload_replace")) { $event->add_part($this->theme->get_replace_html($event->image->id)); } } diff --git a/ext/index/theme.php b/ext/index/theme.php index c7d2cb95..0a4b1fa8 100644 --- a/ext/index/theme.php +++ b/ext/index/theme.php @@ -79,23 +79,8 @@ and of course start organising your images :-) $h_search_string = html_escape(implode(" ", $search_terms)); $h_search_link = make_link(); $h_search = " -

- diff --git a/ext/setup/main.php b/ext/setup/main.php index f93ac7fa..4e5b950a 100644 --- a/ext/setup/main.php +++ b/ext/setup/main.php @@ -187,7 +187,7 @@ class Setup extends SimpleExtension { } if($event->page_matches("setup")) { - if(!$user->is_admin()) { + if(!$user->can("change_setting")) { $this->theme->display_permission_denied($page); } else { @@ -329,7 +329,7 @@ class Setup extends SimpleExtension { public function onUserBlockBuilding(UserBlockBuildingEvent $event) { global $user; - if($user->is_admin()) { + if($user->can("change_setting")) { $event->add_link("Board Config", make_link("setup")); } } diff --git a/ext/tag_edit/main.php b/ext/tag_edit/main.php index a06425c1..184a5dba 100644 --- a/ext/tag_edit/main.php +++ b/ext/tag_edit/main.php @@ -60,7 +60,7 @@ class TagEdit extends SimpleExtension { global $user, $page; if($event->page_matches("tag_edit")) { if($event->get_arg(0) == "replace") { - if($user->is_admin() && isset($_POST['search']) && isset($_POST['replace'])) { + if($user->can("mass_tag_edit") && isset($_POST['search']) && isset($_POST['replace'])) { $search = $_POST['search']; $replace = $_POST['replace']; $this->mass_tag_edit($search, $replace); @@ -82,7 +82,7 @@ class TagEdit extends SimpleExtension { else { $this->theme->display_error($page, "Error", "Anonymous tag editing is disabled"); } - if($user->is_admin()) { + if($user->can("lock_image")) { $locked = isset($_POST['tag_edit__locked']) && $_POST['tag_edit__locked']=="on"; send_event(new LockSetEvent($event->image, $locked)); } @@ -90,21 +90,21 @@ class TagEdit extends SimpleExtension { public function onTagSet(TagSetEvent $event) { global $user; - if($user->is_admin() || !$event->image->is_locked()) { + if($user->can("edit_tag") || !$event->image->is_locked()) { $event->image->set_tags($event->tags); } } public function onSourceSet(SourceSetEvent $event) { global $user; - if($user->is_admin() || !$event->image->is_locked()) { + if($user->can("edit_tag") || !$event->image->is_locked()) { $event->image->set_source($event->source); } } public function onLockSet(LockSetEvent $event) { global $user; - if($user->is_admin()) { + if($user->can("lock_image")) { $event->image->set_locked($event->locked); } } @@ -130,7 +130,7 @@ class TagEdit extends SimpleExtension { if($this->can_source($event->image)) { $event->add_part($this->theme->get_source_editor_html($event->image), 41); } - if($user->is_admin()) { + if($user->can("lock_image")) { $event->add_part($this->theme->get_lock_editor_html($event->image), 42); } } @@ -147,7 +147,7 @@ class TagEdit extends SimpleExtension { global $config, $user; return ( ($config->get_bool("tag_edit_anon") || !$user->is_anonymous()) && - ($user->is_admin() || !$image->is_locked()) + ($user->can("edit_tag") || !$image->is_locked()) ); } @@ -155,7 +155,7 @@ class TagEdit extends SimpleExtension { global $config, $user; return ( ($config->get_bool("source_edit_anon") || !$user->is_anonymous()) && - ($user->is_admin() || !$image->is_locked()) + ($user->can("edit_source") || !$image->is_locked()) ); } diff --git a/ext/tag_list/main.php b/ext/tag_list/main.php index c30aacb6..c7d57716 100644 --- a/ext/tag_list/main.php +++ b/ext/tag_list/main.php @@ -1,7 +1,8 @@ + * Link: http://code.shishnet.org/shimmie2/ * Description: Show the tags in various ways */ @@ -107,13 +108,18 @@ class TagList extends SimpleExtension { return make_link("post/list/$u_tag/1"); } + /** + * Get the minimum number of times a tag needs to be used + * in order to be considered in the tag list. + * @retval int + */ private function get_tags_min() { if(isset($_GET['mincount'])) { return int_escape($_GET['mincount']); } else { global $config; - return $config->get_int('tags_min'); + return $config->get_int('tags_min'); // get the default. } } @@ -170,6 +176,8 @@ class TagList extends SimpleExtension { $tags_min = $this->get_tags_min(); $starts_with = $this->get_starts_with(); + + // check if we have a cached version $cache_key = "data/tag_cloud-" . md5("tc" . $tags_min . $starts_with) . ".html"; if(file_exists($cache_key)) {return file_get_contents($cache_key);} @@ -205,6 +213,8 @@ class TagList extends SimpleExtension { $tags_min = $this->get_tags_min(); $starts_with = $this->get_starts_with(); + + // check if we have a cached version $cache_key = "data/tag_alpha-" . md5("ta" . $tags_min . $starts_with) . ".html"; if(file_exists($cache_key)) {return file_get_contents($cache_key);} @@ -239,6 +249,8 @@ class TagList extends SimpleExtension { global $database; $tags_min = $this->get_tags_min(); + + // check if we have a cached version $cache_key = "data/tag_popul-" . md5("tp" . $tags_min) . ".html"; if(file_exists($cache_key)) {return file_get_contents($cache_key);} diff --git a/ext/upgrade/main.php b/ext/upgrade/main.php index 4d8f32b9..7445d2d4 100644 --- a/ext/upgrade/main.php +++ b/ext/upgrade/main.php @@ -1,7 +1,8 @@ + * Link: http://code.shishnet.org/shimmie2/ * Description: Keeps things happy behind the scenes * Visibility: admin */ @@ -10,6 +11,8 @@ class Upgrade extends SimpleExtension { public function onInitExt(InitExtEvent $event) { global $config, $database; + if($config->get_bool("in_upgrade")) return; + if(!is_numeric($config->get_string("db_version"))) { $config->set_int("db_version", 2); } @@ -18,28 +21,43 @@ class Upgrade extends SimpleExtension { // cry :S } - if($config->get_int("db_version") < 7) { - /* - // mysql-adodb specific - if($database->engine->name == "mysql") { - $tables = $database->db->MetaTables(); - foreach($tables as $table) { - log_info("upgrade", "converting $table to innodb"); - $database->execute("ALTER TABLE $table TYPE=INNODB"); - } - } - */ - $config->set_int("db_version", 7); - log_info("upgrade", "Database at version 7"); - } + // v7 is convert to innodb with adodb + // now done again as v9 with PDO if($config->get_int("db_version") < 8) { // if this fails, don't try again + $config->set_bool("in_upgrade", true); $config->set_int("db_version", 8); $database->execute($database->engine->scoreql_to_sql( "ALTER TABLE images ADD COLUMN locked SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N" )); log_info("upgrade", "Database at version 8"); + $config->set_bool("in_upgrade", false); + } + + if($config->get_int("db_version") < 9) { + $config->set_bool("in_upgrade", true); + if($database->db->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql') { + $tables = $database->get_col("SHOW TABLES"); + foreach($tables as $table) { + log_info("upgrade", "converting $table to innodb"); + $database->execute("ALTER TABLE $table TYPE=INNODB"); + } + } + $config->set_int("db_version", 9); + log_info("upgrade", "Database at version 9"); + $config->set_bool("in_upgrade", false); + } + + if($config->get_int("db_version") < 10) { + $config->set_bool("in_upgrade", true); + + log_info("upgrade", "Adding foreign keys to images"); + $database->Execute("ALTER TABLE images ADD CONSTRAINT foreign_images_owner_id FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT"); + + $config->set_int("db_version", 10); + log_info("upgrade", "Database at version 10"); + $config->set_bool("in_upgrade", false); } } diff --git a/ext/upload/main.php b/ext/upload/main.php index 2aee068f..41787276 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -1,5 +1,5 @@ is_admin()) { + if(!$user->can("replace_image")) { $this->theme->display_permission_denied($page); } else { @@ -224,14 +224,28 @@ class Upload extends SimpleExtension { } // }}} // do things {{{ + + /** + * Check if a given user can upload. + * @param $user The user to check. + * @retval bool + */ private function can_upload(User $user) { global $config; return ($config->get_bool("upload_anon") || !$user->is_anonymous()); } - // Helper function based on the one from the online PHP Documentation - // which is licensed under Creative Commons Attribution 3.0 License - // TODO: Make these messages user/admin editable + /** + * Returns a descriptive error message for the specified PHP error code. + * + * This is a helper function based on the one from the online PHP Documentation + * which is licensed under Creative Commons Attribution 3.0 License + * + * TODO: Make these messages user/admin editable + * + * @param $error_code PHP error code (int) + * @retval String + */ private function upload_error_message($error_code) { switch ($error_code) { case UPLOAD_ERR_INI_SIZE: @@ -253,6 +267,10 @@ class Upload extends SimpleExtension { } } + /** + * Handle an upload. + * @retval bool TRUE on upload successful. + */ private function try_upload($file, $tags, $source, $replace='') { global $page; global $config; @@ -299,6 +317,10 @@ class Upload extends SimpleExtension { return $ok; } + /** + * Handle an transload. + * @retval bool TRUE on transload successful. + */ private function try_transload($url, $tags, $source, $replace='') { global $page; global $config; @@ -314,7 +336,7 @@ class Upload extends SimpleExtension { } // Checks if user is admin > check if you want locked. - if($user->is_admin() && !empty($_GET['locked'])){ + if($user->can("lock_image") && !empty($_GET['locked'])){ $locked = bool_escape($_GET['locked']); } diff --git a/ext/user/main.php b/ext/user/main.php index 0950c220..678b5b9c 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -158,7 +158,7 @@ class UserPage extends SimpleExtension { $this->theme->display_error($page, "Not Logged In", "You aren't logged in. First do that, then you can see your stats."); } - else if(!is_null($display_user)) { + else if(!is_null($display_user) && ($display_user->id != $config->get_int("anon_id"))) { send_event(new UserPageBuildingEvent($display_user)); } else { @@ -187,7 +187,7 @@ class UserPage extends SimpleExtension { $this->theme->display_user_links($page, $user, $ubbe->parts); } if( - ($user->is_admin() || ($user->is_logged_in() && $user->id == $event->display_user->id)) && # admin or self-user + ($user->can("view_ip") || ($user->is_logged_in() && $user->id == $event->display_user->id)) && # admin or self-user ($event->display_user->id != $config->get_int('anon_id')) # don't show anon's IP list, it is le huge ) { $this->theme->display_ip_list( @@ -256,7 +256,7 @@ class UserPage extends SimpleExtension { $user_id = int_escape($matches[2]); $event->add_querylet(new Querylet("images.owner_id = $user_id")); } - else if($user->is_admin() && preg_match("/^(poster|user)_ip=([0-9\.]+)$/i", $event->term, $matches)) { + else if($user->can("view_ip") && preg_match("/^(poster|user)_ip=([0-9\.]+)$/i", $event->term, $matches)) { $user_ip = $matches[2]; // FIXME: ip_escape? $event->add_querylet(new Querylet("images.owner_ip = '$user_ip'")); } @@ -354,7 +354,7 @@ class UserPage extends SimpleExtension { $duser = User::by_id($id); - if((!$user->is_admin()) && ($duser->name != $user->name)) { + if((!$user->can("change_user_info")) && ($duser->name != $user->name)) { $this->theme->display_error($page, "Error", "You need to be an admin to change other people's passwords"); } @@ -392,7 +392,7 @@ class UserPage extends SimpleExtension { $duser = User::by_id($id); - if((!$user->is_admin()) && ($duser->name != $user->name)) { + if((!$user->can("change_user_info")) && ($duser->name != $user->name)) { $this->theme->display_error($page, "Error", "You need to be an admin to change other people's addressess"); } @@ -419,7 +419,7 @@ class UserPage extends SimpleExtension { $page->set_title("Error"); $page->set_heading("Error"); $page->add_block(new NavBlock()); - if(!$user->is_admin()) { + if(!$user->can("change_user_info")) { $page->add_block(new Block("Not Admin", "Only admins can edit accounts")); } else if(!isset($_POST['id']) || !is_numeric($_POST['id'])) { @@ -479,7 +479,7 @@ class UserPage extends SimpleExtension { $page->set_heading("Error"); $page->add_block(new NavBlock()); - if (!$user->is_admin()) { + if (!$user->can("delete_user")) { $page->add_block(new Block("Not Admin", "Only admins can delete accounts")); } else if(!isset($_POST['id']) || !is_numeric($_POST['id'])) { @@ -510,7 +510,7 @@ class UserPage extends SimpleExtension { $page->set_heading("Error"); $page->add_block(new NavBlock()); - if (!$user->is_admin()) { + if (!$user->can("delete_user") || !$user->can("delete_image")) { $page->add_block(new Block("Not Admin", "Only admins can delete accounts")); } else if(!isset($_POST['id']) || !is_numeric($_POST['id'])) { diff --git a/ext/user/theme.php b/ext/user/theme.php index df6b7553..69b852fc 100644 --- a/ext/user/theme.php +++ b/ext/user/theme.php @@ -141,7 +141,7 @@ class UserPageTheme extends Themelet { $page->add_block(new Block("Stats", join("
", $stats), "main", 0)); if(!$user->is_anonymous()) { - if($user->id == $duser->id || $user->is_admin()) { + if($user->id == $duser->id || $user->can("change_user_info")) { $page->add_block(new Block("Options", $this->build_options($duser), "main", 20)); } } @@ -173,7 +173,7 @@ class UserPageTheme extends Themelet {
"; - if($user->is_admin()) { + if($user->can("change_user_info")) { $i_user_id = int_escape($duser->id); $h_is_admin = $duser->is_admin() ? " checked" : ""; $html .= " diff --git a/ext/view/theme.php b/ext/view/theme.php index 59f6b730..666ac8bf 100644 --- a/ext/view/theme.php +++ b/ext/view/theme.php @@ -90,7 +90,7 @@ class ViewImageTheme extends Themelet { $html = ""; $html .= "

Uploaded by $h_owner $h_date"; - if($user->is_admin()) { + if($user->can("view_ip")) { $html .= " ($h_ip)"; } if(!is_null($image->source)) { diff --git a/index.php b/index.php index 8bd09936..f392be7c 100644 --- a/index.php +++ b/index.php @@ -50,7 +50,7 @@ * Each of these can be imported at the start of a function with eg "global $page, $user;" */ -if(empty($database_dsn) && !file_exists("config.php")) { +if(!file_exists("config.php")) { header("Location: install.php"); exit; } diff --git a/install.php b/install.php index 09da4d33..139bc4c5 100755 --- a/install.php +++ b/install.php @@ -1,7 +1,8 @@ - + + Shimmie Installation - @@ -31,9 +33,10 @@ TD INPUT {width: 350px;}

Install Error

Shimmie needs to be run via a web server with PHP support -- you appear to be either opening the file from your hard disk, or your - web server is mis-configured. + web server is mis-configured.

If you've installed a web server on your desktop PC, you probably - want to visit the local web server. + want to visit the local web server.

+

@@ -51,9 +54,32 @@ if(is_readable("config.php")) { <div id="iblock"> <h1>Shimmie Repair Console</h1> <?php - include "config.php"; - if($_SESSION['dsn'] == DATABASE_DSN || $_POST['dsn'] == DATABASE_DSN) { - if($_POST['dsn']) {$_SESSION['dsn'] = $_POST['dsn'];} + + /* + * Compute the path to the folder containing "install.php" and + * store it as the 'Shimmie Root' folder for later on. + * + * Example: + * __SHIMMIE_ROOT__ = '/var/www/shimmie2/' + * + */ + define('__SHIMMIE_ROOT__', trim( remove_trailing_slash( dirname(__FILE__) ) ) . '/' ); + + // Pull in necessary files + require_once __SHIMMIE_ROOT__."config.php"; // Load user/site specifics First + require_once __SHIMMIE_ROOT__."core/default_config.inc.php"; // Defaults for the rest. + require_once __SHIMMIE_ROOT__."core/util.inc.php"; + require_once __SHIMMIE_ROOT__."core/database.class.php"; + + if ( + ( array_key_exists('dsn', $_SESSION) && $_SESSION['dsn'] === DATABASE_DSN ) || + ( array_key_exists('dsn', $_POST) && $_POST['dsn'] === DATABASE_DSN ) + ) + { + if ( array_key_exists('dsn', $_POST) && !empty($_POST['dsn']) ) + { + $_SESSION['dsn'] = $_POST['dsn']; + } if(empty($_GET["action"])) { echo "<h3>Basic Checks</h3>"; @@ -76,15 +102,6 @@ if(is_readable("config.php")) { </form> "; */ - echo "<h3>Database quick fix for User deletion</h3>"; - echo "just a database fix for those who instaled shimmie before 2012 january the 22rd.<br>"; - echo "Note: some things needs to be done manually, to work properly.<br>"; - echo "WARNING: ONLY PROCEEDS IF YOU KNOW WHAT YOU ARE DOING!"; - echo " - <form action='install.php?action=Database_user_deletion_fix' method='POST'> - <input type='submit' value='go!'> - </form> - "; echo "<h3>Log Out</h3>"; echo " @@ -95,15 +112,12 @@ if(is_readable("config.php")) { } else if($_GET["action"] == "logout") { session_destroy(); - } - else if($_GET["action"] == "Database_user_deletion_fix") { - Database_user_deletion_fix(); + echo "<h3>Logged Out</h3><p>You have been logged out.</p><a href='index.php'>Main Shimmie Page</a>"; } } else { echo " <h3>Login</h3> - Enter the database DSN exactly as in config.php (ie, as originally - installed) to access advanced recovery tools: + <p>Enter the database DSN exactly as in config.php (ie, as originally installed) to access advanced recovery tools:</p> <form action='install.php' method='POST'> <center> @@ -118,13 +132,24 @@ if(is_readable("config.php")) { echo "\t\t</div>"; exit; } -require_once "core/compat.inc.php"; -require_once "core/util.inc.php"; -require_once "core/database.class.php"; do_install(); // utilities {{{ + +/** + * Strips off any kind of slash at the end so as to normalise the path. + * @param string $path Path to normalise. + * @return string Path without trailing slash. + */ +function remove_trailing_slash($path) { + if ((substr($path, -1) === '/') || (substr($path, -1) === '\\')) { + return substr($path, 0, -1); + } else { + return $path; + } +} + function check_gd_version() { $gdversion = 0; @@ -249,7 +274,7 @@ function begin() { // {{{ <h3>Help</h3> <p>Please make sure the database you have chosen exists and is empty.<br> - The username provided must have access to create tables within the database. + The username provided must have access to create tables within the database.</p> </div> EOD; @@ -315,7 +340,7 @@ function create_tables() { // {{{ CONSTRAINT foreign_image_tags_image_id FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE, CONSTRAINT foreign_image_tags_tag_id FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE "); - $db->execute("INSERT INTO config(name, value) VALUES('db_version', 8)"); + $db->execute("INSERT INTO config(name, value) VALUES('db_version', 10)"); } catch (PDOException $e) { @@ -354,11 +379,12 @@ function build_dirs() { // {{{ !file_exists("images") || !file_exists("thumbs") || !file_exists("data") || !is_writable("images") || !is_writable("thumbs") || !is_writable("data") ) { - print "Shimmie needs three folders in it's directory, 'images', 'thumbs', and 'data', - and they need to be writable by the PHP user (if you see this error, - if probably means the folders are owned by you, and they need to be - writable by the web server). - <p>Once you have created these folders, hit 'refresh' to continue."; + print "<p>Shimmie needs three folders in it's directory, 'images', 'thumbs', and 'data', + and they need to be writable by the PHP user.</p> + <p>If you see this error, if probably means the folders are owned by you, and they need to be + writable by the web server.</p> + <p>PHP reports that it is currently running as user: ".$_ENV["USER"]." (". $_SERVER["USER"] .")</p> + <p>Once you have created these folders and/or changed the ownership of the shimmie folder, hit 'refresh' to continue.</p>"; exit; } } // }}} @@ -387,49 +413,6 @@ EOD; exit; } } // }}} - -function Database_user_deletion_fix() { - try { - require_once "core/database.class.php"; - $db = new Database(); - - echo "Fixing user_favorites table...."; - - ($db->Execute("ALTER TABLE user_favorites ENGINE=InnoDB;")) ? print_r("ok<br>") : print_r("failed<br>"); - echo "adding Foreign key to user ids..."; - - ($db->Execute("ALTER TABLE user_favorites ADD CONSTRAINT foreign_user_favorites_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;"))? print_r("ok<br>"):print_r("failed<br>"); - echo "cleaning, the table from deleted image favorites...<br>"; - - $rows = $db->get_all("SELECT * FROM user_favorites WHERE image_id NOT IN ( SELECT id FROM images );"); - - foreach( $rows as $key => $value) - $db->Execute("DELETE FROM user_favorites WHERE image_id = :image_id;", array("image_id" => $value["image_id"])); - - echo "adding forign key to image ids..."; - - ($db->Execute("ALTER TABLE user_favorites ADD CONSTRAINT user_favorites_image_id FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE;"))? print_r("ok<br>"):print_r("failed<br>"); - - echo "adding foreign keys to private messages..."; - - ($db->Execute("ALTER TABLE private_message - ADD CONSTRAINT foreign_private_message_from_id FOREIGN KEY (from_id) REFERENCES users(id) ON DELETE CASCADE, - ADD CONSTRAINT foreign_private_message_to_id FOREIGN KEY (to_id) REFERENCES users(id) ON DELETE CASCADE;")) ? print_r("ok<br>"):print_r("failed<br>"); - - echo "Just one more step...which you need to do manually:<br>"; - echo "You need to go to your database and Delete the foreign key on the owner_id in the images table.<br><br>"; - echo "<a href='http://www.justin-cook.com/wp/2006/05/09/how-to-remove-foreign-keys-in-mysql/'>How to remove foreign keys</a><br><br>"; - echo "and finally execute this querry:<br><br>"; - echo "ALTER TABLE images ADD CONSTRAINT foreign_images_owner_id FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT;<br><br>"; - echo "if this is all sucesfull you are done!"; - - } - catch (PDOException $e) - { - // FIXME: Make the error message user friendly - exit($e->getMessage()); - } -} ?> </body> </html> diff --git a/lib/shimmie.js b/lib/shimmie.js index 258d592b..611d49ee 100644 --- a/lib/shimmie.js +++ b/lib/shimmie.js @@ -27,70 +27,25 @@ $(document).ready(function() { }); $("time").timeago(); -}); -var defaultTexts = new Array(); + $('.search_input').DefaultValue('Search'); + $('#search_input').autocomplete(base_href + '/api/internal/tag_list/complete', { + width: 320, + max: 15, + highlight: false, + multiple: true, + multipleSeparator: ' ', + scroll: true, + scrollHeight: 300, + selectFirst: false + }); -window.onload = function(e) { var sections=get_sections(); for(var i=0;i<sections.length;i++) toggle(sections[i]); - initGray("search_input", "Search"); - initGray("commentBox", "Comment"); - initGray("tagBox", "tagme"); - - // if we're going to show with JS, hide with JS first - pass_confirm = byId("pass_confirm"); - if(pass_confirm) { - pass_confirm.style.display = "none"; - } -} - -function initGray(boxname, text) { - var box = byId(boxname); - if(!box) return; - - var clr = function () {cleargray(box, text);}; - var set = function () {setgray(box, text);}; - - addEvent(box, "focus", clr, false); - addEvent(box, "blur", set, false); - - if(box.value == text) { - box.style.color = "#999"; - box.style.textAlign = "center"; - } - else { - box.style.color = "#000"; - box.style.textAlign = "left"; - } -} - -function cleargray(box, text) { - if(box.value == text) { - box.value = ""; - box.style.color = "#000"; - box.style.textAlign = "left"; - } -} -function setgray(box, text) { - if(box.value == "") { - box.style.textAlign = "center"; - box.style.color = "gray"; - box.value = text; - } -} - -function showUp(elem) { - e = document.getElementById(elem) - if(!e) return; - e.style.display = ""; -// alert(e.type+": "+e.value); - if(e.value.match(/^http|^ftp/)) { - e.type = "text"; - alert("Box is web upload"); - } -} + $("#commentBox").DefaultValue("Comment"); + $("#tagBox").DefaultValue("tagme"); +}); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ @@ -111,6 +66,7 @@ function byId(id) { } +// used once in ext/setup/main function getHTTPObject() { if (window.XMLHttpRequest){ return new XMLHttpRequest(); @@ -120,15 +76,6 @@ function getHTTPObject() { } } -function ajaxRequest(url, callback) { - var http = getHTTPObject(); - http.open("GET", url, true); - http.onreadystatechange = function() { - if(http.readyState == 4) callback(http.responseText); - } - http.send(null); -} - /* get, set, and delete cookies */ function getCookie( name ) { @@ -164,3 +111,10 @@ function deleteCookie( name, path, domain ) { ";expires=Thu, 01-Jan-1970 00:00:01 GMT"; } +function replyTo(imageId, commentId) { + var box = $("#comment_on_"+imageId); + var text = ">>"+imageId+"#"+commentId+": "; + + box.focus(); + box.val(box.val() + text); +} diff --git a/themes/danbooru/layout.class.php b/themes/danbooru/layout.class.php index b1b06999..534ea285 100644 --- a/themes/danbooru/layout.class.php +++ b/themes/danbooru/layout.class.php @@ -223,7 +223,9 @@ $header_html <em> Images &copy; their respective owners, <a href="http://code.shishnet.org/shimmie2/">Shimmie</a> &copy; - <a href="http://www.shishnet.org/">Shish</a> &amp; Co 2007-2012, + <a href="http://www.shishnet.org/">Shish</a> &amp; + <a href="https://github.com/shish/shimmie2/contributors">The Team</a> + 2007-2012, based on the Danbooru concept. $debug $contact diff --git a/themes/default/layout.class.php b/themes/default/layout.class.php index 3846c72c..08584b62 100644 --- a/themes/default/layout.class.php +++ b/themes/default/layout.class.php @@ -71,7 +71,9 @@ $header_html <div id="footer"> Images &copy; their respective owners, <a href="http://code.shishnet.org/shimmie2/">Shimmie</a> &copy; - <a href="http://www.shishnet.org/">Shish</a> &amp; Co 2007-2012, + <a href="http://www.shishnet.org/">Shish</a> &amp; + <a href="https://github.com/shish/shimmie2/contributors">The Team</a> + 2007-2012, based on the Danbooru concept. $debug $contact diff --git a/themes/default/style.css b/themes/default/style.css index db384169..1b2c7355 100644 --- a/themes/default/style.css +++ b/themes/default/style.css @@ -128,9 +128,8 @@ UL { .comment { text-align: left; } -.comment .timeago { - float: right; - font-size: 75%; +.comment .info { + display: none; } .more:after { diff --git a/themes/flat/layout.class.php b/themes/flat/layout.class.php index 91b24ebf..c7b2ac64 100644 --- a/themes/flat/layout.class.php +++ b/themes/flat/layout.class.php @@ -71,7 +71,9 @@ $header_html <div id="footer"> Images &copy; their respective owners, <a href="http://code.shishnet.org/shimmie2/">Shimmie</a> &copy; - <a href="http://www.shishnet.org/">Shish</a> &amp; Co 2007-2012, + <a href="http://www.shishnet.org/">Shish</a> &amp; + <a href="https://github.com/shish/shimmie2/contributors">The Team</a> + 2007-2012, based on the Danbooru concept. $debug $contact diff --git a/themes/futaba/layout.class.php b/themes/futaba/layout.class.php index 3ca6727e..d434de74 100644 --- a/themes/futaba/layout.class.php +++ b/themes/futaba/layout.class.php @@ -79,7 +79,9 @@ $header_html <hr> Images &copy; their respective owners, <a href="http://code.shishnet.org/shimmie2/">Shimmie</a> &copy; - <a href="http://www.shishnet.org/">Shish</a> &amp; Co 2007-2012, + <a href="http://www.shishnet.org/">Shish</a> &amp; + <a href="https://github.com/shish/shimmie2/contributors">The Team</a> + 2007-2012, based on the Danbooru concept. <br>Futaba theme based on 4chan's layout and CSS :3 $debug diff --git a/themes/lite/layout.class.php b/themes/lite/layout.class.php index 16fa89ef..227a3cad 100644 --- a/themes/lite/layout.class.php +++ b/themes/lite/layout.class.php @@ -184,7 +184,9 @@ class Layout { <div id="footer"> Images &copy; their respective owners, <a href="http://code.shishnet.org/shimmie2/">Shimmie</a> &copy; - <a href="http://www.shishnet.org/">Shish</a> &amp; Co 2007-2012, + <a href="http://www.shishnet.org/">Shish</a> &amp; + <a href="https://github.com/shish/shimmie2/contributors">The Team</a> + 2007-2012, based on the Danbooru concept.<br /> Lite Theme by <a href="http://seemslegit.com">Zach</a> $debug diff --git a/themes/old_default/layout.class.php b/themes/old_default/layout.class.php index 1e5325cb..2660709d 100644 --- a/themes/old_default/layout.class.php +++ b/themes/old_default/layout.class.php @@ -69,7 +69,9 @@ $header_html <hr> Images &copy; their respective owners, <a href="http://code.shishnet.org/shimmie2/">Shimmie</a> &copy; - <a href="http://www.shishnet.org/">Shish</a> &amp; Co 2007-2012, + <a href="http://www.shishnet.org/">Shish</a> &amp; + <a href="https://github.com/shish/shimmie2/contributors">The Team</a> + 2007-2012, based on the Danbooru concept. $debug $contact diff --git a/themes/warm/layout.class.php b/themes/warm/layout.class.php index e60a7e4a..264d5984 100644 --- a/themes/warm/layout.class.php +++ b/themes/warm/layout.class.php @@ -85,7 +85,9 @@ $header_html <div id="footer"> Images &copy; their respective owners, <a href="http://code.shishnet.org/shimmie2/">Shimmie</a> &copy; - <a href="http://www.shishnet.org/">Shish</a> &amp; Co 2007-2012, + <a href="http://www.shishnet.org/">Shish</a> &amp; + <a href="https://github.com/shish/shimmie2/contributors">The Team</a> + 2007-2012, based on the Danbooru concept. $debug $contact