diff options
Diffstat (limited to 'classes')
-rw-r--r-- | classes/Avatar.php | 30 | ||||
-rw-r--r-- | classes/Design.php | 155 | ||||
-rw-r--r-- | classes/Fave.php | 46 | ||||
-rw-r--r-- | classes/File.php | 80 | ||||
-rw-r--r-- | classes/File_oembed.php | 39 | ||||
-rw-r--r-- | classes/File_redirection.php | 126 | ||||
-rw-r--r-- | classes/File_thumbnail.php | 19 | ||||
-rw-r--r-- | classes/File_to_post.php | 30 | ||||
-rw-r--r-- | classes/Foreign_user.php | 22 | ||||
-rw-r--r-- | classes/Group_alias.php | 41 | ||||
-rw-r--r-- | classes/Group_block.php | 115 | ||||
-rw-r--r-- | classes/Group_inbox.php | 6 | ||||
-rw-r--r-- | classes/Memcached_DataObject.php | 22 | ||||
-rw-r--r-- | classes/Notice.php | 248 | ||||
-rw-r--r-- | classes/Notice_inbox.php | 18 | ||||
-rw-r--r-- | classes/Notice_tag.php | 2 | ||||
-rw-r--r-- | classes/Profile.php | 87 | ||||
-rw-r--r-- | classes/Profile_block.php | 2 | ||||
-rw-r--r-- | classes/Remote_profile.php | 2 | ||||
-rw-r--r-- | classes/Status_network.php | 143 | ||||
-rw-r--r-- | classes/Subscription.php | 2 | ||||
-rw-r--r-- | classes/User.php | 91 | ||||
-rw-r--r-- | classes/User_group.php | 133 | ||||
-rwxr-xr-x[-rw-r--r--] | classes/laconica.ini | 65 | ||||
-rw-r--r--[-rwxr-xr-x] | classes/statusnet.ini | 5 |
25 files changed, 1203 insertions, 326 deletions
diff --git a/classes/Avatar.php b/classes/Avatar.php index db9d78e47..5e8b315fe 100644 --- a/classes/Avatar.php +++ b/classes/Avatar.php @@ -55,19 +55,43 @@ class Avatar extends Memcached_DataObject static function path($filename) { - return INSTALLDIR . '/avatar/' . $filename; + $dir = common_config('avatar', 'dir'); + + if ($dir[strlen($dir)-1] != '/') { + $dir .= '/'; + } + + return $dir . $filename; } static function url($filename) { - return common_path('avatar/'.$filename); + $path = common_config('avatar', 'path'); + + if ($path[strlen($path)-1] != '/') { + $path .= '/'; + } + + if ($path[0] != '/') { + $path = '/'.$path; + } + + $server = common_config('avatar', 'server'); + + if (empty($server)) { + $server = common_config('site', 'server'); + } + + // XXX: protocol + + return 'http://'.$server.$path.$filename; } function displayUrl() { $server = common_config('avatar', 'server'); if ($server) { - return 'http://'.$server.'/'.$this->filename; + return Avatar::url($this->filename); } else { return $this->url; } diff --git a/classes/Design.php b/classes/Design.php new file mode 100644 index 000000000..da4b670be --- /dev/null +++ b/classes/Design.php @@ -0,0 +1,155 @@ +<?php +/* + * Laconica - the distributed open-source microblogging tool + * Copyright (C) 2009, Control Yourself, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('LACONICA')) { + exit(1); +} + +define('BACKGROUND_ON', 1); +define('BACKGROUND_OFF', 2); +define('BACKGROUND_TILE', 4); + +/** + * Table Definition for design + */ + +require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; +require_once INSTALLDIR . '/lib/webcolor.php'; + +class Design extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'design'; // table name + public $id; // int(4) primary_key not_null + public $backgroundcolor; // int(4) + public $contentcolor; // int(4) + public $sidebarcolor; // int(4) + public $textcolor; // int(4) + public $linkcolor; // int(4) + public $backgroundimage; // varchar(255) + public $disposition; // tinyint(1) default_1 + + /* Static get */ + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Design',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + function showCSS($out) + { + try { + + $bgcolor = new WebColor($this->backgroundcolor); + $ccolor = new WebColor($this->contentcolor); + $sbcolor = new WebColor($this->sidebarcolor); + $tcolor = new WebColor($this->textcolor); + $lcolor = new WebColor($this->linkcolor); + + } catch (WebColorException $e) { + // This shouldn't happen + common_log(LOG_ERR, "Unable to create color for design $id.", + __FILE__); + } + + $css = 'body { background-color: #' . $bgcolor->hexValue() . ' }' . "\n"; + $css .= '#content, #site_nav_local_views .current a { background-color: #'; + $css .= $ccolor->hexValue() . '} '."\n"; + $css .= '#aside_primary { background-color: #'. $sbcolor->hexValue() . ' }' . "\n"; + $css .= 'html body { color: #'. $tcolor->hexValue() . ' }'. "\n"; + $css .= 'a { color: #' . $lcolor->hexValue() . ' }' . "\n"; + + if (!empty($this->backgroundimage) && + $this->disposition & BACKGROUND_ON) { + + $repeat = ($this->disposition & BACKGROUND_TILE) ? + 'background-repeat:repeat;' : + 'background-repeat:no-repeat;'; + + $css .= 'body { background-image:url(' . + Design::url($this->backgroundimage) . + '); ' . $repeat . ' }' . "\n"; + } + + $out->element('style', array('type' => 'text/css'), $css); + + } + + static function filename($id, $extension, $extra=null) + { + return $id . (($extra) ? ('-' . $extra) : '') . $extension; + } + + static function path($filename) + { + $dir = common_config('background', 'dir'); + + if ($dir[strlen($dir)-1] != '/') { + $dir .= '/'; + } + + return $dir . $filename; + } + + static function url($filename) + { + $path = common_config('background', 'path'); + + if ($path[strlen($path)-1] != '/') { + $path .= '/'; + } + + if ($path[0] != '/') { + $path = '/'.$path; + } + + $server = common_config('background', 'server'); + + if (empty($server)) { + $server = common_config('site', 'server'); + } + + // XXX: protocol + + return 'http://'.$server.$path.$filename; + } + + function setDisposition($on, $off, $tile) + { + if ($on) { + $this->disposition |= BACKGROUND_ON; + } else { + $this->disposition &= ~BACKGROUND_ON; + } + + if ($off) { + $this->disposition |= BACKGROUND_OFF; + } else { + $this->disposition &= ~BACKGROUND_OFF; + } + + if ($tile) { + $this->disposition |= BACKGROUND_TILE; + } else { + $this->disposition &= ~BACKGROUND_TILE; + } + } + +} diff --git a/classes/Fave.php b/classes/Fave.php index 572334ce4..f4cf6256f 100644 --- a/classes/Fave.php +++ b/classes/Fave.php @@ -37,52 +37,62 @@ class Fave extends Memcached_DataObject return Memcached_DataObject::pkeyGet('Fave', $kv); } - function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE) + function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $own=false) { $ids = Notice::stream(array('Fave', '_streamDirect'), - array($user_id), - 'fave:ids_by_user:'.$user_id, + array($user_id, $own), + ($own) ? 'fave:ids_by_user_own:'.$user_id : + 'fave:by_user:'.$user_id, $offset, $limit); return $ids; } - function _streamDirect($user_id, $offset, $limit, $since_id, $max_id, $since) + function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id, $since) { $fav = new Fave(); - - $fav->user_id = $user_id; - - $fav->selectAdd(); - $fav->selectAdd('notice_id'); + $qry = null; + + if ($own) { + $qry = 'SELECT fave.* FROM fave '; + $qry .= 'WHERE fave.user_id = ' . $user_id . ' '; + } else { + $qry = 'SELECT fave.* FROM fave '; + $qry .= 'INNER JOIN notice ON fave.notice_id = notice.id '; + $qry .= 'WHERE fave.user_id = ' . $user_id . ' '; + $qry .= 'AND notice.is_local != ' . NOTICE_GATEWAY . ' '; + } if ($since_id != 0) { - $fav->whereAdd('notice_id > ' . $since_id); + $qry .= 'AND notice_id > ' . $since_id . ' '; } if ($max_id != 0) { - $fav->whereAdd('notice_id <= ' . $max_id); + $qry .= 'AND notice_id <= ' . $max_id . ' '; } if (!is_null($since)) { - $fav->whereAdd('modified > \'' . date('Y-m-d H:i:s', $since) . '\''); + $qry .= 'AND modified > \'' . date('Y-m-d H:i:s', $since) . '\' '; } // NOTE: we sort by fave time, not by notice time! - $fav->orderBy('modified DESC'); + $qry .= 'ORDER BY modified DESC '; if (!is_null($offset)) { - $fav->limit($offset, $limit); + $qry .= "LIMIT $offset, $limit"; } + $fav->query($qry); + $ids = array(); - if ($fav->find()) { - while ($fav->fetch()) { - $ids[] = $fav->notice_id; - } + while ($fav->fetch()) { + $ids[] = $fav->notice_id; } + $fav->free(); + unset($fav); + return $ids; } } diff --git a/classes/File.php b/classes/File.php index 24ab11b8e..5dd7cd865 100644 --- a/classes/File.php +++ b/classes/File.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -30,22 +30,24 @@ require_once INSTALLDIR.'/classes/File_to_post.php'; * Table Definition for file */ -class File extends Memcached_DataObject +class File extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ public $__table = 'file'; // table name - public $id; // int(11) not_null primary_key group_by + public $id; // int(4) primary_key not_null public $url; // varchar(255) unique_key - public $mimetype; // varchar(50) - public $size; // int(11) group_by - public $title; // varchar(255) - public $date; // int(11) group_by - public $protected; // int(1) group_by + public $mimetype; // varchar(50) + public $size; // int(4) + public $title; // varchar(255) + public $date; // int(4) + public $protected; // int(4) + public $filename; // varchar(255) + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File',$k,$v); } + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE @@ -79,7 +81,6 @@ class File extends Memcached_DataObject && ('text/html' === substr($redir_data['type'], 0, 9)) && ($oembed_data = File_oembed::_getOembed($given_url)) && isset($oembed_data['json'])) { - File_oembed::saveNew($oembed_data['json'], $file_id); } return $x; @@ -90,15 +91,15 @@ class File extends Memcached_DataObject $given_url = File_redirection::_canonUrl($given_url); if (empty($given_url)) return -1; // error, no url to process $file = File::staticGet('url', $given_url); - if (empty($file->id)) { + if (empty($file)) { $file_redir = File_redirection::staticGet('url', $given_url); - if (empty($file_redir->id)) { + if (empty($file_redir)) { + common_debug("processNew() '$given_url' not a known redirect.\n"); $redir_data = File_redirection::where($given_url); $redir_url = $redir_data['url']; if ($redir_url === $given_url) { $x = File::saveNew($redir_data, $given_url); $file_id = $x->id; - } else { $x = File::processNew($redir_url, $notice_id); $file_id = $x->id; @@ -116,7 +117,7 @@ class File extends Memcached_DataObject $x = File::staticGet($file_id); if (empty($x)) die('Impossible!'); } - + File_to_post::processNew($file_id, $notice_id); return $x; } @@ -124,8 +125,8 @@ class File extends Memcached_DataObject function isRespectsQuota($user) { if ($_FILES['attach']['size'] > common_config('attachments', 'file_quota')) { return sprintf(_('No file may be larger than %d bytes ' . - 'and the file you sent was %d bytes. Try to upload a smaller version.'), - common_config('attachments', 'file_quota'), $_FILES['attach']['size']); + 'and the file you sent was %d bytes. Try to upload a smaller version.'), + common_config('attachments', 'file_quota'), $_FILES['attach']['size']); } $query = "select sum(size) as total from file join file_to_post on file_to_post.file_id = file.id join notice on file_to_post.post_id = notice.id where profile_id = {$user->id} and file.url like '%/notice/%/file'"; @@ -145,5 +146,52 @@ class File extends Memcached_DataObject } return true; } + + // where should the file go? + + static function filename($profile, $basename, $mimetype) + { + require_once 'MIME/Type/Extension.php'; + $mte = new MIME_Type_Extension(); + $ext = $mte->getExtension($mimetype); + $nickname = $profile->nickname; + $datestamp = strftime('%Y%m%dT%H%M%S', time()); + $random = strtolower(common_confirmation_code(32)); + return "$nickname-$datestamp-$random.$ext"; + } + + static function path($filename) + { + $dir = common_config('attachments', 'dir'); + + if ($dir[strlen($dir)-1] != '/') { + $dir .= '/'; + } + + return $dir . $filename; + } + + static function url($filename) + { + $path = common_config('attachments', 'path'); + + if ($path[strlen($path)-1] != '/') { + $path .= '/'; + } + + if ($path[0] != '/') { + $path = '/'.$path; + } + + $server = common_config('attachments', 'server'); + + if (empty($server)) { + $server = common_config('site', 'server'); + } + + // XXX: protocol + + return 'http://'.$server.$path.$filename; + } } diff --git a/classes/File_oembed.php b/classes/File_oembed.php index f1b2cb13c..69230e4a4 100644 --- a/classes/File_oembed.php +++ b/classes/File_oembed.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -25,35 +25,39 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; * Table Definition for file_oembed */ -class File_oembed extends Memcached_DataObject +class File_oembed extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ public $__table = 'file_oembed'; // table name - public $id; // int(11) not_null primary_key group_by - public $file_id; // int(11) unique_key group_by - public $version; // varchar(20) - public $type; // varchar(20) - public $provider; // varchar(50) - public $provider_url; // varchar(255) - public $width; // int(11) group_by - public $height; // int(11) group_by - public $html; // blob(65535) blob - public $title; // varchar(255) - public $author_name; // varchar(50) - public $author_url; // varchar(255) - public $url; // varchar(255) + public $file_id; // int(4) primary_key not_null + public $version; // varchar(20) + public $type; // varchar(20) + public $provider; // varchar(50) + public $provider_url; // varchar(255) + public $width; // int(4) + public $height; // int(4) + public $html; // text() + public $title; // varchar(255) + public $author_name; // varchar(50) + public $author_url; // varchar(255) + public $url; // varchar(255) + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_oembed',$k,$v); } + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File_oembed',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + function sequenceKey() + { + return array(false, false, false); + } function _getOembed($url, $maxwidth = 500, $maxheight = 400, $format = 'json') { - $cmd = 'http://oohembed.com/oohembed/?url=' . urlencode($url); + $cmd = common_config('oohembed', 'endpoint') . '?url=' . urlencode($url); if (is_int($maxwidth)) $cmd .= "&maxwidth=$maxwidth"; if (is_int($maxheight)) $cmd .= "&maxheight=$maxheight"; if (is_string($format)) $cmd .= "&format=$format"; @@ -84,4 +88,3 @@ class File_oembed extends Memcached_DataObject } } - diff --git a/classes/File_redirection.php b/classes/File_redirection.php index 212cc3615..d6fa0bcb6 100644 --- a/classes/File_redirection.php +++ b/classes/File_redirection.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -25,31 +25,28 @@ require_once INSTALLDIR.'/classes/File_oembed.php'; define('USER_AGENT', 'Laconica user agent / file probe'); - /** * Table Definition for file_redirection */ -class File_redirection extends Memcached_DataObject +class File_redirection extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ public $__table = 'file_redirection'; // table name - public $id; // int(11) not_null primary_key group_by - public $url; // varchar(255) unique_key - public $file_id; // int(11) group_by - public $redirections; // int(11) group_by - public $httpcode; // int(11) group_by + public $url; // varchar(255) primary_key not_null + public $file_id; // int(4) + public $redirections; // int(4) + public $httpcode; // int(4) + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_redirection',$k,$v); } + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File_redirection',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - - function _commonCurl($url, $redirs) { $curlh = curl_init(); curl_setopt($curlh, CURLOPT_URL, $url); @@ -69,24 +66,18 @@ class File_redirection extends Memcached_DataObject // let's see if we know this... $a = File::staticGet('url', $short_url); - if (empty($a->id)) { + + if (!empty($a)) { + // this is a direct link to $a->url + return $a->url; + } else { $b = File_redirection::staticGet('url', $short_url); - if (empty($b->id)) { - // we'll have to figure it out - } else { + if (!empty($b)) { // this is a redirect to $b->file_id - $a = File::staticGet($b->file_id); - $url = $a->url; + $a = File::staticGet('id', $b->file_id); + return $a->url; } - } else { - // this is a direct link to $a->url - $url = $a->url; } - if (isset($url)) { - return $url; - } - - $curlh = File_redirection::_commonCurl($short_url, $redirs); // Don't include body in output @@ -123,83 +114,22 @@ class File_redirection extends Memcached_DataObject } function makeShort($long_url) { - $long_url = File_redirection::_canonUrl($long_url); - // do we already know this long_url and have a short redirection for it? - $file = new File; - $file_redir = new File_redirection; - $file->url = $long_url; - $file->joinAdd($file_redir); - $file->selectAdd('length(file_redirection.url) as len'); - $file->limit(1); - $file->orderBy('len'); - $file->find(true); - if (!empty($file->url) && (strlen($file->url) < strlen($long_url))) { - return $file->url; - } - - // if yet unknown, we must find a short url according to user settings - $short_url = File_redirection::_userMakeShort($long_url, common_current_user()); - return $short_url; - } - - function _userMakeShort($long_url, $user) { - if (empty($user)) { - // common current user does not find a user when called from the XMPP daemon - // therefore we'll set one here fix, so that XMPP given URLs may be shortened - $user->urlshorteningservice = 'ur1.ca'; - } - $curlh = curl_init(); - curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait - curl_setopt($curlh, CURLOPT_USERAGENT, 'Laconica'); - curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true); - switch($user->urlshorteningservice) { - case 'ur1.ca': - require_once INSTALLDIR.'/lib/Shorturl_api.php'; - $short_url_service = new LilUrl; - $short_url = $short_url_service->shorten($long_url); - break; + $canon = File_redirection::_canonUrl($long_url); - case '2tu.us': - $short_url_service = new TightUrl; - require_once INSTALLDIR.'/lib/Shorturl_api.php'; - $short_url = $short_url_service->shorten($long_url); - break; + $short_url = File_redirection::_userMakeShort($canon); - case 'ptiturl.com': - require_once INSTALLDIR.'/lib/Shorturl_api.php'; - $short_url_service = new PtitUrl; - $short_url = $short_url_service->shorten($long_url); - break; - - case 'bit.ly': - curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($long_url)); - $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl; - break; - - case 'is.gd': - curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($long_url)); - $short_url = curl_exec($curlh); - break; - case 'snipr.com': - curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($long_url)); - $short_url = curl_exec($curlh); - break; - case 'metamark.net': - curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($long_url)); - $short_url = curl_exec($curlh); - break; - case 'tinyurl.com': - curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($long_url)); - $short_url = curl_exec($curlh); - break; - default: - $short_url = false; + // Did we get one? Is it shorter? + if (!empty($short_url) && mb_strlen($short_url) < mb_strlen($long_url)) { + return $short_url; + } else { + return $long_url; } + } - curl_close($curlh); - - if ($short_url) { + function _userMakeShort($long_url) { + $short_url = common_shorten_url($long_url); + if (!empty($short_url) && $short_url != $long_url) { $short_url = (string)$short_url; // store it $file = File::staticGet('url', $long_url); @@ -222,7 +152,7 @@ class File_redirection extends Memcached_DataObject } return $short_url; } - return $long_url; + return null; } function _canonUrl($in_url, $default_scheme = 'http://') { diff --git a/classes/File_thumbnail.php b/classes/File_thumbnail.php index 1a65b92c9..44b92a2fa 100644 --- a/classes/File_thumbnail.php +++ b/classes/File_thumbnail.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -25,24 +25,29 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; * Table Definition for file_thumbnail */ -class File_thumbnail extends Memcached_DataObject +class File_thumbnail extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ public $__table = 'file_thumbnail'; // table name - public $id; // int(11) not_null primary_key group_by - public $file_id; // int(11) unique_key group_by + public $file_id; // int(4) primary_key not_null public $url; // varchar(255) unique_key - public $width; // int(11) group_by - public $height; // int(11) group_by + public $width; // int(4) + public $height; // int(4) + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_thumbnail',$k,$v); } + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File_thumbnail',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + function sequenceKey() + { + return array(false, false, false); + } + function saveNew($data, $file_id) { $tn = new File_thumbnail; $tn->file_id = $file_id; diff --git a/classes/File_to_post.php b/classes/File_to_post.php index 00ddebe6b..d35febb77 100644 --- a/classes/File_to_post.php +++ b/classes/File_to_post.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -25,18 +25,18 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; * Table Definition for file_to_post */ -class File_to_post extends Memcached_DataObject +class File_to_post extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ public $__table = 'file_to_post'; // table name - public $id; // int(11) not_null primary_key group_by - public $file_id; // int(11) multiple_key group_by - public $post_id; // int(11) group_by + public $file_id; // int(4) primary_key not_null + public $post_id; // int(4) primary_key not_null + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_to_post',$k,$v); } + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File_to_post',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE @@ -44,17 +44,27 @@ class File_to_post extends Memcached_DataObject function processNew($file_id, $notice_id) { static $seen = array(); if (empty($seen[$notice_id]) || !in_array($file_id, $seen[$notice_id])) { - $f2p = new File_to_post; - $f2p->file_id = $file_id; - $f2p->post_id = $notice_id; - $f2p->insert(); + + $f2p = File_to_post::pkeyGet(array('post_id' => $notice_id, + 'file_id' => $file_id)); + if (empty($f2p)) { + $f2p = new File_to_post; + $f2p->file_id = $file_id; + $f2p->post_id = $notice_id; + $f2p->insert(); + } + if (empty($seen[$notice_id])) { $seen[$notice_id] = array($file_id); } else { $seen[$notice_id][] = $file_id; } } + } + function &pkeyGet($kv) + { + return Memcached_DataObject::pkeyGet('File_to_post', $kv); } } diff --git a/classes/Foreign_user.php b/classes/Foreign_user.php index 61727abe5..8b3e03dfb 100644 --- a/classes/Foreign_user.php +++ b/classes/Foreign_user.php @@ -4,42 +4,41 @@ */ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -class Foreign_user extends Memcached_DataObject +class Foreign_user extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ public $__table = 'foreign_user'; // table name - public $id; // int(4) primary_key not_null + public $id; // bigint(8) primary_key not_null public $service; // int(4) primary_key not_null public $uri; // varchar(255) unique_key not_null - public $nickname; // varchar(255) + public $nickname; // varchar(255) public $created; // datetime() not_null public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=null) - { return Memcached_DataObject::staticGet('Foreign_user',$k,$v); } + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Foreign_user',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - + // XXX: This only returns a 1->1 single obj mapping. Change? Or make // a getForeignUsers() that returns more than one? --Zach - static function getForeignUser($id, $service) { + static function getForeignUser($id, $service) { $fuser = new Foreign_user(); $fuser->whereAdd("service = $service"); $fuser->whereAdd("id = $id"); $fuser->limit(1); - + if ($fuser->find()) { $fuser->fetch(); return $fuser; } - - return null; + + return null; } - + function updateKeys(&$orig) { $parts = array(); @@ -68,5 +67,4 @@ class Foreign_user extends Memcached_DataObject return $result; } - } diff --git a/classes/Group_alias.php b/classes/Group_alias.php new file mode 100644 index 000000000..e801e50e1 --- /dev/null +++ b/classes/Group_alias.php @@ -0,0 +1,41 @@ +<?php +/** + * Table Definition for group_alias + * + * Laconica - a distributed open-source microblogging tool + * Copyright (C) 2009, Control Yourself, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('LACONICA')) { exit(1); } + +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + +class Group_alias extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'group_alias'; // table name + public $alias; // varchar(64) primary_key not_null + public $group_id; // int(4) not_null + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + + /* Static get */ + function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('Group_alias',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE +} diff --git a/classes/Group_block.php b/classes/Group_block.php new file mode 100644 index 000000000..7922c19a9 --- /dev/null +++ b/classes/Group_block.php @@ -0,0 +1,115 @@ +<?php +/** + * Table Definition for group_block + * + * Laconica - a distributed open-source microblogging tool + * Copyright (C) 2008, 2009, Control Yourself, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('LACONICA')) { exit(1); } + +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + +class Group_block extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'group_block'; // table name + public $group_id; // int(4) primary_key not_null + public $blocked; // int(4) primary_key not_null + public $blocker; // int(4) not_null + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + + /* Static get */ + function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('Group_block',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + function &pkeyGet($kv) + { + return Memcached_DataObject::pkeyGet('Group_block', $kv); + } + + static function isBlocked($group, $profile) + { + $block = Group_block::pkeyGet(array('group_id' => $group->id, + 'blocked' => $profile->id)); + return !empty($block); + } + + static function blockProfile($group, $profile, $blocker) + { + // Insert the block + + $block = new Group_block(); + + $block->query('BEGIN'); + + $block->group_id = $group->id; + $block->blocked = $profile->id; + $block->blocker = $blocker->id; + + $result = $block->insert(); + + if (!$result) { + common_log_db_error($block, 'INSERT', __FILE__); + return null; + } + + // Delete membership if any + + $member = new Group_member(); + + $member->group_id = $group->id; + $member->profile_id = $profile->id; + + if ($member->find(true)) { + $result = $member->delete(); + if (!$result) { + common_log_db_error($member, 'DELETE', __FILE__); + return null; + } + } + + // Commit, since both have been done + + $block->query('COMMIT'); + + return $block; + } + + static function unblockProfile($group, $profile) + { + $block = Group_block::pkeyGet(array('group_id' => $group->id, + 'blocked' => $profile->id)); + + if (empty($block)) { + return null; + } + + $result = $block->delete(); + + if (!$result) { + common_log_db_error($block, 'DELETE', __FILE__); + return null; + } + + return true; + } + +} diff --git a/classes/Group_inbox.php b/classes/Group_inbox.php index b80ba4272..1af7439f7 100644 --- a/classes/Group_inbox.php +++ b/classes/Group_inbox.php @@ -14,8 +14,14 @@ class Group_inbox extends Memcached_DataObject public $created; // datetime() not_null /* Static get */ + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Group_inbox',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + + function &pkeyGet($kv) + { + return Memcached_DataObject::pkeyGet('Group_inbox', $kv); + } } diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 33ac70dd0..f7cbb9d5b 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -193,7 +193,14 @@ class Memcached_DataObject extends DB_DataObject // unable to connect to sphinx' search daemon if (!$connected) { if ('mysql' === common_config('db', 'type')) { - $search_engine = new MySQLSearch($this, $table); + $type = common_config('search', 'type'); + if ($type == 'like') { + $search_engine = new MySQLLikeSearch($this, $table); + } else if ($type == 'fulltext') { + $search_engine = new MySQLSearch($this, $table); + } else { + throw new ServerException('Unknown search type: ' . $type); + } } else { $search_engine = new PGSearch($this, $table); } @@ -242,13 +249,16 @@ class Memcached_DataObject extends DB_DataObject if (common_config('db', 'type') == 'mysql' && common_config('db', 'utf8')) { $conn = $DB->connection; - if ($DB instanceof DB_mysqli) { - mysqli_set_charset($conn, 'utf8'); - } else if ($DB instanceof DB_mysql) { - mysql_set_charset('utf8', $conn); + if (!empty($conn)) { + if ($DB instanceof DB_mysqli) { + mysqli_set_charset($conn, 'utf8'); + } else if ($DB instanceof DB_mysql) { + mysql_set_charset('utf8', $conn); + } } } } return $result; } + } diff --git a/classes/Notice.php b/classes/Notice.php index 1c4858149..fdcef1bc2 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -29,6 +29,11 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; define('NOTICE_CACHE_WINDOW', 61); +define('NOTICE_LOCAL_PUBLIC', 1); +define('NOTICE_REMOTE_OMB', 0); +define('NOTICE_LOCAL_NONPUBLIC', -1); +define('NOTICE_GATEWAY', -2); + class Notice extends Memcached_DataObject { ###START_AUTOCODE @@ -125,7 +130,12 @@ class Notice extends Memcached_DataObject $profile = Profile::staticGet($profile_id); - $final = common_shorten_links($content); + $final = common_shorten_links($content); + + if (mb_strlen($final) > 140) { + common_log(LOG_INFO, 'Rejecting notice that is too long.'); + return _('Problem saving notice. Too long.'); + } if (!$profile) { common_log(LOG_ERR, 'Problem saving notice. Unknown user.'); @@ -212,6 +222,13 @@ class Notice extends Memcached_DataObject $notice->addToInboxes(); $notice->saveGroups(); + $notice->saveUrls(); + $orig2 = clone($notice); + $notice->rendered = common_render_content($final, $notice); + if (!$notice->update($orig2)) { + common_log_db_error($notice, 'UPDATE', __FILE__); + return _('Problem saving notice.'); + } $notice->query('COMMIT'); @@ -226,6 +243,22 @@ class Notice extends Memcached_DataObject return $notice; } + /** save all urls in the notice to the db + * + * follow redirects and save all available file information + * (mimetype, date, size, oembed, etc.) + * + * @return void + */ + function saveUrls() { + common_replace_urls_callback($this->content, array($this, 'saveUrl'), $this->id); + } + + function saveUrl($data) { + list($url, $notice_id) = $data; + File::processNew($url, $notice_id); + } + static function checkDupes($profile_id, $content) { $profile = Profile::staticGet($profile_id); if (!$profile) { @@ -298,6 +331,20 @@ class Notice extends Memcached_DataObject return $n_attachments; } + function attachments() { + // XXX: cache this + $att = array(); + $f2p = new File_to_post; + $f2p->post_id = $this->id; + if ($f2p->find()) { + while ($f2p->fetch()) { + $f = File::staticGet($f2p->file_id); + $att[] = clone($f); + } + } + return $att; + } + function blowCaches($blowLast=false) { $this->blowSubsCache($blowLast); @@ -306,6 +353,19 @@ class Notice extends Memcached_DataObject $this->blowPublicCache($blowLast); $this->blowTagCache($blowLast); $this->blowGroupCache($blowLast); + $this->blowConversationCache($blowLast); + } + + function blowConversationCache($blowLast=false) + { + $cache = common_memcache(); + if ($cache) { + $ck = common_cache_key('notice:conversation_ids:'.$this->conversation); + $cache->delete($ck); + if ($blowLast) { + $cache->delete($ck.';last'); + } + } } function blowGroupCache($blowLast=false) @@ -346,6 +406,12 @@ class Notice extends Memcached_DataObject if ($tag->find()) { while ($tag->fetch()) { $tag->blowCache($blowLast); + $ck = 'profile:notice_ids_tagged:' . $this->profile_id . ':' . $tag->tag; + + $cache->delete($ck); + if ($blowLast) { + $cache->delete($ck . ';last'); + } } } $tag->free(); @@ -367,8 +433,10 @@ class Notice extends Memcached_DataObject while ($user->fetch()) { $cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id)); + $cache->delete(common_cache_key('notice_inbox:by_user_own:'.$user->id)); if ($blowLast) { $cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id.';last')); + $cache->delete(common_cache_key('notice_inbox:by_user_own:'.$user->id.';last')); } } $user->free(); @@ -430,8 +498,10 @@ class Notice extends Memcached_DataObject if ($fave->find()) { while ($fave->fetch()) { $cache->delete(common_cache_key('fave:ids_by_user:'.$fave->user_id)); + $cache->delete(common_cache_key('fave:by_user_own:'.$fave->user_id)); if ($blowLast) { $cache->delete(common_cache_key('fave:ids_by_user:'.$fave->user_id.';last')); + $cache->delete(common_cache_key('fave:by_user_own:'.$fave->user_id.';last')); } } } @@ -634,7 +704,10 @@ class Notice extends Memcached_DataObject if (!empty($cache)) { $notices = array(); foreach ($ids as $id) { - $notices[] = Notice::staticGet('id', $id); + $n = Notice::staticGet('id', $id); + if (!empty($n)) { + $notices[] = $n; + } } return new ArrayWrapper($notices); } else { @@ -703,29 +776,116 @@ class Notice extends Memcached_DataObject return $ids; } + function conversationStream($id, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null) + { + $ids = Notice::stream(array('Notice', '_conversationStreamDirect'), + array($id), + 'notice:conversation_ids:'.$id, + $offset, $limit, $since_id, $max_id, $since); + + return Notice::getStreamByIds($ids); + } + + function _conversationStreamDirect($id, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null) + { + $notice = new Notice(); + + $notice->selectAdd(); // clears it + $notice->selectAdd('id'); + + $notice->whereAdd('conversation = '.$id); + + $notice->orderBy('id DESC'); + + if (!is_null($offset)) { + $notice->limit($offset, $limit); + } + + if ($since_id != 0) { + $notice->whereAdd('id > ' . $since_id); + } + + if ($max_id != 0) { + $notice->whereAdd('id <= ' . $max_id); + } + + if (!is_null($since)) { + $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\''); + } + + $ids = array(); + + if ($notice->find()) { + while ($notice->fetch()) { + $ids[] = $notice->id; + } + } + + $notice->free(); + $notice = NULL; + + return $ids; + } + function addToInboxes() { $enabled = common_config('inboxes', 'enabled'); if ($enabled === true || $enabled === 'transitional') { - $inbox = new Notice_inbox(); - $UT = common_config('db','type')=='pgsql'?'"user"':'user'; - $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created) ' . - "SELECT $UT.id, " . $this->id . ", '" . $this->created . "' " . - "FROM $UT JOIN subscription ON $UT.id = subscription.subscriber " . - 'WHERE subscription.subscribed = ' . $this->profile_id . ' ' . - 'AND NOT EXISTS (SELECT user_id, notice_id ' . - 'FROM notice_inbox ' . - "WHERE user_id = $UT.id " . - 'AND notice_id = ' . $this->id . ' )'; - if ($enabled === 'transitional') { - $qry .= " AND $UT.inboxed = 1"; + + $users = $this->getSubscribedUsers(); + + // FIXME: kind of ignoring 'transitional'... + // we'll probably stop supporting inboxless mode + // in 0.9.x + + foreach ($users as $id) { + $this->addToUserInbox($id, NOTICE_INBOX_SOURCE_SUB); } - $inbox->query($qry); } + return; } + function getSubscribedUsers() + { + $user = new User(); + + $qry = + 'SELECT id ' . + 'FROM user JOIN subscription '. + 'ON user.id = subscription.subscriber ' . + 'WHERE subscription.subscribed = %d '; + + $user->query(sprintf($qry, $this->profile_id)); + + $ids = array(); + + while ($user->fetch()) { + $ids[] = $user->id; + } + + $user->free(); + + return $ids; + } + + function addToUserInbox($user_id, $source) + { + $inbox = Notice_inbox::pkeyGet(array('user_id' => $user_id, + 'notice_id' => $this->id)); + if (empty($inbox)) { + $inbox = new Notice_inbox(); + $inbox->user_id = $user_id; + $inbox->notice_id = $this->id; + $inbox->source = $source; + $inbox->created = $this->created; + return $inbox->insert(); + } + + return true; + } + function saveGroups() { $enabled = common_config('inboxes', 'enabled'); @@ -747,16 +907,16 @@ class Notice extends Memcached_DataObject foreach (array_unique($match[1]) as $nickname) { /* XXX: remote groups. */ - $group = User_group::staticGet('nickname', $nickname); + $group = User_group::getForNickname($nickname); - if (!$group) { + if (empty($group)) { continue; } // we automatically add a tag for every group name, too $tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($nickname), - 'notice_id' => $this->id)); + 'notice_id' => $this->id)); if (is_null($tag)) { $this->saveTag($nickname); @@ -764,13 +924,7 @@ class Notice extends Memcached_DataObject if ($profile->isMember($group)) { - $gi = new Group_inbox(); - - $gi->group_id = $group->id; - $gi->notice_id = $this->id; - $gi->created = common_sql_now(); - - $result = $gi->insert(); + $result = $this->addToGroupInbox($group); if (!$result) { common_log_db_error($gi, 'INSERT', __FILE__); @@ -778,27 +932,37 @@ class Notice extends Memcached_DataObject // FIXME: do this in an offline daemon - $this->addToGroupInboxes($group); + $this->addToGroupMemberInboxes($group); } } } - function addToGroupInboxes($group) + function addToGroupInbox($group) { - $inbox = new Notice_inbox(); - $UT = common_config('db','type')=='pgsql'?'"user"':'user'; - $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created, source) ' . - "SELECT $UT.id, " . $this->id . ", '" . $this->created . "', 2 " . - "FROM $UT JOIN group_member ON $UT.id = group_member.profile_id " . - 'WHERE group_member.group_id = ' . $group->id . ' ' . - 'AND NOT EXISTS (SELECT user_id, notice_id ' . - 'FROM notice_inbox ' . - "WHERE user_id = $UT.id " . - 'AND notice_id = ' . $this->id . ' )'; - if ($enabled === 'transitional') { - $qry .= " AND $UT.inboxed = 1"; - } - $result = $inbox->query($qry); + $gi = Group_inbox::pkeyGet(array('group_id' => $group->id, + 'notice_id' => $this->id)); + + if (empty($gi)) { + + $gi = new Group_inbox(); + + $gi->group_id = $group->id; + $gi->notice_id = $this->id; + $gi->created = $this->created; + + return $gi->insert(); + } + + return true; + } + + function addToGroupMemberInboxes($group) + { + $users = $group->getUserMembers(); + + foreach ($users as $id) { + $this->addToUserInbox($id, NOTICE_INBOX_SOURCE_GROUP); + } } function saveReplies() diff --git a/classes/Notice_inbox.php b/classes/Notice_inbox.php index 673e187c7..940381f84 100644 --- a/classes/Notice_inbox.php +++ b/classes/Notice_inbox.php @@ -25,6 +25,11 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; define('INBOX_CACHE_WINDOW', 101); +define('NOTICE_INBOX_SOURCE_SUB', 1); +define('NOTICE_INBOX_SOURCE_GROUP', 2); +define('NOTICE_INBOX_SOURCE_REPLY', 3); +define('NOTICE_INBOX_SOURCE_GATEWAY', -1); + class Notice_inbox extends Memcached_DataObject { ###START_AUTOCODE @@ -43,20 +48,25 @@ class Notice_inbox extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function stream($user_id, $offset, $limit, $since_id, $max_id, $since) + function stream($user_id, $offset, $limit, $since_id, $max_id, $since, $own=false) { return Notice::stream(array('Notice_inbox', '_streamDirect'), - array($user_id), - 'notice_inbox:by_user:'.$user_id, + array($user_id, $own), + ($own) ? 'notice_inbox:by_user:'.$user_id : + 'notice_inbox:by_user_own:'.$user_id, $offset, $limit, $since_id, $max_id, $since); } - function _streamDirect($user_id, $offset, $limit, $since_id, $max_id, $since) + function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id, $since) { $inbox = new Notice_inbox(); $inbox->user_id = $user_id; + if (!$own) { + $inbox->whereAdd('source != ' . NOTICE_INBOX_SOURCE_GATEWAY); + } + if ($since_id != 0) { $inbox->whereAdd('notice_id > ' . $since_id); } diff --git a/classes/Notice_tag.php b/classes/Notice_tag.php index 758a66594..4e52ef269 100644 --- a/classes/Notice_tag.php +++ b/classes/Notice_tag.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/classes/Profile.php b/classes/Profile.php index 4a459b974..a0ed6b3ca 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -153,18 +153,16 @@ class Profile extends Memcached_DataObject return null; } - function getTaggedNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null, $tag=null) + function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0, $since=null) { - // XXX: I'm not sure this is going to be any faster. It probably isn't. $ids = Notice::stream(array($this, '_streamTaggedDirect'), - array(), - 'profile:notice_ids:' . $this->id, - $offset, $limit, $since_id, $before_id, $since, $tag); - common_debug(print_r($ids, true)); + array($tag), + 'profile:notice_ids_tagged:' . $this->id . ':' . $tag, + $offset, $limit, $since_id, $max_id, $since); return Notice::getStreamByIds($ids); } - function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0, $since=null) { // XXX: I'm not sure this is going to be any faster. It probably isn't. $ids = Notice::stream(array($this, '_streamDirect'), @@ -175,18 +173,23 @@ class Profile extends Memcached_DataObject return Notice::getStreamByIds($ids); } - function _streamTaggedDirect($offset, $limit, $since_id, $before_id, $since=null, $tag=null) + function _streamTaggedDirect($tag, $offset, $limit, $since_id, $max_id, $since) { - common_debug('_streamTaggedDirect()'); + // XXX It would be nice to do this without a join + $notice = new Notice(); - $notice->profile_id = $this->id; - $query = "select id from notice join notice_tag on id=notice_id where tag='" . $notice->escape($tag) . "' and profile_id=" . $notice->escape($notice->profile_id); + + $query = + "select id from notice join notice_tag on id=notice_id where tag='". + $notice->escape($tag) . + "' and profile_id=" . $notice->escape($this->id); + if ($since_id != 0) { $query .= " and id > $since_id"; } - if ($before_id != 0) { - $query .= " and id < $before_id"; + if ($max_id != 0) { + $query .= " and id < $max_id"; } if (!is_null($since)) { @@ -198,21 +201,19 @@ class Profile extends Memcached_DataObject if (!is_null($offset)) { $query .= " limit $offset, $limit"; } + $notice->query($query); + $ids = array(); while ($notice->fetch()) { - common_debug(print_r($notice, true)); $ids[] = $notice->id; } return $ids; } - - - - function _streamDirect($offset, $limit, $since_id, $before_id, $since = null) + function _streamDirect($offset, $limit, $since_id, $max_id, $since = null) { $notice = new Notice(); @@ -288,4 +289,52 @@ class Profile extends Memcached_DataObject return Avatar::defaultImage($size); } } + + function getSubscriptions($offset=0, $limit=null) + { + $qry = + 'SELECT profile.* ' . + 'FROM profile JOIN subscription ' . + 'ON profile.id = subscription.subscribed ' . + 'WHERE subscription.subscriber = %d ' . + 'AND subscription.subscribed != subscription.subscriber ' . + 'ORDER BY subscription.created DESC '; + + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + + $profile = new Profile(); + + $profile->query(sprintf($qry, $this->id)); + + return $profile; + } + + function getSubscribers($offset=0, $limit=null) + { + $qry = + 'SELECT profile.* ' . + 'FROM profile JOIN subscription ' . + 'ON profile.id = subscription.subscriber ' . + 'WHERE subscription.subscribed = %d ' . + 'AND subscription.subscribed != subscription.subscriber ' . + 'ORDER BY subscription.created DESC '; + + if ($offset) { + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + } + + $profile = new Profile(); + + $cnt = $profile->query(sprintf($qry, $this->id)); + + return $profile; + } } diff --git a/classes/Profile_block.php b/classes/Profile_block.php index 551e690e2..feadea42d 100644 --- a/classes/Profile_block.php +++ b/classes/Profile_block.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/classes/Remote_profile.php b/classes/Remote_profile.php index 5aa6d913e..975852dd9 100644 --- a/classes/Remote_profile.php +++ b/classes/Remote_profile.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/classes/Status_network.php b/classes/Status_network.php index f7747f71d..f8d6756b6 100644 --- a/classes/Status_network.php +++ b/classes/Status_network.php @@ -1,8 +1,26 @@ <?php /** * Table Definition for status_network + * + * Laconica - a distributed open-source microblogging tool + * Copyright (C) 2009, Control Yourself, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +if (!defined('LACONICA')) { exit(1); } + class Status_network extends DB_DataObject { ###START_AUTOCODE @@ -12,11 +30,13 @@ class Status_network extends DB_DataObject public $nickname; // varchar(64) primary_key not_null public $hostname; // varchar(255) unique_key public $pathname; // varchar(255) unique_key - public $sitename; // varchar(255) public $dbhost; // varchar(255) public $dbuser; // varchar(255) public $dbpass; // varchar(255) public $dbname; // varchar(255) + public $sitename; // varchar(255) + public $theme; // varchar(255) + public $logo; // varchar(255) public $created; // datetime() not_null public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP @@ -26,7 +46,10 @@ class Status_network extends DB_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - static function setupDB($dbhost, $dbuser, $dbpass, $dbname) + static $cache = null; + static $base = null; + + static function setupDB($dbhost, $dbuser, $dbpass, $dbname, $servers) { global $config; @@ -34,28 +57,134 @@ class Status_network extends DB_DataObject $config['db']['ini_'.$dbname] = INSTALLDIR.'/classes/statusnet.ini'; $config['db']['table_status_network'] = $dbname; - return true; + self::$cache = new Memcache(); + + if (is_array($servers)) { + foreach($servers as $server) { + self::$cache->addServer($server); + } + } else { + self::$cache->addServer($servers); + } + + self::$base = $dbname; + } + + static function cacheKey($k, $v) { + return 'laconica:' . self::$base . ':status_network:'.$k.':'.$v; + } + + static function memGet($k, $v) + { + $ck = self::cacheKey($k, $v); + + $sn = self::$cache->get($ck); + + if (empty($sn)) { + $sn = self::staticGet($k, $v); + if (!empty($sn)) { + self::$cache->set($ck, $sn); + } + } + + return $sn; } - static function setupSite($servername, $pathname) + function decache() + { + $keys = array('nickname', 'hostname', 'pathname'); + foreach ($keys as $k) { + $ck = self::cacheKey($k, $this->$k); + self::$cache->delete($ck); + } + } + + function update($orig=null) + { + if (is_object($orig)) { + $orig->decache(); # might be different keys + } + return parent::update($orig); + } + + function delete() + { + $this->decache(); # while we still have the values! + return parent::delete(); + } + + static function setupSite($servername, $pathname, $wildcard) { global $config; - $parts = explode('.', $servername); + $sn = null; - $sn = Status_network::staticGet('nickname', $parts[0]); + // XXX I18N, probably not crucial for hostnames + // XXX This probably needs a tune up + + if (0 == strncasecmp(strrev($wildcard), strrev($servername), strlen($wildcard))) { + // special case for exact match + if (0 == strcasecmp($servername, $wildcard)) { + $sn = self::memGet('nickname', ''); + } else { + $parts = explode('.', $servername); + $sn = self::memGet('nickname', strtolower($parts[0])); + } + } else { + $sn = self::memGet('hostname', strtolower($servername)); + } if (!empty($sn)) { + if (!empty($sn->hostname) && 0 != strcasecmp($sn->hostname, $servername)) { + $sn->redirectToHostname(); + } $dbhost = (empty($sn->dbhost)) ? 'localhost' : $sn->dbhost; $dbuser = (empty($sn->dbuser)) ? $sn->nickname : $sn->dbuser; $dbpass = $sn->dbpass; $dbname = (empty($sn->dbname)) ? $sn->nickname : $sn->dbname; $config['db']['database'] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname"; + $config['site']['name'] = $sn->sitename; - return true; + + if (!empty($sn->theme)) { + $config['site']['theme'] = $sn->theme; + } + if (!empty($sn->logo)) { + $config['site']['logo'] = $sn->logo; + } + + return $sn; } else { + return null; + } + } + + // Code partially mooked from http://www.richler.de/en/php-redirect/ + // (C) 2006 by Heiko Richler http://www.richler.de/ + // LGPL + + function redirectToHostname() + { + $destination = 'http://'.$this->hostname; + $destination .= $_SERVER['REQUEST_URI']; + + $old = 'http'. + (($_SERVER['HTTPS'] == 'on') ? 'S' : ''). + '://'. + $_SERVER['HTTP_HOST']. + $_SERVER['REQUEST_URI']. + $_SERVER['QUERY_STRING']; + if ($old == $destination) { // this would be a loop! + // error_log(...) ? return false; } + + header('HTTP/1.1 301 Moved Permanently'); + header("Location: $destination"); + + print "<a href='$destination'>$destination</a>\n"; + + exit; } } diff --git a/classes/Subscription.php b/classes/Subscription.php index 3fe0d167f..d4580fcba 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -1,7 +1,7 @@ <?php /* * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Copyright (C) 2008, 2009, Control Yourself, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/classes/User.php b/classes/User.php index 08a166d5a..62a3f8a66 100644 --- a/classes/User.php +++ b/classes/User.php @@ -62,14 +62,13 @@ class User extends Memcached_DataObject public $autosubscribe; // tinyint(1) public $urlshorteningservice; // varchar(50) default_ur1.ca public $inboxed; // tinyint(1) + public $design_id; // int(4) + public $viewdesigns; // tinyint(1) default_1 public $created; // datetime() not_null public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) - { - return Memcached_DataObject::staticGet('User',$k,$v); - } + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE @@ -425,9 +424,9 @@ class User extends Memcached_DataObject } } - function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE) + function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE, $own=false) { - $ids = Fave::stream($this->id, $offset, $limit); + $ids = Fave::stream($this->id, $offset, $limit, $own); return Notice::getStreamByIds($ids); } @@ -443,6 +442,33 @@ class User extends Memcached_DataObject $qry = 'SELECT notice.* ' . 'FROM notice JOIN subscription ON notice.profile_id = subscription.subscribed ' . + 'WHERE subscription.subscriber = %d ' . + 'AND notice.is_local != ' . NOTICE_GATEWAY; + return Notice::getStream(sprintf($qry, $this->id), + 'user:notices_with_friends:' . $this->id, + $offset, $limit, $since_id, $before_id, + $order, $since); + } else if ($enabled === true || + ($enabled == 'transitional' && $this->inboxed == 1)) { + + $ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, false); + + return Notice::getStreamByIds($ids); + } + } + + function noticeInbox($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + { + $enabled = common_config('inboxes', 'enabled'); + + // Complicated code, depending on whether we support inboxes yet + // XXX: make this go away when inboxes become mandatory + + if ($enabled === false || + ($enabled == 'transitional' && $this->inboxed == 0)) { + $qry = + 'SELECT notice.* ' . + 'FROM notice JOIN subscription ON notice.profile_id = subscription.subscribed ' . 'WHERE subscription.subscriber = %d '; return Notice::getStream(sprintf($qry, $this->id), 'user:notices_with_friends:' . $this->id, @@ -451,7 +477,7 @@ class User extends Memcached_DataObject } else if ($enabled === true || ($enabled == 'transitional' && $this->inboxed == 1)) { - $ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since); + $ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, true); return Notice::getStreamByIds($ids); } @@ -574,50 +600,16 @@ class User extends Memcached_DataObject function getSubscriptions($offset=0, $limit=null) { - $qry = - 'SELECT profile.* ' . - 'FROM profile JOIN subscription ' . - 'ON profile.id = subscription.subscribed ' . - 'WHERE subscription.subscriber = %d ' . - 'AND subscription.subscribed != subscription.subscriber ' . - 'ORDER BY subscription.created DESC '; - - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } - - $profile = new Profile(); - - $profile->query(sprintf($qry, $this->id)); - - return $profile; + $profile = $this->getProfile(); + assert(!empty($profile)); + return $profile->getSubscriptions($offset, $limit); } function getSubscribers($offset=0, $limit=null) { - $qry = - 'SELECT profile.* ' . - 'FROM profile JOIN subscription ' . - 'ON profile.id = subscription.subscriber ' . - 'WHERE subscription.subscribed = %d ' . - 'AND subscription.subscribed != subscription.subscriber ' . - 'ORDER BY subscription.created DESC '; - - if ($offset) { - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } - } - - $profile = new Profile(); - - $cnt = $profile->query(sprintf($qry, $this->id)); - - return $profile; + $profile = $this->getProfile(); + assert(!empty($profile)); + return $profile->getSubscribers($offset, $limit); } function getTaggedSubscribers($tag, $offset=0, $limit=null) @@ -684,4 +676,9 @@ class User extends Memcached_DataObject return ($cnt > 0); } + + function getDesign() + { + return Design::staticGet('id', $this->design_id); + } } diff --git a/classes/User_group.php b/classes/User_group.php index a135015ba..9b4b01ead 100644 --- a/classes/User_group.php +++ b/classes/User_group.php @@ -19,6 +19,7 @@ class User_group extends Memcached_DataObject public $homepage_logo; // varchar(255) public $stream_logo; // varchar(255) public $mini_logo; // varchar(255) + public $design_id; // int(4) public $created; // datetime() not_null public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP @@ -125,6 +126,29 @@ class User_group extends Memcached_DataObject return $members; } + function getBlocked($offset=0, $limit=null) + { + $qry = + 'SELECT profile.* ' . + 'FROM profile JOIN group_block '. + 'ON profile.id = group_block.blocked ' . + 'WHERE group_block.group_id = %d ' . + 'ORDER BY group_block.modified DESC '; + + if ($limit != null) { + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + } + + $blocked = new Profile(); + + $blocked->query(sprintf($qry, $this->id)); + return $blocked; + } + function setOriginal($filename) { $imagefile = new ImageFile($this->id, Avatar::path($filename)); @@ -137,4 +161,113 @@ class User_group extends Memcached_DataObject common_debug(common_log_objstring($this)); return $this->update($orig); } + + function getBestName() + { + return ($this->fullname) ? $this->fullname : $this->nickname; + } + + function getAliases() + { + $aliases = array(); + + // XXX: cache this + + $alias = new Group_alias(); + + $alias->group_id = $this->id; + + if ($alias->find()) { + while ($alias->fetch()) { + $aliases[] = $alias->alias; + } + } + + $alias->free(); + + return $aliases; + } + + function setAliases($newaliases) { + + $newaliases = array_unique($newaliases); + + $oldaliases = $this->getAliases(); + + # Delete stuff that's old that not in new + + $to_delete = array_diff($oldaliases, $newaliases); + + # Insert stuff that's in new and not in old + + $to_insert = array_diff($newaliases, $oldaliases); + + $alias = new Group_alias(); + + $alias->group_id = $this->id; + + foreach ($to_delete as $delalias) { + $alias->alias = $delalias; + $result = $alias->delete(); + if (!$result) { + common_log_db_error($alias, 'DELETE', __FILE__); + return false; + } + } + + foreach ($to_insert as $insalias) { + $alias->alias = $insalias; + $result = $alias->insert(); + if (!$result) { + common_log_db_error($alias, 'INSERT', __FILE__); + return false; + } + } + + return true; + } + + static function getForNickname($nickname) + { + $nickname = common_canonical_nickname($nickname); + $group = User_group::staticGet('nickname', $nickname); + if (!empty($group)) { + return $group; + } + $alias = Group_alias::staticGet('alias', $nickname); + if (!empty($alias)) { + return User_group::staticGet('id', $alias->group_id); + } + return null; + } + + function getDesign() + { + return Design::staticGet('id', $this->design_id); + } + + function getUserMembers() + { + // XXX: cache this + + $user = new User(); + + $qry = + 'SELECT id ' . + 'FROM user JOIN group_member '. + 'ON user.id = group_member.profile_id ' . + 'WHERE group_member.group_id = %d '; + + $user->query(sprintf($qry, $this->id)); + + $ids = array(); + + while ($user->fetch()) { + $ids[] = $user->id; + } + + $user->free(); + + return $ids; + } } diff --git a/classes/laconica.ini b/classes/laconica.ini index 92bbb35d4..7e9b2b791 100644..100755 --- a/classes/laconica.ini +++ b/classes/laconica.ini @@ -1,3 +1,4 @@ + [avatar] profile_id = 129 original = 17 @@ -37,6 +38,19 @@ modified = 384 [consumer__keys] consumer_key = K +[design] +id = 129 +backgroundcolor = 1 +contentcolor = 1 +sidebarcolor = 1 +textcolor = 1 +linkcolor = 1 +backgroundimage = 2 +disposition = 17 + +[design__keys] +id = N + [fave] notice_id = 129 user_id = 129 @@ -54,13 +68,14 @@ size = 1 title = 2 date = 1 protected = 1 +filename = 2 +modified = 384 [file__keys] id = N [file_oembed] -id = 129 -file_id = 1 +file_id = 129 version = 2 type = 2 provider = 2 @@ -72,37 +87,40 @@ title = 2 author_name = 2 author_url = 2 url = 2 +modified = 384 [file_oembed__keys] -id = N +file_id = K [file_redirection] -id = 129 -url = 2 +url = 130 file_id = 1 redirections = 1 httpcode = 1 +modified = 384 [file_redirection__keys] -id = N +url = K [file_thumbnail] -id = 129 -file_id = 1 +file_id = 129 url = 2 width = 1 height = 1 +modified = 384 [file_thumbnail__keys] -id = N +file_id = K +url = U [file_to_post] -id = 129 -file_id = 1 -post_id = 1 +file_id = 129 +post_id = 129 +modified = 384 [file_to_post__keys] -id = N +file_id = K +post_id = K [foreign_link] user_id = 129 @@ -157,6 +175,24 @@ id = K service = K uri = U +[group_alias] +alias = 130 +group_id = 129 +modified = 384 + +[group_alias__keys] +alias = K + +[group_block] +group_id = 129 +blocked = 129 +blocker = 129 +modified = 384 + +[group_block__keys] +group_id = K +blocked = K + [group_inbox] group_id = 129 notice_id = 129 @@ -411,6 +447,8 @@ uri = 2 autosubscribe = 17 urlshorteningservice = 2 inboxed = 17 +design_id = 1 +viewdesigns = 17 created = 142 modified = 384 @@ -434,6 +472,7 @@ original_logo = 2 homepage_logo = 2 stream_logo = 2 mini_logo = 2 +design_id = 1 created = 142 modified = 384 diff --git a/classes/statusnet.ini b/classes/statusnet.ini index a70cd4122..8123265e4 100755..100644 --- a/classes/statusnet.ini +++ b/classes/statusnet.ini @@ -1,13 +1,14 @@ - [status_network] nickname = 130 hostname = 2 pathname = 2 -sitename = 2 dbhost = 2 dbuser = 2 dbpass = 2 dbname = 2 +sitename = 2 +theme = 2 +logo = 2 created = 142 modified = 384 |