Revision [16033]

This is an old revision of WikiFile made by ChewBakka on 2007-02-02 17:35:16.

 

In Wikka, almost everything is a page.

For example, a category is a page to which other pages link. If you want to stat a new category, you do not need to learn something new - just create the category page and link your pages to it.

Here I would like to present a solution where files are represented by pages too. I will first give a description on how to use it, then the necessary pieces of code.

Description

On the server side, there are no file names any more - any file is "contained" in a particular page and referenced using only the page name. If the file is an image, it is visible in the page (through the { {file} } action) along with explaining text which is writen in the page as usual. The access rights for viewing and mpdifying files are just the page rights.

I order to add a screenshot (or any other file) to your wiki, you will first create a page for it. This is something youshould do anyway, because you should write some explaining text about your screenshot. The page should include a { {file} } action to your page, although this is not necessary for being a file page - it is just for file pages what the { {category} } action is for category pages.

The page footer reads Edit page :: File :: Stats :: Referrers and so on - note the addition of File. Clicking on it opens the file handler, which displays informations about the file (if there is already a file in the page) and an upload form; uploading a file into a page which already contains a file causes the old file to be replaced with the new file - there can be only one file per page.

Code

Below are five pieces of code. The files Wakka.class.php and footer.php are parts of the Wikka software; I have extended them without changing existing functions (hopefully). I have also added two files: actions/file.php and handlers/page/file.php.

Here comes the modification of actions/footer.php; lines 5,6,7 are added, anything else remains unchanged. This adds File:: to the footer.
  1. <div class="footer">
  2. <?php
  3.     echo $this->FormOpen("", "TextSearch", "get");
  4.     echo $this->HasAccess("write") ? "<a href=\"".$this->href("edit")."\" title=\"Click to edit this page\">Edit page</a> ::\n" : "";
  5.     echo ($this->HasAccess("write") || $this->HasAccess("read"))
  6.         ? '<a href="' . $this->href("file"). '" title="Click to view/edit the file in this page">File</a> ::' . "\n"
  7.         : '';
  8.     // ...


In Wakka.class.php I have extended the FormOpen function, because the html form must include an encoding attribute if you want to upload files. I have added a fourth parameter named $withFiles. This is the extended function:
  1.     function FormOpen($method = "", $tag = "", $formMethod = "post", $withFile = false )
  2.     {
  3.         $enctype = '';
  4.         if ($withFile) $enctype = ' enctype="multipart/form-data"';
  5.         $result = "<form action=\"".$this->Href($method, $tag)."\" method=\"".$formMethod."\"$enctype>\n";
  6.         if (!$this->config["rewrite_mode"]) $result .= "<input type=\"hidden\" name=\"wakka\" value=\"".$this->MiniHref($method, $tag)."\" />\n";
  7.         return $result;
  8.     }


Also in Wakka.class.php I have added four functions which perform the most basic file handling without access right checking. (One function is actually only a workaround for probllems with mime_content_type() which I experienced). I have just added them and the end of the class, and they look like this:
  1.     /**
  2.      * Return mime type for the given file extension.
  3.      * Slow, but useful if PHP's mime_content_type() cannot be used.
  4.      * Uses Wikka's mime_types.txt and some hardcoded associations.
  5.      *
  6.      * @author  {@link http://wikkawiki.org/ChewBakka ChewBakka} (first draft)
  7.      * @input   $extension file extension, e.g. 'png', 'jpg', 'ogg'
  8.      * @output  mime content type, e.g. 'image/png', or empty string.
  9.      */
  10.     function MimeTypeFromExtension( $extension )
  11.     {
  12.         // Quick resonse for most frequently used file types
  13.         if ($extension == 'png') return 'image/png';
  14.         if ($extension == 'jpg') return 'image/jpeg';
  15.         if ($extension == 'ogg') return 'application/ogg';
  16.         // If not recoginzed, use mime_types.txt
  17.         if (file_exists( $this->config['mime_types'] ))
  18.         {
  19.             foreach (explode("\n",file_get_contents($this->config['mime_types'])) as $line)
  20.             {
  21.                 $line = trim($line);
  22.                 if ($line != '' && substr($line,0,1) != '#' && strpos($line,$extension))
  23.                 {
  24.                     $a = explode( ' ', str_replace(chr(9),' ',$line) );
  25.                     if (in_array( $extension, $a ))
  26.                     {
  27.                         return $a[0];
  28.                     }
  29.                 }
  30.             }
  31.         }
  32.         return ''; // if nothing was found
  33.     }
  34.  
  35.     /**
  36.      * Return an array describing the file which is stored in a page.
  37.      *
  38.      * @author  {@link http://wikkawiki.org/ChewBakka ChewBakka} (first draft)
  39.      * @input   $tag name of the page; default = current page.
  40.      * @output  Array or (if no file in page) null. Keys:
  41.      *          extension     file extension, e.g. 'png', 'jpg', 'ogg'
  42.      *          content-type  mime content type, e.g. 'image/png'
  43.      *          uploaded      when and who,. e.g. '2007-01-31 ChewBakka'
  44.      *          image         'yes' or 'no'
  45.      *          width         Width in pixels (images only)
  46.      *          height        Height in pixels (images only)
  47.      *          filename      PageName.extension
  48.      *          path          upload_path/PageName.extension
  49.      *          url           'http://..../PageName/file?action=get'
  50.      */
  51.     function GetFile( $tag = '' )
  52.     {
  53.         $data = null; // this variable will be returned
  54.         if (!$tag) $tag = $this->tag;
  55.         $metafile = $this->config['upload_path'].'/'.$tag.'.file';  // metadata
  56.         if (file_exists($metafile) && $contents = file_get_contents($metafile))
  57.         {
  58.             $lines = explode( "\n", $contents );
  59.             // Lines look like "key: value" -> convert into array
  60.             $data = array(
  61.                 'extension'    => '',
  62.                 'content-type' => '',
  63.                 'uploaded'     => '',
  64.                 'image'        => 'false',
  65.                 'width'        => '',
  66.                 'height'       => '',
  67.                 'filename'     => '',
  68.                 'path'         => '',
  69.                 'url'          => ''
  70.             );
  71.             foreach ($lines as $line)
  72.             {
  73.                 $a = explode( ":", trim($line), 2 );
  74.                 if( count($a) == 2 )
  75.                 {
  76.                     $data[ $a[0] ] = trim( $a[1] );
  77.                 }
  78.             }
  79.             // Convenient attributes which can not be stored permanently
  80.             $data['filename'] = $tag . '.' . $data['extension'];
  81.             $data['path'] = $this->config['upload_path'] .'/' . $data['filename'];
  82.             $data['url'] = $this->config['base_url'] . $tag .  '/file' .
  83.                 ($this->config['rewrite_mode']=='1' ? '?' : '&') .
  84.                 'action=get';
  85.             // Final check: file must exist.
  86.             if (! file_exists( $data['path'] ) )
  87.             {
  88.                 $data = null;
  89.             }
  90.         }
  91.         return $data;
  92.     }
  93.  
  94.     /**
  95.      * Remove the file from the current page.
  96.      *
  97.      * @author  {@link http://wikkawiki.org/ChewBakka ChewBakka} (first draft)
  98.      * @input   none
  99.      * @output  none
  100.      */
  101.     function RemoveFile()
  102.     {
  103.         if ($data = $this->GetFile())
  104.         {
  105.             unlink( $this->config['upload_path'] . '/' . $this->tag . '.file' );
  106.             unlink( $this->config['upload_path'] . '/' . $data['filename'] );
  107.         }
  108.     }
  109.  
  110.     /**
  111.      * Store an http-uploaded file in the current page.
  112.      * Any previous file will be replaced with the new file.
  113.      *
  114.      * @author  {@link http://wikkawiki.org/ChewBakka ChewBakka} (first draft)
  115.      * @input   $uploaded_file An item from PHP's $_FILES array (see there)
  116.      * @output  None
  117.      */
  118.     function SaveFile( $uploaded_file )
  119.     {
  120.         $this->RemoveFile();
  121.         $pathinfo = pathinfo( $uploaded_file['name'] );
  122.         $extension = strtolower( $pathinfo['extension'] );
  123.         $pathname = $this->config['upload_path'] . '/' . $this->tag;
  124.         $path = $pathname . '/' . $extension;
  125.         if (move_uploaded_file( $uploaded_file['tmp_name'], $path ))
  126.         {
  127.             $contenttype = mime_content_type($path);
  128.             if( ! $contenttype  )
  129.             {
  130.                 $contenttype = $this->MimeTypeFromExtension( $extension );
  131.             }
  132.             // build an array with metadata
  133.             $data = array(
  134.                 'extension'    => $extension,
  135.                 'content-type' => $contenttype,
  136.                 'uploaded'     => date('Y-m-d H:i') . ' ' . $this->GetUserName(),
  137.                 'image'        => 'false'
  138.             );
  139.             if( substr($data['content-type'],0,6) == 'image/'  )
  140.             {
  141.                 $data['image'] = 'true';
  142.                 $size = getimagesize($path);
  143.                 $data['width']  = $size[0];
  144.                 $data['height'] = $size[1];
  145.             }
  146.             // Create metadata file
  147.             $contents = '';
  148.             foreach ($data as $key => $value)
  149.             {
  150.                 $contents .= ($key . ': ' . $value . "\n");
  151.             }
  152.             file_put_contents( $pathname . '.file', $contents );
  153.         }
  154.     }


The remaining two code pieces are two new files. Both are named file.php, but one is a handler and the other one is the action.

Here comes handlers/page/file.php:
/php;1)
<?php

/**
 * Display file info and a small upload form.
 *
 * @package Handlers
 * @name    File
 *
 * @author  {@link http://wikkawiki.org/ChewBakka ChewBakka} (first draft)
 *
 * @input   ?action=get returns the file via http
 *          uploading a file causes storage of the file "in the page",
 *          overwriting any previous page file.
 *
 */

$has_read_access  = $this->HasAccess('read');
$has_write_access = $this->HasAccess('write') && $has_read_access;

switch (True) 
{
	case $_REQUEST['action'] == 'get':
		// ?action=get
		if ($this->HasAccess('read') && $data = $this->GetFile())
		{
			header('Content-Type: ' . $data['content-type'] );
			@readfile($data['path']);
			exit();
		}
	case isset($_FILES['file']):
		// User uploaded a file
		if ($has_write_access)
		{
			$uploadedfile = $_FILES['file'];
			if ($uploadedfile['error'] > 0)
			{
				// redirect to page
				$this->redirect( $this->Href(), 'Transmitted file was damaged' );
			} 
			else
			{
				$this->SaveFile( $uploadedfile );
				$this->Redirect( $this->Href() );
			}
		}
	default:
		// Display file info and an upload form
		$data = null;
		if ($has_read_access)
		{
			if ($data = $this->GetFile())
			{
				print '<h3>Existing file</h3><p>';
				foreach ($data as $key => $value)
				{
					if ($key != 'url')
					{
						print htmlspecialchars(htmlentities($key))   . ': ' .
							  htmlspecialchars(htmlentities($value)) . '<br />';
					}
				}
				$a = '< href="' . $data['url'] . '">' . $data['url'] . '</a>';
 				print 'url: ' . $this->ReturnSafeHTML($a) . '<br />';
				print 'Wikka syntax: {{file page="' . $this->GetPageTag() . '"}}' . '<br />';
				print '</p>';
			}
			else
			{
				print '<p>There is no file yet</p>';
			}
		}
		else
		{
			print '<div class="page"><em>Sorry, you are nor allowed to view this file.</em></div>';
		}
		if ($has_write_access)
		{
			print '<h3>Upload form</h3><p>';
			print $this->FormOpen( 'file', $this->GetPageTag(), 'POST', true );
			print '<input name="file" type="file" size="72">';
			print '<input type="submit" value="upload">';
			print $this->FormClose();
			if ($data) print '<p>Note: the uploaded file will <em>replace</em> the existing file.</p>';
		}
		else
		{
			print '<div class="page"><em>Sorry, you are nor allowed to put a file here.</em></div>';
		}
}

?>


And finally actions/file.php
  1. <?php
  2. /**
  3.  * Display the page's file, either inline or as link.
  4.  *
  5.  * If the file is an image, then it is displayed inline.
  6.  * For other file types, a download link is printed.
  7.  * Note that at most one file can be stored in a page.
  8.  *
  9.  * Syntax:
  10.  *  {{file [page="SomeWikkaName"]}}
  11.  *
  12.  * @package  Actions
  13.  * @name     File
  14.  *
  15.  * @author   {@link http://wikkawiki.org/ChewBakka ChewBakka} (first draft)
  16.  *
  17.  * @input    string $page: name of the page whose file is to be displayed.
  18.  *           optional; default is the current page itself.
  19.  */
  20.  
  21. $tag = (is_array($vars) && array_key_exists('page',$vars)) ? $vars['page'] : $this->GetPageTag();
  22.  
  23. $output = '';
  24.  
  25. // Check whether current has read access and if there is a file.
  26. if ($this->HasAccess( 'read', $tag ) && $data = $this->GetFile($tag))
  27. {
  28.     if ($data['image'] == 'true')
  29.     {
  30.         // Image file - create an <img> tag
  31.         $output = '<img src="'    . $data['url']    . '"' .
  32.                       ' width="'  . $data['width']  . '"' .
  33.                       ' height="' . $data['height'] . '"' .
  34.                       ' alt="'    . $tag            . '">';
  35.     }
  36.     else
  37.     {
  38.         // Plain file - create a download link
  39.         $output = '<a href="' . $data['url'] . '">' . $data['url'] . '</a>';
  40.     }
  41. }
  42.  
  43. print $this->ReturnSafeHTML($output);
  44.  
  45. ?>
There are no comments on this page.
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki