From b0899bd940adcf89eb4f6500d88c9e9ca1495a49 Mon Sep 17 00:00:00 2001 From: Max Shinn Date: Fri, 31 Dec 2010 16:36:51 -0600 Subject: [PATCH] New plugin: GNUsocialProfileExtensions! Profiles can be extended with administrator-defined fields. --- .../GNUsocialProfileExtensionsPlugin.php | 164 ++++++++++++++++++ .../actions/bio.php | 103 +++++++++++ .../actions/profilefields.php | 139 +++++++++++++++ .../GNUsocialProfileExtensionField.php | 109 ++++++++++++ .../GNUsocialProfileExtensionResponse.php | 109 ++++++++++++ .../classes/ProfileExtensionField.php | 148 ++++++++++++++++ .../lib/profiletools.php | 40 +++++ .../GNUsocialProfileExtensions/res/style.css | 6 + 8 files changed, 818 insertions(+) create mode 100644 plugins/GNUsocialProfileExtensions/GNUsocialProfileExtensionsPlugin.php create mode 100644 plugins/GNUsocialProfileExtensions/actions/bio.php create mode 100644 plugins/GNUsocialProfileExtensions/actions/profilefields.php create mode 100644 plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionField.php create mode 100644 plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionResponse.php create mode 100644 plugins/GNUsocialProfileExtensions/classes/ProfileExtensionField.php create mode 100644 plugins/GNUsocialProfileExtensions/lib/profiletools.php create mode 100644 plugins/GNUsocialProfileExtensions/res/style.css diff --git a/plugins/GNUsocialProfileExtensions/GNUsocialProfileExtensionsPlugin.php b/plugins/GNUsocialProfileExtensions/GNUsocialProfileExtensionsPlugin.php new file mode 100644 index 0000000000..468bc19204 --- /dev/null +++ b/plugins/GNUsocialProfileExtensions/GNUsocialProfileExtensionsPlugin.php @@ -0,0 +1,164 @@ +. + * + * @category Widget + * @package GNU Social + * @author Max Shinn + * @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 GNUsocialProfileExtensionsPlugin extends Plugin +{ + + function onAutoload($cls) + { + $dir = dirname(__FILE__); + + switch ($cls) + { + case 'BioAction': + include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php'; + break; + case 'ProfilefieldsAdminPanelAction': + include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -16)) . '.php'; + break; + default: + break; + } + include_once $dir . '/classes/GNUsocialProfileExtensionField.php'; + include_once $dir . '/classes/GNUsocialProfileExtensionResponse.php'; + include_once $dir . '/lib/profiletools.php'; + return true; + } + + function onCheckSchema() + { + $schema = Schema::get(); + $schema->ensureTable('GNUsocialProfileExtensionField', + array(new ColumnDef('id', 'int(11)', null, false, 'PRI', null, null, true), + new ColumnDef('systemname', 'varchar(64)', null, false), + new ColumnDef('title', 'varchar(256)', null, false), + new ColumnDef('description', 'text', null, false), + new ColumnDef('type', 'varchar(256)', null, false))); + $schema->ensureTable('GNUsocialProfileExtensionResponse', + array(new ColumnDef('id', 'int(11)', null, false, 'PRI', null, null, true), + new ColumnDef('extension_id', 'int(11)', null, false), + new ColumnDef('profile_id', 'int(11)', null, false), + new ColumnDef('value', 'text', null, false))); + + } + + function onRouterInitialized($m) + { + $m->connect(':nickname/bio', array('action' => 'bio')); + $m->connect('admin/profilefields', array('action' => 'profilefieldsAdminPanel')); + 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 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($nav) + { + $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'); + } + +} + diff --git a/plugins/GNUsocialProfileExtensions/actions/bio.php b/plugins/GNUsocialProfileExtensions/actions/bio.php new file mode 100644 index 0000000000..6cfded4e36 --- /dev/null +++ b/plugins/GNUsocialProfileExtensions/actions/bio.php @@ -0,0 +1,103 @@ +. + * + * @category Widget + * @package GNU Social + * @author Max Shinn + * @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'; +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::staticGet('nickname', $args[1]['nickname']); + //die(print_r($this->profile)); + gnusocial_profile_merge($this->profile); + $this->avatar = $this->profile->getAvatar(96); + + 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'); + } + +} diff --git a/plugins/GNUsocialProfileExtensions/actions/profilefields.php b/plugins/GNUsocialProfileExtensions/actions/profilefields.php new file mode 100644 index 0000000000..ae01269a10 --- /dev/null +++ b/plugins/GNUsocialProfileExtensions/actions/profilefields.php @@ -0,0 +1,139 @@ +. + * + * @category Widget + * @package GNU Social + * @author Max Shinn + * @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() + { + $title = $this->trimmed('title'); + $description = $this->trimmed('description'); + $type = $this->trimmed('type'); + $systemname = $this->trimmed('systemname'); + + $field = GNUsocialProfileExtensionField::newField($title, $description, $type, $systemname); + + return; + } + +} + +class ProfilefieldsAdminForm extends AdminForm +{ + + function id() + { + return 'form_profilefields_admin_panel'; + } + + function formClass() + { + return 'form_settings'; + } + + function action() + { + return '/admin/profilefields'; + } + + function formData() + { + //Listing all fields + $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(); + $this->out->element('div', array(), $field->title . ' (' . + $field->type . '): ' . $field->description); + $this->unli(); + } + $this->out->elementEnd('ul'); + $this->out->elementEnd('fieldset'); + + //New fields + $this->out->elementStart('fieldset'); + $this->out->element('legend', null, _('New Profile Field')); + $this->out->elementStart('ul', 'form_data'); + + $this->li(); + $this->input('title', _('Title'), + _('The title of the field')); + $this->unli(); + $this->li(); + $this->input('systemname', _('Internal name'), + _('The name used internally for this field. Also the key used in OStatus user info. (optional)')); + $this->unli(); + $this->li(); + $this->input('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')); + $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')); + } +} diff --git a/plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionField.php b/plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionField.php new file mode 100644 index 0000000000..6877eab1bb --- /dev/null +++ b/plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionField.php @@ -0,0 +1,109 @@ +. + * + * @category Widget + * @package GNU Social + * @author Max Shinn + * @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 Memcached_DataObject +{ + public $__table = 'GNUsocialProfileExtensionField'; + public $id; // int(11) + public $systemname; // varchar(64) + public $title; // varchar(256) + public $description; // text + public $type; // varchar(256) + + /** + * + * k key + * v value + */ + function staticGet($k,$v=NULL) + { + return Memcached_DataObject::staticGet('GNUsocialProfileExtensionField',$k,$v); + } + + + function table() + { + return array('id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'systemname' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'title' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'description' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'type' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL); + } + + function keys() + { + return array_keys($this->keyTypes()); + } + + function keyTypes() + { + return array('id' => 'K'); + } + + function sequenceKey() + { + return array(false, false, false); + } + + 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; + } +} diff --git a/plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionResponse.php b/plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionResponse.php new file mode 100644 index 0000000000..fbf5af0dbb --- /dev/null +++ b/plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionResponse.php @@ -0,0 +1,109 @@ +. + * + * @category Widget + * @package GNU Social + * @author Max Shinn + * @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 Memcached_DataObject +{ + public $__table = 'GNUsocialProfileExtensionResponse'; + public $id; // int(11) + public $extension_id; // int(11) + public $profile_id; // int(11) + public $value; // text + + /** + * + * k key + * v value + */ + function staticGet($k,$v=NULL) + { + return Memcached_DataObject::staticGet('GNUsocialProfileExtensionResponse',$k,$v); + } + + + function table() + { + return array('id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'extension_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'value' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL); + } + + function keys() + { + return array_keys($this->keyTypes()); + } + + function keyTypes() + { + return array('id' => 'K'); + } + + function sequenceKey() + { + return array(false, false, false); + } + + 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; + } + +} diff --git a/plugins/GNUsocialProfileExtensions/classes/ProfileExtensionField.php b/plugins/GNUsocialProfileExtensions/classes/ProfileExtensionField.php new file mode 100644 index 0000000000..142b70230b --- /dev/null +++ b/plugins/GNUsocialProfileExtensions/classes/ProfileExtensionField.php @@ -0,0 +1,148 @@ +. + * + * @category Widget + * @package GNU Social + * @author Max Shinn + * @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 Memcached_DataObject +{ + public $__table = 'GNUsocialPhoto'; + public $id; // int(11) + public $_id; // int(11) + public $album_id; // int(11) + public $uri; // varchar(512) + public $thumb_uri; // varchar(512) + public $title; // varchar(512) + public $photo_description; // text + + + /** + * + * k key + * v value + */ + function staticGet($k,$v=NULL) + { + return Memcached_DataObject::staticGet('GNUsocialPhoto',$k,$v); + } + +/* function delete() + { + if(!unlink(INSTALLDIR . $this->thumb_uri)) { + return false; + } + if(!unlink(INSTALLDIR . $this->path)) { + return false; + } + return parent::delete(); + } */ + + + /* + * TODO: Foriegn key on album_id. + */ + function table() + { + return array('id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'notice_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'album_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'uri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'thumb_uri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'title' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'photo_description' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL); + } + + function keys() + { + return array_keys($this->keyTypes()); + } + + function keyTypes() + { + return array('notice_id' => 'K'); + } + + function sequenceKey() + { + return array(false, false, false); + } + + 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; + } +} diff --git a/plugins/GNUsocialProfileExtensions/lib/profiletools.php b/plugins/GNUsocialProfileExtensions/lib/profiletools.php new file mode 100644 index 0000000000..57f403929e --- /dev/null +++ b/plugins/GNUsocialProfileExtensions/lib/profiletools.php @@ -0,0 +1,40 @@ +. + * + * @category Widget + * @package GNU Social + * @author Max Shinn + * @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; + } +} + + diff --git a/plugins/GNUsocialProfileExtensions/res/style.css b/plugins/GNUsocialProfileExtensions/res/style.css new file mode 100644 index 0000000000..b7d746bd8d --- /dev/null +++ b/plugins/GNUsocialProfileExtensions/res/style.css @@ -0,0 +1,6 @@ +.biotitle { + font-weight: bold; +} +.biovalue { + font-style: italic; +} \ No newline at end of file