Gorilla.php
changeset 0 cac93de16379
child 1 f2ceea4fabe8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Gorilla.php	Sat May 30 02:52:13 2009 -0400
@@ -0,0 +1,749 @@
+<?php
+/**!info**
+{
+  "Plugin Name"  : "Gorilla Paste",
+  "Plugin URI"   : "http://enanocms.org/plugin/gorilla",
+  "Description"  : "For The Toughest Pasting Jobs On Earth.&trade; The pastebin, Enano style. <a href=\"http://enanocms.org/plugin/geshi\" onclick=\"window.open(this.href); return false;\">GeSHi plugin</a> highly recommended.",
+  "Author"       : "Dan Fuhry",
+  "Version"      : "0.1",
+  "Author URI"   : "http://enanocms.org/"
+}
+**!*/
+
+// Register namespace and ACLs
+$plugins->attachHook('acl_rule_init', 'gorilla_setupcore($this, $session);');
+// Add our special page
+$plugins->attachHook('session_started', 'register_special_page(\'NewPaste\', \'gorilla_page_create\', true);');
+
+// constants
+define('PASTE_PRIVATE', 1);
+
+function gorilla_setupcore(&$paths, &$session)
+{
+  // register our paste namespace
+  $nssep = substr($paths->nslist['Special'], -1);
+  $paths->create_namespace('Paste', 'Paste' . $nssep);
+  
+  // create our ACLs
+  /**
+   * @param string $acl_type An identifier for this field
+   * @param int $default_perm Whether permission should be granted or not if it's not specified in the ACLs.
+   * @param string $desc A human readable name for the permission type
+   * @param array $deps The list of dependencies - this should be an array of ACL types
+   * @param string $scope Which namespaces this field should apply to. This should be either a pipe-delimited list of namespace IDs or just "All".
+   */
+   
+  $session->acl_extend_scope('read', 'Paste', $paths);
+  $session->acl_extend_scope('post_comments', 'Paste', $paths);
+  $session->acl_extend_scope('edit_comments', 'Paste', $paths);
+  $session->acl_extend_scope('mod_comments', 'Paste', $paths);
+  $session->acl_extend_scope('create_page', 'Paste', $paths);
+  $session->acl_extend_scope('mod_misc', 'Paste', $paths);
+  
+  $session->register_acl_type('delete_paste_own', AUTH_ALLOW, 'gorilla_acl_delete_paste_own', array(), 'Paste');
+  $session->register_acl_type('delete_paste_others', AUTH_DISALLOW, 'gorilla_acl_delete_paste_others', array(), 'Paste');
+}
+
+// Our paste creation page
+function page_Special_NewPaste()
+{
+  global $db, $session, $paths, $template, $plugins; // Common objects
+  global $lang, $output;
+  
+  $have_geshi = isset($GLOBALS['geshi_supported_formats']);
+  $perms = $session->fetch_page_acl('0', 'Paste');
+  $have_permission = $perms->get_permissions('create_page');
+  
+  if ( $paths->getParam(0) === 'ajaxsubmit' )
+  {
+    header('Content-type: text/plain');
+    echo gorilla_process_post($have_geshi, $have_permission, true);
+    return true;
+  }
+  
+  $private = false;
+  $highlight = isset($_COOKIE['g_highlight']) ? $_COOKIE['g_highlight'] : 'plaintext';
+  $text = '';
+  $title = '';
+  $ttl = 3600;
+  $copy_from = false;
+  
+  if ( preg_match('/^Copy=([0-9]+)$/', $paths->getParam(0), $match) )
+  {
+    $paste_id = intval($match[1]);
+    $q = $db->sql_query('SELECT paste_flags, paste_language, paste_text, paste_title, paste_ttl FROM ' . table_prefix . "pastes WHERE paste_id = $paste_id;");
+    if ( !$q )
+      $db->_die();
+    
+    list($flags, $highlight, $text, $title, $ttl) = $db->fetchrow_num();
+    $db->free_result();
+    $private = $flags & PASTE_PRIVATE ? true : false;
+    $copy_from = $paste_id;
+  }
+  
+  $output->header();
+  
+  ?>
+  <script type="text/javascript">
+  var gorilla_have_permission = <?php echo $have_permission ? 'true' : 'false'; ?>;
+  function gorilla_create_submit()
+  {
+    if ( !window.gorilla_have_permission && user_level < USER_LEVEL_MEMBER )
+    {
+      load_component('login');
+      
+      ajaxLogonInit(function(k, response)
+        {
+          window.gorilla_have_permission = true;
+          document.forms['gorilla_create'].submit();
+        }, USER_LEVEL_MEMBER);
+      return false;
+    }
+    
+    try
+    {
+      load_component(['jquery', 'jquery-ui']);
+      $('#gorilla_submit_result').empty().hide();
+      
+      var whitey = whiteOutElement(document.forms['gorilla_create']);
+      
+      var json_packet = {
+        highlight: $('#gorilla_highlight').val(),
+        text: $('#gorilla_create_text').val(),
+        is_private: $('#gorilla_private:checked').val() ? true : false,
+        nick: $('#gorilla_nick').val(),
+        title: $('#gorilla_title').val(),
+        ttl: parseInt($('.gorilla_ttl:checked').val())
+      };
+      json_packet = ajaxEscape(toJSONString(json_packet));
+      ajaxPost(makeUrlNS('Special', 'NewPaste/ajaxsubmit'), 'r=' + json_packet, function(ajax)
+        {
+          if ( ajax.readyState == 4 && ajax.status == 200 )
+          {
+            var failed = parseInt((String(ajax.responseText)).substr(0, 1));
+            if ( failed == 1 )
+              whiteOutReportFailure(whitey);
+            else
+              whiteOutReportSuccess(whitey);
+              
+            var response = (String(ajax.responseText)).substr(2);
+            
+            setTimeout(function()
+              {
+                window.scroll(0, 0);
+                $('#gorilla_submit_result').html(response).show('blind', 150);
+              }, 1250);
+          }
+        });
+      return false;
+    }
+    catch(e)
+    {}
+    
+    return true;
+  }
+  addOnloadHook(function()
+    {
+      load_component('expander');
+    });
+  </script>
+  <div id="gorilla_submit_result">
+  <?php
+    echo substr(gorilla_process_post($have_geshi, $have_permission), 2);
+  ?>
+  </div>
+  
+  <form action="<?php echo makeUrlNS('Special', 'NewPaste'); ?>" method="post" name="gorilla_create" onsubmit="return gorilla_create_submit();">
+  
+    <?php
+    if ( $copy_from )
+    {
+      echo '<p style="float: left;">' . $lang->get('gorilla_msg_copying_from', array('paste_id' => $copy_from, 'paste_url' => makeUrlNS('Paste', $copy_from, false, true))) . '</p>';
+    }
+    ?>
+  
+    <!-- private -->
+    <div style="float: right; margin: 10px 1%;">
+      <label title="<?php echo $lang->get('gorilla_lbl_private_hint'); ?>">
+        <input type="checkbox" name="is_private" id="gorilla_private" <?php if ( $private ) echo 'checked="checked"'; ?> />
+        <img alt="<?php echo $lang->get('gorilla_lbl_private'); ?>" src="<?php echo cdnPath; ?>/images/lock16.png" />
+      </label>
+    </div>
+    
+    <!-- highlighting -->
+    <div style="float: right; margin: 10px;">
+    
+    <?php echo $lang->get('gorilla_lbl_highlight'); ?>
+    <?php if ( $have_geshi ): ?>
+      <select name="highlight" id="gorilla_highlight">
+        <?php
+        // print out options for each GeSHi format
+        global $geshi_supported_formats;
+        $formats = array_merge(array('plaintext'), $geshi_supported_formats);
+        foreach ( $formats as $format )
+        {
+          // $string = str_replace('-', '_', "geshi_lang_$format");
+          // if ( ($_ = $lang->get($string)) !== $string )
+          //   $string = $_;
+          // else
+            $string = $format;
+            
+          $sel = ( $format == $highlight ) ? ' selected="selected"' : '';
+          echo '<option value="' . $format . '"' . $sel . '>' . $string . '</option>' . "\n          ";
+        }
+        ?>
+      </select>
+    <?php else: ?>
+      <span style="color: #808080;"><?php echo $lang->get('gorilla_msg_no_geshi'); ?></span>
+      <input type="hidden" name="highlight_type" id="gorilla_highlight" value="plaintext" />
+    <?php endif; ?>
+                   
+    </div>
+  
+    <!-- text box -->
+    
+    <textarea id="gorilla_create_text" name="text" rows="30" cols="80" style="width: 98%; display: block; margin: 10px auto; clear: both;"><?php echo htmlspecialchars($text); ?></textarea>
+    
+    <fieldset enano:expand="closed" style="margin-bottom: 10px;">
+      <legend><?php echo $lang->get('gorilla_btn_advanced_options'); ?></legend>
+      <div>
+      
+      <!-- title -->
+      <p>
+        <?php echo $lang->get('gorilla_lbl_title'); ?>
+        <input type="text" name="title" id="gorilla_title" size="40" value="<?php echo htmlspecialchars($title); ?>" />
+      </p>
+      
+      <!-- nick -->
+      <p>
+        <?php echo $lang->get('gorilla_lbl_nick'); ?>
+        <?php
+        if ( !$have_permission && !$session->user_logged_in )
+        {
+          echo '<em>' . $lang->get('gorilla_msg_using_login_nick') . '</em>';
+          echo '<input type="hidden" name="nick" id="gorilla_nick" value="" />';
+        }
+        else if ( $session->user_logged_in )
+        {
+          $rankinfo = $session->get_user_rank($session->user_id);
+          echo '<em>' . $lang->get('gorilla_msg_using_logged_in_nick') . '</em> ';
+          echo '<span style="' . $rankinfo['rank_style'] . '">' . $session->username . '</span>';
+          echo '<input type="hidden" name="nick" id="gorilla_nick" value="" />';
+        }
+        else
+        {
+          echo '<input type="text" name="nick" id="gorilla_nick" value="' . $lang->get('gorilla_nick_anonymous') . '" />';
+        }
+        ?>
+      </p>
+      
+      <!-- ttl -->
+      <p>
+        <?php echo $lang->get('gorilla_lbl_ttl'); ?>
+        <em>
+        <label><input<?php if ( !in_array($ttl, array(0, 86400, 2592000)) ) echo ' checked="checked"'; ?> class="gorilla_ttl" type="radio" name="ttl" value="3600" /> <?php echo $lang->get('gorilla_lbl_ttl_hour'); ?></label>
+        <label><input<?php if ( $ttl == 86400                             ) echo ' checked="checked"'; ?> class="gorilla_ttl" type="radio" name="ttl" value="86400" /> <?php echo $lang->get('gorilla_lbl_ttl_day'); ?></label>
+        <label><input<?php if ( $ttl == 2592000                           ) echo ' checked="checked"'; ?> class="gorilla_ttl" type="radio" name="ttl" value="2592000" /> <?php echo $lang->get('gorilla_lbl_ttl_month'); ?></label>
+        <label><input<?php if ( $ttl == 0                                 ) echo ' checked="checked"'; ?> class="gorilla_ttl" type="radio" name="ttl" value="0" /> <?php echo $lang->get('gorilla_lbl_ttl_forever'); ?></label>
+        </em>
+      </p>
+      
+      </div>
+    </fieldset>
+  
+    <!-- login notice -->
+  
+    <?php if ( !$have_permission && !$session->user_logged_in ): ?>
+    <div class="info-box-mini">
+      <?php echo $lang->get('gorilla_msg_will_prompt_for_login'); ?>
+    </div>
+    <?php endif; ?>
+    
+    <!-- submit -->
+  
+    <input type="submit" style="font-size: x-large;" value="<?php echo $lang->get('gorilla_btn_submit'); ?>" />
+  </form>
+  <?php
+  
+  $output->footer();
+}
+
+// actual processing for submitted pastes
+function gorilla_process_post($have_geshi, $have_permission, $is_ajax = false)
+{
+  global $db, $session, $paths, $template, $plugins; // Common objects
+  global $lang;
+  
+  $fields = array(
+      'highlight' => 'string',
+      'text' => 'string',
+      'is_private' => 'boolean',
+      'nick' => 'string',
+      'title' => 'string',
+      'ttl' => 'integer'
+    );
+  
+  $info = array();
+  
+  if ( $is_ajax )
+  {
+    try
+    {
+      $request = enano_json_decode(@$_POST['r']);
+    }
+    catch ( Exception $e )
+    {
+      return '1;No JSON request given';
+    }
+    foreach ( $fields as $field => $type )
+    {
+      if ( !isset($request[$field]) )
+        return "1;Field \"$field\" not provided";
+      if ( ($ftype = gettype($request[$field])) !== $type )
+        return "1;Field \"$field\": expected $type, got $ftype";
+      
+      $info[$field] = $request[$field];
+    }
+  }
+  else
+  {
+    foreach ( $fields as $field => $type )
+    {
+      if ( !isset($_POST[$field]) && $field != 'is_private' )
+        return '';
+    }
+    $info = array(
+        'highlight' => $_POST['highlight'],
+        'text' => $_POST['text'],
+        'is_private' => isset($_POST['is_private']),
+        'nick' => $_POST['nick'],
+        'title' => $_POST['title'],
+        'ttl' => intval($_POST['ttl'])
+      );
+  }
+  
+  if ( !$have_permission )
+  {
+    return '1;<div class="error-box-mini">' . $lang->get('etc_access_denied') . '</div>';
+  }
+  
+  // validate highlight scheme
+  global $geshi_supported_formats;
+  if ( is_array($geshi_supported_formats) )
+  {
+    if ( !in_array($info['highlight'], $geshi_supported_formats) )
+      $info['highlight'] = 'plaintext';
+  }
+  else
+  {
+    $info['highlight'] = 'plaintext';
+  }
+  
+  setcookie('g_highlight', $info['highlight'], time() + 365 * 24 * 60 * 60);
+  
+  $info_db = $info;
+  foreach ( $info_db as &$item )
+  {
+    if ( is_string($item) )
+      $item = "'" . $db->escape($item) . "'";
+    else if ( is_bool($item) )
+      $item = $item ? '1' : '0';
+    else if ( is_int($item) )
+      $item = strval($item);
+    else
+      $item = "''";
+  }
+  
+  $now = time();
+  $flags = 0;
+  if ( $info['is_private'] )
+    $flags |= PASTE_PRIVATE;
+  
+  $sql = 'INSERT INTO ' . table_prefix . "pastes( paste_title, paste_text, paste_author, paste_author_name, paste_author_ip, paste_language, paste_timestamp, paste_ttl, paste_flags ) VALUES\n"
+       . "  ( {$info_db['title']}, {$info_db['text']}, $session->user_id, {$info_db['nick']}, '{$_SERVER['REMOTE_ADDR']}', {$info_db['highlight']}, $now, {$info_db['ttl']}, $flags );";
+       
+  if ( !$db->sql_query($sql) )
+    ( $is_ajax ) ? $db->die_json() : $db->_die();
+  
+  // avoid insert_id
+  $q = $db->sql_query('SELECT paste_id FROM ' . table_prefix . "pastes WHERE paste_timestamp = $now ORDER BY paste_id DESC LIMIT 1;");
+  if ( !$q )
+    ( $is_ajax ) ? $db->die_json() : $db->_die();
+  list($paste_id) = $db->fetchrow_num();
+  $db->free_result();
+  
+  $params = false;
+  if ( $flags & PASTE_PRIVATE )
+    $params = 'hash=' . hmac_sha1($paste_id, sha1($info['text']));
+  
+  $paste_url = makeUrlComplete('Paste', $paste_id, $params, true);
+  
+  return '0;'
+           . '<div class="info-box-mini">' . $lang->get('gorilla_msg_created') . '</div>'
+           . '<div style="font-size: larger; text-align: center; margin: 30px 0;">' . $lang->get('gorilla_msg_paste_url') . '<br /><input onfocus="this.select()" readonly="readonly" type="text" size="50" style="font-size: larger; text-align: center;" value="' . $paste_url . '" /></div>';
+}
+
+###############################################################################
+## PASTE DISPLAY
+###############################################################################
+
+class Namespace_Paste extends Namespace_Default
+{
+  protected $paste_data = false;
+  
+  public function __construct($page_id, $namespace, $revid = 0)
+  {
+    $this->page_id = $page_id;
+    $this->namespace = $namespace;
+    $this->revision_id = 0;
+    
+    $this->build_cdata();
+  }
+  
+  public function build_cdata()
+  {
+    global $db, $session, $paths, $template, $plugins; // Common objects
+    global $lang;
+  
+    $this->exists = false;
+    if ( ctype_digit($this->page_id) )
+    {
+      $q = $db->sql_query('SELECT p.*, u.username FROM ' . table_prefix . "pastes AS p\n"
+                        . "  LEFT JOIN " . table_prefix . "users AS u\n"
+                        . "    ON ( u.user_id = p.paste_author )\n"
+                        . "  WHERE p.paste_id = $this->page_id;");
+      if ( $db->numrows() > 0 )
+      {
+        $this->exists = true;
+        $this->paste_data = $db->fetchrow();
+      }
+      $db->free_result();
+    }
+    if ( $this->exists )
+    {
+      $this->cdata = array(
+        'name' => empty($this->paste_data['paste_title']) ? $lang->get('gorilla_untitled_paste') : $this->paste_data['paste_title'],
+        'urlname' => $this->page_id,
+        'namespace' => $this->namespace,
+        'special' => 0,
+        'visible' => 0,
+        'comments_on' => 1,
+        'protected' => 0,
+        'delvotes' => 0,
+        'delvote_ips' => '',
+        'wiki_mode' => 2,
+        'page_exists' => true,
+        'page_format' => getConfig('default_page_format', 'wikitext')
+      );
+    }
+    else
+    {
+      $this->cdata = array(
+        'name' => $lang->get('gorilla_title_404'),
+        'urlname' => $this->page_id,
+        'namespace' => $this->namespace,
+        'special' => 0,
+        'visible' => 0,
+        'comments_on' => 0,
+        'protected' => 0,
+        'delvotes' => 0,
+        'delvote_ips' => '',
+        'wiki_mode' => 2,
+        'page_exists' => false,
+        'page_format' => getConfig('default_page_format', 'wikitext')
+      );
+    }
+    $this->cdata = Namespace_Default::bake_cdata($this->cdata);
+  }
+  
+  public function send()
+  {
+    global $db, $session, $paths, $template, $plugins; // Common objects
+    global $output, $lang;
+    
+    $plugins->attachHook('page_type_string_set', '$this->namespace_string = $lang->get(\'gorilla_template_ns_string\');');
+    $template->add_header('<style type="text/css">
+      .geshi_highlighted a {
+        background-image: none !important;
+        padding-right: 0 !important;
+      }
+      </style>
+      ');
+    
+    if ( $this->exists )
+    {
+      gorilla_display_paste($this->paste_data);
+    }
+    else
+    {
+      $output->header();
+      $this->error_404();
+      $output->footer();
+    }
+  }
+  
+  public function error_404()
+  {
+    global $lang;
+    echo '<p>' . $lang->get('gorilla_msg_paste_not_found', array('create_link' => makeUrlNS('Special', 'NewPaste'))) . '</p>';
+  }
+}
+
+function gorilla_display_paste($data)
+{
+  global $db, $session, $paths, $template, $plugins; // Common objects
+  global $lang;
+  global $output;
+  
+  extract($data);
+  $perms = $session->fetch_page_acl($paste_id, 'Paste');
+  
+  if ( isset($_GET['format']) )
+  {
+    switch($_GET['format'])
+    {
+      case 'text':
+      case 'plain':
+        header('Content-type: text/plain');
+        echo $paste_text;
+        return true;
+        break;
+      case 'download':
+        header('Content-type: text/plain');
+        header('Content-disposition: attachment; filename="paste' . $paste_id . '.txt"');
+        header('Content-length: ' . strlen($paste_text));
+        echo $paste_text;
+        return true;
+        break;
+    }
+  }
+  
+  if ( $paste_flags & PASTE_PRIVATE || isset($_GET['delete']) )
+  {
+    if ( @$_GET['hash'] !== hmac_sha1($paste_id, sha1($paste_text)) )
+    {
+      die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('gorilla_msg_wrong_hash') . '</p>');
+    }
+  }
+  
+  $output->header();
+  
+  $perm = $paste_author == $session->user_id ? 'delete_paste_own' : 'delete_paste_others';
+  if ( isset($_GET['delete']) && !isset($_POST['cancel']) )
+  {
+    if ( isset($_POST['delete_confirm']) )
+    {
+      $q = $db->sql_query('DELETE FROM ' . table_prefix . "pastes WHERE paste_id = $paste_id;");
+      if ( !$q )
+        $db->_die();
+      
+      echo '<p>' . $lang->get('gorilla_msg_paste_deleted') . '</p>';
+    }
+    else
+    {
+      $submit_url = makeUrlNS('Paste', $paste_id, 'delete&hash=' . hmac_sha1($paste_id, sha1($paste_text)), true);
+      ?>
+      <form action="<?php echo $submit_url; ?>" method="post">
+        <p><?php echo $lang->get('gorilla_msg_delete_confirm'); ?></p>
+        <p>
+          <input type="submit" value="<?php echo $lang->get('gorilla_btn_delete_confirm'); ?>" name="delete_confirm" style="font-weight: bold;" />
+          <input type="submit" value="<?php echo $lang->get('etc_cancel'); ?>" name="cancel" />
+        </p>
+      </form>
+      <?php
+    }
+    $output->footer();
+    return true;
+  }
+  
+  if ( $paste_author > 1 )
+  {
+    // logged-in user
+    $rank_info = $session->get_user_rank($paste_author);
+    $user_link = '<a href="' . makeUrlNS('User', $username, false, true) . '" style="' . $rank_info['rank_style'] . '">' . htmlspecialchars($username) . '</a>';
+  }
+  else
+  {
+    // anonymous
+    $user_link = '<b>' . htmlspecialchars($paste_author_name) . '</b>';
+  }
+  $date = enano_date('D, j M Y H:i:s', $paste_timestamp);
+  $pasteinfo = $lang->get('gorilla_msg_paste_info', array('user_link' => $user_link, 'date' => $date));
+  
+  echo '<div class="mdg-infobox" style="margin: 10px 0;">';
+  echo '<div style="float: right;">
+          ' . $lang->get('gorilla_msg_other_formats', array('plain_link' => makeUrlNS('Paste', $paste_id, 'format=text', true), 'download_link' => makeUrlNS('Paste', $paste_id, 'format=download', true))) . '
+          /
+          <a title="' . $lang->get('gorilla_tip_new_paste') . '" href="' . makeUrlNS('Special', 'NewPaste') . '">' . $lang->get('gorilla_btn_new_paste') . '</a>
+          /
+          <a title="' . $lang->get('gorilla_tip_copy_from_this') . '" href="' . makeUrlNS('Special', 'NewPaste/Copy=' . $paste_id) . '">' . $lang->get('gorilla_btn_copy_from_this') . '</a>';
+          
+  if ( $perms->get_permissions($perm) && $session->user_logged_in )
+  {
+    echo ' / <a title="' . $lang->get('gorilla_tip_delete') . '" href="' . makeUrlNS('Paste', $paste_id, 'delete&hash=' . hmac_sha1($paste_id, sha1($paste_text)), true) . '">' . $lang->get('gorilla_btn_delete') . '</a>';
+  }
+  if ( $perms->get_permissions('mod_misc') )
+  {
+    echo ' / <span title="' . $lang->get('gorilla_tip_paste_ip') . '">' . $paste_author_ip . '</span>';
+  }
+  
+  echo '</div>';
+  echo $pasteinfo;
+  echo '</div>';
+  
+  if ( preg_match('/^## /m', $paste_text) )
+  {
+    gorilla_show_text_multi($paste_text, $paste_language);
+  }
+  else
+  {
+    gorilla_show_text($paste_text, $paste_language);
+  }
+  
+  $output->footer();
+}
+
+function gorilla_show_text($text, $lang)
+{
+  $have_geshi = isset($GLOBALS['geshi_supported_formats']);
+  
+  if ( $have_geshi )
+  {
+    if ( $lang == 'plaintext' )
+      $lang = 'text';
+    
+    if ( !defined('GESHI_ROOT') )
+    define('GESHI_ROOT', ENANO_ROOT . '/plugins/geshi/');
+  
+    require_once ( GESHI_ROOT . 'base.php' );
+    
+    $geshi = new GeSHi($text, $lang, null);
+    $geshi->set_header_type(GESHI_HEADER_DIV);
+    $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, 2);
+    $geshi->set_overall_class('geshi_highlighted');
+    $parsed = $geshi->parse_code();
+    
+    echo $parsed;
+  }
+  else
+  {
+    echo '<h3>FIXME: WRITE REAL PLAINTEXT FORMATTER</h3>';
+    echo '<pre>' . htmlspecialchars($text) . '</pre>';
+  }
+}
+
+function gorilla_show_text_multi($text, $lang)
+{
+  $sections = preg_split('/^## .*$/m', $text);
+  $headingcount = preg_match_all('/^## (.+?)(?: \[([a-z_-]+)\])? *$/m', $text, $matches);
+  
+  // if we have one heading less than the number of sections, print the first section
+  while ( count($sections) > $headingcount )
+  {
+    gorilla_show_text(trim($sections[0], "\r\n"), $lang);
+    
+    unset($sections[0]);
+    $sections = array_values($sections);
+  }
+  
+  foreach ( $matches[0] as $i => $_ )
+  {
+    $clang = !empty($matches[2][$i]) ? $matches[2][$i] : $lang;
+    echo '<h2>' . htmlspecialchars(trim($matches[1][$i])) . '</h2>';
+    gorilla_show_text(trim($sections[$i], "\r\n"), $clang);
+  }
+}
+
+// make sure pastes are pruned on a regular basis
+function gorilla_prune_expired()
+{
+  global $db, $session, $paths, $template, $plugins; // Common objects
+  
+  $now = time();
+  $q = $db->sql_query('DELETE FROM ' . table_prefix . "pastes WHERE paste_timestamp + paste_ttl < $now AND paste_ttl > 0;");
+}
+
+register_cron_task('gorilla_prune_expired', 1);
+
+/**!install dbms="mysql"; **
+CREATE TABLE {{TABLE_PREFIX}}pastes(
+  paste_id int(18) NOT NULL auto_increment,
+  paste_title text DEFAULT NULL,
+  paste_text text NOT NULL DEFAULT '',
+  paste_author int(12) NOT NULL DEFAULT 1,
+  paste_author_name varchar(255) NOT NULL DEFAULT 'Anonymous',
+  paste_author_ip varchar(39) NOT NULL,
+  paste_language varchar(32) NOT NULL DEFAULT 'plaintext',
+  paste_timestamp int(12) NOT NULL DEFAULT 0,
+  paste_ttl int(12) NOT NULL DEFAULT 86400,
+  paste_flags int(8) NOT NULL DEFAULT 0,
+  PRIMARY KEY ( paste_id )
+) ENGINE=`MyISAM` CHARSET=`UTF8` COLLATE=`utf8_bin`;
+
+**!*/
+
+/**!uninstall **
+DROP TABLE {{TABLE_PREFIX}}pastes;
+**!*/
+
+/**!language**
+<code>
+{
+  eng: {
+    categories: ['meta', 'gorilla'],
+    strings: {
+      meta: {
+        gorilla: 'Gorilla',
+      },
+      gorilla: {
+        acl_delete_paste_own: 'Delete own pastes',
+        acl_delete_paste_others: 'Delete others\' pastes',
+        
+        page_create: 'Create paste',
+        msg_copying_from: 'Copying from <a href="%paste_url%">paste #%paste_id%</a>.',
+        lbl_highlight: 'Language:',
+        msg_no_geshi: 'Not supported',
+        btn_advanced_options: 'Advanced options',
+        lbl_private: 'Private paste',
+        lbl_private_hint: 'Don\'t list this paste or allow it to be included in searches',
+        lbl_title: 'Title:',
+        lbl_nick: 'Nickname:',
+        nick_anonymous: 'Anonymous',
+        msg_using_login_nick: 'Using username provided during login',
+        msg_using_logged_in_nick: 'Logged in; using nickname:',
+        lbl_ttl: 'Keep it for:',
+        lbl_ttl_hour: '1 hour',
+        lbl_ttl_day: '1 day',
+        lbl_ttl_month: '1 month',
+        lbl_ttl_forever: 'forever',
+        msg_will_prompt_for_login: 'You are not logged in. You will be asked to log in when you click the submit button below.',
+        btn_submit: 'Paste it!',
+        
+        msg_created: 'Paste created.',
+        msg_paste_url: 'Share this paste using the following URL:',
+        
+        untitled_paste: 'Untitled paste',
+        title_404: 'Paste not found',
+        msg_paste_not_found: 'This paste cannot be found or has been deleted. <a href="%create_link%">Create a new paste</a>',
+        msg_wrong_hash: 'Either you are trying to view a private paste which requires a hash in the URL or you were linked to this page from an outside source and the CSRF protection kicked in.',
+        
+        msg_paste_info: 'By %user_link%, pasted on %date%',
+        msg_other_formats: '<a title="View as plain text" href="%plain_link%" onclick="window.open(this.href, \'gorillaplaintext\', \'address=no,status=no,toolbar=no,menus=no,scroll=yes,width=640,height=480\'); return false;">raw</a> / <a title="Download paste" href="%download_link%">dl</a>',
+        btn_new_paste: 'new',
+        tip_new_paste: 'Create a new paste',
+        btn_copy_from_this: 'cp',
+        tip_copy_from_this: 'Create a new paste, copying this one into the form',
+        btn_delete: 'rm',
+        tip_delete: 'Delete this paste',
+        tip_paste_ip: 'IP address of paste author',
+        template_ns_string: 'paste',
+        
+        msg_paste_deleted: 'Paste deleted.',
+        msg_delete_confirm: 'Really delete this paste?',
+        btn_delete_confirm: 'Delete',
+      }
+    }
+  }
+}
+</code>
+**!*/