]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.8.x' of git@gitorious.org:+laconica-developers/laconica/dev into...
authorRobin Millette <millette@controlyourself.ca>
Sun, 31 May 2009 21:12:04 +0000 (17:12 -0400)
committerRobin Millette <millette@controlyourself.ca>
Sun, 31 May 2009 21:12:04 +0000 (17:12 -0400)
Conflicts:

js/util.js
lib/attachmentlist.php

18 files changed:
.gitignore
README
actions/file.php [new file with mode: 0644]
actions/newnotice.php
classes/File_redirection.php
config.php.sample
extlib/MIME/Type.php [new file with mode: 0644]
extlib/MIME/Type/Extension.php [new file with mode: 0644]
extlib/MIME/Type/Parameter.php [new file with mode: 0644]
js/jquery.form.js
js/util.js
lib/attachmentlist.php
lib/common.php
lib/form.php
lib/noticeform.php
lib/noticelist.php
lib/router.php
lib/util.php

index da6947bfdc3f27978ab534a07be6a96a4797a6ef..3418d8ee54d1178448469dd417721c9831c1ea2d 100644 (file)
@@ -1,5 +1,6 @@
 avatar/*
 files/*
+file/*
 _darcs/*
 logs/*
 config.php
@@ -16,3 +17,8 @@ dataobject.ini
 .buildpath
 .project
 .settings
+TODO.rym
+config-*.php
+good-config.php
+lac08.log
+php.log
diff --git a/README b/README
index db912f201616bff07dc0807a088111fe48484818..76b56a52e5a869cddd54539bef9849a0e1decc02 100644 (file)
--- a/README
+++ b/README
@@ -1196,6 +1196,21 @@ reporturl: URL to post statistics to. Defaults to Laconica developers'
            set 'run' to 'never' than to set this value to something
            nonsensical.
 
+
+attachments
+-----------
+
+The software lets users upload files with their notices. You can configure
+the types of accepted files by mime types and a trio of quota options:
+per file, per user (total), per user per month.
+
+supported: an array of mime types you accept to store and distribute,
+           like 'image/gif', 'video/mpeg', 'audio/mpeg', etc.
+file_quota: maximum size for a single file upload in bytes.
+user_quota: total size in bytes a user can store.
+monthly_quota: total size permitted in the current month.
+
+
 Troubleshooting
 ===============
 
diff --git a/actions/file.php b/actions/file.php
new file mode 100644 (file)
index 0000000..1179dbe
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, Controlez-Vous, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('LACONICA')) { exit(1); }
+
+require_once(INSTALLDIR.'/actions/shownotice.php');
+
+class FileAction extends ShowNoticeAction
+{
+    function showPage() {
+        $source_url = common_local_url('file', array('notice' => $this->notice->id));
+        $query = "select file_redirection.url as url from file join file_redirection on file.id = file_redirection.file_id where file.url = '$source_url'";
+        $file = new File_redirection;
+        $file->query($query);
+        $file->fetch();
+        if (empty($file->url)) {
+            die('nothing attached here');
+        } else {
+            header("Location: {$file->url}");
+            die();
+        }
+    }
+}
+
index ae0ff9636335809f2e9aaca4697d56b567e85332..29b748dd1ac509cd2e9ca84cbc340217196653a0 100644 (file)
@@ -84,20 +84,24 @@ class NewnoticeAction extends Action
 
     function handle($args)
     {
-        parent::handle($args);
-
         if (!common_logged_in()) {
             $this->clientError(_('Not logged in.'));
         } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            // check for this before token since all POST and FILES data
+            // is losts when size is exceeded
+            if (empty($_POST) && $_SERVER['CONTENT_LENGTH']) {
+                $this->clientError(sprintf(_('The server was unable to handle ' .
+                    'that much POST data (%s bytes) due to its current configuration.'),
+                    $_SERVER['CONTENT_LENGTH']));
+            }
+            parent::handle($args);
 
             // CSRF protection
             $token = $this->trimmed('token');
             if (!$token || $token != common_session_token()) {
                 $this->clientError(_('There was a problem with your session token. '.
                                      'Try again, please.'));
-                return;
             }
-
             try {
                 $this->saveNewNotice();
             } catch (Exception $e) {
@@ -109,6 +113,57 @@ class NewnoticeAction extends Action
         }
     }
 
+    function isSupportedFileType() {
+        require_once 'MIME/Type.php';
+
+        $filetype = MIME_Type::autoDetect($_FILES['attach']['tmp_name']);
+        if (in_array($filetype, common_config('attachments', 'supported'))) {
+            return true;
+        }
+        $media = MIME_Type::getMedia($filetype);
+        if ('application' !== $media) {
+            $hint = sprintf(_(' Try using another %s format.'), $media);
+        } else {
+            $hint = '';
+        }
+        $this->clientError(sprintf(
+            _('%s is not a supported filetype on this server.'), $filetype) . $hint);
+    }
+
+    function isRespectsQuota($user) {
+        if ($_FILES['attach']['size'] > common_config('attachments', 'file_quota')) {
+            $this->clientError(sprintf(_('No file may be larger than %d bytes ' .
+                'and the file you sent was %d bytes. Try to upload a smaller version.'),
+                common_config('attachments', 'file_quota'), $_FILES['attach']['size']));
+        }
+
+        $query = "select sum(size) as total from file join file_to_post on file_to_post.file_id = file.id join notice on file_to_post.post_id = notice.id where profile_id = {$user->id} and file.url like '%/notice/%/file'";
+        $file = new File;
+        $file->query($query);
+        $file->fetch();
+        $total = $file->total + $_FILES['attach']['size'];
+        if ($total > common_config('attachments', 'user_quota')) {
+            $this->clientError(sprintf(_('A file this large would exceed your user quota of %d bytes.'), common_config('attachments', 'user_quota')));
+        }
+
+        $query .= ' month(modified) = month(now()) and year(modified) = year(now())';
+        $file2 = new File;
+        $file2->query($query);
+        $file2->fetch();
+        $total2 = $file2->total + $_FILES['attach']['size'];
+        if ($total2 > common_config('attachments', 'monthly_quota')) {
+            $this->clientError(sprintf(_('A file this large would exceed your monthly quota of %d bytes.'), common_config('attachments', 'monthly_quota')));
+        }
+        return true;
+    }
+
+    function isValidFileAttached($user) {
+        return isset($_FILES['attach']['error'])
+            && ($_FILES['attach']['error'] === UPLOAD_ERR_OK)
+            && $this->isSupportedFileType()
+            && $this->isRespectsQuota($user);
+    }
+
     /**
      * Save a new notice, based on arguments
      *
@@ -131,7 +186,6 @@ class NewnoticeAction extends Action
             $this->clientError(_('No content!'));
         } else {
             $content_shortened = common_shorten_links($content);
-
             if (mb_strlen($content_shortened) > 140) {
                 $this->clientError(_('That\'s too long. '.
                                      'Max notice size is 140 chars.'));
@@ -158,17 +212,54 @@ class NewnoticeAction extends Action
             $replyto = 'false';
         }
 
-//        $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1,
+        switch ($_FILES['attach']['error']) {
+            case UPLOAD_ERR_NO_FILE:
+                // no file uploaded
+                // nothing to do
+                break;
+
+             case UPLOAD_ERR_OK:
+                // file was uploaded alright
+                // lets check if we really support its format
+                // and it doesn't go over quotas
+
+
+                if (!$this->isValidFileAttached($user)) {
+                    die('clientError() should trigger an exception before reaching here.');
+                }
+                break;
+
+            case UPLOAD_ERR_INI_SIZE:
+                $this->clientError(_('The uploaded file exceeds the upload_max_filesize directive in php.ini.'));
+
+            case UPLOAD_ERR_FORM_SIZE:
+                $this->clientError(_('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.'));
+
+            case UPLOAD_ERR_PARTIAL:
+                $this->clientError(_('The uploaded file was only partially uploaded.'));
+
+            case  UPLOAD_ERR_NO_TMP_DIR:
+                $this->clientError(_('Missing a temporary folder.'));
+
+            case UPLOAD_ERR_CANT_WRITE:
+                $this->clientError(_('Failed to write file to disk.'));
+
+            case UPLOAD_ERR_EXTENSION:
+                $this->clientError(_('File upload stopped by extension.'));
+
+            default:
+                die('Should never reach here.');
+        }
+
         $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1,
                                   ($replyto == 'false') ? null : $replyto);
 
         if (is_string($notice)) {
             $this->clientError($notice);
-            return;
         }
 
+        $this->storeFile($notice);
         $this->saveUrls($notice);
-
         common_broadcast_notice($notice);
 
         if ($this->boolean('ajax')) {
@@ -194,6 +285,33 @@ class NewnoticeAction extends Action
         }
     }
 
+    function storeFile($notice) {
+        if (UPLOAD_ERR_NO_FILE === $_FILES['attach']['error']) return;
+        $filename = basename($_FILES['attach']['name']);
+        $destination = "file/{$notice->id}-$filename";
+        if (move_uploaded_file($_FILES['attach']['tmp_name'], INSTALLDIR . "/$destination")) {
+            $file = new File;
+            $file->url = common_local_url('file', array('notice' => $notice->id));
+            $file->size = filesize(INSTALLDIR . "/$destination");
+            $file->date = time();
+            $file->mimetype = $_FILES['attach']['type'];
+            if ($file_id = $file->insert()) {
+                $file_redir = new File_redirection;
+                $file_redir->url = common_path($destination);
+                $file_redir->file_id = $file_id;
+                $file_redir->insert();
+
+                $f2p = new File_to_post;
+                $f2p->file_id = $file_id; 
+                $f2p->post_id = $notice->id; 
+                $f2p->insert();
+            } else {
+                $this->clientError(_('There was a database error while saving your file. Please try again.'));
+            }
+        }
+    }
+
+
     /** save all urls in the notice to the db
      *
      * follow redirects and save all available file information
@@ -203,7 +321,7 @@ class NewnoticeAction extends Action
      *
      * @return void
      */
-    function saveUrls($notice) {
+    function saveUrls($notice, $uploaded = null) {
         common_replace_urls_callback($notice->content, array($this, 'saveUrl'), $notice->id);
     }
 
index 0eae68178373757bf3bf5f042850610d4a341d9a..212cc36158ab3783fd6b72f7fbe9acf13eb28a7f 100644 (file)
@@ -133,7 +133,7 @@ class File_redirection extends Memcached_DataObject
         $file->limit(1);
         $file->orderBy('len');
         $file->find(true);
-        if (!empty($file->id)) {
+        if (!empty($file->url) && (strlen($file->url) < strlen($long_url))) {
             return $file->url;
         }
 
index 282826a7fb075957c32098c8753e0c277d88c911..636f4cf8e22255322c0452e154727f1ff3cb0f41 100644 (file)
@@ -215,3 +215,11 @@ $config['sphinx']['port'] = 3312;
 // $config['snapshot']['run'] = 'never';
 // If you want to report statistics in a cron job instead.
 // $config['snapshot']['run'] = 'cron';
+
+// Support for file uploads (attachments),
+// select supported mimetypes and quotas (in bytes)
+// $config['attachments']['supported'] = array('image/png', 'application/ogg');
+// $config['attachments']['file_quota'] = 5000000;
+// $config['attachments']['user_quota'] = 50000000;
+// $config['attachments']['monthly_quota'] = 15000000;
+
diff --git a/extlib/MIME/Type.php b/extlib/MIME/Type.php
new file mode 100644 (file)
index 0000000..c335f8d
--- /dev/null
@@ -0,0 +1,523 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002, 2008 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Ian Eure <ieure@php.net>                                    |
+// +----------------------------------------------------------------------+
+//
+// $Id: Type.php,v 1.6 2009/01/16 11:49:45 cweiske Exp $
+
+require_once 'PEAR.php';
+
+$_fileCmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd');
+$_fileCmd = 'file';
+
+/**
+ * Class for working with MIME types
+ *
+ * @category MIME
+ * @package  MIME_Type
+ * @license  PHP License 3.0
+ * @version  1.2.0
+ * @link     http://pear.php.net/package/MIME_Type
+ * @author   Ian Eure <ieure@php.net>
+ */
+class MIME_Type
+{
+    /**
+     * The MIME media type
+     *
+     * @var string
+     */
+    var $media = '';
+
+    /**
+     * The MIME media sub-type
+     *
+     * @var string
+     */
+    var $subType = '';
+
+    /**
+     * Optional MIME parameters
+     *
+     * @var array
+     */
+    var $parameters = array();
+
+    /**
+     * List of valid media types.
+     * A media type is the string in front of the slash.
+     * The media type of "text/xml" would be "text".
+     *
+     * @var array
+     */
+    var $validMediaTypes = array(
+        'text',
+        'image',
+        'audio',
+        'video',
+        'application',
+        'multipart',
+        'message'
+    );
+
+
+    /**
+     * Constructor.
+     *
+     * If $type is set, if will be parsed and the appropriate class vars set.
+     * If not, you get an empty class.
+     * This is useful, but not quite as useful as parsing a type.
+     *
+     * @param string $type MIME type
+     *
+     * @return void
+     */
+    function MIME_Type($type = false)
+    {
+        if ($type) {
+            $this->parse($type);
+        }
+    }
+
+
+    /**
+     * Parse a mime-type and set the class variables.
+     *
+     * @param string $type MIME type to parse
+     *
+     * @return void
+     */
+    function parse($type)
+    {
+        $this->media      = $this->getMedia($type);
+        $this->subType    = $this->getSubType($type);
+        $this->parameters = array();
+        if (MIME_Type::hasParameters($type)) {
+            require_once 'MIME/Type/Parameter.php';
+            foreach (MIME_Type::getParameters($type) as $param) {
+                $param = new MIME_Type_Parameter($param);
+                $this->parameters[$param->name] = $param;
+            }
+        }
+    }
+
+
+    /**
+     * Does this type have any parameters?
+     *
+     * @param string $type MIME type to check
+     *
+     * @return boolean true if $type has parameters, false otherwise
+     * @static
+     */
+    function hasParameters($type)
+    {
+        if (strstr($type, ';')) {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Get a MIME type's parameters
+     *
+     * @param string $type MIME type to get parameters of
+     *
+     * @return array $type's parameters
+     * @static
+     */
+    function getParameters($type)
+    {
+        $params = array();
+        $tmp    = explode(';', $type);
+        for ($i = 1; $i < count($tmp); $i++) {
+            $params[] = trim($tmp[$i]);
+        }
+        return $params;
+    }
+
+
+    /**
+     * Strip parameters from a MIME type string.
+     *
+     * @param string $type MIME type string
+     *
+     * @return string MIME type with parameters removed
+     * @static
+     */
+    function stripParameters($type)
+    {
+        if (strstr($type, ';')) {
+            return substr($type, 0, strpos($type, ';'));
+        }
+        return $type;
+    }
+
+
+    /**
+     * Removes comments from a media type, subtype or parameter.
+     *
+     * @param string $string   String to strip comments from
+     * @param string &$comment Comment is stored in there.
+     *
+     * @return string   String without comments
+     * @static
+     */
+    function stripComments($string, &$comment)
+    {
+        if (strpos($string, '(') === false) {
+            return $string;
+        }
+
+        $inquote   = false;
+        $quoting   = false;
+        $incomment = 0;
+        $newstring = '';
+
+        for ($n = 0; $n < strlen($string); $n++) {
+            if ($quoting) {
+                if ($incomment == 0) {
+                    $newstring .= $string[$n];
+                } else if ($comment !== null) {
+                    $comment .= $string[$n];
+                }
+                $quoting = false;
+            } else if ($string[$n] == '\\') {
+                $quoting = true;
+            } else if (!$inquote && $incomment > 0 && $string[$n] == ')') {
+                $incomment--;
+                if ($incomment == 0 && $comment !== null) {
+                    $comment .= ' ';
+                }
+            } else if (!$inquote && $string[$n] == '(') {
+                $incomment++;
+            } else if ($string[$n] == '"') {
+                if ($inquote) {
+                    $inquote = false;
+                } else {
+                    $inquote = true;
+                }
+            } else if ($incomment == 0) {
+                $newstring .= $string[$n];
+            } else if ($comment !== null) {
+                $comment .= $string[$n];
+            }
+        }
+
+        if ($comment !== null) {
+            $comment = trim($comment);
+        }
+
+        return $newstring;
+    }
+
+
+    /**
+     * Get a MIME type's media
+     *
+     * @note 'media' refers to the portion before the first slash
+     *
+     * @param string $type MIME type to get media of
+     *
+     * @return string $type's media
+     * @static
+     */
+    function getMedia($type)
+    {
+        $tmp = explode('/', $type);
+        return strtolower(trim(MIME_Type::stripComments($tmp[0], $null)));
+    }
+
+
+    /**
+     * Get a MIME type's subtype
+     *
+     * @param string $type MIME type to get subtype of
+     *
+     * @return string $type's subtype, null if invalid mime type
+     * @static
+     */
+    function getSubType($type)
+    {
+        $tmp = explode('/', $type);
+        if (!isset($tmp[1])) {
+            return null;
+        }
+        $tmp = explode(';', $tmp[1]);
+        return strtolower(trim(MIME_Type::stripComments($tmp[0], $null)));
+    }
+
+
+    /**
+     * Create a textual MIME type from object values
+     *
+     * This function performs the opposite function of parse().
+     *
+     * @return string MIME type string
+     */
+    function get()
+    {
+        $type = strtolower($this->media . '/' . $this->subType);
+        if (count($this->parameters)) {
+            foreach ($this->parameters as $key => $null) {
+                $type .= '; ' . $this->parameters[$key]->get();
+            }
+        }
+        return $type;
+    }
+
+
+    /**
+     * Is this type experimental?
+     *
+     * @note Experimental types are denoted by a leading 'x-' in the media or
+     *       subtype, e.g. text/x-vcard or x-world/x-vrml.
+     *
+     * @param string $type MIME type to check
+     *
+     * @return boolean true if $type is experimental, false otherwise
+     * @static
+     */
+    function isExperimental($type)
+    {
+        if (substr(MIME_Type::getMedia($type), 0, 2) == 'x-' ||
+            substr(MIME_Type::getSubType($type), 0, 2) == 'x-') {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Is this a vendor MIME type?
+     *
+     * @note Vendor types are denoted with a leading 'vnd. in the subtype.
+     *
+     * @param string $type MIME type to check
+     *
+     * @return boolean true if $type is a vendor type, false otherwise
+     * @static
+     */
+    function isVendor($type)
+    {
+        if (substr(MIME_Type::getSubType($type), 0, 4) == 'vnd.') {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Is this a wildcard type?
+     *
+     * @param string $type MIME type to check
+     *
+     * @return boolean true if $type is a wildcard, false otherwise
+     * @static
+     */
+    function isWildcard($type)
+    {
+        if ($type == '*/*' || MIME_Type::getSubtype($type) == '*') {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Perform a wildcard match on a MIME type
+     *
+     * Example:
+     * MIME_Type::wildcardMatch('image/*', 'image/png')
+     *
+     * @param string $card Wildcard to check against
+     * @param string $type MIME type to check
+     *
+     * @return boolean true if there was a match, false otherwise
+     * @static
+     */
+    function wildcardMatch($card, $type)
+    {
+        if (!MIME_Type::isWildcard($card)) {
+            return false;
+        }
+
+        if ($card == '*/*') {
+            return true;
+        }
+
+        if (MIME_Type::getMedia($card) == MIME_Type::getMedia($type)) {
+            return true;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Add a parameter to this type
+     *
+     * @param string $name    Attribute name
+     * @param string $value   Attribute value
+     * @param string $comment Comment for this parameter
+     *
+     * @return void
+     */
+    function addParameter($name, $value, $comment = false)
+    {
+        $tmp = new MIME_Type_Parameter();
+
+        $tmp->name               = $name;
+        $tmp->value              = $value;
+        $tmp->comment            = $comment;
+        $this->parameters[$name] = $tmp;
+    }
+
+
+    /**
+     * Remove a parameter from this type
+     *
+     * @param string $name Parameter name
+     *
+     * @return void
+     */
+    function removeParameter($name)
+    {
+        unset($this->parameters[$name]);
+    }
+
+
+    /**
+     * Autodetect a file's MIME-type
+     *
+     * This function may be called staticly.
+     *
+     * @internal Tries to use fileinfo extension at first. If that
+     *  does not work, mime_magic is used. If this is also not available
+     *  or does not succeed, "file" command is tried to be executed with
+     *  System_Command. When that fails, too, then we use our in-built
+     *  extension-to-mimetype-mapping list.
+     *
+     * @param string $file   Path to the file to get the type of
+     * @param bool   $params Append MIME parameters if true
+     *
+     * @return string $file's MIME-type on success, PEAR_Error otherwise
+     *
+     * @since 1.0.0beta1
+     * @static
+     */
+    function autoDetect($file, $params = false)
+    {
+        // Sanity checks
+        if (!file_exists($file)) {
+            return PEAR::raiseError("File \"$file\" doesn't exist");
+        }
+
+        if (!is_readable($file)) {
+            return PEAR::raiseError("File \"$file\" is not readable");
+        }
+
+        if (function_exists('finfo_file')) {
+            $finfo = finfo_open(FILEINFO_MIME);
+            $type  = finfo_file($finfo, $file);
+            finfo_close($finfo);
+            if ($type !== false && $type !== '') {
+                return MIME_Type::_handleDetection($type, $params);
+            }
+        }
+
+        if (function_exists('mime_content_type')) {
+            $type = mime_content_type($file);
+            if ($type !== false && $type !== '') {
+                return MIME_Type::_handleDetection($type, $params);
+            }
+        }
+
+        @include_once 'System/Command.php';
+        if (class_exists('System_Command')) {
+            return MIME_Type::_handleDetection(
+                MIME_Type::_fileAutoDetect($file),
+                $params
+            );
+        }
+
+        require_once 'MIME/Type/Extension.php';
+        $mte = new MIME_Type_Extension();
+        return $mte->getMIMEType($file);
+    }
+
+
+    /**
+     * Handles a detected MIME type and modifies it if necessary.
+     *
+     * @param string $type   MIME Type of a file
+     * @param bool   $params Append MIME parameters if true
+     *
+     * @return string $file's MIME-type on success, PEAR_Error otherwise
+     */
+    function _handleDetection($type, $params)
+    {
+        // _fileAutoDetect() may have returned an error.
+        if (PEAR::isError($type)) {
+            return $type;
+        }
+
+        // Don't return an empty string
+        if (!$type || !strlen($type)) {
+            return PEAR::raiseError("Sorry, couldn't determine file type.");
+        }
+
+        // Strip parameters if present & requested
+        if (MIME_Type::hasParameters($type) && !$params) {
+            $type = MIME_Type::stripParameters($type);
+        }
+
+        return $type;
+    }
+
+
+    /**
+     * Autodetect a file's MIME-type with 'file' and System_Command
+     *
+     * This function may be called staticly.
+     *
+     * @param string $file Path to the file to get the type of
+     *
+     * @return string $file's MIME-type
+     *
+     * @since 1.0.0beta1
+     * @static
+     */
+    function _fileAutoDetect($file)
+    {
+        $cmd = new System_Command();
+
+        // Make sure we have the 'file' command.
+        $fileCmd = PEAR::getStaticProperty('MIME_Type', 'fileCmd');
+        if (!$cmd->which($fileCmd)) {
+            unset($cmd);
+            return PEAR::raiseError("Can't find file command \"{$fileCmd}\"");
+        }
+
+        $cmd->pushCommand($fileCmd, "-bi " . escapeshellarg($file));
+        $res = $cmd->execute();
+        unset($cmd);
+
+        return $res;
+    }
+}
+
diff --git a/extlib/MIME/Type/Extension.php b/extlib/MIME/Type/Extension.php
new file mode 100644 (file)
index 0000000..1987e2a
--- /dev/null
@@ -0,0 +1,298 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2009 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Christian Schmidt <schmidt@php.net>                         |
+// +----------------------------------------------------------------------+
+//
+// $Id: Extension.php,v 1.1 2009/01/16 11:49:45 cweiske Exp $
+
+require_once 'PEAR.php';
+
+/**
+ * Class for mapping file extensions to MIME types.
+ *
+ * @category MIME
+ * @package  MIME_Type
+ * @author   Christian Schmidt <schmidt@php.net>
+ * @license  PHP License 3.0
+ * @version  1.2.0
+ * @link     http://pear.php.net/package/MIME_Type
+ */
+class MIME_Type_Extension
+{
+    /**
+     * Mapping between file extension and MIME type.
+     *
+     * @internal The array is sorted alphabetically by value and with primary
+     *  extension first. Be careful about not adding duplicate keys - PHP
+     *  silently ignores duplicates. The following command can be used for
+     *  checking for duplicates:
+     *    grep "=> '" Extension.php | cut -d\' -f2 | sort | uniq -d
+     *  application/octet-stream is generally used as fallback when no other
+     *  MIME-type can be found, but the array does not contain a lot of such
+     *  unknown extension. One entry exists, though, to allow detection of
+     *  file extension for this MIME-type.
+     *
+     * @var array
+     */
+    var $extensionToType = array (
+        'ez'        => 'application/andrew-inset',
+        'atom'      => 'application/atom+xml',
+        'jar'       => 'application/java-archive',
+        'hqx'       => 'application/mac-binhex40',
+        'cpt'       => 'application/mac-compactpro',
+        'mathml'    => 'application/mathml+xml',
+        'doc'       => 'application/msword',
+        'dat'       => 'application/octet-stream',
+        'oda'       => 'application/oda',
+        'ogg'       => 'application/ogg',
+        'pdf'       => 'application/pdf',
+        'ai'        => 'application/postscript',
+        'eps'       => 'application/postscript',
+        'ps'        => 'application/postscript',
+        'rdf'       => 'application/rdf+xml',
+        'rss'       => 'application/rss+xml',
+        'smi'       => 'application/smil',
+        'smil'      => 'application/smil',
+        'gram'      => 'application/srgs',
+        'grxml'     => 'application/srgs+xml',
+        'kml'       => 'application/vnd.google-earth.kml+xml',
+        'kmz'       => 'application/vnd.google-earth.kmz',
+        'mif'       => 'application/vnd.mif',
+        'xul'       => 'application/vnd.mozilla.xul+xml',
+        'xls'       => 'application/vnd.ms-excel',
+        'xlb'       => 'application/vnd.ms-excel',
+        'xlt'       => 'application/vnd.ms-excel',
+        'xlam'      => 'application/vnd.ms-excel.addin.macroEnabled.12',
+        'xlsb'      => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
+        'xlsm'      => 'application/vnd.ms-excel.sheet.macroEnabled.12',
+        'xltm'      => 'application/vnd.ms-excel.template.macroEnabled.12',
+        'docm'      => 'application/vnd.ms-word.document.macroEnabled.12',
+        'dotm'      => 'application/vnd.ms-word.template.macroEnabled.12',
+        'ppam'      => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
+        'pptm'      => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
+        'ppsm'      => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
+        'potm'      => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
+        'ppt'       => 'application/vnd.ms-powerpoint',
+        'pps'       => 'application/vnd.ms-powerpoint',
+        'odc'       => 'application/vnd.oasis.opendocument.chart',
+        'odb'       => 'application/vnd.oasis.opendocument.database',
+        'odf'       => 'application/vnd.oasis.opendocument.formula',
+        'odg'       => 'application/vnd.oasis.opendocument.graphics',
+        'otg'       => 'application/vnd.oasis.opendocument.graphics-template',
+        'odi'       => 'application/vnd.oasis.opendocument.image',
+        'odp'       => 'application/vnd.oasis.opendocument.presentation',
+        'otp'       => 'application/vnd.oasis.opendocument.presentation-template',
+        'ods'       => 'application/vnd.oasis.opendocument.spreadsheet',
+        'ots'       => 'application/vnd.oasis.opendocument.spreadsheet-template',
+        'odt'       => 'application/vnd.oasis.opendocument.text',
+        'odm'       => 'application/vnd.oasis.opendocument.text-master',
+        'ott'       => 'application/vnd.oasis.opendocument.text-template',
+        'oth'       => 'application/vnd.oasis.opendocument.text-web',
+        'potx'      => 'application/vnd.openxmlformats-officedocument.presentationml.template',
+        'ppsx'      => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+        'pptx'      => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+        'xlsx'      => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+        'xltx'      => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+        'docx'      => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+        'dotx'      => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+        'vsd'       => 'application/vnd.visio',
+        'wbxml'     => 'application/vnd.wap.wbxml',
+        'wmlc'      => 'application/vnd.wap.wmlc',
+        'wmlsc'     => 'application/vnd.wap.wmlscriptc',
+        'vxml'      => 'application/voicexml+xml',
+        'bcpio'     => 'application/x-bcpio',
+        'vcd'       => 'application/x-cdlink',
+        'pgn'       => 'application/x-chess-pgn',
+        'cpio'      => 'application/x-cpio',
+        'csh'       => 'application/x-csh',
+        'dcr'       => 'application/x-director',
+        'dir'       => 'application/x-director',
+        'dxr'       => 'application/x-director',
+        'dvi'       => 'application/x-dvi',
+        'spl'       => 'application/x-futuresplash',
+        'tgz'       => 'application/x-gtar',
+        'gtar'      => 'application/x-gtar',
+        'hdf'       => 'application/x-hdf',
+        'js'        => 'application/x-javascript',
+        'skp'       => 'application/x-koan',
+        'skd'       => 'application/x-koan',
+        'skt'       => 'application/x-koan',
+        'skm'       => 'application/x-koan',
+        'latex'     => 'application/x-latex',
+        'nc'        => 'application/x-netcdf',
+        'cdf'       => 'application/x-netcdf',
+        'sh'        => 'application/x-sh',
+        'shar'      => 'application/x-shar',
+        'swf'       => 'application/x-shockwave-flash',
+        'sit'       => 'application/x-stuffit',
+        'sv4cpio'   => 'application/x-sv4cpio',
+        'sv4crc'    => 'application/x-sv4crc',
+        'tar'       => 'application/x-tar',
+        'tcl'       => 'application/x-tcl',
+        'tex'       => 'application/x-tex',
+        'texinfo'   => 'application/x-texinfo',
+        'texi'      => 'application/x-texinfo',
+        't'         => 'application/x-troff',
+        'tr'        => 'application/x-troff',
+        'roff'      => 'application/x-troff',
+        'man'       => 'application/x-troff-man',
+        'me'        => 'application/x-troff-me',
+        'ms'        => 'application/x-troff-ms',
+        'ustar'     => 'application/x-ustar',
+        'src'       => 'application/x-wais-source',
+        'xhtml'     => 'application/xhtml+xml',
+        'xht'       => 'application/xhtml+xml',
+        'xslt'      => 'application/xslt+xml',
+        'xml'       => 'application/xml',
+        'xsl'       => 'application/xml',
+        'dtd'       => 'application/xml-dtd',
+        'zip'       => 'application/zip',
+        'au'        => 'audio/basic',
+        'snd'       => 'audio/basic',
+        'mid'       => 'audio/midi',
+        'midi'      => 'audio/midi',
+        'kar'       => 'audio/midi',
+        'mpga'      => 'audio/mpeg',
+        'mp2'       => 'audio/mpeg',
+        'mp3'       => 'audio/mpeg',
+        'aif'       => 'audio/x-aiff',
+        'aiff'      => 'audio/x-aiff',
+        'aifc'      => 'audio/x-aiff',
+        'm3u'       => 'audio/x-mpegurl',
+        'wma'       => 'audio/x-ms-wma',
+        'wax'       => 'audio/x-ms-wax',
+        'ram'       => 'audio/x-pn-realaudio',
+        'ra'        => 'audio/x-pn-realaudio',
+        'rm'        => 'application/vnd.rn-realmedia',
+        'wav'       => 'audio/x-wav',
+        'pdb'       => 'chemical/x-pdb',
+        'xyz'       => 'chemical/x-xyz',
+        'bmp'       => 'image/bmp',
+        'cgm'       => 'image/cgm',
+        'gif'       => 'image/gif',
+        'ief'       => 'image/ief',
+        'jpeg'      => 'image/jpeg',
+        'jpg'       => 'image/jpeg',
+        'jpe'       => 'image/jpeg',
+        'png'       => 'image/png',
+        'svg'       => 'image/svg+xml',
+        'tiff'      => 'image/tiff',
+        'tif'       => 'image/tiff',
+        'djvu'      => 'image/vnd.djvu',
+        'djv'       => 'image/vnd.djvu',
+        'wbmp'      => 'image/vnd.wap.wbmp',
+        'ras'       => 'image/x-cmu-raster',
+        'ico'       => 'image/x-icon',
+        'pnm'       => 'image/x-portable-anymap',
+        'pbm'       => 'image/x-portable-bitmap',
+        'pgm'       => 'image/x-portable-graymap',
+        'ppm'       => 'image/x-portable-pixmap',
+        'rgb'       => 'image/x-rgb',
+        'xbm'       => 'image/x-xbitmap',
+        'psd'       => 'image/x-photoshop',
+        'xpm'       => 'image/x-xpixmap',
+        'xwd'       => 'image/x-xwindowdump',
+        'eml'       => 'message/rfc822',
+        'igs'       => 'model/iges',
+        'iges'      => 'model/iges',
+        'msh'       => 'model/mesh',
+        'mesh'      => 'model/mesh',
+        'silo'      => 'model/mesh',
+        'wrl'       => 'model/vrml',
+        'vrml'      => 'model/vrml',
+        'ics'       => 'text/calendar',
+        'ifb'       => 'text/calendar',
+        'css'       => 'text/css',
+        'csv'       => 'text/csv',
+        'html'      => 'text/html',
+        'htm'       => 'text/html',
+        'txt'       => 'text/plain',
+        'asc'       => 'text/plain',
+        'rtx'       => 'text/richtext',
+        'rtf'       => 'text/rtf',
+        'sgml'      => 'text/sgml',
+        'sgm'       => 'text/sgml',
+        'tsv'       => 'text/tab-separated-values',
+        'wml'       => 'text/vnd.wap.wml',
+        'wmls'      => 'text/vnd.wap.wmlscript',
+        'etx'       => 'text/x-setext',
+        'mpeg'      => 'video/mpeg',
+        'mpg'       => 'video/mpeg',
+        'mpe'       => 'video/mpeg',
+        'qt'        => 'video/quicktime',
+        'mov'       => 'video/quicktime',
+        'mxu'       => 'video/vnd.mpegurl',
+        'm4u'       => 'video/vnd.mpegurl',
+        'flv'       => 'video/x-flv',
+        'asf'       => 'video/x-ms-asf',
+        'asx'       => 'video/x-ms-asf',
+        'wmv'       => 'video/x-ms-wmv',
+        'wm'        => 'video/x-ms-wm',
+        'wmx'       => 'video/x-ms-wmx',
+        'avi'       => 'video/x-msvideo',
+        'ogv'       => 'video/ogg',
+        'movie'     => 'video/x-sgi-movie',
+        'ice'       => 'x-conference/x-cooltalk',
+    );
+
+
+
+    /**
+     * Autodetect a file's MIME-type.
+     *
+     * @param string $file Path to the file to get the type of
+     *
+     * @return string $file's MIME-type on success, PEAR_Error otherwise
+     */
+    function getMIMEType($file)
+    {
+        $extension = substr(strrchr($file, '.'), 1);
+        if ($extension === false) {
+            return PEAR::raiseError("File has no extension.");
+        }
+
+        if (!isset($this->extensionToType[$extension])) {
+            return PEAR::raiseError("Sorry, couldn't determine file type.");
+        }
+
+        return $this->extensionToType[$extension];
+    }
+
+
+
+    /**
+     * Return default MIME-type for the specified extension.
+     *
+     * @param string $type MIME-type
+     *
+     * @return string A file extension without leading period.
+     */
+    function getExtension($type)
+    {
+        require_once 'MIME/Type.php';
+        // Strip parameters and comments.
+        $type = MIME_Type::getMedia($type) . '/' . MIME_Type::getSubType($type);
+
+        $extension = array_search($type, $this->extensionToType);
+        if ($extension === false) {
+            return PEAR::raiseError("Sorry, couldn't determine extension.");
+        }
+        return $extension;
+    }
+
+}
+
+?>
\ No newline at end of file
diff --git a/extlib/MIME/Type/Parameter.php b/extlib/MIME/Type/Parameter.php
new file mode 100644 (file)
index 0000000..399d3dd
--- /dev/null
@@ -0,0 +1,163 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Ian Eure <ieure@php.net>                                    |
+// +----------------------------------------------------------------------+
+//
+// $Id: Parameter.php,v 1.1 2007/03/25 10:10:21 cweiske Exp $
+
+/**
+ * Class for working with MIME type parameters
+ *
+ * @version 1.2.0
+ * @package MIME_Type
+ * @author Ian Eure <ieure@php.net>
+ */
+class MIME_Type_Parameter {
+    /**
+     * Parameter name
+     *
+     * @var string
+     */
+    var $name;
+
+    /**
+     * Parameter value
+     *
+     * @var string
+     */
+    var $value;
+
+    /**
+     * Parameter comment
+     *
+     * @var string
+     */
+    var $comment;
+
+
+    /**
+     * Constructor.
+     *
+     * @param  string $param MIME parameter to parse, if set.
+     * @return void
+     */
+    function MIME_Type_Parameter($param = false)
+    {
+        if ($param) {
+            $this->parse($param);
+        }
+    }
+
+
+    /**
+     * Parse a MIME type parameter and set object fields
+     *
+     * @param  string $param MIME type parameter to parse
+     * @return void
+     */
+    function parse($param)
+    {
+        $comment = '';
+        $param   = MIME_Type::stripComments($param, $comment);
+        $this->name    = $this->getAttribute($param);
+        $this->value   = $this->getValue($param);
+        $this->comment = $comment;
+    }
+
+
+    /**
+     * Get a parameter attribute (e.g. name)
+     *
+     * @param  string MIME type parameter
+     * @return string Attribute name
+     * @static
+     */
+    function getAttribute($param)
+    {
+        $tmp = explode('=', $param);
+        return trim($tmp[0]);
+    }
+
+
+    /**
+     * Get a parameter value
+     *
+     * @param  string $param MIME type parameter
+     * @return string Value
+     * @static
+     */
+    function getValue($param)
+    {
+        $tmp = explode('=', $param, 2);
+        $value = $tmp[1];
+        $value = trim($value);
+        if ($value[0] == '"' && $value[strlen($value)-1] == '"') {
+            $value = substr($value, 1, -1);
+        }
+        $value = str_replace('\\"', '"', $value);
+        return $value;
+    }
+
+
+    /**
+     * Get a parameter comment
+     *
+     * @param  string $param MIME type parameter
+     * @return string Parameter comment
+     * @see getComment()
+     * @static
+     */
+    function getComment($param)
+    {
+        $cs = strpos($param, '(');
+        $comment = substr($param, $cs);
+        return trim($comment, '() ');
+    }
+
+
+    /**
+     * Does this parameter have a comment?
+     *
+     * @param  string  $param MIME type parameter
+     * @return boolean true if $param has a comment, false otherwise
+     * @static
+     */
+    function hasComment($param)
+    {
+        if (strstr($param, '(')) {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Get a string representation of this parameter
+     *
+     * This function performs the oppsite of parse()
+     *
+     * @return string String representation of parameter
+     */
+    function get()
+    {
+        $val = $this->name . '="' . str_replace('"', '\\"', $this->value) . '"';
+        if ($this->comment) {
+            $val .= ' (' . $this->comment . ')';
+        }
+        return $val;
+    }
+}
+?>
\ No newline at end of file
index cb8b5a6609a9c43b3804e4e90d44d5bddafbb249..936b847abe7bedc9ae44cf68cc75a48b1b94bf5c 100644 (file)
@@ -157,7 +157,7 @@ $.fn.ajaxSubmit = function(options) {
     function fileUpload() {\r
         var form = $form[0];\r
         \r
-        if ($(':input[@name=submit]', form).length) {\r
+        if ($(':input[name=submit]', form).length) {\r
             alert('Error: Form elements must not be named "submit".');\r
             return;\r
         }\r
@@ -570,7 +570,7 @@ $.fn.clearForm = function() {
 $.fn.clearFields = $.fn.clearInputs = function() {\r
     return this.each(function() {\r
         var t = this.type, tag = this.tagName.toLowerCase();\r
-        if (t == 'text' || t == 'password' || tag == 'textarea')\r
+        if (t == 'file' || t == 'text' || t == 'password' || tag == 'textarea')\r
             this.value = '';\r
         else if (t == 'checkbox' || t == 'radio')\r
             this.checked = false;\r
index c710ae839277c053ef6d889b661469dee57feed2..acf44a17c78d9f27fb8cadb76b290652b7dbef9c 100644 (file)
  */
 
 $(document).ready(function(){
+    $('input#notice_data-attach').toggle();
+    $('label[for=notice_data-attach]').text('Upload a file as an attachment?');
+    $('label[for=notice_data-attach]').click(function () {
+        if ('Upload a file as an attachment?' == $(this).text()) {
+            $(this).text('Upload: ');
+            $('input#notice_data-attach').slideDown('fast');
+        } else {
+            $('input#notice_data-attach').slideUp('fast', function() {$('label[for=notice_data-attach]').text('Upload a file as an attachment?');});
+        }
+    });
+
+    $('a.attachment').click(function() {$().jOverlay({url: $('address .url')[0].href+'/attachment/' + ($(this).attr('id').substring('attachment'.length + 1)) + '/ajax'}); return false; });
+    $("a.thumbnail").hover(
+        function() {
+            var anchor = $(this);
+            $("a.thumbnail").children('img').remove();
+
+            setTimeout(function() {
+                anchor.closest(".entry-title").addClass('ov');
+                $.get($('address .url')[0].href+'/attachment/' + (anchor.attr('id').substring('attachment'.length + 1)) + '/thumbnail', null, function(data) {
+                    anchor.append(data);
+                });
+            }, 250);
+
+            setTimeout(function() {
+                anchor.children('img').remove();
+                anchor.closest(".entry-title").removeClass('ov');
+            }, 3000);
+        },
+        function() {
+            $(this).children('img').remove();
+            $(this).closest(".entry-title").removeClass('ov');
+        }
+    );
+
        // count character on keyup
        function counter(event){
                var maxLength = 140;
@@ -203,6 +238,7 @@ $(document).ready(function(){
                                                          }
                                                                                                        }
                                                                                                        $("#notice_data-text").val("");
+                                                                                               $("#notice_data-attach").val("");
                                                     counter();
                                                                                                }
                                                                                                $("#form_notice").removeClass("processing");
index 559962accab60e4614f35773001a22182c5fb734..61749dca578a1cf0688bb2290b26217990b37dd1 100644 (file)
@@ -80,13 +80,14 @@ class AttachmentList extends Widget
 
     function show()
     {
+        $atts = new File;
+        $att = $atts->getAttachments($this->notice->id);
+        if (empty($att)) return 0;
         $this->out->elementStart('dl', array('id' =>'attachments'));
         $this->out->element('dt', null, _('Attachments'));
         $this->out->elementStart('dd');
         $this->out->elementStart('ol', array('class' => 'attachments'));
 
-        $atts = new File;
-        $att = $atts->getAttachments($this->notice->id);
         foreach ($att as $n=>$attachment) {
             $item = $this->newListItem($attachment);
             $item->show();
index 4a98741e8e773e8377453a46ffa55eb7c2311b49..838f52f9d54ab3448b02674a830876c3a5bea7c2 100644 (file)
@@ -163,6 +163,40 @@ $config =
         array('run' => 'web',
               'frequency' => 10000,
               'reporturl' => 'http://laconi.ca/stats/report'),
+        'attachments' =>
+        array('supported' => array('image/png',
+            'image/jpeg',
+            'image/gif',
+            'image/svg+xml',
+            'audio/mpeg',
+            'application/ogg',
+            'application/pdf',
+            'application/vnd.oasis.opendocument.text',
+            'application/vnd.oasis.opendocument.text-template',
+            'application/vnd.oasis.opendocument.graphics',
+            'application/vnd.oasis.opendocument.graphics-template',
+            'application/vnd.oasis.opendocument.presentation',
+            'application/vnd.oasis.opendocument.presentation-template',
+            'application/vnd.oasis.opendocument.spreadsheet',
+            'application/vnd.oasis.opendocument.spreadsheet-template',
+            'application/vnd.oasis.opendocument.chart',
+            'application/vnd.oasis.opendocument.chart-template',
+            'application/vnd.oasis.opendocument.image',
+            'application/vnd.oasis.opendocument.image-template',
+            'application/vnd.oasis.opendocument.formula',
+            'application/vnd.oasis.opendocument.formula-template',
+            'application/vnd.oasis.opendocument.text-master',
+            'application/vnd.oasis.opendocument.text-web',
+            'application/zip',
+            'text/plain',
+            'video/mpeg',
+            'video/mp4',
+            'video/quicktime',
+            'video/mpeg'),
+        'file_quota' => 5000000,
+        'user_quota' => 50000000,
+        'monthly_quota' => 15000000,
+        ),
         );
 
 $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');
@@ -224,19 +258,19 @@ if ($_db_name != 'laconica' && !array_key_exists('ini_'.$_db_name, $config['db']
 
 // XXX: how many of these could be auto-loaded on use?
 
-require_once('Validate.php');
-require_once('markdown.php');
+require_once 'Validate.php';
+require_once 'markdown.php';
 
-require_once(INSTALLDIR.'/lib/util.php');
-require_once(INSTALLDIR.'/lib/action.php');
-require_once(INSTALLDIR.'/lib/theme.php');
-require_once(INSTALLDIR.'/lib/mail.php');
-require_once(INSTALLDIR.'/lib/subs.php');
-require_once(INSTALLDIR.'/lib/Shorturl_api.php');
-require_once(INSTALLDIR.'/lib/twitter.php');
+require_once INSTALLDIR.'/lib/util.php';
+require_once INSTALLDIR.'/lib/action.php';
+require_once INSTALLDIR.'/lib/theme.php';
+require_once INSTALLDIR.'/lib/mail.php';
+require_once INSTALLDIR.'/lib/subs.php';
+require_once INSTALLDIR.'/lib/Shorturl_api.php';
+require_once INSTALLDIR.'/lib/twitter.php';
 
-require_once(INSTALLDIR.'/lib/clientexception.php');
-require_once(INSTALLDIR.'/lib/serverexception.php');
+require_once INSTALLDIR.'/lib/clientexception.php';
+require_once INSTALLDIR.'/lib/serverexception.php';
 
 // XXX: other formats here
 
index 5317df4715946e9638a1b32905f603f3652ca299..f872aef0b55066196d54c61456678664736c1d93 100644 (file)
@@ -52,6 +52,8 @@ require_once INSTALLDIR.'/lib/widget.php';
 
 class Form extends Widget
 {
+    var $enctype = null;
+
     /**
      * Show the form
      *
@@ -63,11 +65,15 @@ class Form extends Widget
 
     function show()
     {
-        $this->out->elementStart('form',
-                                 array('id' => $this->id(),
-                                       'class' => $this->formClass(),
-                                       'method' => 'post',
-                                       'action' => $this->action()));
+        $attributes = array('id' => $this->id(),
+            'class' => $this->formClass(),
+            'method' => 'post',
+            'action' => $this->action());
+
+        if (!empty($this->enctype)) {
+            $attributes['enctype'] = $this->enctype;
+        }
+        $this->out->elementStart('form', $attributes);
         $this->out->elementStart('fieldset');
         $this->formLegend();
         $this->sessionToken();
index 606b5d028e7d73826cdd3d0d4a5f757575af2bbf..707768cd5948afc1a003a4534254151b7a525461 100644 (file)
@@ -89,7 +89,8 @@ class NoticeForm extends Form
         } else {
             $this->user = common_current_user();
         }
-        
+
+        $this->enctype = 'multipart/form-data';
     }
 
     /**
@@ -136,18 +137,25 @@ class NoticeForm extends Form
     {
         $this->out->element('label', array('for' => 'notice_data-text'),
                             sprintf(_('What\'s up, %s?'), $this->user->nickname));
+        $this->out->elementStart('span', array('style' => 'float: right; margin-top: 2em;'));
+//        $this->out->element('a', array('href' => '#attach'), ' [ATTACH]');
+        $this->out->elementEnd('span');
         // XXX: vary by defined max size
         $this->out->element('textarea', array('id' => 'notice_data-text',
                                               'cols' => 35,
                                               'rows' => 4,
                                               'name' => 'status_textarea'),
                             ($this->content) ? $this->content : '');
-
         $this->out->elementStart('dl', 'form_note');
         $this->out->element('dt', null, _('Available characters'));
         $this->out->element('dd', array('id' => 'notice_text-count'),
                             '140');
         $this->out->elementEnd('dl');
+        $this->out->element('br', array('style' => 'clear:both'));
+//        $this->out->elementStart('a', array('href' => '#'));
+        $this->out->element('label', array('for' => 'notice_data-attach'), _('Upload: '));
+//        $this->out->elementEnd('a');
+        $this->out->element('input', array('id' => 'notice_data-attach', 'type' => 'file', 'name' => 'attach'));
 
         if ($this->action) {
             $this->out->hidden('notice_return-to', $this->action, 'returnto');
index 0e80a9778ef6568a288fa444b4058af9aa46a02f..5513e317e0406fff6b8396444eaf892778b298be 100644 (file)
@@ -206,6 +206,7 @@ class NoticeListItem extends Widget
         return 'shownotice' !== $this->out->args['action'];
     }
 
+/*
     function attachmentCount($discriminant = true) {
         $file_oembed = new File_oembed;
         $query = "select count(*) as c from file_oembed join file_to_post on file_oembed.file_id = file_to_post.file_id where post_id=" . $this->notice->id;
@@ -213,11 +214,16 @@ class NoticeListItem extends Widget
         $file_oembed->fetch();
         return intval($file_oembed->c);
     }
+*/
+
+    function showWithAttachment() {
+    }
 
     function showNoticeInfo()
     {
         $this->out->elementStart('div', 'entry-content');
         $this->showNoticeLink();
+//        $this->showWithAttachment();
         $this->showNoticeSource();
         $this->showContext();
         $this->out->elementEnd('div');
@@ -388,6 +394,11 @@ class NoticeListItem extends Widget
         $this->out->element('abbr', array('class' => 'published',
                                           'title' => $dt),
                             common_date_string($this->notice->created));
+
+        $f2p = File_to_post::staticGet('post_id', $this->notice->id);
+        if (!empty($f2p)) {
+            $this->out->text(_(' (with attachments) '));
+        }
         $this->out->elementEnd('a');
         $this->out->elementEnd('dd');
         $this->out->elementEnd('dl');
index fc119821b9260a30cc4ccdc64c04c6d9369383f3..456d1793e3245bf333343aae7ee4213245420fda 100644 (file)
@@ -164,6 +164,10 @@ class Router
                     array('action' => 'newnotice'),
                     array('replyto' => '[A-Za-z0-9_-]+'));
 
+        $m->connect('notice/:notice/file', 
+            array('action' => 'file'), 
+            array('notice' => '[0-9]+'));
+        
         $m->connect('notice/:notice',
                     array('action' => 'shownotice'),
                     array('notice' => '[0-9]+'));
index d56f44f7b45c39a948f8cd4ed488eb081ff783a1..ab12723098b1b706354257b9f9b4ceba925f15ec 100644 (file)
@@ -499,6 +499,11 @@ function common_linkify($url) {
 
 // if this URL is an attachment, then we set class='attachment' and id='attahcment-ID'
 // where ID is the id of the attachment for the given URL.
+//
+// we need a better test telling what can be shown as an attachment
+// we're currently picking up oembeds only.
+// I think the best option is another file_view table in the db
+// and associated dbobject.
     $query = "select file_oembed.file_id as file_id from file join file_oembed on file.id = file_oembed.file_id where file.url='$longurl'";
     $file = new File;
     $file->query($query);