# HG changeset patch # User Dan # Date 1197356634 18000 # Node ID a09fb41e48d5952987b6935b2e3e183a4f5f1a7a First commit! Woohoo! Basic status of things is in extended description. * You can write blog posts and view them in a standard blog-like index page * All blogs are per-user and blogs can be made public or private * Setting permissions on a per-blog/per-post basis should work except for some inheritance issues * Planets are not in any way implemented * Tagging posts does not work * Basically the only thing you can do is write and display posts and make comments. It's basic, but it works. More to come very soon! diff -r 000000000000 -r a09fb41e48d5 plugins/Nuggie.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/Nuggie.php Tue Dec 11 02:03:54 2007 -0500 @@ -0,0 +1,141 @@ +attachHook('base_classes_initted', ' + $paths->add_page(Array( + \'name\'=>\'Install Nuggie\', + \'urlname\'=>\'NuggieInstall\', + \'namespace\'=>\'Special\', + \'special\'=>0,\'visible\'=>0,\'comments_on\'=>0,\'protected\'=>1,\'delvotes\'=>0,\'delvote_ips\'=>\'\', + )); + '); + require( ENANO_ROOT . '/plugins/nuggie/install.php' ); +} + +$plugins->attachHook('base_classes_initted', ' + list($page_id, $namespace) = RenderMan::strToPageId($paths->get_pageid_from_url()); + + if ( $page_id == "Preferences" && $namespace == "Special" ) + { + require( ENANO_ROOT . "/plugins/nuggie/usercp.php" ); + } + '); + +$plugins->attachHook('acl_rule_init', 'nuggie_namespace_setup();'); + +function nuggie_namespace_setup() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + // Insert additional namespaces + + $paths->create_namespace('Blog', 'Blog:'); + $paths->create_namespace('Planet', 'Planet:'); + + // Create custom permissions for Nuggie + + $session->register_acl_type('nuggie_post', AUTH_DISALLOW, 'Post blog entries or create blog', Array(), 'Blog'); + $session->register_acl_type('nuggie_edit_own', AUTH_DISALLOW, 'Edit own blog posts', Array(), 'Blog'); + $session->register_acl_type('nuggie_edit_other', AUTH_DISALLOW, 'Edit others\' blog posts', Array(), 'Blog'); + $session->register_acl_type('nuggie_create_planet', AUTH_DISALLOW, 'Create new planets', Array(), 'Planet'); + $session->register_acl_type('nuggie_publicize_planet', AUTH_DISALLOW, 'Make own planets searchable', Array('nuggie_create_planet'), 'Planet'); + $session->register_acl_type('nuggie_protect_planet', AUTH_DISALLOW, 'Protect planets from public modification', Array(), 'Planet'); + $session->register_acl_type('nuggie_edit_planet_own', AUTH_DISALLOW, 'Edit own planets', Array(), 'Planet'); + $session->register_acl_type('nuggie_edit_planet_other', AUTH_DISALLOW, 'Edit others\' planets', Array(), 'Planet'); + $session->register_acl_type('nuggie_even_when_protected', AUTH_DISALLOW, 'Override protection on editing planets', Array(), 'Planet'); + $session->register_acl_type('nuggie_see_non_public', AUTH_DISALLOW, 'See non-public blogs', Array(), 'Blog'); + + // Extend the core permission set + + $session->acl_extend_scope('read', 'Blog|Planet', $paths); + $session->acl_extend_scope('edit_comments', 'Blog', $paths); + $session->acl_extend_scope('post_comments', 'Blog', $paths); + $session->acl_extend_scope('mod_comments', 'Blog', $paths); +} + +$plugins->attachHook('page_type_string_set', 'nuggie_set_page_string();'); + +function nuggie_set_page_string() +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( $paths->namespace == 'Blog' ) + { + $paths->cpage['comments_on'] = 0; + $template->namespace_string = 'blog'; + if ( strstr($paths->cpage['urlname_nons'], '/') ) + { + $paths->cpage['comments_on'] = 1; + $template->namespace_string = 'blog post'; + } + } + else if ( $paths->namespace == 'Planet' ) + { + $paths->cpage['comments_on'] = 0; + $template->namespace_string = 'planet'; + } +} + +$plugins->attachHook('page_not_found', 'nuggie_handle_namespace($this);'); + +function nuggie_handle_namespace($processor) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( $processor->namespace == 'Blog' ) + { + require( ENANO_ROOT . '/plugins/nuggie/postbit.php' ); + $result = nuggie_blog_uri_handler($processor->page_id); + if ( $result === '_err_access_denied' ) + { + $processor->err_access_denied(); + return true; + } + } + else if ( $processor->namespace == 'Planet' ) + { + $result = nuggie_planet_uri_handler($processor->page_id); + if ( $result === '_err_access_denied' ) + { + $processor->err_access_denied(); + return true; + } + } +} + +/** + * Sanitizes a string for a Nuggie permalink. + * @param string String to sanitize + * @return string + */ + +function nuggie_sanitize_title($title) +{ + // Placeholder for now - this may become more elaborate in the future, we'll see + return sanitize_page_id($title); +} + +?> diff -r 000000000000 -r a09fb41e48d5 plugins/nuggie/install.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/nuggie/install.php Tue Dec 11 02:03:54 2007 -0500 @@ -0,0 +1,368 @@ +Nuggie is already installed - you can\'t reinstall it from here. To upgrade a Nuggie installation, use the upgrade tool.

'); + } + + if ( $session->auth_level < USER_LEVEL_ADMIN ) + { + redirect(makeUrlNS('Special', 'Login/' . $paths->page, 'level=' . USER_LEVEL_ADMIN), 'Login required', 'You need to be an administrator with elevated auth to install Nuggie. You will now be transferred to the login page.'); + exit; + } + + $mode = ( $x = $paths->getParam(0) ) ? $x : 'welcome'; + switch ( $mode ) + { + default: + die_friendly('Invalid action', '

Invalid installer action

'); + break; + case 'welcome': + $template->header(); + $q = $db->sql_query('SELECT group_id, group_name FROM ' . table_prefix . 'groups ORDER BY group_name ASC;'); + if ( !$q ) + $db->_die('plugins/nuggie/install.php selecting group information'); + $groups = array(); + while ( (list($group_id, $group_name) = $db->fetchrow_num()) ) + { + $groups[$group_id] = $group_name; + } + ?> + + '; + echo '

Welcome to Nuggie - the only blogging engine you\'ll ever need.

'; + echo '

Before you can start blogging, we\'ll need to perform a couple of short steps to set up Nuggie on your server. Since + you\'re running Nuggie on top of Enano, you won\'t need to re-enter database information – we just need to create a + few extra tables in your database.

'; + echo '

To get started, who would you like to give posting abilities to?

'; + echo '

'; + echo '

'; + echo ''; + echo '

'; + echo ''; + echo '

'; + echo '

'; + echo ''; + $template->footer(); + break; + case 'install_base': + if ( !file_exists( ENANO_ROOT . '/plugins/nuggie/schema.sql' ) ) + { + die_friendly('Can\'t load schema file', '

Can\'t find the schema.sql file that should be in /plugins/nuggie. Check your Nuggie setup.

'); + } + $schema = @file_get_contents( ENANO_ROOT . '/plugins/nuggie/schema.sql' ); + if ( empty($schema) ) + { + die_friendly('Can\'t load schema file', '

Can\'t read the schema.sql file that should be in /plugins/nuggie. Check your file permissions.

'); + } + + if ( !isset($_POST['blog_perms']) ) + die('Missing essential form field'); + + if ( !in_array($_POST['blog_perms'], array('everyone', 'use_group', 'create_group', 'noone')) ) + die('You tried to hack the form'); + + if ( $_POST['blog_perms'] == 'use_group' && strval(intval($_POST['use_group_id'])) !== $_POST['use_group_id'] ) + die('You tried to hack the form'); + + if ( $_POST['blog_perms'] == 'create_group' && !isset($_POST['create_group_name']) ) + die('You tried to hack the form'); + + // + // PARSE SCHEMA + // + + // Step 1: remove comments and blank lines + $schema = str_replace("\r", '', $schema); + $schema = explode("\n", $schema); + foreach ( $schema as $i => $_ ) + { + $line =& $schema[$i]; + $line = preg_replace('/--(.*)$/', '', $line); + $line = trim($line); + if ( empty($line) ) + unset($schema[$i]); + } + $schema = array_values($schema); + + // Step 2: Split into separate queries + + $queries = array(''); + $query =& $queries[0]; + foreach ( $schema as $line ) + { + if ( preg_match('/;$/', $line) ) + { + $query .= "\n $line"; + $queries[] = ''; + unset($query); + $query =& $queries[count($queries) - 1]; + } + else + { + $query .= "\n $line"; + } + } + unset($query); + foreach ( $queries as $i => $query ) + { + $query = trim($query); + if ( empty($query) ) + unset($queries[$i]); + else + $queries[$i] = $query; + } + $schema = array_values($queries); + unset($queries, $query, $i); + + // Step 3: Assign variables + + foreach ( $schema as $i => $_ ) + { + $sql =& $schema[$i]; + $sql = str_replace('{{TABLE_PREFIX}}', table_prefix, $sql); + unset($sql); + } + unset($sql); + + // Step 4: Check queries + foreach ( $schema as $sql ) + { + if ( !$db->check_query($sql) ) + { + die_friendly('Error during installation', '

DBAL rejected query citing syntax errors. This is probably a bug.

'); + } + } + + // echo '
' . htmlspecialchars(print_r($schema, true)) . '
'; + + // Step 5: Install + foreach ( $schema as $sql ) + { + if ( !$db->sql_query($sql) ) + { + $db->_die('Nuggie during mainstream installation'); + } + } + + $template->header(true); + echo '

Base install complete

'; + echo '

The base install has completed. Please click Next to continue with the setup of ACL rules.

'; + echo '
'; + $group_name = htmlspecialchars($_POST['create_group_name']); + $group_name = str_replace('"', '"', $group_name); + // This is SAFE! It's verified against a whitelist + echo ''; + echo ""; + echo ""; + echo '

'; + echo '
'; + $template->footer(true); + + break; + case 'install_acl': + + if ( !isset($_POST['blog_perms']) ) + die('Missing essential form field'); + + if ( !in_array($_POST['blog_perms'], array('everyone', 'use_group', 'create_group', 'noone')) ) + die('You tried to hack the form'); + + if ( $_POST['blog_perms'] == 'use_group' && strval(intval($_POST['use_group_id'])) !== $_POST['use_group_id'] ) + die('You tried to hack the form'); + + if ( $_POST['blog_perms'] == 'create_group' && !isset($_POST['create_group_name']) ) + die('You tried to hack the form'); + + switch ( $_POST['blog_perms'] ) + { + case 'everyone': + $q = $db->sql_query('SELECT rules,rule_id FROM ' . table_prefix . 'acl WHERE target_type = ' . ACL_TYPE_GROUP . ' AND target_id = 1 AND page_id IS NULL AND namespace IS NULL;'); + if ( !$q ) + $db->_die('Nuggie installer selecting existing ACL rules'); + if ( $db->numrows() < 1 ) + { + // The rule doesn't exist, create it + $rule = $session->perm_to_string(array( + 'nuggie_post' => AUTH_ALLOW, + 'nuggie_edit_own' => AUTH_ALLOW, + 'nuggie_edit_other' => AUTH_DISALLOW, + 'nuggie_create_planet' => AUTH_ALLOW, + 'nuggie_publicize_planet' => AUTH_WIKIMODE, + 'nuggie_protect_planet' => AUTH_DISALLOW, + 'nuggie_edit_planet_own' => AUTH_ALLOW, + 'nuggie_edit_planet_other' => AUTH_DISALLOW, + 'nuggie_even_when_protected' => AUTH_DISALLOW, + 'nuggie_see_non_public' => AUTH_DISALLOW + )); + $q = $db->sql_query('INSERT INTO ' . table_prefix . 'acl(rules, target_type, target_id, page_id, namespace)' . + "\n VALUES( '$rule', " . ACL_TYPE_GROUP . ", 1, NULL, NULL );"); + if ( !$q ) + $db->_die('Nuggie installer setting up permissions'); + } + else + { + list($rule, $rule_id) = $db->fetchrow_num(); + $rule = $session->string_to_perm($rule); + $rule = $session->acl_merge_complete($rule, array( + 'nuggie_post' => AUTH_ALLOW, + 'nuggie_edit_own' => AUTH_ALLOW, + 'nuggie_edit_other' => AUTH_DISALLOW, + 'nuggie_create_planet' => AUTH_ALLOW, + 'nuggie_publicize_planet' => AUTH_WIKIMODE, + 'nuggie_protect_planet' => AUTH_DISALLOW, + 'nuggie_edit_planet_own' => AUTH_ALLOW, + 'nuggie_edit_planet_other' => AUTH_DISALLOW, + 'nuggie_even_when_protected' => AUTH_DISALLOW, + 'nuggie_see_non_public' => AUTH_DISALLOW + )); + $rule = $session->perm_to_string($rule); + $q = $db->sql_query('UPDATE ' . table_prefix . 'acl' . + "\n SET rules='$rule'\n" + . " WHERE rule_id = $rule_id;"); + if ( !$q ) + $db->_die('Nuggie installer setting up permissions'); + } + break; + case "create_group": + $group_name = $db->escape($_POST['create_group_name']); + + $q = $db->sql_query('INSERT INTO ' . table_prefix . "groups ( group_name ) VALUES ( '$group_name' );"); + if ( !$q ) + $db->_die('Nuggie installer creating group'); + + $group_id = $db->insert_id(); + $q = $db->sql_query('INSERT INTO ' . table_prefix . "group_members( group_id, user_id ) VALUES ( $group_id, {$session->user_id} );"); + if ( !$q ) + $db->_die('Nuggie installer adding user to new group'); + + case "use_group": + if ( !isset($group_id) ) + { + $group_id = intval($_POST['use_group_id']); + $q = $db->sql_query('SELECT group_name, group_id FROM ' . table_prefix . "groups WHERE group_id = $group_id;"); + if ( !$q ) + $db->_die('Nuggie installer determining group information'); + if ( $db->numrows() < 1 ) + die('Hacking attempt'); + list($group_name, $group_id) = $db->fetchrow_num(); + } + + $q = $db->sql_query('SELECT rules,rule_id FROM ' . table_prefix . 'acl WHERE target_type = ' . ACL_TYPE_GROUP . " AND target_id = $group_id AND page_id IS NULL AND namespace IS NULL;"); + if ( !$q ) + $db->_die('Nuggie installer selecting existing ACL rules'); + if ( $db->numrows() < 1 ) + { + // The rule doesn't exist, create it + $rule = $session->perm_to_string(array( + 'nuggie_post' => AUTH_ALLOW, + 'nuggie_edit_own' => AUTH_ALLOW, + 'nuggie_edit_other' => AUTH_DISALLOW, + 'nuggie_create_planet' => AUTH_ALLOW, + 'nuggie_publicize_planet' => AUTH_WIKIMODE, + 'nuggie_protect_planet' => AUTH_DISALLOW, + 'nuggie_edit_planet_own' => AUTH_ALLOW, + 'nuggie_edit_planet_other' => AUTH_DISALLOW, + 'nuggie_even_when_protected' => AUTH_DISALLOW, + 'nuggie_see_non_public' => AUTH_DISALLOW + )); + $q = $db->sql_query('INSERT INTO ' . table_prefix . 'acl(rules, target_type, target_id, page_id, namespace)' . + "\n VALUES( '$rule', " . ACL_TYPE_GROUP . ", $group_id, NULL, NULL );"); + if ( !$q ) + $db->_die('Nuggie installer setting up permissions'); + } + else + { + list($rule, $rule_id) = $db->fetchrow_num(); + $rule = $session->string_to_perm($rule); + $rule = $session->acl_merge_complete($rule, array( + 'nuggie_post' => AUTH_ALLOW, + 'nuggie_edit_own' => AUTH_ALLOW, + 'nuggie_edit_other' => AUTH_DISALLOW, + 'nuggie_create_planet' => AUTH_ALLOW, + 'nuggie_publicize_planet' => AUTH_WIKIMODE, + 'nuggie_protect_planet' => AUTH_DISALLOW, + 'nuggie_edit_planet_own' => AUTH_ALLOW, + 'nuggie_edit_planet_other' => AUTH_DISALLOW, + 'nuggie_even_when_protected' => AUTH_DISALLOW, + 'nuggie_see_non_public' => AUTH_DISALLOW + )); + $rule = $session->perm_to_string($rule); + $q = $db->sql_query('UPDATE ' . table_prefix . 'acl' . + "\n SET rules='$rule'\n" + . " WHERE rule_id = $rule_id;"); + if ( !$q ) + $db->_die('Nuggie installer setting up permissions'); + } + + break; + case "noone": + // Don't touch permissions, let the webmaster handle it + break; + default: + die('PHP = douche bag'); + break; + } + + // Mark it as installed to prevent installer module from loading + setConfig('nuggie_installed', '1'); + + $template->header(true); + echo '

Nuggie has been installed.

'; + echo '

You\'ve successfully installed Nuggie. Congratulations!

'; + echo '
'; + echo '

'; + echo '

'; + $template->footer(true); + + break; + } +} + diff -r 000000000000 -r a09fb41e48d5 plugins/nuggie/postbit.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/nuggie/postbit.php Tue Dec 11 02:03:54 2007 -0500 @@ -0,0 +1,409 @@ +theme_id}/blog_post.tpl" ) ) + { + $parser = $template->makeParser('blog_post.tpl'); + } + else + { + $tpl_code = << + +
+
+ +

{POST_TITLE}

+ +
+ {COMMENT_STRING} + + • + Edit this post + +
+
+
+ {POST_TEXT} +
+
+ + + +TPLBLOCK; + $parser = $template->makeParserText($tpl_code); + } + + $this->post_title_clean = nuggie_sanitize_title($this->post_title); + + // List of valid characters for date() + $date_chars = 'dDjlNSwzWFmMntLoYyaABgGhHiseIOTZcrU'; + $date_chars = enano_str_split($date_chars); + + $strings = array(); + foreach ( $date_chars as $char ) + { + $strings["DATE_$char"] = date($char, $this->post_timestamp); + } + + $strings['POST_TITLE'] = htmlspecialchars($this->post_title); + $strings['POST_TEXT'] = RenderMan::render($this->post_text); + $strings['PERMALINK'] = makeUrlNS('Blog', $this->post_author . date('/Y/n/j/', $this->post_timestamp) . $this->post_title_clean, false, true); + $strings['EDIT_LINK'] = makeUrlNS('Special', "Preferences/Blog/Write/{$this->post_id}", false, true); + + if ( $this->num_comments == 0 ) + $comment_string = 'No comments'; + else if ( $this->num_comments == 1 ) + $comment_string = '1 comment'; + else + $comment_string = intval($this->num_comments) . ' comments'; + + $strings['COMMENT_STRING'] = $comment_string; + $strings['TIMESTAMP'] = date('l, F j, Y \a\t h:i <\s\m\a\l\l>A', $this->post_timestamp); + + $parser->assign_vars($strings); + $parser->assign_bool(array( + 'auth_edit' => ( $this->auth_edit ) + )); + + return $parser->run(); + } + /** + * Don't worry about this, it's only called from the paginator. + * @access private + */ + + function paginate_handler($_, $row) + { + global $db, $session, $paths, $template, $plugins; // Common objects + + if ( !is_object($this->blog_perms) ) + { + $this->blog_perms = $session->fetch_page_acl($row['username'], 'Blog'); + } + + $perms = $session->fetch_page_acl("{$row['post_timestamp']}_{$row['post_id']}", 'Blog'); + $perms->perms = $session->acl_merge($this->blog_perms->perms, $perms->perms); + + /* + if ( !$perms->get_permissions('read') ) + { + return "POST {$this->post_id} DENIED"; + } + */ + + $this->post_id = intval($row['post_id']); + $this->post_title = $row['post_title']; + $this->post_text = $row['post_text']; + $this->post_author = $row['username']; + $this->post_timestamp = intval($row['post_timestamp']); + $this->num_comments = intval($row['num_comments']); + + return $this->render_post(); + } +} + +function nuggie_blog_uri_handler($uri) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + $template->add_header(''); + if ( strstr($uri, '/') ) + { + // + // Permalinked post + // + + // Split and parse URI + $particles = explode('/', $uri); + if ( count($particles) < 5 ) + return false; + $sz = count($particles); + for ( $i = 5; $i < $sz; $i++ ) + { + $particles[4] .= '/' . $particles[$i]; + unset($particles[$i]); + } + + $particles[4] = nuggie_sanitize_title($particles[4]); + $poster =& $particles[0]; + $year =& $particles[1]; + $month =& $particles[2]; + $day =& $particles[3]; + $post_title_clean =& $particles[4]; + + $particlecomp = $db->escape(implode('/', $particles)); + + $year = intval($year); + $month = intval($month); + $day = intval($day); + + $time_min = mktime(0, 0, 0, $month, $day, $year); + $time_max = $time_min + 86400; + + $ptc = $db->escape($post_title_clean); + $uname = $db->escape(dirtify_page_id($poster)); + + $q = $db->sql_query("SELECT p.post_id, p.post_title, p.post_title_clean, p.post_author, p.post_timestamp, p.post_text, b.blog_name,\n" + . " b.blog_subtitle, b.blog_type, b.allowed_users, u.username, u.user_level, COUNT(c.comment_id) AS num_comments\n" + . " FROM " . table_prefix . "blog_posts AS p\n" + . " LEFT JOIN " . table_prefix . "blogs AS b\n" + . " ON ( b.user_id = p.post_author )\n" + . " LEFT JOIN " . table_prefix . "users AS u\n" + . " ON ( u.user_id = p.post_author )\n" + . " LEFT JOIN " . table_prefix . "comments AS c\n" + . " ON ( ( c.page_id = '{$particlecomp}' AND c.namespace = 'Blog' ) OR ( c.page_id IS NULL AND c.namespace IS NULL ) )\n" + . " WHERE p.post_timestamp >= $time_min AND p.post_timestamp <= $time_max\n" + . " AND p.post_title_clean = '$ptc' AND u.username = '$uname'\n" + . " GROUP BY p.post_id;"); + if ( !$q ) + $db->_die('Nuggie post handler selecting main post data'); + + if ( $db->numrows() < 1 ) + return false; + + if ( $db->numrows() > 1 ) + { + die_friendly('Ambiguous blog posts', '

FIXME: You have two posts with the same title posted on the same day by the same user. I was + not able to distinguish which post you wish to view.

'); + } + + $row = $db->fetchrow(); + + // + // Determine permissions + // + + // The way we're doing this is first fetching permissions for the blog, and then merging them + // with permissions specific to the post. This way the admin can set custom permissions for the + // entire blog, and they'll be inherited unless individual posts have overriding permissions. + $perms_blog = $session->fetch_page_acl($row['username'], 'Blog'); + $perms = $session->fetch_page_acl("{$row['post_timestamp']}_{$row['post_id']}", 'Blog'); + $perms->perms = $session->acl_merge($perms->perms, $perms_post->perms); + unset($perms_blog); + + if ( $row['blog_type'] == 'private' ) + { + $allowed_users = unserialize($row['allowed_users']); + if ( !in_array($session->username, $allowed_users) && !$perms->get_permissions('nuggie_see_non_public') && $row['username'] != $session->username ) + { + return '_err_access_denied'; + } + } + + $acl_type = ( $row['post_author'] == $session->user_id ) ? 'nuggie_edit_own' : 'nuggie_edit_other'; + + if ( !$perms->get_permissions('read') ) + return '_err_access_denied'; + + // We're validated - display post + $postbit = new NuggiePostbit(); + $postbit->post_id = intval($row['post_id']); + $postbit->post_title = $row['post_title']; + $postbit->post_text = $row['post_text']; + $postbit->post_author = $row['username']; + $postbit->post_timestamp = intval($row['post_timestamp']); + $postbit->auth_edit = $perms->get_permissions($acl_type); + $postbit->num_comments = intval($row['num_comments']); + + $template->tpl_strings['PAGE_NAME'] = htmlspecialchars($row['post_title']) . ' « ' . htmlspecialchars($row['blog_name']); + + $template->header(); + echo '< ' . htmlspecialchars($row['blog_name']) . ''; + echo $postbit->render_post(); + $template->footer(); + + return true; + } + else + { + return nuggie_blog_index($uri); + } +} + +function nuggie_blog_index($username) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + + $username_esc = $db->escape($username); + + // First look for the user's blog so we can get permissions + $q = $db->sql_query('SELECT b.blog_type, b.allowed_users, b.user_id, b.blog_name, b.blog_subtitle FROM ' . table_prefix . "blogs AS b LEFT JOIN " . table_prefix . "users AS u ON ( u.user_id = b.user_id ) WHERE u.username = '$username_esc';"); + if ( !$q ) + $db->_die('Nuggie main blog page doing preliminary security check'); + + if ( $db->numrows() < 1 ) + return false; + + list($blog_type, $allowed_users, $user_id, $blog_name, $blog_subtitle) = $db->fetchrow_num(); + $db->free_result(); + + $perms = $session->fetch_page_acl($username, 'Blog'); + + if ( $blog_type == 'private' ) + { + $allowed_users = unserialize($allowed_users); + if ( !in_array($session->username, $allowed_users) && !$perms->get_permissions('nuggie_see_non_public') && $username != $session->username ) + { + return '_err_access_denied'; + } + } + + // Determine number of posts and prefetch ACL info + $q = $db->sql_query('SELECT post_timestamp, post_id FROM ' . table_prefix . 'blog_posts WHERE post_author = ' . $user_id . ' AND post_published = 1;'); + if ( !$q ) + $db->_die('Nuggie main blog page doing rowcount of blog posts'); + + $count = $db->numrows(); + + while ( $row = $db->fetchrow($q) ) + { + $session->fetch_page_acl("{$row['post_timestamp']}_{$row['post_id']}", 'Blog'); + } + + $db->free_result($q); + + $q = $db->sql_unbuffered_query("SELECT p.post_id, p.post_title, p.post_title_clean, p.post_author, p.post_timestamp, p.post_text, b.blog_name,\n" + . " b.blog_subtitle, u.username, u.user_level, COUNT(c.comment_id) AS num_comments\n" + . " FROM " . table_prefix . "blog_posts AS p\n" + . " LEFT JOIN " . table_prefix . "blogs AS b\n" + . " ON ( b.user_id = p.post_author )\n" + . " LEFT JOIN " . table_prefix . "users AS u\n" + . " ON ( u.user_id = p.post_author )\n" + . " LEFT JOIN " . table_prefix . "comments AS c\n" + . " ON ( ( c.page_id REGEXP CONCAT('([0-9]+)/([0-9]+)/([0-9]+)/', p.post_title_clean) AND c.namespace = 'Blog' ) OR ( c.page_id IS NULL AND c.namespace IS NULL ) )\n" + . " WHERE p.post_author = $user_id AND p.post_published = 1\n" + . " GROUP BY p.post_id\n" + . " ORDER BY p.post_timestamp DESC;"); + if ( !$q ) + $db->_die('Nuggie main blog page selecting the whole shebang'); + + if ( $count < 1 ) + { + // Either the user hasn't created a blog yet, or he isn't even registered + return false; + } + + $template->tpl_strings['PAGE_NAME'] = htmlspecialchars($blog_name) . ' » ' . htmlspecialchars($blog_subtitle); + + $postbit = new NuggiePostbit(); + // $q, $tpl_text, $num_results, $result_url, $start = 0, $perpage = 10, $callers = Array(), $header = '', $footer = '' + $html = paginate( + $q, + '{post_id}', + $count, + makeUrlNS('Blog', $username, "start=%s", true), + 0, + 10, + array( 'post_id' => array($postbit, 'paginate_handler') ), + '' + ); + + $template->header(); + + echo $html; + + $template->footer(); + + return true; +} + +?> diff -r 000000000000 -r a09fb41e48d5 plugins/nuggie/schema.sql --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/nuggie/schema.sql Tue Dec 11 02:03:54 2007 -0500 @@ -0,0 +1,41 @@ +-- Nuggie +-- Version 0.1 +-- Copyright (C) 2007 Dan Fuhry + +-- This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License +-- as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + +-- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +-- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + +CREATE TABLE {{TABLE_PREFIX}}blogs( + blog_id int(12) NOT NULL auto_increment, + blog_name varchar(255) NOT NULL, + blog_subtitle text NOT NULL, + user_id int(12) NOT NULL, + blog_type ENUM('private', 'public') NOT NULL DEFAULT 'public', + allowed_users text, + PRIMARY KEY ( blog_id ) +) ENGINE = MyISAM CHARACTER SET utf8 COLLATE utf8_bin; + +CREATE TABLE {{TABLE_PREFIX}}planets( + planet_id smallint(6) NOT NULL auto_increment, + planet_name varchar(255) NOT NULL, + planet_subtitle text NOT NULL, + planet_creator int(12) NOT NULL DEFAULT 1, + planet_public tinyint(1) NOT NULL DEFAULT 0, + planet_visible tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY ( planet_id ) +) ENGINE = MyISAM CHARACTER SET utf8 COLLATE utf8_bin; + +CREATE TABLE {{TABLE_PREFIX}}blog_posts( + post_id int(15) NOT NULL auto_increment, + post_title text NOT NULL, + post_title_clean text NOT NULL, + post_author int(12) NOT NULL DEFAULT 1, + post_text longtext NOT NULL, + post_timestamp int(32) NOT NULL DEFAULT 0, + post_published tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY ( post_id ) +) ENGINE = MyISAM CHARACTER SET utf8 COLLATE utf8_bin; + diff -r 000000000000 -r a09fb41e48d5 plugins/nuggie/style.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/nuggie/style.css Tue Dec 11 02:03:54 2007 -0500 @@ -0,0 +1,32 @@ +div.blog-post-header { + border-bottom: 1px solid #C0C0C0; + margin: 0 0 10px 0; + padding: 0 0 3px 0; +} +div.blog-post-header div.blog-post-datemark { + float: right; + text-align: center; + font-weight: bold; + color: #505050; + background-color: #E0E0E0; + font-size: 12pt; + padding: 7px; +} +div.blog-post-header h3 { + font-size: 16pt; + font-family: myriad pro, verdana, arial, helvetica, sans-serif; + font-weight: normal; + margin-bottom: 4px; +} +div.blog-post-header h3 a { + color: #202020; + text-decoration: none; + border-bottom: 1px dotted #C0C0C0; +} +div.blog-post-header h3 a:hover { + border-bottom: 1px solid #808080; + color: #305070; +} +div.blog-post-body { + clear: both; +} diff -r 000000000000 -r a09fb41e48d5 plugins/nuggie/usercp.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/nuggie/usercp.php Tue Dec 11 02:03:54 2007 -0500 @@ -0,0 +1,462 @@ +Nuggie not installed'; + echo '

It looks like Nuggie isn\'t installed yet. You\'ll need to install Nuggie before you can do anything more.

'; + return true; + } + + $subsection = $paths->getParam(1); + $initted = true; + + $q = $db->sql_query('SELECT blog_id, blog_name, blog_subtitle, blog_type, allowed_users FROM ' . table_prefix . "blogs WHERE user_id = {$session->user_id};"); + if ( !$q ) + $db->_die('Nuggie User CP selecting blog info'); + + if ( $db->numrows() < 1 ) + { + $subsection = 'Settings'; + $initted = false; + } + + list(, $blog_name, $blog_desc, $blog_type, $allowed_users) = $db->fetchrow_num($q); + + switch($subsection) + { + case false: + case 'Home': + echo 'module Home'; + break; + case 'Settings': + + switch ( isset($_POST['do_save']) ) + { + // We're doing this so we can break out if we need to (if form validation fails) + case true: + + $errors = array(); + + $blog_name = trim($_POST['blog_name']); + $blog_desc = trim($_POST['blog_desc']); + $blog_access = trim($_POST['blog_access']); + $allowed_users = $_POST['allowed_users']; + + if ( empty($blog_name) ) + $errors[] = 'Please enter a name for your blog.'; + + if ( !in_array($blog_access, array('public', 'private')) ) + $errors[] = 'Hacking attempt on blog_access: must be one of public, private.'; + + if ( count($allowed_users) > 500 ) + $errors[] = 'You\'re asking that an unreasonable number of users have access to this blog. If you really have that many readers, you may want to ask the administrator of this site to make a usergroup with read access to your blog.'; + + if ( count($allowed_users) < 1 && $blog_access == 'private' ) + $errors[] = 'Please enter at least one username that will have access to your blog. Note that your account always has access to your blog.'; + + if ( $blog_access == 'public' ) + { + $allowed_users = 'NULL'; + } + else + { + if ( is_array($allowed_users) && count($errors) < 1 ) + { + $allowed_users = array_values(array_unique($allowed_users)); + foreach ( $allowed_users as $i => $_ ) + { + if ( empty( $allowed_users[$i] ) ) + { + unset($allowed_users[$i]); + } + else + { + $allowed_users[$i] = $db->escape($allowed_users[$i]); + } + } + $fragment = "username='" . implode("' OR username='", $allowed_users) . "'"; + $e = $db->sql_query('SELECT COUNT(username) AS num_valid FROM ' . table_prefix . "users WHERE $fragment;"); + if ( !$e ) + $db->_die('Nuggie user CP validating usernames'); + + $row = $db->fetchrow(); + if ( intval($row['num_valid']) != count($allowed_users) ) + $errors[] = 'One or more of the usernames you entered does not exist.'; + } + else + { + $errors[] = 'Invalid datatype on allowed_users.'; + } + } + + if ( count($errors) > 0 ) + { + $initted = true; + echo '
+ The following problems prevented your blog settings from being saved: + +
'; + break; + } + else + { + // Save changes + + if ( !is_string($allowed_users) ) + $allowed_users = "'" . $db->escape( serialize($allowed_users) ) . "'"; + + $blog_name = $db->escape($blog_name); + $blog_desc = $db->escape($blog_desc); + + if ( $initted ) + { + $sql = 'UPDATE ' . table_prefix . "blogs SET blog_name = '$blog_name', blog_subtitle = '$blog_desc', blog_type = '$blog_access', allowed_users = $allowed_users;"; + } + else + { + $sql = 'INSERT INTO ' . table_prefix . 'blogs(blog_name, blog_subtitle, blog_type, allowed_users, user_id)' . + "\n VALUES ( '$blog_name', '$blog_desc', '$blog_access', $allowed_users, {$session->user_id} );"; + } + + if ( $db->sql_query($sql) ) + { + echo '
' . + ( $initted ? 'Your changes have been saved.' : 'Your blog has been created; you can now + start writing some posts and + then view your blog.' ) + . '
'; + } + else + { + $db->_die('Nuggie user CP saving settings'); + } + + // Re-select the blog data + $db->free_result($q); + + $q = $db->sql_query('SELECT blog_id, blog_name, blog_subtitle, blog_type, allowed_users FROM ' . table_prefix . "blogs WHERE user_id = {$session->user_id};"); + if ( !$q ) + $db->_die('Nuggie User CP selecting blog info'); + + list(, $blog_name, $blog_desc, $blog_type, $allowed_users) = $db->fetchrow_num($q); + } + + $initted = true; + } + + if ( !$initted ) + { + echo '
+ It looks like your blog isn\'t set up yet.
+ You\'ll need to set up your blog by entering some basic information here before you can write any posts. +
'; + $blog_name = htmlspecialchars($session->username) . "'s blog"; + $blog_desc = ''; + } + else + { + $blog_name = htmlspecialchars(strtr($blog_name, array('"' => '"'))); + $blog_desc = htmlspecialchars(strtr($blog_desc, array('"' => '"'))); + } + + if ( !isset($blog_type) ) + $blog_type = 'public'; + + if ( !isset($allowed_users) ) + $allowed_users = serialize(array()); + + $form_action = makeUrlNS('Special', 'Preferences/Blog/Settings', false, true); + echo "
"; + + ?> +
+ + + + + + + + + + + + + + + + + + + +
+ +
+ Blog name: + + +
+ Blog description:
+ You're best off keeping this short and sweet. +
+ +
+ Blog access: + +
+
+ Administrators can always read all blogs, including private ones. + +
+ +
+
+ '; + + break; + case 'Posts': + echo 'module Posts'; + break; + case 'Write': + + $post_text = ''; + $post_title = 'Post title'; + + $post_id = $paths->getParam(2); + if ( isset($_POST['post_id']) ) + { + $post_id = $_POST['post_id']; + } + if ( $post_id ) + { + /* + * FIXME: Validate blog public/private status before sending text + * FIXME: Avoid ambiguous post_title_cleans through appending numbers when needed + */ + + $post_id = intval($post_id); + $q = $db->sql_query('SELECT p.post_id, p.post_title, p.post_title_clean, p.post_author, p.post_text, p.post_timestamp, u.username ' + . 'FROM ' . table_prefix . 'blog_posts AS p' + . ' LEFT JOIN ' . table_prefix . 'users AS u' + . ' ON ( p.post_author = u.user_id )' + . ' WHERE post_id = ' . $post_id . ';'); + + if ( !$q ) + $db->_die('Nuggie user CP obtaining post info'); + + if ( $db->numrows() > 0 ) + { + $row = $db->fetchrow(); + if ( $session->user_id != $row['post_author'] ) + { + // We have a possible security issue on our hands - the user is trying + // to edit someone else's post. Verify read and write permissions. + $post_page_id = "{$row['post_timestamp']}_{$row['post_id']}"; + $perms = $session->fetch_page_acl($post_page_id, 'Blog'); + if ( !$perms->get_permissions('read') || !$perms->get_permissions('nuggie_edit_other') ) + { + echo '

Post editing error

'; + echo '

You do not have permission to edit this blog post.

'; + + unset($row); + unset($row); + + $db->free_result(); + // Break out of this entire user CP module + return true; + } + } + else + { + $post_page_id = "{$row['post_timestamp']}_{$row['post_id']}"; + $perms = $session->fetch_page_acl($post_page_id, 'Blog'); + if ( !$perms->get_permissions('nuggie_edit_own') || !$perms->get_permissions('read') ) + { + echo '

Post editing error

'; + echo '

You do not have permission to edit this blog post.

'; + + unset($row); + unset($row); + + $db->free_result(); + // Break out of this entire user CP module + return true; + } + } + // We have permission - load post + $post_title = $row['post_title']; + $post_text = $row['post_text']; + } + } + + if ( isset($_POST['submit']) ) + { + switch($_POST['submit']) + { + case 'save_publish': + $publish = '1'; + case 'save_draft': + if ( !isset($publish) ) + $publish = '0'; + + $save_post_text = $_POST['post_text']; + $save_post_title = $db->escape($_POST['post_title']); + $save_post_title_clean = $db->escape(nuggie_sanitize_title($_POST['post_title'])); + + $save_post_text = RenderMan::preprocess_text($save_post_text, true, true); + + if ( $post_id ) + { + $sql = 'UPDATE ' . table_prefix . "blog_posts SET post_title = '$save_post_title', post_title_clean = '$save_post_title_clean', post_text = '$save_post_text', post_published = $publish WHERE post_id = $post_id;"; + } + else + { + $time = time(); + $sql = 'INSERT INTO ' . table_prefix . 'blog_posts ( post_title, post_title_clean, post_text, post_author, post_timestamp, post_published ) ' + . "VALUES ( '$save_post_title', '$save_post_title_clean', '$save_post_text', {$session->user_id}, $time, $publish );"; + } + + if ( $db->sql_query($sql) ) + { + echo '
+ ' . ( $publish == '1' ? 'Your post has been published.' : 'Your post has been saved.' ) . ' +
'; + } + else + { + $db->_die('Nuggie user CP running post-save query'); + } + + if ( !$post_id ) + { + $post_id = $db->insert_id(); + } + + $post_title = $_POST['post_title']; + $post_text = $_POST['post_text']; + break; + case 'preview': + $preview_text = $_POST['post_text']; + $preview_text = RenderMan::preprocess_text($preview_text, true, false); + $preview_text = RenderMan::render($preview_text); + + /* + * FIXME: Use the real post renderer (when it's ready) + */ + + echo '
'; + echo '

Post preview

'; + echo '

FIXME: This does not use the real post-display API, which is not yet implemented. Eventually this should look just like a real post.

'; + echo '

' . htmlspecialchars($_POST['post_title']) . '

'; + echo $preview_text; + echo '
'; + + $post_title = $_POST['post_title']; + $post_text = $_POST['post_text']; + break; + } + } + + $q = $db->sql_query('SELECT post_id, post_title FROM ' . table_prefix . "blog_posts WHERE post_published = 0 AND post_author = {$session->user_id};"); + if ( !$q ) + $db->_die('Nuggie user CP selecting draft posts'); + if ( $db->numrows() > 0 ) + { + echo '
Your drafts: '; + $posts = array(); + while ( $row = $db->fetchrow() ) + { + $posts[] = '' . htmlspecialchars($row['post_title']) . ''; + } + echo implode(', ', $posts); + echo '
'; + } + + echo ''; + + $post_text = htmlspecialchars($post_text); + $post_title = strtr(htmlspecialchars($post_title), array('"' => '"')); + + echo ''; + echo $template->tinymce_textarea('post_text', $post_text); + + // Buttons! + echo '
'; + echo '  '; + echo '  '; + echo '  '; + echo '
'; + + if ( $post_id ) + { + echo ''; + } + + echo '
'; + + break; + case 'Planets': + echo 'module Planets'; + break; + default: + return false; + } + return true; +} + +$plugins->attachHook("userprefs_jbox", " + userprefs_menu_add('My blog', 'Manage blog settings', makeUrlNS('Special', 'Preferences/Blog/Settings')); + userprefs_menu_add('My blog', 'Manage posts', makeUrlNS('Special', 'Preferences/Blog/Posts')); + userprefs_menu_add('My blog', 'Write new post', makeUrlNS('Special', 'Preferences/Blog/Write')); + userprefs_menu_add('My blog', 'Manage my planets', makeUrlNS('Special', 'Preferences/Blog/Planets')); + \$userprefs_menu_links['My blog'] = makeUrlNS('Blog', \$session->username); + "); +$plugins->attachHook("userprefs_body", "return nuggie_user_cp(\$section);");