Revision [20137]

This is an old revision of WikiFile made by ChewBakka on 2008-07-18 14:13:36.

 

WikiFile action and handler


This module allows you to put images (and other files) anywhere on any page (provided you have write access to that page).

I did everything in Wikka 1.1.6.2 so I can not tell whether it will work in older versions.

How to use it


Example: assume you have a photograph of the Millennium Falcon in a file named IMG_1234.JPG on your computer. You want to put it in the wiki.

So you edit the respective page and put the {{file}} placeholder at the place where you want to see the image.

MillenniumFalcon
===== Millennium Falcon  =====
Below you see a photograph of the Millennium Falcon, last year on Tatooine.
There was lots of sand, as always.
{{file}}


The page now shows a form which allows you to upload the file.

After uploading, you see the image in the page, and below the image another upload form is visible, in case you want to replace the photo with another one later. You can also delete the file.

Now, suppose you want to add more images to the same page. Then you need to distinguish the files by adding an id.

MillenniumFalcon
===== Millennium Falcon  =====
Below you see a photograph of the Millennium Falcon, last year on Tatooine.
There was lots of sand, as always.
{{file}}

On the next images, some Jawas admire the mighty space vessel:
{{file id="jawas1"}}
{{file id="jawas2"}}


As you see above, it is allowed to have one file per page which has no id. Also, don't worry about id of images on other pages - the id just need to be unique inside the wiki page.

Advanced usage


To display an image also on another page, you do not need to upload it twice. Instead, you can tell the file placeholder to fetch the image from a different page:

{{file page="PageWhichContainsTheFile"}}

How to install it


We are going to add two program files to the Wikka software, and create a folder.

In your Wikka root directory, there is a folder named actions which contains several php files. Save the following code (use the grab button) as file.php in the actions folder.

actions/file.php (line 1)
  1. <?php
  2.  
  3. /**
  4.  * WikiFile action
  5.  *
  6.  * Displays a file in the page (as image or link) and optinally an upload form.
  7.  *
  8.  * Syntax:   {{file [id="IdUniqueInPage"] [page="PageToTakeFileFrom"]}}
  9.  *
  10.  * @package  Actions
  11.  * @name     File
  12.  *
  13.  * @author   {@link http://wikkawiki.org/ChewBakka ChewBakka} (first draft)
  14.  */
  15.  
  16.  
  17. /**
  18.  * Reads the file's metadata file and returns it as an array.
  19.  *
  20.  * @author  {@link http://wikkawiki.org/ChewBakka ChewBakka} (first draft)
  21.  * @input   $tag name of the page; default = current page.
  22.  * @output  Array or (if no file in page) null.
  23.  */
  24. if( !function_exists('getFile') ) // for pages which include multiple files
  25. {
  26.    function getFile( $tag, $wikka, $id )
  27.    {
  28.       $data = null; // this variable will be returned
  29.       $sep = ($id ? '~' : '' ); // Separator between tag and id in filename
  30.    
  31.       // Read the metadata from the file TagName~id.file
  32.       $metafile = $wikka->config['upload_path'].'/'.$tag.$sep.$id.'.file';
  33.       if (file_exists($metafile) && $contents = file_get_contents($metafile))
  34.       {
  35.          $lines = explode( "\n", $contents );
  36.          // Lines look like "key: value" -> convert them to an array
  37.          $data = array(
  38.             'extension'    => '',        // file extension, e.g. 'png', 'jpg', 'ogg'
  39.             'content-type' => '',        // mime content type, e.g. 'image/png'
  40.             'uploaded'     => '?',       // date and time, . e.g. '2007-01-31 18:00'
  41.             'uploader'     => '?',       // wikiname, e.g. 'ChewBakka'
  42.             'image'        => 'false',   // 'true' for image files
  43.             'width'        => '',        // width in pixels (images only)
  44.             'height'       => ''         // height in pixels (images only)
  45.          );
  46.          foreach ($lines as $line)
  47.          {
  48.             $a = explode( ':', trim($line), 2 );
  49.             if( count($a) == 2 )
  50.             {
  51.                $data[ $a[0] ] = trim( $a[1] );
  52.             }
  53.          }
  54.          // Add convenient attributes which can not be stored permanently
  55.          $data['filename'] = $tag.$sep.$id . '.' . $data['extension'];
  56.          $data['path']     = $wikka->config['upload_path'] .'/' . $data['filename'];
  57.          $data['metafile'] = $metafile;
  58.          $data['url']      = $wikka->config['base_url'] . $tag .  '/file' . ($id?'?id='.$id:'');
  59.          // Final check: file must exist.
  60.          if (! file_exists( $data['path'] ) )
  61.          {
  62.             $data = null;
  63.          }
  64.       }
  65.       return $data;
  66.    }
  67. }
  68.  
  69.  
  70.  
  71. /**
  72.  * Here the real action starts
  73.  */
  74.  
  75. // Initialize variables
  76. $output = ''; // The HTML code that is being generated by this action
  77. $uploadform = False; // Show an upload form instead of the file?
  78. $tag = $this->GetPageTag(); // The page that the user requested
  79. $id = ''; // Optional file id, appended to the tag name
  80.  
  81. if (is_array($vars) && array_key_exists('page', $vars))
  82. {
  83.    $tag = $vars['page'];
  84. }
  85.  
  86. if (is_array($vars) && array_key_exists('id', $vars))
  87. {
  88.    $id = $vars['id'];
  89. }
  90.  
  91. if( $id )
  92. {
  93.    // A file ID was submitted.
  94.    // Check if it is alphanumeric and not longer than 100 characters
  95.    // in order to prevent security issues.
  96.    if (ereg('[^a-zA-Z0-9_]',$id) || strlen($id) > 100)
  97.    {
  98.       $id = '!BAD!';
  99.       $output = 'An illegal file ID was submittet';
  100.    }
  101. }
  102.  
  103. if( $id != '!BAD!' )
  104. {
  105.  
  106.    $data = getFile($tag,$this,$id);
  107.  
  108.    // Check whether current user has read access and if there is a file.
  109.    if ($this->HasAccess( 'read', $tag ) && $data )
  110.    {
  111.       if ($data['image'] == 'true')
  112.       {
  113.          // Image file - create an <img> tag
  114.          $output =  '<a href="'  . $tag . '">'
  115.                  .  '<img src="' . $data['url']    . '"'
  116.                  .  ' width="'   . $data['width']  . '"'
  117.                  .  ' height="'  . $data['height'] . '"'
  118.                  .  ' alt="'     . $tag            . '"'
  119.                  .  ' border="0">'
  120.                  .  '</a>';
  121.       }
  122.       else
  123.       {
  124.          // Plain file - create a download link
  125.          $output = '<a href="' . $data['url'] . '">' . $data['url'] . '</a>';
  126.       }
  127.    }
  128.  
  129.    if ($this->HasAccess( 'write', $tag ) && $tag == $this->GetPageTag())
  130.    {
  131.  
  132.       // If on the same page, also show an upload form
  133.  
  134.       $headline = 'Upload a file to appear here';
  135.       $buttonlabel = 'Upload';
  136.       $deletelink  = '';
  137.       if( $data )
  138.       {
  139.          $headline    = 'REPLACE the '
  140.                       . ($data['image'] == 'true' ? 'image' : 'file' )
  141.                       . ' above by uploading a new file';
  142.          $buttonlabel = 'Upload';
  143.          $deletelink  = '<br /><a href="' . $data['url'] . (strpos($data['url'],'?')?'&':'?')
  144.                       . 'cmd=delete">DELETE</a>'
  145.                       . ' the '
  146.                       . ($data['image'] == 'true' ? 'image' : 'file' )
  147.                       . ' above from the wiki';
  148.       }
  149.  
  150.       $miniref = $this->Href('file', $tag);
  151.  
  152.       $output = $output
  153.               . '<br />' . $headline . ' <br />'
  154.               . '<form action="' . $miniref . '" method="POST" enctype="multipart/form-data">'
  155.                 . (!$this->config["rewrite_mode"] ? '<input type="hidden" name="wakka" value="' . $miniref . '" />' : '')
  156.               . '<input name="file" type="file" size="72">'
  157.               . '<input type="hidden" name="id" value="' . $id . '">'
  158.               . '<input type="submit" value="' . $buttonlabel . '">'
  159.               . $deletelink
  160.               . '</form><br />'
  161.               ;
  162.    }
  163.  
  164. }
  165.  
  166. print $this->ReturnSafeHTML($output);
  167.  
  168. ?>


Also in your Wikka root directory, there is a folder named handlers, which in turn contains a folder names page which contains several php files. Save the following code (use the grab button) as file.php in the handlers/page folder. (Yes, we have two files with the same name, but in different locations and with different function).
handlers/page/file.php (line 1)
  1. <?php
  2.  
  3. /**
  4.  * WikiFile handler
  5.  *
  6.  * Supports file retrieval and upload.
  7.  *
  8.  * Syntax: http://server/WikiName/file[?id=someid]
  9.  *
  10.  * @package Handlers
  11.  * @name    File
  12.  *
  13.  * @author  {@link http://wikkawiki.org/ChewBakka ChewBakka} (first draft)
  14.  */
  15.  
  16.  
  17. /**
  18.  * Reads the file's metadata file and returns it as an array.
  19.  *
  20.  * @author  {@link http://wikkawiki.org/ChewBakka ChewBakka} (first draft)
  21.  * @input   $tag name of the page; default = current page.
  22.  * @output  Array or (if no file) null.
  23.  */
  24. function getFile( $tag, $wikka, $id )
  25. {
  26.    $data = null; // this variable will be returned
  27.    $sep = ($id ? '~' : '' ); // Separator between tag and id in filename
  28.  
  29.    // Read the metadata from the file TagName~id.file
  30.    $metafile = $wikka->config['upload_path'].'/'.$tag.$sep.$id.'.file';
  31.    if (file_exists($metafile) && $contents = file_get_contents($metafile))
  32.    {
  33.       $lines = explode( "\n", $contents );
  34.       // Lines look like "key: value" -> convert them to an array
  35.       $data = array(
  36.          'extension'    => '',        // file extension, e.g. 'png', 'jpg', 'ogg'
  37.          'content-type' => '',        // mime content type, e.g. 'image/png'
  38.          'uploaded'     => '?',       // date and time, . e.g. '2007-01-31 18:00'
  39.          'uploader'     => '?',       // wikiname, e.g. 'ChewBakka'
  40.          'image'        => 'false',   // 'true' for image files
  41.          'width'        => '',        // width in pixels (images only)
  42.          'height'       => ''         // height in pixels (images only)
  43.       );
  44.       foreach ($lines as $line)
  45.       {
  46.          $a = explode( ':', trim($line), 2 );
  47.          if( count($a) == 2 )
  48.          {
  49.             $data[ $a[0] ] = trim( $a[1] );
  50.          }
  51.       }
  52.       // Add convenient attributes which can not be stored permanently
  53.       $data['filename'] = $tag.$sep.$id . '.' . $data['extension'];
  54.       $data['path']     = $wikka->config['upload_path'] .'/' . $data['filename'];
  55.       $data['metafile'] = $metafile;
  56.       $data['url']      = $wikka->config['base_url'] . $tag .  '/file' . ($id?'?id='.$id:'');
  57.       // Final check: file must exist.
  58.       if (! file_exists( $data['path'] ) )
  59.       {
  60.          $data = null;
  61.       }
  62.    }
  63.    return $data;
  64. }
  65.  
  66.  
  67. /**
  68.  * Store an http-uploaded file.
  69.  *
  70.  * @author  {@link http://wikkawiki.org/ChewBakka ChewBakka} (first draft)
  71.  * @input   $uploaded_file An item from PHP's $_FILES array (see there)
  72.  * @output  None
  73.  */
  74. function saveFile( $uploaded_file, $wikka, $id )
  75. {
  76.    $pathinfo = pathinfo( $uploaded_file['name'] );
  77.    $extension = strtolower( $pathinfo['extension'] );
  78.    $pathname = $wikka->config['upload_path'] . '/' . $wikka->tag . ($id?'~':'') . $id;
  79.    $path = $pathname . '.' . $extension;
  80.    $contenttype = '';
  81.    // Remove existing file if it already exists
  82.    if ($data = getFile( $wikka->GetPageTag(), $wikka, $id ) )
  83.    {
  84.       // File exists; remove it first
  85.       // E.g. if a GIF exists and the user replaces it with a PNG now.
  86.       unlink( $data['path'] );
  87.       unlink( $data['metafile'] );
  88.       $data = null;
  89.    }
  90.    if (move_uploaded_file( $uploaded_file['tmp_name'], $path ))
  91.    {
  92.       // Find the mime type (it will be stored in the metadata)
  93.       if( function_exists('mime_content_type') )
  94.       {
  95.          $contenttype = mime_content_type($path);
  96.       }
  97.       if( ! $contenttype  )
  98.       {
  99.          switch( $extension )
  100.          {
  101.             // Quick resonse for most frequently used file types
  102.             case 'png':
  103.                $contenttype = 'image/png';
  104.                break;
  105.             case 'jpg':
  106.                $contenttype = 'image/jpeg';
  107.                break;
  108.             case 'ogg':
  109.                $contenttype = 'application/ogg';
  110.                break;
  111.             case 'zip':
  112.                $contenttype = 'application/zip';
  113.                break;
  114.             default:
  115.                // Use wikka's own mime_types.txt
  116.                if (file_exists( $wikka->config['mime_types'] ))
  117.                {
  118.                   foreach (explode("\n",file_get_contents($wikka->config['mime_types'])) as $line)
  119.                   {
  120.                      $line = trim($line);
  121.                      if ($line != '' && substr($line,0,1) != '#' && strpos($line,$extension))
  122.                      {
  123.                         $a = explode( ' ', str_replace(chr(9),' ',$line) );
  124.                         if (in_array( $extension, $a ))
  125.                         {
  126.                            $contenttype = $a[0];
  127.                            break;
  128.                         }
  129.                      }
  130.                   }
  131.                }
  132.          }
  133.       }
  134.       // build an array with metadata
  135.       $data = array(
  136.          'extension'    => $extension,
  137.          'content-type' => $contenttype,
  138.          'uploaded'     => date('Y-m-d H:i'),
  139.          'uploader'     => $wikka->GetUserName(),
  140.          'image'        => 'false'
  141.       );
  142.       if( substr($data['content-type'],0,6) == 'image/'  )
  143.       {
  144.          $data['image'] = 'true';
  145.          $size = getimagesize($path);
  146.          $data['width']  = $size[0];
  147.          $data['height'] = $size[1];
  148.       }
  149.       // Save the dat aarry in the metadata file
  150.       $contents = '';
  151.       foreach ($data as $key => $value)
  152.       {
  153.          $contents .= ($key . ': ' . $value . "\n");
  154.       }
  155.       file_put_contents( $pathname . '.file', $contents );
  156.    }
  157. }
  158.  
  159.  
  160.  
  161. /**
  162.  * Here the real handler starts
  163.  */
  164.  
  165.  
  166. $handled = False;
  167. $cmd = (isset($_REQUEST['cmd']) ? $_REQUEST['cmd'] : 'get');
  168.  
  169. if ($id = (isset($_REQUEST['id']) ? $_REQUEST['id'] : ''))
  170. {
  171.    // A file ID was submitted.
  172.    // Check if it is alphanumeric and not longer than 100 characters
  173.    // in order to prevent security issues.
  174.    if (ereg('[^a-zA-Z0-9_]',$id) || strlen($id) > 100)
  175.    {
  176.       $cmd = 'nothing';
  177.    }
  178. }
  179.  
  180. if (isset($_FILES['file']))
  181. {
  182.    $cmd = 'upload';
  183. }
  184.  
  185. switch( $cmd )
  186. {
  187.    case 'upload':
  188.       // User uploaded a file
  189.       if ($this->HasAccess('write'))
  190.       {
  191.          $uploadedfile = $_FILES['file'];
  192.          if ($uploadedfile['error'] > 0)
  193.          {
  194.             // redirect to page
  195.             $this->redirect( $this->Href(), 'Transmitted file was damaged' );
  196.          }
  197.          else
  198.          {
  199.             saveFile( $uploadedfile, $this, $id );
  200.             $this->Redirect( $this->Href() );
  201.          }
  202.          $handled = True;
  203.       }
  204.       break;
  205.    case 'get':
  206.       // Return the file
  207.       if ($this->HasAccess('read') && $data = getFile($this->GetPageTag(),$this,$id))
  208.       {
  209.          header( 'Content-Type: ' . $data['content-type'] );
  210.          if ($data['image'] != 'true')
  211.          {
  212.             header( 'Content-Disposition: attachment; filename="' . $data['filename'] . '"');
  213.          }
  214.          @readfile($data['path']);
  215.          exit();
  216.          $handled = True;
  217.       }
  218.       break;
  219.    case 'delete':
  220.       // Delete the file permanently from the wiki
  221.       if ($this->HasAccess('write') && $data = getFile($this->GetPageTag(),$this,$id))
  222.       {
  223.          unlink( $data['metafile'] );
  224.          unlink( $data['path'] );
  225.       }
  226.       $this->Redirect( $this->Href() );
  227.       $handled = True;
  228.       break;
  229. }
  230.  
  231. if( !$handled )
  232. {
  233.    $this->Redirect( $this->Href() );
  234. }
  235. ?>


And now for the really last step - read your wikka.config.php, it should contain a line like this:
wikka.config.php (line 40)
  1.     // ...
  2.     'upload_path' => 'uploads',
  3.     // ...

This means that the Wikka root folder should contain a folder named uploads; please create this folder if it does not exist.
Should the above line not be in your config file, please add the line too.

Ready! If something does not work yet, leave me a comment. --ChewBakka

How it works


Behind the scenes, an uploaded file is stored in the uploads directory along with a metadata file. In the example above, Wikka stores the uploaded file IMG_1234.JPG as MillenniumFalconOnTatooine.jpg along with MillenniumFalconOnTatooine.file which contains the metadata. The latter is a plain ascii file which tells Wikka that the actual file is a jpg file.

History


2007-Feb-05
handlers/page/file.php: bugfix (switch without break, now if/elseif); sends filename with download (makes download easier)
libs/Wakka.class.php: supports *.*.gz

2008-Jul-15
Complete rewrite.
Only ACTION and HANDLER; no more Wikka.class.php modifications.

2008-Jul-18
Multiple files per page now possible.

Credits


While developing the above code, I browsed through many Wikka sources in order to learn how to write a good extension, and how to document it. They were too many to remember, so I just want to say thanks to all the people who made Wikka.


Category: CategoryUserContributions User contributions
There are no comments on this page.
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki