]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '1.0.x' of git://gitorious.org/statusnet/mainline
authorIan Denhardt <ian@zenhack.net>
Tue, 24 May 2011 01:50:48 +0000 (21:50 -0400)
committerIan Denhardt <ian@zenhack.net>
Tue, 24 May 2011 01:50:48 +0000 (21:50 -0400)
Conflicts:
plugins/OStatus/actions/ostatusinit.php

97 files changed:
README.SOCIAL [new file with mode: 0644]
TODO.SOCIAL [new file with mode: 0644]
dc/better.png [new file with mode: 0644]
dc/dc.png [new file with mode: 0644]
dc/dclogo.png [new file with mode: 0644]
dc/done.html [new file with mode: 0644]
dc/getinvolved.png [new file with mode: 0644]
dc/idea.png [new file with mode: 0644]
dc/index.html [new file with mode: 0644]
dc/list.html [new file with mode: 0644]
dc/offer.png [new file with mode: 0644]
dc/privacy.png [new file with mode: 0644]
dc/signup.html [new file with mode: 0644]
dc/signup.png [new file with mode: 0644]
dc/signup2.html [new file with mode: 0644]
dc/youandus.png [new file with mode: 0644]
doc-src/about
doc-src/faq
lib/action.php
locale/ca/LC_MESSAGES/statusnet.po
plugins/GNUsocialDukiDukiSnail/GNUsocialDukiDukiSnail.php [new file with mode: 0644]
plugins/GNUsocialDukiDukiSnail/README [new file with mode: 0644]
plugins/GNUsocialEvents/GNUsocialEvents.php [new file with mode: 0644]
plugins/GNUsocialLayout/GNUsocialLayout.php [new file with mode: 0644]
plugins/GNUsocialOffTheRecordMessaging/GNUsocialOffTheRecordMessaging.php [new file with mode: 0644]
plugins/GNUsocialPhotos/GNUsocialPhotosPlugin.php [new file with mode: 0644]
plugins/GNUsocialPhotos/actions/editphoto.php [new file with mode: 0644]
plugins/GNUsocialPhotos/actions/photo.php [new file with mode: 0644]
plugins/GNUsocialPhotos/actions/photos.php [new file with mode: 0644]
plugins/GNUsocialPhotos/actions/photoupload.php [new file with mode: 0644]
plugins/GNUsocialPhotos/classes/gnusocialphoto.php [new file with mode: 0644]
plugins/GNUsocialPhotos/classes/gnusocialphotoalbum.php [new file with mode: 0644]
plugins/GNUsocialPhotos/lib/photolib.php [new file with mode: 0644]
plugins/GNUsocialPhotos/lib/photonav.php [new file with mode: 0644]
plugins/GNUsocialPhotos/lib/tempphoto.php [new file with mode: 0644]
plugins/GNUsocialPhotos/res/gnusocialphotos.js [new file with mode: 0644]
plugins/GNUsocialPhotos/res/style.css [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/GNUsocialProfileExtensionsPlugin.php [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/README.txt [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/actions/bio.php [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/actions/newresponse.php [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/actions/profilefields.php [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionField.php [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionResponse.php [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/classes/ProfileExtensionField.php [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/js/profile.js [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/lib/noticetree.php [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/lib/profiletools.php [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/res/bgstripe.gif [new file with mode: 0644]
plugins/GNUsocialProfileExtensions/res/style.css [new file with mode: 0644]
plugins/GNUsocialRelationshipsManager/GNUsocialRelationshipsManager.php [new file with mode: 0644]
plugins/GNUsocialTemplatePlugin.php [new file with mode: 0644]
plugins/GNUsocialVideo/GNUsocialVideoPlugin.php [new file with mode: 0644]
plugins/GNUsocialVideo/Video.php [new file with mode: 0644]
plugins/GNUsocialVideo/actions/postvideo.php [new file with mode: 0644]
plugins/GNUsocialVideo/showvideo.php [new file with mode: 0644]
plugins/GNUsocialVideo/videoform.php [new file with mode: 0644]
plugins/OStatus/actions/ostatusinit.php
plugins/OpenID/doc-src/openid
socialfy-your-domain/README.txt [new file with mode: 0644]
socialfy-your-domain/dot-well-known/host-meta [new file with mode: 0644]
socialfy-your-domain/xrd/example@example.com [new file with mode: 0644]
socialfy-your-domain/xrd/index.php [new file with mode: 0644]
theme/GNUSocial/css/default.css [new file with mode: 0644]
theme/gnusocial/css/combo.css [new file with mode: 0644]
theme/gnusocial/css/debug.css [new file with mode: 0644]
theme/gnusocial/css/display.css [new file with mode: 0644]
theme/gnusocial/css/social.css [new file with mode: 0755]
theme/gnusocial/default-avatar-mini.png [new file with mode: 0755]
theme/gnusocial/default-avatar-profile.png [new file with mode: 0755]
theme/gnusocial/default-avatar-stream.png [new file with mode: 0755]
theme/gnusocial/images/bg.png [new file with mode: 0644]
theme/gnusocial/images/fback.png [new file with mode: 0644]
theme/gnusocial/images/icons/icon_atom.png [new file with mode: 0755]
theme/gnusocial/images/icons/icon_disfavourite.gif [new file with mode: 0755]
theme/gnusocial/images/icons/icon_favourite.gif [new file with mode: 0755]
theme/gnusocial/images/icons/icon_foaf.gif [new file with mode: 0755]
theme/gnusocial/images/icons/icon_processing.gif [new file with mode: 0755]
theme/gnusocial/images/icons/icon_reply.gif [new file with mode: 0755]
theme/gnusocial/images/icons/icon_rss.png [new file with mode: 0755]
theme/gnusocial/images/icons/icon_trash.gif [new file with mode: 0755]
theme/gnusocial/images/icons/icon_vcard.gif [new file with mode: 0755]
theme/gnusocial/images/icons/twotone/green/arrow-left.gif [new file with mode: 0755]
theme/gnusocial/images/icons/twotone/green/arrow-right.gif [new file with mode: 0755]
theme/gnusocial/images/icons/twotone/green/edit.gif [new file with mode: 0755]
theme/gnusocial/images/icons/twotone/green/mail.gif [new file with mode: 0755]
theme/gnusocial/images/icons/twotone/green/news.gif [new file with mode: 0755]
theme/gnusocial/images/icons/twotone/green/quote.gif [new file with mode: 0755]
theme/gnusocial/images/icons/twotone/green/shield.gif [new file with mode: 0755]
theme/gnusocial/images/illustrations/illu_arrow-up-01.gif [new file with mode: 0755]
theme/gnusocial/images/illustrations/illu_clouds-01.gif [new file with mode: 0755]
theme/gnusocial/images/illustrations/illu_jcrop.gif [new file with mode: 0755]
theme/gnusocial/images/illustrations/illu_progress_loading-01.gif [new file with mode: 0755]
theme/gnusocial/images/illustrations/illu_unicorn-01.png [new file with mode: 0755]
theme/gnusocial/index.html [new file with mode: 0644]
theme/gnusocial/logo.png [new file with mode: 0755]
tpl/social.php [new file with mode: 0644]

diff --git a/README.SOCIAL b/README.SOCIAL
new file mode 100644 (file)
index 0000000..3394dca
--- /dev/null
@@ -0,0 +1,23 @@
+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
+
diff --git a/TODO.SOCIAL b/TODO.SOCIAL
new file mode 100644 (file)
index 0000000..4d4866f
--- /dev/null
@@ -0,0 +1,12 @@
+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
diff --git a/dc/better.png b/dc/better.png
new file mode 100644 (file)
index 0000000..f6b5280
Binary files /dev/null and b/dc/better.png differ
diff --git a/dc/dc.png b/dc/dc.png
new file mode 100644 (file)
index 0000000..773b872
Binary files /dev/null and b/dc/dc.png differ
diff --git a/dc/dclogo.png b/dc/dclogo.png
new file mode 100644 (file)
index 0000000..6bb75f5
Binary files /dev/null and b/dc/dclogo.png differ
diff --git a/dc/done.html b/dc/done.html
new file mode 100644 (file)
index 0000000..41cd23d
--- /dev/null
@@ -0,0 +1,47 @@
+<!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 &mdash; 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 &mdash; &copy; 2010 <a href="http://foocorp.net/">Foo Communications, LLC</a></small></p>
+
+</body>
+</html>
diff --git a/dc/getinvolved.png b/dc/getinvolved.png
new file mode 100644 (file)
index 0000000..8c2c0fb
Binary files /dev/null and b/dc/getinvolved.png differ
diff --git a/dc/idea.png b/dc/idea.png
new file mode 100644 (file)
index 0000000..def6895
Binary files /dev/null and b/dc/idea.png differ
diff --git a/dc/index.html b/dc/index.html
new file mode 100644 (file)
index 0000000..6c8e4af
--- /dev/null
@@ -0,0 +1,102 @@
+<!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 &mdash; 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?)
+&mdash; 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 &mdash; &copy; 2010 <a href="http://foocorp.net/">Foo Communications, LLC</a></small></p>
+
+</body>
+</html>
diff --git a/dc/list.html b/dc/list.html
new file mode 100644 (file)
index 0000000..a1e806e
--- /dev/null
@@ -0,0 +1,43 @@
+<!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 &mdash; 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&amp;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"> &nbsp;<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
diff --git a/dc/offer.png b/dc/offer.png
new file mode 100644 (file)
index 0000000..28dac6a
Binary files /dev/null and b/dc/offer.png differ
diff --git a/dc/privacy.png b/dc/privacy.png
new file mode 100644 (file)
index 0000000..b060d0b
Binary files /dev/null and b/dc/privacy.png differ
diff --git a/dc/signup.html b/dc/signup.html
new file mode 100644 (file)
index 0000000..957c53a
--- /dev/null
@@ -0,0 +1,56 @@
+<!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 &mdash; 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
diff --git a/dc/signup.png b/dc/signup.png
new file mode 100644 (file)
index 0000000..046a720
Binary files /dev/null and b/dc/signup.png differ
diff --git a/dc/signup2.html b/dc/signup2.html
new file mode 100644 (file)
index 0000000..f8f4449
--- /dev/null
@@ -0,0 +1,86 @@
+<!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 &mdash; 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"
+/>&nbsp; I've read and agree with the privacy policy. </label></p>
+
+<p><label for="terms"><input type="checkbox" name="terms" id="terms"
+/>&nbsp; I've read and agree with the terms and conditions. </label></p>
+
+<p><label for="bad"><input type="checkbox" name="bad" id="bad"
+/>&nbsp; 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>
+
diff --git a/dc/youandus.png b/dc/youandus.png
new file mode 100644 (file)
index 0000000..682d2ab
Binary files /dev/null and b/dc/youandus.png differ
index 5645c2cebe6ed30007eaae11e95d5178f77b4203..fcf2bf373a9776bfacd8a83cdc2a20dab32e4060 100644 (file)
@@ -3,8 +3,7 @@
 <!-- http://creativecommons.org/licenses/by/3.0/ for details. -->
 
 %%site.name%% is a
-[micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service
-based on the Free Software [StatusNet](http://status.net/) tool.
+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
index 589b18b8d8844193e21188e4d941f90deb643d49..39948723de08197f0ef5d0c1726fdcb242ddb877 100644 (file)
@@ -5,42 +5,17 @@
 These are some *Frequently Asked Questions* about this service, with
 some answers.
 
-What is %%site.name%%?
+What is this site?
 ----------------------
 
-%%site.name%% is a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service.
-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.
+This is a social network, running the GNU social software.
 
-How is %%site.name%% different from Twitter, Jaiku, Pownce, Plurk, others?
---------------------------------------------------------------------------
+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.
 
-%%site.name%% is an [Open Network Service](http://opendefinition.org/ossd). Our main
-goal is to provide a fair and transparent service that preserves users' autonomy. In
-particular, all the software used for %%site.name%% is [Free Software](http://en.wikipedia.org/wiki/Free_Software), and all the data is available
-under the [%%license.title%%](%%license.url%%) license, making it Open Data.
-
-The software also implements the [OpenMicroBlogging](http://openmicroblogging.org/) protocol, meaning that you can have friends on other microblogging services
-that can receive your notices.
-
-The goal here is *autonomy* -- you deserve the right to manage your own on-line
-presence. If you don't like how %%site.name%% works, you can take your data and the source code and set up your own server (or move your account to another one).
-
-Where is feature X?
--------------------
-
-The software we run, [StatusNet](http://status.net/), is still in its early stages,
-and many features people expect from microblogging sites are not yet implemented. Some important ones that are expected "soon":
-
-* More [AJAX](http://en.wikipedia.org/wiki/AJAX)-y interface
-* Maps
-* Cross-post to Pownce, Jaiku, etc.
-* Pull messages from Twitter, Pownce, Jaiku, etc.
-* [Facebook](http://www.facebook.com/) integration
-* Image, video, audio notices
-
-There is [a list of bugs and features](http://status.net/bugs/) that you may find
-interesting. New ideas or complaints are very welcome.
+In the future, we'll be adding support for photo, video and file
+sharing, as well as events, better contact management and mobile
+devices.
 
 
index 3492873c5981574b7d29e71a08641759f901cca6..9ef7af2b983a1a09c2fbb7daedb80eee1aebfa64 100644 (file)
@@ -992,17 +992,17 @@ class Action extends HTMLOutputter // lawsuit
             // 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
index 69ec7744f700b46691b7d9d24c3f26bd8b44dd65..8f96cefacc8d873b46b0b201aeafd0b9f9005f9e 100644 (file)
@@ -8620,8 +8620,7 @@ msgid ""
 "\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"
diff --git a/plugins/GNUsocialDukiDukiSnail/GNUsocialDukiDukiSnail.php b/plugins/GNUsocialDukiDukiSnail/GNUsocialDukiDukiSnail.php
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/plugins/GNUsocialDukiDukiSnail/README b/plugins/GNUsocialDukiDukiSnail/README
new file mode 100644 (file)
index 0000000..98ae652
--- /dev/null
@@ -0,0 +1,151 @@
+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
diff --git a/plugins/GNUsocialEvents/GNUsocialEvents.php b/plugins/GNUsocialEvents/GNUsocialEvents.php
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/plugins/GNUsocialLayout/GNUsocialLayout.php b/plugins/GNUsocialLayout/GNUsocialLayout.php
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/plugins/GNUsocialOffTheRecordMessaging/GNUsocialOffTheRecordMessaging.php b/plugins/GNUsocialOffTheRecordMessaging/GNUsocialOffTheRecordMessaging.php
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/plugins/GNUsocialPhotos/GNUsocialPhotosPlugin.php b/plugins/GNUsocialPhotos/GNUsocialPhotosPlugin.php
new file mode 100644 (file)
index 0000000..499d376
--- /dev/null
@@ -0,0 +1,213 @@
+<?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');
+    }
+}
+
diff --git a/plugins/GNUsocialPhotos/actions/editphoto.php b/plugins/GNUsocialPhotos/actions/editphoto.php
new file mode 100644 (file)
index 0000000..34f41a2
--- /dev/null
@@ -0,0 +1,204 @@
+<?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;
+    }
+
+}
diff --git a/plugins/GNUsocialPhotos/actions/photo.php b/plugins/GNUsocialPhotos/actions/photo.php
new file mode 100644 (file)
index 0000000..4d08e5b
--- /dev/null
@@ -0,0 +1,107 @@
+<?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();
+    }
+}
diff --git a/plugins/GNUsocialPhotos/actions/photos.php b/plugins/GNUsocialPhotos/actions/photos.php
new file mode 100644 (file)
index 0000000..3b8a0fd
--- /dev/null
@@ -0,0 +1,174 @@
+<?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();
+    }
+}
diff --git a/plugins/GNUsocialPhotos/actions/photoupload.php b/plugins/GNUsocialPhotos/actions/photoupload.php
new file mode 100644 (file)
index 0000000..5411ec8
--- /dev/null
@@ -0,0 +1,259 @@
+<?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);
+        }
+    }
+}
diff --git a/plugins/GNUsocialPhotos/classes/gnusocialphoto.php b/plugins/GNUsocialPhotos/classes/gnusocialphoto.php
new file mode 100644 (file)
index 0000000..a122d44
--- /dev/null
@@ -0,0 +1,148 @@
+<?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;
+    }
+}
diff --git a/plugins/GNUsocialPhotos/classes/gnusocialphotoalbum.php b/plugins/GNUsocialPhotos/classes/gnusocialphotoalbum.php
new file mode 100644 (file)
index 0000000..848f92d
--- /dev/null
@@ -0,0 +1,113 @@
+<?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;
+    }
+
+}
diff --git a/plugins/GNUsocialPhotos/lib/photolib.php b/plugins/GNUsocialPhotos/lib/photolib.php
new file mode 100644 (file)
index 0000000..9e5ff61
--- /dev/null
@@ -0,0 +1,87 @@
+<?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;
+}
diff --git a/plugins/GNUsocialPhotos/lib/photonav.php b/plugins/GNUsocialPhotos/lib/photonav.php
new file mode 100644 (file)
index 0000000..180f0ad
--- /dev/null
@@ -0,0 +1,63 @@
+<?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');
+    }
+}
diff --git a/plugins/GNUsocialPhotos/lib/tempphoto.php b/plugins/GNUsocialPhotos/lib/tempphoto.php
new file mode 100644 (file)
index 0000000..119c301
--- /dev/null
@@ -0,0 +1,37 @@
+<?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; 
+}
diff --git a/plugins/GNUsocialPhotos/res/gnusocialphotos.js b/plugins/GNUsocialPhotos/res/gnusocialphotos.js
new file mode 100644 (file)
index 0000000..8681f61
--- /dev/null
@@ -0,0 +1,29 @@
+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
diff --git a/plugins/GNUsocialPhotos/res/style.css b/plugins/GNUsocialPhotos/res/style.css
new file mode 100644 (file)
index 0000000..bb7b619
--- /dev/null
@@ -0,0 +1,14 @@
+.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
diff --git a/plugins/GNUsocialProfileExtensions/GNUsocialProfileExtensionsPlugin.php b/plugins/GNUsocialProfileExtensions/GNUsocialProfileExtensionsPlugin.php
new file mode 100644 (file)
index 0000000..cf5fb0a
--- /dev/null
@@ -0,0 +1,187 @@
+<?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;
+    }
+
+}
+
diff --git a/plugins/GNUsocialProfileExtensions/README.txt b/plugins/GNUsocialProfileExtensions/README.txt
new file mode 100644 (file)
index 0000000..fd496cd
--- /dev/null
@@ -0,0 +1,16 @@
+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';
+
diff --git a/plugins/GNUsocialProfileExtensions/actions/bio.php b/plugins/GNUsocialProfileExtensions/actions/bio.php
new file mode 100644 (file)
index 0000000..bc9f12c
--- /dev/null
@@ -0,0 +1,105 @@
+<?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');
+    }
+
+}
+
+
diff --git a/plugins/GNUsocialProfileExtensions/actions/newresponse.php b/plugins/GNUsocialProfileExtensions/actions/newresponse.php
new file mode 100644 (file)
index 0000000..0e71ee3
--- /dev/null
@@ -0,0 +1,53 @@
+<?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
diff --git a/plugins/GNUsocialProfileExtensions/actions/profilefields.php b/plugins/GNUsocialProfileExtensions/actions/profilefields.php
new file mode 100644 (file)
index 0000000..d8c4e3d
--- /dev/null
@@ -0,0 +1,177 @@
+<?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'));
+    }
+}
diff --git a/plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionField.php b/plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionField.php
new file mode 100644 (file)
index 0000000..6877eab
--- /dev/null
@@ -0,0 +1,109 @@
+<?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;
+    }
+}
diff --git a/plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionResponse.php b/plugins/GNUsocialProfileExtensions/classes/GNUsocialProfileExtensionResponse.php
new file mode 100644 (file)
index 0000000..fbf5af0
--- /dev/null
@@ -0,0 +1,109 @@
+<?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;
+    }
+
+}
diff --git a/plugins/GNUsocialProfileExtensions/classes/ProfileExtensionField.php b/plugins/GNUsocialProfileExtensions/classes/ProfileExtensionField.php
new file mode 100644 (file)
index 0000000..142b702
--- /dev/null
@@ -0,0 +1,148 @@
+<?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;
+    }
+}
diff --git a/plugins/GNUsocialProfileExtensions/js/profile.js b/plugins/GNUsocialProfileExtensions/js/profile.js
new file mode 100644 (file)
index 0000000..4a2f1c5
--- /dev/null
@@ -0,0 +1,4 @@
+SN.U.NoticeReplySet = function(nick,id) {
+    $('div.replyform').hide();
+    $('div#form'+id).show();
+}
diff --git a/plugins/GNUsocialProfileExtensions/lib/noticetree.php b/plugins/GNUsocialProfileExtensions/lib/noticetree.php
new file mode 100644 (file)
index 0000000..bdf9d32
--- /dev/null
@@ -0,0 +1,228 @@
+<?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
diff --git a/plugins/GNUsocialProfileExtensions/lib/profiletools.php b/plugins/GNUsocialProfileExtensions/lib/profiletools.php
new file mode 100644 (file)
index 0000000..fa3cfa9
--- /dev/null
@@ -0,0 +1,47 @@
+<?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
diff --git a/plugins/GNUsocialProfileExtensions/res/bgstripe.gif b/plugins/GNUsocialProfileExtensions/res/bgstripe.gif
new file mode 100644 (file)
index 0000000..035ef80
Binary files /dev/null and b/plugins/GNUsocialProfileExtensions/res/bgstripe.gif differ
diff --git a/plugins/GNUsocialProfileExtensions/res/style.css b/plugins/GNUsocialProfileExtensions/res/style.css
new file mode 100644 (file)
index 0000000..db545b8
--- /dev/null
@@ -0,0 +1,23 @@
+.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
diff --git a/plugins/GNUsocialRelationshipsManager/GNUsocialRelationshipsManager.php b/plugins/GNUsocialRelationshipsManager/GNUsocialRelationshipsManager.php
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/plugins/GNUsocialTemplatePlugin.php b/plugins/GNUsocialTemplatePlugin.php
new file mode 100644 (file)
index 0000000..7db2f73
--- /dev/null
@@ -0,0 +1,359 @@
+<?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];
+}
+
diff --git a/plugins/GNUsocialVideo/GNUsocialVideoPlugin.php b/plugins/GNUsocialVideo/GNUsocialVideoPlugin.php
new file mode 100644 (file)
index 0000000..8507616
--- /dev/null
@@ -0,0 +1,153 @@
+<?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);
+}
+
+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();
+        }
+    }
+}
diff --git a/plugins/GNUsocialVideo/Video.php b/plugins/GNUsocialVideo/Video.php
new file mode 100644 (file)
index 0000000..40c74e0
--- /dev/null
@@ -0,0 +1,112 @@
+<?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 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);
+
+    }
+}
diff --git a/plugins/GNUsocialVideo/actions/postvideo.php b/plugins/GNUsocialVideo/actions/postvideo.php
new file mode 100644 (file)
index 0000000..db27ecd
--- /dev/null
@@ -0,0 +1,86 @@
+<?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);
+}
+
+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();
+
+        $vid = Video::saveNew($profile, $this->url, array());
+
+        common_redirect($vid->uri, 303);
+    }
+    function showContent()
+    {
+        $form  = new VideoForm();
+        $form->show();
+    }
+}
diff --git a/plugins/GNUsocialVideo/showvideo.php b/plugins/GNUsocialVideo/showvideo.php
new file mode 100644 (file)
index 0000000..be4f18d
--- /dev/null
@@ -0,0 +1,67 @@
+<?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 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;
+    }
+}
diff --git a/plugins/GNUsocialVideo/videoform.php b/plugins/GNUsocialVideo/videoform.php
new file mode 100644 (file)
index 0000000..d47605a
--- /dev/null
@@ -0,0 +1,61 @@
+<?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 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 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');
+        $this->out->elementEnd('fieldset');
+    }
+
+    function formActions()
+    {
+        $this->out->submit('submit', _m('BUTTON', 'Save'));
+    }
+}
index cba5ff57fc984777911b1373eb5e63013a45512a..78c559e5799146dc0f26ca040a603f9365da469c 100644 (file)
@@ -98,6 +98,7 @@ class OStatusInitAction extends Action
 
     function showContent()
     {
+    
         if ($this->group) {
             // TRANS: Form legend. %s is a group name.
             $header = sprintf(_m('Join group %s'), $this->group);
index f2dc610a55d0db2bda34add93b5f6bce5e6251dd..0f081285055fb43ce69ebb813ac2503a7b2bd6d4 100644 (file)
@@ -9,3 +9,5 @@ There are many [Public OpenID providers](http://wiki.openid.net/OpenID-Providers
 * [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.
diff --git a/socialfy-your-domain/README.txt b/socialfy-your-domain/README.txt
new file mode 100644 (file)
index 0000000..ead4380
--- /dev/null
@@ -0,0 +1,57 @@
+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.
diff --git a/socialfy-your-domain/dot-well-known/host-meta b/socialfy-your-domain/dot-well-known/host-meta
new file mode 100644 (file)
index 0000000..e44591a
--- /dev/null
@@ -0,0 +1 @@
+<?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
diff --git a/socialfy-your-domain/xrd/example@example.com b/socialfy-your-domain/xrd/example@example.com
new file mode 100644 (file)
index 0000000..0698752
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>
diff --git a/socialfy-your-domain/xrd/index.php b/socialfy-your-domain/xrd/index.php
new file mode 100644 (file)
index 0000000..25f1d8b
--- /dev/null
@@ -0,0 +1,41 @@
+<?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
diff --git a/theme/GNUSocial/css/default.css b/theme/GNUSocial/css/default.css
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/theme/gnusocial/css/combo.css b/theme/gnusocial/css/combo.css
new file mode 100644 (file)
index 0000000..3f31a52
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+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
diff --git a/theme/gnusocial/css/debug.css b/theme/gnusocial/css/debug.css
new file mode 100644 (file)
index 0000000..a77e2bc
--- /dev/null
@@ -0,0 +1,7 @@
+#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
diff --git a/theme/gnusocial/css/display.css b/theme/gnusocial/css/display.css
new file mode 100644 (file)
index 0000000..3f31a52
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+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
diff --git a/theme/gnusocial/css/social.css b/theme/gnusocial/css/social.css
new file mode 100755 (executable)
index 0000000..320ccfc
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+
+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
diff --git a/theme/gnusocial/default-avatar-mini.png b/theme/gnusocial/default-avatar-mini.png
new file mode 100755 (executable)
index 0000000..4fd8bd9
Binary files /dev/null and b/theme/gnusocial/default-avatar-mini.png differ
diff --git a/theme/gnusocial/default-avatar-profile.png b/theme/gnusocial/default-avatar-profile.png
new file mode 100755 (executable)
index 0000000..eb08571
Binary files /dev/null and b/theme/gnusocial/default-avatar-profile.png differ
diff --git a/theme/gnusocial/default-avatar-stream.png b/theme/gnusocial/default-avatar-stream.png
new file mode 100755 (executable)
index 0000000..926b8a9
Binary files /dev/null and b/theme/gnusocial/default-avatar-stream.png differ
diff --git a/theme/gnusocial/images/bg.png b/theme/gnusocial/images/bg.png
new file mode 100644 (file)
index 0000000..a30fca0
Binary files /dev/null and b/theme/gnusocial/images/bg.png differ
diff --git a/theme/gnusocial/images/fback.png b/theme/gnusocial/images/fback.png
new file mode 100644 (file)
index 0000000..cad0194
Binary files /dev/null and b/theme/gnusocial/images/fback.png differ
diff --git a/theme/gnusocial/images/icons/icon_atom.png b/theme/gnusocial/images/icons/icon_atom.png
new file mode 100755 (executable)
index 0000000..6a001f1
Binary files /dev/null and b/theme/gnusocial/images/icons/icon_atom.png differ
diff --git a/theme/gnusocial/images/icons/icon_disfavourite.gif b/theme/gnusocial/images/icons/icon_disfavourite.gif
new file mode 100755 (executable)
index 0000000..2b02ac8
Binary files /dev/null and b/theme/gnusocial/images/icons/icon_disfavourite.gif differ
diff --git a/theme/gnusocial/images/icons/icon_favourite.gif b/theme/gnusocial/images/icons/icon_favourite.gif
new file mode 100755 (executable)
index 0000000..716ce35
Binary files /dev/null and b/theme/gnusocial/images/icons/icon_favourite.gif differ
diff --git a/theme/gnusocial/images/icons/icon_foaf.gif b/theme/gnusocial/images/icons/icon_foaf.gif
new file mode 100755 (executable)
index 0000000..f8f7844
Binary files /dev/null and b/theme/gnusocial/images/icons/icon_foaf.gif differ
diff --git a/theme/gnusocial/images/icons/icon_processing.gif b/theme/gnusocial/images/icons/icon_processing.gif
new file mode 100755 (executable)
index 0000000..d0bce15
Binary files /dev/null and b/theme/gnusocial/images/icons/icon_processing.gif differ
diff --git a/theme/gnusocial/images/icons/icon_reply.gif b/theme/gnusocial/images/icons/icon_reply.gif
new file mode 100755 (executable)
index 0000000..a4379a7
Binary files /dev/null and b/theme/gnusocial/images/icons/icon_reply.gif differ
diff --git a/theme/gnusocial/images/icons/icon_rss.png b/theme/gnusocial/images/icons/icon_rss.png
new file mode 100755 (executable)
index 0000000..0ccd1ce
Binary files /dev/null and b/theme/gnusocial/images/icons/icon_rss.png differ
diff --git a/theme/gnusocial/images/icons/icon_trash.gif b/theme/gnusocial/images/icons/icon_trash.gif
new file mode 100755 (executable)
index 0000000..916a332
Binary files /dev/null and b/theme/gnusocial/images/icons/icon_trash.gif differ
diff --git a/theme/gnusocial/images/icons/icon_vcard.gif b/theme/gnusocial/images/icons/icon_vcard.gif
new file mode 100755 (executable)
index 0000000..6d52947
Binary files /dev/null and b/theme/gnusocial/images/icons/icon_vcard.gif differ
diff --git a/theme/gnusocial/images/icons/twotone/green/arrow-left.gif b/theme/gnusocial/images/icons/twotone/green/arrow-left.gif
new file mode 100755 (executable)
index 0000000..afed190
Binary files /dev/null and b/theme/gnusocial/images/icons/twotone/green/arrow-left.gif differ
diff --git a/theme/gnusocial/images/icons/twotone/green/arrow-right.gif b/theme/gnusocial/images/icons/twotone/green/arrow-right.gif
new file mode 100755 (executable)
index 0000000..ee1707e
Binary files /dev/null and b/theme/gnusocial/images/icons/twotone/green/arrow-right.gif differ
diff --git a/theme/gnusocial/images/icons/twotone/green/edit.gif b/theme/gnusocial/images/icons/twotone/green/edit.gif
new file mode 100755 (executable)
index 0000000..c746aca
Binary files /dev/null and b/theme/gnusocial/images/icons/twotone/green/edit.gif differ
diff --git a/theme/gnusocial/images/icons/twotone/green/mail.gif b/theme/gnusocial/images/icons/twotone/green/mail.gif
new file mode 100755 (executable)
index 0000000..1084c86
Binary files /dev/null and b/theme/gnusocial/images/icons/twotone/green/mail.gif differ
diff --git a/theme/gnusocial/images/icons/twotone/green/news.gif b/theme/gnusocial/images/icons/twotone/green/news.gif
new file mode 100755 (executable)
index 0000000..712c685
Binary files /dev/null and b/theme/gnusocial/images/icons/twotone/green/news.gif differ
diff --git a/theme/gnusocial/images/icons/twotone/green/quote.gif b/theme/gnusocial/images/icons/twotone/green/quote.gif
new file mode 100755 (executable)
index 0000000..4ba1f0c
Binary files /dev/null and b/theme/gnusocial/images/icons/twotone/green/quote.gif differ
diff --git a/theme/gnusocial/images/icons/twotone/green/shield.gif b/theme/gnusocial/images/icons/twotone/green/shield.gif
new file mode 100755 (executable)
index 0000000..419d5ee
Binary files /dev/null and b/theme/gnusocial/images/icons/twotone/green/shield.gif differ
diff --git a/theme/gnusocial/images/illustrations/illu_arrow-up-01.gif b/theme/gnusocial/images/illustrations/illu_arrow-up-01.gif
new file mode 100755 (executable)
index 0000000..577be18
Binary files /dev/null and b/theme/gnusocial/images/illustrations/illu_arrow-up-01.gif differ
diff --git a/theme/gnusocial/images/illustrations/illu_clouds-01.gif b/theme/gnusocial/images/illustrations/illu_clouds-01.gif
new file mode 100755 (executable)
index 0000000..41cd622
Binary files /dev/null and b/theme/gnusocial/images/illustrations/illu_clouds-01.gif differ
diff --git a/theme/gnusocial/images/illustrations/illu_jcrop.gif b/theme/gnusocial/images/illustrations/illu_jcrop.gif
new file mode 100755 (executable)
index 0000000..72ea7cc
Binary files /dev/null and b/theme/gnusocial/images/illustrations/illu_jcrop.gif differ
diff --git a/theme/gnusocial/images/illustrations/illu_progress_loading-01.gif b/theme/gnusocial/images/illustrations/illu_progress_loading-01.gif
new file mode 100755 (executable)
index 0000000..82290f4
Binary files /dev/null and b/theme/gnusocial/images/illustrations/illu_progress_loading-01.gif differ
diff --git a/theme/gnusocial/images/illustrations/illu_unicorn-01.png b/theme/gnusocial/images/illustrations/illu_unicorn-01.png
new file mode 100755 (executable)
index 0000000..6cb51b2
Binary files /dev/null and b/theme/gnusocial/images/illustrations/illu_unicorn-01.png differ
diff --git a/theme/gnusocial/index.html b/theme/gnusocial/index.html
new file mode 100644 (file)
index 0000000..c1a3b7a
--- /dev/null
@@ -0,0 +1,213 @@
+<!DOCTYPE html>
+       <head>
+               <title>Public timeline - Lorraine Lee &mdash; 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>
diff --git a/theme/gnusocial/logo.png b/theme/gnusocial/logo.png
new file mode 100755 (executable)
index 0000000..cf18391
Binary files /dev/null and b/theme/gnusocial/logo.png differ
diff --git a/tpl/social.php b/tpl/social.php
new file mode 100644 (file)
index 0000000..a76e008
--- /dev/null
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title><?php echo section('title'); ?> &mdash; 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> &mdash; 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>