- &$profiles: Profiles that were pre-filled
- $avatarSize: The avatar size for the list
+OtherAccountProfiles: Hook to add account profiles to a user account profile block
+- $profile: the Profile being shown
+- &$others: Modifiable array of profile info arrays. Each one has the following fields:
+ href: link to the profile
+ text: text for the profile
+ image: mini image for the profile
1. Unpack the tarball you downloaded on your Web server. Usually a
command like this will work:
- tar zxf statusnet-1.1.0-alpha1.tar.gz
+ tar zxf statusnet-1.1.1.tar.gz
- ...which will make a statusnet-1.1.0-alpha1 subdirectory in your current
+ ...which will make a statusnet-1.1.1 subdirectory in your current
directory. (If you don't have shell access on your Web server, you
may have to unpack the tarball on your local computer and FTP the
files to the server.)
2. Move the tarball to a directory of your choosing in your Web root
directory. Usually something like this will work:
- mv statusnet-1.1.0-alpha1 /var/www/statusnet
+ mv statusnet-1.1.1 /var/www/statusnet
This will make your StatusNet instance available in the statusnet path of
your server, like "http://example.net/statusnet". "microblog" or
README
------
-StatusNet 1.1.0
-2 July 2012
+StatusNet 1.1.1
+16 July 2013
This is the README file for StatusNet, the Open Source social
networking platform. It includes general information about the
New this version
================
-This is a minor bug fix and feature release since 1.0.1, released 3
-October 2011. (Because the plugin interface has changed in an upwardly
-compatible way, we've incremented the minor version number. See
-http://semver.org/ for the semantic versioning scheme we follow.)
+This is a security fix and bug fix release since 1.1.0,
+released 2 July 2012. All 1.1.0 sites should upgrade to this version.
It includes the following changes:
-- ActivitySpam plugin to check updates against spamicity.info
-- Options to hide users who've been silenced or have posted spammy updates.
-- OfflineBackup experimental plugin.
-- Fixes for TwitterBridge to correctly handle replies through the bridge.
-- Improvements in ActivityStreams JSON output to better match 1.0 spec.
-- Console scripts for managing groups.
-- Bug fix for conversation counts in conversation streams.
-- Rights for moderators to manage spam.
+- Fixes for SQL injection errors in profile lists.
+- Improved ActivityStreams JSON representation of activities and objects.
+- Upgrade to the Twitter 1.1 API.
+- More robust handling of errors in distribution.
+- Fix error in OStatus subscription for remote groups.
+- Fix error in XMPP distribution.
-A full changelog is available at http://status.net/wiki/StatusNet_1.1.0.
+A full changelog is available at http://status.net/wiki/StatusNet_1.1.1.
Troubleshooting
===============
--- /dev/null
+This is GNU social, a free software, decentralized social network,
+based on StatusNet.
+
+Developers
+==========
+
+Matt Lee
+Rob Myers
+Sean Corbett
+Ian Denhardt
+Steve DuBois
+Mike Sheldon
+
+With help from
+==============
+
+Bradley M. Kuhn
+
+Special help from
+=================
+
+Craig Andrews
+
--- /dev/null
+Things to be done
+=================
+
+* Create a theme for GNU social
+
+* Create a set of plugins to give StatusNet a more social-network UI
+
+* Work on improvements for annoying things in StatusNet (ie. no
+ redirect to login page when you need to be logged in, etc)
+
+* Work on adding further Activities, such as sharing photos/video,
+ events, UI for managing relationships.
\ No newline at end of file
5. Once all writing processes to your site are turned off, make a
final backup of the Web directory and database.
6. Move your StatusNet directory to a backup spot, like "statusnet.bak".
-7. Unpack your StatusNet 1.1.0-alpha1 tarball and move it to "statusnet" or
+7. Unpack your StatusNet 1.1.1 tarball and move it to "statusnet" or
wherever your code used to be.
8. Copy the config.php file and the contents of the avatar/, background/,
file/, and local/ subdirectories from your old directory to your new
reversed. YOU CAN EASILY DESTROY YOUR SITE WITH THIS STEP. Don't
do it without a known-good backup!
- In your new StatusNet 1.1.0-alpha1 directory and AFTER YOU MAKE A
+ In your new StatusNet 1.1.1 directory and AFTER YOU MAKE A
BACKUP run the upgrade.php script like this:
php ./scripts/upgrade.php
$this->since_id = $this->trimmed('since_id');
$this->geocode = $this->trimmed('geocode');
+ if (!empty($this->auth_user)) {
+ $this->auth_profile = $this->auth_user->getProfile();
+ } else {
+ $this->auth_profile = null;
+ }
+
return true;
}
*/
function showResults()
{
- // TODO: Support search operators like from: and to:, boolean, etc.
+ $q = strtolower($this->query);
- $notice = new Notice();
+ // TODO: Support search operators like from: and to:, boolean, etc.
- // lcase it for comparison
- $q = strtolower($this->query);
+ if (preg_match('/^#([\pL\pN_\-\.]{1,64})$/ue', $q)) {
+ $stream = new TagNoticeStream(substr($q, 1), $this->auth_profile);
+ } else if ($this->isAnURL($q)) {
+ $canon = File_redirection::_canonUrl($q);
+ $file = File::staticGet('url', $canon);
+ if (!empty($file)) {
+ $stream = new FileNoticeStream($file, $this->auth_profile);
+ }
+ } else {
+ $stream = new SearchNoticeStream($q, $this->auth_profile);
+ }
- $search_engine = $notice->getSearchEngine('notice');
- $search_engine->set_sort_mode('chron');
- $search_engine->limit(($this->page - 1) * $this->rpp, $this->rpp + 1, true);
- if (false === $search_engine->query($q)) {
- $cnt = 0;
+ if (empty($stream)) {
+ // XXX: This is hackish, but need some simple way to say "There's no results"
+ $notice = new ArrayWrapper(array());
} else {
- $cnt = $notice->find();
+ $notice = $stream->getNotices(($this->page - 1) * $this->rpp, $this->rpp + 1);
}
// TODO: max_id, lang, geocode
$this->endDocument('json');
}
+ function isAnURL($q) {
+ $regex = '#^'.
+ '(?:^|[\s\<\>\(\)\[\]\{\}\\\'\\\";]+)(?![\@\!\#])'.
+ '('.
+ '(?:'.
+ '(?:'. //Known protocols
+ '(?:'.
+ '(?:(?:https?|ftps?|mms|rtsp|gopher|news|nntp|telnet|wais|file|prospero|webcal|irc)://)'.
+ '|'.
+ '(?:(?:mailto|aim|tel|xmpp):)'.
+ ')'.
+ '(?:[\pN\pL\-\_\+\%\~]+(?::[\pN\pL\-\_\+\%\~]+)?\@)?'. //user:pass@
+ '(?:'.
+ '(?:'.
+ '\[[\pN\pL\-\_\:\.]+(?<![\.\:])\]'. //[dns]
+ ')|(?:'.
+ '[\pN\pL\-\_\:\.]+(?<![\.\:])'. //dns
+ ')'.
+ ')'.
+ ')'.
+ '|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'. //IPv4
+ '|(?:'. //IPv6
+ '\[?(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}(?:(?:[0-9A-Fa-f]{1,4})|:))|(?:(?:[0-9A-Fa-f]{1,4}:){6}(?::|(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})|(?::[0-9A-Fa-f]{1,4})))|(?:(?:[0-9A-Fa-f]{1,4}:){5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){4}(?::[0-9A-Fa-f]{1,4}){0,1}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){3}(?::[0-9A-Fa-f]{1,4}){0,2}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){2}(?::[0-9A-Fa-f]{1,4}){0,3}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:)(?::[0-9A-Fa-f]{1,4}){0,4}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?::(?::[0-9A-Fa-f]{1,4}){0,5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))\]?(?<!:)'.
+ ')|(?:'. //DNS
+ '(?:[\pN\pL\-\_\+\%\~]+(?:\:[\pN\pL\-\_\+\%\~]+)?\@)?'. //user:pass@
+ '[\pN\pL\-\_]+(?:\.[\pN\pL\-\_]+)*\.'.
+ //tld list from http://data.iana.org/TLD/tlds-alpha-by-domain.txt, also added local, loc, and onion
+ '(?:AC|AD|AE|AERO|AF|AG|AI|AL|AM|AN|AO|AQ|AR|ARPA|AS|ASIA|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BIZ|BJ|BM|BN|BO|BR|BS|BT|BV|BW|BY|BZ|CA|CAT|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|COM|COOP|CR|CU|CV|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EDU|EE|EG|ER|ES|ET|EU|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GOV|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|INFO|INT|IO|IQ|IR|IS|IT|JE|JM|JO|JOBS|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MG|MH|MIL|MK|ML|MM|MN|MO|MOBI|MP|MQ|MR|MS|MT|MU|MUSEUM|MV|MW|MX|MY|MZ|NA|NAME|NC|NE|NET|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|ORG|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PRO|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|ST|SU|SV|SY|SZ|TC|TD|TEL|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TP|TR|TRAVEL|TT|TV|TW|TZ|UA|UG|UK|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|XN--0ZWM56D|测试|XN--11B5BS3A9AJ6G|परीक्षा|XN--80AKHBYKNJ4F|испытание|XN--9T4B11YI5A|테스트|XN--DEBA0AD|טעסט|XN--G6W251D|測試|XN--HGBK6AJ7F53BBA|آزمایشی|XN--HLCJ6AYA9ESC7A|பரிட்சை|XN--JXALPDLP|δοκιμή|XN--KGBECHTV|إختبار|XN--ZCKZAH|テスト|YE|YT|YU|ZA|ZM|ZW|local|loc|onion)'.
+ ')(?![\pN\pL\-\_])'.
+ ')'.
+ '(?:'.
+ '(?:\:\d+)?'. //:port
+ '(?:/[\pN\pL$\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'@]*)?'. // /path
+ '(?:\?[\pN\pL\$\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'@\/]*)?'. // ?query string
+ '(?:\#[\pN\pL$\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\@/\?\#]*)?'. // #fragment
+ ')(?<![\?\.\,\#\,])'.
+ ')'.
+ '$#ixu';
+ return preg_match($regex, $q);
+ }
+
/**
* Do we need to write to the database?
*
$cnt = $member_list->show();
}
- $members->free();
-
$this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
$this->page, 'groupmembers',
array('nickname' => $this->group->nickname));
function asActivity()
{
$member = $this->getMember();
+
+ if (!$member) {
+ throw new Exception("No such member: " . $this->profile_id);
+ }
+
$group = $this->getGroup();
+ if (!$group) {
+ throw new Exception("No such group: " . $this->group_id);
+ }
+
$act = new Activity();
$act->id = $this->getURI();
function setNickname($nickname)
{
$this->decache();
- $qry = 'UPDATE local_group set nickname = "'.$nickname.'" where group_id = ' . $this->group_id;
+ $qry = 'UPDATE local_group set nickname = "'.$this->escape($nickname).'" where group_id = ' . $this->group_id;
$result = $this->query($qry);
}
$act->actor = ActivityObject::fromProfile($profile);
- $act->actor->extra[] = $profile->profileInfo();
+ $act->actor->extra[] = $profile->profileInfo(null);
$act->verb = ActivityVerb::POST;
}
foreach ($ni as $id => $source) {
- $user = User::staticGet('id', $id);
- if (empty($user) || $user->hasBlocked($profile) ||
- ($originalProfile && $user->hasBlocked($originalProfile))) {
+ try {
+ $user = User::staticGet('id', $id);
+ if (empty($user) ||
+ $user->hasBlocked($profile) ||
+ ($originalProfile && $user->hasBlocked($originalProfile))) {
+ unset($ni[$id]);
+ }
+ } catch (UserNoProfileException $e) {
+ // User doesn't have a profile; invalid; skip them.
unset($ni[$id]);
}
}
* @return Activity activity object representing this Notice.
*/
- function asActivity($cur)
+ function asActivity($cur=null)
{
$act = self::cacheGet(Cache::codeKey('notice:as-activity:'.$this->id));
if (Event::handle('StartJoinGroup', array($group, $this))) {
$join = Group_member::join($group->id, $this->id);
self::blow('profile:groups:%d', $this->id);
+ self::blow('group:member_ids:%d', $group->id);
+ self::blow('group:member_count:%d', $group->id);
Event::handle('EndJoinGroup', array($group, $this));
}
}
if (Event::handle('StartLeaveGroup', array($group, $this))) {
Group_member::leave($group->id, $this->id);
self::blow('profile:groups:%d', $this->id);
+ self::blow('group:member_ids:%d', $group->id);
+ self::blow('group:member_count:%d', $group->id);
Event::handle('EndLeaveGroup', array($group, $this));
}
}
$profile = new Profile();
$tagged = array();
- $cnt = $profile->query(sprintf($qry, $this->id, $this->id, $tag));
+ $cnt = $profile->query(sprintf($qry, $this->id, $this->id, $profile->escape($tag)));
while ($profile->fetch()) {
$tagged[] = clone($profile);
'tag = "%s", tagger = "%s" ' .
'WHERE tag = "%s" ' .
'AND tagger = "%s"';
- $result = $tags->query(sprintf($qry, $new->tag, $new->tagger,
- $orig->tag, $orig->tagger));
+ $result = $tags->query(sprintf($qry,
+ $tags->escape($new->tag),
+ $tags->escape($new->tagger),
+ $tags->escape($orig->tag),
+ $tags->escape($orig->tagger)));
if (!$result) {
common_log_db_error($tags, 'UPDATE', __FILE__);
$profile->query('SELECT profile.* ' .
'FROM profile JOIN profile_tag ' .
'ON profile.id = profile_tag.tagged ' .
- 'WHERE profile_tag.tagger = ' . $tagger . ' ' .
- 'AND profile_tag.tag = "' . $tag . '" ');
+ 'WHERE profile_tag.tagger = ' . $profile->escape($tagger) . ' ' .
+ 'AND profile_tag.tag = "' . $profile->escape($tag) . '" ');
$tagged = array();
while ($profile->fetch()) {
$tagged[] = clone($profile);
'subscription_token_idx' => array('token'),
),
);
- }
+ }
/* Static get */
function staticGet($k,$v=null)
$subscriber = Profile::staticGet('id', $this->subscriber);
$subscribed = Profile::staticGet('id', $this->subscribed);
+ if (empty($subscriber)) {
+ throw new Exception(sprintf(_('No profile for the subscriber: %d'), $this->subscriber));
+ }
+
+ if (empty($subscribed)) {
+ throw new Exception(sprintf(_('No profile for the subscribed: %d'), $this->subscribed));
+ }
+
$act = new Activity();
$act->verb = ActivityVerb::FOLLOW;
$profile = new Profile();
- $cnt = $profile->query(sprintf($qry, $this->id, $tag));
+ $cnt = $profile->query(sprintf($qry, $this->id, $profile->escape($tag)));
return $profile;
}
$profile = new Profile();
- $profile->query(sprintf($qry, $this->id, $tag));
+ $profile->query(sprintf($qry, $this->id, $profile->escape($tag)));
return $profile;
}
{
const JOIN_POLICY_OPEN = 0;
const JOIN_POLICY_MODERATE = 1;
+ const CACHE_WINDOW = 201;
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
return !in_array($nickname, $blacklist);
}
- function getMembers($offset=0, $limit=null)
+ function getMembers($offset=0, $limit=null) {
+ $ids = null;
+ if (is_null($limit) || $offset + $limit > User_group::CACHE_WINDOW) {
+ $ids = $this->getMemberIDs($offset,
+ $limit);
+ } else {
+ $key = sprintf('group:member_ids:%d', $this->id);
+ $window = self::cacheGet($key);
+ if ($window === false) {
+ $window = $this->getMemberIDs(0,
+ User_group::CACHE_WINDOW);
+ self::cacheSet($key, $window);
+ }
+
+ $ids = array_slice($window,
+ $offset,
+ $limit);
+ }
+
+ return Profile::multiGet('id', $ids);
+ }
+
+ function getMemberIDs($offset=0, $limit=null)
{
- $qry =
- 'SELECT profile.* ' .
- 'FROM profile JOIN group_member '.
- 'ON profile.id = group_member.profile_id ' .
- 'WHERE group_member.group_id = %d ' .
- 'ORDER BY group_member.created DESC ';
+ $gm = new Group_member();
- if ($limit != null) {
- if (common_config('db','type') == 'pgsql') {
- $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
- } else {
- $qry .= ' LIMIT ' . $offset . ', ' . $limit;
- }
+ $gm->selectAdd();
+ $gm->selectAdd('profile_id');
+
+ $gm->group_id = $this->id;
+
+ $gm->orderBy('created DESC');
+
+ if (!is_null($limit)) {
+ $gm->limit($offset, $limit);
}
- $members = new Profile();
+ $ids = array();
- $members->query(sprintf($qry, $this->id));
- return $members;
+ if ($gm->find()) {
+ while ($gm->fetch()) {
+ $ids[] = $gm->profile_id;
+ }
+ }
+
+ return $ids;
}
/**
function getMemberCount()
{
- // XXX: WORM cache this
+ $key = sprintf("group:member_count:%d", $this->id);
- $members = $this->getMembers();
- $member_count = 0;
+ $cnt = self::cacheGet($key);
- /** $member->count() doesn't work. */
- while ($members->fetch()) {
- $member_count++;
+ if (is_integer($cnt)) {
+ return (int) $cnt;
}
- return $member_count;
+ $mem = new Group_member();
+ $mem->group_id = $this->id;
+
+ // XXX: why 'distinct'?
+
+ $cnt = (int) $mem->count('distinct profile_id');
+
+ self::cacheSet($key, $cnt);
+
+ return $cnt;
}
function getBlockedCount()
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+@import url('http://s.libre.fm/combo.css');
+html {margin: 0; padding: 0;}
+body {width: 480px; margin: 70px auto; text-align: left; font-size: 110%; }
+
+img {border: 0;}
+
+p {text-align: left; font-weight: normal; line-height: 1.6em; color: #111;}
+
+li {line-height: 1.4em; margin-bottom: 1em;}
+
+ul {margin: 0; padding-left: 1em;}
+
+h1, h2, h3 {text-align: center;}
+
+#privacy {background-color: #ffb; padding: 10px; border: 2px solid orange; margin-bottom: 1em; }
+
+#signup {text-align: center;}
+
+#allset {background-color: yellow; padding: 5px;}
+
+</style>
+<title>daisycha.in — your social circle</title>
+</head>
+<body>
+<h1><a href="/"><img src="dclogo.png" alt="daisycha.in" height="58" /></a></h1>
+
+<p id="allset">You're all set... please go check your email and click the link we
+sent you.</p>
+
+<h2><img src="getinvolved.png" alt="Get involved:" /></h2>
+
+<ul>
+ <li><a href="list.html">Join our mailing list</a> for updates and news.</li>
+ <li><a href="http://social.shapado.com">Send us your feedback, ideas and suggestions</a>.</li>
+ <li><a href="http://gitorious.org/+socialites">Check out our software</a> and get <a href="http://lists.gnu.org/mailman/listinfo/social">on the mailing list for
+ developers and artists</a>.</li>
+</ul>
+
+
+<p><small>FOO300 — © 2010 <a href="http://foocorp.net/">Foo Communications, LLC</a></small></p>
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+@import url('http://s.libre.fm/combo.css');
+html {margin: 0; padding: 0;}
+body {width: 480px; margin: 70px auto; text-align: left; font-size: 110%; }
+
+img {border: 0;}
+
+p {text-align: left; font-weight: normal; line-height: 1.6em; color: #111;}
+
+li {line-height: 1.4em; margin-bottom: 1em;}
+
+ul {margin: 0; padding-left: 1em;}
+
+h1, h2, h3 {text-align: center;}
+
+#privacy {background-color: #ffb; padding: 10px; border: 2px solid orange; margin-bottom: 1em; }
+
+#signup {text-align: center;}
+
+</style>
+<title>daisycha.in — your social circle</title>
+</head>
+<body>
+<h1><a href="/"><img src="dclogo.png" alt="daisycha.in" height="58" /></a></h1>
+
+<p>This is daisychain, our first public unveiling of a project we call
+<b>social</b>. The idea behind <b>social</b> was to make it easy to
+connect with all the people in your life, but without giving up
+control of your relationships.</p>
+
+<p>If you're using Facebook, and one day you decide to stop, or
+another service comes along (Remember MySpace? Friendster? Orkut?)
+— any and all contacts you have cannot be exported. <b>All your
+friends go away, and you have to find people again</b>.</p>
+
+<p>We think this is a bad idea.</p>
+
+<p><b>Bad for you, and bad for society.</b></p>
+
+<h2><img src="idea.png" alt="The idea:" /></h2>
+
+<p>Start simple, start with status updates like Twitter and Facebook
+already allow, and start getting people to begin updating their
+profiles from something they <b>can</b> control:</p>
+
+<p>Once you're comfortable and on-board with this, let's take your
+ideas, suggestions and feedback, and build the social networking
+software that we all want, and can all use.</p>
+
+<h2><img src="offer.png" alt="What we offer:" /></h2>
+
+<ul>
+
+<li>Let's make that software available to everyone in the world, so
+instead of having one big company in California with all of our
+secrets, we can rely on friends and family, or even run our own
+personal networks.</li>
+
+<li>Let's build the escape rope in to all these networks, so people are
+always able to pick up and leave whenever they feel like it, leaving a
+forwarding address if they choose.</li>
+
+<li>Let's make changing your social network a little more like moving
+house and a little less like starting again without friends or
+contacts in a foreign city.</li>
+
+<h2><img src="youandus.png" alt="You and us:" /></h2>
+
+<p>Who are we? We're a ragtail group of artists and geeks working to
+bring this to you.</p>
+
+<p>You don't have to trust us with your personal information, and in
+fact, we'd rather you didn't.</p>
+
+<p>We'd like you to join us and be a part of a revolution in the way
+people share and socialize on the web.</p>
+
+<p>Let's make the World Wide Web into the Social Web.</p>
+
+<h2><img src="better.png" alt="Make it better:" /></h2>
+
+<p>Anyone reading this, regardless of your ability to program
+computers or make things look pretty can help make this software
+better.</p>
+
+<p>You can tell us what you like, what you hate, how things should be
+different, and what features you'd like to see next.</p>
+
+<p>We'll take the time to listen to you.</p>
+
+
+
+
+<p id="signup"><a href="signup.html"><img src="signup.png" alt="Sign up" /></a></p>
+
+<p><small>FOO300 — © 2010 <a href="http://foocorp.net/">Foo Communications, LLC</a></small></p>
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+@import url('http://s.libre.fm/combo.css');
+html {margin: 0; padding: 0;}
+body {width: 480px; margin: 70px auto; text-align: left; font-size: 110%; }
+
+img {border: 0;}
+
+p {text-align: left; font-weight: normal; line-height: 1.6em; color: #111;}
+
+li {line-height: 1.4em; margin-bottom: 1em;}
+
+ul {margin: 0; padding-left: 1em;}
+
+h1, h2, h3 {text-align: center;}
+
+#privacy {background-color: #ffb; padding: 10px; border: 2px solid orange; margin-bottom: 1em; }
+
+#signup {text-align: center;}
+
+</style>
+<title>daisycha.in — your social circle</title>
+</head>
+<body>
+<h1><a href="/"><img src="dclogo.png" alt="daisycha.in" height="58" /></a></h1>
+
+<p>Get on our mailing list and keep up to date with news and updates with social and daisychain.</p>
+
+<form action="http://foocorp.us2.list-manage.com/subscribe/post?u=ae361e483eddafa57feb63bb0&id=7b50a5946b" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank">
+ <fieldset>
+
+<div class="mc-field-group">
+<label for="mce-EMAIL">Email Address </label>
+<input type="text" value="" name="EMAIL" class="required email" id="mce-EMAIL"> <input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="btn"></div>
+ </fieldset>
+</form>
+
+<p id="signup"><a href="signup.html"><img src="signup.png" alt="Sign up" /></a></p>
+
+</body>
+</html>
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+@import url('http://s.libre.fm/combo.css');
+html {margin: 0; padding: 0;}
+body {width: 480px; margin: 70px auto; text-align: left; font-size: 110%; }
+
+img {border: 0;}
+
+p {text-align: left; font-weight: normal; line-height: 1.6em; color: #111;}
+
+li {line-height: 1.4em; margin-bottom: 1em;}
+
+ul {margin: 0; padding-left: 1em;}
+
+h1, h2, h3 {text-align: center;}
+
+#privacy {background-color: #ffb; padding: 10px; border: 2px solid orange; margin-bottom: 1em; }
+
+</style>
+<title>daisycha.in — your social circle</title>
+</head>
+<body>
+<h1><a href="/"><img src="dclogo.png" alt="daisycha.in" height="58" /></a></h1>
+
+<h2><img src="privacy.png" alt="The idea:" /></h2>
+
+<div id="privacy">
+
+<p>To us, your privacy is vital.</p>
+
+<p>As things currently stand, <b>anything you say on daisychain is
+available to everyone in the world</b>.</p>
+
+<p>Privacy and controls for sharing is something we take extremely
+seriously, but it's also something we cannot do by ourselves.</p>
+
+<p>We invite you to join us and help us define a privacy policy and
+the privacy controls needed to make people feel comfortable sharing on
+the social web.</p>
+
+<p>Finally, we ask that you always consider that anything you put onto
+this or any other website, could be shared outside of any privacy
+controls by anyone you share it with.</p>
+
+<p><b>Put simply: if someone can see it, that person can copy it (or take
+a screenshot) and share it with the world and nobody is ever going to
+be able fix that.</b></p>
+
+<p>So please, think twice about who you identify yourself as, and what
+you post.</p>
+
+<p>With that in mind, <a href="signup2.html">we invite you to sign up</a> and please, enjoy yourself.</p>
+
+</div>
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+@import url('http://s.libre.fm/combo.css');
+html {margin: 0; padding: 0;}
+body {width: 480px; margin: 70px auto; text-align: left; font-size: 110%; }
+
+img {border: 0;}
+
+p {text-align: left; font-weight: normal; color: #111;}
+
+li {line-height: 1.4em; margin-bottom: 1em;}
+
+ul {margin: 0; padding-left: 1em;}
+
+h1, h2, h3 {text-align: center;}
+
+#privacy {background-color: #ffb; padding: 10px; border: 2px solid orange; margin-bottom: 1em; }
+
+#signup {text-align: center;}
+
+small {font-size: 12px; color: #222;}
+
+</style>
+<title>daisycha.in — your social circle</title>
+</head>
+<body>
+<h1><a href="/"><img src="dclogo.png" alt="daisycha.in" height="58" /></a></h1>
+
+<p>To sign up, please fill out this very simple form, or <a href="#">use your OpenID</a>:</p>
+
+<hr />
+
+<form action="">
+
+<p><b>Choose your daisychain profile address:</b></p>
+
+<p>http://<input type="text" size="18" id="url" name="url" />.daisycha.in</p>
+
+<p><small>Please don't choose an offensive word, or attempt to impersonate a brand or celebrity.</small></p>
+
+<hr />
+
+<p><b>Tell us about yourself:</b></p>
+
+<p><label for="email">Email address: <input name="email" id="email" type="text" size="30" maxlength="150" /></label></p>
+
+<p><small>Please consider using a temporary email address service such
+as <a href="http://mailinator.com">Mailinator</a>, or mail forwarding
+service such as <a href="http://sneakemail.com">Sneakemail</a>, so
+that we can't identify you.</small></p>
+
+<hr />
+
+<p><b>Pick a password, enter it twice:</b></p>
+
+<p><label for="pw1">Password: <input type="password" size="30" maxlength="150" name="pw1" id="pw1" /></label></p>
+
+<p><label for="pw2">Confirmed: <input type="password" size="30" maxlength="150" name="pw2" id="pw2" /></label></p>
+
+<hr />
+
+<p><b>Last thing, we need to give you a name:</b></p>
+
+<p><label for="nm">Name: <input type="text" size="30" maxlength="150" name="nm" id="nm" /></label></p>
+
+<p><small>This really doesn't need to be your real name.</small></p>
+
+<hr />
+
+<p><label for="privacy"><input type="checkbox" name="prv" id="prv"
+/> I've read and agree with the privacy policy. </label></p>
+
+<p><label for="terms"><input type="checkbox" name="terms" id="terms"
+/> I've read and agree with the terms and conditions. </label></p>
+
+<p><label for="bad"><input type="checkbox" name="bad" id="bad"
+/> I've read this form correctly and won't check this box. </label></p>
+
+<p><input type="submit" value="Sign up" /></p>
+
+</form>
+</body>
+</html>
+
<!-- Document licensed under Creative Commons Attribution 3.0 Unported. See -->
<!-- http://creativecommons.org/licenses/by/3.0/ for details. -->
-%%site.name%% is a stream-oriented social network service based on the
-Free Software [StatusNet](http://status.net/) tool.
+%%site.name%% is a
+social network based on the Free Software [GNU social](http://www.gnu.org/software/social/) tool.
-If you [register](%%action.register%%) for an account, you can post
-small (%%site.textlimit%% chars or less) text notices about yourself,
-where you are, what you're doing, what you're working on or what
-you're thinking about. You can also subscribe to the notices of your
-friends, or other people you're interested in, and follow them
-privately or on the Web.
-
-You can also post event invitations, bookmarks, polls, questions, or
-other kinds of data.
+If you [register](%%action.register%%) for an account,
+you can post small (%%site.textlimit%% chars or less) text notices
+about yourself, where you are, what you're doing, or practically
+anything you want. You can also subscribe to the notices of your
+friends, or other people you're interested in, and follow them on the
+Web or in an [RSS](http://en.wikipedia.org/wiki/RSS) feed.
These are some *Frequently Asked Questions* about this service, with
some answers.
-What is %%site.name%%?
+What is this site?
----------------------
-%%site.name%% is a stream oriented social network service.
+This is a social network, running the GNU social software.
-You can use it to write short notices about yourself, where you are,
-and what you're doing, and those notices will be sent to all your friends
-and fans.
+You can use it to make connections between friends, family and
+colleagues -- writing short notices about yourself, where you are, and
+what you're doing, and those notices will be sent to all your contacts.
-You can also post event invitations, bookmarks, polls, and questions or
-other kinds of social broadcasts.
+In the future, we'll be adding support for photo, video and file
+sharing, as well as events, better contact management and mobile
+devices.
-How is %%site.name%% different from Twitter?
---------------------------------------------
-
-Like [Twitter](http://twitter.com/), %%site.name%% is a light service
-with a stream-oriented interface. It uses @-replies, hashtags,
-provides search, and has private messages. It provides an API, and can
-be integrated with SMS systems. You can create lists.
-
-Unlike Twitter, %%site.name%% allows more data than just plain text
-and links to travel across the network. You can install the StatusNet
-software that runs %%site.name%% on your own servers, since it's Free
-and Open Source software. You can make groups, and share privately
-with those groups.
-
-You can make your site available only to people you choose. You can
-customize it with your own themes and plugins, or download plugins
-from the StatusNet site.
-
-How is %%site.name%% different from Yammer, SocialCast, or Salesforce Chatter?
-------------------------------------------------------------------------------
-
-Like some enterprise social software services, StatusNet lets you
-share privately with other people in your company. You can use
-microapps to post richer kinds of data, use public and private groups
-to share with team members, use extended profiles to show more about
-yourself and your company, and review groups and users in a directory.
-
-Unlike those other services, StatusNet is free and open source
-software. You can install it on your own servers. You can customize
-the theme and add plugins.
* @author Alan Knowles <alan@akbkhome.com>
* @copyright 1997-2006 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
- * @version CVS: $Id: DataObject.php 301030 2010-07-07 02:26:31Z alan_k $
+ * @version CVS: $Id: DataObject.php 329992 2013-04-03 11:38:43Z alan_k $
* @link http://pear.php.net/package/DB_DataObject
*/
// this will be horrifically slow!!!!
-// NOTE: Overload SEGFAULTS ON PHP4 + Zend Optimizer (see define before..)
// these two are BC/FC handlers for call in PHP4/5
-if ( substr(phpversion(),0,1) == 5) {
+
+if (!defined('DB_DATAOBJECT_NO_OVERLOAD')) {
+
class DB_DataObject_Overload
{
function __call($method,$args)
}
}
} else {
- if (version_compare(phpversion(),'4.3.10','eq') && !defined('DB_DATAOBJECT_NO_OVERLOAD')) {
- trigger_error(
- "overload does not work with PHP4.3.10, either upgrade
- (snaps.php.net) or more recent version
- or define DB_DATAOBJECT_NO_OVERLOAD as per the manual.
- ",E_USER_ERROR);
- }
-
- if (!function_exists('clone')) {
- // emulate clone - as per php_compact, slow but really the correct behaviour..
- eval('function clone($t) { $r = $t; if (method_exists($r,"__clone")) { $r->__clone(); } return $r; }');
- }
- eval('
- class DB_DataObject_Overload {
- function __call($method,$args,&$return) {
- return $this->_call($method,$args,$return);
- }
- }
- ');
+ class DB_DataObject_Overload {}
}
+
* @access private
* @var string
*/
- var $_DB_DataObject_version = "1.9.5";
+ var $_DB_DataObject_version = "1.11.0";
/**
* The Database table (used by table extends)
$v = $k;
$keys = $this->keys();
if (!$keys) {
- $this->raiseError("No Keys available for {$this->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ $this->raiseError("No Keys available for {$this->tableName()}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
return false;
}
$k = $keys[0];
$this->$k = $v;
return $this->find(1);
}
-
+
/**
- * An autoloading, caching static get method using key, value (based on get)
- * (depreciated - use ::get / and your own caching method)
- *
- * Usage:
- * $object = DB_DataObject::staticGet("DbTable_mytable",12);
- * or
- * $object = DB_DataObject::staticGet("DbTable_mytable","name","fred");
+ * Get the value of the primary id
*
- * or write it into your extended class:
- * function &staticGet($k,$v=NULL) { return DB_DataObject::staticGet("This_Class",$k,$v); }
+ * While I normally use 'id' as the PRIMARY KEY value, some database use
+ * {table}_id as the column name.
*
- * @param string $class class name
- * @param string $k column (or value if using keys)
- * @param string $v value (optional)
- * @access public
- * @return object
+ * To save a bit of typing,
+ *
+ * $id = $do->pid();
+ *
+ * @return the id
*/
- function &staticGet($class, $k, $v = null)
+ function pid()
{
- $lclass = strtolower($class);
- global $_DB_DATAOBJECT;
- if (empty($_DB_DATAOBJECT['CONFIG'])) {
- DB_DataObject::_loadConfig();
- }
-
-
-
- $key = "$k:$v";
- if ($v === null) {
- $key = $k;
- }
- if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- DB_DataObject::debug("$class $key","STATIC GET - TRY CACHE");
- }
- if (!empty($_DB_DATAOBJECT['CACHE'][$lclass][$key])) {
- return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
- }
- if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- DB_DataObject::debug("$class $key","STATIC GET - NOT IN CACHE");
- }
-
- $obj = DB_DataObject::factory(substr($class,strlen($_DB_DATAOBJECT['CONFIG']['class_prefix'])));
- if (PEAR::isError($obj)) {
- DB_DataObject::raiseError("could not autoload $class", DB_DATAOBJECT_ERROR_NOCLASS);
- $r = false;
- return $r;
- }
-
- if (!isset($_DB_DATAOBJECT['CACHE'][$lclass])) {
- $_DB_DATAOBJECT['CACHE'][$lclass] = array();
+ $keys = $this->keys();
+ if (!$keys) {
+ $this->raiseError("No Keys available for {$this->tableName()}",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return false;
}
- if (!$obj->get($k,$v)) {
- DB_DataObject::raiseError("No Data return from get $k $v", DB_DATAOBJECT_ERROR_NODATA);
-
- $r = false;
- return $r;
+ $k = $keys[0];
+ if (empty($this->$k)) { // we do not
+ $this->raiseError("pid() called on Object where primary key value not available",
+ DB_DATAOBJECT_ERROR_NODATA);
+ return false;
}
- $_DB_DATAOBJECT['CACHE'][$lclass][$key] = $obj;
- return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
+ return $this->$k;
}
+
+
/**
* build the basic select query.
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
if ($quoteIdentifiers) {
$this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ }
+ $tn = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName()) ;
+ if (!empty($this->_query['derive_table']) && !empty($this->_query['derive_select']) ) {
+
+ // this is a derived select..
+ // not much support in the api yet..
+
+ $sql = 'SELECT ' .
+ $this->_query['derive_select']
+ .' FROM ( SELECT'.
+ $this->_query['data_select'] . " \n" .
+ " FROM $tn \n" .
+ $this->_join . " \n" .
+ $this->_query['condition'] . " \n" .
+ $this->_query['group_by'] . " \n" .
+ $this->_query['having'] . " \n" .
+ ') ' . $this->_query['derive_table'];
+
+ return $sql;
+
+
}
+
+
+
$sql = 'SELECT ' .
$this->_query['data_select'] . " \n" .
- ' FROM ' . ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table) . " \n" .
+ " FROM $tn \n" .
$this->_join . " \n" .
$this->_query['condition'] . " \n" .
$this->_query['group_by'] . " \n" .
* will set $object->N to number of rows, and expects next command to fetch rows
* will return $object->N
*
+ * if an error occurs $object->N will be set to false and return value will also be false;
+ * if numRows is not supported it will
+ *
+ *
* @param boolean $n Fetch first result
* @access public
* @return mixed (number of rows returned, or true if numRows fetching is not supported)
$this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
$sql = $this->_build_select();
}
- $this->_query($sql);
+ $err = $this->_query($sql);
+ if (is_a($err,'PEAR_Error')) {
+ return false;
+ }
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
$this->debug("CHECK autofetchd $n", "find", 1);
}
if (empty($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]) ||
- !is_object($result = &$_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]))
+ !is_object($result = $_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]))
{
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
$this->debug('fetched on object after fetch completed (no results found)');
// note: we dont declare this to keep the print_r size down.
$_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]= array_flip(array_keys($array));
}
-
+ $replace = array('.', ' ');
foreach($array as $k=>$v) {
- $kk = str_replace(".", "_", $k);
- $kk = str_replace(" ", "_", $kk);
+ // use strpos as str_replace is slow.
+ $kk = (strpos($k, '.') === false && strpos($k, ' ') === false) ?
+ $k : str_replace($replace, '_', $k);
+
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
$this->debug("$kk = ". $array[$k], "fetchrow LINE", 3);
}
// set link flag
$this->_link_loaded=false;
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- $this->debug("{$this->__table} DONE", "fetchrow",2);
+ $this->debug("{$this->tableName()} DONE", "fetchrow",2);
}
if (($this->_query !== false) && empty($_DB_DATAOBJECT['CONFIG']['keep_query_after_fetch'])) {
$this->_query = false;
$ar = array();
foreach($list as $k) {
settype($k, $type);
- $ar[] = $type =='string' ? $this->_quote($k) : $k;
+ $ar[] = $type == 'string' ? $this->_quote($k) : $k;
}
+
if (!$ar) {
return $not ? $this->_query['condition'] : $this->whereAdd("1=0");
}
}
global $_DB_DATAOBJECT;
$this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
$this->_query['limit_start'] = ($b == null) ? 0 : (int)$a;
$this->_query['limit_count'] = ($b == null) ? (int)$a : (int)$b;
}
- $table = $this->__table;
+ $table = $this->tableName();
if (is_object($from)) {
- $table = $from->__table;
+ $table = $from->tableName();
$from = array_keys($from->table());
}
$s = '%s';
if (!empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers'])) {
$this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
$s = $DB->quoteIdentifier($s);
$format = $DB->quoteIdentifier($format);
}
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
- $items = isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) ?
- $_DB_DATAOBJECT['INI'][$this->_database][$this->__table] : $this->table();
+ $items = $this->table();
if (!$items) {
- $this->raiseError("insert:No table definition for {$this->__table}",
+ $this->raiseError("insert:No table definition for {$this->tableName()}",
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
return false;
}
- $options = &$_DB_DATAOBJECT['CONFIG'];
+ $options = $_DB_DATAOBJECT['CONFIG'];
$datasaved = 1;
$leftq = '';
$rightq = '';
- $seqKeys = isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table]) ?
- $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] :
+ $seqKeys = isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()]) ?
+ $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] :
$this->sequenceKey();
$key = isset($seqKeys[0]) ? $seqKeys[0] : false;
if (($key !== false) && !$useNative) {
if (!$seq) {
- $keyvalue = $DB->nextId($this->__table);
+ $keyvalue = $DB->nextId($this->tableName());
} else {
$f = $DB->getOption('seqname_format');
$DB->setOption('seqname_format','%s');
$leftq .= ($quoteIdentifiers ? ($DB->quoteIdentifier($k) . ' ') : "$k ");
- if (is_a($this->$k,'DB_DataObject_Cast')) {
+ if (is_object($this->$k) && is_a($this->$k,'DB_DataObject_Cast')) {
$value = $this->$k->toString($v,$DB);
if (PEAR::isError($value)) {
$this->raiseError($value->toString() ,DB_DATAOBJECT_ERROR_INVALIDARGS);
if ($leftq || $useNative) {
- $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table);
+ $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName());
if (($dbtype == 'pgsql') && empty($leftq)) {
case 'pgsql':
if (!$seq) {
- $seq = $DB->getSequenceName(strtolower($this->__table));
+ $seq = $DB->getSequenceName(strtolower($this->tableName()));
}
$db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
$method = ($db_driver == 'DB') ? 'getOne' : 'queryOne';
$original_query = $this->_query;
- $items = isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) ?
- $_DB_DATAOBJECT['INI'][$this->_database][$this->__table] : $this->table();
+ $items = $this->table();
// only apply update against sequence key if it is set?????
if (!$items) {
- $this->raiseError("update:No table definition for {$this->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ $this->raiseError("update:No table definition for {$this->tableName()}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
return false;
}
$datasaved = 1;
$settings = '';
$this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
$dbtype = $DB->dsn["phptype"];
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
$options = $_DB_DATAOBJECT['CONFIG'];
$kSql = ($quoteIdentifiers ? $DB->quoteIdentifier($k) : $k);
- if (is_a($this->$k,'DB_DataObject_Cast')) {
+ if (is_object($this->$k) && is_a($this->$k,'DB_DataObject_Cast')) {
$value = $this->$k->toString($v,$DB);
if (PEAR::isError($value)) {
$this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
// echo " $settings, $this->condition ";
if ($settings && isset($this->_query) && $this->_query['condition']) {
- $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table);
+ $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName());
$r = $this->_query("UPDATE {$table} SET {$settings} {$this->_query['condition']} ");
global $_DB_DATAOBJECT;
// connect will load the config!
$this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
$extra_cond = ' ' . (isset($this->_query['order_by']) ? $this->_query['order_by'] : '');
// don't delete without a condition
if (($this->_query !== false) && $this->_query['condition']) {
- $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table);
+ $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName());
$sql = "DELETE ";
// using a joined delete. - with useWhere..
$sql .= (!empty($this->_join) && $useWhere) ?
$this->_loadConfig();
}
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- $this->debug("{$this->__table} $row of {$this->N}", "fetchrow",3);
+ $this->debug("{$this->tableName()} $row of {$this->N}", "fetchrow",3);
}
- if (!$this->__table) {
+ if (!$this->tableName()) {
$this->raiseError("fetchrow: No table", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
return false;
}
return false;
}
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- $this->debug("{$this->__table} $row of {$this->N}", "fetchrow",3);
+ $this->debug("{$this->tableName()} $row of {$this->N}", "fetchrow",3);
}
- $result = &$_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid];
+ $result = $_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid];
$array = $result->fetchrow(DB_DATAOBJECT_FETCHMODE_ASSOC,$row);
if (!is_array($array)) {
$this->raiseError("fetchrow: No results available", DB_DATAOBJECT_ERROR_NODATA);
return false;
}
-
+ $replace = array('.', ' ');
foreach($array as $k => $v) {
- $kk = str_replace(".", "_", $k);
+ // use strpos as str_replace is slow.
+ $kk = (strpos($k, '.') === false && strpos($k, ' ') === false) ?
+ $k : str_replace($replace, '_', $k);
+
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
$this->debug("$kk = ". $array[$k], "fetchrow LINE", 3);
}
}
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- $this->debug("{$this->__table} DONE", "fetchrow", 3);
+ $this->debug("{$this->tableName()} DONE", "fetchrow", 3);
}
return true;
}
return false;
}
$this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
if (!$whereAddOnly && $items) {
return false;
}
- $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table);
+ $table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->tableName()) : $this->tableName());
$key_col = empty($keys[0]) ? '' : (($quoteIdentifiers ? $DB->quoteIdentifier($keys[0]) : $keys[0]));
$as = ($quoteIdentifiers ? $DB->quoteIdentifier('DATAOBJECT_NUM') : 'DATAOBJECT_NUM');
return false;
}
- $result = &$_DB_DATAOBJECT['RESULTS'][$t->_DB_resultid];
+ $result = $_DB_DATAOBJECT['RESULTS'][$t->_DB_resultid];
$l = $result->fetchRow(DB_DATAOBJECT_FETCHMODE_ORDERED);
// free the results - essential on oracle.
$t->free();
-
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug('Count returned '. $l[0] ,1);
+ }
return (int) $l[0];
}
{
global $_DB_DATAOBJECT;
$this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
// mdb2 uses escape...
$dd = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ? 'DB' : $_DB_DATAOBJECT['CONFIG']['db_driver'];
$ret = ($dd == 'DB') ? $DB->escapeSimple($string) : $DB->escape($string);
'limit_start' => '', // the LIMIT condition
'limit_count' => '', // the LIMIT condition
'data_select' => '*', // the columns to be SELECTed
- 'unions' => array(), // the added unions
+ 'unions' => array(), // the added unions,
+ 'derive_table' => '', // derived table name (BETA)
+ 'derive_select' => '', // derived table select (BETA)
);
$x = new DB_DataObject;
$x->_database = $args[0];
$this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
$tables = $DB->getListOf('tables');
class_exists('DB_DataObject_Generator') ? '' :
$this->_connect();
}
- // loaded already?
- if (!empty($_DB_DATAOBJECT['INI'][$this->_database])) {
-
- // database loaded - but this is table is not available..
- if (
- empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])
- && !empty($_DB_DATAOBJECT['CONFIG']['proxy'])
- ) {
- if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- $this->debug("Loading Generator to fetch Schema",1);
- }
- class_exists('DB_DataObject_Generator') ? '' :
- require_once 'DB/DataObject/Generator.php';
-
-
- $x = new DB_DataObject_Generator;
- $x->fillTableSchema($this->_database,$this->__table);
- }
+
+ // if this table is already loaded this table..
+ if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
return true;
}
-
+ // initialize the ini data.. if empt..
+ if (empty($_DB_DATAOBJECT['INI'][$this->_database])) {
+ $_DB_DATAOBJECT['INI'][$this->_database] = array();
+ }
+
if (empty($_DB_DATAOBJECT['CONFIG'])) {
DB_DataObject::_loadConfig();
}
+ // we do not have the data for this table yet...
+
+ // if we are configured to use the proxy..
+
+ if ( !empty($_DB_DATAOBJECT['CONFIG']['proxy']) ) {
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ $this->debug("Loading Generator to fetch Schema",1);
+ }
+ class_exists('DB_DataObject_Generator') ? '' :
+ require_once 'DB/DataObject/Generator.php';
+
+
+ $x = new DB_DataObject_Generator;
+ $x->fillTableSchema($this->_database,$this->tableName());
+ return true;
+ }
+
+
+
+
// if you supply this with arguments, then it will take those
// as the database and links array...
}
}
+ // are table name lowecased..
+ if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
+ foreach($_DB_DATAOBJECT['INI'][$this->_database] as $k=>$v) {
+ // results in duplicate cols.. but not a big issue..
+ $_DB_DATAOBJECT['INI'][$this->_database][strtolower($k)] = $v;
+ }
+ }
+
+
// now have we loaded the structure..
- if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
+ if (!empty($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
return true;
}
// - if not try building it..
require_once 'DB/DataObject/Generator.php';
$x = new DB_DataObject_Generator;
- $x->fillTableSchema($this->_database,$this->__table);
+ $x->fillTableSchema($this->_database,$this->tableName());
// should this fail!!!???
return true;
}
- $this->debug("Cant find database schema: {$this->_database}/{$this->__table} \n".
+ $this->debug("Cant find database schema: {$this->_database}/{$this->tableName()} \n".
"in links file data: " . print_r($_DB_DATAOBJECT['INI'],true),"databaseStructure",5);
// we have to die here!! - it causes chaos if we dont (including looping forever!)
$this->raiseError( "Unable to load schema for database and table (turn debugging up to 5 for full error message)", DB_DATAOBJECT_ERROR_INVALIDARGS, PEAR_ERROR_DIE);
*/
function tableName()
{
+ global $_DB_DATAOBJECT;
$args = func_get_args();
if (count($args)) {
$this->__table = $args[0];
}
+ if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
+ return strtolower($this->__table);
+ }
return $this->__table;
}
$args = func_get_args();
if (count($args)) {
$this->_database = $args[0];
+ } else {
+ $this->_connect();
}
+
return $this->_database;
}
if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
$this->_connect();
}
-
- if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
- return $_DB_DATAOBJECT['INI'][$this->_database][$this->__table];
+
+ if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
+ return $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()];
}
$this->databaseStructure();
$ret = array();
- if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) {
- $ret = $_DB_DATAOBJECT['INI'][$this->_database][$this->__table];
+ if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()])) {
+ $ret = $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()];
}
return $ret;
* or you do not want to use ini tables, you can override this.
* @param string optional set the key
* @param * optional set more keys
- * @access private
+ * @access public
* @return array
*/
function keys()
if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
$this->_connect();
}
- if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
- return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]);
+ if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"])) {
+ return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"]);
}
$this->databaseStructure();
- if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
- return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]);
+ if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"])) {
+ return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"]);
}
return array();
}
* @param string optional the key sequence/autoinc. key
* @param boolean optional use native increment. default false
* @param false|string optional native sequence name
- * @access private
+ * @access public
* @return array (column,use_native,sequence_name)
*/
function sequenceKey()
if (count($args)) {
$args[1] = isset($args[1]) ? $args[1] : false;
$args[2] = isset($args[2]) ? $args[2] : false;
- $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = $args;
+ $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = $args;
}
- if (isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table])) {
- return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table];
+ if (isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()])) {
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()];
}
// end call setting (eg. $do->sequenceKeys(a,b,c); )
$keys = $this->keys();
if (!$keys) {
- return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table]
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()]
= array(false,false,false);
}
- $table = isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) ?
- $_DB_DATAOBJECT['INI'][$this->_database][$this->__table] : $this->table();
+ $table = $this->table();
$dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'];
$seqname = false;
- if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table])) {
- $seqname = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table];
+ if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->tableName()])) {
+ $seqname = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->tableName()];
if (strpos($seqname,':') !== false) {
list($usekey,$seqname) = explode(':',$seqname);
}
// if the key is not an integer - then it's not a sequence or native
if (empty($table[$usekey]) || !($table[$usekey] & DB_DATAOBJECT_INT)) {
- return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,false);
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array(false,false,false);
}
if (!empty($_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'])) {
$ignore = $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'];
if (is_string($ignore) && (strtoupper($ignore) == 'ALL')) {
- return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,$seqname);
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array(false,false,$seqname);
}
if (is_string($ignore)) {
$ignore = $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'] = explode(',',$ignore);
}
- if (in_array($this->__table,$ignore)) {
- return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,$seqname);
+ if (in_array($this->tableName(),$ignore)) {
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array(false,false,$seqname);
}
}
- $realkeys = $_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"];
+ $realkeys = $_DB_DATAOBJECT['INI'][$this->_database][$this->tableName()."__keys"];
// if you are using an old ini file - go back to old behaviour...
if (is_numeric($realkeys[$usekey])) {
// multiple unique primary keys without a native sequence...
if (($realkeys[$usekey] == 'K') && (count($keys) > 1)) {
- return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,$seqname);
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array(false,false,$seqname);
}
// use native sequence keys...
// technically postgres native here...
// we need to get the new improved tabledata sorted out first.
+ // support named sequence keys.. - currently postgres only..
+
+ if ( in_array($dbtype , array('pgsql')) &&
+ ($table[$usekey] & DB_DATAOBJECT_INT) &&
+ isset($realkeys[$usekey]) && strlen($realkeys[$usekey]) > 1) {
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array($usekey,true, $realkeys[$usekey]);
+ }
+
if ( in_array($dbtype , array('pgsql', 'mysql', 'mysqli', 'mssql', 'ifx')) &&
($table[$usekey] & DB_DATAOBJECT_INT) &&
isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N')
) {
- return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array($usekey,true,$seqname);
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array($usekey,true,$seqname);
}
+
+
// if not a native autoinc, and we have not assumed all primary keys are sequence
if (($realkeys[$usekey] != 'N') &&
!empty($_DB_DATAOBJECT['CONFIG']['dont_use_pear_sequences'])) {
return array(false,false,false);
}
+
+
+
// I assume it's going to try and be a nextval DB sequence.. (not native)
- return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array($usekey,false,$seqname);
+ return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->tableName()] = array($usekey,false,$seqname);
}
// it's not currently connected!
// try and work out what to use for the dsn !
- $options= &$_DB_DATAOBJECT['CONFIG'];
+ $options= $_DB_DATAOBJECT['CONFIG'];
// if the databse dsn dis defined in the object..
$dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
if (!$dsn) {
- if (!$this->_database) {
- $this->_database = isset($options["table_{$this->__table}"]) ? $options["table_{$this->__table}"] : null;
+ if (!$this->_database && !empty($this->__table)) {
+ $this->_database = isset($options["table_{$this->tableName()}"]) ? $options["table_{$this->tableName()}"] : null;
}
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
$this->debug("Checking for database specific ini ('{$this->_database}') : database_{$this->_database} in options","CONNECT");
$db_options = PEAR::getStaticProperty('DB','options');
require_once 'DB.php';
if ($db_options) {
- $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = &DB::connect($dsn,$db_options);
+ $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = DB::connect($dsn,$db_options);
} else {
- $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = &DB::connect($dsn);
+ $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = DB::connect($dsn);
}
} else {
$db_options = is_array($db_options) ? $db_options : array();
$db_options['portability'] = isset($db_options['portability'] )
? $db_options['portability'] : MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE;
- $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = &MDB2::connect($dsn,$db_options);
+ $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = MDB2::connect($dsn,$db_options);
}
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- $this->debug(serialize($_DB_DATAOBJECT['CONNECTIONS']), "CONNECT",5);
+ $this->debug(print_r($_DB_DATAOBJECT['CONNECTIONS'],true), "CONNECT",5);
}
if (PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) {
$this->debug($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->toString(), "CONNECT FAILED",5);
}
// Oracle need to optimize for portibility - not sure exactly what this does though :)
- $c = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
return true;
}
$this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
- $options = &$_DB_DATAOBJECT['CONFIG'];
+ $options = $_DB_DATAOBJECT['CONFIG'];
$_DB_driver = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ?
'DB': $_DB_DATAOBJECT['CONFIG']['db_driver'];
if (strtoupper($string) == 'BEGIN') {
if ($_DB_driver == 'DB') {
$DB->autoCommit(false);
+ $DB->simpleQuery('BEGIN');
} else {
$DB->beginTransaction();
}
- // db backend adds begin anyway from now on..
return true;
}
if (strtoupper($string) == 'COMMIT') {
}
// see if we got a failure.. - try again a few times..
- if (!is_a($result,'PEAR_Error')) {
+ if (!is_object($result) || !is_a($result,'PEAR_Error')) {
break;
}
if ($result->getCode() != -14) { // *DB_ERROR_NODBSELECTED
}
- if (is_a($result,'PEAR_Error')) {
+ if (is_object($result) && is_a($result,'PEAR_Error')) {
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
$this->debug($result->toString(), "Query Error",1 );
}
+ $this->N = false;
return $this->raiseError($result);
}
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
$this->debug(serialize($result), 'RESULT',5);
}
- if (method_exists($result, 'numrows')) {
+ if (method_exists($result, 'numRows')) {
if ($_DB_driver == 'DB') {
$DB->expectError(DB_ERROR_UNSUPPORTED);
} else {
$DB->expectError(MDB2_ERROR_UNSUPPORTED);
}
- $this->N = $result->numrows();
- if (is_a($this->N,'PEAR_Error')) {
+
+ $this->N = $result->numRows();
+ //var_dump($this->N);
+
+ if (is_object($this->N) && is_a($this->N,'PEAR_Error')) {
$this->N = true;
}
$DB->popExpect();
{
global $_DB_DATAOBJECT;
$this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
$options = $_DB_DATAOBJECT['CONFIG'];
}
$kSql = $quoteIdentifiers
- ? ( $DB->quoteIdentifier($this->__table) . '.' . $DB->quoteIdentifier($k) )
- : "{$this->__table}.{$k}";
+ ? ( $DB->quoteIdentifier($this->tableName()) . '.' . $DB->quoteIdentifier($k) )
+ : "{$this->tableName()}.{$k}";
- if (is_a($this->$k,'DB_DataObject_Cast')) {
+ if (is_object($this->$k) && is_a($this->$k,'DB_DataObject_Cast')) {
$dbtype = $DB->dsn["phptype"];
$value = $this->$k->toString($v,$DB);
if (PEAR::isError($value)) {
}
}
- /**
- * autoload Class relating to a table
- * (depreciated - use ::factory)
- *
- * @param string $table table
- * @access private
- * @return string classname on Success
- */
- function staticAutoloadTable($table)
- {
- global $_DB_DATAOBJECT;
- if (empty($_DB_DATAOBJECT['CONFIG'])) {
- DB_DataObject::_loadConfig();
- }
- $p = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
- $_DB_DATAOBJECT['CONFIG']['class_prefix'] : '';
- $class = $p . preg_replace('/[^A-Z0-9]/i','_',ucfirst($table));
-
- $ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
- $class = $ce ? $class : DB_DataObject::_autoloadClass($class);
- return $class;
- }
/**
- function factory($table = '') {
+ function factory($table = '')
+ {
global $_DB_DATAOBJECT;
if ($table === '') {
- if (is_a($this,'DB_DataObject') && strlen($this->__table)) {
- $table = $this->__table;
+ if (is_a($this,'DB_DataObject') && strlen($this->tableName())) {
+ $table = $this->tableName();
} else {
return DB_DataObject::raiseError(
"factory did not recieve a table name",
$cp = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
explode(PATH_SEPARATOR, $_DB_DATAOBJECT['CONFIG']['class_prefix']) : '';
+ //print_r($cp);
+
// multiprefix support.
$tbl = preg_replace('/[^A-Z0-9]/i','_',ucfirst($table));
if (is_array($cp)) {
$rclass = $ce ? $class : DB_DataObject::_autoloadClass($class, $table);
-
// proxy = full|light
if (!$rclass && isset($_DB_DATAOBJECT['CONFIG']['proxy'])) {
$d = new DB_DataObject;
$d->__table = $table;
- if (is_a($ret = $d->_connect(), 'PEAR_Error')) {
+
+ $ret = $d->_connect();
+ if (is_object($ret) && is_a($ret, 'PEAR_Error')) {
return $ret;
}
return $x->$proxyMethod( $d->_database, $table);
}
- if (!$rclass) {
+ if (!$rclass || !class_exists($rclass)) {
return DB_DataObject::raiseError(
"factory could not find class " .
(is_array($class) ? implode(PATH_SEPARATOR, $class) : $class ).
"from $table",
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
}
- $ret = new $rclass;
+
+ $ret = new $rclass();
+
if (!empty($database)) {
DB_DataObject::debug("Setting database to $database","FACTORY",1);
$ret->database($database);
* Should look a bit like
* [local_col_name] => "related_tablename:related_col_name"
*
+ * @param array $new_links optional - force update of the links for this table
+ * You probably want to restore it to it's original state after,
+ * as modifying here does it for the whole PHP request.
*
* @return array|null
* array = if there are links defined for this table.
* empty array - if there is a links.ini file, but no links on this table
- * null - if no links.ini exists for this database (hence try auto_links).
+ * false - if no links.ini exists for this database (hence try auto_links).
* @access public
* @see DB_DataObject::getLinks(), DB_DataObject::getLink()
*/
// have to connect.. -> otherwise things break later.
$this->_connect();
- if (isset($_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table])) {
- return $_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table];
+ // alias for shorter code..
+ $lcfg = &$_DB_DATAOBJECT['LINKS'];
+ $cfg = $_DB_DATAOBJECT['CONFIG'];
+
+ if ($args = func_get_args()) {
+ // an associative array was specified, that updates the current
+ // schema... - be careful doing this
+ if (empty( $lcfg[$this->_database])) {
+ $lcfg[$this->_database] = array();
+ }
+ $lcfg[$this->_database][$this->tableName()] = $args[0];
+
+ }
+ // loaded and available.
+ if (isset($lcfg[$this->_database][$this->tableName()])) {
+ return $lcfg[$this->_database][$this->tableName()];
+ }
+
+ // loaded
+ if (isset($lcfg[$this->_database])) {
+ // either no file, or empty..
+ return $lcfg[$this->_database] === false ? null : array();
}
-
-
-
-
- // attempt to load links file here..
-
- if (!isset($_DB_DATAOBJECT['LINKS'][$this->_database])) {
- $schemas = isset($_DB_DATAOBJECT['CONFIG']['schema_location']) ?
- array("{$_DB_DATAOBJECT['CONFIG']['schema_location']}/{$this->_database}.ini") :
- array() ;
-
- if (isset($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"])) {
- $schemas = is_array($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]) ?
- $_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"] :
- explode(PATH_SEPARATOR,$_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]);
+ // links are same place as schema by default.
+ $schemas = isset($cfg['schema_location']) ?
+ array("{$cfg['schema_location']}/{$this->_database}.ini") :
+ array() ;
+
+ // if ini_* is set look there instead.
+ // and support multiple locations.
+ if (isset($cfg["ini_{$this->_database}"])) {
+ $schemas = is_array($cfg["ini_{$this->_database}"]) ?
+ $cfg["ini_{$this->_database}"] :
+ explode(PATH_SEPARATOR,$cfg["ini_{$this->_database}"]);
+ }
+
+ // default to not available.
+ $lcfg[$this->_database] = false;
+
+ foreach ($schemas as $ini) {
+
+ $links = isset($cfg["links_{$this->_database}"]) ?
+ $cfg["links_{$this->_database}"] :
+ str_replace('.ini','.links.ini',$ini);
+
+ // file really exists..
+ if (!file_exists($links) || !is_file($links)) {
+ if (!empty($cfg['debug'])) {
+ $this->debug("Missing links.ini file: $links","links",1);
+ }
+ continue;
}
+
+ // set to empty array - as we have at least one file now..
+ $lcfg[$this->_database] = empty($lcfg[$this->_database]) ? array() : $lcfg[$this->_database];
+
+ // merge schema file into lcfg..
+ $lcfg[$this->_database] = array_merge(
+ $lcfg[$this->_database],
+ parse_ini_file($links, true)
+ );
+
+ if (!empty($cfg['debug'])) {
+ $this->debug("Loaded links.ini file: $links","links",1);
+ }
- $_DB_DATAOBJECT['LINKS'][$this->_database] = array();
- foreach ($schemas as $ini) {
+ }
+
+ if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
+ foreach($lcfg[$this->_database] as $k=>$v) {
- $links =
- isset($_DB_DATAOBJECT['CONFIG']["links_{$this->_database}"]) ?
- $_DB_DATAOBJECT['CONFIG']["links_{$this->_database}"] :
- str_replace('.ini','.links.ini',$ini);
-
- if (file_exists($links) && is_file($links)) {
- /* not sure why $links = ... here - TODO check if that works */
- $_DB_DATAOBJECT['LINKS'][$this->_database] = array_merge(
- $_DB_DATAOBJECT['LINKS'][$this->_database],
- parse_ini_file($links, true)
- );
-
- if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- $this->debug("Loaded links.ini file: $links","links",1);
- }
- } else {
- if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
- $this->debug("Missing links.ini file: $links","links",1);
- }
+ $nk = strtolower($k);
+ // results in duplicate cols.. but not a big issue..
+ $lcfg[$this->_database][$nk] = isset($lcfg[$this->_database][$nk])
+ ? $lcfg[$this->_database][$nk] : array();
+
+ foreach($v as $kk =>$vv) {
+ //var_Dump($vv);exit;
+ $vv =explode(':', $vv);
+ $vv[0] = strtolower($vv[0]);
+ $lcfg[$this->_database][$nk][$kk] = implode(':', $vv);
}
+
+
}
}
-
+ //echo '<PRE>';print_r($lcfg);exit;
// if there is no link data at all on the file!
// we return null.
- if (!isset($_DB_DATAOBJECT['LINKS'][$this->_database])) {
+ if ($lcfg[$this->_database] === false) {
return null;
}
- if (isset($_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table])) {
- return $_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table];
+ if (isset($lcfg[$this->_database][$this->tableName()])) {
+ return $lcfg[$this->_database][$this->tableName()];
}
return array();
}
+
+
/**
- * load related objects
+ * generic getter/setter for links
*
- * There are two ways to use this, one is to set up a <dbname>.links.ini file
- * into a static property named <dbname>.links and specifies the table joins,
- * the other highly dependent on naming columns 'correctly' :)
- * using colname = xxxxx_yyyyyy
- * xxxxxx = related table; (yyyyy = user defined..)
- * looks up table xxxxx, for value id=$this->xxxxx
- * stores it in $this->_xxxxx_yyyyy
- * you can change what object vars the links are stored in by
- * changeing the format parameter
+ * This is the new 'recommended' way to get get/set linked objects.
+ * must be used with links.ini
*
+ * usage:
+ * get:
+ * $obj = $do->link('company_id');
+ * $obj = $do->link(array('local_col', 'linktable:linked_col'));
+ *
+ * set:
+ * $do->link('company_id',0);
+ * $do->link('company_id',$obj);
+ * $do->link('company_id', array($obj));
*
- * @param string format (default _%s) where %s is the table name.
- * @author Tim White <tim@cyface.com>
+ * example function
+ *
+ * function company() {
+ * $this->link(array('company_id','company:id'), func_get_args());
+ * }
+ *
+ *
+ *
+ * @param mixed $link_spec link specification (normally a string)
+ * uses similar rules to joinAdd() array argument.
+ * @param mixed $set_value (optional) int, DataObject, or array('set')
+ * @author Alan Knowles
* @access public
- * @return boolean , true on success
+ * @return mixed true or false on setting, object on getting
*/
- function getLinks($format = '_%s')
+ function link($field, $set_args = array())
{
-
- // get table will load the options.
- if ($this->_link_loaded) {
- return true;
- }
- $this->_link_loaded = false;
- $cols = $this->table();
- $links = $this->links();
-
- $loaded = array();
-
- if ($links) {
- foreach($links as $key => $match) {
- list($table,$link) = explode(':', $match);
- $k = sprintf($format, str_replace('.', '_', $key));
- // makes sure that '.' is the end of the key;
- if ($p = strpos($key,'.')) {
- $key = substr($key, 0, $p);
- }
-
- $this->$k = $this->getLink($key, $table, $link);
-
- if (is_object($this->$k)) {
- $loaded[] = $k;
- }
- }
- $this->_link_loaded = $loaded;
- return true;
- }
- // this is the autonaming stuff..
- // it sends the column name down to getLink and lets that sort it out..
- // if there is a links file then it is not used!
- // IT IS DEPRECIATED!!!! - USE
- if (!is_null($links)) {
- return false;
- }
-
+ require_once 'DB/DataObject/Links.php';
+ $l = new DB_DataObject_Links($this);
+ return $l->link($field,$set_args) ;
- foreach (array_keys($cols) as $key) {
- if (!($p = strpos($key, '_'))) {
- continue;
- }
- // does the table exist.
- $k =sprintf($format, $key);
- $this->$k = $this->getLink($key);
- if (is_object($this->$k)) {
- $loaded[] = $k;
- }
- }
- $this->_link_loaded = $loaded;
- return true;
}
-
- /**
- * return name from related object
- *
- * There are two ways to use this, one is to set up a <dbname>.links.ini file
- * into a static property named <dbname>.links and specifies the table joins,
- * the other is highly dependant on naming columns 'correctly' :)
+
+ /**
+ * load related objects
*
- * NOTE: the naming convention is depreciated!!! - use links.ini
+ * Generally not recommended to use this.
+ * The generator should support creating getter_setter methods which are better suited.
*
- * using colname = xxxxx_yyyyyy
- * xxxxxx = related table; (yyyyy = user defined..)
- * looks up table xxxxx, for value id=$this->xxxxx
- * stores it in $this->_xxxxx_yyyyy
+ * Relies on <dbname>.links.ini
*
- * you can also use $this->getLink('thisColumnName','otherTable','otherTableColumnName')
+ * Sets properties on the calling dataobject you can change what
+ * object vars the links are stored in by changeing the format parameter
*
*
- * @param string $row either row or row.xxxxx
- * @param string $table name of table to look up value in
- * @param string $link name of column in other table to match
+ * @param string format (default _%s) where %s is the table name.
* @author Tim White <tim@cyface.com>
* @access public
- * @return mixed object on success
+ * @return boolean , true on success
*/
- function getLink($row, $table = null, $link = false)
+ function getLinks($format = '_%s')
{
-
-
- // GUESS THE LINKED TABLE.. (if found - recursevly call self)
-
- if ($table === null) {
- $links = $this->links();
-
- if (is_array($links)) {
-
- if ($links[$row]) {
- list($table,$link) = explode(':', $links[$row]);
- if ($p = strpos($row,".")) {
- $row = substr($row,0,$p);
- }
- return $this->getLink($row,$table,$link);
-
- }
-
- $this->raiseError(
- "getLink: $row is not defined as a link (normally this is ok)",
- DB_DATAOBJECT_ERROR_NODATA);
-
- $r = false;
- return $r;// technically a possible error condition?
-
- }
- // use the old _ method - this shouldnt happen if called via getLinks()
- if (!($p = strpos($row, '_'))) {
- $r = null;
- return $r;
- }
- $table = substr($row, 0, $p);
- return $this->getLink($row, $table);
-
+ require_once 'DB/DataObject/Links.php';
+ $l = new DB_DataObject_Links($this);
+ return $l->applyLinks($format);
+
+ }
- }
-
-
-
- if (!isset($this->$row)) {
- $this->raiseError("getLink: row not set $row", DB_DATAOBJECT_ERROR_NODATA);
- return false;
- }
-
- // check to see if we know anything about this table..
-
- $obj = $this->factory($table);
-
- if (!is_a($obj,'DB_DataObject')) {
- $this->raiseError(
- "getLink:Could not find class for row $row, table $table",
- DB_DATAOBJECT_ERROR_INVALIDCONFIG);
- return false;
- }
- if ($link) {
- if ($obj->get($link, $this->$row)) {
- return $obj;
- }
- return false;
- }
-
- if ($obj->get($this->$row)) {
- return $obj;
- }
- return false;
+ /**
+ * deprecited : @use link()
+ */
+ function getLink($row, $table = null, $link = false)
+ {
+ require_once 'DB/DataObject/Links.php';
+ $l = new DB_DataObject_Links($this);
+ return $l->getLink($row, $table === null ? false: $table, $link);
+
}
* }
*
*/
- function &getLinkArray($row, $table = null)
+ function getLinkArray($row, $table = null)
{
-
- $ret = array();
- if (!$table) {
- $links = $this->links();
-
- if (is_array($links)) {
- if (!isset($links[$row])) {
- // failed..
- return $ret;
- }
- list($table,$link) = explode(':',$links[$row]);
- } else {
- if (!($p = strpos($row,'_'))) {
- return $ret;
- }
- $table = substr($row,0,$p);
- }
- }
-
- $c = $this->factory($table);
-
- if (!is_a($c,'DB_DataObject')) {
- $this->raiseError(
- "getLinkArray:Could not find class for row $row, table $table",
- DB_DATAOBJECT_ERROR_INVALIDCONFIG
- );
- return $ret;
- }
-
- // if the user defined method list exists - use it...
- if (method_exists($c, 'listFind')) {
- $c->listFind($this->id);
- } else {
- $c->find();
- }
- while ($c->fetch()) {
- $ret[] = $c;
- }
- return $ret;
+ require_once 'DB/DataObject/Links.php';
+ $l = new DB_DataObject_Links($this);
+ return $l->getLinkArray($row, $table === null ? false: $table);
+
}
/**
* @param optional $obj object |array the joining object (no value resets the join)
* If you use an array here it should be in the format:
* array('local_column','remotetable:remote_column');
- * if remotetable does not have a definition, you should
- * use @ to hide the include error message..
- *
+ * if remotetable does not have a definition, you should
+ * use @ to hide the include error message..
+ * array('local_column', $dataobject , 'remote_column');
+ * if array has 3 args, then second is assumed to be the linked dataobject.
*
* @param optional $joinType string | array
* 'LEFT'|'INNER'|'RIGHT'|'' Inner is default, '' indicates
$toTable = false;
if (is_array($obj)) {
$tfield = $obj[0];
- list($toTable,$ofield) = explode(':',$obj[1]);
- $obj = DB_DataObject::factory($toTable);
- if (!$obj || is_a($obj,'PEAR_Error')) {
- $obj = new DB_DataObject;
- $obj->__table = $toTable;
+ if (count($obj) == 3) {
+ $ofield = $obj[2];
+ $obj = $obj[1];
+ } else {
+ list($toTable,$ofield) = explode(':',$obj[1]);
+
+ $obj = is_string($toTable) ? DB_DataObject::factory($toTable) : $toTable;
+
+ if (!$obj || !is_object($obj) || is_a($obj,'PEAR_Error')) {
+ $obj = new DB_DataObject;
+ $obj->__table = $toTable;
+ }
+ $obj->_connect();
}
- $obj->_connect();
// set the table items to nothing.. - eg. do not try and match
// things in the child table...???
$items = array();
}
/* make sure $this->_database is set. */
$this->_connect();
- $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
+ $DB = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
/// CHANGED 26 JUN 2009 - we prefer links from our local table over the remote one.
$ar[1] = explode(',', $ar[1]);
}
- if ($ar[0] != $obj->__table) {
+ if ($ar[0] != $obj->tableName()) {
continue;
}
if ($joinCol !== false) {
$ar[1] = explode(',', $ar[1]);
}
- if ($ar[0] != $this->__table) {
+ if ($ar[0] != $this->tableName()) {
continue;
}
if ($ofield === false) {
$this->raiseError(
- "joinAdd: {$obj->__table} has no link with {$this->__table}",
+ "joinAdd: {$obj->tableName()} has no link with {$this->tableName()}",
DB_DATAOBJECT_ERROR_NODATA);
return false;
}
// we default to joining as the same name (this is remvoed later..)
if ($joinAs === false) {
- $joinAs = $obj->__table;
+ $joinAs = $obj->tableName();
}
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']);
// not sure how portable adding database prefixes is..
$objTable = $quoteIdentifiers ?
- $DB->quoteIdentifier($obj->__table) :
- $obj->__table ;
+ $DB->quoteIdentifier($obj->tableName()) :
+ $obj->tableName() ;
$dbPrefix = '';
if (strlen($obj->_database) && in_array($DB->dsn['phptype'],array('mysql','mysqli'))) {
continue;
}
- if (is_a($obj->$k,'DB_DataObject_Cast')) {
+ if (is_object($obj->$k) && is_a($obj->$k,'DB_DataObject_Cast')) {
$value = $obj->$k->toString($v,$DB);
if (PEAR::isError($value)) {
$this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG);
- $table = $this->__table;
+ $table = $this->tableName();
if ($quoteIdentifiers) {
$joinAs = $DB->quoteIdentifier($joinAs);
$fullJoinAs = '';
- $addJoinAs = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->__table) : $obj->__table) != $joinAs;
+ $addJoinAs = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->tableName()) : $obj->tableName()) != $joinAs;
if ($addJoinAs) {
// join table a AS b - is only supported by a few databases and is probably not needed
// , however since it makes the whole Statement alot clearer we are leaving it in
}
+ /**
+ * autoJoin - using the links.ini file, it builds a query with all the joins
+ * usage:
+ * $x = DB_DataObject::factory('mytable');
+ * $x->autoJoin();
+ * $x->get(123);
+ * will result in all of the joined data being added to the fetched object..
+ *
+ * $x = DB_DataObject::factory('mytable');
+ * $x->autoJoin();
+ * $ar = $x->fetchAll();
+ * will result in an array containing all the data from the table, and any joined tables..
+ *
+ * $x = DB_DataObject::factory('mytable');
+ * $jdata = $x->autoJoin();
+ * $x->selectAdd(); //reset..
+ * foreach($_REQUEST['requested_cols'] as $c) {
+ * if (!isset($jdata[$c])) continue; // ignore columns not available..
+ * $x->selectAdd( $jdata[$c] . ' as ' . $c);
+ * }
+ * $ar = $x->fetchAll();
+ * will result in only the columns requested being fetched...
+ *
+ *
+ *
+ * @param array Configuration
+ * exclude Array of columns to exclude from results (eg. modified_by_id)
+ * links The equivilant links.ini data for this table eg.
+ * array( 'person_id' => 'person:id', .... )
+ * include Array of columns to include
+ * distinct Array of distinct columns.
+ *
+ * @return array info about joins
+ * cols => map of resulting {joined_tablename}.{joined_table_column_name}
+ * join_names => map of resulting {join_name_as}.{joined_table_column_name}
+ * count => the column to count on.
+ * @access public
+ */
+ function autoJoin($cfg = array())
+ {
+ //var_Dump($cfg);exit;
+ $pre_links = $this->links();
+ if (!empty($cfg['links'])) {
+ $this->links(array_merge( $pre_links , $cfg['links']));
+ }
+ $map = $this->links( );
+
+
+ //print_r($map);
+ $tabdef = $this->table();
+
+ // we need this as normally it's only cleared by an empty selectAs call.
+
+
+ $keys = array_keys($tabdef);
+ if (!empty($cfg['exclude'])) {
+ $keys = array_intersect($keys, array_diff($keys, $cfg['exclude']));
+ }
+ if (!empty($cfg['include'])) {
+
+ $keys = array_intersect($keys, $cfg['include']);
+ }
+
+ $selectAs = array();
+
+ if (!empty($keys)) {
+ $selectAs = array(array( $keys , '%s', false));
+ }
+
+ $ret = array(
+ 'cols' => array(),
+ 'join_names' => array(),
+ 'count' => false,
+ );
+
+
+
+ $has_distinct = false;
+ if (!empty($cfg['distinct']) && $keys) {
+
+ // reset the columsn?
+ $cols = array();
+
+ //echo '<PRE>' ;print_r($xx);exit;
+ foreach($keys as $c) {
+ //var_dump($c);
+
+ if ( $cfg['distinct'] == $c) {
+ $has_distinct = 'DISTINCT( ' . $this->tableName() .'.'. $c .') as ' . $c;
+ $ret['count'] = 'DISTINCT ' . $this->tableName() .'.'. $c .'';
+ continue;
+ }
+ // cols is in our filtered keys...
+ $cols = $c;
+
+ }
+ // apply our filtered version, which excludes the distinct column.
+
+ $selectAs = empty($cols) ? array() : array(array( $cols , '%s', false)) ;
+
+
+
+ }
+
+ foreach($keys as $k) {
+ $ret['cols'][$k] = $this->tableName(). '.' . $k;
+ }
+
+
+
+ foreach($map as $ocl=>$info) {
+
+ list($tab,$col) = explode(':', $info);
+ // what about multiple joins on the same table!!!
+ $xx = DB_DataObject::factory($tab);
+ if (!is_object($xx) || !is_a($xx, 'DB_DataObject')) {
+ continue;
+ }
+ // skip columns that are excluded.
+
+ // we ignore include here... - as
+
+ // this is borked ... for multiple jions..
+ $this->joinAdd($xx, 'LEFT', 'join_'.$ocl.'_'. $col, $ocl);
+
+ if (!empty($cfg['exclude']) && in_array($ocl, $cfg['exclude'])) {
+ continue;
+ }
+
+ $tabdef = $xx->table();
+ $table = $xx->tableName();
+
+ $keys = array_keys($tabdef);
+
+
+ if (!empty($cfg['exclude'])) {
+ $keys = array_intersect($keys, array_diff($keys, $cfg['exclude']));
+
+ foreach($keys as $k) {
+ if (in_array($ocl.'_'.$k, $cfg['exclude'])) {
+ $keys = array_diff($keys, $k); // removes the k..
+ }
+ }
+
+ }
+
+ if (!empty($cfg['include'])) {
+ // include will basically be BASECOLNAME_joinedcolname
+ $nkeys = array();
+ foreach($keys as $k) {
+ if (in_array( sprintf($ocl.'_%s', $k), $cfg['include'])) {
+ $nkeys[] = $k;
+ }
+ }
+ $keys = $nkeys;
+ }
+
+ if (empty($keys)) {
+ continue;
+ }
+ // got distinct, and not yet found it..
+ if (!$has_distinct && !empty($cfg['distinct'])) {
+ $cols = array();
+ foreach($keys as $c) {
+ $tn = sprintf($ocl.'_%s', $c);
+
+ if ( $tn == $cfg['distinct']) {
+
+ $has_distinct = 'DISTINCT( ' . 'join_'.$ocl.'_'.$col.'.'.$c .') as ' . $tn ;
+ $ret['count'] = 'DISTINCT join_'.$ocl.'_'.$col.'.'.$c;
+ // var_dump($this->countWhat );
+ continue;
+ }
+ $cols[] = $c;
+
+ }
+
+ if (!empty($cols)) {
+ $selectAs[] = array($cols, $ocl.'_%s', 'join_'.$ocl.'_'. $col);
+ }
+
+ } else {
+ $selectAs[] = array($keys, $ocl.'_%s', 'join_'.$ocl.'_'. $col);
+ }
+
+ foreach($keys as $k) {
+ $ret['cols'][sprintf('%s_%s', $ocl, $k)] = $tab.'.'.$k;
+ $ret['join_names'][sprintf('%s_%s', $ocl, $k)] = sprintf('join_%s_%s.%s',$ocl, $col, $k);
+ }
+
+ }
+
+ // fill in the select details..
+ $this->selectAdd();
+
+ if ($has_distinct) {
+ $this->selectAdd($has_distinct);
+ }
+
+ foreach($selectAs as $ar) {
+
+ $this->selectAs($ar[0], $ar[1], $ar[2]);
+ }
+ // restore links..
+ $this->links( $pre_links );
+
+ return $ret;
+
+ }
+
+ /**
+ * Factory method for calling DB_DataObject_Cast
+ *
+ * if used with 1 argument DB_DataObject_Cast::sql($value) is called
+ *
+ * if used with 2 arguments DB_DataObject_Cast::$value($callvalue) is called
+ * valid first arguments are: blob, string, date, sql
+ *
+ * eg. $member->updated = $member->sqlValue('NOW()');
+ *
+ *
+ * might handle more arguments for escaping later...
+ *
+ *
+ * @param string $value (or type if used with 2 arguments)
+ * @param string $callvalue (optional) used with date/null etc..
+ */
+
+ function sqlValue($value)
+ {
+ $method = 'sql';
+ if (func_num_args() == 2) {
+ $method = $value;
+ $value = func_get_arg(1);
+ }
+ require_once 'DB/DataObject/Cast.php';
+ return call_user_func(array('DB_DataObject_Cast', $method), $value);
+
+ }
+
+
/**
* Copies items that are in the table definitions from an
* array or object into the current object
$items = $this->table();
if (!$items) {
$this->raiseError(
- "setFrom:Could not find table definition for {$this->__table}",
+ "setFrom:Could not find table definition for {$this->tableName()}",
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
return;
}
}
continue;
}
- if (is_object($from[sprintf($format,$k)])) {
+ $val = $from[sprintf($format,$k)];
+ if (is_a($val, 'DB_DataObject_Cast')) {
+ $this->$k = $val;
continue;
}
- if (is_array($from[sprintf($format,$k)])) {
+ if (is_object($val) || is_array($val)) {
continue;
}
- $ret = $this->fromValue($k,$from[sprintf($format,$k)]);
+ $ret = $this->fromValue($k,$val);
if ($ret !== true) {
$overload_return[$k] = 'Not A Valid Value';
}
* will also return links converted to arrays.
*
* @param string sprintf format for array
- * @param bool empty only return elemnts that have a value set.
+ * @param bool||number [true = elemnts that have a value set],
+ * [false = table + returned colums] ,
+ * [0 = returned columsn only]
*
* @access public
* @return array of key => value for row
function toArray($format = '%s', $hideEmpty = false)
{
global $_DB_DATAOBJECT;
+
+ // we use false to ignore sprintf.. (speed up..)
+ $format = $format == '%s' ? false : $format;
+
$ret = array();
$rf = ($this->_resultFields !== false) ? $this->_resultFields :
- (isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]) ? $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid] : false);
+ (isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]) ?
+ $_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid] : false);
+
$ar = ($rf !== false) ?
- array_merge($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid],$this->table()) :
+ (($hideEmpty === 0) ? $rf : array_merge($rf, $this->table())) :
$this->table();
foreach($ar as $k=>$v) {
if (!isset($this->$k)) {
if (!$hideEmpty) {
- $ret[sprintf($format,$k)] = '';
+ $ret[$format === false ? $k : sprintf($format,$k)] = '';
}
continue;
}
// call the overloaded getXXXX() method. - except getLink and getLinks
if (method_exists($this,'get'.$k) && !in_array(strtolower($k),array('links','link'))) {
- $ret[sprintf($format,$k)] = $this->{'get'.$k}();
+ $ret[$format === false ? $k : sprintf($format,$k)] = $this->{'get'.$k}();
continue;
}
// should this call toValue() ???
- $ret[sprintf($format,$k)] = $this->$k;
+ $ret[$format === false ? $k : sprintf($format,$k)] = $this->$k;
}
if (!$this->_link_loaded) {
return $ret;
}
foreach($this->_link_loaded as $k) {
- $ret[sprintf($format,$k)] = $this->$k->toArray();
+ $ret[$format === false ? $k : sprintf($format,$k)] = $this->$k->toArray();
}
* @access public
* @return object The DB connection
*/
- function &getDatabaseConnection()
+ function getDatabaseConnection()
{
global $_DB_DATAOBJECT;
* @return object The DB result object
*/
- function &getDatabaseResult()
+ function getDatabaseResult()
{
global $_DB_DATAOBJECT;
$this->_connect();
$options = $_DB_DATAOBJECT['CONFIG'];
$cols = $this->table();
// dont know anything about this col..
- if (!isset($cols[$col])) {
+ if (!isset($cols[$col]) || is_a($value, 'DB_DataObject_Cast')) {
$this->$col = $value;
return true;
}
if ($behaviour == PEAR_ERROR_DIE && !empty($_DB_DATAOBJECT['CONFIG']['dont_die'])) {
$behaviour = null;
}
- $error = &PEAR::getStaticProperty('DB_DataObject','lastError');
+ $error = PEAR::getStaticProperty('DB_DataObject','lastError');
- // this will never work totally with PHP's object model.
- // as this is passed on static calls (like staticGet in our case)
-
- if (isset($this) && is_object($this) && is_subclass_of($this,'db_dataobject')) {
- $this->_lastError = $error;
- }
-
- $_DB_DATAOBJECT['LASTERROR'] = $error;
-
+
// no checks for production here?....... - we log errors before we throw them.
DB_DataObject::debug($message,'ERROR',1);
$opts=null, $userinfo=null, 'DB_DataObject_Error'
);
}
+ // this will never work totally with PHP's object model.
+ // as this is passed on static calls (like staticGet in our case)
+
+ $_DB_DATAOBJECT['LASTERROR'] = $error;
+
+ if (isset($this) && is_object($this) && is_subclass_of($this,'db_dataobject')) {
+ $this->_lastError = $error;
+ }
return $error;
}
}
+ /**
+ * (deprecated - use ::get / and your own caching method)
+ */
+ function staticGet($class, $k, $v = null)
+ {
+ $lclass = strtolower($class);
+ global $_DB_DATAOBJECT;
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ DB_DataObject::_loadConfig();
+ }
+
+
+
+ $key = "$k:$v";
+ if ($v === null) {
+ $key = $k;
+ }
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ DB_DataObject::debug("$class $key","STATIC GET - TRY CACHE");
+ }
+ if (!empty($_DB_DATAOBJECT['CACHE'][$lclass][$key])) {
+ return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
+ }
+ if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
+ DB_DataObject::debug("$class $key","STATIC GET - NOT IN CACHE");
+ }
+
+ $obj = DB_DataObject::factory(substr($class,strlen($_DB_DATAOBJECT['CONFIG']['class_prefix'])));
+ if (PEAR::isError($obj)) {
+ DB_DataObject::raiseError("could not autoload $class", DB_DATAOBJECT_ERROR_NOCLASS);
+ $r = false;
+ return $r;
+ }
+
+ if (!isset($_DB_DATAOBJECT['CACHE'][$lclass])) {
+ $_DB_DATAOBJECT['CACHE'][$lclass] = array();
+ }
+ if (!$obj->get($k,$v)) {
+ DB_DataObject::raiseError("No Data return from get $k $v", DB_DATAOBJECT_ERROR_NODATA);
+
+ $r = false;
+ return $r;
+ }
+ $_DB_DATAOBJECT['CACHE'][$lclass][$key] = $obj;
+ return $_DB_DATAOBJECT['CACHE'][$lclass][$key];
+ }
+
+ /**
+ * autoload Class relating to a table
+ * (deprecited - use ::factory)
+ *
+ * @param string $table table
+ * @access private
+ * @return string classname on Success
+ */
+ function staticAutoloadTable($table)
+ {
+ global $_DB_DATAOBJECT;
+ if (empty($_DB_DATAOBJECT['CONFIG'])) {
+ DB_DataObject::_loadConfig();
+ }
+ $p = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ?
+ $_DB_DATAOBJECT['CONFIG']['class_prefix'] : '';
+ $class = $p . preg_replace('/[^A-Z0-9]/i','_',ucfirst($table));
+
+ $ce = substr(phpversion(),0,1) > 4 ? class_exists($class,false) : class_exists($class);
+ $class = $ce ? $class : DB_DataObject::_autoloadClass($class);
+ return $class;
+ }
+
/* ---- LEGACY BC METHODS - NOT DOCUMENTED - See Documentation on New Methods. ---*/
function _get_table() { return $this->table(); }
* @author Alan Knowles <alan@akbkhome.com>
* @copyright 1997-2008 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
- * @version CVS: $Id: Cast.php 287158 2009-08-12 13:58:31Z alan_k $
+ * @version CVS: $Id: Cast.php 326604 2012-07-12 03:02:00Z alan_k $
* @link http://pear.php.net/package/DB_DataObject
*/
// this is funny - the parameter order is reversed ;)
return "'".sqlite_escape_string($this->value)."'";
+ case 'mssql':
+
+ if(is_numeric($this->value)) {
+ return $this->value;
+ }
+ $unpacked = unpack('H*hex', $this->value);
+ return '0x' . $unpacked['hex'];
+
+
default:
return PEAR::raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet");
}
// perhaps we should support TEXT fields???
//
- if (!($to & DB_DATAOBJECT_BLOB)) {
- return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::string to something other than a blob!'.
- ' (why not just use native features)');
- }
+ // $to == a string field which is the default type (0)
+ // so we do not test it here. - we assume that number fields
+ // will accept a string?? - which is stretching it a bit ...
+ // should probaly add that test as some point.
switch ($db->dsn['phptype']) {
case 'pgsql':
case 'mysqli':
return "'".mysqli_real_escape_string($db->connection, $this->value)."'";
-
+ case 'mssql':
+ // copied from the old DB mssql code...?? not sure how safe this is.
+ return "'" . str_replace(
+ array("'", "\\\r\n", "\\\n"),
+ array("''", "\\\\\r\n\r\n", "\\\\\n\n"),
+ $this->value
+ ) . "'";
+
+
default:
return PEAR::raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet");
}
-
}
* @author Alan Knowles <alan@akbkhome.com>
* @copyright 1997-2006 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
- * @version CVS: $Id: Generator.php 298560 2010-04-25 23:01:51Z alan_k $
+ * @version CVS: $Id: Generator.php 327926 2012-10-08 02:42:09Z alan_k $
* @link http://pear.php.net/package/DB_DataObject
*/
*/
var $table; // active tablename
+ /**
+ * links (generated)
+ *
+ * @var array
+ * @access private
+ */
+ var $_fkeys; // active tablename
/**
* The 'starter' = call this to start the process
$t->_database = basename($t->_database);
}
$t->_createTableList();
+ $t->_createForiegnKeys();
foreach(get_class_methods($class) as $method) {
if (substr($method,0,8 ) != 'generate') {
function _createTableList()
{
$this->_connect();
+
$options = &PEAR::getStaticProperty('DB_DataObject','options');
+
+
$__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
$db_driver = empty($options['db_driver']) ? 'DB' : $options['db_driver'];
$is_MDB2 = ($db_driver != 'DB') ? true : false;
- if (is_a($__DB , 'PEAR_Error')) {
+ if (is_object($__DB) && is_a($__DB , 'PEAR_Error')) {
return PEAR::raiseError($__DB->toString(), null, PEAR_ERROR_DIE);
}
$__DB->loadModule('Reverse');
}
- if ((empty($this->tables) || is_a($this->tables , 'PEAR_Error'))) {
+ if ((empty($this->tables) || (is_object($this->tables) && is_a($this->tables , 'PEAR_Error')))) {
//if that fails fall back to clasic tables list.
if (!$is_MDB2) {
// try getting a list of schema tables first. (postgres)
}
}
- if (is_a($this->tables , 'PEAR_Error')) {
+ if (is_object($this->tables) && is_a($this->tables , 'PEAR_Error')) {
return PEAR::raiseError($this->tables->toString(), null, PEAR_ERROR_DIE);
}
// build views as well if asked to.
if (!empty($options['build_views'])) {
if (!$is_MDB2) {
- $views = $__DB->getListOf('views');
+ $views = $__DB->getListOf(is_string($options['build_views']) ?
+ $options['build_views'] : 'views');
} else {
$views = $__DB->manager->listViews();
}
- if (is_a($views,'PEAR_Error')) {
+ if (is_object($views) && is_a($views,'PEAR_Error')) {
return PEAR::raiseError(
'Error getting Views (check the PEAR bug database for the fix to DB), ' .
$views->toString(),
foreach($this->tables as $table) {
if (isset($options['generator_include_regex']) &&
- !preg_match($options['generator_include_regex'],$table)) {
+ !preg_match($options['generator_include_regex'],$table)) {
+ $this->debug("SKIPPING (generator_include_regex) : $table");
continue;
- } else if (isset($options['generator_exclude_regex']) &&
- preg_match($options['generator_exclude_regex'],$table)) {
+ }
+
+ if (isset($options['generator_exclude_regex']) &&
+ preg_match($options['generator_exclude_regex'],$table)) {
continue;
}
+
+ $strip = empty($options['generator_strip_schema']) ? false : $options['generator_strip_schema'];
+ $strip = is_numeric($strip) ? (bool) $strip : $strip;
+ $strip = (is_string($strip) && strtolower($strip) == 'true') ? true : $strip;
+
// postgres strip the schema bit from the
- if (!empty($options['generator_strip_schema'])) {
- $bits = explode('.', $table,2);
- $table = $bits[0];
- if (count($bits) > 1) {
- $table = $bits[1];
+ if (!empty($strip) ) {
+
+ if (!is_string($strip) || preg_match($strip, $table)) {
+ $bits = explode('.', $table,2);
+ $table = $bits[0];
+ if (count($bits) > 1) {
+ $table = $bits[1];
+ }
}
}
+ $this->debug("EXTRACTING : $table");
+
$quotedTable = !empty($options['quote_identifiers_tableinfo']) ?
$__DB->quoteIdentifier($table) : $table;
-
+
if (!$is_MDB2) {
$defs = $__DB->tableInfo($quotedTable);
}
- if (is_a($defs,'PEAR_Error')) {
+ if (is_object($defs) && is_a($defs,'PEAR_Error')) {
// running in debug mode should pick this up as a big warning..
+ $this->debug("Error reading tableInfo: $table");
$this->raiseError('Error reading tableInfo, '. $defs->toString());
continue;
}
// the temporary table array is now the right one (tables names matching
// with regex expressions have been removed)
$this->tables = $tmp_table;
+
//print_r($this->_definitions);
}
$tmpname = tempnam(session_save_path(),'DataObject_');
//print_r($this->_newConfig);
$fh = fopen($tmpname,'w');
+ if (!$fh) {
+ return PEAR::raiseError(
+ "Failed to create temporary file: $tmpname\n".
+ "make sure session.save_path is set and is writable\n"
+ ,null, PEAR_ERROR_DIE);
+ }
fwrite($fh,$this->_newConfig);
fclose($fh);
$perms = file_exists($file) ? fileperms($file) : 0755;
// return PEAR::raiseError($ret->message,null,PEAR_ERROR_DIE);
// }
}
-
- /**
- * generate Foreign Keys (for links.ini)
- * Currenly only works with mysql / mysqli
+ /**
+ * create the data for Foreign Keys (for links.ini)
+ * Currenly only works with mysql / mysqli / posgtreas
* to use, you must set option: generate_links=true
*
* @author Pascal Schöni
*/
- function generateForeignKeys()
+
+ function _createForiegnKeys()
{
$options = PEAR::getStaticProperty('DB_DataObject','options');
if (empty($options['generate_links'])) {
echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
return; // cant handle non-mysql introspection for defaults.
}
-
+ $this->debug("generateForeignKeys: Start");
$DB = $this->getDatabaseConnection();
$fk = array();
}
}
+
+
+ $this->_fkeys = $fk;
+
+
+
+
+
+ }
+
+
+ /**
+ * generate Foreign Keys (for links.ini)
+ * Currenly only works with mysql / mysqli
+ * to use, you must set option: generate_links=true
+ *
+ * @author Pascal Schöni
+ */
+ function generateForeignKeys()
+ {
+ $options = PEAR::getStaticProperty('DB_DataObject','options');
+ if (empty($options['generate_links'])) {
+ return false;
+ }
+ $__DB = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
+ if (!in_array($__DB->phptype, array('mysql', 'mysqli', 'pgsql'))) {
+ echo "WARNING: cant handle non-mysql and pgsql introspection for defaults.";
+ return; // cant handle non-mysql introspection for defaults.
+ }
+ $this->debug("generateForeignKeys: Start");
+
+ $fk = $this->_fkeys;
$links_ini = "";
foreach($fk as $table => $details) {
}
$links_ini .= "\n";
}
-
+
// dont generate a schema if location is not set
// it's created on the fly!
$options = PEAR::getStaticProperty('DB_DataObject','options');
- if (empty($options['schema_location'])) {
+ if (!empty($options['schema_location'])) {
+ $file = "{$options['schema_location']}/{$this->_database}.links.ini";
+ } elseif (isset($options["ini_{$this->_database}"])) {
+ $file = preg_replace('/\.ini/','.links.ini',$options["ini_{$this->_database}"]);
+ } else {
+ $this->debug("generateForeignKeys: SKIP - schema_location or ini_{database} was not set");
return;
}
-
-
- $file = "{$options['schema_location']}/{$this->_database}.links.ini";
+
if (!file_exists(dirname($file))) {
- require_once 'System.php';
- System::mkdir(array('-p','-m',0755,dirname($file)));
+ mkdir(dirname($file),0755, true);
}
$this->debug("Writing ini as {$file}\n");
$tmpname = tempnam(session_save_path(),'DataObject_');
$fh = fopen($tmpname,'w');
+ if (!$fh) {
+ return PEAR::raiseError(
+ "Failed to create temporary file: $tmpname\n".
+ "make sure session.save_path is set and is writable\n"
+ ,null, PEAR_ERROR_DIE);
+ }
fwrite($fh,$links_ini);
fclose($fh);
$perms = file_exists($file) ? fileperms($file) : 0755;
case 'TEXT':
case 'MEDIUMTEXT':
case 'LONGTEXT':
+ case '_TEXT': //postgres (?? view ??)
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT;
break;
//echo "\n{$t->name} => {$t->flags}\n";
$secondary_key_match = isset($options['generator_secondary_key_match']) ? $options['generator_secondary_key_match'] : 'primary|unique';
- if (preg_match('/(auto_increment|nextval\()/i',rawurldecode($t->flags))
+ $m = array();
+ if (preg_match('/(auto_increment|nextval\(([^)]*))/i',rawurldecode($t->flags),$m)
|| (isset($t->autoincrement) && ($t->autoincrement === true))) {
-
+
+ $sn = 'N';
+ if ($DB->phptype == 'pgsql' && !empty($m[2])) {
+ $sn = preg_replace('/[("]+/','', $m[2]);
+ //echo urldecode($t->flags) . "\n" ;
+ }
// native sequences = 2
if ($write_ini) {
- $keys_out_primary .= "{$t->name} = N\n";
+ $keys_out_primary .= "{$t->name} = $sn\n";
}
- $ret_keys_primary[$t->name] = 'N';
+ $ret_keys_primary[$t->name] = $sn;
} else if ($secondary_key_match && preg_match('/('.$secondary_key_match.')/i',$t->flags)) {
// keys.. = 1
//echo "Generating Class files: \n";
$options = &PEAR::getStaticProperty('DB_DataObject','options');
- $this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
- $this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
-
+ $this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
+ $this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
+
foreach($this->tables as $this->table) {
$this->table = trim($this->table);
$tmpname = tempnam(session_save_path(),'DataObject_');
$fh = fopen($tmpname, "w");
+ if (!$fh) {
+ return PEAR::raiseError(
+ "Failed to create temporary file: $tmpname\n".
+ "make sure session.save_path is set and is writable\n"
+ ,null, PEAR_ERROR_DIE);
+ }
fputs($fh,$out);
fclose($fh);
$perms = file_exists($outfilename) ? fileperms($outfilename) : 0755;
$body .= " {$var} \$__table = '{$this->table}'; {$p}// table name\n";
-
// if we are using the option database_{databasename} = dsn
// then we should add var $_database = here
// as database names may not always match..
// Only include the $_database property if the omit_database_var is unset or false
if (isset($options["database_{$this->_database}"]) && empty($GLOBALS['_DB_DATAOBJECT']['CONFIG']['generator_omit_database_var'])) {
- $p = str_repeat(' ', max(2, (16 - strlen($this->table))));
+ $p = str_repeat(' ', max(2, (16 - strlen($this->_database))));
$body .= " {$var} \$_database = '{$this->_database}'; {$p}// database name (used with database_{*} config)\n";
}
// grep -r __clone * to find all it's uses
// and replace them with $x = clone($y);
// due to the change in the PHP5 clone design.
-
+ $static = 'static';
if ( substr(phpversion(),0,1) < 5) {
$body .= "\n";
$body .= " /* ZE2 compatibility trick*/\n";
$body .= " function __clone() { return \$this;}\n";
}
-
- // simple creation tools ! (static stuff!)
- $body .= "\n";
- $body .= " /* Static get */\n";
- $body .= " function staticGet(\$k,\$v=NULL) { return DB_DataObject::staticGet('{$this->classname}',\$k,\$v); }\n";
+
+ // depricated - in here for BC...
+ if (!empty($options['static_get'])) {
+
+ // simple creation tools ! (static stuff!)
+ $body .= "\n";
+ $body .= " /* Static get */\n";
+ $body .= " $static function staticGet(\$k,\$v=NULL) { " .
+ "return DB_DataObject::staticGet('{$this->classname}',\$k,\$v = null); }\n";
+ }
// generate getter and setter methods
$body .= $this->_generateGetters($input);
$body .= $this->_generateSetters($input);
-
+ $body .= $this->_generateLinkMethods($input);
/*
theoretically there is scope here to introduce 'list' methods
based up 'xxxx_up' column!!! for heiracitcal trees..
$class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
- $this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
+ $this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
$classname = $this->classname = $this->getClassNameFromTableName($this->table);
return $getters;
}
+ /**
+ * Generate link setter/getter methods for class definition
+ *
+ * @param string Existing class contents
+ * @return string
+ * @access public
+ */
+ function _generateLinkMethods($input)
+ {
+
+ $options = &PEAR::getStaticProperty('DB_DataObject','options');
+ $setters = '';
+
+ // only generate if option is set to true
+
+ // generate_link_methods true::
+
+
+ if (empty($options['generate_link_methods'])) {
+ //echo "skip lm? - not set";
+ return '';
+ }
+
+ if (empty($this->_fkeys)) {
+ // echo "skip lm? - fkyes empty";
+ return '';
+ }
+ if (empty($this->_fkeys[$this->table])) {
+ //echo "skip lm? - no fkeys for {$this->table}";
+ return '';
+ }
+
+ // remove auto-generated code from input to be able to check if the method exists outside of the auto-code
+ $input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
+
+ $setters .= "\n";
+ $defs = $this->_fkeys[$this->table];
+
+
+ // $fk[$this->table][$tref[1]] = $tref[2] . ":" . $tref[3];
+
+ // loop through properties and create setter methods
+ foreach ($defs as $k => $info) {
+ // build mehtod name
+ $methodName = is_callable($options['generate_link_methods']) ?
+ $options['generate_link_methods']($k) : $k;
+
+ if (!strlen(trim($k)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
+ continue;
+ }
+
+ $setters .= " /**\n";
+ $setters .= " * Getter / Setter for \${$k}\n";
+ $setters .= " *\n";
+ $setters .= " * @param mixed (optional) value to assign\n";
+ $setters .= " * @access public\n";
+
+ $setters .= " */\n";
+ $setters .= (substr(phpversion(),0,1) > 4) ? ' public '
+ : ' ';
+ $setters .= "function $methodName() {\n";
+ $setters .= " return \$this->link('$k', func_get_args());\n";
+ $setters .= " }\n\n";
+ }
+
+ return $setters;
+ }
/**
* Generate setter methods for class definition
--- /dev/null
+<?php
+/**
+ * Link tool for DB_DataObject
+ *
+ * PHP versions 5
+ *
+ * LICENSE: This source file is subject to version 3.01 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_01.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Database
+ * @package DB_DataObject
+ * @author Alan Knowles <alan@akbkhome.com>
+ * @copyright 1997-2006 The PHP Group
+ * @license http://www.php.net/license/3_01.txt PHP License 3.01
+ * @version : FIXME
+ * @link http://pear.php.net/package/DB_DataObject
+ */
+
+
+/**
+ *
+ * Example of how this could be used..
+ *
+ * The lind method are now in here.
+ *
+ * Currenly only supports existing methods, and new 'link()' method
+ *
+ */
+
+
+/**
+ * Links class
+ *
+ * @package DB_DataObject
+ */
+class DB_DataObject_Links
+{
+ /**
+ * @property {DB_DataObject} do DataObject to apply this to.
+ */
+ var $do = false;
+
+
+ /**
+ * @property {Array|String} load What to load, 'all' or an array of properties. (default all)
+ */
+ var $load = 'all';
+ /**
+ * @property {String|Boolean} scanf use part of column name as resulting
+ * property name. (default false)
+ */
+ var $scanf = false;
+ /**
+ * @property {String|Boolean} printf use column name as sprintf for resulting property name..
+ * (default %s_link if apply is true, otherwise it is %s)
+ */
+ var $printf = false;
+ /**
+ * @property {Boolean} cached cache the result, so future queries will use cache rather
+ * than running the expensive sql query.
+ */
+ var $cached = false;
+ /**
+ * @property {Boolean} apply apply the result to this object, (default true)
+ */
+ var $apply = true;
+
+
+ //------------------------- RETURN ------------------------------------
+ /**
+ * @property {Array} links key value associative array of links.
+ */
+ var $links;
+
+
+ /**
+ * Constructor
+ * -- good ole style..
+ * @param {DB_DataObject} do DataObject to apply to.
+ * @param {Array} cfg Configuration (basically properties of this object)
+ */
+
+ function DB_DataObject_Links($do,$cfg= array())
+ {
+ // check if do is set!!!?
+ $this->do = $do;
+
+ foreach($cfg as $k=>$v) {
+ $this->$k = $v;
+ }
+
+
+ }
+
+ /**
+ * return name from related object
+ *
+ * The relies on a <dbname>.links.ini file, unless you specify the arguments.
+ *
+ * you can also use $this->getLink('thisColumnName','otherTable','otherTableColumnName')
+ *
+ *
+ * @param string $field|array either row or row.xxxxx or links spec.
+ * @param string|DB_DataObject $table (optional) name of table to look up value in
+ * @param string $link (optional) name of column in other table to match
+ * @author Tim White <tim@cyface.com>
+ * @access public
+ * @return mixed object on success false on failure or '0' when not linked
+ */
+ function getLink($field, $table= false, $link='')
+ {
+
+ static $cache = array();
+
+ // GUESS THE LINKED TABLE.. (if found - recursevly call self)
+
+ if ($table == false) {
+
+
+ $info = $this->linkInfo($field);
+
+ if ($info) {
+ return $this->getLink($field, $info[0], $link === false ? $info[1] : $link );
+ }
+
+ // no links defined.. - use borked BC method...
+ // use the old _ method - this shouldnt happen if called via getLinks()
+ if (!($p = strpos($field, '_'))) {
+ return false;
+ }
+ $table = substr($field, 0, $p);
+ return $this->getLink($field, $table);
+
+
+
+ }
+
+ $tn = is_string($table) ? $table : $table->tableName();
+
+
+
+ if (!isset($this->do->$field)) {
+ $this->do->raiseError("getLink: row not set $field", DB_DATAOBJECT_ERROR_NODATA);
+ return false;
+ }
+
+ // check to see if we know anything about this table..
+
+
+ if (empty($this->do->$field) || $this->do->$field < 0) {
+ return 0; // no record.
+ }
+
+ if ($this->cached && isset($cache[$tn.':'. $link .':'. $this->do->$field])) {
+ return $cache[$tn.':'. $link .':'. $this->do->$field];
+ }
+
+ $obj = is_string($table) ? $this->do->factory($tn) : $table;;
+
+ if (!is_a($obj,'DB_DataObject')) {
+ $this->do->raiseError(
+ "getLink:Could not find class for row $field, table $tn",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return false;
+ }
+ // -1 or 0 -- no referenced record..
+
+ $ret = false;
+ if ($link) {
+
+ if ($obj->get($link, $this->do->$field)) {
+ $ret = $obj;
+ }
+
+
+ // this really only happens when no link config is set (old BC stuff)
+ } else if ($obj->get($this->do->$field)) {
+ $ret= $obj;
+
+ }
+ if ($this->cached) {
+ $cache[$tn.':'. $link .':'. $this->do->$field] = $ret;
+ }
+ return $ret;
+
+ }
+ /**
+ * get link information for a field or field specification
+ *
+ * alll link (and join methods accept the 'link' info ) in various ways
+ * string : 'field' = which field to get (uses ???.links.ini to work out what)
+ * array(2) : 'field', 'table:remote_col' << just like the links.ini def.
+ * array(3) : 'field', $dataobject, 'remote_col' (handy for joinAdd to do nested joins.)
+ *
+ * @param string|array $field or link spec to use.
+ * @return (false|array) array of dataobject and linked field or false.
+ *
+ *
+ */
+
+ function linkInfo($field)
+ {
+
+ if (is_array($field)) {
+ if (count($field) == 3) {
+ // array with 3 args:
+ // local_col , dataobject, remote_col
+ return array(
+ $field[1],
+ $field[2],
+ $field[0]
+ );
+
+ }
+ list($table,$link) = explode(':', $field[1]);
+
+ return array(
+ $this->do->factory($table),
+ $link,
+ $field[0]
+ );
+
+ }
+ // work out the link.. (classic way)
+
+ $links = $this->do->links();
+
+ if (empty($links) || !is_array($links)) {
+
+ return false;
+ }
+
+
+ if (!isset($links[$field])) {
+
+ return false;
+ }
+ list($table,$link) = explode(':', $links[$field]);
+
+
+ //??? needed???
+ if ($p = strpos($field,".")) {
+ $field = substr($field,0,$p);
+ }
+
+ return array(
+ $this->do->factory($table),
+ $link,
+ $field
+ );
+
+
+
+
+ }
+
+
+
+ /**
+ * a generic geter/setter provider..
+ *
+ * provides a generic getter setter for the referenced object
+ * eg.
+ * $link->link('company_id') returns getLink for the object
+ * if nothing is linked (it will return an empty dataObject)
+ * $link->link('company_id', array(1)) - just sets the
+ *
+ * also array as the field speck supports
+ * $link->link(array('company_id', 'company:id'))
+ *
+ *
+ * @param string|array $field the field to fetch or link spec.
+ * @params array $args the arguments sent to the getter setter
+ * @return mixed true of false on set, the object on getter.
+ *
+ */
+ function link($field, $args = array())
+ {
+ $info = $this->linkInfo($field);
+
+ if (!$info) {
+ $this->do->raiseError(
+ "getLink:Could not find link for row $field",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG);
+ return false;
+ }
+ $field = $info[2];
+
+
+ if (empty($args)) { // either an empty array or really empty....
+
+ if (!isset($this->do->$field)) {
+ return $info[0]; // empty dataobject.
+ }
+
+ $ret = $this->getLink($field);
+ // nothing linked -- return new object..
+ return ($ret === 0) ? $info[0] : $ret;
+
+ }
+ $assign = is_array($args) ? $args[0] : $args;
+
+ // otherwise it's a set call..
+ if (!is_a($assign , 'DB_DataObject')) {
+
+ if (is_numeric($assign) && is_integer($assign * 1)) {
+ if ($assign > 0) {
+
+ if (!$info) {
+ return false;
+ }
+ // check that record exists..
+ if (!$info[0]->get($info[1], $assign )) {
+ return false;
+ }
+
+ }
+
+ $this->do->$field = $assign ;
+ return true;
+ }
+
+ return false;
+ }
+
+ // otherwise we are assigning it ...
+
+ $this->do->$field = $assign->{$info[1]};
+ return true;
+
+
+ }
+ /**
+ * load related objects
+ *
+ * Generally not recommended to use this.
+ * The generator should support creating getter_setter methods which are better suited.
+ *
+ * Relies on <dbname>.links.ini
+ *
+ * Sets properties on the calling dataobject you can change what
+ * object vars the links are stored in by changeing the format parameter
+ *
+ *
+ * @param string format (default _%s) where %s is the table name.
+ * @author Tim White <tim@cyface.com>
+ * @access public
+ * @return boolean , true on success
+ */
+
+ function applyLinks($format = '_%s')
+ {
+
+ // get table will load the options.
+ if ($this->do->_link_loaded) {
+ return true;
+ }
+
+ $this->do->_link_loaded = false;
+ $cols = $this->do->table();
+ $links = $this->do->links();
+
+ $loaded = array();
+
+ if ($links) {
+ foreach($links as $key => $match) {
+ list($table,$link) = explode(':', $match);
+ $k = sprintf($format, str_replace('.', '_', $key));
+ // makes sure that '.' is the end of the key;
+ if ($p = strpos($key,'.')) {
+ $key = substr($key, 0, $p);
+ }
+
+ $this->do->$k = $this->getLink($key, $table, $link);
+
+ if (is_object($this->do->$k)) {
+ $loaded[] = $k;
+ }
+ }
+ $this->do->_link_loaded = $loaded;
+ return true;
+ }
+ // this is the autonaming stuff..
+ // it sends the column name down to getLink and lets that sort it out..
+ // if there is a links file then it is not used!
+ // IT IS DEPRECATED!!!! - DO NOT USE
+ if (!is_null($links)) {
+ return false;
+ }
+
+
+ foreach (array_keys($cols) as $key) {
+ if (!($p = strpos($key, '_'))) {
+ continue;
+ }
+ // does the table exist.
+ $k =sprintf($format, $key);
+ $this->do->$k = $this->getLink($key);
+ if (is_object($this->do->$k)) {
+ $loaded[] = $k;
+ }
+ }
+ $this->do->_link_loaded = $loaded;
+ return true;
+ }
+
+ /**
+ * getLinkArray
+ * Fetch an array of related objects. This should be used in conjunction with a
+ * <dbname>.links.ini file configuration (see the introduction on linking for details on this).
+ *
+ * You may also use this with all parameters to specify, the column and related table.
+ *
+ * @access public
+ * @param string $field- either column or column.xxxxx
+ * @param string $table (optional) name of table to look up value in
+ * @param string $fkey (optional) fetchall key see DB_DataObject::fetchAll()
+ * @param string $fval (optional)fetchall val DB_DataObject::fetchAll()
+ * @param string $fval (optional) fetchall method DB_DataObject::fetchAll()
+ * @return array - array of results (empty array on failure)
+ *
+ * Example - Getting the related objects
+ *
+ * $person = new DataObjects_Person;
+ * $person->get(12);
+ * $children = $person->getLinkArray('children');
+ *
+ * echo 'There are ', count($children), ' descendant(s):<br />';
+ * foreach ($children as $child) {
+ * echo $child->name, '<br />';
+ * }
+ *
+ */
+ function getLinkArray($field, $table = null, $fkey = false, $fval = false, $fmethod = false)
+ {
+
+ $ret = array();
+ if (!$table) {
+
+
+ $links = $this->do->links();
+
+ if (is_array($links)) {
+ if (!isset($links[$field])) {
+ // failed..
+ return $ret;
+ }
+ list($table,$link) = explode(':',$links[$field]);
+ return $this->getLinkArray($field,$table);
+ }
+ if (!($p = strpos($field,'_'))) {
+ return $ret;
+ }
+ return $this->getLinkArray($field,substr($field,0,$p));
+
+
+ }
+
+ $c = $this->do->factory($table);
+
+ if (!is_object($c) || !is_a($c,'DB_DataObject')) {
+ $this->do->raiseError(
+ "getLinkArray:Could not find class for row $field, table $table",
+ DB_DATAOBJECT_ERROR_INVALIDCONFIG
+ );
+ return $ret;
+ }
+
+ // if the user defined method list exists - use it...
+ if (method_exists($c, 'listFind')) {
+ $c->listFind($this->id);
+ while ($c->fetch()) {
+ $ret[] = clone($c);
+ }
+ return $ret;
+ }
+ return $c->fetchAll($fkey, $fval, $fmethod);
+
+
+ }
+
+}
\ No newline at end of file
// | Author: Alan Knowles <alan@akbkhome.com>
// +----------------------------------------------------------------------+
//
-// $Id: createTables.php 277015 2009-03-12 05:51:03Z alan_k $
+// $Id: createTables.php 315758 2011-08-30 08:11:59Z alan_k $
//
// since this version doesnt use overload,
//require_once 'DB/DataObject/Generator.php';
require_once 'DB/DataObject/Generator.php';
+if (php_sapi_name() != 'cli') {
+ PEAR::raiseError("\nERROR: You must turn use the cli sapi to run this", null, PEAR_ERROR_DIE);
+}
+
if (!ini_get('register_argc_argv')) {
PEAR::raiseError("\nERROR: You must turn register_argc_argv On in you php.ini file for this to work\neg.\n\nregister_argc_argv = On\n\n", null, PEAR_ERROR_DIE);
exit;
}
if (!@$_SERVER['argv'][1]) {
- PEAR::raiseError("\nERROR: createTable.php usage:\n\nC:\php\pear\DB\DataObjects\createTable.php example.ini\n\n", null, PEAR_ERROR_DIE);
+ PEAR::raiseError("\nERROR: createTable.php usage:\n\n" .$_SERVER['argv'][0] . " example.ini\n\n", null, PEAR_ERROR_DIE);
exit;
}
return $this->profile->bio;
}
+ function otherProfiles()
+ {
+ $others = array();
+
+ Event::handle('OtherAccountProfiles', array($this->profile, &$others));
+
+ return $others;
+ }
+
function showTags()
{
$cur = common_current_user();
// TRANS: Text between [] is a link description, text between () is the link itself.
// TRANS: Make sure there is no whitespace between "]" and "(".
// TRANS: "%%site.broughtby%%" is the value of the variable site.broughtby
- $instr = _('**%%site.name%%** is a microblogging service brought to you by [%%site.broughtby%%](%%site.broughtbyurl%%).');
+ $instr = _('**%%site.name%%** is a social network, courtesy of [%%site.broughtby%%](%%site.broughtbyurl%%).');
} else {
// TRANS: First sentence of the StatusNet site license. Used if 'broughtby' is not set.
- $instr = _('**%%site.name%%** is a microblogging service.');
+ $instr = _('**%%site.name%%** is a social network.');
}
$instr .= ' ';
// TRANS: Second sentence of the StatusNet site license. Mentions the StatusNet source code license.
// TRANS: Make sure there is no whitespace between "]" and "(".
// TRANS: Text between [] is a link description, text between () is the link itself.
// TRANS: %s is the version of StatusNet that is being used.
- $instr .= sprintf(_('It runs the [StatusNet](http://status.net/) microblogging software, version %s, available under the [GNU Affero General Public License](http://www.fsf.org/licensing/licenses/agpl-3.0.html).'), STATUSNET_VERSION);
+ $instr .= sprintf(_('It runs on [GNU social](http://www.gnu.org/software/social/), version %s, available under the [GNU Affero General Public License](http://www.fsf.org/licensing/licenses/agpl-3.0.html).'), STATUSNET_VERSION);
$output = common_markup_to_html($instr);
$this->raw($output);
// do it
if ($object instanceof Activity) {
// Sharing a post activity is more like sharing the original object
- if ($this->verb == 'share' && $object->verb == 'post') {
+ if (ActivityVerb::canonical($this->verb) == ActivityVerb::canonical(ActivityVerb::SHARE) &&
+ ActivityVerb::canonical($object->verb) == ActivityVerb::canonical(ActivityVerb::POST)) {
// XXX: Here's one for the obfuscation record books
- $object = $object->object;
+ $object = $object->objects[0];
}
}
list($lat, $lon) = explode(' ', $this->geopoint);
- $object['location'] = array(
- 'objectType' => 'place',
- 'position' => sprintf("%+02.5F%+03.5F/", $lat, $lon),
- 'lat' => $lat,
- 'lon' => $lon
- );
+ if (!empty($lat) && !empty($lon)) {
+ $object['location'] = array(
+ 'objectType' => 'place',
+ 'position' => sprintf("%+02.5F%+03.5F/", $lat, $lon),
+ 'lat' => $lat,
+ 'lon' => $lon
+ );
- $loc = Location::fromLatLon($lat, $lon);
+ $loc = Location::fromLatLon((float)$lat, (float)$lon);
- if ($loc) {
- $name = $loc->getName();
+ if ($loc) {
+ $name = $loc->getName();
- if ($name) {
- $object['location']['displayName'] = $name;
- }
- $url = $loc->getURL();
+ if ($name) {
+ $object['location']['displayName'] = $name;
+ }
+ $url = $loc->getURL();
- if ($url) {
- $object['location']['url'] = $url;
+ if ($url) {
+ $object['location']['url'] = $url;
+ }
}
}
}
{
return null;
}
+
+ function otherProfiles()
+ {
+ return array();
+ }
}
\ No newline at end of file
$profile = Profile::current();
}
parent::__construct(new CachingNoticeStream(new RawFileNoticeStream($file),
- 'file:notice-ids:'.$this->url),
+ 'file:notice-ids:'.$file->id),
$profile);
}
}
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-define('STATUSNET_BASE_VERSION', '1.1.0');
-define('STATUSNET_LIFECYCLE', 'alpha1'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
+define('STATUSNET_BASE_VERSION', '1.1.1');
+define('STATUSNET_LIFECYCLE', 'release'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
define('STATUSNET_VERSION', STATUSNET_BASE_VERSION . '-' . STATUSNET_LIFECYCLE);
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
-define('STATUSNET_CODENAME', 'Fight for Your Right');
+define('STATUSNET_CODENAME', 'OK');
define('AVATAR_PROFILE_SIZE', 96);
define('AVATAR_STREAM_SIZE', 48);
return $this->group->description;
}
+ function otherProfiles()
+ {
+ return array();
+ }
+
function showActions()
{
$cur = common_current_user();
$this->id = $this->notice->id;
$this->from_user_id = $this->profile->id;
- $user = User::staticGet('id', $this->profile->id);
-
- $this->iso_language_code = $user->language;
+ $user = $this->profile->getUser();
+ if (empty($user)) {
+ // Gonna have to do till we can detect it
+ $this->iso_language_code = common_config('site', 'language');
+ } else {
+ $this->iso_language_code = $user->language;
+ }
+
$this->source = $this->getSourceLink($this->notice->source);
$avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE);
$this->showName();
$this->showLocation();
$this->showHomepage();
+ $this->showOtherProfiles();
$this->showDescription();
$this->showTags();
}
}
}
+ function showOtherProfiles()
+ {
+ $otherProfiles = $this->otherProfiles();
+
+ if (!empty($otherProfiles)) {
+
+ $this->out->elementStart('ul',
+ array('class' => 'profile_block_otherprofile_list'));
+
+ foreach ($otherProfiles as $otherProfile) {
+ $this->out->elementStart('li');
+ $this->out->elementStart('a',
+ array('href' => $otherProfile['href'],
+ 'rel' => 'me',
+ 'class' => 'profile_block_otherprofile',
+ 'title' => $otherProfile['text']));
+ $this->out->element('img',
+ array('src' => $otherProfile['image'],
+ 'class' => 'profile_block_otherprofile_icon'));
+ $this->out->elementEnd('a');
+ $this->out->elementEnd('li');
+ }
+
+ $this->out->elementEnd('ul');
+ }
+ }
+
function avatarSize()
{
return AVATAR_PROFILE_SIZE;
),
'plugins' => array(
'default' => array(
- 'Activity' => null,
'Bookmark' => null,
'ClientSideShorten' => null,
'Directory' => null,
),
'plugins' => array(
'default' => array(
- 'Activity' => null,
'Bookmark' => null,
'ClientSideShorten' => null,
'Directory' => null,
),
'plugins' => array(
'default' => array(
- 'Activity' => null,
'Bookmark' => null,
'ClientSideShorten' => null,
'Directory' => null,
),
'plugins' => array(
'default' => array(
- 'Activity' => null,
'Bookmark' => null,
'ClientSideShorten' => null,
'Event' => null,
* Raw output mode will attempt to stream, keeping less
* data in memory but will leave $this->activities incomplete.
*/
- function __construct($user, $indent = true, $outputMode = UserActivityStream::OUTPUT_STRING)
+ function __construct($user, $indent = true, $outputMode = UserActivityStream::OUTPUT_STRING, $after = null)
{
parent::__construct($user, null, $indent);
$this->outputMode = $outputMode;
+
if ($this->outputMode == self::OUTPUT_STRING) {
// String buffering? Grab all the notices now.
$notices = $this->getNotices();
throw new Exception('Invalid outputMode provided to ' . __METHOD__);
}
+ $this->after = $after;
+
// Assume that everything but notices is feasible
// to pull at once and work with in memory...
$notices = $this->getNoticesBetween($start, $end);
foreach ($notices as $noticeAct) {
try {
- $nact = $noticeAct->asActivity();
+ $nact = $noticeAct->asActivity($this->user);
if ($format == Feed::ATOM) {
$nact->outputTo($this, false, false);
} else {
if ($this->outputMode == self::OUTPUT_RAW) {
// Grab anything after the last pre-sorted activity.
try {
- $notices = $this->getNoticesBetween(0, $end);
+ if (!empty($this->after)) {
+ $notices = $this->getNoticesBetween($this->after, $end);
+ } else {
+ $notices = $this->getNoticesBetween(0, $end);
+ }
foreach ($notices as $noticeAct) {
try {
- $nact = $noticeAct->asActivity();
+ $nact = $noticeAct->asActivity($this->user);
if ($format == Feed::ATOM) {
$nact->outputTo($this, false, false);
} else {
}
}
- // We always add the registration activity at the end, even if
- // they have older activities (from restored backups) in their stream.
+ if (empty($this->after) || strtotime($this->user->created) > $this->after) {
+ // We always add the registration activity at the end, even if
+ // they have older activities (from restored backups) in their stream.
- try {
- $ract = $this->user->registrationActivity();
- if ($format == Feed::ATOM) {
- $ract->outputTo($this, false, false);
- } else {
- if ($haveOne) {
- fwrite($handle, ",");
+ try {
+ $ract = $this->user->registrationActivity();
+ if ($format == Feed::ATOM) {
+ $ract->outputTo($this, false, false);
+ } else {
+ if ($haveOne) {
+ fwrite($handle, ",");
+ }
+ fwrite($handle, json_encode($ract->asArray()));
+ $haveOne = true;
}
- fwrite($handle, json_encode($ract->asArray()));
- $haveOne = true;
+ } catch (Exception $e) {
+ common_log(LOG_ERR, $e->getMessage());
+ continue;
}
- } catch (Exception $e) {
- common_log(LOG_ERR, $e->getMessage());
- continue;
}
}
$sub->subscriber = $this->user->id;
+ if (!empty($this->after)) {
+ $sub->whereAdd("created > '" . common_sql_date($this->after) . "'");
+ }
+
if ($sub->find()) {
while ($sub->fetch()) {
if ($sub->subscribed != $this->user->id) {
$sub->subscribed = $this->user->id;
+ if (!empty($this->after)) {
+ $sub->whereAdd("created > '" . common_sql_date($this->after) . "'");
+ }
+
if ($sub->find()) {
while ($sub->fetch()) {
if ($sub->subscriber != $this->user->id) {
$fave->user_id = $this->user->id;
+ if (!empty($this->after)) {
+ $fave->whereAdd("modified > '" . common_sql_date($this->after) . "'");
+ }
+
if ($fave->find()) {
while ($fave->fetch()) {
$faves[] = clone($fave);
$notice->profile_id = $this->user->id;
+ // Only stuff after $this->after
+
+ if (!empty($this->after)) {
+ if ($start) {
+ $start = max($start, $this->after);
+ }
+ if ($end) {
+ $end = max($end, $this->after);
+ }
+ }
+
if ($start) {
$tsstart = common_sql_date($start);
$notice->whereAdd("created >= '$tsstart'");
function getNotices()
{
- return $this->getNoticesBetween();
+ if (!empty($this->after)) {
+ return $this->getNoticesBetween($this->after);
+ } else {
+ return $this->getNoticesBetween();
+ }
}
function getGroups()
$gm->profile_id = $this->user->id;
+ if (!empty($this->after)) {
+ $gm->whereAdd("created > '" . common_sql_date($this->after) . "'");
+ }
+
if ($gm->find()) {
while ($gm->fetch()) {
$groups[] = clone($gm);
{
$msgMap = Memcached_DataObject::listGet('Message', 'to_profile', array($this->user->id));
- return $msgMap[$this->user->id];
+ $messages = $msgMap[$this->user->id];
+
+ if (!empty($this->after)) {
+ $messages = array_filter($messages, array($this, 'createdAfter'));
+ }
+
+ return $messages;
}
function getMessagesFrom()
{
$msgMap = Memcached_DataObject::listGet('Message', 'from_profile', array($this->user->id));
- return $msgMap[$this->user->id];
+ $messages = $msgMap[$this->user->id];
+
+ if (!empty($this->after)) {
+ $messages = array_filter($messages, array($this, 'createdAfter'));
+ }
+
+ return $messages;
+ }
+
+ function createdAfter($item) {
+ $created = strtotime((empty($item->created)) ? $item->modified : $item->created);
+ return ($created >= $this->after);
}
function writeJSON($handle)
"\n"
"Thanks for your time, \n"
"%2$s\n"
-msgstr ""
-"Hola, %1$s.\n"
+msgstr "Hola, %1$s.\n"
"\n"
"Algú ha introduït aquesta adreça electrònica a %2$s.\n"
"\n"
--- /dev/null
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2013 StatusNet, 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/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
+
+$shortoptions = 'i:n:a';
+$longoptions = array('id=', 'nickname=', 'all');
+
+$helptext = <<<END_OF_SILENCESPAMMER_HELP
+silencespammer.php [options]
+Users who post a lot of spam get silenced
+
+ -i --id ID of user to test and silence
+ -n --nickname nickname of the user to test and silence
+ -a --all All users
+END_OF_SILENCESPAMMER_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+function testAllUsers($filter, $minimum, $percent) {
+ $found = false;
+ $offset = 0;
+ $limit = 1000;
+
+ do {
+
+ $user = new User();
+ $user->orderBy('created');
+ $user->limit($offset, $limit);
+
+ $found = $user->find();
+
+ if ($found) {
+ while ($user->fetch()) {
+ try {
+ silencespammer($filter, $user, $minimum, $percent);
+ } catch (Exception $e) {
+ printfnq("ERROR testing user %s\n: %s", $user->nickname, $e->getMessage());
+ }
+ }
+ $offset += $found;
+ }
+
+ } while ($found > 0);
+}
+
+function silencespammer($filter, $user, $minimum, $percent) {
+
+ printfnq("Testing user %s\n", $user->nickname);
+
+ $profile = Profile::staticGet('id', $user->id);
+
+ if ($profile->isSilenced()) {
+ printfnq("Already silenced %s\n", $user->nickname);
+ return;
+ }
+
+ $cnt = $profile->noticeCount();
+
+ if ($cnt < $minimum) {
+ printfnq("Only %d notices posted (minimum %d); skipping\n", $cnt, $minimum);
+ return;
+ }
+
+ $ss = new Spam_score();
+
+ $ss->query(sprintf("SELECT count(*) as spam_count ".
+ "FROM notice join spam_score on notice.id = spam_score.notice_id ".
+ "WHERE notice.profile_id = %d AND spam_score.is_spam = 1", $profile->id));
+
+ while ($ss->fetch()) {
+ $spam_count = $ss->spam_count;
+ }
+
+ $spam_percent = ($spam_count * 100.0 / $cnt);
+
+ if ($spam_percent > $percent) {
+ printfnq("Silencing user %s (%d/%d = %0.2f%% spam)\n", $user->nickname, $spam_count, $cnt, $spam_percent);
+ try {
+ $profile->silence();
+ } catch(Exception $e) {
+ printfnq("Error: %s", $e->getMessage());
+ }
+ }
+}
+
+try {
+ $filter = null;
+ $minimum = 5;
+ $percent = 80;
+ Event::handle('GetSpamFilter', array(&$filter));
+ if (empty($filter)) {
+ throw new Exception(_("No spam filter."));
+ }
+ if (have_option('a', 'all')) {
+ testAllUsers($filter, $minimum, $percent);
+ } else {
+ $user = getUser();
+ silencespammer($filter, $user, $minimum, $percent);
+ }
+} catch (Exception $e) {
+ print $e->getMessage()."\n";
+ exit(1);
+}
return true;
}
+ /**
+ * Add links in the user's profile block to their Facebook profile URL.
+ *
+ * @param Profile $profile The profile being shown
+ * @param Array &$links Writeable array of arrays (href, text, image).
+ *
+ * @return boolean hook value (true)
+ */
+
+ function onOtherAccountProfiles($profile, &$links)
+ {
+ $fuser = null;
+
+ $flink = Foreign_link::getByUserID($profile->id, FACEBOOK_SERVICE);
+
+ if (!empty($flink)) {
+
+ $fuser = $this->getFacebookUser($flink->foreign_id);
+
+ if (!empty($fuser)) {
+ $links[] = array("href" => $fuser->link,
+ "text" => sprintf(_("%s on Facebook"), $fuser->name),
+ "image" => $this->path("images/f_logo.png"));
+ }
+ }
+
+ return true;
+ }
+
+ function getFacebookUser($id) {
+
+ $key = Cache::key(sprintf("FacebookBridgePlugin:userdata:%s", $id));
+
+ $c = Cache::instance();
+
+ if ($c) {
+ $obj = $c->get($key);
+ if ($obj) {
+ return $obj;
+ }
+ }
+
+ $url = sprintf("https://graph.facebook.com/%s", $id);
+ $client = new HTTPClient();
+ $resp = $client->get($url);
+
+ if (!$resp->isOK()) {
+ return null;
+ }
+
+ $user = json_decode($resp->getBody());
+
+ if ($user->error) {
+ return null;
+ }
+
+ if ($c) {
+ $c->set($key, $user);
+ }
+
+ return $user;
+ }
+
/*
* Add version info for this plugin
*
"Cordiali saluti,\n"
"\n"
"%2$s\n"
-" "
#. TRANS: E-mail subject. %s is the StatusNet sitename.
#, php-format
--- /dev/null
+GNU Social -- DukiDuki Snail
+============================
+
+Duki Duki Snail is a classic PC game, released in Czechoslovakia in
+the 1980s, and later reborn in free software in the mid 1990s.
+
+It is our intention to create a free software version of DukiDuki
+Snail for GNU social in which players can share their high scores.
+
+The game is built like such:
+
+A maze is drawn:
+
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXX XXX XXXX
+XXXX XXX XXXX
+XXXX XXX XXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXX
+XXXX XXXX XXXX
+XXXX XXXX XXXX
+XXXX XXXX XXXX
+XXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXX XXX XXX XXX XXX XXXX XXXX
+XXXX XXX XXX XXX XXX XXXX XXXX
+XXXX XXX XXX XXX XXX XXXX XXXX
+XXXX XXX XXXX XXX XXXX XXXXXXXXXX XXX XXXX XXXXXXXXXX
+XXXX XXX XXXX XXX XXXX XXXXXXXXXX XXX XXXX XXXXXXXXXX
+XXXX XXX XXXX XXX XXXX XXXXXXXXXX XXX XXXX XXXXXXXXXX
+XXXX XXX XXXX XXXX XXXX XXXX XXXX
+XXXX XXX XXXX XXXX XXXX XXXX XXXX
+XXXX XXX XXXX XXXX XXXX XXXX XXXX
+XXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXX
+XXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXX
+XXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXX
+XXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXXX
+XXXX XXX XXXX XXX XXXX XXX XXXX
+XXXX XXX XXXX XXX XXXX XXX XXXX
+XXXX XXX XXXX XXX XXXX XXX XXXX
+XXXX XXX XXXX XXXXXXXXXXXXXXXX XXXX XXXXXXXXXXXXXXXX XXXX
+XXXX XXX XXXX XXXXXXXXXXXXXXXX XXXX XXXXXXXXXXXXXXXX XXXX
+XXXX XXX XXXX XXXXXXXXXXXXXXXX XXXX XXXXXXXXXXXXXXXX XXXX
+XXXX XXX XXXX XXX XXX XXXX XXXX XXXX
+XXXX XXX XXXX XXX XXX XXXX XXXX XXXX
+XXXX XXX XXXX XXX XXX XXXX XXXX XXXX
+XXXX XXX XXXX XXX XXXX XXX XXXX XXXXXXXXXX XXXXXXXXXX
+XXXX XXX XXXX XXX XXXX XXX XXXX XXXXXXXXXX XXXXXXXXXX
+XXXX XXX XXXX XXX XXXX XXX XXXX XXXXXXXXXX XXXXXXXXXX
+XXXX XXX XXXX XXX XXXX XXX XXXX XXXXXXXXXX XXXXXXXXXX
+XXXX XXX XXXX XXX XXXX XXXX XXX XXX XXXX
+XXXX XXX XXXX XXX XXXX XXXX XXX XXX XXXX
+XXXX XXX XXXX XXX XXXX XXXX XXX XXX XXXX
+XXXX XXX XXXX XXX XXXXXXXXXXXXXXXXX XXX XXXXXXXXXX XXXX
+XXXX XXX XXXX XXX XXXXXXXXXXXXXXXXX XXX XXXXXXXXXX XXXX
+XXXX XXX XXXX XXX XXXXXXXXXXXXXXXXX XXX XXXXXXXXXX XXXX
+XXXX XXX XXXX XXX XXXX XXX XXX XXXX
+XXXX XXX XXXX XXX XXXX XXX XXX XXXX
+XXXX XXX XXXX XXX XXXX XXX XXX XXXX
+XXXX XXX XXXX XXX XXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXX XXX XXXX XXX XXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXX XXX XXXX XXX XXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXX XXX XXXX XXX XXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXX XXX XXXX XXX XXX XXXX
+XXXX XXX XXXX XXX XXX XXXX
+XXXX XXX XXXX XXX XXX XXXX
+XXXX XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXX XXXX
+XXXX XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXX XXXX
+XXXX XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXX XXXX
+XXXX XXX XXXX XXX XXXX XXXX
+XXXX XXX XXXX XXX XXXX XXXX
+XXXX XXX XXXX XXX XXXX XXXX
+XXXX XXX XXXX XXXXXXXXXXXXXXXX XXXXXXXXXX XXXX XXXXXXXXXX
+XXXX XXX XXXX XXXXXXXXXXXXXXXX XXXXXXXXXX XXXX XXXXXXXXXX
+XXXX XXX XXXX XXXXXXXXXXXXXXXX XXXXXXXXXX XXXX XXXXXXXXXX
+XXXX XXX XXXX XXXXXXXXXXXXXXXX XXXXXXXXXX XXXX XXXXXXXXXX
+XXXX XXX XXX XXX XXXX XXX XXXX
+XXXX XXX XXX XXX XXXX XXX XXXX
+XXXX XXX XXX XXX XXXX XXX XXXX
+XXXX XXX XXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXX XXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXX XXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXX XXXX XXX XXX XXXX
+XXXX XXX XXXX XXX XXX XXXX
+XXXX XXX XXXX XXX XXX XXXX
+XXXX XXXXXXXXXXXXXXXX XXXX XXXXXXXXXX XXX XXXXXXXXXX XXXX
+XXXX XXXXXXXXXXXXXXXX XXXX XXXXXXXXXX XXX XXXXXXXXXX XXXX
+XXXX XXXXXXXXXXXXXXXX XXXX XXXXXXXXXX XXX XXXXXXXXXX XXXX
+XXXX XXXXXXXXXXXXXXXX XXXX XXXXXXXXXX XXX XXXXXXXXXX XXXX
+XXXX XXX XXXX XXXX XXXX XXXX
+XXXX XXX XXXX XXXX XXXX XXXX
+XXXX XXX XXXX XXXX XXXX XXXX
+XXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXX XXXX XXX XXX XXX XXXX
+XXXX XXX XXXX XXX XXX XXX XXXX
+XXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXX XXXXXXXXXX XXXX
+XXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXX XXXXXXXXXX XXXX
+XXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXX XXXXXXXXXX XXXX
+XXXX XXXXXXXXXX XXXXXXXXXX XXXXXXXXXX XXX XXXXXXXXXX XXXX
+XXXX XXX XXX XXXX XXXX XXX XXXX
+XXXX XXX XXX XXXX XXXX XXX XXXX
+XXXX XXX XXX XXXX XXXX XXX XXXX
+XXXX XXXXXXXXXXXXXXXX XXXX XXX XXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXXXXXXXXXXXXXXX XXXX XXX XXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXXXXXXXXXXXXXXX XXXX XXX XXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXXXXXXXXXXXXXXX XXXX XXX XXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXX XXX XXXX XXX XXXX
+XXXX XXX XXX XXXX XXX XXXX
+XXXXXXXXXX XXX XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXXXXXXXX XXXX XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXXXXXXXX XXXX XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXXXXXXXX XXXX XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXX XXXX XXXX XXX XXXX
+XXXX XXXX XXXX XXX XXXX
+XXXX XXXX XXXX XXX XXXX
+XXXX XXXXXXXXXXXXXXXX XXXX XXXXXXXXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXXXXXXXXXXXXXXX XXXX XXXXXXXXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXXXXXXXXXXXXXXX XXXX XXXXXXXXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXXXXXXXXXXXXXXX XXXX XXXXXXXXXXXXXXXXXXXXXXX XXX XXXX
+XXXX XXX XXX XXXX XXXX XXXX XXX XXXX
+XXXX XXX XXX XXXX XXXX XXXX XXX XXXX
+XXXX XXX XXX XXXX XXXX XXXX XXX XXXX
+XXXX XXX XXXX XXX XXXX XXXXXXXXXX XXX XXXX XXX XXXX
+XXXX XXX XXXX XXX XXXX XXXXXXXXXX XXX XXXX XXX XXXX
+XXXX XXX XXXX XXX XXXX XXXXXXXXXX XXX XXXX XXX XXXX
+XXXX XXX XXXX XXXX XXX XXX XXX XXXX
+XXXX XXX XXXX XXXX XXX XXX XXX XXXX
+XXXX XXX XXXX XXXX XXX XXX XXX XXXX
+XXXX XXXXXXXXXXXXXXXXXXXXXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXX XXXXXXXXXXXXXXXXXXXXXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXX XXXXXXXXXXXXXXXXXXXXXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXX
+XXXX XXXXXXXXXXXXXXXXXXXXXXX XXX XXXXXXXXXXXXXXXXXXXXXXX XXXX
+ XXXX XXX
+@_K XXXX XXX [ exit ]
+ XXXX XXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXX
+
+and you must guide your snail @_K through the maze in record time.
+
+This seems like an ideal thing to build using something like <canvas> too.
\ No newline at end of file
--- /dev/null
+<?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 GNUsocialPhotoPlugin extends MicroAppPlugin
+{
+
+ function onCheckSchema()
+ {
+ $schema = Schema::get();
+
+ $schema->ensureTable('photo', Photo::schemaDef());
+
+ return true;
+ }
+
+ function onAutoload($cls)
+ {
+ $dir = dirname(__FILE__);
+ switch($cls)
+ {
+ case 'Photo':
+ include_once $dir . '/Photo.php';
+ break;
+ case 'NewPhotoForm':
+ include_once $dir . '/newphotoform.php';
+ break;
+ case 'NewphotoAction':
+ include_once $dir . '/newphoto.php';
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ function onRouterInitialized($m)
+ {
+ $m->connect('main/photo/new', array('action' => 'newphoto'));
+ $m->connect('main/photo/:id', array('action' => 'showphoto'));
+ return true;
+ }
+
+ function entryForm($out)
+ {
+ return new NewPhotoForm($out);
+ }
+
+ function appTitle()
+ {
+ return _('Photo');
+ }
+
+ function tag()
+ {
+ return 'Photo';
+ }
+
+ function types()
+ {
+ return array(Photo::OBJECT_TYPE);
+ }
+
+ function saveNoticeFromActivity($activity, $actor, $options=array())
+ {
+
+ if(count($activity->objects) != 1) {
+ throw new Exception('Too many activity objects.');
+ }
+
+ $photoObj = $activity->objects[0];
+
+ if ($photoObj->type != Photo::OBJECT_TYPE) {
+ throw new Exception('Wrong type for object.');
+ }
+
+ $photo_uri = $photoObj->largerImage;
+ $thumb_uri = $photo_uri;
+ if(!empty($photoObj->thumbnail)){
+ $thumb_uri = $photoObj->thumbnail;
+ }
+
+ $description = $photoObj->description;
+ $title = $photoObj->title;
+
+ $options['object_type'] = Photo::OBJECT_TYPE;
+
+ Photo::saveNew($actor, $photo_uri, $thumb_uri, $title, $description, $options);
+
+ }
+
+ function activityObjectFromNotice($notice)
+ {
+
+ $photo = Photo::getByNotice($notice);
+
+ $object = new ActivityObject();
+ $object->id = $notice->uri;
+ $object->type = Photo::OBJECT_TYPE;
+ $object->title = $photo->title;
+ $object->summary = $notice->content;
+ $object->link = $notice->bestUrl();
+
+ $object->largerImage = $photo->photo_uri;
+ $object->thumbnail = $photo->thumb_uri;
+ $object->description = $photo->description;
+
+ return $object;
+
+ }
+
+ function showNotice($notice, $out)
+ {
+ $photo = Photo::getByNotice($notice);
+ if ($photo) {
+ if($photo->title){
+ // TODO: ugly. feel like we should have a more abstract way
+ // of choosing the h-level.
+ $out->element('h3', array(), $title);
+ }
+ $out->element('img', array('src' => $photo->photo_uri,
+ 'width' => '100%'));
+ // TODO: add description
+ }
+ }
+
+ function deleteRelated($notice)
+ {
+ $photo = Photo::getByNotice($notice);
+ if ($photo) {
+ $photo->delete();
+ }
+ }
+}
--- /dev/null
+<?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 photos.
+ */
+
+class Photo extends Managed_DataObject
+{
+ const OBJECT_TYPE = 'http://activitystrea.ms/schema/1.0/photo';
+
+ public $__table = 'photo'; // table name
+ public $id; // char (36) // UUID
+ public $uri; // varchar (255) // This is the corresponding notice's uri.
+ public $photo_uri; // varchar (255)
+ public $thumb_uri; // varchar (255)
+ public $title; // varchar (255)
+ public $description; // text
+ public $profile_id; // int
+
+ public function staticGet($k, $v=null)
+ {
+ return Memcached_DataObject::staticGet('photo', $k, $v);
+ }
+
+ public function getByNotice($notice)
+ {
+ return self::staticGet('uri', $notice->uri);
+ }
+
+ public function getNotice()
+ {
+ return Notice::staticGet('uri', $this->uri);
+ }
+
+ public static function schemaDef()
+ {
+ return array(
+ 'description' => 'A photograph',
+ 'fields' => array(
+ 'id' => array('type' => 'char',
+ 'length' => 36,
+ 'not null' => true,
+ 'description' => 'UUID'),
+ 'uri' => array('type' => 'varchar',
+ 'length' => 255,
+ 'not null' => true),
+ 'photo_uri' => array('type' => 'varchar',
+ 'length' => 255,
+ 'not null' => true),
+ 'photo_uri' => array('type' => 'varchar',
+ 'length' => 255,
+ 'not null' => true),
+ 'profile_id' => array('type' => 'int', 'not null' => true),
+ ),
+ 'primary key' => array('id'),
+ 'foreign keys' => array('photo_profile_id__key' => array('profile' => array('profile_id' => 'id'))),
+ );
+ }
+
+ function saveNew($profile, $photo_uri, $thumb_uri, $title, $description, $options=array())
+ {
+ $photo = new Photo();
+
+ $photo->id = UUID::gen();
+ $photo->profile_id = $profile->id;
+ $photo->photo_uri = $photo_uri;
+ $photo->thumb_uri = $thumb_uri;
+
+
+ $options['object_type'] = Photo::OBJECT_TYPE;
+
+ if (!array_key_exists('uri', $options)) {
+ $options['uri'] = common_local_url('showphoto', array('id' => $photo->id));
+ }
+
+ if (!array_key_exists('rendered', $options)) {
+ $options['rendered'] = sprintf("<img src=\"%s\" alt=\"%s\"></img>", $photo_uri,
+ $title);
+ }
+
+ $photo->uri = $options['uri'];
+
+ $photo->insert();
+
+ return Notice::saveNew($profile->id,
+ '',
+ 'web',
+ $options);
+
+ }
+}
--- /dev/null
+<?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 NewphotoAction extends Action
+{
+ var $user = 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 photo'),
+ 403);
+ }
+
+ if($this->isPost()){
+ $this->checkSessionToken();
+ }
+
+ return true;
+ }
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if ($this->isPost()) {
+ $this->handlePost($args);
+ } else {
+ $this->showPage();
+ }
+ }
+
+ function handlePost($args)
+ {
+
+ /*
+ // 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;
+ } */
+
+ $profile = $this->user->getProfile();
+
+ $options = array();
+
+ ToSelector::fillOptions($this, $options);
+
+ try {
+ $this->handleUpload();
+ } catch (Exception $e) {
+ $this->showForm($e->getMessage());
+ return;
+ }
+
+
+ common_redirect($photo->uri, 303);
+ }
+
+ function getUpload()
+ {
+ $imagefile = ImageFile::fromUpload('photo_upload');
+
+ if($imagefile === null) {
+ throw new Exception(_('No file uploaded'));
+ }
+
+ $title = $this->trimmed('title');
+ $description = $this->trimmed('description');
+
+ $new_filename = UUID::gen() . image_type_to_extension($imagefile->type);
+ move_uploaded_file($imagefile->filepath, INSTALLDIR . '/file/' . $new_filename);
+
+ // XXX: we should be using https where we can. TODO: detect whether the server
+ // supports this.
+ $photo_uri = 'http://' . common_config('site', 'server') . '/file/'
+ . $new_filename;
+ $thumb_uri = $photo_uri;
+
+
+ $photo = Photo::saveNew($profile, $photo_uri, $thumb_uri, $title,
+ $description, $options);
+
+ }
+
+ function showContent()
+ {
+ $form = new NewPhotoForm();
+ $form->show();
+ }
+}
+
+
--- /dev/null
+<?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'));
+ }
+}
--- /dev/null
+<?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);
+}
+
+
--- /dev/null
+<?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);
+}
+
+class GNUsocialPhotosPlugin extends Plugin
+{
+
+ function onAutoload($cls)
+ {
+ $dir = dirname(__FILE__);
+
+ include_once $dir . '/lib/tempphoto.php';
+ include_once $dir . '/lib/photonav.php';
+ switch ($cls)
+ {
+ case 'PhotosAction':
+ include_once $dir . '/lib/photolib.php';
+ include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
+ break;
+ case 'PhotouploadAction':
+ include_once $dir . '/lib/photolib.php';
+ include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
+ break;
+ case 'PhotoAction':
+ include_once $dir . '/lib/photolib.php';
+ include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
+ break;
+ case 'EditphotoAction':
+ include_once $dir . '/lib/photolib.php';
+ include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
+ break;
+ default:
+ break;
+ }
+ include_once $dir . '/classes/gnusocialphoto.php';
+ include_once $dir . '/classes/gnusocialphotoalbum.php';
+ return true;
+ }
+
+ function onCheckSchema()
+ {
+ $schema = Schema::get();
+ $schema->ensureTable('GNUsocialPhoto',
+ array(new ColumnDef('id', 'int(11)', null, false, 'PRI', null, null, true),
+ new ColumnDef('notice_id', 'int(11)', null, false),
+ new ColumnDef('album_id', 'int(11)', null, false),
+ new ColumnDef('uri', 'varchar(512)', null, false),
+ new ColumnDef('thumb_uri', 'varchar(512)', null, false),
+ new ColumnDef('title', 'varchar(512)', null, false),
+ new ColumnDef('photo_description', 'text', null, false)));
+ $schema->ensureTable('GNUsocialPhotoAlbum',
+ array(new ColumnDef('album_id', 'int(11)', null, false, 'PRI', null, null, true),
+ new ColumnDef('profile_id', 'int(11)', null, false),
+ new ColumnDef('album_name', 'varchar(256)', null, false),
+ new ColumnDef('album_description', 'text', null, false)));
+
+ }
+
+ 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, &$activity)
+ {
+ common_log(LOG_INFO, 'photo plugin: EndNoticeAsActivity');
+ $photo = GNUsocialPhoto::staticGet('notice_id', $notice->id);
+ if(!$photo) {
+ common_log(LOG_INFO, 'not a photo.');
+ return true;
+ }
+
+ $activity->objects[0]->type = ActivityObject::PHOTO;
+ $activity->objects[0]->thumbnail = $photo->thumb_uri;
+ $activity->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::staticGet('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($nav)
+ {
+
+ $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');
+ }
+}
+
--- /dev/null
+<?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::staticGet('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::staticGet('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');
+ $this->showForm(_('Success!'), true);
+
+ }
+
+ function deletePhoto()
+ {
+ //For redirection
+ $oldalbum = $this->album_id;
+
+ $notice = Notice::staticGet('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');
+ return;
+ }
+
+}
--- /dev/null
+<?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);
+}
+
+include_once INSTALLDIR . '/actions/conversation.php';
+include_once INSTALLDIR . '/classes/Notice.php';
+
+class PhotoAction extends Action
+{
+ var $user = null;
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $args = $this->returnToArgs();
+ $this->photoid = $args[1]['photoid'];
+ $this->photo = GNUsocialPhoto::staticGet('id', $this->photoid);
+ $this->notice = Notice::staticGet('id', $this->photo->notice_id);
+
+ $this->user = Profile::staticGet('id', $this->notice->profile_id);
+
+ $notices = Notice::conversationStream((int)$this->notice->conversation, null, null);
+ $this->conversation = new ConversationTree($notices, $this);
+ return true;
+
+ }
+
+ function handle($args)
+ {
+ parent::handle($args);
+ $this->showPage();
+ }
+
+ 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 }");
+ $this->conversation->show();
+ }
+}
--- /dev/null
+<?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::staticGet('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::staticGet('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();
+ }
+}
--- /dev/null
+<?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::staticGet('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::staticGet('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::staticGet('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);
+ }
+ }
+}
--- /dev/null
+<?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 Memcached_DataObject
+{
+ public $__table = 'GNUsocialPhoto';
+ public $id; // int(11)
+ public $notice_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;
+ }
+}
--- /dev/null
+<?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 Memcached_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(256) -- Title for this album
+ public $album_description; // text -- A description of the album
+
+
+ function staticGet($k,$v=NULL)
+ {
+ return Memcached_DataObject::staticGet('GNUsocialPhotoAlbum',$k,$v);
+ }
+
+
+ /* TODO: Primary key on both album_id, profile_id / foriegn key on profile_id */
+ function table()
+ {
+ return array('album_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
+ 'profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
+ 'album_name' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
+ 'album_description' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL);
+ }
+
+ function keys()
+ {
+ return array_keys($this->keyTypes());
+ }
+
+
+ /* Using album_id as the primary key for now.. */
+ function keyTypes()
+ {
+ return array('album_id' => 'K');
+ }
+
+ function sequenceKey()
+ {
+ return array('album_id', true, false);
+ }
+
+ function getPageLink()
+ {
+ $profile = Profile::StaticGet('id', $this->profile_id);
+ return '/' . $profile->nickname . '/photos/' . $this->album_id;
+ }
+
+ function getThumbUri()
+ {
+ $photo = GNUsocialPhoto::staticGet('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::staticGet('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;
+ }
+
+}
--- /dev/null
+<?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;
+}
--- /dev/null
+<?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');
+ }
+}
--- /dev/null
+<?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;
+}
--- /dev/null
+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;
+}
\ No newline at end of file
--- /dev/null
+.photocontainer, .albumcontainer {
+ display: block;
+ background-color: yellow;
+ float: left;
+ padding: 20px;
+ margin: 15px;
+}
+
+.photodescription {
+ display: block;
+ background-color: #dddddd;
+ padding: 20px;
+ margin: 15px;
+}
\ No newline at end of file
--- /dev/null
+<?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);
+}
+
+class GNUsocialProfileExtensionsPlugin extends Plugin
+{
+
+ function onAutoload($cls)
+ {
+ $dir = dirname(__FILE__);
+
+ switch ($cls)
+ {
+ case 'BioAction':
+ case 'NewresponseAction':
+ 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';
+ include_once $dir . '/lib/noticetree.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'));
+ $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($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');
+ }
+
+ //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;
+ }
+
+}
+
--- /dev/null
+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';
+
--- /dev/null
+<?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::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');
+ }
+
+}
+
+
--- /dev/null
+<?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();
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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::staticGet('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!'));
+ return false;
+ }
+ if ($field->id) {
+ if ($field->validate())
+ $field->update();
+ else {
+ $this->clientError(_('There was an error with the field data.'));
+ return false;
+ }
+ }
+ 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::staticGet('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'));
+ }
+}
--- /dev/null
+<?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 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;
+ }
+}
--- /dev/null
+<?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 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;
+ }
+
+}
--- /dev/null
+<?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 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;
+ }
+}
--- /dev/null
+SN.U.NoticeReplySet = function(nick,id) {
+ $('div.replyform').hide();
+ $('div#form'+id).show();
+}
--- /dev/null
+<?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' => 'hentry 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;
+ }
+
+ function showContext()
+ {
+ 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->user->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));
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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);
+}
\ No newline at end of file
--- /dev/null
+.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%;
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Plugin to render GNU social
+ *
+ * Captures rendered parts from the output buffer, passes them through a template file: tpl/social.php
+ * Adds an API method at index.php/template/update which lets you overwrite the template file
+ * Requires username/password and a single POST parameter called "template"
+ * The method is disabled unless the user is #1, the first user of the system
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Brian Hendrickson <brian@megapump.com>
+ * @author Matt Lee <mattl@cnuk.org>
+ * @copyright 2009 Megapump, Inc.
+ * @copyright 2010 Free Software Foundation, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://megapump.com/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+define('TEMPLATEPLUGIN_VERSION', '0.1');
+
+class TemplatePlugin extends Plugin {
+
+ var $blocks = array();
+
+ function __construct() {
+ parent::__construct();
+ }
+
+ // capture the RouterInitialized event
+ // and connect a new API method
+ // for updating the template
+ function onRouterInitialized( $m ) {
+ $m->connect( 'template/update', array(
+ 'action' => 'template',
+ ));
+ }
+
+ // <%styles%>
+ // <%scripts%>
+ // <%search%>
+ // <%feeds%>
+ // <%description%>
+ // <%head%>
+ function onStartShowHead( $act ) {
+ $this->clear_xmlWriter($act);
+ $act->extraHead();
+ $this->blocks['head'] = $act->xw->flush();
+ $act->showStylesheets();
+ $this->blocks['styles'] = $act->xw->flush();
+ $act->showScripts();
+ $this->blocks['scripts'] = $act->xw->flush();
+ $act->showFeeds();
+ $this->blocks['feeds'] = $act->xw->flush();
+ $act->showOpenSearch();
+ $this->blocks['search'] = $act->xw->flush();
+ $act->showDescription();
+ $this->blocks['description'] = $act->xw->flush();
+ return false;
+ }
+
+ // <%bodytext%>
+ function onStartShowContentBlock( $act ) {
+ $this->clear_xmlWriter($act);
+ return true;
+ }
+ function onEndShowContentBlock( $act ) {
+ $this->blocks['bodytext'] = $act->xw->flush();
+ }
+
+ // <%localnav%>
+ function onStartShowLocalNavBlock( $act ) {
+ $this->clear_xmlWriter($act);
+ return true;
+ }
+ function onEndShowLocalNavBlock( $act ) {
+ $this->blocks['localnav'] = $act->xw->flush();
+ }
+
+ // <%export%>
+ function onStartShowExportData( $act ) {
+ $this->clear_xmlWriter($act);
+ return true;
+ }
+ function onEndShowExportData( $act ) {
+ $this->blocks['export'] = $act->xw->flush();
+ }
+
+ // <%subscriptions%>
+ // <%subscribers%>
+ // <%groups%>
+ // <%statistics%>
+ // <%cloud%>
+ // <%groupmembers%>
+ // <%groupstatistics%>
+ // <%groupcloud%>
+ // <%popular%>
+ // <%groupsbyposts%>
+ // <%featuredusers%>
+ // <%groupsbymembers%>
+ function onStartShowSections( $act ) {
+ global $action;
+ $this->clear_xmlWriter($act);
+ switch ($action) {
+ case "showstream":
+ $act->showSubscriptions();
+ $this->blocks['subscriptions'] = $act->xw->flush();
+ $act->showSubscribers();
+ $this->blocks['subscribers'] = $act->xw->flush();
+ $act->showGroups();
+ $this->blocks['groups'] = $act->xw->flush();
+ $act->showStatistics();
+ $this->blocks['statistics'] = $act->xw->flush();
+ $cloud = new PersonalTagCloudSection($act, $act->user);
+ $cloud->show();
+ $this->blocks['cloud'] = $act->xw->flush();
+ break;
+ case "showgroup":
+ $act->showMembers();
+ $this->blocks['groupmembers'] = $act->xw->flush();
+ $act->showStatistics();
+ $this->blocks['groupstatistics'] = $act->xw->flush();
+ $cloud = new GroupTagCloudSection($act, $act->group);
+ $cloud->show();
+ $this->blocks['groupcloud'] = $act->xw->flush();
+ break;
+ case "public":
+ $pop = new PopularNoticeSection($act);
+ $pop->show();
+ $this->blocks['popular'] = $act->xw->flush();
+ $gbp = new GroupsByPostsSection($act);
+ $gbp->show();
+ $this->blocks['groupsbyposts'] = $act->xw->flush();
+ $feat = new FeaturedUsersSection($act);
+ $feat->show();
+ $this->blocks['featuredusers'] = $act->xw->flush();
+ break;
+ case "groups":
+ $gbp = new GroupsByPostsSection($act);
+ $gbp->show();
+ $this->blocks['groupsbyposts'] = $act->xw->flush();
+ $gbm = new GroupsByMembersSection($act);
+ $gbm->show();
+ $this->blocks['groupsbymembers'] = $act->xw->flush();
+ break;
+ }
+ return false;
+ }
+
+ // <%logo%>
+ // <%nav%>
+ // <%notice%>
+ // <%noticeform%>
+ function onStartShowHeader( $act ) {
+ $this->clear_xmlWriter($act);
+ $act->showLogo();
+ $this->blocks['logo'] = $act->xw->flush();
+ $act->showPrimaryNav();
+ $this->blocks['nav'] = $act->xw->flush();
+ $act->showSiteNotice();
+ $this->blocks['notice'] = $act->xw->flush();
+ if (common_logged_in()) {
+ $act->showNoticeForm();
+ } else {
+ $act->showAnonymousMessage();
+ }
+ $this->blocks['noticeform'] = $act->xw->flush();
+ return false;
+ }
+
+ // <%secondarynav%>
+ // <%licenses%>
+ function onStartShowFooter( $act ) {
+ $this->clear_xmlWriter($act);
+ $act->showSecondaryNav();
+ $this->blocks['secondarynav'] = $act->xw->flush();
+ $act->showLicenses();
+ $this->blocks['licenses'] = $act->xw->flush();
+ return false;
+ }
+
+ // capture the EndHTML event
+ // and include the template
+ function onEndEndHTML($act) {
+
+ global $action, $tags;
+
+ // set the action and title values
+ $vars = array(
+ 'action'=>$action,
+ 'title'=>$act->title(). " - ". common_config('site', 'name')
+ );
+
+ // use the PHP template
+ // unless statusnet config:
+ // $config['template']['mode'] = 'html';
+ if (!(common_config('template', 'mode') == 'html')) {
+ $tpl_file = $this->templateFolder() . '/social.php';
+ $tags = array_merge($vars,$this->blocks);
+ include $tpl_file;
+ return;
+ }
+
+ $tpl_file = $this->templateFolder() . '/index.html';
+
+ // read the static template
+ $output = file_get_contents( $tpl_file );
+
+ $tags = array();
+
+ // get a list of the <%tags%> in the template
+ $pattern='/<%([a-z]+)%>/';
+
+ if ( 1 <= preg_match_all( $pattern, $output, $found ))
+ $tags[] = $found;
+
+ // for each found tag, set its value from the rendered blocks
+ foreach( $tags[0][1] as $pos=>$tag ) {
+ if (isset($this->blocks[$tag]))
+ $vars[$tag] = $this->blocks[$tag];
+
+ // didn't find a block for the tag
+ elseif (!isset($vars[$tag]))
+ $vars[$tag] = '';
+ }
+
+ // replace the tags in the template
+ foreach( $vars as $key=>$val )
+ $output = str_replace( '<%'.$key.'%>', $val, $output );
+
+ echo $output;
+
+ return true;
+
+ }
+ function templateFolder() {
+ return 'tpl';
+ }
+
+ // catching the StartShowHTML event to halt the rendering
+ function onStartShowHTML( $act ) {
+ $this->clear_xmlWriter($act);
+ return true;
+ }
+
+ // clear the xmlWriter
+ function clear_xmlWriter( $act ) {
+ $act->xw->openMemory();
+ $act->xw->setIndent(true);
+ }
+
+}
+
+/**
+ * Action for updating the template remotely
+ *
+ * "template/update" -- a POST method that requires a single
+ * parameter "template", containing the new template code
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Brian Hendrickson <brian@megapump.com>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://megapump.com/
+ *
+ */
+
+class TemplateAction extends Action
+{
+
+ function prepare($args) {
+ parent::prepare($args);
+ return true;
+ }
+
+ function handle($args) {
+
+ parent::handle($args);
+
+ if (!isset($_SERVER['PHP_AUTH_USER'])) {
+
+ // not authenticated, show login form
+ header('WWW-Authenticate: Basic realm="StatusNet API"');
+
+ // cancelled the browser login form
+ $this->clientError(_('Authentication error!'), $code = 401);
+
+ } else {
+
+ $nick = $_SERVER['PHP_AUTH_USER'];
+ $pass = $_SERVER['PHP_AUTH_PW'];
+
+ // check username and password
+ $user = common_check_user($nick,$pass);
+
+ if ($user) {
+
+ // verify that user is admin
+ if (!($user->id == 1))
+ $this->clientError(_('Only User #1 can update the template.'), $code = 401);
+
+ // open the old template
+ $tpl_file = $this->templateFolder() . '/index.html';
+ $fp = fopen( $tpl_file, 'w+' );
+
+ // overwrite with the new template
+ fwrite($fp, $this->arg('template'));
+ fclose($fp);
+
+ header('HTTP/1.1 200 OK');
+ header('Content-type: text/plain');
+ print "Template Updated!";
+
+ } else {
+
+ // bad username and password
+ $this->clientError(_('Authentication error!'), $code = 401);
+
+ }
+
+ }
+ }
+ function onPluginVersion(&$versions)
+ {
+ $versions[] = array('name' => 'Template',
+ 'version' => TEMPLATEPLUGIN_VERSION,
+ 'author' => 'Brian Hendrickson',
+ 'homepage' => 'http://status.net/wiki/Plugin:Template',
+ 'rawdescription' =>
+ _m('Use an HTML template for Web output.'));
+ return true;
+ }
+
+}
+
+/**
+ * Function for retrieving a statusnet display section
+ *
+ * requires one parameter, the name of the section
+ * section names are listed in the comments of the TemplatePlugin class
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Brian Hendrickson <brian@megapump.com>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://megapump.com/
+ *
+ */
+
+function section($tagname) {
+ global $tags;
+ if (isset($tags[$tagname]))
+ return $tags[$tagname];
+}
+
--- /dev/null
+<?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
+{
+
+ function onCheckSchema()
+ {
+ $schema = Schema::get();
+
+ $schema->ensureTable('video', Video::schemaDef());
+
+ return true;
+ }
+
+ function onAutoload($cls)
+ {
+ $dir = dirname(__FILE__);
+ switch($cls)
+ {
+ case 'PostvideoAction':
+ include_once $dir . '/actions/postvideo.php';
+ break;
+ case 'Video':
+ include_once $dir . '/Video.php';
+ break;
+ case 'VideoForm':
+ include_once $dir . '/videoform.php';
+ break;
+ case 'ShowvideoAction':
+ include_once $dir . '/showvideo.php';
+ break;
+ default:
+ break;
+ }
+ 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, $actor, $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)
+ {
+ $object = new ActivityObject();
+ $object->id = $notice->uri;
+ $object->type = Video::OBJECT_TYPE;
+ $object->title = $notice->content;
+ $object->summary = $notice->content;
+ $object->link = $notice->bestUrl();
+
+ $vid = Video::getByNotice($notice);
+
+ if ($vid) {
+ $object->extra[] = array('link', array('rel' => 'enclosure', 'href' => $vid->url), array());
+ }
+
+ return $object;
+
+ }
+
+ function showNotice($notice, $out)
+ {
+ $vid = Video::getByNotice($notice);
+ if ($vid) {
+ $out->element('video', array('src' => $vid->url,
+ 'width' => '100%',
+ 'controls' => 'controls'));
+ }
+ }
+
+ function deleteRelated($notice)
+ {
+ $vid = Video::getByNotice($notice);
+ if ($vid) {
+ $vid->delete();
+ }
+ }
+}
--- /dev/null
+<?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 function staticGet($k, $v=null)
+ {
+ return Memcached_DataObject::staticGet('Video', $k, $v);
+ }
+
+ public function getByNotice($notice)
+ {
+ return self::staticGet('uri', $notice->uri);
+ }
+
+ public function getNotice()
+ {
+ return Notice::staticGet('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'))),
+ );
+ }
+
+ function saveNew($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);
+
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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::staticGet('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::staticGet('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;
+ }
+}
--- /dev/null
+<?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'));
+ }
+}
--- /dev/null
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2012, StatusNet, Inc.
+ *
+ * ModLog.php -- data object to store moderation logs
+ *
+ * PHP version 5
+ *
+ * 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 Moderation
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * Class comment here
+ *
+ * @category Category here
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * @see DB_DataObject
+ */
+
+class ModLog extends Managed_DataObject
+{
+ public $__table = 'mod_log'; // table name
+
+ public $id; // UUID
+ public $profile_id; // profile id
+ public $moderator_id; // profile id
+ public $role; // the role
+ public $grant; // 1 = grant, 0 = revoke
+ public $created; // datetime
+
+ /**
+ * Get an instance by key
+ *
+ * @param string $k Key to use to lookup (usually 'user_id' for this class)
+ * @param mixed $v Value to lookup
+ *
+ * @return TagSub object found, or null for no hits
+ *
+ */
+ function staticGet($k, $v=null)
+ {
+ return Managed_DataObject::staticGet('ModLog', $k, $v);
+ }
+
+ /**
+ * Get an instance by compound key
+ *
+ * @param array $kv array of key-value mappings
+ *
+ * @return TagSub object found, or null for no hits
+ *
+ */
+ function pkeyGet($kv)
+ {
+ return Managed_DataObject::pkeyGet('ModLog', $kv);
+ }
+
+ /**
+ * The One True Thingy that must be defined and declared.
+ */
+ public static function schemaDef()
+ {
+ return array('description' => 'Log of moderation events',
+ 'fields' => array(
+ 'id' => array('type' => 'varchar',
+ 'length' => 36,
+ 'not null' => true,
+ 'description' => 'unique event ID'),
+ 'profile_id' => array('type' => 'int',
+ 'not null' => true,
+ 'description' => 'profile getting the role'),
+ 'moderator_id' => array('type' => 'int',
+ 'description' => 'profile granting or revoking the role'),
+ 'role' => array('type' => 'varchar',
+ 'length' => 32,
+ 'not null' => true,
+ 'description' => 'role granted or revoked'),
+ 'is_grant' => array('type' => 'int',
+ 'size' => 'tiny',
+ 'default' => 1,
+ 'description' => 'Was this a grant or revocation of a role'),
+ 'created' => array('type' => 'datetime',
+ 'not null' => true,
+ 'description' => 'date this record was created')
+ ),
+ 'primary key' => array('id'),
+ 'foreign keys' => array(
+ 'mod_log_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
+ 'mod_log_moderator_id_fkey' => array('user', array('user_id' => 'id'))
+ ),
+ 'indexes' => array(
+ 'mod_log_profile_id_created_idx' => array('profile_id', 'created'),
+ ),
+ );
+ }
+}
--- /dev/null
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2012, StatusNet, Inc.
+ *
+ * ModLogPlugin.php
+ *
+ * PHP version 5
+ *
+ * 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 Moderation
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Moderation logging
+ *
+ * Shows a history of moderation for this user in the sidebar
+ *
+ * @category Moderation
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class ModLogPlugin extends Plugin
+{
+ const VIEWMODLOG = 'ModLogPlugin::VIEWMODLOG';
+
+ /**
+ * Database schema setup
+ *
+ * We keep a moderation log table
+ *
+ * @see Schema
+ * @see ColumnDef
+ *
+ * @return boolean hook value; true means continue processing, false means stop.
+ */
+
+ function onCheckSchema()
+ {
+ $schema = Schema::get();
+
+ $schema->ensureTable('mod_log', ModLog::schemaDef());
+
+ return true;
+ }
+
+ /**
+ * Load related modules when needed
+ *
+ * @param string $cls Name of the class to be loaded
+ *
+ * @return boolean hook value; true means continue processing, false means stop.
+ */
+
+ function onAutoload($cls)
+ {
+ $dir = dirname(__FILE__);
+
+ switch ($cls)
+ {
+ case 'ModLog':
+ include_once $dir . '/'.$cls.'.php';
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ function onEndGrantRole($profile, $role)
+ {
+ $modlog = new ModLog();
+
+ $modlog->id = UUID::gen();
+ $modlog->profile_id = $profile->id;
+
+ $cur = common_current_user();
+
+ if (!empty($cur)) {
+ $modlog->moderator_id = $cur->id;
+ }
+
+ $modlog->role = $role;
+ $modlog->is_grant = 1;
+ $modlog->created = common_sql_now();
+
+ $modlog->insert();
+
+ return true;
+ }
+
+ function onEndRevokeRole($profile, $role)
+ {
+ $modlog = new ModLog();
+
+ $modlog->id = UUID::gen();
+
+ $modlog->profile_id = $profile->id;
+
+ $cur = common_current_user();
+
+ if (!empty($cur)) {
+ $modlog->moderator_id = $cur->id;
+ }
+
+ $modlog->role = $role;
+ $modlog->is_grant = 0;
+ $modlog->created = common_sql_now();
+
+ $modlog->insert();
+
+ return true;
+ }
+
+ function onEndShowSections($action)
+ {
+ if ($action->arg('action') != 'showstream') {
+ return true;
+ }
+
+ $cur = common_current_user();
+
+ if (empty($cur) || !$cur->hasRight(self::VIEWMODLOG)) {
+ return true;
+ }
+
+ $profile = $action->profile;
+
+ $ml = new ModLog();
+
+ $ml->profile_id = $profile->id;
+ $ml->orderBy("created");
+
+ $cnt = $ml->find();
+
+ if ($cnt > 0) {
+
+ $action->elementStart('div', array('id' => 'entity_mod_log',
+ 'class' => 'section'));
+
+ $action->element('h2', null, _('Moderation'));
+
+ $action->elementStart('table');
+
+ while ($ml->fetch()) {
+ $action->elementStart('tr');
+ $action->element('td', null, strftime('%y-%m-%d', strtotime($ml->created)));
+ $action->element('td', null, sprintf(($ml->is_grant) ? _('+%s') : _('-%s'), $ml->role));
+ $action->elementStart('td');
+ if ($ml->moderator_id) {
+ $mod = Profile::staticGet('id', $ml->moderator_id);
+ if (empty($mod)) {
+ $action->text(_('[unknown]'));
+ } else {
+ $action->element('a', array('href' => $mod->profileurl,
+ 'title' => $mod->fullname),
+ $mod->nickname);
+ }
+ } else {
+ $action->text(_('[unknown]'));
+ }
+ $action->elementEnd('td');
+ $action->elementEnd('tr');
+ }
+
+ $action->elementEnd('table');
+
+ $action->elementEnd('div');
+ }
+ }
+
+ function onUserRightsCheck($profile, $right, &$result) {
+ switch ($right) {
+ case self::VIEWMODLOG:
+ $result = ($profile->hasRole(Profile_role::MODERATOR) || $profile->hasRole('modhelper'));
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ function onPluginVersion(&$versions)
+ {
+ $versions[] = array('name' => 'ModLog',
+ 'version' => STATUSNET_VERSION,
+ 'author' => 'Evan Prodromou',
+ 'homepage' => 'http://status.net/wiki/Plugin:ModLog',
+ 'description' =>
+ _m('Show the moderation history for a profile in the sidebar'));
+ return true;
+ }
+}
try {
$user->joinGroup($group);
} catch (Exception $e) {
+ common_log(LOG_ERR, "Exception on remote group join: " . $e->getMessage());
+ common_log(LOG_ERR, $e->getTraceAsString());
// TRANS: OStatus remote group subscription dialog error.
$this->showForm(_m('Remote group join failed!'));
return;
function showContent()
{
+
if ($this->group) {
// TRANS: Form legend. %s is a group name.
$header = sprintf(_m('Join group %s'), $this->group);
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
$shortoptions = 'u:a';
$longoptions = array('uri=', 'all');
* @category Plugin
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
- * @author Craig Andrews <candrews@integralblue.com>
+ * @author Craig Andrews <candrews@integralblue.com>
* @copyright 2009-2010 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
require_once dirname(__FILE__) . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
return false;
case 'User_openid':
- require_once dirname(__FILE__) . '/User_openid.php';
- return false;
+ case 'User_openid_prefs':
case 'User_openid_trustroot':
- require_once dirname(__FILE__) . '/User_openid_trustroot.php';
+ require_once dirname(__FILE__) . '/' . $cls . '.php';
return false;
case 'Auth_OpenID_TeamsExtension':
case 'Auth_OpenID_TeamsRequest':
null, false),
new ColumnDef('modified', 'timestamp')));
+ $schema->ensureTable('user_openid_prefs', User_openid_prefs::schemaDef());
+
/* These are used by JanRain OpenID library */
$schema->ensureTable('oid_associations',
return true;
}
+
+ /**
+ * Add links in the user's profile block to their OpenID URLs.
+ *
+ * @param Profile $profile The profile being shown
+ * @param Array &$links Writeable array of arrays (href, text, image).
+ *
+ * @return boolean hook value (true)
+ */
+
+ function onOtherAccountProfiles($profile, &$links)
+ {
+ $prefs = User_openid_prefs::staticGet('user_id', $profile->id);
+
+ if (empty($prefs) || !$prefs->hide_profile_link) {
+
+ $oid = new User_openid();
+
+ $oid->user_id = $profile->id;
+
+ if ($oid->find()) {
+ while ($oid->fetch()) {
+ $links[] = array('href' => $oid->display,
+ 'text' => _('OpenID'),
+ 'image' => $this->path("icons/openid-16x16.gif"));
+ }
+ }
+ }
+
+ return true;
+ }
}
--- /dev/null
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2012, StatusNet, Inc.
+ *
+ * User_openid_prefs.php
+ *
+ * PHP version 5
+ *
+ * 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 OpenID
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * Store preferences for OpenID use in StatusNet
+ *
+ * @category OpenID
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * @see DB_DataObject
+ */
+
+class User_openid_prefs extends Managed_DataObject
+{
+ public $__table = 'user_openid_prefs'; // table name
+
+ public $user_id; // The User with the prefs
+ public $hide_profile_link; // Hide the link on the profile block?
+ public $created; // datetime
+ public $modified; // datetime
+
+ /**
+ * Get an instance by key
+ *
+ * This is a utility method to get a single instance with a given key value.
+ *
+ * @param string $k Key to use to lookup (usually 'user_id' for this class)
+ * @param mixed $v Value to lookup
+ *
+ * @return TagSub object found, or null for no hits
+ *
+ */
+ function staticGet($k, $v=null)
+ {
+ return Managed_DataObject::staticGet('User_openid_prefs', $k, $v);
+ }
+
+ /**
+ * The One True Thingy that must be defined and declared.
+ */
+
+ public static function schemaDef()
+ {
+ return array(
+ 'description' => 'Per-user preferences for OpenID display',
+ 'fields' => array('user_id' => array('type' => 'integer',
+ 'not null' => true,
+ 'description' => 'User whose prefs we are saving'),
+ 'hide_profile_link' => array('type' => 'int',
+ 'not null' => true,
+ 'default' => 0,
+ 'description' => 'Whether to hide profile links from profile block'),
+ 'created' => array('type' => 'datetime',
+ 'not null' => true,
+ 'description' => 'date this record was created'),
+ 'modified' => array('type' => 'datetime',
+ 'not null' => true,
+ 'description' => 'date this record was modified'),
+ ),
+ 'primary key' => array('user_id'),
+ 'foreign keys' => array('user_openid_prefs_user_id_fkey' => array('user', array('user_id' => 'id')),
+ ),
+ 'indexes' => array(),
+ );
+ }
+}
If you already have an account on %%site.name%%, you can [login](%%action.login%%) with your username and password as usual.
To use OpenID in the future, you can [add an OpenID to your account](%%action.openidsettings%%) after you have logged in normally.
-There are many [Public OpenID providers](http://openid.net/get-an-openid/), and you may already have an OpenID-enabled account on another service.
+There are many [Public OpenID providers](http://wiki.openid.net/OpenID-Providers), and you may already have an OpenID-enabled account on another service.
-* [Google](http://www.google.com/) : If you have a Google profile, you can log in to this site by entering your profile URL.
-* [Yahoo!](http://openid.yahoo.com/) : If you have an account with Yahoo!, you can log in to this site by entering your Yahoo!-provided OpenID. Yahoo! OpenID URLs have the form *https://me.yahoo.com/yourusername*.
-* [AOL](http://dev.aol.com/aol-and-63-million-openids) : If you have an account with [AOL](http://www.aol.com/), like an [AIM](http://www.aim.com/) account, you can log in to %%site.name%% by entering your AOL-provided OpenID. AOL OpenID URLs have the form *http://openid.aol.com/yourusername*. Your username should be all lowercase, no spaces.
-* [Blogger](http://bloggerindraft.blogspot.com/2008/01/new-feature-blogger-as-openid-provider.html), [Wordpress.com](http://faq.wordpress.com/2007/03/06/what-is-openid/), [LiveJournal](http://www.livejournal.com/openid/about.bml): If you have a blog on any of these services, enter your blog URL in the box above. For example, *http://yourusername.blogspot.com/*, *http://yourusername.wordpress.com/*, *http://yourusername.livejournal.com/*, or *http://yourusername.vox.com/*.
+* On wikis: If you have an account on an OpenID-enabled wiki, like [Wikitravel](http://wikitravel.org/), [wikiHow](http://www.wikihow.com/), [Vinismo](http://vinismo.com/), [AboutUs](http://aboutus.org/) or [Keiki](http://kei.ki/), you can log in to %%site.name%% by entering the **full URL** of your user page on that other wiki in the box above. For example, *http://kei.ki/en/User:Evan*.
+* [Yahoo!](http://openid.yahoo.com/) : If you have an account with Yahoo!, you can log in to this site by entering your Yahoo!-provided OpenID in the box above. Yahoo! OpenID URLs have the form *https://me.yahoo.com/yourusername*.
+* [AOL](http://dev.aol.com/aol-and-63-million-openids) : If you have an account with [AOL](http://www.aol.com/), like an [AIM](http://www.aim.com/) account, you can log in to %%site.name%% by entering your AOL-provided OpenID in the box above. AOL OpenID URLs have the form *http://openid.aol.com/yourusername*. Your username should be all lowercase, no spaces.
+* [Blogger](http://bloggerindraft.blogspot.com/2008/01/new-feature-blogger-as-openid-provider.html), [Wordpress.com](http://faq.wordpress.com/2007/03/06/what-is-openid/), [LiveJournal](http://www.livejournal.com/openid/about.bml), [Vox](http://bradfitz.vox.com/library/post/openid-for-vox.html) : If you have a blog on any of these services, enter your blog URL in the box above. For example, *http://yourusername.blogspot.com/*, *http://yourusername.wordpress.com/*, *http://yourusername.livejournal.com/*, or *http://yourusername.vox.com/*.
+
+Additionally, once you have an account on %%site.name%%, you can use your profile's URL (https://%%site.server%%/yourusername) as an OpenID elsewhere as well.
// TRANS: Button text to remove an OpenID trustroot.
'value' => _m('BUTTON','Remove')));
$this->elementEnd('fieldset');
+
+ $prefs = User_openid_prefs::staticGet('user_id', $user->id);
+
+ $this->elementStart('fieldset');
+ $this->element('legend', null, _m('LEGEND','Preferences'));
+ $this->elementStart('ul', 'form_data');
+ $this->checkBox('hide_profile_link', "Hide OpenID links from my profile", !empty($prefs) && $prefs->hide_profile_link);
+ $this->element('input', array('type' => 'submit',
+ 'id' => 'settings_openid_prefs_save',
+ 'name' => 'save_prefs',
+ 'class' => 'submit',
+ // TRANS: Button text to save OpenID prefs
+ 'value' => _m('BUTTON','Save')));
+ $this->elementEnd('ul');
+ $this->elementEnd('fieldset');
+
$this->elementEnd('form');
}
$this->removeOpenid();
} else if($this->arg('remove_trustroots')) {
$this->removeTrustroots();
+ } else if($this->arg('save_prefs')) {
+ $this->savePrefs();
} else {
// TRANS: Unexpected form validation error.
$this->showForm(_m('Something weird happened.'));
$this->showForm(_m('OpenID removed.'), true);
return;
}
+
+ /**
+ * Handles a request to save preferences
+ *
+ * Validates input and, if everything is OK, deletes the OpenID.
+ * Reloads the form with a success or error notification.
+ *
+ * @return void
+ */
+ function savePrefs()
+ {
+ $cur = common_current_user();
+
+ if (empty($cur)) {
+ throw new ClientException(_("Not logged in."));
+ }
+
+ $orig = null;
+ $prefs = User_openid_prefs::staticGet('user_id', $cur->id);
+
+ if (empty($prefs)) {
+ $prefs = new User_openid_prefs();
+ $prefs->user_id = $cur->id;
+ $prefs->created = common_sql_now();
+ } else {
+ $orig = clone($prefs);
+ }
+
+ $prefs->hide_profile_link = $this->boolean('hide_profile_link');
+
+ if (empty($orig)) {
+ $prefs->insert();
+ } else {
+ $prefs->update($orig);
+ }
+
+ $this->showForm(_m('OpenID preferences saved.'), true);
+ return;
+ }
}
$schema = Schema::get();
$schema->ensureTable('poll', Poll::schemaDef());
$schema->ensureTable('poll_response', Poll_response::schemaDef());
+ $schema->ensureTable('user_poll_prefs', User_poll_prefs::schemaDef());
return true;
}
case 'ShowpollAction':
case 'NewpollAction':
case 'RespondpollAction':
+ case 'PollsettingsAction':
include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
return false;
case 'Poll':
case 'Poll_response':
+ case 'User_poll_prefs':
include_once $dir.'/'.$cls.'.php';
return false;
case 'NewPollForm':
array('action' => 'respondpoll'),
array('id' => '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'));
+ $m->connect('settings/poll',
+ array('action' => 'pollsettings'));
+
return true;
}
}
return true;
}
+
+ // Hide poll responses for @chuck
+
+ function onEndNoticeWhoGets($notice, &$ni) {
+ if ($notice->object_type == self::POLL_RESPONSE_OBJECT) {
+ foreach ($ni as $id => $source) {
+ $user = User::staticGet('id', $id);
+ if (!empty($user)) {
+ $pollPrefs = User_poll_prefs::staticGet('user_id', $user->id);
+ if (!empty($pollPrefs) && ($pollPrefs->hide_responses)) {
+ unset($ni[$id]);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Menu item for personal subscriptions/groups area
+ *
+ * @param Action $action action being executed
+ *
+ * @return boolean hook return
+ */
+
+ function onEndAccountSettingsNav($action)
+ {
+ $action_name = $action->trimmed('action');
+
+ $action->menuItem(common_local_url('pollsettings'),
+ // TRANS: Poll plugin menu item on user settings page.
+ _m('MENU', 'Polls'),
+ // TRANS: Poll plugin tooltip for user settings menu item.
+ _m('Configure poll behavior'),
+ $action_name === 'pollsettings');
+
+ return true;
+ }
}
--- /dev/null
+<?php
+/**
+ * Data class to record user prefs for polls
+ *
+ * PHP version 5
+ *
+ * @category PollPlugin
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2012, StatusNet, 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('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * For storing the poll prefs
+ *
+ * @category PollPlugin
+ * @package StatusNet
+ * @author Brion Vibber <brion@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * @see DB_DataObject
+ */
+class User_poll_prefs extends Managed_DataObject
+{
+ public $__table = 'user_poll_prefs'; // table name
+ public $user_id; // int id
+ public $hide_responses; // boolean
+ public $created; // datetime
+ public $modified; // datetime
+
+ /**
+ * Get an instance by key
+ *
+ * This is a utility method to get a single instance with a given key value.
+ *
+ * @param string $k Key to use to lookup (usually 'user_id' for this class)
+ * @param mixed $v Value to lookup
+ *
+ * @return User_greeting_count object found, or null for no hits
+ *
+ */
+ function staticGet($k, $v=null)
+ {
+ return Memcached_DataObject::staticGet('User_poll_prefs', $k, $v);
+ }
+
+ /**
+ * The One True Thingy that must be defined and declared.
+ */
+ public static function schemaDef()
+ {
+ return array(
+ 'description' => 'Record of user preferences for polls',
+ 'fields' => array(
+ 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id'),
+ 'hide_responses' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'Hide all poll responses'),
+ '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')
+ );
+ }
+}
--- /dev/null
+<?php
+/**
+ * Form to set your personal poll settings
+ *
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * 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 Plugins
+ * @package StatusNet
+ * @author Brion Vibber <brion@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+class PollSettingsAction extends SettingsAction
+{
+ /**
+ * Title of the page
+ *
+ * @return string Page title
+ */
+ function title()
+ {
+ // TRANS: Page title.
+ return _m('Poll settings');
+ }
+
+ /**
+ * Instructions for use
+ *
+ * @return string Instructions for use
+ */
+
+ function getInstructions()
+ {
+ // TRANS: Page instructions.
+ return _m('Set your poll preferences');
+ }
+
+ /**
+ * Show the form for Poll
+ *
+ * @return void
+ */
+ function showContent()
+ {
+ $user = common_current_user();
+
+ $prefs = User_poll_prefs::staticGet('user_id', $user->id);
+
+ $form = new PollPrefsForm($this, $prefs);
+
+ $form->show();
+ }
+
+ /**
+ * Handler method
+ *
+ * @param array $argarray is ignored since it's now passed in in prepare()
+ *
+ * @return void
+ */
+
+ function handlePost()
+ {
+ $user = common_current_user();
+
+ $upp = User_poll_prefs::staticGet('user_id', $user->id);
+ $orig = null;
+
+ if (!empty($upp)) {
+ $orig = clone($upp);
+ } else {
+ $upp = new User_poll_prefs();
+ $upp->user_id = $user->id;
+ $upp->created = common_sql_now();
+ }
+
+ $upp->hide_responses = $this->boolean('hide_responses');
+ $upp->modified = common_sql_now();
+
+ if (!empty($orig)) {
+ $upp->update($orig);
+ } else {
+ $upp->insert();
+ }
+
+ // TRANS: Confirmation shown when user profile settings are saved.
+ $this->showForm(_('Settings saved.'), true);
+
+ return;
+ }
+}
+
+class PollPrefsForm extends Form
+{
+ var $prefs;
+
+ function __construct($out, $prefs)
+ {
+ parent::__construct($out);
+ $this->prefs = $prefs;
+ }
+
+ /**
+ * Visible or invisible data elements
+ *
+ * Display the form fields that make up the data of the form.
+ * Sub-classes should overload this to show their data.
+ *
+ * @return void
+ */
+
+ function formData()
+ {
+ $this->elementStart('fieldset');
+ $this->elementStart('ul', 'form_data');
+ $this->elementStart('li');
+ $this->checkbox('hide_responses',
+ _('Do not deliver poll responses to my home timeline'),
+ (!empty($this->prefs) && $this->prefs->hide_responses));
+ $this->elementEnd('li');
+ $this->elementEnd('ul');
+ $this->elementEnd('fieldset');
+ }
+
+ /**
+ * Buttons for form actions
+ *
+ * Submit and cancel buttons (or whatever)
+ * Sub-classes should overload this to show their own buttons.
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ $this->submit('submit', _('Save'));
+ }
+
+ /**
+ * ID of the form
+ *
+ * Should be unique on the page. Sub-classes should overload this
+ * to show their own IDs.
+ *
+ * @return int ID of the form
+ */
+
+ function id()
+ {
+ return 'form_poll_prefs';
+ }
+
+ /**
+ * Action of the form.
+ *
+ * URL to post to. Should be overloaded by subclasses to give
+ * somewhere to post to.
+ *
+ * @return string URL to post to
+ */
+
+ function action()
+ {
+ return common_local_url('pollsettings');
+ }
+
+ /**
+ * Class of the form. May include space-separated list of multiple classes.
+ *
+ * @return string the form's class
+ */
+
+ function formClass()
+ {
+ return 'form_settings';
+ }
+}
list($action, $arg1, $arg2) = $path;
$channels = Realtime_channel::getAllChannels($action, $arg1, $arg2);
+ $this->log(LOG_INFO, sprintf(_("%d candidate channels for notice %d"),
+ count($channels),
+ $notice->id));
foreach ($channels as $channel) {
$profile = Profile::staticGet('id', $channel->user_id);
}
if ($notice->inScope($profile)) {
+ $this->log(LOG_INFO,
+ sprintf(_("Delivering notice %d to channel (%s, %s, %s) for user '%s'"),
+ $notice->id,
+ $channel->action,
+ $channel->arg1,
+ $channel->arg2,
+ ($profile) ? ($profile->nickname) : "<public>"));
$timeline = $this->_pathToChannel(array($channel->channel_key));
$this->_publish($timeline, $json);
}
}
return true;
}
+
+ /**
+ * Add links in the user's profile block to their Twitter profile URL.
+ *
+ * @param Profile $profile The profile being shown
+ * @param Array &$links Writeable array of arrays (href, text, image).
+ *
+ * @return boolean hook value (true)
+ */
+
+ function onOtherAccountProfiles($profile, &$links)
+ {
+ $fuser = null;
+
+ $flink = Foreign_link::getByUserID($profile->id, TWITTER_SERVICE);
+
+ if (!empty($flink)) {
+ $fuser = $flink->getForeignUser();
+
+ if (!empty($fuser)) {
+ $links[] = array("href" => $fuser->uri,
+ "text" => sprintf(_("@%s on Twitter"), $fuser->nickname),
+ "image" => $this->path("icons/twitter-bird-white-on-blue.png"));
+ }
+ }
+
+ return true;
+ }
}
{
protected $userIds;
- public function __construct(TwitterOAuthClient $auth, $baseUrl='http://betastream.twitter.com')
+ public function __construct(TwitterOAuthClient $auth, $baseUrl='https://sitestream.twitter.com')
{
parent::__construct($auth, $baseUrl);
}
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-$shortoptions = 'i:n:f:j';
-$longoptions = array('id=', 'nickname=', 'file=', 'json');
+$shortoptions = 'i:n:f:a:j';
+$longoptions = array('id=', 'nickname=', 'file=', 'after=', 'json');
$helptext = <<<END_OF_EXPORTACTIVITYSTREAM_HELP
exportactivitystream.php [options]
-i --id ID of user to export
-n --nickname nickname of the user to export
-j --json Output JSON (default Atom)
+ -a --after Only activities after the given date
END_OF_EXPORTACTIVITYSTREAM_HELP;
try {
$user = getUser();
- $actstr = new UserActivityStream($user, true, UserActivityStream::OUTPUT_RAW);
+ if (have_option('a', 'after')) {
+ $afterStr = get_option_value('a', 'after');
+ $after = strtotime($afterStr);
+ $actstr = new UserActivityStream($user, true, UserActivityStream::OUTPUT_RAW, $after);
+ } else {
+ $actstr = new UserActivityStream($user, true, UserActivityStream::OUTPUT_RAW);
+ }
if (have_option('j', 'json')) {
$actstr->writeJSON(STDOUT);
} else {
'users=',
'words=',
'prefix=',
- 'groupprefix'
+ 'groupprefix=',
+ 'faves='
);
$helptext = <<<END_OF_CREATESIM_HELP
-b --subscriptions Average subscriptions per user (default no. users/20)
-g --groups Number of groups (default 20)
-j --joins Number of groups per user (default 5)
+ -f --faves Number of faves per user (default notices/10)
-n --notices Average notices per user (default 100)
-t --tags Number of distinct hash tags (default 10000)
-u --users Number of users (default 100)
$options['scope'] |= Notice::ADDRESSEE_SCOPE;
}
}
+ } else {
+ $is_directed = rand(0, 4);
+
+ if ($is_directed == 0) {
+ $subs = $user->getSubscriptions(0, 100)->fetchAll();
+ if (count($subs) > 0) {
+ $seen = array();
+ $f = rand(0, 9);
+ if ($f <= 6) {
+ $addrs = 1;
+ } else if ($f <= 8) {
+ $addrs = 2;
+ } else {
+ $addrs = 3;
+ }
+ for ($m = 0; $m < $addrs; $m++) {
+ $x = rand(0, count($subs) - 1);
+ if ($seen[$x]) {
+ continue;
+ }
+ if ($subs[$x]->id == $user->id) {
+ continue;
+ }
+ $seen[$x] = true;
+ $rprofile = $subs[$x];
+ $content = "@".$rprofile->nickname." ".$content;
+ }
+ $private_to_addressees = rand(0, 4);
+ if ($private_to_addressees == 0) {
+ $options['scope'] |= Notice::ADDRESSEE_SCOPE;
+ }
+ }
+ }
}
$has_hash = rand(0, 2);
$notice = Notice::saveNew($user->id, $content, 'createsim', $options);
}
+function newMessage($i)
+{
+ global $userprefix;
+
+ $n = rand(0, $i - 1);
+ $user = User::staticGet('nickname', sprintf('%s%d', $userprefix, $n));
+
+ $content = testNoticeContent();
+
+ $friends = $user->mutuallySubscribedUsers()->fetchAll();
+
+ if (count($friends) == 0) {
+ return;
+ }
+
+ $j = rand(0, count($friends) - 1);
+
+ $other = $friends[$j];
+
+ $message = Message::saveNew($user->id, $other->id, $content, 'createsim');
+}
+
function newSub($i)
{
global $userprefix;
}
}
+function newFave($u)
+{
+ global $userprefix;
+ global $groupprefix;
+
+ $userNumber = rand(0, $u - 1);
+
+ $userNick = sprintf('%s%d', $userprefix, $userNumber);
+
+ $user = User::staticGet('nickname', $userNick);
+
+ if (empty($user)) {
+ throw new Exception("Can't find user '$userNick'.");
+ }
+
+ // NB: it's OK to like your own stuff!
+
+ $otherNumber = rand(0, $u - 1);
+
+ $otherNick = sprintf('%s%d', $userprefix, $otherNumber);
+
+ $other = User::staticGet('nickname', $otherNick);
+
+ if (empty($other)) {
+ throw new Exception("Can't find user '$otherNick'.");
+ }
+
+ $notices = $other->getNotices()->fetchAll();
+
+ if (count($notices) == 0) {
+ return;
+ }
+
+ $idx = rand(0, count($notices) - 1);
+
+ $notice = $notices[$idx];
+
+ if ($user->hasFave($notice)) {
+ return;
+ }
+
+ Fave::addNew($user->getProfile(), $notice);
+}
+
function testNoticeContent()
{
global $words;
return $text;
}
-function main($usercount, $groupcount, $noticeavg, $subsavg, $joinsavg, $tagmax)
+function main($usercount, $groupcount, $noticeavg, $subsavg, $joinsavg, $favesavg, $messageavg, $tagmax)
{
global $config;
$config['site']['dupelimit'] = -1;
// # registrations + # notices + # subs
- $events = $usercount + $groupcount + ($usercount * ($noticeavg + $subsavg + $joinsavg));
+ $events = $usercount + $groupcount + ($usercount * ($noticeavg + $subsavg + $joinsavg + $favesavg + $messageavg));
$events -= $preuser;
$events -= $pregroup;
$nt = $gt + ($usercount * $noticeavg);
$st = $nt + ($usercount * $subsavg);
$jt = $st + ($usercount * $joinsavg);
+ $ft = $jt + ($usercount * $favesavg);
+ $mt = $ft + ($usercount * $messageavg);
- printfv("$events events ($ut, $gt, $nt, $st, $jt)\n");
+ printfv("$events events ($ut, $gt, $nt, $st, $jt, $ft, $mt)\n");
for ($i = 0; $i < $events; $i++)
{
} else if ($e > $st && $e <= $jt) {
printfv("$i Making a new group join\n");
newJoin($n, $g);
+ } else if ($e > $jt && $e <= $ft) {
+ printfv("$i Making a new fave\n");
+ newFave($n);
+ } else if ($e > $ft && $e <= $mt) {
+ printfv("$i Making a new message\n");
+ newMessage($n);
} else {
printfv("No event for $i!");
}
$noticeavg = (have_option('n', 'notices')) ? get_option_value('n', 'notices') : 100;
$subsavg = (have_option('b', 'subscriptions')) ? get_option_value('b', 'subscriptions') : max($usercount/20, 10);
$joinsavg = (have_option('j', 'joins')) ? get_option_value('j', 'joins') : 5;
+$favesavg = (have_option('f', 'faves')) ? get_option_value('f', 'faves') : max($noticeavg/10, 5);
+$messageavg = (have_option('m', 'messages')) ? get_option_value('m', 'messages') : max($noticeavg/10, 5);
$tagmax = (have_option('t', 'tags')) ? get_option_value('t', 'tags') : 10000;
$userprefix = (have_option('x', 'prefix')) ? get_option_value('x', 'prefix') : 'testuser';
$groupprefix = (have_option('z', 'groupprefix')) ? get_option_value('z', 'groupprefix') : 'testgroup';
}
try {
- main($usercount, $groupcount, $noticeavg, $subsavg, $joinsavg, $tagmax);
+ main($usercount, $groupcount, $noticeavg, $subsavg, $joinsavg, $favesavg, $messageavg, $tagmax);
} catch (Exception $e) {
printfv("Got an exception: ".$e->getMessage());
}
'--admin-nick' => 'adminNick',
'--admin-pass' => 'adminPass',
'--admin-email' => 'adminEmail',
- '--admin-updates' => 'adminUpdates'
+ '--admin-updates' => 'adminUpdates',
+
+ '--site-profile' => 'siteProfile'
);
foreach ($map as $arg => $target) {
if (substr($arg, 0, 2) == '--') {
--admin-updates 'yes' (default) or 'no', whether to subscribe
admin to update@status.net (default yes)
+ --site-profile site profile ['public', 'private' (default), 'community', 'singleuser']
+
--skip-config Don't write a config.php -- use with caution,
requires a global configuration file.
--- /dev/null
+Initial simple way to Webfinger enable your domain -- needs PHP.
+================================================================
+
+Step 1
+======
+
+First, put the folders 'xrd' and 'dot-well-known' on your website, so
+they load at:
+
+ http://yourname.com/xrd/
+
+ and
+
+ http://yourname.com/.well-known/
+
+ (Remember the . at the beginning of this one)
+
+Step 2
+======
+
+Next, edit xrd/index.php and enter a secret in this line:
+
+$s = "";
+
+This can be anything you like...
+
+$s = "johnny-five";
+
+or
+
+$s = "12345";
+
+It really doesn't matter too much.
+
+Step 3
+======
+
+For each user on your site, and this might only be you...
+
+Make a copy of the example@example.com.xml file so that it's called...
+
+ yoursecretusername@domain.com.xml
+
+ So, if your secret is 'johnny5' and your name is ben and your
+ domain is titanictoycorp.biz, your file should be called
+ johnny5ben@titanictoycorp.biz.xml
+
+Finally, edit the file to point at your account on your social
+site. If you are the only user, then you probably don't need to worry
+about user/1 as this will be you. For multi user sites, the user ID is
+on the profile page.
+
+Finally
+=======
+
+Using this method, though fiddly, you can now be @user@domain without
+the need for any prefixes for subdomains, etc.
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0" xmlns:hm="http://host-meta.net/xrd/1.0"><hm:Host>example.com</hm:Host><Link rel="lrdd" template="http://example.com/xrd?uri={uri}"><Title>Resource Descriptor</Title></Link></XRD>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
+ <Subject>acct:example@example.com</Subject>
+ <Alias>acct:example@social.example.com</Alias>
+ <Alias>http://social.example.com/user/1</Alias>
+ <Link rel="http://webfinger.net/rel/profile-page" type="text/html" href="http://social.example.com/user/1"/>
+ <Link rel="http://schemas.google.com/g/2010#updates-from" type="application/atom+xml" href="http://social.example.com/api/statuses/user_timeline/1.atom"/>
+ <Link rel="http://microformats.org/profile/hcard" type="text/html" href="http://social.example.com/hcard"/>
+ <Link rel="http://gmpg.org/xfn/11" type="text/html" href="http://social.example.com/user/1"/>
+ <Link rel="describedby" type="application/rdf+xml" href="http://social.example.com/foaf"/>
+ <Link rel="http://salmon-protocol.org/ns/salmon-replies" href="http://social.example.com/main/salmon/user/1"/>
+ <Link rel="http://salmon-protocol.org/ns/salmon-mention" href="http://social.example.com/main/salmon/user/1"/>
+ <Link rel="http://ostatus.org/schema/1.0/subscribe" template="http://social.example.com/main/ostatussub?profile={uri}"/>
+</XRD>
--- /dev/null
+<?php
+
+/*
+ * GNU social
+ * Copyright (C) 2010, Free Software Foundation, 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/>.
+ */
+
+
+$s = "";
+
+/* this should be a secret */
+
+$u = $_GET['uri'];
+
+$u = substr($u, 5);
+
+$f = $s . $u . ".xml";
+
+if (file_exists($f)) {
+ $fh = fopen($f, 'r');
+ $c = fread($fh, filesize($f));
+ fclose($fh);
+ header('Content-type: text/xml');
+ echo $c;
+}
+
+
+?>
\ No newline at end of file
display:none;
}
+.profile_block_otherprofile_list li {
+ display: inline;
+ list-style-type: none;
+}
+
/*end of @media screen, projection, tv*/
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.7.0
+*/
+html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}select,input,button,textarea,button{font:99% arial,helvetica,clean,sans-serif;}table{font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}body{text-align:center;}#doc,#doc2,#doc3,#doc4,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6,.yui-t7{margin:auto;text-align:left;width:57.69em;*width:56.25em;}#doc2{width:73.076em;*width:71.25em;}#doc3{margin:auto 10px;width:auto;}#doc4{width:74.923em;*width:73.05em;}.yui-b{position:relative;}.yui-b{_position:static;}#yui-main .yui-b{position:static;}#yui-main,.yui-g .yui-u .yui-g{width:100%;}.yui-t1 #yui-main,.yui-t2 #yui-main,.yui-t3 #yui-main{float:right;margin-left:-25em;}.yui-t4 #yui-main,.yui-t5 #yui-main,.yui-t6 #yui-main{float:left;margin-right:-25em;}.yui-t1 .yui-b{float:left;width:12.30769em;*width:12.00em;}.yui-t1 #yui-main .yui-b{margin-left:13.30769em;*margin-left:13.05em;}.yui-t2 .yui-b{float:left;width:13.8461em;*width:13.50em;}.yui-t2 #yui-main .yui-b{margin-left:14.8461em;*margin-left:14.55em;}.yui-t3 .yui-b{float:left;width:23.0769em;*width:22.50em;}.yui-t3 #yui-main .yui-b{margin-left:24.0769em;*margin-left:23.62em;}.yui-t4 .yui-b{float:right;width:13.8456em;*width:13.50em;}.yui-t4 #yui-main .yui-b{margin-right:14.8456em;*margin-right:14.55em;}.yui-t5 .yui-b{float:right;width:18.4615em;*width:18.00em;}.yui-t5 #yui-main .yui-b{margin-right:19.4615em;*margin-right:19.125em;}.yui-t6 .yui-b{float:right;width:23.0769em;*width:22.50em;}.yui-t6 #yui-main .yui-b{margin-right:24.0769em;*margin-right:23.62em;}.yui-t7 #yui-main .yui-b{display:block;margin:0 0 1em 0;}#yui-main .yui-b{float:none;width:auto;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf,.yui-gc .yui-u,.yui-gd .yui-g,.yui-g .yui-gc .yui-u,.yui-ge .yui-u,.yui-ge .yui-g,.yui-gf .yui-g,.yui-gf .yui-u{float:right;}.yui-g div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first,.yui-ge div.first,.yui-gf div.first,.yui-g .yui-gc div.first,.yui-g .yui-ge div.first,.yui-gc div.first div.first{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf{width:49.1%;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{width:32%;margin-left:1.99%;}.yui-gb .yui-u{*margin-left:1.9%;*width:31.9%;}.yui-gc div.first,.yui-gd .yui-u{width:66%;}.yui-gd div.first{width:32%;}.yui-ge div.first,.yui-gf .yui-u{width:74.2%;}.yui-ge .yui-u,.yui-gf div.first{width:24%;}.yui-g .yui-gb div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first{margin-left:0;}.yui-g .yui-g .yui-u,.yui-gb .yui-g .yui-u,.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u,.yui-ge .yui-g .yui-u,.yui-gf .yui-g .yui-u{width:49%;*width:48.1%;*margin-left:0;}.yui-g .yui-g .yui-u{width:48.1%;}.yui-g .yui-gb div.first,.yui-gb .yui-gb div.first{*margin-right:0;*width:32%;_width:31.7%;}.yui-g .yui-gc div.first,.yui-gd .yui-g{width:66%;}.yui-gb .yui-g div.first{*margin-right:4%;_margin-right:1.3%;}.yui-gb .yui-gc div.first,.yui-gb .yui-gd div.first{*margin-right:0;}.yui-gb .yui-gb .yui-u,.yui-gb .yui-gc .yui-u{*margin-left:1.8%;_margin-left:4%;}.yui-g .yui-gb .yui-u{_margin-left:1.0%;}.yui-gb .yui-gd .yui-u{*width:66%;_width:61.2%;}.yui-gb .yui-gd div.first{*width:31%;_width:29.5%;}.yui-g .yui-gc .yui-u,.yui-gb .yui-gc .yui-u{width:32%;_float:right;margin-right:0;_margin-left:0;}.yui-gb .yui-gc div.first{width:66%;*float:left;*margin-left:0;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf .yui-u{margin:0;}.yui-gb .yui-gb .yui-u{_margin-left:.7%;}.yui-gb .yui-g div.first,.yui-gb .yui-gb div.first{*margin-left:0;}.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u{*width:48.1%;*margin-left:0;}.yui-gb .yui-gd div.first{width:32%;}.yui-g .yui-gd div.first{_width:29.9%;}.yui-ge .yui-g{width:24%;}.yui-gf .yui-g{width:74.2%;}.yui-gb .yui-ge div.yui-u,.yui-gb .yui-gf div.yui-u{float:right;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf div.first{float:left;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf div.first{*width:24%;_width:20%;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf .yui-u{*width:73.5%;_width:65.5%;}.yui-ge div.first .yui-gd .yui-u{width:65%;}.yui-ge div.first .yui-gd div.first{width:32%;}#hd:after,#bd:after,#ft:after,.yui-g:after,.yui-gb:after,.yui-gc:after,.yui-gd:after,.yui-ge:after,.yui-gf:after{content:".";display:block;height:0;clear:both;visibility:hidden;}#hd,#bd,#ft,.yui-g,.yui-gb,.yui-gc,.yui-gd,.yui-ge,.yui-gf{zoom:1;}/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.7.0
+*/
+body{margin:10px;}h1{font-size:138.5%;}h2{font-size:123.1%;}h3{font-size:108%;}h1,h2,h3{margin:1em 0;}h1,h2,h3,h4,h5,h6,strong,dt{font-weight:bold;}optgroup{font-weight:normal;}abbr,acronym{border-bottom:1px dotted #000;cursor:help;}em{font-style:italic;}del{text-decoration:line-through;}blockquote,ul,ol,dl{margin:1em;}ol,ul,dl{margin-left:2em;}ol li{list-style:decimal outside;}ul li{list-style:disc outside;}dl dd{margin-left:1em;}th,td{border:1px solid #000;padding:.5em;}th{font-weight:bold;text-align:center;}caption{margin-bottom:.5em;text-align:center;}sup{vertical-align:super;}sub{vertical-align:sub;}p,fieldset,table,pre{margin-bottom:1em;}button,input[type="checkbox"],input[type="radio"],input[type="reset"],input[type="submit"]{padding:1px;}
\ No newline at end of file
--- /dev/null
+#hd { background-color: orange !important; }
+
+#bd { background-color: red !important; }
+
+#ft { background-color: lime !important; }
+
+#yui-main { background-color: yellow !important; }
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.7.0
+*/
+html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}select,input,button,textarea,button{font:99% arial,helvetica,clean,sans-serif;}table{font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}body{text-align:center;}#doc,#doc2,#doc3,#doc4,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6,.yui-t7{margin:auto;text-align:left;width:57.69em;*width:56.25em;}#doc2{width:73.076em;*width:71.25em;}#doc3{margin:auto 10px;width:auto;}#doc4{width:74.923em;*width:73.05em;}.yui-b{position:relative;}.yui-b{_position:static;}#yui-main .yui-b{position:static;}#yui-main,.yui-g .yui-u .yui-g{width:100%;}.yui-t1 #yui-main,.yui-t2 #yui-main,.yui-t3 #yui-main{float:right;margin-left:-25em;}.yui-t4 #yui-main,.yui-t5 #yui-main,.yui-t6 #yui-main{float:left;margin-right:-25em;}.yui-t1 .yui-b{float:left;width:12.30769em;*width:12.00em;}.yui-t1 #yui-main .yui-b{margin-left:13.30769em;*margin-left:13.05em;}.yui-t2 .yui-b{float:left;width:13.8461em;*width:13.50em;}.yui-t2 #yui-main .yui-b{margin-left:14.8461em;*margin-left:14.55em;}.yui-t3 .yui-b{float:left;width:23.0769em;*width:22.50em;}.yui-t3 #yui-main .yui-b{margin-left:24.0769em;*margin-left:23.62em;}.yui-t4 .yui-b{float:right;width:13.8456em;*width:13.50em;}.yui-t4 #yui-main .yui-b{margin-right:14.8456em;*margin-right:14.55em;}.yui-t5 .yui-b{float:right;width:18.4615em;*width:18.00em;}.yui-t5 #yui-main .yui-b{margin-right:19.4615em;*margin-right:19.125em;}.yui-t6 .yui-b{float:right;width:23.0769em;*width:22.50em;}.yui-t6 #yui-main .yui-b{margin-right:24.0769em;*margin-right:23.62em;}.yui-t7 #yui-main .yui-b{display:block;margin:0 0 1em 0;}#yui-main .yui-b{float:none;width:auto;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf,.yui-gc .yui-u,.yui-gd .yui-g,.yui-g .yui-gc .yui-u,.yui-ge .yui-u,.yui-ge .yui-g,.yui-gf .yui-g,.yui-gf .yui-u{float:right;}.yui-g div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first,.yui-ge div.first,.yui-gf div.first,.yui-g .yui-gc div.first,.yui-g .yui-ge div.first,.yui-gc div.first div.first{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf{width:49.1%;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{width:32%;margin-left:1.99%;}.yui-gb .yui-u{*margin-left:1.9%;*width:31.9%;}.yui-gc div.first,.yui-gd .yui-u{width:66%;}.yui-gd div.first{width:32%;}.yui-ge div.first,.yui-gf .yui-u{width:74.2%;}.yui-ge .yui-u,.yui-gf div.first{width:24%;}.yui-g .yui-gb div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first{margin-left:0;}.yui-g .yui-g .yui-u,.yui-gb .yui-g .yui-u,.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u,.yui-ge .yui-g .yui-u,.yui-gf .yui-g .yui-u{width:49%;*width:48.1%;*margin-left:0;}.yui-g .yui-g .yui-u{width:48.1%;}.yui-g .yui-gb div.first,.yui-gb .yui-gb div.first{*margin-right:0;*width:32%;_width:31.7%;}.yui-g .yui-gc div.first,.yui-gd .yui-g{width:66%;}.yui-gb .yui-g div.first{*margin-right:4%;_margin-right:1.3%;}.yui-gb .yui-gc div.first,.yui-gb .yui-gd div.first{*margin-right:0;}.yui-gb .yui-gb .yui-u,.yui-gb .yui-gc .yui-u{*margin-left:1.8%;_margin-left:4%;}.yui-g .yui-gb .yui-u{_margin-left:1.0%;}.yui-gb .yui-gd .yui-u{*width:66%;_width:61.2%;}.yui-gb .yui-gd div.first{*width:31%;_width:29.5%;}.yui-g .yui-gc .yui-u,.yui-gb .yui-gc .yui-u{width:32%;_float:right;margin-right:0;_margin-left:0;}.yui-gb .yui-gc div.first{width:66%;*float:left;*margin-left:0;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf .yui-u{margin:0;}.yui-gb .yui-gb .yui-u{_margin-left:.7%;}.yui-gb .yui-g div.first,.yui-gb .yui-gb div.first{*margin-left:0;}.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u{*width:48.1%;*margin-left:0;}.yui-gb .yui-gd div.first{width:32%;}.yui-g .yui-gd div.first{_width:29.9%;}.yui-ge .yui-g{width:24%;}.yui-gf .yui-g{width:74.2%;}.yui-gb .yui-ge div.yui-u,.yui-gb .yui-gf div.yui-u{float:right;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf div.first{float:left;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf div.first{*width:24%;_width:20%;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf .yui-u{*width:73.5%;_width:65.5%;}.yui-ge div.first .yui-gd .yui-u{width:65%;}.yui-ge div.first .yui-gd div.first{width:32%;}#hd:after,#bd:after,#ft:after,.yui-g:after,.yui-gb:after,.yui-gc:after,.yui-gd:after,.yui-ge:after,.yui-gf:after{content:".";display:block;height:0;clear:both;visibility:hidden;}#hd,#bd,#ft,.yui-g,.yui-gb,.yui-gc,.yui-gd,.yui-ge,.yui-gf{zoom:1;}/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.7.0
+*/
+body{margin:10px;}h1{font-size:138.5%;}h2{font-size:123.1%;}h3{font-size:108%;}h1,h2,h3{margin:1em 0;}h1,h2,h3,h4,h5,h6,strong,dt{font-weight:bold;}optgroup{font-weight:normal;}abbr,acronym{border-bottom:1px dotted #000;cursor:help;}em{font-style:italic;}del{text-decoration:line-through;}blockquote,ul,ol,dl{margin:1em;}ol,ul,dl{margin-left:2em;}ol li{list-style:decimal outside;}ul li{list-style:disc outside;}dl dd{margin-left:1em;}th,td{border:1px solid #000;padding:.5em;}th{font-weight:bold;text-align:center;}caption{margin-bottom:.5em;text-align:center;}sup{vertical-align:super;}sub{vertical-align:sub;}p,fieldset,table,pre{margin-bottom:1em;}button,input[type="checkbox"],input[type="radio"],input[type="reset"],input[type="submit"]{padding:1px;}
\ No newline at end of file
--- /dev/null
+/*
+
+GNU social alpha CSS
+
+Enable this line to debug:
+
+@import url('./debug.css');
+
+*/
+
+/** theme: GNU social (portions from StatusNet base CSS)
+ *
+ * @package GNU social
+ * @author Matt Lee <mattl@cnuk.org>
+ * @copyright 2010 Free Software Foundation, Inc
+ * @copyright 2009-2010 StatusNet, Inc.
+ * @license http://creativecommons.org/licenses/by/3.0/ Creative Commons Attribution 3.0 Unported
+ */
+
+/* stuff we want to hide..... */
+
+legend, #anon_notice, #notices_primary h2, #site_nav_local_views dt, #ft dt, .entity_profile dt, .entity_profile h2, .entity_actions h2, .entity_nickname, .entities .nickname, .anon_notice, .entity_profile, .entity_subscribers { display: none !important; }
+
+#hd a{ color: white !important; }
+
+.entities li { display: inline; list-style: none !important; }
+
+.entity_profile dd { margin-bottom: 1em; }
+
+.notice { background-color: #eee; margin-bottom: 1em; border-top: 1px solid #ddd; padding: 0.4em; }
+
+.entity_fn { font-size: 130%; }
+
+dl, dd { margin: 0 !important; padding: 0 !important;}
+
+#yui-main { margin-bottom: 0 !important; padding-bottom: 0 !important;}
+
+.notices { margin: 0; padding: 0; }
+.notices li { list-style: none; }
+
+#ft { padding-top: 12px;}
+
+#custom-doc { width:76.23em;*width:74.39em;min-width:991px; margin:auto; text-align:left; }
+
+#yui-main { background-color: white; position: relative; }
+
+#sidebar *, #right-nav * { background: none !important; border: none !important; }
+
+ html, body{padding: 0; margin: 0;}
+
+ body {background-image: url(/theme/gnusocial/images/bg.png) !important; background-repeat: repeat-x !important; background-color: white;}
+
+ #hd h1 {margin: 0; line-height: 57px; font-size: 14px; font-weight: bold;}
+
+ #hd h1 a{color: #111; text-decoration: none;}
+
+ #hd dt {display: none;}
+
+ #hd ul {padding: 0; margin: 0; line-height: 48px; position: absolute; top: 0; right: 10px; }
+
+ #hd li {display: inline; list-style: none; margin-left: 12px;}
+
+ #hd {height: 40px; position: relative;}
+
+
+ form {margin: 0 auto; width: 70%;}
+
+ table {width: 100%;}
+
+ tr, td{border: 0;}
+
+ .update-text{ font-size: 12px; font-weight: bold;}
+
+ .update-icon{ text-align: center;}
+
+ #stream li{list-style: none; position: relative; margin-top: 12px; }
+
+ #stream dl {position: absolute; top: 0; left: 50px;}
+
+ #stream dd {color: #333; font-size: 80%; padding: 0; margin: 0; margin-top: 6px;}
+
+
+ #social { padding-left: 10px;}
+
+ #sidebar ul{margin: 0; padding: 0;}
+
+ #sidebar li {list-style: none;}
+
+ #sidebar li a{display: block; width: 180px; padding: 4px;}
+
+ #sidebar li a:hover {background-color: #ececec;}
+
+ .selected {background-color: cyan; width: 180px;}
+
+ #right-nav {background-color: #ececec;}
+
+ #right-nav div {padding: 10px;}
+
+ .form_notice { position: relative; top: 0; left: 0; }
+
+
+
+form label.submit {
+display:none;
+}
+
+.form_settings {
+clear:both;
+}
+
+.form_settings fieldset {
+margin-bottom:29px;
+}
+.form_settings input.remove {
+margin-left:11px;
+}
+.form_settings .form_data li {
+width:100%;
+float:left;
+}
+.form_settings .form_data label {
+float:left;
+}
+.form_settings .form_data textarea,
+.form_settings .form_data select,
+.form_settings .form_data input {
+margin-left:11px;
+float:left;
+}
+.form_settings .form_data textarea {
+width:325px;
+}
+
+.form_settings .form_data input.submit {
+margin-left:0;
+}
+
+.form_settings label {
+margin-top:2px;
+width:143px;
+}
+
+.form_actions label {
+display:none;
+}
+.form_guide {
+font-style:italic;
+}
+
+.form_settings #settings_autosubscribe label {
+display:inline;
+font-weight:bold;
+}
+
+#form_settings_profile legend,
+#form_login legend,
+#form_register legend,
+#form_password legend,
+#form_settings_avatar legend,
+#newgroup legend,
+#editgroup legend,
+#form_tag_user legend,
+#form_remote_subscribe legend,
+#form_openid_login legend,
+#form_search legend,
+#form_invite legend,
+#form_notice_delete legend,
+#form_password_recover legend,
+#form_password_change legend {
+display:none;
+}
+
+.form_settings .form_data p.form_guide {
+clear:both;
+margin-left:155px;
+margin-bottom:0;
+}
+
+.form_settings p {
+margin-bottom:11px;
+}
+
+.form_settings input.checkbox {
+margin-top:0;
+margin-left:0;
+}
+.form_settings label.checkbox {
+font-weight:normal;
+margin-top:0;
+margin-right:0;
+margin-left:11px;
+float:left;
+width:90%;
+}
+
+
+#form_login p.form_guide,
+#form_register #settings_rememberme p.form_guide,
+#form_openid_login #settings_rememberme p.form_guide,
+#settings_twitter_remove p.form_guide,
+#form_search ul.form_data #q {
+margin-left:0;
+}
+
+.form_settings .form_note {
+border-radius:4px;
+-moz-border-radius:4px;
+-webkit-border-radius:4px;
+padding:0 7px;
+}
+
+
+.form_settings input.form_action-primary {
+padding:0;
+}
+.form_settings input.form_action-secondary {
+margin-left:29px;
+}
+
+#form_search .submit {
+margin-left:11px;
+}
+caption {
+font-weight:bold;
+}
+legend {
+font-weight:bold;
+font-size:1.3em;
+}
+input, textarea, select, option {
+padding:4px;
+font-family:sans-serif;
+font-size:1em;
+}
+input, textarea, select {
+border-width:2px;
+border-style: solid;
+border-radius:4px;
+-moz-border-radius:4px;
+-webkit-border-radius:4px;
+}
+
+input.submit {
+font-weight:bold;
+cursor:pointer;
+}
+textarea {
+overflow:auto;
+}
+option {
+padding-bottom:0;
+}
+fieldset {
+padding:0;
+border:0;
+}
+form ul li {
+list-style-type:none;
+margin:0 0 18px 0;
+}
+form label {
+font-weight:bold;
+}
+input.checkbox {
+position:relative;
+top:2px;
+left:0;
+border:0;
+}
+
+.error,
+.success {
+padding:4px 7px;
+border-radius:4px;
+-moz-border-radius:4px;
+-webkit-border-radius:4px;
+margin-bottom:18px;
+}
+
+/* #all .notice, #public .notice { padding-bottom: 12px;} */
+
+/* #all .notice .entry-title, #public .notice .entry-title { position: relative;} */
+
+/* #all .notice .entry-title .entry-content, #public .notice .entry-title .entry-content { position: absolute; top: 25px; left: 55px; } */
+
+/* #all .notice .entry-content .timestamp, #public .notice .entry-content .timestamp { color: #666; margin-left: 55px; font-size: 80%; text-decoration: none !important; } */
+
+.notices div.entry-content { font-size: 80%; }
+
+.notices div.entry-content a, .notices div.entry-content abbr{ font-color: #666 !important; text-decoration: none !important; }
+
+abbr { border: 0px !important; }
+
+#all .entry-title .author .nickname, #public .entry-title .author .nickname { position: absolute; top: 0; left: 55px; font-weight: bold; }
+
+#showstream #i { position: absolute; top: 0; left: 0; background-color: white; z-index: 100; width: 185px; }
+
+
+.notice-options, .form_favor .submit, .form_repeat .submit { background-color: white; border: 0; display: none !important; }
+
+#form_notice { margin-top: 10px;}
+
+
+/* tagcloud */
+.tag-cloud {
+list-style-type:none;
+text-align:center;
+}
+.aside .tag-cloud {
+font-size:0.8em;
+word-wrap:break-word;
+}
+.tag-cloud li {
+display:inline;
+margin-right:7px;
+line-height:1.25;
+}
+
+.tag-cloud li:before {
+content:'\0009';
+}
+
+.aside .tag-cloud li {
+line-height:1.5;
+}
+.tag-cloud li a {
+text-decoration:none;
+}
+#tagcloud.section dt {
+text-transform:uppercase;
+font-weight:bold;
+}
+.tag-cloud-1 {
+font-size:1em;
+}
+.tag-cloud-2 {
+font-size:1.25em;
+}
+.tag-cloud-3 {
+font-size:1.75em;
+}
+.tag-cloud-4 {
+font-size:2em;
+}
+.tag-cloud-5 {
+font-size:2.25em;
+}
+.tag-cloud-6 {
+font-size:2.75em;
+}
+.tag-cloud-7 {
+font-size:3.25em;
+}
+
+
+
+
+.entity_subscriptions ul:before { content: 'Fan of...'; }
+
+#feedback-button-of-doom { position: fixed; top: 350px; left: 0; }
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html>
+ <head>
+ <title>Public timeline - Lorraine Lee — GNU social</title>
+
+
+ <link rel="stylesheet" href="/theme/gnusocial/combo.css" type="text/css">
+ <style type="text/css">
+ #custom-doc { width:76.23em;*width:74.39em;min-width:991px; margin:auto; text-align:left; }
+
+ html, body{padding: 0; margin: 0;}
+
+ body {background-image: url(/theme/gnusocial/bg.png); background-repeat: repeat-x;}
+
+ #hd h1 {margin: 0; line-height: 48px; font-size: 30px; font-weight: bold;}
+
+ #hd h1 a{color: #111; text-decoration: none;}
+
+ #hd dt {display: none;}
+
+ #hd ul {padding: 0; margin: 0; line-height: 48px; position: absolute; top: 0; right: 10px; }
+
+ #hd li {display: inline; list-style: none; margin-left: 12px;}
+
+ #hd {height: 48px; position: relative;}
+
+ #bd {margin-top: 12px;}
+
+ #ft {font-size: 10px; text-align: right; margin-top: 12px}
+
+ form {margin: 0 auto; width: 70%;}
+
+ table {width: 100%;}
+
+ tr, td{border: 0;}
+
+ .update-text{ font-size: 12px; font-weight: bold;}
+
+ .update-icon{ text-align: center;}
+
+ #stream li{list-style: none; position: relative; margin-top: 12px; }
+
+ #stream dl {position: absolute; top: 0; left: 50px;}
+
+ #stream dd {color: #333; font-size: 80%; padding: 0; margin: 0; margin-top: 6px;}
+
+
+ #social {border-left: 1px solid #999; border-right: 1px solid #999; padding-left: 10px;}
+
+ #sidebar ul{margin: 0; padding: 0;}
+
+ #sidebar li {list-style: none;}
+
+ #sidebar li a{display: block; width: 180px; padding: 4px;}
+
+ #sidebar li a:hover {background-color: #ececec;}
+
+ .selected {background-color: cyan; width: 180px;}
+
+ #right-nav {background-color: #ececec;}
+
+ #right-nav div {padding: 10px;}
+
+ </style>
+
+
+ </head>
+ <body id="public">
+<div id="custom-doc" class="yui-t2">
+ <div id="hd">
+ <h1>GNU social</h1>
+
+ <dl id="site_nav_global_primary">
+ <dt>Primary site navigation</dt>
+ <dd>
+ <ul class="nav">
+ <li id="nav_login">
+ <a href="http://lorrainelee.co.uk/main/login" title="Login to the site">Login</a>
+</li>
+ <li id="nav_help">
+ <a href="http://lorrainelee.co.uk/doc/help" title="Help me!">Help</a>
+</li>
+ <li id="nav_search">
+ <a href="http://lorrainelee.co.uk/search/people" title="Search for people or text">Search</a>
+</li>
+</ul>
+</dd>
+</dl>
+ </div>
+ <div id="bd">
+
+ <div id="yui-main">
+
+ <div class="yui-b">
+
+ <div class="yui-gc">
+
+ <div class="yui-u first">
+
+ <dl id="site_notice" class="system_notice">
+ <dt>Site notice</dt>
+ <dd>Powered by <a href="http://www.gnu.org/software/social/">GNU social</a></dd>
+</dl>
+ <div id="anon_notice"><p>This is Lorraine Lee, a <a href="http://en.wikipedia.org/wiki/Micro-blogging">micro-blogging</a> service based on the Free Software <a href="http://status.net/">StatusNet</a> tool.</p>
+</div>
+ <div id="content">
+ <h1>Public timeline</h1>
+ <div id="content_inner">
+ <div id="notices_primary">
+ <h2>Notices</h2>
+ <ol class="notices xoxo">
+ <li class="hentry notice" id="notice-5">
+ <div class="entry-title">
+ <span class="vcard author">
+ <a href="http://lorrainelee.co.uk/lorraine" class="url" title="Lorraine Lee (lorraine)">
+ <img src="http://lorrainelee.co.uk/avatar/3-48-20100722212232.jpeg" class="avatar photo" width="48" height="48" alt="Lorraine Lee"/>
+ <span class="nickname fn">lorraine</span></a>
+</span>
+ <p class="entry-content">im going to brush my teeth</p>
+</div>
+ <div class="entry-content">
+ <a rel="bookmark" class="timestamp" href="http://lorrainelee.co.uk/notice/5">
+ <abbr class="published" title="2010-07-22T21:41:40+00:00">about 5 hours ago</abbr>
+</a>
+ <span class="source">from <span class="device">web</span>
+</span>
+ <span class="location">at <a href="http://www.geonames.org/2651292" rel="external"><abbr class="geo" title="50.7500000;-3.7500000">County of Devon, England, United Kingdom of Great Britain and Northern Ireland</abbr></a></span>
+</div>
+</li>
+ <li class="hentry notice" id="notice-2">
+ <div class="entry-title">
+ <span class="vcard author">
+ <a href="http://lorrainelee.co.uk/lorraine" class="url" title="Lorraine Lee (lorraine)">
+ <img src="http://lorrainelee.co.uk/avatar/3-48-20100722212232.jpeg" class="avatar photo" width="48" height="48" alt="Lorraine Lee"/>
+ <span class="nickname fn">lorraine</span></a>
+</span>
+ <p class="entry-content">nothing im fine thank you</p>
+</div>
+ <div class="entry-content">
+ <a rel="bookmark" class="timestamp" href="http://lorrainelee.co.uk/notice/2">
+ <abbr class="published" title="2010-07-22T21:38:18+00:00">about 5 hours ago</abbr>
+</a>
+ <span class="source">from <span class="device">web</span>
+</span>
+ <span class="location">at <a href="http://www.geonames.org/2651292" rel="external"><abbr class="geo" title="50.7500000;-3.7500000">County of Devon, England, United Kingdom of Great Britain and Northern Ireland</abbr></a></span>
+ <a href="http://lorrainelee.co.uk/conversation/2#notice-2" class="response">in context</a>
+</div>
+</li>
+</ol>
+</div>
+</div>
+</div>
+
+ </div>
+
+
+ <div class="yui-u" id="right-nav">
+
+
+
+
+
+ <div id="aside_primary" class="aside">
+ <div id="featured_users" class="section">
+ <h2>Featured users</h2>
+</div>
+ </div>
+
+
+ </div>
+</div>
+ </div>
+</div>
+
+ <div class="yui-b" id="sidebar">
+ <dl id="site_nav_local_views">
+ <dt>Local views</dt>
+ <dd>
+ <ul class="nav">
+ <li class="current" id="nav_timeline_public">
+ <a href="http://lorrainelee.co.uk/" title="Public timeline">Public</a>
+</li>
+ <li id="nav_groups">
+ <a href="http://lorrainelee.co.uk/group" title="User groups">Groups</a>
+</li>
+ <li id="nav_recent-tags">
+ <a href="http://lorrainelee.co.uk/tags" title="Recent tags">Recent tags</a>
+</li>
+ <li id="nav_timeline_favorited">
+ <a href="http://lorrainelee.co.uk/favorited" title="Popular notices">Popular</a>
+</li>
+</ul>
+</dd>
+</dl>
+ </div>
+
+ </div>
+ <div id="ft">
+
+ <dl id="licenses">
+ <dt id="site_statusnet_license">StatusNet software licence</dt>
+ <dd><p><strong>Lorraine Lee</strong> is a microblogging service brought to you by <a href="http://www.gnu.org/s/social/">GNU social</a>. It runs the <a href="http://status.net/">StatusNet</a> microblogging software, version 0.9.3, available under the <a href="http://www.fsf.org/licensing/licenses/agpl-3.0.html">GNU Affero General Public Licence</a>.</p>
+</dd>
+ <dt id="site_content_license">Site content license</dt>
+ <dd id="site_content_license_cc">
+ <p>
+ <img id="license_cc" src="http://i.creativecommons.org/l/by/3.0/80x15.png" alt="Creative Commons Attribution 3.0" width="80" height="15"/>
+ All Lorraine Lee content and data are available under the <a class="license" rel="external license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> licence.</p>
+</dd>
+</dl>
+ </div>
+ </div>
+ </body>
+ </html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+ <head>
+ <title><?php echo section('title'); ?> — GNU social</title>
+
+
+ <link rel="stylesheet" href="/theme/gnusocial/css/combo.css" type="text/css">
+ <link rel="stylesheet" href="/theme/gnusocial/css/social.css" type="text/css">
+ <?php echo section('scripts'); ?>
+ <?php echo section('search'); ?>
+ <?php echo section('feeds'); ?>
+ <?php echo section('description'); ?>
+ <?php echo section('head'); ?>
+ </head>
+ <body id="<?php echo section('action'); ?>">
+
+ <div id="feedback-button-of-doom"><a href="http://social.foocorp.net/shapado.html"><img src="/theme/gnusocial/images/fback.png" title="Send us your ideas and suggestions" alt="Feedback" /></a></div>
+
+
+ <div id="doc2" class="yui-t6">
+ <div id="hd">
+ <h1><a href="/">GNU social</a></h1>
+ <?php echo section('nav'); ?>
+ </div>
+ <div id="bd">
+ <div id="yui-main">
+ <div class="yui-b" id="social">
+ <div class="yui-g">
+ <?php echo section('noticeform'); ?>
+ <?php echo section('bodytext'); ?>
+ </div>
+</div>
+</div>
+
+
+ <div class="yui-b" id="right-nav">
+ <div id="aside_primary" class="aside">
+ <?php echo section('subscriptions'); ?>
+ <?php echo section('subscribers'); ?>
+ <?php echo section('groups'); ?>
+ <?php echo section('cloud'); ?>
+ <?php echo section('popular'); ?>
+ <?php echo section('localnav'); ?>
+ </div>
+ </div>
+ <div id="ft">
+ <p>This is <a href="http://www.gnu.org/software/social">GNU social</a> — licensed under the <a href="http://www.gnu.org/licenses/agpl-3.0.html">GNU Affero General Public License</a> version 3.0 or later. <a href="http://gitorious.org/+socialites/statusnet/gnu-social">Get the code</a>.</p>
+ </div>
+ </div>
+ </body>
+</html>