Merge branch 'branch_2.3' of ssh://shish@marigold.shishnet.org/home/shish/git/shimmie2 into branch_2.3

This commit is contained in:
Shish 2009-07-22 22:09:28 +01:00
commit 88c7233ced
39 changed files with 340 additions and 280 deletions

View File

@ -1,5 +1,5 @@
<?php <?php
class DowntimeTest extends ShimmieWebTestCase { class DowntimeTest extends SCoreWebTestCase {
function testDowntime() { function testDowntime() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("setup"); $this->get_page("setup");

View File

@ -1,7 +1,7 @@
<?php <?php
class DowntimeTheme Extends Themelet { class DowntimeTheme Extends Themelet {
/* /**
* Show the admin that downtime mode is enabled * Show the admin that downtime mode is enabled
*/ */
public function display_notification(Page $page) { public function display_notification(Page $page) {
@ -9,7 +9,7 @@ class DowntimeTheme Extends Themelet {
"<span style='font-size: 1.5em'><b>DOWNTIME MODE IS ON!</b></span>", "left", 0)); "<span style='font-size: 1.5em'><b>DOWNTIME MODE IS ON!</b></span>", "left", 0));
} }
/* /**
* Display $message and exit * Display $message and exit
*/ */
public function display_message($message) { public function display_message($message) {

View File

@ -1,5 +1,5 @@
<?php <?php
class IPBanTest extends ShimmieWebTestCase { class IPBanTest extends SCoreWebTestCase {
function testIPBan() { function testIPBan() {
$this->get_page('ip_ban/list'); $this->get_page('ip_ban/list');
$this->assertResponse(403); $this->assertResponse(403);

View File

@ -1,5 +1,5 @@
<?php <?php
class NewsTest extends ShimmieWebTestCase { class NewsTest extends SCoreWebTestCase {
function testNews() { function testNews() {
$this->log_in_as_admin(); $this->log_in_as_admin();

View File

@ -1,5 +1,5 @@
<?php <?php
class PMTest extends ShimmieWebTestCase { class PMTest extends SCoreWebTestCase {
function testPM() { function testPM() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("user/test"); $this->get_page("user/test");

View File

@ -19,14 +19,10 @@
* <code>/random_image/download/size:1024x768+cute</code> * <code>/random_image/download/size:1024x768+cute</code>
*/ */
class RandomImage implements Extension { class RandomImage extends SimpleExtension {
var $theme; public function onPageRequest($event) {
public function receive_event(Event $event) {
global $config, $database, $page, $user; global $config, $database, $page, $user;
if(is_null($this->theme)) $this->theme = get_theme_object($this); if($event->page_matches("random_image")) {
if(($event instanceof PageRequestEvent) && $event->page_matches("random_image")) {
if($event->count_args() == 1) { if($event->count_args() == 1) {
$action = $event->get_arg(0); $action = $event->get_arg(0);
$search_terms = array(); $search_terms = array();
@ -50,14 +46,16 @@ class RandomImage implements Extension {
} }
} }
} }
}
if(($event instanceof SetupBuildingEvent)) { public function onSetupBuilding($event) {
$sb = new SetupBlock("Random Image"); $sb = new SetupBlock("Random Image");
$sb->add_bool_option("show_random_block", "Show Random Block: "); $sb->add_bool_option("show_random_block", "Show Random Block: ");
$event->panel->add_block($sb); $event->panel->add_block($sb);
} }
if($event instanceof PostListBuildingEvent) { public function onPostListBuilding($event) {
global $config, $page;
if($config->get_bool("show_random_block")) { if($config->get_bool("show_random_block")) {
$image = Image::by_random($event->search_terms); $image = Image::by_random($event->search_terms);
if(!is_null($image)) { if(!is_null($image)) {
@ -66,6 +64,4 @@ class RandomImage implements Extension {
} }
} }
} }
}
add_event_listener(new RandomImage());
?> ?>

View File

@ -6,6 +6,19 @@
* Description: adds unit testing to SCore * Description: adds unit testing to SCore
*/ */
/**
* \page unittests Unit Tests
*
* Each extension should (although doesn't technically have to) come with a
* test.php file, for example ext/index/test.php. The SimpleSCoreTest
* extension will look for these files and load any SCoreWebTestCase classes
* it finds inside them, then run them and report whether or not the test
* passes.
*
* For Shimmie2 specific extensions, there is a ShimmieWebTestCase class which
* includes functions to upload and delete images.
*/
require_once('simpletest/web_tester.php'); require_once('simpletest/web_tester.php');
require_once('simpletest/unit_tester.php'); require_once('simpletest/unit_tester.php');
require_once('simpletest/reporter.php'); require_once('simpletest/reporter.php');
@ -15,6 +28,9 @@ define('USER_PASS', "test");
define('ADMIN_NAME', "demo"); define('ADMIN_NAME', "demo");
define('ADMIN_PASS', "demo"); define('ADMIN_PASS', "demo");
/**
* A set of common SCore activities to test
*/
class SCoreWebTestCase extends WebTestCase { class SCoreWebTestCase extends WebTestCase {
protected function get_page($page) { protected function get_page($page) {
$url = "http://".$_SERVER["HTTP_HOST"].get_base_href().'/'.make_link($page); $url = "http://".$_SERVER["HTTP_HOST"].get_base_href().'/'.make_link($page);
@ -49,6 +65,9 @@ class SCoreWebTestCase extends WebTestCase {
} }
} }
/**
* A set of common Shimmie activities to test
*/
class ShimmieWebTestCase extends SCoreWebTestCase { class ShimmieWebTestCase extends SCoreWebTestCase {
protected function post_image($filename, $tags) { protected function post_image($filename, $tags) {
$image_id = -1; $image_id = -1;
@ -81,6 +100,7 @@ class ShimmieWebTestCase extends SCoreWebTestCase {
} }
} }
/** @private */
class TestFinder extends TestSuite { class TestFinder extends TestSuite {
function TestFinder($hint) { function TestFinder($hint) {
if(strpos($hint, "..") !== FALSE) return; if(strpos($hint, "..") !== FALSE) return;

View File

@ -2,6 +2,7 @@
class SimpleSCoreTestTheme extends Themelet { class SimpleSCoreTestTheme extends Themelet {
} }
/** @private */
class SCoreReporter extends HtmlReporter { class SCoreReporter extends HtmlReporter {
var $current_html = ""; var $current_html = "";
var $clear_modules = array(); var $clear_modules = array();

View File

@ -8,22 +8,19 @@
* This extension sets the "description" meta tag in the header * This extension sets the "description" meta tag in the header
* of pages so that search engines can pick it up * of pages so that search engines can pick it up
*/ */
class SiteDescription implements Extension { class SiteDescription extends SimpleExtension {
public function receive_event(Event $event) { public function onPageRequest(PageRequestEvent $event) {
global $config, $database, $page, $user; global $config, $page;
if($event instanceof PageRequestEvent) {
if(strlen($config->get_string("site_description")) > 0) { if(strlen($config->get_string("site_description")) > 0) {
$description = $config->get_string("site_description"); $description = $config->get_string("site_description");
$page->add_header("<meta name=\"description\" content=\"$description\">"); $page->add_header("<meta name=\"description\" content=\"$description\">");
} }
} }
if($event instanceof SetupBuildingEvent) { public function onSetupBuilding(SetupBuildingEvent $event) {
$sb = new SetupBlock("Site Description"); $sb = new SetupBlock("Site Description");
$sb->add_longtext_option("site_description"); $sb->add_longtext_option("site_description");
$event->panel->add_block($sb); $event->panel->add_block($sb);
} }
} }
}
add_event_listener(new SiteDescription());
?> ?>

View File

@ -1,5 +1,5 @@
<?php <?php
class SiteDescriptionTest extends ShimmieWebTestCase { class SiteDescriptionTest extends SCoreWebTestCase {
function testSiteDescription() { function testSiteDescription() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page('setup'); $this->get_page('setup');

View File

@ -1,5 +1,5 @@
<?php <?php
class WikiTest extends ShimmieWebTestCase { class WikiTest extends SCoreWebTestCase {
function testWiki() { function testWiki() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("wiki"); $this->get_page("wiki");

View File

@ -1,6 +1,6 @@
<?php <?php
class WikiTheme { class WikiTheme extends Themelet {
/* /*
* Show a page * Show a page
* *

View File

@ -1,43 +0,0 @@
The Highest Level
~~~~~~~~~~~~~~~~~
index.php takes care of loading the globals:
$config -- some variety of Config class
$database -- a class used to get raw SQL access
$page -- a GenericPage object, a data structure which holds all the page parts
$user -- the currently logged in User
then it sends an InitExtEvent and PageRequestEvent, these can each trigger
more events of their own.
Once the chain of events comes to an end, the $page object is passed
to the theme's layout.class.php to be turned into HTML
Events and Extensions
~~~~~~~~~~~~~~~~~~~~~
An event is a little blob of data saying "something happened", possibly
"something happened, here's the specific data". Events are sent with the
send_event() function.
An extension is something which is capable of reacting to events. They
register themselves using the add_event_listener() command. (Although for
subclasses of SimpleExtension, registration is handled automatically).
Themes
~~~~~~
Each extension has a theme with a specific name -- the extension Cake which
is stored in ext/cake/main.php will have a theme called CakeTheme stored in
ext/cake/theme.php. If you want to customise it, create a class in the file
themes/mytheme/cake.theme.php called CustomCakeTheme which extends CakeTheme
and overrides some of its methods.
Generally an extension should only deal with processing data; whenever it
wants to display something, it should pass the $page data structure along
with the data to be displayed to the theme object, and the theme will add
the data into the page.

View File

@ -1,8 +1,4 @@
<?php <?php
/**
* @package SCore
*/
/** /**
* A basic chunk of a page * A basic chunk of a page
*/ */
@ -10,20 +6,20 @@ class Block {
/** /**
* The block's title * The block's title
* *
* @var string * @retval string
*/ */
var $header; var $header;
/** /**
* The content * The content
* *
* @var string * @retval string
*/ */
var $body; var $body;
/** /**
* Where the block should be placed. The default theme supports * Where the block should be placed. The default theme supports
* "main" and "left", other themes can add their own areas * "main" and "left", other themes can add their own areas
* *
* @var string * @retval string
*/ */
var $section; var $section;
/** /**
@ -31,7 +27,7 @@ class Block {
* numbers appear lower. The scale is 0-100 by convention, * numbers appear lower. The scale is 0-100 by convention,
* though any number or string will work. * though any number or string will work.
* *
* @var int * @retval int
*/ */
var $position; var $position;

View File

@ -3,17 +3,13 @@
* Functions which are only in some versions of PHP, * Functions which are only in some versions of PHP,
* or only implemented on some platforms * or only implemented on some platforms
* *
* @ignore * \privatesection
* @package SCore
*/ */
# (PHP 5 >= 5.2.1) # (PHP 5 >= 5.2.1)
# Based on http://www.phpit.net/ # Based on http://www.phpit.net/
# article/creating-zip-tar-archives-dynamically-php/2/ # article/creating-zip-tar-archives-dynamically-php/2/
if(!function_exists('sys_get_temp_dir')) { if(!function_exists('sys_get_temp_dir')) {
/**
* @ignore
*/
function sys_get_temp_dir() { function sys_get_temp_dir() {
// Try to get from environment variable // Try to get from environment variable
if(!empty($_ENV['TMP'])) { if(!empty($_ENV['TMP'])) {
@ -46,9 +42,6 @@ function sys_get_temp_dir() {
# (PHP >= 5.1) # (PHP >= 5.1)
# from http://www.php.net/inet_pton # from http://www.php.net/inet_pton
if(!function_exists('inet_pton')) { if(!function_exists('inet_pton')) {
/**
* @ignore
*/
function inet_pton($ip) { function inet_pton($ip) {
# ipv4 # ipv4
if(strpos($ip, '.') !== FALSE) { if(strpos($ip, '.') !== FALSE) {
@ -70,9 +63,6 @@ function inet_pton($ip) {
# (PHP >= 5.1) # (PHP >= 5.1)
# from http://www.php.net/inet_ntop # from http://www.php.net/inet_ntop
if(!function_exists('inet_ntop')) { if(!function_exists('inet_ntop')) {
/**
* @ignore
*/
function inet_ntop($ip) { function inet_ntop($ip) {
if (strlen($ip)==4) { if (strlen($ip)==4) {
// ipv4 // ipv4

View File

@ -1,8 +1,4 @@
<?php <?php
/**
* @package SCore
*/
/** /**
* an abstract interface for altering a name:value pair list * an abstract interface for altering a name:value pair list
*/ */
@ -29,8 +25,6 @@ interface Config {
/** /**
* Common methods for manipulating the list, loading and saving is * Common methods for manipulating the list, loading and saving is
* left to the concrete implementation * left to the concrete implementation
*
* @ignore
*/ */
abstract class BaseConfig implements Config { abstract class BaseConfig implements Config {
var $values = array(); var $values = array();
@ -106,8 +100,6 @@ abstract class BaseConfig implements Config {
* $config['foo'] = "bar"; * $config['foo'] = "bar";
* $config['baz'] = "qux"; * $config['baz'] = "qux";
* ?> * ?>
*
* @ignore
*/ */
class StaticConfig extends BaseConfig { class StaticConfig extends BaseConfig {
public function __construct($filename) { public function __construct($filename) {
@ -135,12 +127,12 @@ class StaticConfig extends BaseConfig {
* Loads the config list from a table in a given database, the table should * Loads the config list from a table in a given database, the table should
* be called config and have the schema: * be called config and have the schema:
* *
* \code
* CREATE TABLE config( * CREATE TABLE config(
* name VARCHAR(255) NOT NULL, * name VARCHAR(255) NOT NULL,
* value TEXT * value TEXT
* ); * );
* * \endcode
* @ignore
*/ */
class DatabaseConfig extends BaseConfig { class DatabaseConfig extends BaseConfig {
var $database = null; var $database = null;

View File

@ -1,16 +1,10 @@
<?php <?php
/**
* @package SCore
*/
require_once "compat.inc.php"; require_once "compat.inc.php";
$ADODB_CACHE_DIR=sys_get_temp_dir(); $ADODB_CACHE_DIR=sys_get_temp_dir();
require_once "lib/adodb/adodb.inc.php"; require_once "lib/adodb/adodb.inc.php";
require_once "lib/adodb/adodb-exceptions.inc.php"; require_once "lib/adodb/adodb-exceptions.inc.php";
/**#@+ /** @privatesection */
* @ignore
*/
// Querylet {{{ // Querylet {{{
class Querylet { class Querylet {
var $sql; var $sql;
@ -202,7 +196,7 @@ class MemCache implements CacheEngine {
public function get_misses() {return $this->misses;} public function get_misses() {return $this->misses;}
} }
// }}} // }}}
/**#@-*/ /** @publicsection */
/** /**
* A class for controlled database access * A class for controlled database access

View File

@ -1,8 +1,4 @@
<?php <?php
/**
* @package SCore
*/
/** /**
* Generic parent class for all events. * Generic parent class for all events.
* *
@ -14,7 +10,9 @@ abstract class Event {
/** /**
* A wake-up call for extensions * A wake-up call for extensions. Upon recieving an InitExtEvent an extension
* should check that it's database tables are there and install them if not,
* and set any defaults with Config::set_default_int() and such.
*/ */
class InitExtEvent extends Event {} class InitExtEvent extends Event {}
@ -111,28 +109,28 @@ class LogEvent extends Event {
/** /**
* a category, normally the extension name * a category, normally the extension name
* *
* @var string * @retval string
*/ */
var $section; var $section;
/** /**
* See python... * See python...
* *
* @var int * @retval int
*/ */
var $priority = 0; var $priority = 0;
/** /**
* Free text to be logged * Free text to be logged
* *
* @var text * @retval text
*/ */
var $message; var $message;
/** /**
* The time that the event was created * The time that the event was created
* *
* @var int * @retval int
*/ */
var $time; var $time;

View File

@ -1,8 +1,4 @@
<?php <?php
/**
* @package SCore
*/
/** /**
* A base exception to be caught by the upper levels * A base exception to be caught by the upper levels
*/ */

View File

@ -1,6 +1,65 @@
<?php <?php
/** /**
* @package SCore * \page eande Events and Extensions
*
* An event is a little blob of data saying "something happened", possibly
* "something happened, here's the specific data". Events are sent with the
* send_event() function. Since events can store data, they can be used to
* return data to the extension which sent them, for example:
*
* \code
* $tfe = new TextFormattingEvent($original_text);
* send_event($tfe);
* $formatted_text = $tfe->formatted;
* \endcode
*
* An extension is something which is capable of reacting to events. They
* register themselves using the add_event_listener() function, after which
* events will be sent to the object's recieve_event() function.
*
* SimpleExtension subclasses are slightly different -- they are registered
* automatically, and events are sent to a named method, eg PageRequestEvent
* will be sent to onPageRequest()
*
*
* \page hello The Hello World Extension
*
* \code
* // ext/hello/main.php
* public class Hello extends SimpleExtension {
* public void onPageRequest(PageRequestEvent $event) {
* global $page, $user;
* $this->theme->display_hello($page, $user);
* }
* }
*
* // ext/hello/theme.php
* public class HelloTheme extends Themelet {
* public void display_hello(Page $page, User $user) {
* $page->add_block(new Block("Hello!", "Hello there ".html_escape($user->name));
* }
* }
*
* // ext/hello/test.php
* public class HelloTest extends ShimmieWebTestCase {
* public void testHello() {
* $this->get_page("post/list");
* $this->assertText("Hello there");
* }
* }
*
* // themes/mytheme/hello.theme.php
* public class CustomHelloTheme extends HelloTheme {
* public function display_hello(Page $page, User $user) {
* $h_user = html_escape($user->name);
* $page->add_block(new Block(
* "Hello!",
* "Hello there $h_user, look at my snazzy custom theme!"
* );
* }
* }
* \endcode
*
*/ */
/** /**
@ -19,7 +78,7 @@ interface Extension {
* priority, so no need for register_extension(new Foo()) * priority, so no need for register_extension(new Foo())
* *
* Hopefully this removes as much copy & paste code from the extension * Hopefully this removes as much copy & paste code from the extension
* files as possible \o/ * files as possible~
* *
* The original concept came from Artanis's SimpleExtension extension * The original concept came from Artanis's SimpleExtension extension
* --> http://github.com/Artanis/simple-extension/tree/master * --> http://github.com/Artanis/simple-extension/tree/master

View File

@ -2,8 +2,21 @@
/** /**
* All the imageboard-specific bits of code should be in this file, everything * All the imageboard-specific bits of code should be in this file, everything
* else in /core should be standard SCore bits. * else in /core should be standard SCore bits.
*/
/**
* \page search Shimmie2: Searching
* *
* @package SCore * The current search system is built of several search item -> image ID list
* translators, eg:
*
* \li the item "fred" will search the image_tags table to find image IDs with the fred tag
* \li the item "size=640x480" will search the images table to find image IDs of 640x480 images
*
* So the search "fred size=640x480" will calculate two lists and take the
* intersection. (There are some optimisations in there making it more
* complicated behind the scenes, but as long as you can turn a single word
* into a list of image IDs, making a search plugin should be simple)
*/ */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
@ -42,7 +55,7 @@ class Image {
/** /**
* Find an image by ID * Find an image by ID
* *
* @var Image * @retval Image
*/ */
public static function by_id($id) { public static function by_id($id) {
assert(is_numeric($id)); assert(is_numeric($id));
@ -55,7 +68,7 @@ class Image {
/** /**
* Find an image by hash * Find an image by hash
* *
* @var Image * @retval Image
*/ */
public static function by_hash($hash) { public static function by_hash($hash) {
assert(is_string($hash)); assert(is_string($hash));
@ -68,7 +81,7 @@ class Image {
/** /**
* Pick a random image out of a set * Pick a random image out of a set
* *
* @var Image * @retval Image
*/ */
public static function by_random($tags=array()) { public static function by_random($tags=array()) {
assert(is_array($tags)); assert(is_array($tags));
@ -145,7 +158,7 @@ class Image {
* Rather than simply $this_id + 1, one must take into account * Rather than simply $this_id + 1, one must take into account
* deleted images and search queries * deleted images and search queries
* *
* @var Image * @retval Image
*/ */
public function get_next($tags=array(), $next=true) { public function get_next($tags=array(), $next=true) {
assert(is_array($tags)); assert(is_array($tags));
@ -177,7 +190,7 @@ class Image {
/** /**
* The reverse of get_next * The reverse of get_next
* *
* @var Image * @retval Image
*/ */
public function get_prev($tags=array()) { public function get_prev($tags=array()) {
return $this->get_next($tags, false); return $this->get_next($tags, false);
@ -186,7 +199,7 @@ class Image {
/** /**
* Find the User who owns this Image * Find the User who owns this Image
* *
* @var User * @retval User
*/ */
public function get_owner() { public function get_owner() {
return User::by_id($this->owner_id); return User::by_id($this->owner_id);
@ -223,7 +236,7 @@ class Image {
/** /**
* Get the URL for the full size image * Get the URL for the full size image
* *
* @var string * @retval string
*/ */
public function get_image_link() { public function get_image_link() {
global $config; global $config;
@ -242,7 +255,7 @@ class Image {
* Get a short link to the full size image * Get a short link to the full size image
* *
* @deprecated * @deprecated
* @var string * @retval string
*/ */
public function get_short_link() { public function get_short_link() {
global $config; global $config;
@ -252,7 +265,7 @@ class Image {
/** /**
* Get the URL for the thumbnail * Get the URL for the thumbnail
* *
* @var string * @retval string
*/ */
public function get_thumb_link() { public function get_thumb_link() {
global $config; global $config;
@ -271,7 +284,7 @@ class Image {
* Get the tooltip for this image, formatted according to the * Get the tooltip for this image, formatted according to the
* configured template * configured template
* *
* @var string * @retval string
*/ */
public function get_tooltip() { public function get_tooltip() {
global $config; global $config;
@ -281,7 +294,7 @@ class Image {
/** /**
* Figure out where the full size image is on disk * Figure out where the full size image is on disk
* *
* @var string * @retval string
*/ */
public function get_image_filename() { public function get_image_filename() {
$hash = $this->hash; $hash = $this->hash;
@ -293,7 +306,7 @@ class Image {
/** /**
* Figure out where the thumbnail is on disk * Figure out where the thumbnail is on disk
* *
* @var string * @retval string
*/ */
public function get_thumb_filename() { public function get_thumb_filename() {
$hash = $this->hash; $hash = $this->hash;
@ -304,7 +317,7 @@ class Image {
/** /**
* Get the original filename * Get the original filename
* *
* @var string * @retval string
*/ */
public function get_filename() { public function get_filename() {
return $this->filename; return $this->filename;
@ -315,7 +328,7 @@ class Image {
* *
* FIXME: now we handle more than just images * FIXME: now we handle more than just images
* *
* @var string * @retval string
*/ */
public function get_mime_type() { public function get_mime_type() {
return "image/".($this->ext); return "image/".($this->ext);
@ -324,7 +337,7 @@ class Image {
/** /**
* Get the image's filename extension * Get the image's filename extension
* *
* @var string * @retval string
*/ */
public function get_ext() { public function get_ext() {
return $this->ext; return $this->ext;
@ -333,7 +346,7 @@ class Image {
/** /**
* Get the image's source URL * Get the image's source URL
* *
* @var string * @retval string
*/ */
public function get_source() { public function get_source() {
return $this->source; return $this->source;
@ -418,9 +431,9 @@ class Image {
} }
/** /**
* ...? * Someone please explain this
* *
* @var string * @retval string
*/ */
public function parse_link_template($tmpl, $_escape="url_escape") { public function parse_link_template($tmpl, $_escape="url_escape") {
global $config; global $config;

View File

@ -1,15 +1,39 @@
<?php <?php
/** /**
* @package SCore * \page themes Themes
*
* Each extension has a theme with a specific name -- eg. the extension Setup
* which is stored in ext/setup/main.php will have a theme called SetupTheme
* stored in ext/setup/theme.php. If you want to customise it, create a class
* in the file themes/mytheme/setup.theme.php called CustomSetupTheme which
* extends SetupTheme and overrides some of its methods.
*
* Generally an extension should only deal with processing data; whenever it
* wants to display something, it should pass the $page data structure along
* with the data to be displayed to the theme object, and the theme will add
* the data into the page.
*
* A page should make sure that all the data it outputs is free from dangerous
* data by using html_escape(), url_escape(), or int_escape() as appropriate.
*
* Because some HTML can be placed anywhere according to the theme, coming up
* with the correct way to link to a page can be hard -- thus we have the
* make_link() function, which will take a path like "post/list" and turn it
* into a full and correct link, eg /myboard/post/list, /foo/index.php?q=post/list,
* etc depending on how things are set up. This should always be used to link
* to pages rather than hardcoding a path.
*
* Various other common functions are available as part of the Themelet class.
*/ */
/** /**
* A data structure for holding all the bits of data that make up a page. * A data structure for holding all the bits of data that make up a page.
* *
* The various extensions all add whatever they want to this structure, * The various extensions all add whatever they want to this structure,
* then layout.class.php turns it into HTML * then Layout turns it into HTML
*/ */
class GenericPage { class Page {
var $mode = "page"; var $mode = "page";
var $type = "text/html"; var $type = "text/html";

View File

@ -1,11 +1,5 @@
<?php <?php
/** /** @private */
* @package SCore
*/
/**
* @ignore
*/
function _new_user($row) { function _new_user($row) {
return new User($row); return new User($row);
} }
@ -94,7 +88,7 @@ class User {
/** /**
* Test if this user is anonymous (not logged in) * Test if this user is anonymous (not logged in)
* *
* @var bool * @retval bool
*/ */
public function is_anonymous() { public function is_anonymous() {
global $config; global $config;
@ -104,7 +98,7 @@ class User {
/** /**
* Test if this user is an administrator * Test if this user is an administrator
* *
* @var bool * @retval bool
*/ */
public function is_admin() { public function is_admin() {
return $this->admin; return $this->admin;

View File

@ -1,9 +1,4 @@
<?php <?php
/**
* @package SCore
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* Input / Output Sanitising * * Input / Output Sanitising *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@ -11,7 +6,7 @@
/** /**
* Make some data safe for printing into HTML * Make some data safe for printing into HTML
* *
* @var string * @retval string
*/ */
function html_escape($input) { function html_escape($input) {
return htmlentities($input, ENT_QUOTES, "UTF-8"); return htmlentities($input, ENT_QUOTES, "UTF-8");
@ -20,7 +15,7 @@ function html_escape($input) {
/** /**
* Make sure some data is safe to be used in integer context * Make sure some data is safe to be used in integer context
* *
* @var int * @retval int
*/ */
function int_escape($input) { function int_escape($input) {
return (int)$input; return (int)$input;
@ -29,7 +24,7 @@ function int_escape($input) {
/** /**
* Make sure some data is safe to be used in URL context * Make sure some data is safe to be used in URL context
* *
* @var string * @retval string
*/ */
function url_escape($input) { function url_escape($input) {
$input = str_replace('^', '^^', $input); $input = str_replace('^', '^^', $input);
@ -41,7 +36,7 @@ function url_escape($input) {
/** /**
* Make sure some data is safe to be used in SQL context * Make sure some data is safe to be used in SQL context
* *
* @var string * @retval string
*/ */
function sql_escape($input) { function sql_escape($input) {
global $database; global $database;
@ -51,7 +46,7 @@ function sql_escape($input) {
/** /**
* Turn a human readable filesize into an integer, eg 1KB -> 1024 * Turn a human readable filesize into an integer, eg 1KB -> 1024
* *
* @var int * @retval int
*/ */
function parse_shorthand_int($limit) { function parse_shorthand_int($limit) {
if(is_numeric($limit)) { if(is_numeric($limit)) {
@ -77,7 +72,7 @@ function parse_shorthand_int($limit) {
/** /**
* Turn an integer into a human readable filesize, eg 1024 -> 1KB * Turn an integer into a human readable filesize, eg 1024 -> 1KB
* *
* @var string * @retval string
*/ */
function to_shorthand_int($int) { function to_shorthand_int($int) {
if($int >= pow(1024, 3)) { if($int >= pow(1024, 3)) {
@ -103,7 +98,7 @@ function to_shorthand_int($int) {
* Figure out the correct way to link to a page, taking into account * Figure out the correct way to link to a page, taking into account
* things like the nice URLs setting * things like the nice URLs setting
* *
* @var string * @retval string
*/ */
function make_link($page=null, $query=null) { function make_link($page=null, $query=null) {
global $config; global $config;
@ -132,6 +127,10 @@ function make_link($page=null, $query=null) {
} }
} }
/**
* Make a link to a static file in the current theme's
* directory
*/
function theme_file($filepath) { function theme_file($filepath) {
global $config; global $config;
$theme = $config->get_string("theme","default"); $theme = $config->get_string("theme","default");
@ -144,21 +143,21 @@ function theme_file($filepath) {
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/** /**
* @ignore * @private
*/ */
function version_check() { function _version_check() {
if(version_compare(PHP_VERSION, "5.0.0") == -1) { if(version_compare(PHP_VERSION, "5.0.0") == -1) {
print <<<EOD print "
Currently SCore Engine doesn't support versions of PHP lower than 5.0.0 -- Currently SCore Engine doesn't support versions of PHP lower than 5.0.0 --
PHP4 and earlier are officially dead according to their creators, PHP4 and earlier are officially dead according to their creators,
please tell your host to upgrade. please tell your host to upgrade.
EOD; ";
exit; exit;
} }
} }
/** /**
* @ignore * @private
*/ */
function check_cli() { function check_cli() {
if(isset($_SERVER['REMOTE_ADDR'])) { if(isset($_SERVER['REMOTE_ADDR'])) {
@ -171,7 +170,7 @@ function check_cli() {
/** /**
* $db is the connection object * $db is the connection object
* *
* @ignore * @private
*/ */
function _count_execs($db, $sql, $inputarray) { function _count_execs($db, $sql, $inputarray) {
global $_execs; global $_execs;
@ -213,9 +212,9 @@ function get_theme_object(Extension $class, $fatal=true) {
/** /**
* Compare two Block objects, used to sort them before being displayed * Compare two Block objects, used to sort them before being displayed
* *
* @var int * @retval int
*/ */
function blockcmp($a, $b) { function blockcmp(Block $a, Block $b) {
if($a->position == $b->position) { if($a->position == $b->position) {
return 0; return 0;
} }
@ -227,7 +226,7 @@ function blockcmp($a, $b) {
/** /**
* Figure out PHP's internal memory limit * Figure out PHP's internal memory limit
* *
* @var int * @retval int
*/ */
function get_memory_limit() { function get_memory_limit() {
global $config; global $config;
@ -257,7 +256,7 @@ function get_memory_limit() {
* Get the currently active IP, masked to make it not change when the last * Get the currently active IP, masked to make it not change when the last
* octet or two change, for use in session cookies and such * octet or two change, for use in session cookies and such
* *
* @var string * @retval string
*/ */
function get_session_ip($config) { function get_session_ip($config) {
$mask = $config->get_string("session_hash_mask", "255.255.0.0"); $mask = $config->get_string("session_hash_mask", "255.255.0.0");
@ -271,7 +270,7 @@ function get_session_ip($config) {
* *
* PHP really, really sucks. * PHP really, really sucks.
* *
* @var string * @retval string
*/ */
function get_base_href() { function get_base_href() {
$possible_vars = array('SCRIPT_NAME', 'PHP_SELF', 'PATH_INFO', 'ORIG_PATH_INFO'); $possible_vars = array('SCRIPT_NAME', 'PHP_SELF', 'PATH_INFO', 'ORIG_PATH_INFO');
@ -292,7 +291,7 @@ function get_base_href() {
* A shorthand way to send a TextFormattingEvent and get the * A shorthand way to send a TextFormattingEvent and get the
* results * results
* *
* @var string * @retval string
*/ */
function format_text($string) { function format_text($string) {
$tfe = new TextFormattingEvent($string); $tfe = new TextFormattingEvent($string);
@ -312,10 +311,16 @@ if(!defined("LOG_INFO")) define("LOG_INFO", 20);
if(!defined("LOG_DEBUG")) define("LOG_DEBUG", 10); if(!defined("LOG_DEBUG")) define("LOG_DEBUG", 10);
if(!defined("LOG_NOTSET")) define("LOG_NOTSET", 0); if(!defined("LOG_NOTSET")) define("LOG_NOTSET", 0);
/**
* A shorthand way to send a LogEvent
*/
function log_msg($section, $priority, $message) { function log_msg($section, $priority, $message) {
send_event(new LogEvent($section, $priority, $message)); send_event(new LogEvent($section, $priority, $message));
} }
/**
* A shorthand way to send a LogEvent
*/
function log_info($section, $message) { function log_info($section, $message) {
log_msg($section, LOG_INFO, $message); log_msg($section, LOG_INFO, $message);
} }
@ -328,7 +333,7 @@ function log_info($section, $message) {
/** /**
* Remove an item from an array * Remove an item from an array
* *
* @var array * @retval array
*/ */
function array_remove($array, $to_remove) { function array_remove($array, $to_remove) {
$array = array_unique($array); $array = array_unique($array);
@ -344,7 +349,7 @@ function array_remove($array, $to_remove) {
/** /**
* Add an item to an array * Add an item to an array
* *
* @var array * @retval array
*/ */
function array_add($array, $element) { function array_add($array, $element) {
$array[] = $element; $array[] = $element;
@ -355,7 +360,7 @@ function array_add($array, $element) {
/** /**
* Return the unique elements of an array, case insensitively * Return the unique elements of an array, case insensitively
* *
* @var array * @retval array
*/ */
function array_iunique($array) { function array_iunique($array) {
$ok = array(); $ok = array();
@ -378,7 +383,7 @@ function array_iunique($array) {
* *
* from http://uk.php.net/network * from http://uk.php.net/network
* *
* @var bool * @retval bool
*/ */
function ip_in_range($IP, $CIDR) { function ip_in_range($IP, $CIDR) {
list ($net, $mask) = split ("/", $CIDR); list ($net, $mask) = split ("/", $CIDR);
@ -446,33 +451,7 @@ function full_copy($source, $target) {
} }
/** /**
* @ignore * @private
*/
function stripslashes_r($arr) {
return is_array($arr) ? array_map('stripslashes_r', $arr) : stripslashes($arr);
}
/**
* @ignore
*/
function sanitise_environment() {
if(DEBUG) {
error_reporting(E_ALL);
assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_BAIL, 1);
}
ob_start();
if(get_magic_quotes_gpc()) {
$_GET = stripslashes_r($_GET);
$_POST = stripslashes_r($_POST);
$_COOKIE = stripslashes_r($_COOKIE);
}
}
/**
* @ignore
*/ */
function weighted_random($weights) { function weighted_random($weights) {
$total = 0; $total = 0;
@ -493,9 +472,7 @@ function weighted_random($weights) {
* Event API * * Event API *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/** /** @private */
* @ignore
*/
$_event_listeners = array(); $_event_listeners = array();
/** /**
@ -509,9 +486,7 @@ function add_event_listener(Extension $extension, $pos=50) {
$_event_listeners[$pos] = $extension; $_event_listeners[$pos] = $extension;
} }
/** /** @private */
* @ignore
*/
$_event_count = 0; $_event_count = 0;
/** /**
@ -605,13 +580,33 @@ function print_GET() {
* Request initialisation stuff * * Request initialisation stuff *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/** @privatesection */
function _stripslashes_r($arr) {
return is_array($arr) ? array_map('stripslashes_r', $arr) : stripslashes($arr);
}
function _sanitise_environment() {
if(DEBUG) {
error_reporting(E_ALL);
assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_BAIL, 1);
}
ob_start();
if(get_magic_quotes_gpc()) {
$_GET = _stripslashes_r($_GET);
$_POST = _stripslashes_r($_POST);
$_COOKIE = _stripslashes_r($_COOKIE);
}
}
/** /**
* Turn ^^ into ^ and ^s into / * Turn ^^ into ^ and ^s into /
* *
* Necessary because various servers and various clients * Necessary because various servers and various clients
* think that / is special... * think that / is special...
*
* @ignore
*/ */
function _decaret($str) { function _decaret($str) {
$out = ""; $out = "";
@ -628,9 +623,6 @@ function _decaret($str) {
return $out; return $out;
} }
/**
* @ignore
*/
function _get_query_parts() { function _get_query_parts() {
if(isset($_GET["q"])) { if(isset($_GET["q"])) {
$path = $_GET["q"]; $path = $_GET["q"];
@ -660,9 +652,6 @@ function _get_query_parts() {
} }
} }
/**
* @ignore
*/
function _get_page_request() { function _get_page_request() {
global $config; global $config;
$args = _get_query_parts(); $args = _get_query_parts();
@ -674,9 +663,6 @@ function _get_page_request() {
return new PageRequestEvent($args); return new PageRequestEvent($args);
} }
/**
* @ignore
*/
function _get_user() { function _get_user() {
global $config, $database; global $config, $database;
$user = null; $user = null;

View File

@ -1,6 +1,5 @@
<?php <?php
/* AdminBuildingEvent {{{ /**
*
* Sent when the admin page is ready to be added to * Sent when the admin page is ready to be added to
*/ */
class AdminBuildingEvent extends Event { class AdminBuildingEvent extends Event {
@ -9,7 +8,6 @@ class AdminBuildingEvent extends Event {
$this->page = $page; $this->page = $page;
} }
} }
// }}}
class AdminPage implements Extension { class AdminPage implements Extension {
var $theme; var $theme;

View File

@ -7,7 +7,8 @@
* Description: A thing for point & click extension management * Description: A thing for point & click extension management
*/ */
class ExtensionInfo { // {{{ /** @private */
class ExtensionInfo {
var $ext_name, $name, $link, $author, $email, $description, $documentation, $version; var $ext_name, $name, $link, $author, $email, $description, $documentation, $version;
function ExtensionInfo($main) { function ExtensionInfo($main) {
@ -66,7 +67,7 @@ class ExtensionInfo { // {{{
private function is_enabled($fname) { private function is_enabled($fname) {
return file_exists("ext/$fname"); return file_exists("ext/$fname");
} }
} // }}} }
class ExtManager extends SimpleExtension { class ExtManager extends SimpleExtension {
public function onPageRequest($event) { public function onPageRequest($event) {

View File

@ -1,5 +1,5 @@
<?php <?php
class ExtManagerTest extends ShimmieWebTestCase { class ExtManagerTest extends SCoreWebTestCase {
function testAuth() { function testAuth() {
$this->get_page('ext_manager'); $this->get_page('ext_manager');
$this->assertResponse(403); $this->assertResponse(403);

View File

@ -1,5 +1,5 @@
<?php <?php
class Handle404Test extends ShimmieWebTestCase { class Handle404Test extends SCoreWebTestCase {
function test404Handler() { function test404Handler() {
$this->get_page('not/a/page'); $this->get_page('not/a/page');
$this->assertResponse(404); $this->assertResponse(404);

View File

@ -1,5 +1,5 @@
<?php <?php
class SetupTest extends ShimmieWebTestCase { class SetupTest extends SCoreWebTestCase {
function testAuth() { function testAuth() {
$this->get_page('setup'); $this->get_page('setup');
$this->assertResponse(403); $this->assertResponse(403);

View File

@ -1,5 +1,5 @@
<?php <?php
class UserPageTest extends ShimmieWebTestCase { class UserPageTest extends SCoreWebTestCase {
function testUserPage() { function testUserPage() {
$this->get_page('user'); $this->get_page('user');
$this->assertTitle("Not Logged In"); $this->assertTitle("Not Logged In");

View File

@ -1,4 +1,55 @@
<?php <?php
/**
* \mainpage Shimmie2 / SCore Documentation
*
* SCore is a framework designed for writing flexible, extendable applications.
* Whereas most PHP apps are built monolithicly, score's event-based nature
* allows parts to be mixed and matched. For instance, the most famous
* collection of score extensions is the Shimmie image board, which includes
* user management, a wiki, a private messaging system, etc. But one could
* easily remove the image board bits and simply have a wiki with users and
* PMs; or one could replace it with a blog module; or one could have a blog
* which links to images on an image board, with no wiki or messaging, and so
* on and so on...
*
* To learn about the innards of SCore, start with the \ref overview.
*
*
* \page overview High Level Overview
*
* Dijkstra will kill me for personifying my architecture, but I can't think
* of a better way without going into all the little details.
*
* There are a bunch of Extension subclasses, they talk to eachother by sending
* and recieving Event subclasses. The topic of conversation is decided by the
* initial PageRequestEvent, and each extension puts its notes into the shared
* Page data store. Once the conversation is over, the Page is passed to the
* current theme's Layout class which will tidy up the data and present it to
* the user.
*
* To learn more about the architecture:
*
* \li \ref eande
* \li \ref themes
*
* To learn more about practical development:
*
* \li \ref scglobals
* \li \ref unittests
* \li \ref hello
*
* \page scglobals SCore Globals
*
* There are four global variables which are pretty essential to most extensions:
*
* \li $config -- some variety of Config subclass
* \li $database -- a Database object used to get raw SQL access
* \li $page -- a Page to holds all the loose bits of extension output
* \li $user -- the currently logged in User
*
* Each of these can be imported at the start of a function with eg "global $page, $user;"
*/
// set up and purify the environment // set up and purify the environment
define("DEBUG", true); define("DEBUG", true);
define("SCORE_VERSION", 's2hack'); define("SCORE_VERSION", 's2hack');
@ -10,8 +61,8 @@ if(!file_exists("config.php")) {
} }
require_once "core/util.inc.php"; require_once "core/util.inc.php";
version_check(); _version_check();
sanitise_environment(); _sanitise_environment();
try { try {
@ -31,7 +82,7 @@ try {
// load the theme parts // load the theme parts
$_theme = $config->get_string("theme", "default"); $_theme = $config->get_string("theme", "default");
if(!file_exists("themes/$_theme")) $_theme = "default"; if(!file_exists("themes/$_theme")) $_theme = "default";
require_once "themes/$_theme/page.class.php"; if(file_exists("themes/$_theme/custompage.class.php")) require_once "themes/$_theme/custompage.class.php";
require_once "themes/$_theme/layout.class.php"; require_once "themes/$_theme/layout.class.php";
require_once "themes/$_theme/themelet.class.php"; require_once "themes/$_theme/themelet.class.php";
@ -63,7 +114,7 @@ try {
// start the page generation waterfall // start the page generation waterfall
$page = new Page(); $page = class_exists("CustomPage") ? new CustomPage() : new Page();
$user = _get_user($config, $database); $user = _get_user($config, $database);
send_event(new InitExtEvent()); send_event(new InitExtEvent());
send_event(_get_page_request()); send_event(_get_page_request());

View File

@ -1,6 +1,6 @@
<?php <?php
class Page extends GenericPage { class CustomPage extends Page {
var $left_enabled = true; var $left_enabled = true;
public function disable_left() { public function disable_left() {
$this->left_enabled = false; $this->left_enabled = false;

View File

@ -1,7 +1,12 @@
<?php <?php
/**
* A class to turn a Page data structure into a blob of HTML
*/
class Layout { class Layout {
function display_page($page) { /**
* turns the Page into HTML
*/
public function display_page(Page $page) {
global $config; global $config;
$theme_name = $config->get_string('theme', 'default'); $theme_name = $config->get_string('theme', 'default');
@ -71,7 +76,10 @@ $header_html
EOD; EOD;
} }
function block_to_html($block, $hidable=false, $salt="") { /**
* A handy function which does exactly what it says in the method name
*/
private function block_to_html($block, $hidable=false, $salt="") {
$h = $block->header; $h = $block->header;
$b = $block->body; $b = $block->body;
$html = ""; $html = "";

View File

@ -1,6 +0,0 @@
<?php
class Page extends GenericPage {
// no changes from default
}
?>

View File

@ -1,6 +1,11 @@
<?php <?php
/**
* A customised version of the Setup theme
*/
class CustomSetupTheme extends SetupTheme { class CustomSetupTheme extends SetupTheme {
/**
* Turn a SetupBlock into HTML... with rounded corners.
*/
protected function sb_to_html(SetupBlock $block) { protected function sb_to_html(SetupBlock $block) {
return " return "
<div class='rr setupblock'> <div class='rr setupblock'>

View File

@ -1,5 +1,7 @@
<?php <?php
/**
* A collection of common functions for theme parts
*/
class Themelet { class Themelet {
/** /**
* Generic error message display * Generic error message display

View File

@ -1,6 +1,6 @@
<?php <?php
class Page extends GenericPage { class CustomPage extends Page {
var $left_enabled = true; var $left_enabled = true;
public function disable_left() { public function disable_left() {
$this->left_enabled = false; $this->left_enabled = false;

View File

@ -1,6 +0,0 @@
<?php
class Page extends GenericPage {
// no changes from default
}
?>

View File

@ -1,6 +0,0 @@
<?php
class Page extends GenericPage {
// no changes from default
}
?>