code:
<?php
/**
 * GNU Social
 * Copyright (C) 2011, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @copyright 2011 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if(!defined('STATUSNET')){
    exit(1);
}

class NewPhotoForm extends Form
{
    function id()
    {
        return "form_new_photo";
    }

    function action()
    {
        return common_local_url('newphoto');
    }

    function formClass()
    {
        return 'form_settings ajax-notice';
    }

    function formData()
    {
        $this->out->elementStart('fieldset', array('id' => 'new_photo_data'));
        $this->out->elementStart('ul', 'form_data');

        $this->li();
        $this->out->input('title', _('Title'), null, _('Photo title (optional).'));
        $this->unli();

        $this->li();
        $this->out->element('input', array('name' => 'photo_upload',
                                           'type' => 'file',
                                           'id' => 'photo_upload'));
        $this->unli();

        $this->li();
        $this->textarea('description', _('Description'), null, _('Description of the photo (optional).'));
        $this->unli();

        $this->out->elementEnd('ul');

        $toWidget = new ToSelector($this->out,
                                   common_current_user(),
                                   null);
        $toWidget->show();

        $this->out->elementEnd('fieldset');
    }

    function formActions()
    {
        $this->out->submit('photo-submit', _m('BUTTON', 'Save'));
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @author    Max Shinn    <trombonechamp@gmail.com>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

/* Photo sharing plugin */

if (!defined('STATUSNET')) {
    exit(1);
}

include_once $dir . '/lib/photolib.php';

class GNUsocialPhotosPlugin extends Plugin
{
    function onCheckSchema()
    {
        $schema = Schema::get();
        $schema->ensureTable('GNUsocialPhoto', GNUsocialPhoto::schemaDef());
        $schema->ensureTable('GNUsocialPhotoAlbum', GNUsocialPhotoAlbum::schemaDef());
    }

    function onRouterInitialized($m)
    {
        $m->connect(':nickname/photos', array('action' => 'photos'));
        $m->connect(':nickname/photos/:albumid', array('action' => 'photos'));
        $m->connect('main/uploadphoto', array('action' => 'photoupload'));
        $m->connect('photo/:photoid', array('action' => 'photo'));
        $m->connect('editphoto/:photoid', array('action' => 'editphoto'));
        return true;
    }

    function onStartNoticeDistribute($notice)
    {
        common_log(LOG_INFO, "event: StartNoticeDistribute");
        if (GNUsocialPhotoTemp::$tmp) {
            GNUsocialPhotoTemp::$tmp->notice_id = $notice->id;
            $photo_id = GNUsocialPhotoTemp::$tmp->insert();
            if (!$photo_id) {
                common_log_db_error($photo, 'INSERT', __FILE__);
                throw new ServerException(_m('Problem saving photo.'));
            }
        }
        return true;
    }

    function onEndNoticeAsActivity(Notice $stored, Activity $act, Profile $scoped=null)
    {
        common_log(LOG_INFO, 'photo plugin: EndNoticeAsActivity');
        $photo = GNUsocialPhoto::getKV('notice_id', $stored->id);
        if(!$photo) {
            common_log(LOG_INFO, 'not a photo.');
            return true;
        }

        $act->objects[0]->type = ActivityObject::PHOTO;
        $act->objects[0]->thumbnail = $photo->thumb_uri;
        $act->objects[0]->largerImage = $photo->uri;
        return false;
    }

    function onStartHandleFeedEntry($activity)
    {
        common_log(LOG_INFO, 'photo plugin: onEndAtomPubNewActivity');
        $oprofile = Ostatus_profile::ensureActorProfile($activity);
        foreach ($activity->objects as $object) {
            if($object->type == ActivityObject::PHOTO) {
                $uri = $object->largerImage;
                $thumb_uri = $object->thumbnail;
                $profile_id = $oprofile->profile_id;
                $source = 'unknown'; // TODO: put something better here.

                common_log(LOG_INFO, 'uri : ' .  $uri);
                common_log(LOG_INFO, 'thumb_uri : ' . $thumb_uri);

                // It's possible this is validated elsewhere, but I'm not sure and
                // would rather be safe.
                $uri = filter_var($uri, FILTER_SANITIZE_URL);
                $thumb_uri = filter_var($thumb_uri, FILTER_SANITIZE_URL);
                $uri = filter_var($uri, FILTER_VALIDATE_URL);
                $thumb_uri = filter_var($thumb_uri, FILTER_VALIDATE_URL);

                if(empty($thumb_uri)) {
                    // We need a thumbnail, so if we aren't given one, use the actual picture for now.
                    $thumb_uri = $uri;
                }

                if (!empty($uri) && !empty($thumb_uri)) {
                    GNUsocialPhoto::saveNew($profile_id, $thumb_uri, $uri, $source, false);
                } else {
                    common_log(LOG_INFO, 'bad URI for photo');
                }
                return false;
            }
        }
        return true;
    }

    function onStartShowNoticeItem($action)
    {
        $photo = GNUsocialPhoto::getKV('notice_id', $action->notice->id);
        if($photo) {
            $action->out->elementStart('div', 'entry-title');
            $action->showAuthor();
            $action->out->elementStart('a', array('href' => $photo->getPageLink()));
            $action->out->element('img', array('src' => $photo->thumb_uri,
                                    'width' => 256, 'height' => 192));
            $action->out->elementEnd('a');
            $action->out->elementEnd('div');
            $action->showNoticeInfo();
            $action->showNoticeOptions();
            return false;
        }
        return true;
    }

    /*    function onEndShowNoticeFormData($action)
    {
        $link = "/main/uploadphoto";
        $action->out->element('label', array('for' => 'photofile'),_('Attach'));
        $action->out->element('input', array('id' => 'photofile',
                                     'type' => 'file',
                                     'name' => 'photofile',
                                     'title' => _('Upload a photo')));
    }
    */
    function onEndPersonalGroupNav(Menu $nav, Profile $target, Profile $scoped=null)
    {

        $nav->out->menuItem(common_local_url('photos',
                           array('nickname' => $nav->action->trimmed('nickname'))), _('Photos'),
                           _('Photo gallery'), $nav->action->trimmed('action') == 'photos', 'nav_photos');
    }

    function onEndShowStyles($action)
    {
        $action->cssLink('/plugins/GNUsocialPhotos/res/style.css');
    }

    function onEndShowScripts($action)
    {
        $action->script('plugins/GNUsocialPhotos/res/gnusocialphotos.js');
    }
}

license: mit


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @author    Sean Corbett <sean@gnu.org>
 * @author    Max Shinn    <trombonechamp@gmail.com>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

class EditphotoAction extends Action
{
    var $user = null;

    function prepare($args)
    {
        parent::prepare($args);
        $args = $this->returnToArgs();
        $this->user = common_current_user();
        $this->photoid = $args[1]['photoid'];
        $this->photo = GNUsocialPhoto::getKV('id', $this->photoid);
        return true;
    }

    function handle($args)
    {
        parent::handle($args);
        if($_SERVER['REQUEST_METHOD'] == 'POST') {
            $this->handlePost();
        }
        $this->showPage();
    }

    function title()
    {
        if ($this->photo->title)
            return _m('Edit photo - ' . $this->photo->title);
        else
            return _m('Edit photo');
    }

    function showContent()
    {

        if ($this->photo->album_id == 0) {
            $this->element('p', array(), _('This photo does not exist or was deleted.'));
            return;
        }

        if ($this->user->profile_id != $this->photo->profile_id) {
            $this->element('p', array(), _('You are not authorized to edit this photo.'));
            return;
        }

        //showForm() data
        if(!empty($this->msg)) {
            $class = ($this->success) ? 'success' : 'error';
            $this->element('p', array('class' => $class), $this->msg);
        }

        $this->element('img', array('src' => $this->photo->uri));
        $this->elementStart('form', array('method' => 'post',
                                          'action' => '/editphoto/' . $this->photo->id));
        $this->elementStart('ul', 'form_data');
        $this->elementStart('li');
        $this->input('phototitle', _("Title"), $this->photo->title, _("The title of the photo. (Optional)"));
        $this->elementEnd('li');
        $this->elementStart('li');
        $this->textarea('photo_description', _("Description"), $this->photo->photo_description, _("A description of the photo. (Optional)"));
        $this->elementEnd('li');
        $this->elementStart('li');
        $this->dropdown('album', _("Album"), $this->albumList(), _("The album in which to place this photo"), false, $this->photo->album_id);
        $this->elementEnd('li');
        $this->elementEnd('ul');
        $this->submit('update', _('Update'));
        $this->elementEnd('form');
        $this->element('br');
        $this->elementStart('form', array('method' => 'post',
                                          'action' => '/editphoto/' . $this->photo->id));
        $this->element('input', array('type' => 'submit',
                                      'name' => 'delete',
                                      'value' => _('Delete Photo'),
                                      'id' => 'delete',
                                      'class' => 'submit',
                                      'onclick' => 'return confirm(\'Are you sure you would like to delete this photo?\')'));
        $this->elementEnd('form');

    }

    function handlePost()
    {

        common_log(LOG_INFO, 'handlPost()!');

        if ($this->photo->album_id == 0) {
            $this->element('p', array(), _('This photo does not exist or was deleted.'));
            return;
        }

        if ($this->user->profile_id != $this->photo->profile_id) {
            $this->element('p', array(), _('You are not authorized to edit this photo.'));
            return;
        }

        if ($this->arg('update')) {
            $this->updatePhoto();
        }
        if ($this->arg('delete')) {
            $this->deletePhoto();
        }
    }

    function showForm($msg, $success=false)
    {
        $this->msg = $msg;
        $this->success = $success;

//        $this->showPage();
    }

    function albumList()
    {
        $cur = common_current_user();
        $album = new GNUsocialPhotoAlbum();
        $album->user_id = $cur->id;

        $albumlist = array();
        if (!$album->find()) {
            GNUsocialPhotoAlbum::newAlbum($cur->id, 'Default');
        }
        while ($album->fetch()) {
            $albumlist[$album->album_id] = $album->album_name;
        }
        return $albumlist;
    }

    function updatePhoto()
    {
        $cur = common_current_user();
        $this->photo->title = $this->trimmed('phototitle');
        $this->photo->photo_description = $this->trimmed('photo_description');

        $profile_id = $cur->id;

        $album = GNUsocialPhotoAlbum::getKV('album_id', $this->trimmed('album'));
        if ($album->profile_id != $profile_id) {
            $this->showForm(_('Error: This is not your album!'));
            return;
        }
        $this->photo->album_id = $album->album_id;
        if ($this->photo->validate())
            $this->photo->update();
        else {
            $this->showForm(_('Error: The photo data is not valid.'));
            return;
        }
        common_redirect('/photo/' . $this->photo->id, '303');
        // common_redirect exits
    }

    function deletePhoto()
    {
        //For redirection
        $oldalbum = $this->album_id;

        $notice = Notice::getKV('id', $this->photo->notice_id);

        $this->photo->delete();

        if (Event::handle('StartDeleteOwnNotice', array($this->user, $notice))) {
            $notice->delete();
            Event::handle('EndDeleteOwnNotice', array($this->user, $notice));
        }
       $this->showForm(_('Success!'));
        common_redirect('/' . $this->user->nickname . '/photos/' . $oldalbum, '303');
    }

}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @author    Sean Corbett <sean@gnu.org>
 * @author    Max Shinn    <trombonechamp@gmail.com>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('GNUSOCIAL')) { exit(1); }

class PhotoAction extends ManagedAction
{
    var $user = null;
    var $conv = null;   // Conversation dataobject

    protected function prepare(array $args=array())
    {
        parent::prepare($args);

        $args = $this->returnToArgs();
        $this->photoid = $args[1]['photoid'];
        $this->photo = GNUsocialPhoto::getKV('id', $this->photoid);
        $this->notice = Notice::getKV('id', $this->photo->notice_id);

        $this->user = Profile::getKV('id', $this->notice->profile_id);
        $this->conv = $this->notice->getConversation();

        return true;
    }

    function title()
    {
        if (empty($this->user)) {
            return _m('No such user.');
        } else if (empty($this->photo)) {
            return _m('No such photo.');
        } else if (!empty($this->photo->title)) {
            return $this->photo->title;
        } else {
            return sprintf(_m("%s's Photo."), $this->user->nickname);
        }
    }

    function showLocalNav()
    {
        $nav = new GNUsocialPhotoNav($this, $this->user->nickname);
        $nav->show();
    }

    function showContent()
    {
        if(empty($this->user)) {
            return;
        }

        $this->elementStart('a', array('href' => $this->photo->uri));
        $this->element('img', array('src' => $this->photo->uri));
        $this->elementEnd('a');

        //Image "toolbar"
        $cur = common_current_user();
        if($this->photo->profile_id == $cur->profile_id) {
            $this->elementStart('div', array('id' => 'image_toolbar'));
            $this->element('a', array('href' => '/editphoto/' . $this->photo->id), 'Edit');
            $this->elementEnd('div');
        }

        $this->element('p', array('class' => 'photodescription'), $this->photo->photo_description);
        //This is a hack to hide the top-level comment
        $this->element('style', array(), "#notice-{$this->photo->notice_id} div { display: none } #notice-{$this->photo->notice_id} ol li div { display: inline }");

        if (Event::handle('StartShowConversation', array($this, $this->conv, $this->scoped))) {
            $notices = $this->conv->getNotices();
            $nl = new FullThreadedNoticeList($notices, $this, $this->scoped);
            $cnt = $nl->show();
        }
        Event::handle('EndShowConversation', array($this, $this->conv, $this->scoped));
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @author    Sean Corbett <sean@gnu.org>
 * @author    Max Shinn    <trombonechamp@gmail.com>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

require_once INSTALLDIR.'/lib/personalgroupnav.php';

class PhotosAction extends Action
{
    var $user = null;

    function prepare($args)
    {
        parent::prepare($args);

        $args = $this->returnToArgs();
        $username = $args[1]['nickname'];
        $this->albumid = $args[1]['albumid'];
        if (common_valid_profile_tag($username) == 0) {
            $this->user = null;
        } else {
            $this->user = Profile::getKV('nickname', $username);
        }
        return true;
    }

    function handle($args)
    {
        parent::handle($args);
        $this->showPage();
    }

    function title()
    {
        if (empty($this->user)) {
            return _m('No such user.');
        } else {
            return sprintf(_m("%s's Photos."), $this->user->nickname);
        }
    }

    function showLocalNav()
    {
        $nav = new PersonalGroupNav($this);
        $nav->show();
    }

    function showResizeImagesBox()
    {
        $this->elementStart('select', array('onchange' => 'return scalePhotosToSize(this.value)'));
        $this->element('option', array('value' => ''), "");
        $this->element('option', array('value' => '60'), _("Thumbnail"));
        $this->element('option', array('value' => '120'), _("Medium"));
        $this->element('option', array('value' => '400'), _("Normal"));
        $this->elementEnd('select');
    }

    function showAlbums()
    {
        $album = new GNUsocialPhotoAlbum();
        $album->profile_id = $this->user->id;

        $albums = array();
        if (!$album->find()) {
            GNUsocialPhotoAlbum::newAlbum($this->user->id, 'Default');
        }

        $this->elementStart('div', array('class' => 'galleryheader'));
        //$this->element('a', array('href' => '#',
        //                          'onclick' => 'return increasePhotoSize()'), '+');
        //$this->raw(' | ');
        //$this->element('a', array('href' => '#',
        //                          'onclick' => 'return decreasePhotoSize()'), '-');

        $this->showResizeImagesBox();
        $this->elementEnd('div');

        while ($album->fetch()) {
            $this->elementStart('div', array('class' => 'photocontainer'));
            $this->elementStart('a', array('href' => $album->getPageLink()));
            $this->element('img', array('src' => $album->getThumbUri(),
                                        'class' => 'albumingallery'));
            $this->elementEnd('a');
            $this->element('h3', array(), $album->album_name);
            $this->elementEnd('div');
        }

    }

    function showAlbum($album_id)
    {
        $album = GNUsocialPhotoAlbum::getKV('album_id', $album_id);
        if (!$album) {
            return;
        }

        $page = $_GET['pageid'];
        if (!filter_var($page, FILTER_VALIDATE_INT)){
            $page = 1;
        }

        $photos = GNUsocialPhoto::getGalleryPage($page, $album->album_id, 9);
        $this->elementStart('div', array('class' => 'galleryheader'));
        if ($page > 1) {
            $this->element('a', array('href' => $album->getPageLink() . '?pageid=' . ($page-1)), 'Previous page');
            $this->raw(' | ');
        }
        if (GNUsocialPhoto::getGalleryPage($page+1, $album->album_id, 9)) {
            $this->element('a', array('href' => $album->getPageLink() . '?pageid=' . ($page+1) ), 'Next page');
            $this->raw(' | ');
        }

        //$this->element('a', array('href' => '#',
        //                          'onclick' => 'return increasePhotoSize()'), '+');
        //$this->raw(' | ');
        //$this->element('a', array('href' => '#',
        //                          'onclick' => 'return decreasePhotoSize()'), '-');
        //$this->raw(' | ');

        $this->showResizeImagesBox();
        $this->elementEnd('div');

        foreach ($photos as $photo) {
            $this->elementStart('a', array('href' => $photo->getPageLink()));
            $this->elementStart('div', array('class' => 'photocontainer'));
            $this->element('img', array('src' => $photo->thumb_uri,
                                        'class' => 'photoingallery'));
            $this->element('div', array('class' => 'phototitle'), $photo->title);
            $this->elementEnd('div');
            $this->elementEnd('a');
        }

    }

    function showContent()
    {
        if (!empty($this->albumid))
            $this->showAlbum($this->albumid);
        else
            $this->showAlbums();
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @author    Sean Corbett <sean@gnu.org>
 * @author    Max Shinn    <trombonechamp@gmail.com>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

class PhotouploadAction extends Action
{
    var $user = null;

    function prepare($args)
    {
        parent::prepare($args);
        $this->user = common_current_user();
        return true;
    }

    function handle($args)
    {
        parent::handle($args);
        if($_SERVER['REQUEST_METHOD'] == 'POST') {
            $this->handlePost();
        }
        $this->showPage();
    }

    function title()
    {
        return _m('Upload Photos');
    }

    function showContent()
    {
        //Upload a photo
        if(empty($this->user)) {
            $this->element('p', array(), 'You are not logged in.');
        } else {
            //showForm() data
            if(!empty($this->msg)) {
                $class = ($this->success) ? 'success' : 'error';
                $this->element('p', array('class' => $class), $this->msg);
            }

            $this->elementStart('form', array('enctype' => 'multipart/form-data',
                                              'method' => 'post',
                                              'action' => common_local_url('photoupload')));
            $this->elementStart('ul', 'form_data');
            $this->elementStart('li');
            $this->element('input', array('name' => 'photofile',
                                          'type' => 'file',
                                          'id' => 'photofile'));
            $this->elementEnd('li');
            //$this->element('br');
            $this->elementStart('li');
            $this->input('phototitle', _("Title"), null, _("The title of the photo. (Optional)"));
            $this->elementEnd('li');
            $this->elementStart('li');
            $this->textarea('photo_description', _("Description"), null, _("A description of the photo. (Optional)"));
            $this->elementEnd('li');
            $this->elementStart('li');
            $this->dropdown('album', _("Album"), $this->albumList(), _("The album in which to place this photo"), false);
            $this->elementEnd('li');
            $this->elementEnd('ul');
            $this->submit('upload', _('Upload'));
            $this->elementEnd('form');
            $this->element('br');

            //Create a new album
            $this->element('h3', array(), _("Create a new album"));
            $this->elementStart('form', array('method' => 'post',
                                              'action' =>common_local_url('photoupload')));
            $this->elementStart('ul', 'form_data');
            $this->elementStart('li');
            $this->input('album_name', _("Title"), null, _("The title of the album."));
            $this->elementEnd('li');
            $this->elementStart('li');
            $this->textarea('album_description', _("Description"), null, _("A description of the album. (Optional)"));
            $this->elementEnd('li');
            $this->elementEnd('ul');
            $this->submit('create', _('Create'));
            $this->elementEnd('form');

            //Delete an album
            $this->element('h3', array(), _("Delete an album"));
            $this->elementStart('form', array('method' => 'post',
                                              'action' =>common_local_url('photoupload')));
            $this->elementStart('ul', 'form_data');
            $this->elementStart('li');
            $this->dropdown('album', _("Album"), $this->albumList(), _("The album in which to place this photo"), false);
            $this->elementEnd('li');
            $this->elementEnd('ul');
            $this->submit('deletealbum', _('Delete'));
            $this->elementEnd('form');

        }
    }

    function handlePost()
    {

        common_log(LOG_INFO, 'handlPost()!');
        // Workaround for PHP returning empty $_POST and $_FILES when POST
        // length > post_max_size in php.ini

        if (empty($_FILES)
            && empty($_POST)
            && ($_SERVER['CONTENT_LENGTH'] > 0)
        ) {
            $msg = _('The server was unable to handle that much POST ' .
                'data (%s bytes) due to its current configuration.');

            $this->showForm(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
            return;
        }

        // CSRF protection

/*        $token = $this->trimmed('token');
        if (!$token || $token != common_session_token()) {
            $this->showForm(_('There was a problem with your session token. '.
                               'Try again, please.'));
            return;
        } */

        if ($this->arg('upload')) {
            $this->uploadPhoto();
        }
        if ($this->arg('create')) {
            $this->createAlbum();
        }
        if ($this->arg('deletealbum')) {
            $this->deleteAlbum();
        }
    }

    function showForm($msg, $success=false)
    {
        $this->msg = $msg;
        $this->success = $success;

//        $this->showPage();
    }

    function albumList()
    {
        $cur = common_current_user();
        $album = new GNUsocialPhotoAlbum();
        $album->user_id = $cur->id;

        $albumlist = array();
        if (!$album->find()) {
            GNUsocialPhotoAlbum::newAlbum($cur->id, 'Default');
        }
        while ($album->fetch()) {
            $albumlist[$album->album_id] = $album->album_name;
        }
        return $albumlist;
    }

    function uploadPhoto()
    {
        $cur = common_current_user();
        if(empty($cur)) {
            return;
        }
        try {
            $imagefile = ImageFile::fromUpload('photofile');
        } catch (Exception $e) {
            $this->showForm($e->getMessage());
            return;
        }
        if ($imagefile === null) {
            $this->showForm(_('No file uploaded.'));
            return;
        }

        $title = $this->trimmed('phototitle');
        $photo_description = $this->trimmed('photo_description');

        common_log(LOG_INFO, 'upload path : ' . $imagefile->filepath);

        $filename = $cur->nickname . '-' . common_timestamp() . sha1_file($imagefile->filepath) .  image_type_to_extension($imagefile->type);
        move_uploaded_file($imagefile->filepath, INSTALLDIR . '/file/' . $filename);
        photo_make_thumbnail($filename);
        $uri = 'http://' . common_config('site', 'server') . '/file/' . $filename;
        $thumb_uri = 'http://' . common_config('site', 'server') . '/file/thumb.' . $filename;
        $profile_id = $cur->id;

        $album = GNUsocialPhotoAlbum::getKV('album_id', $this->trimmed('album'));
        if ($album->profile_id != $profile_id) {
            $this->showForm(_('Error: This is not your album!'));
            return;
        }
        GNUsocialPhoto::saveNew($profile_id, $album->album_id, $thumb_uri, $uri, 'web', false, $title, $photo_description);
    }

    function createAlbum()
    {
        $cur = common_current_user();
        if(empty($cur)) {
            return;
        }

        $album_name = $this->trimmed('album_name');
        $album_description = $this->trimmed('album_description');

        GNUsocialPhotoAlbum::newAlbum($cur->id, $album_name, $album_description);
    }

    function deleteAlbum()
    {
        $cur = common_current_user();
        if(empty($cur)) return;

        $album_id = $this->trimmed('album');
        $album = GNUsocialPhotoAlbum::getKV('album_id', $album_id);
        if (empty($album)) {
            $this->showForm(_('This album does not exist or has been deleted.'));
            return;
        }
        //Check if the album has any photos in it before deleting
        $photos = GNUsocialPhoto::getKV('album_id', $album_id);
        if(empty($photos)) {
            $album->delete();
            $this->showForm(_('Album deleted'), true);
        }
        else {
            $this->showForm(_('Cannot delete album when there are photos in it!'), false);
        }
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

require_once INSTALLDIR . '/classes/Memcached_DataObject.php';

class GNUsocialPhoto extends Managed_DataObject
{
    public $__table = 'GNUsocialPhoto';
    public $id;         // int(11)
    public $notice_id;  // int(11)
    public $album_id;   // int(11)
    public $uri;        // varchar(255)
    public $thumb_uri;  // varchar(255)
	public $title;      // varchar(255)
	public $photo_description; // text
    public $created;           // datetime()   not_null
    public $modified;          // timestamp()   not_null default_CURRENT_TIMESTAMP

/*    function delete()
    {
        if(!unlink(INSTALLDIR . $this->thumb_uri)) {
            return false;
        }
        if(!unlink(INSTALLDIR . $this->path)) {
            return false;
        }
        return parent::delete();
    } */

    public static function schemaDef()
    {
        return array(
            'fields' => array(
                'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique ID for Photo'),
                'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'Notice ID for the related notice'),
                'album_id' => array('type' => 'int', 'not null' => true, 'description' => 'The parent album ID'),
                'uri' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'unique address for this photo'),
                'thumb_uri' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'unique address for this photo thumbnail'),
                'title' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'The Photo title'),
                'photo_description' => array('type' => 'text', 'not null' => true, 'description' => 'A description for this photo'),
                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
            ),
            'primary key' => array('id'),
            'unique keys' => array(
                'gnusocialphoto_id_key' => array('notice_id'),
                'gnusocialphoto_uri_key' => array('uri'),
            ),
            'foreign keys' => array(
                'gnusocialphoto_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
                'gnusocialphoto_album_id_fkey' => array('GNUsocialPhotoAlbum', array('album_id' => 'id')),
            ),
            'indexes' => array(
                'gnusocialphoto_title_idx' => array('title'),
            ),
        );
    }

    static function saveNew($profile_id, $album_id, $thumb_uri, $uri, $source, $insert_now, $title = null, $photo_description = null)
    {
        $photo = new GNUsocialPhoto();
        $photo->thumb_uri = $thumb_uri;
        $photo->uri = $uri;
		$photo->album_id = $album_id;
		if(!empty($title)) $photo->title = $title;
		if(!empty($photo_description)) $photo->photo_description = (string)$photo_description;

        if($insert_now) {
            $notice = Notice::saveNew($profile_id, $uri, $source);
            $photo->notice_id = $notice->id;
            $photo_id = $photo->insert();
            if (!$photo_id) {
                common_log_db_error($photo, 'INSERT', __FILE__);
                throw new ServerException(_m('Problem Saving Photo.'));
            }
        } else {
            GNUsocialPhotoTemp::$tmp = $photo;
            Notice::saveNew($profile_id, $uri, $source);
        }
    }

    function getPageLink()
    {
        return '/photo/' . $this->id;
    }

    /*
     * TODO: -Sanitize input
     * @param int page_id The desired page of the gallery to show.
     * @param int album_id The id of the album to get photos from.
     * @param int gallery_size The number of thumbnails to show per page in the gallery.
     * @return array Array of GNUsocialPhotos for this gallery page.
     */
    static function getGalleryPage($page_id, $album_id, $gallery_size)
    {
		$page_offset = ($page_id-1) * $gallery_size;
        $sql = 'SELECT * FROM GNUsocialPhoto WHERE album_id = ' . $album_id .
               ' ORDER BY notice_id LIMIT ' . $page_offset . ',' . $gallery_size;
        $photo = new GNUsocialPhoto();
        $photo->query($sql);
        $photos = array();

        while ($photo->fetch()) {
            $photos[] = clone($photo);
        }

        return $photos;
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @author    Sean Corbett <sean@gnu.org>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

require_once INSTALLDIR . '/classes/Memcached_DataObject.php';

class GNUsocialPhotoAlbum extends Managed_DataObject
{
    public $__table = 'GNUsocialPhotoAlbum';
    public $album_id;          // int(11) -- Unique identifier for the album
    public $profile_id;        // int(11) -- Profile ID for the owner of the album
    public $album_name;        // varchar(255) -- Title for this album
    public $album_description; // text -- A description of the album
    public $created;           // datetime()   not_null
    public $modified;          // timestamp()   not_null default_CURRENT_TIMESTAMP

    /* TODO: Primary key on both album_id, profile_id / foriegn key on profile_id */
    public static function schemaDef()
    {
        return array(
            'fields' => array(
                'album_id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique identifier for the album'),
                'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'Profile ID for the owner of the album'),
                'album_name' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'Title for this album'),
                'album_description' => array('type' => 'text', 'not null' => true, 'description' => 'A description for this album'),
                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
            ),
            'primary key' => array('user_id'),
            'foreign keys' => array(
                'gnusocialphotoalbum_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
            ),
            'indexes' => array(
                'gnusocialphotoalbum_album_name_idx' => array('album_name'),
            ),
        );
    }

    function getPageLink()
    {
        $profile = Profile::getKV('id', $this->profile_id);
        return '/' . $profile->nickname . '/photos/' . $this->album_id;
    }

    function getThumbUri()
    {
        $photo = GNUsocialPhoto::getKV('album_id', $this->album_id);
        if (empty($photo))
            return '/theme/default/default-avatar-profile.png'; //For now...
        return $photo->thumb_uri;
    }

    static function newAlbum($profile_id, $album_name, $album_description)
    {
        //TODO: Should use foreign key instead...
        if (!Profile::getKV('id', $profile_id)){
            //Is this a bit extreme?
            throw new ServerException(_m('No such user exists with id ' . $profile_id . ', couldn\'t create album.'));
        }

        $album = new GNUsocialPhotoAlbum();
        $album->profile_id = $profile_id;
        $album->album_name = $album_name;
        $album->album_description = $album_description;

        $album->album_id = $album->insert();
        if (!$album->album_id){
            common_log_db_error($album, 'INSERT', __FILE__);
            throw new ServerException(_m('Error creating new album.'));
        }
        common_log(LOG_INFO, 'album_id : ' . $album->album_id);
        return $album;
    }

}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @author    Max Shinn <trombonechamp@gmail.com>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if(!defined('STATUSNET')) {
    exit(1);
}

class GNUsocialPhotoNav extends Widget {
    var $action = null;

    function __construct($action = null, $nickname = null)
    {
        parent::__construct($action);
        $this->action = $action;
        $this->nickname = $nickname;
    }

    function show()
    {
        if (empty($this->nickname))
            $this->nickname = $this->action->trimmed('nickname');

        $this->out->elementStart('ul', array('class' => 'nav'));

        $this->out->menuItem(common_local_url('showstream', array('nickname' => $this->nickname)), _('Profile'));

        $this->out->menuItem(common_local_url('photos', array('nickname' => $this->nickname)),
            _('Photos'));

        $user = common_current_user();
        if (!empty($user)) {
            $this->out->menuItem(common_local_url('photoupload', array()),
                _('Upload Photos'));
        }

        $this->out->elementEnd('ul');
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

// XXX: This entire file is an ugly hack and needs to be replaced. It is essentially a global variable
// used to store information about a photo we want to insert in onStartNoticeDistribute(), which we
// can't actually pass to that function.
class GNUsocialPhotoTemp {
    public static $tmp = null;
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

function photo_make_thumbnail($filename)
{
    $height_dest = 192;
    $width_dest = 256;

    $size_src = getimagesize(INSTALLDIR . '/file/' . $filename);
    $image_type = $size_src[2];

    switch($image_type) {
    case IMAGETYPE_JPEG:
        $image_src = imagecreatefromjpeg(INSTALLDIR . '/file/' . $filename);
        break;
    case IMAGETYPE_PNG:
        $image_src = imagecreatefrompng(INSTALLDIR . '/file/' . $filename);
        break;
    case IMAGETYPE_GIF:
        $image_src = imagecreatefromgif(INSTALLDIR . '/file/' . $filename);
        break;
    default:
        return false;
    }

    $width_src = $size_src[0];
    $height_src = $size_src[1];

    $ratio_src = (float) $width_src / (float) $height_src;
    $ratio_dest = (float) $width_dest / (float) $height_dest;

    if ($ratio_src > $ratio_dest) {
        $height_crop = $height_src;
        $width_crop = (int)($height_crop * $ratio_dest);
        $x_crop = ($width_src - $width_crop) / 2;
    } else {
        $width_crop = $width_src;
        $height_crop = (int)($width_crop / $ratio_dest);
        $x_crop = 0;
    }

    $image_dest = imagecreatetruecolor($width_dest, $height_dest);

    imagecopyresampled($image_dest, $image_src, 0, 0, $x_crop, 0, $width_dest, $height_dest, $width_crop, $height_crop);
    switch ($image_type) {
    case IMAGETYPE_JPEG:
        imagejpeg($image_dest, INSTALLDIR . '/file/' . 'thumb.' . $filename, 100);
        break;
    case IMAGETYPE_PNG:
        imagepng($image_dest, INSTALLDIR . '/file/thumb.' . $filename);
        break;
    case IMAGETYPE_GIF:
        imagegif($image_dest, INSTALLDIR . '/file/thumb.' . $filename);
        break;
    }

    imagedestroy($image_src);
    imagedestroy($image_dest);

    return true;
}


code:
function increasePhotoSize() {
    $('.photoingallery, .albumingallery').each(function(index) {
            this.height *= 1.1;
            this.width *= 1.1;
        });
    return false;
}

function decreasePhotoSize() {
    $('.photoingallery, .albumingallery').each(function(index) {
            this.height /= 1.1;
            this.width /= 1.1;
        });
    return false;
}

function scalePhotosToSize(size) {
    $('.photoingallery, .albumingallery').each(function(index) {
            if(this.height > this.width) {
                this.width = this.width*size/this.height;
                this.height = size;
            }
            else {
                this.height = this.height*size/this.width;
                this.width = size;
            }
        });
    return false;
}


code:
.photocontainer, .albumcontainer {
    display: block;
    background-color: yellow;
    float: left;
    padding: 20px;
    margin: 15px;
}

.photodescription {
    display: block;
    background-color: #dddddd;
    padding: 20px;
    margin: 15px;
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Max Shinn <trombonechamp@gmail.com>
 * @copyright 2011 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

include_once $dir . '/lib/profiletools.php';

class GNUsocialProfileExtensionsPlugin extends Plugin
{

    function onCheckSchema()
    {
        $schema = Schema::get();
        $schema->ensureTable('GNUsocialProfileExtensionField', GNUsocialProfileExtensionField::schemaDef());
        $schema->ensureTable('GNUsocialProfileExtensionResponse', GNUsocialProfileExtensionResponse::schemaDef());

    }

    function onRouterInitialized($m)
    {
        $m->connect(':nickname/bio', array('action' => 'bio'));
        $m->connect('admin/profilefields', array('action' => 'profilefieldsAdminPanel'));
        $m->connect('notice/respond', array('action' => 'newresponse'));
        return true;
    }

    function onEndProfileFormData($action)
    {
        $fields = GNUsocialProfileExtensionField::allFields();
        $user = common_current_user();
        $profile = $user->getProfile();
        gnusocial_profile_merge($profile);
        foreach ($fields as $field) {
            $action->elementStart('li');
            $fieldname = $field->systemname;
            if ($field->type == 'str') {
                $action->input($fieldname, $field->title,
                               ($action->arg($fieldname)) ? $action->arg($fieldname) : $profile->$fieldname,
                               $field->description);
            }
            else if ($field->type == 'text') {
                $action->textarea($fieldname, $field->title,
                                  ($action->arg($fieldname)) ? $action->arg($fieldname) : $profile->$fieldname,
                                  $field->description);
            }
            $action->elementEnd('li');
        }
    }

    function onEndProfileSaveForm($action)
    {
        $fields = GNUsocialProfileExtensionField::allFields();
        $user = common_current_user();
        $profile = $user->getProfile();
        foreach ($fields as $field) {
            $val = $action->trimmed($field->systemname);

            $response = new GNUsocialProfileExtensionResponse();
            $response->profile_id = $profile->id;
            $response->extension_id = $field->id;

            if ($response->find()) {
                $response->fetch();
                $response->value = $val;
                if ($response->validate()) {
                    if (empty($val))
                        $response->delete();
                    else
                        $response->update();
                }
            }
            else {
                $response->value = $val;
                $response->insert();
            }
        }
    }

    function onEndShowStyles($action)
    {
        $action->cssLink('/plugins/GNUsocialProfileExtensions/res/style.css');
    }

    function onEndShowScripts($action)
    {
        $action->script('plugins/GNUsocialProfileExtensions/js/profile.js');
    }

    function onEndAdminPanelNav($nav)
    {
        if (AdminPanelAction::canAdmin('profilefields')) {

            $action_name = $nav->action->trimmed('action');

            $nav->out->menuItem(
                '/admin/profilefields',
                _m('Profile Fields'),
                _m('Custom profile fields'),
                $action_name == 'profilefieldsadminpanel',
                'nav_profilefields_admin_panel'
            );
        }

        return true;
    }

    function onStartPersonalGroupNav(Menu $nav, Profile $target, Profile $scoped=null)
    {
        $nav->out->menuItem(common_local_url('bio',
                           array('nickname' => $nav->action->trimmed('nickname'))), _('Bio'),
                           _('The user\'s extended profile'), $nav->action->trimmed('action') == 'bio', 'nav_bio');
    }

    //Why the heck is this shoved into this plugin!?!?  It deserves its own!
    function onShowStreamNoticeList($notice, $action, &$pnl)
    {
        //TODO: This function is called after the notices in $notice are superfluously retrieved in showstream.php
        $newnotice = new Notice();
        $newnotice->profile_id = $action->user->id;
        $newnotice->orderBy('modified DESC');
        $newnotice->whereAdd('reply_to IS NULL');
        $newnotice->limit(($action->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
        $newnotice->find();

        $pnl = new NoticeTree($newnotice, $action);
        return false;
    }

}


code:
GNU Social Profile Extensions
=============================

Allows administrators to define additional profile fields for the
users of a GNU Social installation.

Installation
------------

To install, copy this directory into your plugins directory and add
the following lines to your config.php file:

addPlugin('GNUsocialProfileExtensions');
$config['admin']['panels'][] = 'profilefields';


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Max Shinn <trombonechamp@gmail.com>
 * @copyright 2011 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

require_once INSTALLDIR . '/lib/personalgroupnav.php';
require_once INSTALLDIR . '/classes/Profile.php';
require_once INSTALLDIR . '/lib/profilelist.php';

class BioAction extends Action
{
    var $user = null;

    function prepare($args)
    {
        parent::prepare($args);

        $args = $this->returnToArgs();
        $this->profile = Profile::getKV('nickname', $args[1]['nickname']);
        //die(print_r($this->profile));
        gnusocial_profile_merge($this->profile);

        return true;

    }

    function handle($args)
    {
        parent::handle($args);
        $this->showPage();
    }

    function title()
    {
        return sprintf(_m("%s's Bio."), $this->profile->nickname);
    }

    function showLocalNav()
    {
        $nav = new PersonalGroupNav($this);
        $nav->show();
    }

    function showContent()
    {
        if(empty($this->profile)) {
            return;
        }

        $profilelistitem = new ProfileListItem($this->profile, $this);
        $profilelistitem->show();
        $this->elementStart('ul');
        $fields = GNUsocialProfileExtensionField::allFields();
        foreach ($fields as $field) {
            $fieldname = $field->systemname;
            if (!empty($this->profile->$fieldname)) {
                $this->elementStart('li', array('class' => 'biolistitem'));
                $this->elementStart('div', array('class' => 'biolistitemcontainer'));
                if ($field->type == 'text') {
                    $this->element('h3', array(), $field->title);
                    $this->element('p', array('class' => 'biovalue'), $this->profile->$fieldname);
                }
                else {
                    $this->element('span', array('class' => 'biotitle'), $field->title);
                    $this->text(' ');
                    $this->element('span', array('class' => 'biovalue'), $this->profile->$fieldname);
                }
                $this->elementEnd('div');
                $this->elementEnd('li');
            }
        }
        $this->elementEnd('ul');
    }

}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Max Shinn <trombonechamp@gmail.com>
 * @copyright 2011 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

require_once INSTALLDIR . '/actions/newnotice.php';

class NewresponseAction extends NewnoticeAction
{
     /**
     * Same as the parent, but not including the @-whoever in replies
     *
     * @return void
     */

    function showNoticeForm()
    {
        $content = $this->trimmed('status_textarea');
        if (!$content) {
            $replyto = $this->trimmed('replyto');
            $inreplyto = $this->trimmed('inreplyto');
        } else {
            // @fixme most of these bits above aren't being passed on above
            $inreplyto = null;
        }

        $notice_form = new NoticeForm($this, '', $content, null, $inreplyto);
        $notice_form->show();
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Max Shinn <trombonechamp@gmail.com>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

require_once INSTALLDIR . '/lib/adminpanelaction.php';

class ProfilefieldsAdminPanelAction extends AdminPanelAction
{

    function title()
    {
        return _('Profile fields');
    }

    function getInstructions()
    {
        return _('GNU Social custom profile fields');
    }

    function showForm()
    {
        $form = new ProfilefieldsAdminForm($this);
        $form->show();
        return;
    }

    function saveSettings()
    {
        $field = GNUsocialProfileExtensionField::getKV('id', $this->trimmed('id'));
        if (!$field)
            $field = new GNUsocialProfileExtensionField();
        $field->title = $this->trimmed('title');
        $field->description = $this->trimmed('description');
        $field->type = $this->trimmed('type');
        $field->systemname = $this->trimmed('systemname');
        if (!gnusocial_field_systemname_validate($field->systemname)) {
            $this->clientError(_('Internal system name must be unique and consist of only alphanumeric characters!'));
        }
        if ($field->id) {
            if ($field->validate())
                $field->update();
            else {
                $this->clientError(_('There was an error with the field data.'));
            }
        }
        else {
            $field->insert();
        }

        return;
    }

}

class ProfilefieldsAdminForm extends AdminForm
{

    function id()
    {
        return 'form_profilefields_admin_panel';
    }

    function formClass()
    {
        return 'form_settings';
    }

    function action()
    {
        return '/admin/profilefields';
    }

    function formData()
    {
        $title = null;
        $description = null;
        $type = null;
        $systemname = null;
        $id = null;
        $fieldsettitle = _("New Profile Field");
        //Edit a field
        if ($this->out->trimmed('edit')) {
            $field = GNUsocialProfileExtensionField::getKV('id', $this->out->trimmed('edit'));
            $title = $field->title;
            $description = $field->description;
            $type = $field->type;
            $systemname = $field->systemname;
            $this->out->hidden('id', $field->id, 'id');
            $fieldsettitle = _("Edit Profile Field");
        }
        //Don't show the list of all fields when editing one
        else {
            $this->out->elementStart('fieldset');
            $this->out->element('legend', null, _('Existing Custom Profile Fields'));
            $this->out->elementStart('ul', 'form_data');
            $fields = GNUsocialProfileExtensionField::allFields();
            foreach ($fields as $field) {
                $this->li();
                $content =
                $this->out->elementStart('div');
                $this->out->element('a', array('href' => '/admin/profilefields?edit=' . $field->id),
                                    $field->title);
                $this->out->text(' (' . $field->type . '): ' . $field->description);
                $this->out->elementEnd('div');
                $this->unli();
            }
            $this->out->elementEnd('ul');
            $this->out->elementEnd('fieldset');
        }

        //New fields
        $this->out->elementStart('fieldset');
        $this->out->element('legend', null, $fieldsettitle);
        $this->out->elementStart('ul', 'form_data');

        $this->li();
        $this->out->input('title', _('Title'), $title,
                     _('The title of the field'));
        $this->unli();
        $this->li();
        $this->out->input('systemname', _('Internal name'), $systemname,
                     _('The alphanumeric name used internally for this field.  Also the key used in OStatus user info. (optional)'));
        $this->unli();
        $this->li();
        $this->out->input('description', _('Description'), $description,
                     _('An optional more detailed description of the field'));
        $this->unli();
        $this->li();
        $this->out->dropdown('type', _('Type'), array('text' => _("Text"),
                                                      'str' => _("String")),
                             _('The type of the datafield'), false, $type);
        $this->unli();
        $this->out->elementEnd('ul');
        $this->out->elementEnd('fieldset');
    }

    /**
     * Action elements
     *
     * @return void
     */

    function formActions()
    {
        $this->out->submit('submit', _('Save'), 'submit', null, _('Save new field'));
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Max Shinn <trombonechamp@gmail.com>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

require_once INSTALLDIR . '/classes/Memcached_DataObject.php';

class GNUsocialProfileExtensionField extends Managed_DataObject
{
    public $__table = 'gnusocialprofileextensionfield';
    public $id;          // int(11)
    public $systemname;  // varchar(64)
    public $title;       // varchar(255)
    public $description; // text
    public $type;        // varchar(255)
    public $created;     // datetime()   not_null
    public $modified;    // timestamp()   not_null default_CURRENT_TIMESTAMP

    public static function schemaDef()
    {
        return array(
            'fields' => array(
                'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique ID for extension field'),
                'systemname' => array('type' => 'varchar', 'not null' => true, 'length' => 64, 'description' => 'field systemname'),
                'title' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'field title'),
                'description' => array('type' => 'text', 'not null' => true, 'description' => 'field description'),
                'type' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'field type'),
                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
            ),
            'primary key' => array('id'),
            'indexes' => array(
                'gnusocialprofileextensionfield_title_idx' => array('title'),
            ),
        );
    }

    static function newField($title, $description=null, $type='str', $systemname=null)
    {
        $field = new GNUsocialProfileExtensionField();
        $field->title = $title;
        $field->description = $description;
        $field->type = $type;
        if (empty($systemname))
            $field->systemname = 'field' + $field->id;
        else
            $field->systemname = $systemname;

        $field->id = $field->insert();
        if (!$field->id){
            common_log_db_error($field, 'INSERT', __FILE__);
            throw new ServerException(_m('Error creating new field.'));
        }
        return $field;
    }

    static function allFields()
    {
        $field = new GNUsocialProfileExtensionField();
        $fields = array();
        if ($field->find()) {
            while($field->fetch()) {
                $fields[] = clone($field);
            }
        }
        return $fields;
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Max Shinn <trombonechamp@gmail.com>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

require_once INSTALLDIR . '/classes/Memcached_DataObject.php';

class GNUsocialProfileExtensionResponse extends Managed_DataObject
{
    public $__table = 'gnusocialprofileextensionresponse';
    public $id;           // int(11)
    public $extension_id; // int(11)
    public $profile_id;   // int(11)
    public $value;     // text
    public $created;      // datetime()   not_null
    public $modified;     // timestamp()   not_null default_CURRENT_TIMESTAMP

    public static function schemaDef()
    {
        return array(
            'fields' => array(
                'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique ID for extension response'),
                'extension_id' => array('type' => 'int', 'not null' => true, 'description' => 'The extension field ID'),
                'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'Profile id that made the response'),
                'value' => array('type' => 'text', 'not null' => true, 'description' => 'response entry'),
                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
            ),
            'primary key' => array('id'),
            'foreign keys' => array(
                'gnusocialprofileextensionresponse_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
                'gnusocialprofileextensionresponse_extension_id_fkey' => array('gnusocialprofileextensionfield', array('extension_id' => 'id')),
            ),
            'indexes' => array(
                'gnusocialprofileextensionresponse_extension_id_idx' => array('extension_id'),
            ),
        );
    }

    static function newResponse($extension_id, $profile_id, $value)
    {

        $response = new GNUsocialProfileExtensionResponse();
        $response->extension_id = $extension_id;
        $response->profile_id = $profile_id;
        $response->value = $value;

        $response->id = $response->insert();
        if (!$response->id){
            common_log_db_error($response, 'INSERT', __FILE__);
            throw new ServerException(_m('Error creating new response.'));
        }
        return $response;
    }

    static function findResponsesByProfile($id)
    {
        $extf = 'gnusocialprofileextensionfield';
        $extr = 'gnusocialprofileextensionresponse';
        $sql = "SELECT $extr.*, $extf.title, $extf.description, $extf.type, $extf.systemname FROM $extr JOIN $extf ON $extr.extension_id=$extf.id WHERE $extr.profile_id = $id";
        $response = new GNUsocialProfileExtensionResponse();
        $response->query($sql);
        $responses = array();

        while ($response->fetch()) {
            $responses[] = clone($response);
        }

        return $responses;
    }

}


code:
SN.U.NoticeReplySet = function(nick,id) {
    $('div.replyform').hide();
    $('div#form'+id).show();
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Max Shinn <trombonechamp@gmail.com>
 * @copyright 2011 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */
            //functions to sort replies
class NoticeTree extends NoticeList
{
    function show()
    {
        $this->out->elementStart('div', array('id' =>'notices_primary'));
        // TRANS: Header on conversation page. Hidden by default (h2).
        $this->out->element('h2', null, _('Notices'));
        $this->out->elementStart('ol', array('class' => 'notices xoxo'));

        $cnt = 0;

        while ($this->notice->fetch() && $cnt <= NOTICES_PER_PAGE) {
            if (!empty($this->notice->reply_to))
                continue;

            $cnt++;

            if ($cnt > NOTICES_PER_PAGE) {
                break;
            }

            try {
                $this->showNoticePlus($this->newListItem($this->notice));
            } catch (Exception $e) {
                // we log exceptions and continue
                print "ERROR!" . $e->getMessage();
                common_log(LOG_ERR, $e->getMessage());
                continue;
            }
        }

        $this->out->elementEnd('ol');
        $this->out->elementEnd('div');

        return $cnt;
    }

    function _cmpDate($a, $b) { return strcmp($a->notice->modified, $b->notice->modified); }
    function _cmpFav($a, $b) { return (int)$a->faves < (int)$b->faves; }

    function showNoticePlus($item)
    {
        $replies = new Notice();
        $replies->reply_to = $item->notice->id;

        // We take responsibility for doing the li

        $this->out->elementStart('li', array('class' => 'h-entry notice',
                                             'id' => 'notice-' . $item->notice->id));

        $item->show();

        if ($replies->find()) {
            $this->out->elementStart('ol', array('class' => 'notices'));

            $replieslist = array();
            while ($replies->fetch()) {
                $replieslist[] = $this->newListItem(clone($replies));
            }

            //Sorting based on url argument
            if($_GET['sort'] == 'faves')
                usort($replieslist, array($this, '_cmpFav'));
            else
                usort($replieslist, array($this, '_cmpDate'));

            foreach($replieslist as $reply) {
                $this->showNoticePlus($reply);
            }

            $this->out->elementEnd('ol');
        }

        $this->out->elementEnd('li');
    }

    function newListItem($notice)
    {
        return new NoticeTreeItem($notice, $this->out);
    }
}

class NoticeTreeItem extends NoticeListItem
{
    function __construct($notice, $out=null)
    {
        parent::__construct($notice, $out);
        //TODO: Rewrite this
        //Showing number of favorites
        $fave = new Fave();
        $fave->notice_id = $this->notice->id;
        $cnt = 0;
        if ($fave->find()) {
            while ($fave->fetch())
                $cnt++;
        }
        $this->faves = $cnt;
    }

    function showStart()
    {
        return;
    }

    function showEnd()
    {
        if ($this->faves > 0) {
            $this->out->text(_m("Favorited by $this->faves user"));
            if ($this->faves > 1) $this->out->text("s"); //there has to be a better way to do this...
        }

        //Show response form
        $this->out->elementStart('div', array('id' => 'form' . $this->notice->id, 'class' => 'replyform'));
        $noticeform = new ReplyForm($this->out, null, null, null, $this->notice->id);
        $noticeform->show();
        $this->out->elementEnd('div');
        return;
    }

    //Just changing the link...
    function showReplyLink()
    {
        if (common_logged_in()) {
            $this->out->text(' ');
            //Why doesn't common_local_url work here?
            $reply_url = '/notice/respond?replyto=' . $this->profile->nickname . '&inreplyto=' . $this->notice->id;
            $this->out->elementStart('a', array('href' => $reply_url,
                                                'class' => 'notice_reply',
                                                'title' => _('Reply to this notice')));
            $this->out->text(_('Reply'));
            $this->out->text(' ');
            $this->out->element('span', 'notice_id', $this->notice->id);
            $this->out->elementEnd('a');
        }
    }

}

class ReplyForm extends NoticeForm
{
    //Changing the text.  We have to either repeat ids or get hacky.  I vote repeat ids.
    function formData()
    {
        if (Event::handle('StartShowNoticeFormData', array($this))) {
            // 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 : '');

            $contentLimit = Notice::maxContent();

            if ($contentLimit > 0) {
                $this->out->elementStart('dl', 'form_note');
                $this->out->element('dt', null, _('Available characters'));
                $this->out->element('dd', array('id' => 'notice_text-count'),
                                    $contentLimit);
                $this->out->elementEnd('dl');
            }

            if (common_config('attachments', 'uploads')) {
                $this->out->element('label', array('for' => 'notice_data-attach'),_('Attach'));
                $this->out->element('input', array('id' => 'notice_data-attach',
                                                   'type' => 'file',
                                                   'name' => 'attach',
                                                   'title' => _('Attach a file')));
                $this->out->hidden('MAX_FILE_SIZE', common_config('attachments', 'file_quota'));
            }
            if ($this->action) {
                $this->out->hidden('notice_return-to', $this->action, 'returnto');
            }
            $this->out->hidden('notice_in-reply-to', $this->inreplyto, 'inreplyto');

            if ($this->profile->shareLocation()) {
                $this->out->hidden('notice_data-lat', empty($this->lat) ? (empty($this->profile->lat) ? null : $this->profile->lat) : $this->lat, 'lat');
                $this->out->hidden('notice_data-lon', empty($this->lon) ? (empty($this->profile->lon) ? null : $this->profile->lon) : $this->lon, 'lon');
                $this->out->hidden('notice_data-location_id', empty($this->location_id) ? (empty($this->profile->location_id) ? null : $this->profile->location_id) : $this->location_id, 'location_id');
                $this->out->hidden('notice_data-location_ns', empty($this->location_ns) ? (empty($this->profile->location_ns) ? null : $this->profile->location_ns) : $this->location_ns, 'location_ns');

                $this->out->elementStart('div', array('id' => 'notice_data-geo_wrap',
                                                      'title' => common_local_url('geocode')));
                $this->out->checkbox('notice_data-geo', _('Share my location'), true);
                $this->out->elementEnd('div');
                $this->out->inlineScript(' var NoticeDataGeo_text = {'.
                    'ShareDisable: ' .json_encode(_('Do not share my location')).','.
                    'ErrorTimeout: ' .json_encode(_('Sorry, retrieving your geo location is taking longer than expected, please try again later')).
                    '}');
            }

            Event::handle('EndShowNoticeFormData', array($this));
        }
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2010, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Max Shinn <trombonechamp@gmail.com>
 * @copyright 2010 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

function gnusocial_profile_merge(&$profile)
{
    $responses = GNUsocialProfileExtensionResponse::findResponsesByProfile($profile->id);
    $profile->customfields = array();
    foreach ($responses as $response) {
        $title = $response->systemname;
        $profile->$title = $response->value;
        $profile->customfields[] = $title;
    }
}

function gnusocial_field_systemname_validate($systemname)
{
    $fields = GNUsocialProfileExtensionField::allFields();
    foreach ($fields as $field)
        if ($field->systemname == $systemname)
            return false;
    return ctype_alphanum($systemname);
}


code:
.biotitle {
    font-weight: bold;
}
.biovalue {
    font-style: italic;
}
#showstream ol.notices ol.notices {
    background-image: url(/plugins/GNUsocialProfileExtensions/res/bgstripe.gif);
    background-repeat: repeat-y;
    background-position: left top;
    padding-left: 15px;
    color: #333333;
}
#showstream ol.notices ol.notices ol.notices {
    padding-left: 5px;
}
div.replyform {
    display: none;
    padding-left: 15px;
}
.replyform .form_notice {
    width: 75%;
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2011, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @copyright 2011 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

class GNUsocialVideoPlugin extends MicroAppPlugin
{

    var $oldSaveNew = true;

    function onCheckSchema()
    {
        $schema = Schema::get();

        $schema->ensureTable('video', Video::schemaDef());

        return true;
    }

    function onRouterInitialized($m)
    {
        $m->connect('main/postvideo', array('action' => 'postvideo'));
        $m->connect('showvideo/:id', array('action' => 'showvideo'));
        return true;
    }

    function entryForm($out)
    {
        return new VideoForm($out);
    }

    function appTitle()
    {
        return _('Video');
    }

    function tag()
    {
        return 'GNUsocialVideo';
    }

    function types()
    {
        return array(Video::OBJECT_TYPE);
    }

    function saveNoticeFromActivity(Activity $activity, Profile $actor, array $options=array())
    {
        if(count($activity->objects) != 1) {
            throw new Exception('Too many activity objects.');
        }

        $videoObj = $activity->objects[0];

        if ($videoObj->type != Video::OBJECT_TYPE) {
            throw new Exception('Wrong type for object.');
        }

        // For now we read straight from the xml tree, no other way to get this information.
        // When there's a better API for this, we should change to it.
        $uri = ActivityUtils::getLink($activity->entry, 'enclosure');

        $options['object_type'] = Video::OBJECT_TYPE;

        Video::saveNew($actor, $uri, $options);

    }

    function activityObjectFromNotice(Notice $notice)
    {
        $object = new ActivityObject();
        $object->id = $notice->uri;
        $object->type = Video::OBJECT_TYPE;
        $object->title = $notice->content;
        $object->summary = $notice->content;
        $object->link = $notice->getUrl();

        $vid = Video::getByNotice($notice);

        if ($vid) {
            $object->extra[] = array('link', array('rel' => 'enclosure', 'href' => $vid->url), array());
        }

        return $object;

    }

    function showNotice(Notice $notice, HTMLOutputter $out)
    {
        $vid = Video::getByNotice($notice);
        if ($vid) {
            $out->element('video', array('src' => $vid->url,
                'width' => '100%',
                'controls' => 'controls'));
        }
    }

    function deleteRelated(Notice $notice)
    {
        $vid = Video::getByNotice($notice);
        if ($vid) {
            $vid->delete();
        }
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2011, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @category  Widget
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @copyright 2011 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if (!defined('STATUSNET')) {
    exit(1);
}

class PostvideoAction extends Action {
    var $user = null;
    var $url = null;

    function prepare($args)
    {
        parent::prepare($args);
        $this->user = common_current_user();

        if(empty($this->user)){
            throw new ClientException(_('Must be logged in to post a video'),
                403);
        }

        if($this->isPost()){
            $this->checkSessionToken();
        }

        $this->url = filter_var($this->trimmed('url'), FILTER_SANITIZE_URL);
        $this->url = filter_var($this->url, FILTER_VALIDATE_URL);

        return true;
    }

    function handle($args)
    {
        parent::handle($args);

        if ($this->isPost()) {
            $this->handlePost($args);
        } else {
            $this->showPage();
        }
    }

    function handlePost($args)
    {
        if (empty($this->url)) {
            throw new ClientException(_('Bad URL.'));
        }

        $profile = $this->user->getProfile();

        $options = array();

        ToSelector::fillOptions($this, $options);

        $vid = Video::saveNew($profile, $this->url, $options);

        common_redirect($vid->uri, 303);
    }

    function showContent()
    {
        $form  = new VideoForm();
        $form->show();
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2011, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @copyright 2011 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if(!defined('STATUSNET')){
    exit(1);
}

class ShowvideoAction extends ShownoticeAction
{
    protected $id = null;
    protected $vid = null;

    function prepare($args)
    {
        OwnerDesignAction::prepare($args);
        $this->id = $this->trimmed('id');
        $this->vid = Video::getKV('id', $this->id);

        if (empty($this->vid)) {
            throw new ClientException(_('No such video.'), 404);
        }

        $this->notice = $this->vid->getNotice();

        if (empty($this->notice)) {
            throw new ClientException(_('No such video'), 404);
        }

        $this->user = User::getKV('id', $this->vid->profile_id);

        if (empty($this->user)) {
            throw new ClientException(_('No such user.'), 404);
        }

        $this->profile = $this->user->getProfile();

        if (empty($this->profile)) {
            throw new ServerException(_('User without a profile.'));
        }

        return true;
    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2011, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @copyright 2011 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if(!defined('STATUSNET')){
    exit(1);
}

/**
 * Data class for videos.
 */

class Video extends Managed_DataObject
{
    const OBJECT_TYPE = 'http://activitystrea.ms/schema/1.0/video';

    public $__table = 'video'; // table name
    public $id;                // char (36) // UUID
    public $uri;               // varchar (255)  // This is the corresponding notice's uri.
    public $url;               // varchar (255)
    public $profile_id;        // int

    public static function getByNotice($notice)
    {
        return self::getKV('uri', $notice->uri);
    }

    public function getNotice()
    {
        return Notice::getKV('uri', $this->uri);
    }

    public static function schemaDef()
    {
        return array(
            'description' => 'A video clip',
            'fields' => array(
                'id' => array('type' => 'char',
                              'length' => 36,
                              'not null' => true,
                              'description' => 'UUID'),
                'uri' => array('type' => 'varchar',
                               'length' => 255,
                               'not null' => true),
                'url' => array('type' => 'varchar',
                               'length' => 255,
                               'not null' => true),
                'profile_id' => array('type' => 'int', 'not null' => true),
            ),
            'primary key' => array('id'),
            'foreign keys' => array('video_profile_id__key' => array('profile' => array('profile_id' => 'id'))),
        );
    }

    static function saveNew(Profile $profile, $url, $options=array())
    {
        $vid = new Video();

        $vid->id =  UUID::gen();
        $vid->profile_id = $profile->id;
        $vid->url = $url;

        $options['object_type'] = Video::OBJECT_TYPE;

        if (!array_key_exists('uri', $options)) {
            $options['uri'] = common_local_url('showvideo', array('id' => $vid->id));
        }

        if (!array_key_exists('rendered', $options)) {
            $options['rendered'] = sprintf("<video src=\"%s\">Sorry, your browser doesn't support the video tag.</video>", $url);
        }

        $vid->uri = $options['uri'];

        $vid->insert();

        return Notice::saveNew($profile->id,
                               '',
                               'web',
                               $options);

    }
}


code:
<?php
/**
 * GNU Social
 * Copyright (C) 2011, Free Software Foundation, Inc.
 *
 * PHP version 5
 *
 * LICENCE:
 * 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/>.
 *
 * @package   GNU Social
 * @author    Ian Denhardt <ian@zenhack.net>
 * @copyright 2011 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 */

if(!defined('STATUSNET')){
    exit(1);
}

class VideoForm extends Form
{
    function id()
    {
        return "form_new_video";
    }

    function action()
    {
        return common_local_url('postvideo');
    }

    function formClass()
    {
        return 'form_settings ajax-notice';
    }

    function formData()
    {
        $this->out->elementStart('fieldset', array('id' => 'new_video_data'));
        $this->out->elementStart('ul', 'form_data');

        $this->li();
        $this->out->input('url', _('URL'), null, _('URL of the video'));
        $this->unli();

        $this->out->elementEnd('ul');

        $toWidget = new ToSelector($this->out,
                                   common_current_user(),
                                   null);
        $toWidget->show();

        $this->out->elementEnd('fieldset');
    }

    function formActions()
    {
        $this->out->submit('submit', _m('BUTTON', 'Save'));
    }
}