]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
plugins onAutoload now only overloads if necessary (extlibs etc.)
authorMikael Nordfeldth <mmn@hethane.se>
Wed, 28 Aug 2013 13:54:26 +0000 (15:54 +0200)
committerMikael Nordfeldth <mmn@hethane.se>
Wed, 28 Aug 2013 14:10:30 +0000 (16:10 +0200)
lib/plugin.php now has a parent onAutoload function that finds most common
files that are used in plugins (actions, dataobjects, forms, libs etc.) if
they are put in the standardised directories ('actions', 'classes', 'forms',
'lib' and perhaps some others in the future).

494 files changed:
lib/plugin.php
plugins/AccountManager/AccountManagementControlDocumentAction.php [deleted file]
plugins/AccountManager/AccountManagementSessionStatusAction.php [deleted file]
plugins/AccountManager/AccountManagerPlugin.php
plugins/AccountManager/actions/accountmanagementcontroldocument.php [new file with mode: 0644]
plugins/AccountManager/actions/accountmanagementsessionstatus.php [new file with mode: 0644]
plugins/Activity/ActivityPlugin.php
plugins/Activity/followlistitem.php [deleted file]
plugins/Activity/joinlistitem.php [deleted file]
plugins/Activity/leavelistitem.php [deleted file]
plugins/Activity/lib/followlistitem.php [new file with mode: 0644]
plugins/Activity/lib/joinlistitem.php [new file with mode: 0644]
plugins/Activity/lib/leavelistitem.php [new file with mode: 0644]
plugins/Activity/lib/systemlistitem.php [new file with mode: 0644]
plugins/Activity/lib/unfollowlistitem.php [new file with mode: 0644]
plugins/Activity/systemlistitem.php [deleted file]
plugins/Activity/unfollowlistitem.php [deleted file]
plugins/ActivitySpam/ActivitySpamPlugin.php
plugins/ActivitySpam/Spam_score.php [deleted file]
plugins/ActivitySpam/actions/spam.php [new file with mode: 0644]
plugins/ActivitySpam/actions/train.php [new file with mode: 0644]
plugins/ActivitySpam/classes/spam_score.php [new file with mode: 0644]
plugins/ActivitySpam/forms/trainham.php [new file with mode: 0644]
plugins/ActivitySpam/forms/trainspam.php [new file with mode: 0644]
plugins/ActivitySpam/lib/spamfilter.php [new file with mode: 0644]
plugins/ActivitySpam/lib/spamnoticestream.php [new file with mode: 0644]
plugins/ActivitySpam/spam.php [deleted file]
plugins/ActivitySpam/spamfilter.php [deleted file]
plugins/ActivitySpam/spamnoticestream.php [deleted file]
plugins/ActivitySpam/train.php [deleted file]
plugins/ActivitySpam/trainhamform.php [deleted file]
plugins/ActivitySpam/trainspamform.php [deleted file]
plugins/Adsense/AdsensePlugin.php
plugins/Adsense/actions/adsenseadminpanel.php [new file with mode: 0644]
plugins/Adsense/adsenseadminpanel.php [deleted file]
plugins/Aim/AimPlugin.php
plugins/Aim/Fake_Aim.php [deleted file]
plugins/Aim/aimmanager.php [deleted file]
plugins/Aim/classes/Fake_Aim.php [new file with mode: 0644]
plugins/Aim/lib/aimmanager.php [new file with mode: 0644]
plugins/AnonymousFave/AnonymousFavePlugin.php
plugins/AnonymousFave/Fave_tally.php [deleted file]
plugins/AnonymousFave/actions/anondisfavor.php [new file with mode: 0644]
plugins/AnonymousFave/actions/anonfavor.php [new file with mode: 0644]
plugins/AnonymousFave/anondisfavor.php [deleted file]
plugins/AnonymousFave/anondisfavorform.php [deleted file]
plugins/AnonymousFave/anonfavor.php [deleted file]
plugins/AnonymousFave/anonfavorform.php [deleted file]
plugins/AnonymousFave/classes/Fave_tally.php [new file with mode: 0644]
plugins/AnonymousFave/forms/anondisfavor.php [new file with mode: 0644]
plugins/AnonymousFave/forms/anonfavor.php [new file with mode: 0644]
plugins/Autocomplete/AutocompletePlugin.php
plugins/Autocomplete/actions/autocomplete.php [new file with mode: 0644]
plugins/Autocomplete/autocomplete.php [deleted file]
plugins/BitlyUrl/BitlyUrlPlugin.php
plugins/BitlyUrl/actions/bitlyadminpanel.php [new file with mode: 0644]
plugins/BitlyUrl/bitlyadminpanelaction.php [deleted file]
plugins/Blacklist/BlacklistPlugin.php
plugins/Blacklist/Homepage_blacklist.php [deleted file]
plugins/Blacklist/Nickname_blacklist.php [deleted file]
plugins/Blacklist/actions/blacklistadminpanel.php [new file with mode: 0644]
plugins/Blacklist/blacklistadminpanel.php [deleted file]
plugins/Blacklist/classes/Homepage_blacklist.php [new file with mode: 0644]
plugins/Blacklist/classes/Nickname_blacklist.php [new file with mode: 0644]
plugins/Blog/BlogPlugin.php
plugins/Blog/Blog_entry.php [deleted file]
plugins/Blog/actions/newblogentry.php [new file with mode: 0644]
plugins/Blog/actions/showblogentry.php [new file with mode: 0644]
plugins/Blog/blogentryform.php [deleted file]
plugins/Blog/blogentrylistitem.php [deleted file]
plugins/Blog/classes/Blog_entry.php [new file with mode: 0644]
plugins/Blog/forms/blogentry.php [new file with mode: 0644]
plugins/Blog/lib/blogentrylistitem.php [new file with mode: 0644]
plugins/Blog/newblogentry.php [deleted file]
plugins/Blog/showblogentry.php [deleted file]
plugins/Bookmark/Bookmark.php [deleted file]
plugins/Bookmark/BookmarkPlugin.php
plugins/Bookmark/actions/apitimelinebookmarks.php [new file with mode: 0644]
plugins/Bookmark/actions/bookmarkforurl.php [new file with mode: 0644]
plugins/Bookmark/actions/bookmarkpopup.php [new file with mode: 0644]
plugins/Bookmark/actions/bookmarks.php [new file with mode: 0644]
plugins/Bookmark/actions/bookmarksrss.php [new file with mode: 0644]
plugins/Bookmark/actions/importdelicious.php [new file with mode: 0644]
plugins/Bookmark/actions/newbookmark.php [new file with mode: 0644]
plugins/Bookmark/actions/noticebyurl.php [new file with mode: 0644]
plugins/Bookmark/actions/showbookmark.php [new file with mode: 0644]
plugins/Bookmark/apitimelinebookmarks.php [deleted file]
plugins/Bookmark/bookmarkform.php [deleted file]
plugins/Bookmark/bookmarkforurl.php [deleted file]
plugins/Bookmark/bookmarklistitem.php [deleted file]
plugins/Bookmark/bookmarkpopup.php [deleted file]
plugins/Bookmark/bookmarks.php [deleted file]
plugins/Bookmark/bookmarksnoticestream.php [deleted file]
plugins/Bookmark/bookmarksrss.php [deleted file]
plugins/Bookmark/classes/Bookmark.php [new file with mode: 0644]
plugins/Bookmark/deliciousbackupimporter.php [deleted file]
plugins/Bookmark/deliciousbookmarkimporter.php [deleted file]
plugins/Bookmark/forms/bookmark.php [new file with mode: 0644]
plugins/Bookmark/forms/initialbookmark.php [new file with mode: 0644]
plugins/Bookmark/importbookmarks.php [deleted file]
plugins/Bookmark/importdelicious.php [deleted file]
plugins/Bookmark/initialbookmarkform.php [deleted file]
plugins/Bookmark/lib/bookmarklistitem.php [new file with mode: 0644]
plugins/Bookmark/lib/bookmarksnoticestream.php [new file with mode: 0644]
plugins/Bookmark/lib/deliciousbackupimporter.php [new file with mode: 0644]
plugins/Bookmark/lib/deliciousbookmarkimporter.php [new file with mode: 0644]
plugins/Bookmark/newbookmark.php [deleted file]
plugins/Bookmark/noticebyurl.php [deleted file]
plugins/Bookmark/scripts/importbookmarks.php [new file with mode: 0644]
plugins/Bookmark/showbookmark.php [deleted file]
plugins/CasAuthentication/CasAuthenticationPlugin.php
plugins/CasAuthentication/actions/caslogin.php [new file with mode: 0644]
plugins/CasAuthentication/caslogin.php [deleted file]
plugins/ClientSideShorten/ClientSideShortenPlugin.php
plugins/ClientSideShorten/actions/shorten.php [new file with mode: 0644]
plugins/ClientSideShorten/shorten.php [deleted file]
plugins/Directory/DirectoryPlugin.php
plugins/DomainStatusNetwork/DomainStatusNetworkPlugin.php
plugins/DomainStatusNetwork/actions/globalapi.php [new file with mode: 0644]
plugins/DomainStatusNetwork/lib/globalapiaction.php [deleted file]
plugins/DomainWhitelist/DomainWhitelistPlugin.php
plugins/DomainWhitelist/forms/whitelistinvite.php [new file with mode: 0644]
plugins/DomainWhitelist/lib/whitelistinviteform.php [deleted file]
plugins/EmailRegistration/EmailRegistrationPlugin.php
plugins/EmailRegistration/actions/emailregister.php [new file with mode: 0644]
plugins/EmailRegistration/confirmregistrationform.php [deleted file]
plugins/EmailRegistration/emailregister.php [deleted file]
plugins/EmailRegistration/emailregistrationform.php [deleted file]
plugins/EmailRegistration/forms/confirmregistration.php [new file with mode: 0644]
plugins/EmailRegistration/forms/emailregistration.php [new file with mode: 0644]
plugins/EmailReminder/EmailReminderPlugin.php
plugins/EmailSummary/EmailSummaryPlugin.php
plugins/EmailSummary/Email_summary_status.php [deleted file]
plugins/EmailSummary/classes/Email_summary_status.php [new file with mode: 0644]
plugins/EmailSummary/lib/siteemailsummaryhandler.php [new file with mode: 0644]
plugins/EmailSummary/lib/useremailsummaryhandler.php [new file with mode: 0644]
plugins/EmailSummary/scripts/sendemailsummary.php [new file with mode: 0644]
plugins/EmailSummary/sendemailsummary.php [deleted file]
plugins/EmailSummary/siteemailsummaryhandler.php [deleted file]
plugins/EmailSummary/useremailsummaryhandler.php [deleted file]
plugins/Event/EventPlugin.php
plugins/Event/Happening.php [deleted file]
plugins/Event/RSVP.php [deleted file]
plugins/Event/actions/cancelrsvp.php [new file with mode: 0644]
plugins/Event/actions/newevent.php [new file with mode: 0644]
plugins/Event/actions/newrsvp.php [new file with mode: 0644]
plugins/Event/actions/showevent.php [new file with mode: 0644]
plugins/Event/actions/showrsvp.php [new file with mode: 0644]
plugins/Event/actions/timelist.php [new file with mode: 0644]
plugins/Event/cancelrsvp.php [deleted file]
plugins/Event/cancelrsvpform.php [deleted file]
plugins/Event/classes/Happening.php [new file with mode: 0644]
plugins/Event/classes/RSVP.php [new file with mode: 0644]
plugins/Event/eventform.php [deleted file]
plugins/Event/eventlistitem.php [deleted file]
plugins/Event/eventtimelist.php [deleted file]
plugins/Event/forms/cancelrsvp.php [new file with mode: 0644]
plugins/Event/forms/event.php [new file with mode: 0644]
plugins/Event/forms/rsvp.php [new file with mode: 0644]
plugins/Event/lib/eventlistitem.php [new file with mode: 0644]
plugins/Event/lib/eventtimelist.php [new file with mode: 0644]
plugins/Event/lib/rsvplistitem.php [new file with mode: 0644]
plugins/Event/newevent.php [deleted file]
plugins/Event/newrsvp.php [deleted file]
plugins/Event/rsvpform.php [deleted file]
plugins/Event/rsvplistitem.php [deleted file]
plugins/Event/showevent.php [deleted file]
plugins/Event/showrsvp.php [deleted file]
plugins/Event/timelist.php [deleted file]
plugins/ExtendedProfile/ExtendedProfilePlugin.php
plugins/FacebookBridge/FacebookBridgePlugin.php
plugins/FollowEveryone/FollowEveryonePlugin.php
plugins/FollowEveryone/User_followeveryone_prefs.php [deleted file]
plugins/FollowEveryone/classes/User_followeveryone_prefs.php [new file with mode: 0644]
plugins/GNUsocialPhoto/GNUsocialPhotoPlugin.php
plugins/GNUsocialPhoto/Photo.php [deleted file]
plugins/GNUsocialPhoto/actions/newphoto.php [new file with mode: 0644]
plugins/GNUsocialPhoto/actions/showphoto.php [new file with mode: 0644]
plugins/GNUsocialPhoto/classes/Photo.php [new file with mode: 0644]
plugins/GNUsocialPhoto/forms/newphoto.php [new file with mode: 0644]
plugins/GNUsocialPhoto/newphoto.php [deleted file]
plugins/GNUsocialPhoto/newphotoform.php [deleted file]
plugins/GNUsocialPhoto/showphoto.php [deleted file]
plugins/GNUsocialPhotos/GNUsocialPhotosPlugin.php
plugins/GNUsocialPhotos/lib/gnusocialphotonav.php [new file with mode: 0644]
plugins/GNUsocialPhotos/lib/gnusocialphototemp.php [new file with mode: 0644]
plugins/GNUsocialPhotos/lib/photonav.php [deleted file]
plugins/GNUsocialPhotos/lib/tempphoto.php [deleted file]
plugins/GNUsocialProfileExtensions/GNUsocialProfileExtensionsPlugin.php
plugins/GNUsocialVideo/GNUsocialVideoPlugin.php
plugins/GNUsocialVideo/Video.php [deleted file]
plugins/GNUsocialVideo/actions/showvideo.php [new file with mode: 0644]
plugins/GNUsocialVideo/classes/Video.php [new file with mode: 0644]
plugins/GNUsocialVideo/forms/video.php [new file with mode: 0644]
plugins/GNUsocialVideo/showvideo.php [deleted file]
plugins/GNUsocialVideo/videoform.php [deleted file]
plugins/GroupFavorited/GroupFavoritedPlugin.php
plugins/GroupFavorited/actions/groupfavorited.php [new file with mode: 0644]
plugins/GroupFavorited/groupfavoritedaction.php [deleted file]
plugins/GroupPrivateMessage/GroupPrivateMessagePlugin.php
plugins/GroupPrivateMessage/Group_message.php [deleted file]
plugins/GroupPrivateMessage/Group_message_profile.php [deleted file]
plugins/GroupPrivateMessage/Group_privacy_settings.php [deleted file]
plugins/GroupPrivateMessage/actions/groupinbox.php [new file with mode: 0644]
plugins/GroupPrivateMessage/actions/newgroupmessage.php [new file with mode: 0644]
plugins/GroupPrivateMessage/actions/showgroupmessage.php [new file with mode: 0644]
plugins/GroupPrivateMessage/classes/Group_message.php [new file with mode: 0644]
plugins/GroupPrivateMessage/classes/Group_message_profile.php [new file with mode: 0644]
plugins/GroupPrivateMessage/classes/Group_privacy_settings.php [new file with mode: 0644]
plugins/GroupPrivateMessage/forms/groupmessage.php [new file with mode: 0644]
plugins/GroupPrivateMessage/groupinbox.php [deleted file]
plugins/GroupPrivateMessage/groupmessagecommand.php [deleted file]
plugins/GroupPrivateMessage/groupmessageform.php [deleted file]
plugins/GroupPrivateMessage/groupmessagelist.php [deleted file]
plugins/GroupPrivateMessage/groupmessagelistitem.php [deleted file]
plugins/GroupPrivateMessage/lib/groupmessagecommand.php [new file with mode: 0644]
plugins/GroupPrivateMessage/lib/groupmessagelist.php [new file with mode: 0644]
plugins/GroupPrivateMessage/lib/groupmessagelistitem.php [new file with mode: 0644]
plugins/GroupPrivateMessage/newgroupmessage.php [deleted file]
plugins/GroupPrivateMessage/showgroupmessage.php [deleted file]
plugins/Imap/ImapPlugin.php
plugins/Imap/imapmailhandler.php [deleted file]
plugins/Imap/imapmanager.php [deleted file]
plugins/Imap/lib/imapmailhandler.php [new file with mode: 0644]
plugins/Imap/lib/imapmanager.php [new file with mode: 0644]
plugins/Irc/ChannelResponseChannel.php [deleted file]
plugins/Irc/Fake_Irc.php [deleted file]
plugins/Irc/IrcPlugin.php
plugins/Irc/Irc_waiting_message.php [deleted file]
plugins/Irc/classes/Irc_waiting_message.php [new file with mode: 0644]
plugins/Irc/ircmanager.php [deleted file]
plugins/Irc/lib/channelresponsechannel.php [new file with mode: 0644]
plugins/Irc/lib/fake_irc.php [new file with mode: 0644]
plugins/Irc/lib/ircmanager.php [new file with mode: 0644]
plugins/LdapAuthentication/LdapAuthenticationPlugin.php
plugins/LdapAuthorization/LdapAuthorizationPlugin.php
plugins/LdapCommon/LdapCommon.php
plugins/LinkPreview/LinkPreviewPlugin.php
plugins/LinkPreview/actions/oembedproxy.php [new file with mode: 0644]
plugins/LinkPreview/oembedproxyaction.php [deleted file]
plugins/Mapstraction/MapstractionPlugin.php
plugins/Mapstraction/actions/allmap.php [new file with mode: 0644]
plugins/Mapstraction/actions/map.php [new file with mode: 0644]
plugins/Mapstraction/actions/usermap.php [new file with mode: 0644]
plugins/Mapstraction/allmap.php [deleted file]
plugins/Mapstraction/map.php [deleted file]
plugins/Mapstraction/usermap.php [deleted file]
plugins/Minify/MinifyPlugin.php
plugins/Minify/actions/minify.php [new file with mode: 0644]
plugins/Minify/minify.php [deleted file]
plugins/ModLog/ModLog.php [deleted file]
plugins/ModLog/ModLogPlugin.php
plugins/ModLog/classes/ModLog.php [new file with mode: 0644]
plugins/ModPlus/ModPlusPlugin.php
plugins/ModPlus/actions/remoteprofile.php [new file with mode: 0644]
plugins/ModPlus/remoteprofileaction.php [deleted file]
plugins/Msn/MsnPlugin.php
plugins/Msn/classes/msn_waiting_message.php [new file with mode: 0644]
plugins/Msn/lib/msnmanager.php [new file with mode: 0644]
plugins/Msn/msn_waiting_message.php [deleted file]
plugins/Msn/msnmanager.php [deleted file]
plugins/NoticeTitle/NoticeTitlePlugin.php
plugins/NoticeTitle/Notice_title.php [deleted file]
plugins/NoticeTitle/classes/Notice_title.php [new file with mode: 0644]
plugins/OMB/OMBPlugin.php
plugins/OMB/lib/omb.php
plugins/OMB/lib/omboauthdatastore.php [new file with mode: 0644]
plugins/OMB/lib/omboauthstore.php [deleted file]
plugins/OStatus/OStatusPlugin.php
plugins/OStatus/actions/salmon.php [new file with mode: 0644]
plugins/OStatus/actions/xrd.php [new file with mode: 0644]
plugins/OStatus/lib/salmonaction.php [deleted file]
plugins/OStatus/lib/xrdaction.php [deleted file]
plugins/OfflineBackup/OfflineBackupPlugin.php
plugins/OfflineBackup/actions/offlinebackup.php [new file with mode: 0644]
plugins/OfflineBackup/lib/offlinebackupqueuehandler.php [new file with mode: 0644]
plugins/OfflineBackup/offlinebackup.php [deleted file]
plugins/OfflineBackup/offlinebackupqueuehandler.php [deleted file]
plugins/OpenID/OpenIDPlugin.php
plugins/OpenID/User_openid.php [deleted file]
plugins/OpenID/User_openid_prefs.php [deleted file]
plugins/OpenID/User_openid_trustroot.php [deleted file]
plugins/OpenID/actions/finishaddopenid.php [new file with mode: 0644]
plugins/OpenID/actions/finishopenidlogin.php [new file with mode: 0644]
plugins/OpenID/actions/openidadminpanel.php [new file with mode: 0644]
plugins/OpenID/actions/openidlogin.php [new file with mode: 0644]
plugins/OpenID/actions/openidserver.php [new file with mode: 0644]
plugins/OpenID/actions/openidsettings.php [new file with mode: 0644]
plugins/OpenID/actions/openidtrust.php [new file with mode: 0644]
plugins/OpenID/classes/User_openid.php [new file with mode: 0644]
plugins/OpenID/classes/User_openid_prefs.php [new file with mode: 0644]
plugins/OpenID/classes/User_openid_trustroot.php [new file with mode: 0644]
plugins/OpenID/finishaddopenid.php [deleted file]
plugins/OpenID/finishopenidlogin.php [deleted file]
plugins/OpenID/openidadminpanel.php [deleted file]
plugins/OpenID/openidlogin.php [deleted file]
plugins/OpenID/openidserver.php [deleted file]
plugins/OpenID/openidsettings.php [deleted file]
plugins/OpenID/openidtrust.php [deleted file]
plugins/OpenX/OpenXPlugin.php
plugins/OpenX/actions/openxadminpanel.php [new file with mode: 0644]
plugins/OpenX/openxadminpanel.php [deleted file]
plugins/Poll/Poll.php [deleted file]
plugins/Poll/PollPlugin.php
plugins/Poll/Poll_response.php [deleted file]
plugins/Poll/User_poll_prefs.php [deleted file]
plugins/Poll/actions/newpoll.php [new file with mode: 0644]
plugins/Poll/actions/pollsettings.php [new file with mode: 0644]
plugins/Poll/actions/respondpoll.php [new file with mode: 0644]
plugins/Poll/actions/showpoll.php [new file with mode: 0644]
plugins/Poll/classes/Poll.php [new file with mode: 0644]
plugins/Poll/classes/Poll_response.php [new file with mode: 0644]
plugins/Poll/classes/User_poll_prefs.php [new file with mode: 0644]
plugins/Poll/forms/newpoll.php [new file with mode: 0644]
plugins/Poll/forms/pollresponse.php [new file with mode: 0644]
plugins/Poll/forms/pollresult.php [new file with mode: 0644]
plugins/Poll/newpoll.php [deleted file]
plugins/Poll/newpollform.php [deleted file]
plugins/Poll/pollresponseform.php [deleted file]
plugins/Poll/pollresultform.php [deleted file]
plugins/Poll/pollsettings.php [deleted file]
plugins/Poll/respondpoll.php [deleted file]
plugins/Poll/showpoll.php [deleted file]
plugins/QnA/QnAPlugin.php
plugins/QnA/forms/qnanewanswer.php [new file with mode: 0644]
plugins/QnA/forms/qnanewquestion.php [new file with mode: 0644]
plugins/QnA/forms/qnareviseanswer.php [new file with mode: 0644]
plugins/QnA/forms/qnashowanswer.php [new file with mode: 0644]
plugins/QnA/forms/qnashowquestion.php [new file with mode: 0644]
plugins/QnA/forms/qnavote.php [new file with mode: 0644]
plugins/QnA/lib/qnanewanswerform.php [deleted file]
plugins/QnA/lib/qnanewquestionform.php [deleted file]
plugins/QnA/lib/qnareviseanswerform.php [deleted file]
plugins/QnA/lib/qnashowanswerform.php [deleted file]
plugins/QnA/lib/qnashowquestionform.php [deleted file]
plugins/QnA/lib/qnavoteform.php [deleted file]
plugins/RSSCloud/LoggingAggregator.php [deleted file]
plugins/RSSCloud/RSSCloudNotifier.php [deleted file]
plugins/RSSCloud/RSSCloudPlugin.php
plugins/RSSCloud/RSSCloudQueueHandler.php [deleted file]
plugins/RSSCloud/RSSCloudRequestNotify.php [deleted file]
plugins/RSSCloud/RSSCloudSubscription.php [deleted file]
plugins/RSSCloud/actions/loggingaggregator.php [new file with mode: 0644]
plugins/RSSCloud/actions/rsscloudrequestnotify.php [new file with mode: 0644]
plugins/RSSCloud/classes/RSSCloudSubscription.php [new file with mode: 0644]
plugins/RSSCloud/lib/rsscloudnotifier.php [new file with mode: 0644]
plugins/RSSCloud/lib/rsscloudqueuehandler.php [new file with mode: 0644]
plugins/Realtime/RealtimePlugin.php
plugins/Realtime/Realtime_channel.php [deleted file]
plugins/Realtime/actions/closechannel.php [new file with mode: 0644]
plugins/Realtime/actions/keepalivechannel.php [new file with mode: 0644]
plugins/Realtime/classes/Realtime_channel.php [new file with mode: 0644]
plugins/Realtime/cleanupchannels.php [deleted file]
plugins/Realtime/closechannel.php [deleted file]
plugins/Realtime/keepalivechannel.php [deleted file]
plugins/Realtime/scripts/cleanupchannels.php [new file with mode: 0644]
plugins/RegisterThrottle/RegisterThrottlePlugin.php
plugins/RegisterThrottle/Registration_ip.php [deleted file]
plugins/RegisterThrottle/classes/Registration_ip.php [new file with mode: 0644]
plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php
plugins/RequireValidatedEmail/actions/confirmfirstemail.php [new file with mode: 0644]
plugins/RequireValidatedEmail/confirmfirstemail.php [deleted file]
plugins/RequireValidatedEmail/registerbyemail.php [deleted file]
plugins/RequireValidatedEmail/scripts/registerbyemail.php [new file with mode: 0644]
plugins/Sample/SamplePlugin.php
plugins/Sample/User_greeting_count.php [deleted file]
plugins/Sample/actions/hello.php [new file with mode: 0644]
plugins/Sample/classes/User_greeting_count.php [new file with mode: 0644]
plugins/Sample/hello.php [deleted file]
plugins/SearchSub/SearchSub.php [deleted file]
plugins/SearchSub/SearchSubPlugin.php
plugins/SearchSub/actions/searchsub.php [new file with mode: 0644]
plugins/SearchSub/actions/searchsubs.php [new file with mode: 0644]
plugins/SearchSub/actions/searchunsub.php [new file with mode: 0644]
plugins/SearchSub/classes/SearchSub.php [new file with mode: 0644]
plugins/SearchSub/forms/searchsub.php [new file with mode: 0644]
plugins/SearchSub/forms/searchunsub.php [new file with mode: 0644]
plugins/SearchSub/lib/searchsubmenu.php [new file with mode: 0644]
plugins/SearchSub/lib/searchsubtrackcommand.php [new file with mode: 0644]
plugins/SearchSub/lib/searchsubtrackingcommand.php [new file with mode: 0644]
plugins/SearchSub/lib/searchsubtrackoffcommand.php [new file with mode: 0644]
plugins/SearchSub/lib/searchsubuntrackcommand.php [new file with mode: 0644]
plugins/SearchSub/searchsubaction.php [deleted file]
plugins/SearchSub/searchsubform.php [deleted file]
plugins/SearchSub/searchsubmenu.php [deleted file]
plugins/SearchSub/searchsubsaction.php [deleted file]
plugins/SearchSub/searchsubtrackcommand.php [deleted file]
plugins/SearchSub/searchsubtrackingcommand.php [deleted file]
plugins/SearchSub/searchsubtrackoffcommand.php [deleted file]
plugins/SearchSub/searchsubuntrackcommand.php [deleted file]
plugins/SearchSub/searchunsubaction.php [deleted file]
plugins/SearchSub/searchunsubform.php [deleted file]
plugins/SiteNoticeInSidebar/SiteNoticeInSidebarPlugin.php
plugins/SiteNoticeInSidebar/lib/sitenoticesection.php [new file with mode: 0644]
plugins/SiteNoticeInSidebar/sitenoticesection.php [deleted file]
plugins/Sitemap/SitemapPlugin.php
plugins/Sitemap/Sitemap_notice_count.php [deleted file]
plugins/Sitemap/Sitemap_user_count.php [deleted file]
plugins/Sitemap/actions/noticesitemap.php [new file with mode: 0644]
plugins/Sitemap/actions/sitemap.php [new file with mode: 0644]
plugins/Sitemap/actions/sitemapadminpanel.php [new file with mode: 0644]
plugins/Sitemap/actions/sitemapindex.php [new file with mode: 0644]
plugins/Sitemap/actions/usersitemap.php [new file with mode: 0644]
plugins/Sitemap/classes/Sitemap_notice_count.php [new file with mode: 0644]
plugins/Sitemap/classes/Sitemap_user_count.php [new file with mode: 0644]
plugins/Sitemap/noticesitemap.php [deleted file]
plugins/Sitemap/sitemapaction.php [deleted file]
plugins/Sitemap/sitemapadminpanel.php [deleted file]
plugins/Sitemap/sitemapindex.php [deleted file]
plugins/Sitemap/usersitemap.php [deleted file]
plugins/SlicedFavorites/SlicedFavoritesPlugin.php
plugins/SlicedFavorites/actions/favoritedslice.php [new file with mode: 0644]
plugins/SlicedFavorites/favoritedsliceaction.php [deleted file]
plugins/SphinxSearch/SphinxSearchPlugin.php
plugins/SubMirror/SubMirrorPlugin.php
plugins/SubMirror/forms/addmirror.php [new file with mode: 0644]
plugins/SubMirror/forms/addtwitter.php [new file with mode: 0644]
plugins/SubMirror/forms/editmirror.php [new file with mode: 0644]
plugins/SubMirror/lib/addmirrorform.php [deleted file]
plugins/SubMirror/lib/addtwittermirrorform.php [deleted file]
plugins/SubMirror/lib/editmirrorform.php [deleted file]
plugins/TagSub/TagSub.php [deleted file]
plugins/TagSub/TagSubPlugin.php
plugins/TagSub/actions/tagsub.php [new file with mode: 0644]
plugins/TagSub/actions/tagsubs.php [new file with mode: 0644]
plugins/TagSub/actions/tagunsub.php [new file with mode: 0644]
plugins/TagSub/classes/TagSub.php [new file with mode: 0644]
plugins/TagSub/forms/tagsub.php [new file with mode: 0644]
plugins/TagSub/forms/tagunsub.php [new file with mode: 0644]
plugins/TagSub/lib/tagsubmenu.php [new file with mode: 0644]
plugins/TagSub/tagsubaction.php [deleted file]
plugins/TagSub/tagsubform.php [deleted file]
plugins/TagSub/tagsubmenu.php [deleted file]
plugins/TagSub/tagsubsaction.php [deleted file]
plugins/TagSub/tagunsubaction.php [deleted file]
plugins/TagSub/tagunsubform.php [deleted file]
plugins/TwitterBridge/Notice_to_status.php [deleted file]
plugins/TwitterBridge/TwitterBridgePlugin.php
plugins/TwitterBridge/Twitter_synch_status.php [deleted file]
plugins/TwitterBridge/actions/twitteradminpanel.php [new file with mode: 0644]
plugins/TwitterBridge/actions/twitterauthorization.php [new file with mode: 0644]
plugins/TwitterBridge/actions/twitterlogin.php [new file with mode: 0644]
plugins/TwitterBridge/actions/twittersettings.php [new file with mode: 0644]
plugins/TwitterBridge/classes/Notice_to_status.php [new file with mode: 0644]
plugins/TwitterBridge/classes/Twitter_synch_status.php [new file with mode: 0644]
plugins/TwitterBridge/daemons/twitterstatusfetcher.php
plugins/TwitterBridge/jsonstreamreader.php [deleted file]
plugins/TwitterBridge/lib/jsonstreamreader.php [new file with mode: 0644]
plugins/TwitterBridge/lib/tweetinqueuehandler.php [new file with mode: 0644]
plugins/TwitterBridge/lib/twitterimport.php [new file with mode: 0644]
plugins/TwitterBridge/lib/twitteroauthclient.php [new file with mode: 0644]
plugins/TwitterBridge/lib/twitterqueuehandler.php [new file with mode: 0644]
plugins/TwitterBridge/lib/twittersitestream.php [new file with mode: 0644]
plugins/TwitterBridge/lib/twitterstreamreader.php [new file with mode: 0644]
plugins/TwitterBridge/lib/twitteruserstream.php [new file with mode: 0644]
plugins/TwitterBridge/scripts/streamtest.php
plugins/TwitterBridge/tweetinqueuehandler.php [deleted file]
plugins/TwitterBridge/twitteradminpanel.php [deleted file]
plugins/TwitterBridge/twitterauthorization.php [deleted file]
plugins/TwitterBridge/twitterimport.php [deleted file]
plugins/TwitterBridge/twitterlogin.php [deleted file]
plugins/TwitterBridge/twitteroauthclient.php [deleted file]
plugins/TwitterBridge/twitterqueuehandler.php [deleted file]
plugins/TwitterBridge/twittersettings.php [deleted file]
plugins/TwitterBridge/twitterstreamreader.php [deleted file]
plugins/UserFlag/UserFlagPlugin.php
plugins/UserFlag/User_flag_profile.php [deleted file]
plugins/UserFlag/actions/adminprofileflag.php [new file with mode: 0644]
plugins/UserFlag/actions/clearflag.php [new file with mode: 0644]
plugins/UserFlag/actions/flagprofile.php [new file with mode: 0644]
plugins/UserFlag/adminprofileflag.php [deleted file]
plugins/UserFlag/classes/User_flag_profile.php [new file with mode: 0644]
plugins/UserFlag/clearflag.php [deleted file]
plugins/UserFlag/clearflagform.php [deleted file]
plugins/UserFlag/flagprofile.php [deleted file]
plugins/UserFlag/flagprofileform.php [deleted file]
plugins/UserFlag/forms/clearflag.php [new file with mode: 0644]
plugins/UserFlag/forms/flagprofile.php [new file with mode: 0644]
plugins/Xmpp/Queued_XMPP.php [deleted file]
plugins/Xmpp/Sharing_XMPP.php [deleted file]
plugins/Xmpp/XmppPlugin.php
plugins/Xmpp/lib/queued_xmpp.php [new file with mode: 0644]
plugins/Xmpp/lib/sharing_xmpp.php [new file with mode: 0644]
plugins/Xmpp/lib/xmppmanager.php [new file with mode: 0644]
plugins/Xmpp/xmppmanager.php [deleted file]
plugins/YammerImport/YammerImportPlugin.php
plugins/YammerImport/forms/yammerapikey.php [new file with mode: 0644]
plugins/YammerImport/forms/yammerauthinit.php [new file with mode: 0644]
plugins/YammerImport/forms/yammerauthverify.php [new file with mode: 0644]
plugins/YammerImport/forms/yammerprogress.php [new file with mode: 0644]
plugins/YammerImport/lib/yammerapikeyform.php [deleted file]
plugins/YammerImport/lib/yammerauthinitform.php [deleted file]
plugins/YammerImport/lib/yammerauthverifyform.php [deleted file]
plugins/YammerImport/lib/yammerprogressform.php [deleted file]

index a5424e06ca2ca6855349302544ab64939b639576..f97a07fe5ae5cae34da4a9b1e56c9c7fce82bc69 100644 (file)
@@ -79,6 +79,49 @@ class Plugin
         return true;
     }
 
+    /**
+     * Load related modules when needed
+     *
+     * Most non-trivial plugins will require extra modules to do their work. Typically
+     * these include data classes, action classes, widget classes, or external libraries.
+     *
+     * This method receives a class name and loads the PHP file related to that class. By
+     * tradition, action classes typically have files named for the action, all lower-case.
+     * Data classes are in files with the data class name, initial letter capitalized.
+     *
+     * Note that this method will be called for *all* overloaded classes, not just ones
+     * in this plugin! So, make sure to return true by default to let other plugins, and
+     * the core code, get a chance.
+     *
+     * @param string $cls Name of the class to be loaded
+     *
+     * @return boolean hook value; true means continue processing, false means stop.
+     */
+    public function onAutoload($cls) {
+        $cls = basename($cls);
+        $basedir = INSTALLDIR . '/plugins/' . mb_substr(get_called_class(), 0, -6);
+        $file = null;
+
+        if (preg_match('/^(\w+)(Action|Form)$/', $cls, $type)) {
+            $type = array_map('strtolower', $type);
+            $file = "$basedir/{$type[2]}s/{$type[1]}.php";
+        } else {
+            $file = "$basedir/classes/{$cls}.php";
+
+            if (!file_exists($file)) {
+                $type = strtolower($cls);
+                $file = "$basedir/lib/{$type}.php";
+            }
+        }
+
+        if (!is_null($file) && file_exists($file)) {
+            require_once($file);
+            return false;
+        }
+
+        return true;
+    }
+
     /**
      * Checks if this plugin has localization that needs to be set up.
      * Gettext localizations can be called via the _m() helper function.
diff --git a/plugins/AccountManager/AccountManagementControlDocumentAction.php b/plugins/AccountManager/AccountManagementControlDocumentAction.php
deleted file mode 100644 (file)
index 955779b..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Implements the JSON Account Management endpoint
- *
- * 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  AccountManager
- * @package   StatusNet
- * @author    Craig Andrews <candrews@integralblue.com>
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Implements the JSON Account Management endpoint
- *
- * @category AccountManager
- * @package  StatusNet
- * @author   ECraig Andrews <candrews@integralblue.com>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class AccountManagementControlDocumentAction extends Action
-{
-    /**
-     * handle the action
-     *
-     * @param array $args unused.
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-
-        header('Content-Type: application/json; charset=utf-8');
-
-        $amcd = array();
-
-        if(Event::handle('StartAccountManagementControlDocument', array(&$amcd))) {
-
-            $amcd['version'] = 1;
-            $amcd['sessionstatus'] = array(
-                'method' => 'GET',
-                'path' => common_local_url('AccountManagementSessionStatus')
-            );
-            $amcd['auth-methods'] = array(
-                'username-password-form' => array(
-                    'connect' => array(
-                        'method' => 'POST',
-                        'path' => common_local_url('login'),
-                        'params' => array(
-                            'username' => 'nickname',
-                            'password' => 'password'
-                        )
-                    ),
-                    'disconnect' => array(
-                        'method' => 'GET',
-                        'path' => common_local_url('logout')
-                    )
-                )
-            );
-
-            Event::handle('EndAccountManagementControlDocument', array(&$amcd));
-        }
-        
-        print json_encode($amcd);
-
-        return true;
-    }
-
-    function isReadOnly()
-    {
-        return true;
-    }
-}
diff --git a/plugins/AccountManager/AccountManagementSessionStatusAction.php b/plugins/AccountManager/AccountManagementSessionStatusAction.php
deleted file mode 100644 (file)
index 9eeff72..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Implements the session status Account Management endpoint
- *
- * 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  AccountManager
- * @package   StatusNet
- * @author    Craig Andrews <candrews@integralblue.com>
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Implements the session status Account Management endpoint
- *
- * @category AccountManager
- * @package  StatusNet
- * @author   ECraig Andrews <candrews@integralblue.com>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class AccountManagementSessionStatusAction extends Action
-{
-    /**
-     * handle the action
-     *
-     * @param array $args unused.
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-
-        $cur = common_current_user();
-        if(empty($cur)) {
-            print 'none';
-        } else {
-            //TODO it seems " should be escaped in the name and id, but the spec doesn't seem to indicate how to do that
-            print 'active; name="' . $cur->nickname . '"; id="' . $cur->nickname . '"';
-        }
-
-        return true;
-    }
-
-    function isReadOnly()
-    {
-        return true;
-    }
-}
index 3b399af8c8fdf11ac73c9ecf19e52fdca8af624c..a4ddc747ca7ca31fa7af1d5ab51c283e1b783a18 100644 (file)
@@ -40,19 +40,6 @@ class AccountManagerPlugin extends Plugin
         parent::__construct();
     }
 
-    function onAutoload($cls)
-    {
-        switch ($cls)
-        {
-         case 'AccountManagementControlDocumentAction':
-            require_once(INSTALLDIR.'/plugins/AccountManager/AccountManagementControlDocumentAction.php');
-            return false;
-         case 'AccountManagementSessionStatusAction':
-            require_once(INSTALLDIR.'/plugins/AccountManager/AccountManagementSessionStatusAction.php');
-            return false;
-        }
-    }
-
     /**
      * Hook for RouterInitialized event.
      *
diff --git a/plugins/AccountManager/actions/accountmanagementcontroldocument.php b/plugins/AccountManager/actions/accountmanagementcontroldocument.php
new file mode 100644 (file)
index 0000000..955779b
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Implements the JSON Account Management endpoint
+ *
+ * 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  AccountManager
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Implements the JSON Account Management endpoint
+ *
+ * @category AccountManager
+ * @package  StatusNet
+ * @author   ECraig Andrews <candrews@integralblue.com>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class AccountManagementControlDocumentAction extends Action
+{
+    /**
+     * handle the action
+     *
+     * @param array $args unused.
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+
+        header('Content-Type: application/json; charset=utf-8');
+
+        $amcd = array();
+
+        if(Event::handle('StartAccountManagementControlDocument', array(&$amcd))) {
+
+            $amcd['version'] = 1;
+            $amcd['sessionstatus'] = array(
+                'method' => 'GET',
+                'path' => common_local_url('AccountManagementSessionStatus')
+            );
+            $amcd['auth-methods'] = array(
+                'username-password-form' => array(
+                    'connect' => array(
+                        'method' => 'POST',
+                        'path' => common_local_url('login'),
+                        'params' => array(
+                            'username' => 'nickname',
+                            'password' => 'password'
+                        )
+                    ),
+                    'disconnect' => array(
+                        'method' => 'GET',
+                        'path' => common_local_url('logout')
+                    )
+                )
+            );
+
+            Event::handle('EndAccountManagementControlDocument', array(&$amcd));
+        }
+        
+        print json_encode($amcd);
+
+        return true;
+    }
+
+    function isReadOnly()
+    {
+        return true;
+    }
+}
diff --git a/plugins/AccountManager/actions/accountmanagementsessionstatus.php b/plugins/AccountManager/actions/accountmanagementsessionstatus.php
new file mode 100644 (file)
index 0000000..9eeff72
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Implements the session status Account Management endpoint
+ *
+ * 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  AccountManager
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Implements the session status Account Management endpoint
+ *
+ * @category AccountManager
+ * @package  StatusNet
+ * @author   ECraig Andrews <candrews@integralblue.com>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class AccountManagementSessionStatusAction extends Action
+{
+    /**
+     * handle the action
+     *
+     * @param array $args unused.
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+
+        $cur = common_current_user();
+        if(empty($cur)) {
+            print 'none';
+        } else {
+            //TODO it seems " should be escaped in the name and id, but the spec doesn't seem to indicate how to do that
+            print 'active; name="' . $cur->nickname . '"; id="' . $cur->nickname . '"';
+        }
+
+        return true;
+    }
+
+    function isReadOnly()
+    {
+        return true;
+    }
+}
index ff9536028130836378e46beb3dd56c61e4777c75..bffd7a3a10f43314f4f33bd7658b89a81c50f238 100644 (file)
@@ -57,24 +57,6 @@ class ActivityPlugin extends Plugin
     public $StartLike = false;
     public $StopLike = false;
 
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'JoinListItem':
-        case 'LeaveListItem':
-        case 'FollowListItem':
-        case 'UnfollowListItem':
-        case 'SystemListItem':
-            include_once $dir . '/'.strtolower($cls).'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     function onEndSubscribe($subscriber, $other)
     {
         // Only do this if config is enabled
diff --git a/plugins/Activity/followlistitem.php b/plugins/Activity/followlistitem.php
deleted file mode 100644 (file)
index d8b9819..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Title of module
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Cache
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * NoticeListItemAdapter for join activities
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class FollowListItem extends SystemListItem
-{
-}
diff --git a/plugins/Activity/joinlistitem.php b/plugins/Activity/joinlistitem.php
deleted file mode 100644 (file)
index 6e3dbcf..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * List item for when you join a group
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Cache
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * NoticeListItemAdapter for join activities
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class JoinListItem extends SystemListItem
-{
-    function showContent()
-    {
-        $notice = $this->nli->notice;
-        $out    = $this->nli->out;
-
-        $mem = Group_member::getKV('uri', $notice->uri);
-
-        if (!empty($mem)) {
-            $out->elementStart('div', 'join-activity');
-            $profile = $mem->getMember();
-            $group = $mem->getGroup();
-
-            // TRANS: Text for "joined list" item in activity plugin.
-            // TRANS: %1$s is a profile URL, %2$s is a profile name,
-            // TRANS: %3$s is a group home URL, %4$s is a group name.
-            $out->raw(sprintf(_m('<a href="%1$s">%2$s</a> joined the group <a href="%3$s">%4$s</a>.'),
-                                $profile->profileurl,
-                                $profile->getBestName(),
-                                $group->homeUrl(),
-                                $group->getBestName()));
-
-            $out->elementEnd('div');
-        } else {
-            parent::showContent();
-        }
-    }
-}
diff --git a/plugins/Activity/leavelistitem.php b/plugins/Activity/leavelistitem.php
deleted file mode 100644 (file)
index 8448d70..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * List item for when you leave a group
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Cache
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * NoticeListItemAdapter for leave activities
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class LeaveListItem extends SystemListItem
-{
-}
diff --git a/plugins/Activity/lib/followlistitem.php b/plugins/Activity/lib/followlistitem.php
new file mode 100644 (file)
index 0000000..d8b9819
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Title of module
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Cache
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * NoticeListItemAdapter for join activities
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class FollowListItem extends SystemListItem
+{
+}
diff --git a/plugins/Activity/lib/joinlistitem.php b/plugins/Activity/lib/joinlistitem.php
new file mode 100644 (file)
index 0000000..6e3dbcf
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * List item for when you join a group
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Cache
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * NoticeListItemAdapter for join activities
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class JoinListItem extends SystemListItem
+{
+    function showContent()
+    {
+        $notice = $this->nli->notice;
+        $out    = $this->nli->out;
+
+        $mem = Group_member::getKV('uri', $notice->uri);
+
+        if (!empty($mem)) {
+            $out->elementStart('div', 'join-activity');
+            $profile = $mem->getMember();
+            $group = $mem->getGroup();
+
+            // TRANS: Text for "joined list" item in activity plugin.
+            // TRANS: %1$s is a profile URL, %2$s is a profile name,
+            // TRANS: %3$s is a group home URL, %4$s is a group name.
+            $out->raw(sprintf(_m('<a href="%1$s">%2$s</a> joined the group <a href="%3$s">%4$s</a>.'),
+                                $profile->profileurl,
+                                $profile->getBestName(),
+                                $group->homeUrl(),
+                                $group->getBestName()));
+
+            $out->elementEnd('div');
+        } else {
+            parent::showContent();
+        }
+    }
+}
diff --git a/plugins/Activity/lib/leavelistitem.php b/plugins/Activity/lib/leavelistitem.php
new file mode 100644 (file)
index 0000000..8448d70
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * List item for when you leave a group
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Cache
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * NoticeListItemAdapter for leave activities
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class LeaveListItem extends SystemListItem
+{
+}
diff --git a/plugins/Activity/lib/systemlistitem.php b/plugins/Activity/lib/systemlistitem.php
new file mode 100644 (file)
index 0000000..c6753e5
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Superclass for system event items
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Activity
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * NoticeListItemAdapter for system activities
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class SystemListItem extends NoticeListItemAdapter
+{
+    /**
+     * Show the activity
+     *
+     * @return void
+     */
+    function showNotice()
+    {
+        $out = $this->nli->out;
+        $out->elementStart('div', 'entry-title');
+        $this->showContent();
+        $out->elementEnd('div');
+    }
+
+    function showContent()
+    {
+        $notice = $this->nli->notice;
+        $out    = $this->nli->out;
+
+        // FIXME: get the actual data on the leave
+
+        $out->elementStart('div', 'system-activity');
+
+        $out->raw($notice->rendered);
+
+        $out->elementEnd('div');
+    }
+
+    function showNoticeOptions()
+    {
+        if (Event::handle('StartShowNoticeOptions', array($this))) {
+            $user = common_current_user();
+            if (!empty($user)) {
+                $this->nli->out->elementStart('div', 'notice-options');
+                $this->showFaveForm();
+                $this->showReplyLink();
+                $this->nli->out->elementEnd('div');
+            }
+            Event::handle('EndShowNoticeOptions', array($this));
+        }
+    }
+}
diff --git a/plugins/Activity/lib/unfollowlistitem.php b/plugins/Activity/lib/unfollowlistitem.php
new file mode 100644 (file)
index 0000000..0ecb8be
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Unfollow list item
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Activity
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * NoticeListItemAdapter for join activities
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class UnfollowListItem extends SystemListItem
+{
+    /**
+     * Show the join activity
+     *
+     * @return void
+     */
+    function showNotice()
+    {
+        $out = $this->nli->out;
+        $out->elementStart('div', 'entry-title');
+        $this->showContent();
+        $out->elementEnd('div');
+    }
+
+    function showContent()
+    {
+        $notice = $this->nli->notice;
+        $out    = $this->nli->out;
+
+               // FIXME: get the actual data on the leave
+
+        $out->elementStart('div', 'unfollow-activity');
+
+        $out->raw($notice->rendered);
+
+        $out->elementEnd('div');
+    }
+}
diff --git a/plugins/Activity/systemlistitem.php b/plugins/Activity/systemlistitem.php
deleted file mode 100644 (file)
index c6753e5..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Superclass for system event items
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Activity
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * NoticeListItemAdapter for system activities
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class SystemListItem extends NoticeListItemAdapter
-{
-    /**
-     * Show the activity
-     *
-     * @return void
-     */
-    function showNotice()
-    {
-        $out = $this->nli->out;
-        $out->elementStart('div', 'entry-title');
-        $this->showContent();
-        $out->elementEnd('div');
-    }
-
-    function showContent()
-    {
-        $notice = $this->nli->notice;
-        $out    = $this->nli->out;
-
-        // FIXME: get the actual data on the leave
-
-        $out->elementStart('div', 'system-activity');
-
-        $out->raw($notice->rendered);
-
-        $out->elementEnd('div');
-    }
-
-    function showNoticeOptions()
-    {
-        if (Event::handle('StartShowNoticeOptions', array($this))) {
-            $user = common_current_user();
-            if (!empty($user)) {
-                $this->nli->out->elementStart('div', 'notice-options');
-                $this->showFaveForm();
-                $this->showReplyLink();
-                $this->nli->out->elementEnd('div');
-            }
-            Event::handle('EndShowNoticeOptions', array($this));
-        }
-    }
-}
diff --git a/plugins/Activity/unfollowlistitem.php b/plugins/Activity/unfollowlistitem.php
deleted file mode 100644 (file)
index 0ecb8be..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Unfollow list item
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Activity
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * NoticeListItemAdapter for join activities
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class UnfollowListItem extends SystemListItem
-{
-    /**
-     * Show the join activity
-     *
-     * @return void
-     */
-    function showNotice()
-    {
-        $out = $this->nli->out;
-        $out->elementStart('div', 'entry-title');
-        $this->showContent();
-        $out->elementEnd('div');
-    }
-
-    function showContent()
-    {
-        $notice = $this->nli->notice;
-        $out    = $this->nli->out;
-
-               // FIXME: get the actual data on the leave
-
-        $out->elementStart('div', 'unfollow-activity');
-
-        $out->raw($notice->rendered);
-
-        $out->elementEnd('div');
-    }
-}
index 975fc6885c0c3be709c90c1b9553c8bc98b2c297..c33097fd8159eb13daa1e515cb7772f509f09cdd 100644 (file)
@@ -92,38 +92,6 @@ class ActivitySpamPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'TrainAction':
-        case 'SpamAction':
-            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'Spam_score':
-            include_once $dir . '/'.$cls.'.php';
-            return false;
-        case 'SpamFilter':
-        case 'SpamNoticeStream':
-        case 'TrainSpamForm':
-        case 'TrainHamForm':
-            include_once $dir . '/'.strtolower($cls).'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * When a notice is saved, check its spam score
      * 
diff --git a/plugins/ActivitySpam/Spam_score.php b/plugins/ActivitySpam/Spam_score.php
deleted file mode 100644 (file)
index 2ae32fb..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-<?php
-  /**
-   * StatusNet - the distributed open-source microblogging tool
-   * Copyright (C) 2011, StatusNet, Inc.
-   *
-   * Score of a notice by activity spam service
-   * 
-   * PHP version 5
-   *
-   * This program is free software: you can redistribute it and/or modify
-   * it under the terms of the GNU Affero General Public License as published by
-   * the Free Software Foundation, either version 3 of the License, or
-   * (at your option) any later version.
-   *
-   * This program is distributed in the hope that it will be useful,
-   * but WITHOUT ANY WARRANTY; without even the implied warranty of
-   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   * GNU Affero General Public License for more details.
-   *
-   * You should have received a copy of the GNU Affero General Public License
-   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
-   *
-   * @category  Spam
-   * @package   StatusNet
-   * @author    Evan Prodromou <evan@status.net>
-   * @copyright 2011 StatusNet, Inc.
-   * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
-   * @link      http://status.net/
-   */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Score of a notice per the activity spam service
- *
- * @category Spam
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-
-class Spam_score extends Managed_DataObject
-{
-    const MAX_SCALE = 10000;
-    public $__table = 'spam_score'; // table name
-
-    public $notice_id;   // int
-    public $score;       // float
-    public $created;     // datetime
-
-    function saveNew($notice, $result) {
-
-        $score = new Spam_score();
-
-        $score->notice_id      = $notice->id;
-        $score->score          = $result->probability;
-        $score->is_spam        = $result->isSpam;
-        $score->scaled         = Spam_score::scale($score->score);
-        $score->created        = common_sql_now();
-        $score->notice_created = $notice->created;
-
-        $score->insert();
-
-        self::blow('spam_score:notice_ids');
-
-        return $score;
-    }
-
-    function save($notice, $result) {
-
-        $orig  = null;
-        $score = Spam_score::getKV('notice_id', $notice->id);
-
-        if (empty($score)) {
-            $score = new Spam_score();
-        } else {
-            $orig = clone($score);
-        }
-
-        $score->notice_id      = $notice->id;
-        $score->score          = $result->probability;
-        $score->is_spam        = $result->isSpam;
-        $score->scaled         = Spam_score::scale($score->score);
-        $score->created        = common_sql_now();
-        $score->notice_created = $notice->created;
-
-        if (empty($orig)) {
-            $score->insert();
-        } else {
-            $score->update($orig);
-        }
-        
-        self::blow('spam_score:notice_ids');
-
-        return $score;
-    }
-
-    function delete()
-    {
-        self::blow('spam_score:notice_ids');
-        self::blow('spam_score:notice_ids;last');
-        parent::delete();
-    }
-
-    /**
-     * The One True Thingy that must be defined and declared.
-     */
-    public static function schemaDef()
-    {
-        return array(
-            'description' => 'score of the notice per activityspam',
-            'fields' => array(
-                'notice_id' => array('type' => 'int',
-                                     'not null' => true,
-                                     'description' => 'notice getting scored'),
-                'score' => array('type' => 'double',
-                                 'not null' => true,
-                                 'description' => 'score for the notice (0.0, 1.0)'),
-                'scaled' => array('type' => 'int',
-                                  'description' => 'scaled score for the notice (0, 10000)'),
-                'is_spam' => array('type' => 'tinyint',
-                                   'description' => 'flag for spamosity'),
-                'created' => array('type' => 'datetime',
-                                   'not null' => true,
-                                   'description' => 'date this record was created'),
-                'notice_created' => array('type' => 'datetime',
-                                          'description' => 'date the notice was created'),
-            ),
-            'primary key' => array('notice_id'),
-            'foreign keys' => array(
-                'spam_score_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
-            ),
-            'indexes' => array(
-                'spam_score_created_idx' => array('created'),
-                'spam_score_scaled_idx' => array('scaled'),
-            ),
-        );
-    }
-
-    public static function upgrade()
-    {
-        Spam_score::upgradeScaled();
-        Spam_score::upgradeIsSpam();
-        Spam_score::upgradeNoticeCreated();
-    }
-
-    protected static function upgradeScaled()
-    {
-        $score = new Spam_score();
-        $score->whereAdd('scaled IS NULL');
-        
-        if ($score->find()) {
-            while ($score->fetch()) {
-                $orig = clone($score);
-                $score->scaled = Spam_score::scale($score->score);
-                $score->update($orig);
-            }
-        }
-    }
-
-    protected static function upgradeIsSpam()
-    {
-        $score = new Spam_score();
-        $score->whereAdd('is_spam IS NULL');
-        
-        if ($score->find()) {
-            while ($score->fetch()) {
-                $orig = clone($score);
-                $score->is_spam = ($score->score >= 0.90) ? 1 : 0;
-                $score->update($orig);
-            }
-        }
-    }
-
-    protected static function upgradeNoticeCreated()
-    {
-        $score = new Spam_score();
-        $score->whereAdd('notice_created IS NULL');
-        
-        if ($score->find()) {
-            while ($score->fetch()) {
-                $notice = Notice::getKV('id', $score->notice_id);
-                if (!empty($notice)) {
-                    $orig = clone($score);
-                    $score->notice_created = $notice->created;
-                    $score->update($orig);
-                }
-            }
-        }
-    }
-
-    public static function scale($score)
-    {
-        $raw = round($score * Spam_score::MAX_SCALE);
-        return max(0, min(Spam_score::MAX_SCALE, $raw));
-    }
-}
diff --git a/plugins/ActivitySpam/actions/spam.php b/plugins/ActivitySpam/actions/spam.php
new file mode 100644 (file)
index 0000000..a66b73a
--- /dev/null
@@ -0,0 +1,165 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2012, StatusNet, Inc.
+ *
+ * Stream of latest spam messages
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Spam
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+require_once INSTALLDIR.'/lib/noticelist.php';
+
+/**
+ * SpamAction
+ * 
+ * Shows the latest spam on the service
+ * 
+ * @category  Spam
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class SpamAction extends Action
+{
+    var $page = null;
+    var $notices = null;
+
+    function title() {
+        return _("Latest Spam");
+    }
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
+
+        // User must be logged in.
+
+        $user = common_current_user();
+
+        if (empty($user)) {
+            throw new ClientException(_("You must be logged in to review."), 403);
+        }
+
+        // User must have the right to review spam
+
+        if (!$user->hasRight(ActivitySpamPlugin::REVIEWSPAM)) {
+            throw new ClientException(_('You cannot review spam on this site.'), 403);
+        }
+
+        $stream = new SpamNoticeStream($user->getProfile());
+
+        $this->notices = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
+                                             NOTICES_PER_PAGE + 1);
+
+        if($this->page > 1 && $this->notices->N == 0) {
+            throw new ClientException(_('No such page.'), 404);
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+
+    function handle($argarray=null)
+    {
+        parent::handle($args);
+
+        $this->showPage();
+    }
+
+    /**
+     * Fill the content area
+     *
+     * Shows a list of the notices in the public stream, with some pagination
+     * controls.
+     *
+     * @return void
+     */
+
+    function showContent()
+    {
+        $nl = new NoticeList($this->notices, $this);
+
+        $cnt = $nl->show();
+
+        if ($cnt == 0) {
+            $this->showEmptyList();
+        }
+
+        $this->pagination($this->page > 1, 
+                          $cnt > NOTICES_PER_PAGE,
+                          $this->page,
+                          'spam');
+    }
+
+    function showEmptyList()
+    {
+        // TRANS: Text displayed for public feed when there are no public notices.
+        $message = _('This is the timeline of spam messages for %%site.name%% but none have been detected yet.');
+
+        $this->elementStart('div', 'guide');
+        $this->raw(common_markup_to_html($message));
+        $this->elementEnd('div');
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+
+    function isReadOnly($args)
+    {
+        return true;
+    }
+}
diff --git a/plugins/ActivitySpam/actions/train.php b/plugins/ActivitySpam/actions/train.php
new file mode 100644 (file)
index 0000000..69124b0
--- /dev/null
@@ -0,0 +1,155 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2012, StatusNet, Inc.
+ *
+ * Train a notice as spam
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Spam
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Train a notice as spam
+ *
+ * @category  Spam
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class TrainAction extends Action
+{
+    protected $notice = null;
+    protected $filter = null;
+    protected $category = null;
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        // User must be logged in.
+
+        $user = common_current_user();
+
+        if (empty($user)) {
+            throw new ClientException(_("You must be logged in to train spam."), 403);
+        }
+
+        // User must have the right to review spam
+
+        if (!$user->hasRight(ActivitySpamPlugin::TRAINSPAM)) {
+            throw new ClientException(_('You cannot review spam on this site.'), 403);
+        }
+
+        $id = $this->trimmed('notice');
+
+        $this->notice = Notice::getKV('id', $id);
+
+        if (empty($this->notice)) {
+            throw new ClientException(_("No such notice."));
+        }
+
+        $this->checkSessionToken();
+
+        $filter = null;
+
+        Event::handle('GetSpamFilter', array(&$filter));
+
+        if (empty($filter)) {
+            throw new ServerException(_("No spam filter configured."));
+        }
+
+        $this->filter = $filter;
+
+        $this->category = $this->trimmed('category');
+
+        if ($this->category !== SpamFilter::SPAM &&
+            $this->category !== SpamFilter::HAM)
+        {
+            throw new ClientException(_("No such category."));
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+
+    function handle($argarray=null)
+    {
+        // Train
+
+        $this->filter->trainOnError($this->notice, $this->category);
+
+        // Re-test
+
+        $result = $this->filter->test($this->notice);
+
+        // Update or insert
+
+        $score = Spam_score::save($this->notice, $result);
+
+        // Show new toggle form
+
+        if ($this->category === SpamFilter::SPAM) {
+            $form = new TrainHamForm($this, $this->notice);
+        } else {
+            $form = new TrainSpamForm($this, $this->notice);
+        }
+
+        if ($this->boolean('ajax')) {
+            $this->startHTML('text/xml;charset=utf-8');
+            $this->elementStart('head');
+            // TRANS: Page title for page on which favorite notices can be unfavourited.
+            $this->element('title', null, _('Disfavor favorite.'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $form->show();
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            common_redirect(common_local_url('spam'), 303);
+        }
+    }
+}
diff --git a/plugins/ActivitySpam/classes/spam_score.php b/plugins/ActivitySpam/classes/spam_score.php
new file mode 100644 (file)
index 0000000..2ae32fb
--- /dev/null
@@ -0,0 +1,202 @@
+<?php
+  /**
+   * StatusNet - the distributed open-source microblogging tool
+   * Copyright (C) 2011, StatusNet, Inc.
+   *
+   * Score of a notice by activity spam service
+   * 
+   * PHP version 5
+   *
+   * This program is free software: you can redistribute it and/or modify
+   * it under the terms of the GNU Affero General Public License as published by
+   * the Free Software Foundation, either version 3 of the License, or
+   * (at your option) any later version.
+   *
+   * This program is distributed in the hope that it will be useful,
+   * but WITHOUT ANY WARRANTY; without even the implied warranty of
+   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   * GNU Affero General Public License for more details.
+   *
+   * You should have received a copy of the GNU Affero General Public License
+   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+   *
+   * @category  Spam
+   * @package   StatusNet
+   * @author    Evan Prodromou <evan@status.net>
+   * @copyright 2011 StatusNet, Inc.
+   * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+   * @link      http://status.net/
+   */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Score of a notice per the activity spam service
+ *
+ * @category Spam
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+
+class Spam_score extends Managed_DataObject
+{
+    const MAX_SCALE = 10000;
+    public $__table = 'spam_score'; // table name
+
+    public $notice_id;   // int
+    public $score;       // float
+    public $created;     // datetime
+
+    function saveNew($notice, $result) {
+
+        $score = new Spam_score();
+
+        $score->notice_id      = $notice->id;
+        $score->score          = $result->probability;
+        $score->is_spam        = $result->isSpam;
+        $score->scaled         = Spam_score::scale($score->score);
+        $score->created        = common_sql_now();
+        $score->notice_created = $notice->created;
+
+        $score->insert();
+
+        self::blow('spam_score:notice_ids');
+
+        return $score;
+    }
+
+    function save($notice, $result) {
+
+        $orig  = null;
+        $score = Spam_score::getKV('notice_id', $notice->id);
+
+        if (empty($score)) {
+            $score = new Spam_score();
+        } else {
+            $orig = clone($score);
+        }
+
+        $score->notice_id      = $notice->id;
+        $score->score          = $result->probability;
+        $score->is_spam        = $result->isSpam;
+        $score->scaled         = Spam_score::scale($score->score);
+        $score->created        = common_sql_now();
+        $score->notice_created = $notice->created;
+
+        if (empty($orig)) {
+            $score->insert();
+        } else {
+            $score->update($orig);
+        }
+        
+        self::blow('spam_score:notice_ids');
+
+        return $score;
+    }
+
+    function delete()
+    {
+        self::blow('spam_score:notice_ids');
+        self::blow('spam_score:notice_ids;last');
+        parent::delete();
+    }
+
+    /**
+     * The One True Thingy that must be defined and declared.
+     */
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'score of the notice per activityspam',
+            'fields' => array(
+                'notice_id' => array('type' => 'int',
+                                     'not null' => true,
+                                     'description' => 'notice getting scored'),
+                'score' => array('type' => 'double',
+                                 'not null' => true,
+                                 'description' => 'score for the notice (0.0, 1.0)'),
+                'scaled' => array('type' => 'int',
+                                  'description' => 'scaled score for the notice (0, 10000)'),
+                'is_spam' => array('type' => 'tinyint',
+                                   'description' => 'flag for spamosity'),
+                'created' => array('type' => 'datetime',
+                                   'not null' => true,
+                                   'description' => 'date this record was created'),
+                'notice_created' => array('type' => 'datetime',
+                                          'description' => 'date the notice was created'),
+            ),
+            'primary key' => array('notice_id'),
+            'foreign keys' => array(
+                'spam_score_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
+            ),
+            'indexes' => array(
+                'spam_score_created_idx' => array('created'),
+                'spam_score_scaled_idx' => array('scaled'),
+            ),
+        );
+    }
+
+    public static function upgrade()
+    {
+        Spam_score::upgradeScaled();
+        Spam_score::upgradeIsSpam();
+        Spam_score::upgradeNoticeCreated();
+    }
+
+    protected static function upgradeScaled()
+    {
+        $score = new Spam_score();
+        $score->whereAdd('scaled IS NULL');
+        
+        if ($score->find()) {
+            while ($score->fetch()) {
+                $orig = clone($score);
+                $score->scaled = Spam_score::scale($score->score);
+                $score->update($orig);
+            }
+        }
+    }
+
+    protected static function upgradeIsSpam()
+    {
+        $score = new Spam_score();
+        $score->whereAdd('is_spam IS NULL');
+        
+        if ($score->find()) {
+            while ($score->fetch()) {
+                $orig = clone($score);
+                $score->is_spam = ($score->score >= 0.90) ? 1 : 0;
+                $score->update($orig);
+            }
+        }
+    }
+
+    protected static function upgradeNoticeCreated()
+    {
+        $score = new Spam_score();
+        $score->whereAdd('notice_created IS NULL');
+        
+        if ($score->find()) {
+            while ($score->fetch()) {
+                $notice = Notice::getKV('id', $score->notice_id);
+                if (!empty($notice)) {
+                    $orig = clone($score);
+                    $score->notice_created = $notice->created;
+                    $score->update($orig);
+                }
+            }
+        }
+    }
+
+    public static function scale($score)
+    {
+        $raw = round($score * Spam_score::MAX_SCALE);
+        return max(0, min(Spam_score::MAX_SCALE, $raw));
+    }
+}
diff --git a/plugins/ActivitySpam/forms/trainham.php b/plugins/ActivitySpam/forms/trainham.php
new file mode 100644 (file)
index 0000000..5a4c9c0
--- /dev/null
@@ -0,0 +1,146 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Toggle indicating spam, click to train as ham
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Spam
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form 
+ *
+ * @category  Spam
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class TrainHamForm extends Form {
+
+    var $notice  = null;
+
+    function __construct($out, $notice) {
+        parent::__construct($out);
+        $this->notice = $notice;
+    }
+
+    /**
+     * Name of the form
+     *
+     * Sub-classes should overload this with the name of their form.
+     *
+     * @return void
+     */
+
+    function formLegend()
+    {
+        return _("Train ham");
+    }
+
+    /**
+     * Visible or invisible data elements
+     *
+     * Display the form fields that make up the data of the form.
+     * Sub-classes should overload this to show their data.
+     *
+     * @return void
+     */
+
+    function formData()
+    {
+        $this->hidden('notice', $this->notice->id);
+    }
+
+    /**
+     * Buttons for form actions
+     *
+     * Submit and cancel buttons (or whatever)
+     * Sub-classes should overload this to show their own buttons.
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+        $this->submit('train-ham-submit-' . $this->notice->id,
+                      _('Clear spam'),
+                      'submit',
+                      null,
+                      _("Clear spam"));
+    }
+
+    /**
+     * ID of the form
+     *
+     * Should be unique on the page. Sub-classes should overload this
+     * to show their own IDs.
+     *
+     * @return int ID of the form
+     */
+
+    function id()
+    {
+        return 'train-ham-' . $this->notice->id;
+    }
+
+    /**
+     * Action of the form.
+     *
+     * URL to post to. Should be overloaded by subclasses to give
+     * somewhere to post to.
+     *
+     * @return string URL to post to
+     */
+
+    function action()
+    {
+        return common_local_url('train', array('category' => 'ham'));
+    }
+
+    /**
+     * Class of the form. May include space-separated list of multiple classes.
+     *
+     * If 'ajax' is included, the form will automatically be submitted with
+     * an 'ajax=1' parameter added, and the resulting form or error message
+     * will replace the form after submission.
+     *
+     * It's up to you to make sure that the target action supports this!
+     *
+     * @return string the form's class
+     */
+
+    function formClass()
+    {
+        return 'form-train-ham ajax';
+    }
+}
diff --git a/plugins/ActivitySpam/forms/trainspam.php b/plugins/ActivitySpam/forms/trainspam.php
new file mode 100644 (file)
index 0000000..ee1ecd2
--- /dev/null
@@ -0,0 +1,146 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Toggle indicating ham, click to train as spam
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Spam
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form 
+ *
+ * @category  Spam
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class TrainSpamForm extends Form {
+
+    var $notice  = null;
+
+    function __construct($out, $notice) {
+        parent::__construct($out);
+        $this->notice = $notice;
+    }
+
+    /**
+     * Name of the form
+     *
+     * Sub-classes should overload this with the name of their form.
+     *
+     * @return void
+     */
+
+    function formLegend()
+    {
+        return _("Train spam");
+    }
+
+    /**
+     * Visible or invisible data elements
+     *
+     * Display the form fields that make up the data of the form.
+     * Sub-classes should overload this to show their data.
+     *
+     * @return void
+     */
+
+    function formData()
+    {
+        $this->hidden('notice', $this->notice->id);
+    }
+
+    /**
+     * Buttons for form actions
+     *
+     * Submit and cancel buttons (or whatever)
+     * Sub-classes should overload this to show their own buttons.
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+        $this->submit('train-spam-submit-' . $this->notice->id,
+                      _('Train spam'),
+                      'submit',
+                      null,
+                      _("Mark as spam"));
+    }
+
+    /**
+     * ID of the form
+     *
+     * Should be unique on the page. Sub-classes should overload this
+     * to show their own IDs.
+     *
+     * @return int ID of the form
+     */
+
+    function id()
+    {
+        return 'train-spam-' . $this->notice->id;
+    }
+
+    /**
+     * Action of the form.
+     *
+     * URL to post to. Should be overloaded by subclasses to give
+     * somewhere to post to.
+     *
+     * @return string URL to post to
+     */
+
+    function action()
+    {
+        return common_local_url('train', array('category' => 'spam'));
+    }
+
+    /**
+     * Class of the form. May include space-separated list of multiple classes.
+     *
+     * If 'ajax' is included, the form will automatically be submitted with
+     * an 'ajax=1' parameter added, and the resulting form or error message
+     * will replace the form after submission.
+     *
+     * It's up to you to make sure that the target action supports this!
+     *
+     * @return string the form's class
+     */
+
+    function formClass()
+    {
+        return 'form-train-spam ajax';
+    }
+}
diff --git a/plugins/ActivitySpam/lib/spamfilter.php b/plugins/ActivitySpam/lib/spamfilter.php
new file mode 100644 (file)
index 0000000..3ddfdad
--- /dev/null
@@ -0,0 +1,171 @@
+<?php
+  /**
+   * StatusNet - the distributed open-source microblogging tool
+   * Copyright (C) 2012, StatusNet, Inc.
+   *
+   * Spam filter class
+   * 
+   * PHP version 5
+   *
+   * This program is free software: you can redistribute it and/or modify
+   * it under the terms of the GNU Affero General Public License as published by
+   * the Free Software Foundation, either version 3 of the License, or
+   * (at your option) any later version.
+   *
+   * This program is distributed in the hope that it will be useful,
+   * but WITHOUT ANY WARRANTY; without even the implied warranty of
+   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   * GNU Affero General Public License for more details.
+   *
+   * You should have received a copy of the GNU Affero General Public License
+   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+   *
+   * @category  Spam
+   * @package   StatusNet
+   * @author    Evan Prodromou <evan@status.net>
+   * @copyright 2012 StatusNet, Inc.
+   * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+   * @link      http://status.net/
+   */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Spam filter class
+ *
+ * Local proxy for remote filter
+ *
+ * @category  Spam
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class SpamFilter extends OAuthClient {
+
+    const HAM  = 'ham';
+    const SPAM = 'spam';
+
+    public $server;
+
+    function __construct($server, $consumerKey, $secret) {
+        parent::__construct($consumerKey, $secret);
+        $this->server = $server;
+    }
+
+    protected function toActivity($notice) {
+        // FIXME: need this to autoload ActivityStreamsMediaLink
+        $doc = new ActivityStreamJSONDocument();
+
+        $activity = $notice->asActivity(null);
+
+        return $activity;
+    }
+
+    public function test($notice) {
+
+        $activity = $this->toActivity($notice);
+        return $this->testActivity($activity);
+    }
+    
+    public function testActivity($activity) {
+
+        $response = $this->postJSON($this->server . "/is-this-spam", $activity->asArray());
+
+        $result = json_decode($response->getBody());
+
+        return $result;
+    }
+
+    public function train($notice, $category) {
+
+        $activity = $this->toActivity($notice);
+        return $this->trainActivity($activity, $category);
+
+    }
+
+    public function trainActivity($activity, $category) {
+
+        switch ($category) {
+        case self::HAM:
+            $endpoint = '/this-is-ham';
+            break;
+        case self::SPAM:
+            $endpoint = '/this-is-spam';
+            break;
+        default:
+            throw new Exception("Unknown category: " + $category);
+        }
+
+        $response = $this->postJSON($this->server . $endpoint, $activity->asArray());
+
+        // We don't do much with the results
+        return true;
+    }
+
+    public function trainOnError($notice, $category) {
+
+        $activity = $this->toActivity($notice);
+
+        return $this->trainActivityOnError($activity, $category);
+    }
+    
+    public function trainActivityOnError($activity, $category) {
+
+        $result = $this->testActivity($activity);
+
+        if (($category === self::SPAM && $result->isSpam) ||
+            ($category === self::HAM && !$result->isSpam)) {
+            return true;
+        } else {
+            return $this->trainActivity($activity, $category);
+        }
+    }
+
+    function postJSON($url, $body)
+    {
+        $request = OAuthRequest::from_consumer_and_token($this->consumer,
+                                                         $this->token,
+                                                         'POST',
+                                                         $url);
+
+        $request->sign_request($this->sha1_method,
+                               $this->consumer,
+                               $this->token);
+
+        $hclient = new HTTPClient($url);
+
+        $hclient->setConfig(array('connect_timeout' => 120,
+                                  'timeout' => 120,
+                                  'follow_redirects' => true,
+                                  'ssl_verify_peer' => false,
+                                  'ssl_verify_host' => false));
+
+        $hclient->setMethod(HTTP_Request2::METHOD_POST);
+        $hclient->setBody(json_encode($body));
+        $hclient->setHeader('Content-Type', 'application/json');
+        $hclient->setHeader($request->to_header());
+
+        // Twitter is strict about accepting invalid "Expect" headers
+        // No reason not to clear it still here -ESP
+
+        $hclient->setHeader('Expect', '');
+
+        try {
+            $response = $hclient->send();
+            $code = $response->getStatus();
+            if (!$response->isOK()) {
+                throw new OAuthClientException($response->getBody(), $code);
+            }
+            return $response;
+        } catch (Exception $e) {
+            throw new OAuthClientException($e->getMessage(), $e->getCode());
+        }
+    }
+}
diff --git a/plugins/ActivitySpam/lib/spamnoticestream.php b/plugins/ActivitySpam/lib/spamnoticestream.php
new file mode 100644 (file)
index 0000000..ffb8d08
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2012, StatusNet, Inc.
+ *
+ * Spam notice stream
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Spam
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Spam notice stream
+ *
+ * @category  Spam
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class SpamNoticeStream extends ScopingNoticeStream
+{
+    function __construct($tag, $profile = -1)
+    {
+        if (is_int($profile) && $profile == -1) {
+            $profile = Profile::current();
+        }
+        parent::__construct(new CachingNoticeStream(new RawSpamNoticeStream(),
+                                                    'spam_score:notice_ids'));
+    }
+}
+
+/**
+ * Raw stream of spammy notices
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class RawSpamNoticeStream extends NoticeStream
+{
+    function getNoticeIds($offset, $limit, $since_id, $max_id)
+    {
+        $ss = new Spam_score();
+
+        $ss->is_spam = 1;
+
+        $ss->selectAdd();
+        $ss->selectAdd('notice_id');
+
+        Notice::addWhereSinceId($ss, $since_id, 'notice_id');
+        Notice::addWhereMaxId($ss, $max_id, 'notice_id');
+
+        $ss->orderBy('notice_created DESC, notice_id DESC');
+
+        if (!is_null($offset)) {
+            $ss->limit($offset, $limit);
+        }
+
+        $ids = array();
+
+        if ($ss->find()) {
+            while ($ss->fetch()) {
+                $ids[] = $ss->notice_id;
+            }
+        }
+
+        return $ids;
+    }
+}
diff --git a/plugins/ActivitySpam/spam.php b/plugins/ActivitySpam/spam.php
deleted file mode 100644 (file)
index a66b73a..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2012, StatusNet, Inc.
- *
- * Stream of latest spam messages
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Spam
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2012 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/noticelist.php';
-
-/**
- * SpamAction
- * 
- * Shows the latest spam on the service
- * 
- * @category  Spam
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2012 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class SpamAction extends Action
-{
-    var $page = null;
-    var $notices = null;
-
-    function title() {
-        return _("Latest Spam");
-    }
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
-
-        // User must be logged in.
-
-        $user = common_current_user();
-
-        if (empty($user)) {
-            throw new ClientException(_("You must be logged in to review."), 403);
-        }
-
-        // User must have the right to review spam
-
-        if (!$user->hasRight(ActivitySpamPlugin::REVIEWSPAM)) {
-            throw new ClientException(_('You cannot review spam on this site.'), 403);
-        }
-
-        $stream = new SpamNoticeStream($user->getProfile());
-
-        $this->notices = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
-                                             NOTICES_PER_PAGE + 1);
-
-        if($this->page > 1 && $this->notices->N == 0) {
-            throw new ClientException(_('No such page.'), 404);
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-
-    function handle($argarray=null)
-    {
-        parent::handle($args);
-
-        $this->showPage();
-    }
-
-    /**
-     * Fill the content area
-     *
-     * Shows a list of the notices in the public stream, with some pagination
-     * controls.
-     *
-     * @return void
-     */
-
-    function showContent()
-    {
-        $nl = new NoticeList($this->notices, $this);
-
-        $cnt = $nl->show();
-
-        if ($cnt == 0) {
-            $this->showEmptyList();
-        }
-
-        $this->pagination($this->page > 1, 
-                          $cnt > NOTICES_PER_PAGE,
-                          $this->page,
-                          'spam');
-    }
-
-    function showEmptyList()
-    {
-        // TRANS: Text displayed for public feed when there are no public notices.
-        $message = _('This is the timeline of spam messages for %%site.name%% but none have been detected yet.');
-
-        $this->elementStart('div', 'guide');
-        $this->raw(common_markup_to_html($message));
-        $this->elementEnd('div');
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-
-    function isReadOnly($args)
-    {
-        return true;
-    }
-}
diff --git a/plugins/ActivitySpam/spamfilter.php b/plugins/ActivitySpam/spamfilter.php
deleted file mode 100644 (file)
index 3ddfdad..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-<?php
-  /**
-   * StatusNet - the distributed open-source microblogging tool
-   * Copyright (C) 2012, StatusNet, Inc.
-   *
-   * Spam filter class
-   * 
-   * PHP version 5
-   *
-   * This program is free software: you can redistribute it and/or modify
-   * it under the terms of the GNU Affero General Public License as published by
-   * the Free Software Foundation, either version 3 of the License, or
-   * (at your option) any later version.
-   *
-   * This program is distributed in the hope that it will be useful,
-   * but WITHOUT ANY WARRANTY; without even the implied warranty of
-   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   * GNU Affero General Public License for more details.
-   *
-   * You should have received a copy of the GNU Affero General Public License
-   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
-   *
-   * @category  Spam
-   * @package   StatusNet
-   * @author    Evan Prodromou <evan@status.net>
-   * @copyright 2012 StatusNet, Inc.
-   * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
-   * @link      http://status.net/
-   */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Spam filter class
- *
- * Local proxy for remote filter
- *
- * @category  Spam
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2012 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class SpamFilter extends OAuthClient {
-
-    const HAM  = 'ham';
-    const SPAM = 'spam';
-
-    public $server;
-
-    function __construct($server, $consumerKey, $secret) {
-        parent::__construct($consumerKey, $secret);
-        $this->server = $server;
-    }
-
-    protected function toActivity($notice) {
-        // FIXME: need this to autoload ActivityStreamsMediaLink
-        $doc = new ActivityStreamJSONDocument();
-
-        $activity = $notice->asActivity(null);
-
-        return $activity;
-    }
-
-    public function test($notice) {
-
-        $activity = $this->toActivity($notice);
-        return $this->testActivity($activity);
-    }
-    
-    public function testActivity($activity) {
-
-        $response = $this->postJSON($this->server . "/is-this-spam", $activity->asArray());
-
-        $result = json_decode($response->getBody());
-
-        return $result;
-    }
-
-    public function train($notice, $category) {
-
-        $activity = $this->toActivity($notice);
-        return $this->trainActivity($activity, $category);
-
-    }
-
-    public function trainActivity($activity, $category) {
-
-        switch ($category) {
-        case self::HAM:
-            $endpoint = '/this-is-ham';
-            break;
-        case self::SPAM:
-            $endpoint = '/this-is-spam';
-            break;
-        default:
-            throw new Exception("Unknown category: " + $category);
-        }
-
-        $response = $this->postJSON($this->server . $endpoint, $activity->asArray());
-
-        // We don't do much with the results
-        return true;
-    }
-
-    public function trainOnError($notice, $category) {
-
-        $activity = $this->toActivity($notice);
-
-        return $this->trainActivityOnError($activity, $category);
-    }
-    
-    public function trainActivityOnError($activity, $category) {
-
-        $result = $this->testActivity($activity);
-
-        if (($category === self::SPAM && $result->isSpam) ||
-            ($category === self::HAM && !$result->isSpam)) {
-            return true;
-        } else {
-            return $this->trainActivity($activity, $category);
-        }
-    }
-
-    function postJSON($url, $body)
-    {
-        $request = OAuthRequest::from_consumer_and_token($this->consumer,
-                                                         $this->token,
-                                                         'POST',
-                                                         $url);
-
-        $request->sign_request($this->sha1_method,
-                               $this->consumer,
-                               $this->token);
-
-        $hclient = new HTTPClient($url);
-
-        $hclient->setConfig(array('connect_timeout' => 120,
-                                  'timeout' => 120,
-                                  'follow_redirects' => true,
-                                  'ssl_verify_peer' => false,
-                                  'ssl_verify_host' => false));
-
-        $hclient->setMethod(HTTP_Request2::METHOD_POST);
-        $hclient->setBody(json_encode($body));
-        $hclient->setHeader('Content-Type', 'application/json');
-        $hclient->setHeader($request->to_header());
-
-        // Twitter is strict about accepting invalid "Expect" headers
-        // No reason not to clear it still here -ESP
-
-        $hclient->setHeader('Expect', '');
-
-        try {
-            $response = $hclient->send();
-            $code = $response->getStatus();
-            if (!$response->isOK()) {
-                throw new OAuthClientException($response->getBody(), $code);
-            }
-            return $response;
-        } catch (Exception $e) {
-            throw new OAuthClientException($e->getMessage(), $e->getCode());
-        }
-    }
-}
diff --git a/plugins/ActivitySpam/spamnoticestream.php b/plugins/ActivitySpam/spamnoticestream.php
deleted file mode 100644 (file)
index ffb8d08..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2012, StatusNet, Inc.
- *
- * Spam notice stream
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Spam
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2012 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Spam notice stream
- *
- * @category  Spam
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2012 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class SpamNoticeStream extends ScopingNoticeStream
-{
-    function __construct($tag, $profile = -1)
-    {
-        if (is_int($profile) && $profile == -1) {
-            $profile = Profile::current();
-        }
-        parent::__construct(new CachingNoticeStream(new RawSpamNoticeStream(),
-                                                    'spam_score:notice_ids'));
-    }
-}
-
-/**
- * Raw stream of spammy notices
- *
- * @category  Stream
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class RawSpamNoticeStream extends NoticeStream
-{
-    function getNoticeIds($offset, $limit, $since_id, $max_id)
-    {
-        $ss = new Spam_score();
-
-        $ss->is_spam = 1;
-
-        $ss->selectAdd();
-        $ss->selectAdd('notice_id');
-
-        Notice::addWhereSinceId($ss, $since_id, 'notice_id');
-        Notice::addWhereMaxId($ss, $max_id, 'notice_id');
-
-        $ss->orderBy('notice_created DESC, notice_id DESC');
-
-        if (!is_null($offset)) {
-            $ss->limit($offset, $limit);
-        }
-
-        $ids = array();
-
-        if ($ss->find()) {
-            while ($ss->fetch()) {
-                $ids[] = $ss->notice_id;
-            }
-        }
-
-        return $ids;
-    }
-}
diff --git a/plugins/ActivitySpam/train.php b/plugins/ActivitySpam/train.php
deleted file mode 100644 (file)
index 69124b0..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2012, StatusNet, Inc.
- *
- * Train a notice as spam
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Spam
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2012 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Train a notice as spam
- *
- * @category  Spam
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2012 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class TrainAction extends Action
-{
-    protected $notice = null;
-    protected $filter = null;
-    protected $category = null;
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        // User must be logged in.
-
-        $user = common_current_user();
-
-        if (empty($user)) {
-            throw new ClientException(_("You must be logged in to train spam."), 403);
-        }
-
-        // User must have the right to review spam
-
-        if (!$user->hasRight(ActivitySpamPlugin::TRAINSPAM)) {
-            throw new ClientException(_('You cannot review spam on this site.'), 403);
-        }
-
-        $id = $this->trimmed('notice');
-
-        $this->notice = Notice::getKV('id', $id);
-
-        if (empty($this->notice)) {
-            throw new ClientException(_("No such notice."));
-        }
-
-        $this->checkSessionToken();
-
-        $filter = null;
-
-        Event::handle('GetSpamFilter', array(&$filter));
-
-        if (empty($filter)) {
-            throw new ServerException(_("No spam filter configured."));
-        }
-
-        $this->filter = $filter;
-
-        $this->category = $this->trimmed('category');
-
-        if ($this->category !== SpamFilter::SPAM &&
-            $this->category !== SpamFilter::HAM)
-        {
-            throw new ClientException(_("No such category."));
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-
-    function handle($argarray=null)
-    {
-        // Train
-
-        $this->filter->trainOnError($this->notice, $this->category);
-
-        // Re-test
-
-        $result = $this->filter->test($this->notice);
-
-        // Update or insert
-
-        $score = Spam_score::save($this->notice, $result);
-
-        // Show new toggle form
-
-        if ($this->category === SpamFilter::SPAM) {
-            $form = new TrainHamForm($this, $this->notice);
-        } else {
-            $form = new TrainSpamForm($this, $this->notice);
-        }
-
-        if ($this->boolean('ajax')) {
-            $this->startHTML('text/xml;charset=utf-8');
-            $this->elementStart('head');
-            // TRANS: Page title for page on which favorite notices can be unfavourited.
-            $this->element('title', null, _('Disfavor favorite.'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $form->show();
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            common_redirect(common_local_url('spam'), 303);
-        }
-    }
-}
diff --git a/plugins/ActivitySpam/trainhamform.php b/plugins/ActivitySpam/trainhamform.php
deleted file mode 100644 (file)
index 5a4c9c0..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Toggle indicating spam, click to train as ham
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Spam
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form 
- *
- * @category  Spam
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class TrainHamForm extends Form {
-
-    var $notice  = null;
-
-    function __construct($out, $notice) {
-        parent::__construct($out);
-        $this->notice = $notice;
-    }
-
-    /**
-     * Name of the form
-     *
-     * Sub-classes should overload this with the name of their form.
-     *
-     * @return void
-     */
-
-    function formLegend()
-    {
-        return _("Train ham");
-    }
-
-    /**
-     * Visible or invisible data elements
-     *
-     * Display the form fields that make up the data of the form.
-     * Sub-classes should overload this to show their data.
-     *
-     * @return void
-     */
-
-    function formData()
-    {
-        $this->hidden('notice', $this->notice->id);
-    }
-
-    /**
-     * Buttons for form actions
-     *
-     * Submit and cancel buttons (or whatever)
-     * Sub-classes should overload this to show their own buttons.
-     *
-     * @return void
-     */
-
-    function formActions()
-    {
-        $this->submit('train-ham-submit-' . $this->notice->id,
-                      _('Clear spam'),
-                      'submit',
-                      null,
-                      _("Clear spam"));
-    }
-
-    /**
-     * ID of the form
-     *
-     * Should be unique on the page. Sub-classes should overload this
-     * to show their own IDs.
-     *
-     * @return int ID of the form
-     */
-
-    function id()
-    {
-        return 'train-ham-' . $this->notice->id;
-    }
-
-    /**
-     * Action of the form.
-     *
-     * URL to post to. Should be overloaded by subclasses to give
-     * somewhere to post to.
-     *
-     * @return string URL to post to
-     */
-
-    function action()
-    {
-        return common_local_url('train', array('category' => 'ham'));
-    }
-
-    /**
-     * Class of the form. May include space-separated list of multiple classes.
-     *
-     * If 'ajax' is included, the form will automatically be submitted with
-     * an 'ajax=1' parameter added, and the resulting form or error message
-     * will replace the form after submission.
-     *
-     * It's up to you to make sure that the target action supports this!
-     *
-     * @return string the form's class
-     */
-
-    function formClass()
-    {
-        return 'form-train-ham ajax';
-    }
-}
diff --git a/plugins/ActivitySpam/trainspamform.php b/plugins/ActivitySpam/trainspamform.php
deleted file mode 100644 (file)
index ee1ecd2..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Toggle indicating ham, click to train as spam
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Spam
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form 
- *
- * @category  Spam
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class TrainSpamForm extends Form {
-
-    var $notice  = null;
-
-    function __construct($out, $notice) {
-        parent::__construct($out);
-        $this->notice = $notice;
-    }
-
-    /**
-     * Name of the form
-     *
-     * Sub-classes should overload this with the name of their form.
-     *
-     * @return void
-     */
-
-    function formLegend()
-    {
-        return _("Train spam");
-    }
-
-    /**
-     * Visible or invisible data elements
-     *
-     * Display the form fields that make up the data of the form.
-     * Sub-classes should overload this to show their data.
-     *
-     * @return void
-     */
-
-    function formData()
-    {
-        $this->hidden('notice', $this->notice->id);
-    }
-
-    /**
-     * Buttons for form actions
-     *
-     * Submit and cancel buttons (or whatever)
-     * Sub-classes should overload this to show their own buttons.
-     *
-     * @return void
-     */
-
-    function formActions()
-    {
-        $this->submit('train-spam-submit-' . $this->notice->id,
-                      _('Train spam'),
-                      'submit',
-                      null,
-                      _("Mark as spam"));
-    }
-
-    /**
-     * ID of the form
-     *
-     * Should be unique on the page. Sub-classes should overload this
-     * to show their own IDs.
-     *
-     * @return int ID of the form
-     */
-
-    function id()
-    {
-        return 'train-spam-' . $this->notice->id;
-    }
-
-    /**
-     * Action of the form.
-     *
-     * URL to post to. Should be overloaded by subclasses to give
-     * somewhere to post to.
-     *
-     * @return string URL to post to
-     */
-
-    function action()
-    {
-        return common_local_url('train', array('category' => 'spam'));
-    }
-
-    /**
-     * Class of the form. May include space-separated list of multiple classes.
-     *
-     * If 'ajax' is included, the form will automatically be submitted with
-     * an 'ajax=1' parameter added, and the resulting form or error message
-     * will replace the form after submission.
-     *
-     * It's up to you to make sure that the target action supports this!
-     *
-     * @return string the form's class
-     */
-
-    function formClass()
-    {
-        return 'form-train-spam ajax';
-    }
-}
index fa8afb18078fce4440ade49011f7ebf48ac7cde3..78637b446e75df8dc24090b26678302546a0263c 100644 (file)
@@ -174,20 +174,6 @@ class AdsensePlugin extends UAPPlugin
         return true;
     }
 
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'AdsenseadminpanelAction':
-            require_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     function onEndAdminPanelNav($menu) {
         if (AdminPanelAction::canAdmin('adsense')) {
             // TRANS: Menu item title/tooltip
diff --git a/plugins/Adsense/actions/adsenseadminpanel.php b/plugins/Adsense/actions/adsenseadminpanel.php
new file mode 100644 (file)
index 0000000..8a5c0cf
--- /dev/null
@@ -0,0 +1,227 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Adsense administration panel
+ *
+ * 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  Adsense
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Administer adsense settings
+ *
+ * @category Adsense
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class AdsenseadminpanelAction extends AdminPanelAction
+{
+    /**
+     * Returns the page title
+     *
+     * @return string page title
+     */
+    function title()
+    {
+        // TRANS: Title of AdSense administrator panel.
+        return _m('TITLE', 'AdSense');
+    }
+
+    /**
+     * Instructions for using this form.
+     *
+     * @return string instructions
+     */
+    function getInstructions()
+    {
+        // TRANS: Instructions for AdSense administrator panel.
+        return _m('AdSense settings for this StatusNet site');
+    }
+
+    /**
+     * Show the site admin panel form
+     *
+     * @return void
+     */
+    function showForm()
+    {
+        $form = new AdsenseAdminPanelForm($this);
+        $form->show();
+        return;
+    }
+
+    /**
+     * Save settings from the form
+     *
+     * @return void
+     */
+    function saveSettings()
+    {
+        static $settings = array('adsense' => array('adScript', 'client', 'mediumRectangle', 'rectangle', 'leaderboard', 'wideSkyscraper'));
+
+        $values = array();
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                $values[$section][$setting] = $this->trimmed($setting);
+            }
+        }
+
+        // This throws an exception on validation errors
+        $this->validate($values);
+
+        // assert(all values are valid);
+        $config = new Config();
+
+        $config->query('BEGIN');
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                Config::save($section, $setting, $values[$section][$setting]);
+            }
+        }
+
+        $config->query('COMMIT');
+
+        return;
+    }
+
+    function validate(&$values)
+    {
+    }
+}
+
+/**
+ * Form for the adsense admin panel
+ */
+class AdsenseAdminPanelForm extends AdminForm
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'form_adsense_admin_panel';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_adsense';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('adsenseadminpanel');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('fieldset', array('id' => 'adsense_admin'));
+        $this->out->elementStart('ul', 'form_data');
+        $this->li();
+        $this->input('client',
+                     // TRANS: Field label in AdSense administration panel.
+                     _m('Client ID'),
+                     // TRANS: Field title in AdSense administration panel.
+                     _m('Google client ID.'),
+                     'adsense');
+        $this->unli();
+        $this->li();
+        $this->input('adScript',
+                     // TRANS: Field label in AdSense administration panel.
+                     _m('Ad script URL'),
+                     // TRANS: Field title in AdSense administration panel.
+                     _m('Script URL (advanced).'),
+                     'adsense');
+        $this->unli();
+        $this->li();
+        $this->input('mediumRectangle',
+                     // TRANS: Field label in AdSense administration panel.
+                     _m('Medium rectangle'),
+                     // TRANS: Field title in AdSense administration panel.
+                     _m('Medium rectangle slot code.'),
+                     'adsense');
+        $this->unli();
+        $this->li();
+        $this->input('rectangle',
+                     // TRANS: Field label in AdSense administration panel.
+                     _m('Rectangle'),
+                     // TRANS: Field title in AdSense administration panel.
+                     _m('Rectangle slot code.'),
+                     'adsense');
+        $this->unli();
+        $this->li();
+        $this->input('leaderboard',
+                     // TRANS: Field label in AdSense administration panel.
+                     _m('Leaderboard'),
+                     // TRANS: Field title in AdSense administration panel.
+                     _m('Leaderboard slot code.'),
+                     'adsense');
+        $this->unli();
+        $this->li();
+        $this->input('wideSkyscraper',
+                     // TRANS: Field label in AdSense administration panel.
+                     _m('Skyscraper'),
+                     // TRANS: Field title in AdSense administration panel.
+                     _m('Wide skyscraper slot code.'),
+                     'adsense');
+        $this->unli();
+        $this->out->elementEnd('ul');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text to save settings in AdSense administration panel.
+        $this->out->submit('submit', _m('BUTTON','Save'),
+        // TRANS: Button title to save settings in AdSense administration panel.
+        'submit', null, _m('Save AdSense settings.'));
+    }
+}
diff --git a/plugins/Adsense/adsenseadminpanel.php b/plugins/Adsense/adsenseadminpanel.php
deleted file mode 100644 (file)
index 8a5c0cf..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Adsense administration panel
- *
- * 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  Adsense
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Administer adsense settings
- *
- * @category Adsense
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class AdsenseadminpanelAction extends AdminPanelAction
-{
-    /**
-     * Returns the page title
-     *
-     * @return string page title
-     */
-    function title()
-    {
-        // TRANS: Title of AdSense administrator panel.
-        return _m('TITLE', 'AdSense');
-    }
-
-    /**
-     * Instructions for using this form.
-     *
-     * @return string instructions
-     */
-    function getInstructions()
-    {
-        // TRANS: Instructions for AdSense administrator panel.
-        return _m('AdSense settings for this StatusNet site');
-    }
-
-    /**
-     * Show the site admin panel form
-     *
-     * @return void
-     */
-    function showForm()
-    {
-        $form = new AdsenseAdminPanelForm($this);
-        $form->show();
-        return;
-    }
-
-    /**
-     * Save settings from the form
-     *
-     * @return void
-     */
-    function saveSettings()
-    {
-        static $settings = array('adsense' => array('adScript', 'client', 'mediumRectangle', 'rectangle', 'leaderboard', 'wideSkyscraper'));
-
-        $values = array();
-
-        foreach ($settings as $section => $parts) {
-            foreach ($parts as $setting) {
-                $values[$section][$setting] = $this->trimmed($setting);
-            }
-        }
-
-        // This throws an exception on validation errors
-        $this->validate($values);
-
-        // assert(all values are valid);
-        $config = new Config();
-
-        $config->query('BEGIN');
-
-        foreach ($settings as $section => $parts) {
-            foreach ($parts as $setting) {
-                Config::save($section, $setting, $values[$section][$setting]);
-            }
-        }
-
-        $config->query('COMMIT');
-
-        return;
-    }
-
-    function validate(&$values)
-    {
-    }
-}
-
-/**
- * Form for the adsense admin panel
- */
-class AdsenseAdminPanelForm extends AdminForm
-{
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'form_adsense_admin_panel';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_adsense';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('adsenseadminpanel');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('fieldset', array('id' => 'adsense_admin'));
-        $this->out->elementStart('ul', 'form_data');
-        $this->li();
-        $this->input('client',
-                     // TRANS: Field label in AdSense administration panel.
-                     _m('Client ID'),
-                     // TRANS: Field title in AdSense administration panel.
-                     _m('Google client ID.'),
-                     'adsense');
-        $this->unli();
-        $this->li();
-        $this->input('adScript',
-                     // TRANS: Field label in AdSense administration panel.
-                     _m('Ad script URL'),
-                     // TRANS: Field title in AdSense administration panel.
-                     _m('Script URL (advanced).'),
-                     'adsense');
-        $this->unli();
-        $this->li();
-        $this->input('mediumRectangle',
-                     // TRANS: Field label in AdSense administration panel.
-                     _m('Medium rectangle'),
-                     // TRANS: Field title in AdSense administration panel.
-                     _m('Medium rectangle slot code.'),
-                     'adsense');
-        $this->unli();
-        $this->li();
-        $this->input('rectangle',
-                     // TRANS: Field label in AdSense administration panel.
-                     _m('Rectangle'),
-                     // TRANS: Field title in AdSense administration panel.
-                     _m('Rectangle slot code.'),
-                     'adsense');
-        $this->unli();
-        $this->li();
-        $this->input('leaderboard',
-                     // TRANS: Field label in AdSense administration panel.
-                     _m('Leaderboard'),
-                     // TRANS: Field title in AdSense administration panel.
-                     _m('Leaderboard slot code.'),
-                     'adsense');
-        $this->unli();
-        $this->li();
-        $this->input('wideSkyscraper',
-                     // TRANS: Field label in AdSense administration panel.
-                     _m('Skyscraper'),
-                     // TRANS: Field title in AdSense administration panel.
-                     _m('Wide skyscraper slot code.'),
-                     'adsense');
-        $this->unli();
-        $this->out->elementEnd('ul');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text to save settings in AdSense administration panel.
-        $this->out->submit('submit', _m('BUTTON','Save'),
-        // TRANS: Button title to save settings in AdSense administration panel.
-        'submit', null, _m('Save AdSense settings.'));
-    }
-}
index ffb934587706e6ebdb458b6c4a406e5fc96ebd1a..f24472820503d334d0aadd420d2c676ec946c7db 100644 (file)
@@ -96,15 +96,9 @@ class AimPlugin extends ImPlugin
         case 'Aim':
             require_once(INSTALLDIR.'/plugins/Aim/extlib/phptoclib/aimclassw.php');
             return false;
-        case 'AimManager':
-            include_once $dir . '/'.strtolower($cls).'.php';
-            return false;
-        case 'Fake_Aim':
-            include_once $dir . '/'. $cls .'.php';
-            return false;
-        default:
-            return true;
         }
+
+        return parent::onAutoload($cls);
     }
 
     function onStartImDaemonIoManagers(&$classes)
diff --git a/plugins/Aim/Fake_Aim.php b/plugins/Aim/Fake_Aim.php
deleted file mode 100644 (file)
index e24c0f2..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Instead of sending AIM messages, retrieve the raw data that would be sent
- *
- * 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  Network
- * @package   StatusNet
- * @author    Craig Andrews <candrews@integralblue.com>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-class Fake_Aim extends Aim
-{
-    public $would_be_sent = null;
-
-    function sflapSend($sflap_type, $sflap_data, $no_null, $formatted)
-    {
-        $this->would_be_sent = array($sflap_type, $sflap_data, $no_null, $formatted);
-    }
-}
diff --git a/plugins/Aim/aimmanager.php b/plugins/Aim/aimmanager.php
deleted file mode 100644 (file)
index 619a9ad..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-/**
- * AIM background connection manager for AIM-using queue handlers,
- * allowing them to send outgoing messages on the right connection.
- *
- * Input is handled during socket select loop, keepalive pings during idle.
- * Any incoming messages will be handled.
- *
- * In a multi-site queuedaemon.php run, one connection will be instantiated
- * for each site being handled by the current process that has XMPP enabled.
- */
-class AimManager extends ImManager
-{
-    public $conn = null;
-    /**
-     * Initialize connection to server.
-     * @return boolean true on success
-     */
-    public function start($master)
-    {
-        if(parent::start($master))
-        {
-            $this->connect();
-            return true;
-        }else{
-            return false;
-        }
-    }
-
-    public function getSockets()
-    {
-        $this->connect();
-        if($this->conn){
-            return array($this->conn->myConnection);
-        }else{
-            return array();
-        }
-    }
-
-    /**
-     * Process AIM events that have come in over the wire.
-     * @param resource $socket
-     */
-    public function handleInput($socket)
-    {
-        common_log(LOG_DEBUG, "Servicing the AIM queue.");
-        $this->stats('aim_process');
-        $this->conn->receive();
-    }
-
-    function connect()
-    {
-        if (!$this->conn) {
-            $this->conn=new Aim($this->plugin->user,$this->plugin->password,4);
-            $this->conn->registerHandler("IMIn",array($this,"handle_aim_message"));
-            $this->conn->myServer="toc.oscar.aol.com";
-            $this->conn->signon();
-            // @todo i18n FIXME: Update translator documentation, please.
-            // TRANS: No idea what the use case for this message is.
-            $this->conn->setProfile(_m('Send me a message to post a notice'),false);
-        }
-        return $this->conn;
-    }
-
-    function handle_aim_message($data)
-    {
-        $this->plugin->enqueueIncomingRaw($data);
-        return true;
-    }
-
-    function send_raw_message($data)
-    {
-        $this->connect();
-        if (!$this->conn) {
-            return false;
-        }
-        $this->conn->sflapSend($data[0],$data[1],$data[2],$data[3]);
-        return true;
-    }
-}
diff --git a/plugins/Aim/classes/Fake_Aim.php b/plugins/Aim/classes/Fake_Aim.php
new file mode 100644 (file)
index 0000000..e24c0f2
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Instead of sending AIM messages, retrieve the raw data that would be sent
+ *
+ * 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  Network
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+class Fake_Aim extends Aim
+{
+    public $would_be_sent = null;
+
+    function sflapSend($sflap_type, $sflap_data, $no_null, $formatted)
+    {
+        $this->would_be_sent = array($sflap_type, $sflap_data, $no_null, $formatted);
+    }
+}
diff --git a/plugins/Aim/lib/aimmanager.php b/plugins/Aim/lib/aimmanager.php
new file mode 100644 (file)
index 0000000..619a9ad
--- /dev/null
@@ -0,0 +1,100 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+/**
+ * AIM background connection manager for AIM-using queue handlers,
+ * allowing them to send outgoing messages on the right connection.
+ *
+ * Input is handled during socket select loop, keepalive pings during idle.
+ * Any incoming messages will be handled.
+ *
+ * In a multi-site queuedaemon.php run, one connection will be instantiated
+ * for each site being handled by the current process that has XMPP enabled.
+ */
+class AimManager extends ImManager
+{
+    public $conn = null;
+    /**
+     * Initialize connection to server.
+     * @return boolean true on success
+     */
+    public function start($master)
+    {
+        if(parent::start($master))
+        {
+            $this->connect();
+            return true;
+        }else{
+            return false;
+        }
+    }
+
+    public function getSockets()
+    {
+        $this->connect();
+        if($this->conn){
+            return array($this->conn->myConnection);
+        }else{
+            return array();
+        }
+    }
+
+    /**
+     * Process AIM events that have come in over the wire.
+     * @param resource $socket
+     */
+    public function handleInput($socket)
+    {
+        common_log(LOG_DEBUG, "Servicing the AIM queue.");
+        $this->stats('aim_process');
+        $this->conn->receive();
+    }
+
+    function connect()
+    {
+        if (!$this->conn) {
+            $this->conn=new Aim($this->plugin->user,$this->plugin->password,4);
+            $this->conn->registerHandler("IMIn",array($this,"handle_aim_message"));
+            $this->conn->myServer="toc.oscar.aol.com";
+            $this->conn->signon();
+            // @todo i18n FIXME: Update translator documentation, please.
+            // TRANS: No idea what the use case for this message is.
+            $this->conn->setProfile(_m('Send me a message to post a notice'),false);
+        }
+        return $this->conn;
+    }
+
+    function handle_aim_message($data)
+    {
+        $this->plugin->enqueueIncomingRaw($data);
+        return true;
+    }
+
+    function send_raw_message($data)
+    {
+        $this->connect();
+        if (!$this->conn) {
+            return false;
+        }
+        $this->conn->sflapSend($data[0],$data[1],$data[2],$data[3]);
+        return true;
+    }
+}
index e0b2a17d1fef126cf88735c314405f5c9b313b25..67fe851d0f43c6e91e5dd95d0ba6590731d3a83a 100644 (file)
@@ -100,31 +100,6 @@ class AnonymousFavePlugin extends Plugin
         $action->inlineScript('SN.U.NoticeFavor();');
     }
 
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls) {
-            case 'Fave_tally':
-                include_once $dir . '/' . $cls . '.php';
-                return false;
-            case 'AnonFavorAction':
-                include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-                return false;
-            case 'AnonDisFavorAction':
-                include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-                return false;
-            case 'AnonFavorForm':
-                include_once $dir . '/anonfavorform.php';
-                return false;
-            case 'AnonDisFavorForm':
-                include_once $dir . '/anondisfavorform.php';
-                return false;
-            default:
-                return true;
-        }
-    }
-
     function onStartInitializeRouter($m)
     {
         $m->connect('main/anonfavor', array('action' => 'AnonFavor'));
diff --git a/plugins/AnonymousFave/Fave_tally.php b/plugins/AnonymousFave/Fave_tally.php
deleted file mode 100644 (file)
index c9fe181..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-<?php
-/**
- * Data class for favorites talley
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for favorites tally
- *
- * A class representing a total number of times a notice has been favored
- *
- * @category Action
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- */
-class Fave_tally extends Managed_DataObject
-{
-    ###START_AUTOCODE
-    /* the code below is auto generated do not remove the above tag */
-
-    public $__table = 'fave_tally';          // table name
-    public $notice_id;                       // int(4)  primary_key not_null
-    public $count;                           // int(4)  not_null
-    public $created;                         // datetime()   not_null
-    public $modified;                        // datetime   not_null default_0000-00-00%2000%3A00%3A00
-
-    /* the code above is auto generated do not remove the tag below */
-    ###END_AUTOCODE
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice id'),
-                'count' => array('type' => 'int', 'not null' => true, 'description' => 'the fave tally count'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('notice_id'),
-            'foreign keys' => array(
-                'fave_tally_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
-            ),
-        );
-    }
-
-    /**
-     * Increment a notice's tally
-     *
-     * @param integer $noticeID ID of notice we're tallying
-     *
-     * @return Fave_tally $tally the tally data object
-     */
-    static function increment($noticeID)
-    {
-        $tally = Fave_tally::ensureTally($noticeID);
-
-        $orig = clone($tally);
-        $tally->count++;
-        $result = $tally->update($orig);
-
-        if (!$result) {
-            $msg = sprintf(
-                // TRANS: Server exception.
-                // TRANS: %d is the notice ID (number).
-                _m("Could not update favorite tally for notice ID %d."),
-                $noticeID
-            );
-            throw new ServerException($msg);
-        }
-
-        return $tally;
-    }
-
-    /**
-     * Decrement a notice's tally
-     *
-     * @param integer $noticeID ID of notice we're tallying
-     *
-     * @return Fave_tally $tally the tally data object
-     */
-    static function decrement($noticeID)
-    {
-        $tally = Fave_tally::ensureTally($noticeID);
-
-        if ($tally->count > 0) {
-            $orig = clone($tally);
-            $tally->count--;
-            $result = $tally->update($orig);
-
-            if (!$result) {
-                $msg = sprintf(
-                    // TRANS: Server exception.
-                    // TRANS: %d is the notice ID (number).
-                    _m("Could not update favorite tally for notice ID %d."),
-                    $noticeID
-                );
-                throw new ServerException($msg);
-            }
-        }
-
-        return $tally;
-    }
-
-    /**
-     * Ensure a tally exists for a given notice. If we can't find
-     * one create one with the total number of existing faves
-     *
-     * @param integer $noticeID
-     *
-     * @return Fave_tally the tally data object
-     */
-    static function ensureTally($noticeID)
-    {
-        $tally = Fave_tally::getKV('notice_id', $noticeID);
-
-        if (!$tally) {
-            $tally = new Fave_tally();
-            $tally->notice_id = $noticeID;
-            $tally->count = Fave_tally::countExistingFaves($noticeID);
-            $result = $tally->insert();
-            if (!$result) {
-                $msg = sprintf(
-                    // TRANS: Server exception.
-                    // TRANS: %d is the notice ID (number).
-                    _m("Could not create favorite tally for notice ID %d."),
-                    $noticeID
-                );
-                throw new ServerException($msg);
-            }
-        }
-
-        return $tally;
-    }
-
-    /**
-     * Count the number of faves a notice already has. Used to initalize
-     * a tally for a notice.
-     *
-     * @param integer $noticeID ID of the notice to count faves for
-     *
-     * @return integer $total total number of time the notice has been favored
-     */
-    static function countExistingFaves($noticeID)
-    {
-        $fave = new Fave();
-        $fave->notice_id = $noticeID;
-        $total = $fave->count();
-        return $total;
-    }
-}
diff --git a/plugins/AnonymousFave/actions/anondisfavor.php b/plugins/AnonymousFave/actions/anondisfavor.php
new file mode 100644 (file)
index 0000000..e5ae096
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+/**
+ * Anonymous disfavor action
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Anonymous disfavor class
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class AnonDisfavorAction extends RedirectingAction
+{
+    /**
+     * Class handler.
+     *
+     * @param array $args query arguments
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+
+        $profile = AnonymousFavePlugin::getAnonProfile();
+
+        if (empty($profile) || $_SERVER['REQUEST_METHOD'] != 'POST') {
+            $this->clientError(
+                // TRANS: Client error.
+                _m('Could not disfavor notice! Please make sure your browser has cookies enabled.')
+            );
+            return;
+        }
+
+        $id     = $this->trimmed('notice');
+        $notice = Notice::getKV($id);
+        $token  = $this->trimmed('token-' . $notice->id);
+
+        if (!$token || $token != common_session_token()) {
+            // TRANS: Client error.
+            $this->clientError(_m('There was a problem with your session token. Try again, please.'));
+            return;
+        }
+
+        $fave            = new Fave();
+        $fave->user_id   = $profile->id;
+        $fave->notice_id = $notice->id;
+
+        if (!$fave->find(true)) {
+            // TRANS: Client error.
+            $this->clientError(_m('This notice is not a favorite!'));
+            return;
+        }
+
+        $result = $fave->delete();
+
+        if (!$result) {
+            common_log_db_error($fave, 'DELETE', __FILE__);
+            // TRANS: Server error.
+            $this->serverError(_m('Could not delete favorite.'));
+            return;
+        }
+
+        $profile->blowFavesCache();
+
+        if ($this->boolean('ajax')) {
+            $this->startHTML('text/xml;charset=utf-8');
+            $this->elementStart('head');
+            // TRANS: Title.
+            $this->element('title', null, _m('Add to favorites'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $favor = new AnonFavorForm($this, $notice);
+            $favor->show();
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            $this->returnToPrevious();
+        }
+    }
+
+    /**
+     * If returnto not set, return to the public stream.
+     *
+     * @return string URL
+     */
+    function defaultReturnTo()
+    {
+        $returnto = common_get_returnto();
+        if (empty($returnto)) {
+            return common_local_url('public');
+        } else {
+            return $returnto;
+        }
+    }
+}
diff --git a/plugins/AnonymousFave/actions/anonfavor.php b/plugins/AnonymousFave/actions/anonfavor.php
new file mode 100644 (file)
index 0000000..401b6a8
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+/**
+ * Anonyous favor action
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Anonymous favor class
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class AnonFavorAction extends RedirectingAction
+{
+    /**
+     * Class handler.
+     *
+     * @param array $args query arguments
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+
+        $profile = AnonymousFavePlugin::getAnonProfile();
+
+        if (empty($profile) || $_SERVER['REQUEST_METHOD'] != 'POST') {
+            // TRANS: Client error.
+            $this->clientError(_m('Could not favor notice! Please make sure your browser has cookies enabled.')
+            );
+            return;
+        }
+
+        $id     = $this->trimmed('notice');
+        $notice = Notice::getKV($id);
+        $token  = $this->trimmed('token-' . $notice->id);
+
+        if (empty($token) || $token != common_session_token()) {
+            // TRANS: Client error.
+            $this->clientError(_m('There was a problem with your session token. Try again, please.'));
+            return;
+        }
+
+
+        if ($profile->hasFave($notice)) {
+            // TRANS: Client error.
+            $this->clientError(_m('This notice is already a favorite!'));
+            return;
+        }
+        $fave = Fave::addNew($profile, $notice);
+
+        if (!$fave) {
+            // TRANS: Server error.
+            $this->serverError(_m('Could not create favorite.'));
+            return;
+        }
+
+        $profile->blowFavesCache();
+
+        if ($this->boolean('ajax')) {
+            $this->startHTML('text/xml;charset=utf-8');
+            $this->elementStart('head');
+            // TRANS: Title.
+            $this->element('title', null, _m('Disfavor favorite'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $disfavor = new AnonDisFavorForm($this, $notice);
+            $disfavor->show();
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            $this->returnToPrevious();
+        }
+    }
+
+    /**
+     * If returnto not set, return to the public stream.
+     *
+     * @return string URL
+     */
+    function defaultReturnTo()
+    {
+        $returnto = common_get_returnto();
+        if (empty($returnto)) {
+            return common_local_url('public');
+        } else {
+            return $returnto;
+        }
+    }
+}
diff --git a/plugins/AnonymousFave/anondisfavor.php b/plugins/AnonymousFave/anondisfavor.php
deleted file mode 100644 (file)
index e5ae096..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php
-/**
- * Anonymous disfavor action
- *
- * PHP version 5
- *
- * @category Action
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Anonymous disfavor class
- *
- * @category Action
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- */
-class AnonDisfavorAction extends RedirectingAction
-{
-    /**
-     * Class handler.
-     *
-     * @param array $args query arguments
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-
-        $profile = AnonymousFavePlugin::getAnonProfile();
-
-        if (empty($profile) || $_SERVER['REQUEST_METHOD'] != 'POST') {
-            $this->clientError(
-                // TRANS: Client error.
-                _m('Could not disfavor notice! Please make sure your browser has cookies enabled.')
-            );
-            return;
-        }
-
-        $id     = $this->trimmed('notice');
-        $notice = Notice::getKV($id);
-        $token  = $this->trimmed('token-' . $notice->id);
-
-        if (!$token || $token != common_session_token()) {
-            // TRANS: Client error.
-            $this->clientError(_m('There was a problem with your session token. Try again, please.'));
-            return;
-        }
-
-        $fave            = new Fave();
-        $fave->user_id   = $profile->id;
-        $fave->notice_id = $notice->id;
-
-        if (!$fave->find(true)) {
-            // TRANS: Client error.
-            $this->clientError(_m('This notice is not a favorite!'));
-            return;
-        }
-
-        $result = $fave->delete();
-
-        if (!$result) {
-            common_log_db_error($fave, 'DELETE', __FILE__);
-            // TRANS: Server error.
-            $this->serverError(_m('Could not delete favorite.'));
-            return;
-        }
-
-        $profile->blowFavesCache();
-
-        if ($this->boolean('ajax')) {
-            $this->startHTML('text/xml;charset=utf-8');
-            $this->elementStart('head');
-            // TRANS: Title.
-            $this->element('title', null, _m('Add to favorites'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $favor = new AnonFavorForm($this, $notice);
-            $favor->show();
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            $this->returnToPrevious();
-        }
-    }
-
-    /**
-     * If returnto not set, return to the public stream.
-     *
-     * @return string URL
-     */
-    function defaultReturnTo()
-    {
-        $returnto = common_get_returnto();
-        if (empty($returnto)) {
-            return common_local_url('public');
-        } else {
-            return $returnto;
-        }
-    }
-}
diff --git a/plugins/AnonymousFave/anondisfavorform.php b/plugins/AnonymousFave/anondisfavorform.php
deleted file mode 100644 (file)
index 38e2903..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Form for disfavoring a notice anonymously
- *
- * 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  Form
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/form.php';
-
-/**
- * Form for disfavoring a notice anonymously
- *
- * @category Form
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- * @see      DisFavorForm
- */
-class AnonDisfavorForm extends DisFavorForm
-{
-    /**
-     * Constructor
-     *
-     * @param HTMLOutputter $out    output channel
-     * @param Notice        $notice notice to disfavor
-     */
-    function __construct($out=null, $notice=null)
-    {
-        parent::__construct($out, $notice);
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('AnonDisFavor');
-    }
-}
diff --git a/plugins/AnonymousFave/anonfavor.php b/plugins/AnonymousFave/anonfavor.php
deleted file mode 100644 (file)
index 401b6a8..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-<?php
-/**
- * Anonyous favor action
- *
- * PHP version 5
- *
- * @category Action
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Anonymous favor class
- *
- * @category Action
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- */
-class AnonFavorAction extends RedirectingAction
-{
-    /**
-     * Class handler.
-     *
-     * @param array $args query arguments
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-
-        $profile = AnonymousFavePlugin::getAnonProfile();
-
-        if (empty($profile) || $_SERVER['REQUEST_METHOD'] != 'POST') {
-            // TRANS: Client error.
-            $this->clientError(_m('Could not favor notice! Please make sure your browser has cookies enabled.')
-            );
-            return;
-        }
-
-        $id     = $this->trimmed('notice');
-        $notice = Notice::getKV($id);
-        $token  = $this->trimmed('token-' . $notice->id);
-
-        if (empty($token) || $token != common_session_token()) {
-            // TRANS: Client error.
-            $this->clientError(_m('There was a problem with your session token. Try again, please.'));
-            return;
-        }
-
-
-        if ($profile->hasFave($notice)) {
-            // TRANS: Client error.
-            $this->clientError(_m('This notice is already a favorite!'));
-            return;
-        }
-        $fave = Fave::addNew($profile, $notice);
-
-        if (!$fave) {
-            // TRANS: Server error.
-            $this->serverError(_m('Could not create favorite.'));
-            return;
-        }
-
-        $profile->blowFavesCache();
-
-        if ($this->boolean('ajax')) {
-            $this->startHTML('text/xml;charset=utf-8');
-            $this->elementStart('head');
-            // TRANS: Title.
-            $this->element('title', null, _m('Disfavor favorite'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $disfavor = new AnonDisFavorForm($this, $notice);
-            $disfavor->show();
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            $this->returnToPrevious();
-        }
-    }
-
-    /**
-     * If returnto not set, return to the public stream.
-     *
-     * @return string URL
-     */
-    function defaultReturnTo()
-    {
-        $returnto = common_get_returnto();
-        if (empty($returnto)) {
-            return common_local_url('public');
-        } else {
-            return $returnto;
-        }
-    }
-}
diff --git a/plugins/AnonymousFave/anonfavorform.php b/plugins/AnonymousFave/anonfavorform.php
deleted file mode 100644 (file)
index a68fdaf..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Form for favoring a notice anonymously
- *
- * 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  Form
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 20010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/form.php';
-
-/**
- * Form for favoring a notice anonymously
- *
- * @category Form
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- * @see      AnonDisfavorForm
- */
-class AnonFavorForm extends FavorForm
-{
-    /**
-     * Constructor
-     *
-     * @param HTMLOutputter $out    output channel
-     * @param Notice        $notice notice to favor
-     */
-    function __construct($out=null, $notice=null)
-    {
-        parent::__construct($out, $notice);
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('AnonFavor');
-    }
-}
diff --git a/plugins/AnonymousFave/classes/Fave_tally.php b/plugins/AnonymousFave/classes/Fave_tally.php
new file mode 100644 (file)
index 0000000..c9fe181
--- /dev/null
@@ -0,0 +1,181 @@
+<?php
+/**
+ * Data class for favorites talley
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for favorites tally
+ *
+ * A class representing a total number of times a notice has been favored
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class Fave_tally extends Managed_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'fave_tally';          // table name
+    public $notice_id;                       // int(4)  primary_key not_null
+    public $count;                           // int(4)  not_null
+    public $created;                         // datetime()   not_null
+    public $modified;                        // datetime   not_null default_0000-00-00%2000%3A00%3A00
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice id'),
+                'count' => array('type' => 'int', 'not null' => true, 'description' => 'the fave tally count'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('notice_id'),
+            'foreign keys' => array(
+                'fave_tally_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
+            ),
+        );
+    }
+
+    /**
+     * Increment a notice's tally
+     *
+     * @param integer $noticeID ID of notice we're tallying
+     *
+     * @return Fave_tally $tally the tally data object
+     */
+    static function increment($noticeID)
+    {
+        $tally = Fave_tally::ensureTally($noticeID);
+
+        $orig = clone($tally);
+        $tally->count++;
+        $result = $tally->update($orig);
+
+        if (!$result) {
+            $msg = sprintf(
+                // TRANS: Server exception.
+                // TRANS: %d is the notice ID (number).
+                _m("Could not update favorite tally for notice ID %d."),
+                $noticeID
+            );
+            throw new ServerException($msg);
+        }
+
+        return $tally;
+    }
+
+    /**
+     * Decrement a notice's tally
+     *
+     * @param integer $noticeID ID of notice we're tallying
+     *
+     * @return Fave_tally $tally the tally data object
+     */
+    static function decrement($noticeID)
+    {
+        $tally = Fave_tally::ensureTally($noticeID);
+
+        if ($tally->count > 0) {
+            $orig = clone($tally);
+            $tally->count--;
+            $result = $tally->update($orig);
+
+            if (!$result) {
+                $msg = sprintf(
+                    // TRANS: Server exception.
+                    // TRANS: %d is the notice ID (number).
+                    _m("Could not update favorite tally for notice ID %d."),
+                    $noticeID
+                );
+                throw new ServerException($msg);
+            }
+        }
+
+        return $tally;
+    }
+
+    /**
+     * Ensure a tally exists for a given notice. If we can't find
+     * one create one with the total number of existing faves
+     *
+     * @param integer $noticeID
+     *
+     * @return Fave_tally the tally data object
+     */
+    static function ensureTally($noticeID)
+    {
+        $tally = Fave_tally::getKV('notice_id', $noticeID);
+
+        if (!$tally) {
+            $tally = new Fave_tally();
+            $tally->notice_id = $noticeID;
+            $tally->count = Fave_tally::countExistingFaves($noticeID);
+            $result = $tally->insert();
+            if (!$result) {
+                $msg = sprintf(
+                    // TRANS: Server exception.
+                    // TRANS: %d is the notice ID (number).
+                    _m("Could not create favorite tally for notice ID %d."),
+                    $noticeID
+                );
+                throw new ServerException($msg);
+            }
+        }
+
+        return $tally;
+    }
+
+    /**
+     * Count the number of faves a notice already has. Used to initalize
+     * a tally for a notice.
+     *
+     * @param integer $noticeID ID of the notice to count faves for
+     *
+     * @return integer $total total number of time the notice has been favored
+     */
+    static function countExistingFaves($noticeID)
+    {
+        $fave = new Fave();
+        $fave->notice_id = $noticeID;
+        $total = $fave->count();
+        return $total;
+    }
+}
diff --git a/plugins/AnonymousFave/forms/anondisfavor.php b/plugins/AnonymousFave/forms/anondisfavor.php
new file mode 100644 (file)
index 0000000..3d715f0
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for disfavoring a notice anonymously
+ *
+ * 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  Form
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/lib/form.php';
+
+/**
+ * Form for disfavoring a notice anonymously
+ *
+ * @category Form
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ * @see      DisFavorForm
+ */
+class AnonDisfavorForm extends DisFavorForm
+{
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out    output channel
+     * @param Notice        $notice notice to disfavor
+     */
+    function __construct($out=null, $notice=null)
+    {
+        parent::__construct($out, $notice);
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('AnonDisFavor');
+    }
+}
+
diff --git a/plugins/AnonymousFave/forms/anonfavor.php b/plugins/AnonymousFave/forms/anonfavor.php
new file mode 100644 (file)
index 0000000..491165a
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for favoring a notice anonymously
+ *
+ * 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  Form
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 20010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/lib/form.php';
+
+/**
+ * Form for favoring a notice anonymously
+ *
+ * @category Form
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ * @see      AnonDisfavorForm
+ */
+class AnonFavorForm extends FavorForm
+{
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out    output channel
+     * @param Notice        $notice notice to favor
+     */
+    function __construct($out=null, $notice=null)
+    {
+        parent::__construct($out, $notice);
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('AnonFavor');
+    }
+}
+
index 39b933afd95cedd6bba1696fb7b9138e4bedd90d..450a22ad62565673627ee071c22d2a2fc1106511 100644 (file)
@@ -39,16 +39,6 @@ class AutocompletePlugin extends Plugin
         parent::__construct();
     }
 
-    function onAutoload($cls)
-    {
-        switch ($cls)
-        {
-         case 'AutocompleteAction':
-            require_once(INSTALLDIR.'/plugins/Autocomplete/autocomplete.php');
-            return false;
-        }
-    }
-
     function onEndShowScripts($action){
         if (common_logged_in()) {
             $action->element('span', array('id' => 'autocomplete-api',
diff --git a/plugins/Autocomplete/actions/autocomplete.php b/plugins/Autocomplete/actions/autocomplete.php
new file mode 100644 (file)
index 0000000..0bb1b78
--- /dev/null
@@ -0,0 +1,181 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * List users for autocompletion
+ *
+ * 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  Plugin
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * List users for autocompletion
+ *
+ * This is the form for adding a new g
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class AutocompleteAction extends Action
+{
+    private $result;
+
+    /**
+     * Last-modified date for page
+     *
+     * When was the content of this page last modified? Based on notice,
+     * profile, avatar.
+     *
+     * @return int last-modified date as unix timestamp
+     */
+    function lastModified()
+    {
+        $max=0;
+        foreach($this->users as $user){
+            $max = max($max,strtotime($user->modified),strtotime($user->getProfile()->modified));
+        }
+        foreach($this->groups as $group){
+            $max = max($max,strtotime($group->modified));
+        }
+        return $max;
+    }
+
+    /**
+     * An entity tag for this page
+     *
+     * Shows the ETag for the page, based on the notice ID and timestamps
+     * for the notice, profile, and avatar. It's weak, since we change
+     * the date text "one hour ago", etc.
+     *
+     * @return string etag
+     */
+    function etag()
+    {
+        return '"' . implode(':', array($this->arg('action'),
+            common_user_cache_hash(),
+            crc32($this->arg('q')), //the actual string can have funny characters in we don't want showing up in the etag
+            $this->arg('limit'),
+            $this->lastModified())) . '"';
+    }
+
+    function prepare($args)
+    {
+        // If we die, show short error messages.
+        StatusNet::setApi(true);
+
+        parent::prepare($args);
+
+        $cur = common_current_user();
+        if (!$cur) {
+            // TRANS: Client exception in autocomplete plugin.
+            throw new ClientException(_m('Access forbidden.'), true);
+        }
+        $this->groups=array();
+        $this->users=array();
+        $q = $this->arg('q');
+        $limit = $this->arg('limit');
+        if($limit > 200) $limit=200; //prevent DOS attacks
+        if(substr($q,0,1)=='@'){
+            //user search
+            $q=substr($q,1);
+            $user = new User();
+            $user->limit($limit);
+            $user->whereAdd('nickname like \'' . trim($user->escape($q), '\'') . '%\'');
+            if($user->find()){
+                while($user->fetch()) {
+                    $this->users[]=clone($user);
+                }
+            }
+        }
+        if(substr($q,0,1)=='!'){
+            //group search
+            $q=substr($q,1);
+            $group = new User_group();
+            $group->limit($limit);
+            $group->whereAdd('nickname like \'' . trim($group->escape($q), '\'') . '%\'');
+            if($group->find()){
+                while($group->fetch()) {
+                    $this->groups[]=clone($group);
+                }
+            }
+        }
+        return true;
+    }
+
+    function handle($args)
+    {
+        parent::handle($args);
+        $results = array();
+        foreach($this->users as $user){
+            $profile = $user->getProfile();
+            $avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
+            // sigh.... encapsulate this upstream!
+            if ($avatar) {
+                $avatar = $avatar->displayUrl();
+            } else {
+                $avatar = Avatar::defaultImage(AVATAR_MINI_SIZE);
+            }
+            $results[] = array(
+                'nickname' => $user->nickname,
+                'fullname'=> $profile->fullname,
+                'avatar' => $avatar,
+                'type' => 'user'
+            );
+        }
+        foreach($this->groups as $group){
+            // sigh.... encapsulate this upstream!
+            if ($group->mini_logo) {
+                $avatar = $group->mini_logo;
+            } else {
+                $avatar = User_group::defaultLogo(AVATAR_MINI_SIZE);
+            }
+            $results[] = array(
+                'nickname' => $group->nickname,
+                'fullname'=> $group->fullname,
+                'avatar' => $avatar,
+                'type' => 'group');
+        }
+        foreach($results as $result) {
+            print json_encode($result) . "\n";
+        }
+    }
+
+    /**
+     * Is this action read-only?
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        return true;
+    }
+}
diff --git a/plugins/Autocomplete/autocomplete.php b/plugins/Autocomplete/autocomplete.php
deleted file mode 100644 (file)
index 0bb1b78..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * List users for autocompletion
- *
- * 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  Plugin
- * @package   StatusNet
- * @author    Craig Andrews <candrews@integralblue.com>
- * @copyright 2008-2009 StatusNet, Inc.
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * List users for autocompletion
- *
- * This is the form for adding a new g
- *
- * @category Plugin
- * @package  StatusNet
- * @author   Craig Andrews <candrews@integralblue.com>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class AutocompleteAction extends Action
-{
-    private $result;
-
-    /**
-     * Last-modified date for page
-     *
-     * When was the content of this page last modified? Based on notice,
-     * profile, avatar.
-     *
-     * @return int last-modified date as unix timestamp
-     */
-    function lastModified()
-    {
-        $max=0;
-        foreach($this->users as $user){
-            $max = max($max,strtotime($user->modified),strtotime($user->getProfile()->modified));
-        }
-        foreach($this->groups as $group){
-            $max = max($max,strtotime($group->modified));
-        }
-        return $max;
-    }
-
-    /**
-     * An entity tag for this page
-     *
-     * Shows the ETag for the page, based on the notice ID and timestamps
-     * for the notice, profile, and avatar. It's weak, since we change
-     * the date text "one hour ago", etc.
-     *
-     * @return string etag
-     */
-    function etag()
-    {
-        return '"' . implode(':', array($this->arg('action'),
-            common_user_cache_hash(),
-            crc32($this->arg('q')), //the actual string can have funny characters in we don't want showing up in the etag
-            $this->arg('limit'),
-            $this->lastModified())) . '"';
-    }
-
-    function prepare($args)
-    {
-        // If we die, show short error messages.
-        StatusNet::setApi(true);
-
-        parent::prepare($args);
-
-        $cur = common_current_user();
-        if (!$cur) {
-            // TRANS: Client exception in autocomplete plugin.
-            throw new ClientException(_m('Access forbidden.'), true);
-        }
-        $this->groups=array();
-        $this->users=array();
-        $q = $this->arg('q');
-        $limit = $this->arg('limit');
-        if($limit > 200) $limit=200; //prevent DOS attacks
-        if(substr($q,0,1)=='@'){
-            //user search
-            $q=substr($q,1);
-            $user = new User();
-            $user->limit($limit);
-            $user->whereAdd('nickname like \'' . trim($user->escape($q), '\'') . '%\'');
-            if($user->find()){
-                while($user->fetch()) {
-                    $this->users[]=clone($user);
-                }
-            }
-        }
-        if(substr($q,0,1)=='!'){
-            //group search
-            $q=substr($q,1);
-            $group = new User_group();
-            $group->limit($limit);
-            $group->whereAdd('nickname like \'' . trim($group->escape($q), '\'') . '%\'');
-            if($group->find()){
-                while($group->fetch()) {
-                    $this->groups[]=clone($group);
-                }
-            }
-        }
-        return true;
-    }
-
-    function handle($args)
-    {
-        parent::handle($args);
-        $results = array();
-        foreach($this->users as $user){
-            $profile = $user->getProfile();
-            $avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
-            // sigh.... encapsulate this upstream!
-            if ($avatar) {
-                $avatar = $avatar->displayUrl();
-            } else {
-                $avatar = Avatar::defaultImage(AVATAR_MINI_SIZE);
-            }
-            $results[] = array(
-                'nickname' => $user->nickname,
-                'fullname'=> $profile->fullname,
-                'avatar' => $avatar,
-                'type' => 'user'
-            );
-        }
-        foreach($this->groups as $group){
-            // sigh.... encapsulate this upstream!
-            if ($group->mini_logo) {
-                $avatar = $group->mini_logo;
-            } else {
-                $avatar = User_group::defaultLogo(AVATAR_MINI_SIZE);
-            }
-            $results[] = array(
-                'nickname' => $group->nickname,
-                'fullname'=> $group->fullname,
-                'avatar' => $avatar,
-                'type' => 'group');
-        }
-        foreach($results as $result) {
-            print json_encode($result) . "\n";
-        }
-    }
-
-    /**
-     * Is this action read-only?
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        return true;
-    }
-}
index 5efd573896c4f3fd807a17b6b4b25a549352bdf2..8b2fc6551f0b53a95b886d2dc801aaa5dff8afaa 100644 (file)
@@ -205,27 +205,6 @@ class BitlyUrlPlugin extends UrlShortenerPlugin
         return true;
     }
 
-    /**
-     * Automatically load the actions and libraries used by the plugin
-     *
-     * @param Class $cls the class
-     *
-     * @return boolean hook return
-     *
-     */
-    function onAutoload($cls)
-    {
-        $base = dirname(__FILE__);
-        $lower = strtolower($cls);
-        switch ($lower) {
-        case 'bitlyadminpanelaction':
-            require_once "$base/$lower.php";
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Internal hook point to check the default global credentials so
      * the admin form knows if we have a fallback or not.
diff --git a/plugins/BitlyUrl/actions/bitlyadminpanel.php b/plugins/BitlyUrl/actions/bitlyadminpanel.php
new file mode 100644 (file)
index 0000000..53e0ec9
--- /dev/null
@@ -0,0 +1,244 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Admin panel for plugin to use bit.ly URL shortening services.
+ *
+ * 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  Settings
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Administer global bit.ly URL shortener settings
+ *
+ * @category Admin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class BitlyadminpanelAction extends AdminPanelAction
+{
+    /**
+     * Returns the page title
+     *
+     * @return string page title
+     */
+    function title()
+    {
+        // TRANS: Title of administration panel.
+        return _m('bit.ly URL shortening');
+    }
+
+    /**
+     * Instructions for using this form.
+     *
+     * @return string instructions
+     */
+    function getInstructions()
+    {
+        // TRANS: Instructions for administration panel.
+        // TRANS: This message contains Markdown links in the form [decsription](link).
+        return _m('URL shortening with bit.ly requires ' .
+                  '[a bit.ly account and API key](http://bit.ly/a/your_api_key). ' .
+                  'This verifies that this is an authorized account, and ' .
+                  'allow you to use bit.ly\'s tracking features and custom domains.');
+    }
+
+    /**
+     * Show the bit.ly admin panel form
+     *
+     * @return void
+     */
+    function showForm()
+    {
+        $form = new BitlyAdminPanelForm($this);
+        $form->show();
+        return;
+    }
+
+    /**
+     * Save settings from the form
+     *
+     * @return void
+     */
+    function saveSettings()
+    {
+        static $settings = array(
+            'bitly' => array('default_login', 'default_apikey')
+        );
+
+        $values = array();
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                $values[$section][$setting]
+                    = $this->trimmed($setting);
+            }
+        }
+
+        // This throws an exception on validation errors
+
+        $this->validate($values);
+
+        // assert(all values are valid);
+
+        $config = new Config();
+
+        $config->query('BEGIN');
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                Config::save($section, $setting, $values[$section][$setting]);
+            }
+        }
+
+        $config->query('COMMIT');
+
+        return;
+    }
+
+    function validate(&$values)
+    {
+        // Validate consumer key and secret (can't be too long)
+
+        if (mb_strlen($values['bitly']['default_apikey']) > 255) {
+            $this->clientError(
+                // TRANS: Client error displayed when using too long a key.
+                _m('Invalid login. Maximum length is 255 characters.')
+            );
+        }
+
+        if (mb_strlen($values['bitly']['default_apikey']) > 255) {
+            $this->clientError(
+                // TRANS: Client error displayed when using too long a key.
+                _m('Invalid API key. Maximum length is 255 characters.')
+            );
+        }
+    }
+}
+
+class BitlyAdminPanelForm extends AdminForm
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'bitlyadminpanel';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('bitlyadminpanel');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart(
+            'fieldset',
+            array('id' => 'settings_bitly')
+        );
+        // TRANS: Fieldset legend in administration panel for bit.ly username and API key.
+        $this->out->element('legend', null, _m('LEGEND','Credentials'));
+
+        // Do we have global defaults to fall back on?
+        $login = $apiKey = false;
+        Event::handle('BitlyDefaultCredentials', array(&$login, &$apiKey));
+        $haveGlobalDefaults = ($login && $apiKey);
+        if ($login && $apiKey) {
+            $this->out->element('p', 'form_guide',
+                // TRANS: Form guide in administration panel for bit.ly URL shortening.
+                _m('Leave these empty to use global default credentials.'));
+        } else {
+            $this->out->element('p', 'form_guide',
+                // TRANS: Form guide in administration panel for bit.ly URL shortening.
+                _m('If you leave these empty, bit.ly will be unavailable to users.'));
+        }
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->input(
+            'default_login',
+            // TRANS: Field label in administration panel for bit.ly URL shortening.
+            _m('Login name'),
+            null,
+            'bitly'
+        );
+        $this->unli();
+
+        $this->li();
+        $this->input(
+            'default_apikey',
+            // TRANS: Field label in administration panel for bit.ly URL shortening.
+             _m('API key'),
+            null,
+            'bitly'
+        );
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        $this->out->submit('submit',
+                           // TRANS: Button text to save setting in administration panel for bit.ly URL shortening.
+                           _m('BUTTON','Save'),
+                           'submit',
+                           null,
+                           // TRANS: Button title to save setting in administration panel for bit.ly URL shortening.
+                           _m('Save bit.ly settings'));
+    }
+}
diff --git a/plugins/BitlyUrl/bitlyadminpanelaction.php b/plugins/BitlyUrl/bitlyadminpanelaction.php
deleted file mode 100644 (file)
index 53e0ec9..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Admin panel for plugin to use bit.ly URL shortening services.
- *
- * 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  Settings
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Administer global bit.ly URL shortener settings
- *
- * @category Admin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class BitlyadminpanelAction extends AdminPanelAction
-{
-    /**
-     * Returns the page title
-     *
-     * @return string page title
-     */
-    function title()
-    {
-        // TRANS: Title of administration panel.
-        return _m('bit.ly URL shortening');
-    }
-
-    /**
-     * Instructions for using this form.
-     *
-     * @return string instructions
-     */
-    function getInstructions()
-    {
-        // TRANS: Instructions for administration panel.
-        // TRANS: This message contains Markdown links in the form [decsription](link).
-        return _m('URL shortening with bit.ly requires ' .
-                  '[a bit.ly account and API key](http://bit.ly/a/your_api_key). ' .
-                  'This verifies that this is an authorized account, and ' .
-                  'allow you to use bit.ly\'s tracking features and custom domains.');
-    }
-
-    /**
-     * Show the bit.ly admin panel form
-     *
-     * @return void
-     */
-    function showForm()
-    {
-        $form = new BitlyAdminPanelForm($this);
-        $form->show();
-        return;
-    }
-
-    /**
-     * Save settings from the form
-     *
-     * @return void
-     */
-    function saveSettings()
-    {
-        static $settings = array(
-            'bitly' => array('default_login', 'default_apikey')
-        );
-
-        $values = array();
-
-        foreach ($settings as $section => $parts) {
-            foreach ($parts as $setting) {
-                $values[$section][$setting]
-                    = $this->trimmed($setting);
-            }
-        }
-
-        // This throws an exception on validation errors
-
-        $this->validate($values);
-
-        // assert(all values are valid);
-
-        $config = new Config();
-
-        $config->query('BEGIN');
-
-        foreach ($settings as $section => $parts) {
-            foreach ($parts as $setting) {
-                Config::save($section, $setting, $values[$section][$setting]);
-            }
-        }
-
-        $config->query('COMMIT');
-
-        return;
-    }
-
-    function validate(&$values)
-    {
-        // Validate consumer key and secret (can't be too long)
-
-        if (mb_strlen($values['bitly']['default_apikey']) > 255) {
-            $this->clientError(
-                // TRANS: Client error displayed when using too long a key.
-                _m('Invalid login. Maximum length is 255 characters.')
-            );
-        }
-
-        if (mb_strlen($values['bitly']['default_apikey']) > 255) {
-            $this->clientError(
-                // TRANS: Client error displayed when using too long a key.
-                _m('Invalid API key. Maximum length is 255 characters.')
-            );
-        }
-    }
-}
-
-class BitlyAdminPanelForm extends AdminForm
-{
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'bitlyadminpanel';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('bitlyadminpanel');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart(
-            'fieldset',
-            array('id' => 'settings_bitly')
-        );
-        // TRANS: Fieldset legend in administration panel for bit.ly username and API key.
-        $this->out->element('legend', null, _m('LEGEND','Credentials'));
-
-        // Do we have global defaults to fall back on?
-        $login = $apiKey = false;
-        Event::handle('BitlyDefaultCredentials', array(&$login, &$apiKey));
-        $haveGlobalDefaults = ($login && $apiKey);
-        if ($login && $apiKey) {
-            $this->out->element('p', 'form_guide',
-                // TRANS: Form guide in administration panel for bit.ly URL shortening.
-                _m('Leave these empty to use global default credentials.'));
-        } else {
-            $this->out->element('p', 'form_guide',
-                // TRANS: Form guide in administration panel for bit.ly URL shortening.
-                _m('If you leave these empty, bit.ly will be unavailable to users.'));
-        }
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-        $this->input(
-            'default_login',
-            // TRANS: Field label in administration panel for bit.ly URL shortening.
-            _m('Login name'),
-            null,
-            'bitly'
-        );
-        $this->unli();
-
-        $this->li();
-        $this->input(
-            'default_apikey',
-            // TRANS: Field label in administration panel for bit.ly URL shortening.
-             _m('API key'),
-            null,
-            'bitly'
-        );
-        $this->unli();
-
-        $this->out->elementEnd('ul');
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        $this->out->submit('submit',
-                           // TRANS: Button text to save setting in administration panel for bit.ly URL shortening.
-                           _m('BUTTON','Save'),
-                           'submit',
-                           null,
-                           // TRANS: Button title to save setting in administration panel for bit.ly URL shortening.
-                           _m('Save bit.ly settings'));
-    }
-}
index 5f31be63d472178ac7a74f874521c0a2016cb8c5..2a99e65f112dd41f0035caeb48a684d64cf6d409 100644 (file)
@@ -284,30 +284,6 @@ class BlacklistPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Auto-load our classes if called
-     *
-     * @param string $cls Class to load
-     *
-     * @return boolean hook return
-     */
-    function onAutoload($cls)
-    {
-        switch (strtolower($cls))
-        {
-        case 'nickname_blacklist':
-        case 'homepage_blacklist':
-            include_once INSTALLDIR.'/plugins/Blacklist/'.ucfirst($cls).'.php';
-            return false;
-        case 'blacklistadminpanelaction':
-            $base = strtolower(mb_substr($cls, 0, -6));
-            include_once INSTALLDIR.'/plugins/Blacklist/'.$base.'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Plugin version data
      *
diff --git a/plugins/Blacklist/Homepage_blacklist.php b/plugins/Blacklist/Homepage_blacklist.php
deleted file mode 100644 (file)
index fb2712f..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-<?php
-/**
- * Data class for homepage blacklisting
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for Homepage blacklist
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Homepage_blacklist extends Managed_DataObject
-{
-    public $__table = 'homepage_blacklist'; // table name
-    public $pattern;                        // varchar(255) pattern
-    public $created;                        // datetime not_null
-    public $modified;                       // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'pattern' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'blacklist pattern'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('pattern'),
-        );
-    }
-
-    /**
-     * Return a list of patterns to check
-     *
-     * @return array string patterns to check
-     */
-    static function getPatterns()
-    {
-        $patterns = self::cacheGet('homepage_blacklist:patterns');
-
-        if ($patterns === false) {
-
-            $patterns = array();
-
-            $nb = new Homepage_blacklist();
-
-            $nb->find();
-
-            while ($nb->fetch()) {
-                $patterns[] = $nb->pattern;
-            }
-
-            self::cacheSet('homepage_blacklist:patterns', $patterns);
-        }
-
-        return $patterns;
-    }
-
-    /**
-     * Save new list of patterns
-     *
-     * @return array of patterns to check
-     */
-    static function saveNew($newPatterns)
-    {
-        $oldPatterns = self::getPatterns();
-
-        // Delete stuff that's old that not in new
-        $toDelete = array_diff($oldPatterns, $newPatterns);
-
-        // Insert stuff that's in new and not in old
-        $toInsert = array_diff($newPatterns, $oldPatterns);
-
-        foreach ($toDelete as $pattern) {
-            $nb = Homepage_blacklist::getKV('pattern', $pattern);
-            if (!empty($nb)) {
-                $nb->delete();
-            }
-        }
-
-        foreach ($toInsert as $pattern) {
-            $nb = new Homepage_blacklist();
-            $nb->pattern = $pattern;
-            $nb->created = common_sql_now();
-            $nb->insert();
-        }
-
-        self::blow('homepage_blacklist:patterns');
-    }
-
-    static function ensurePattern($pattern)
-    {
-        $hb = Homepage_blacklist::getKV('pattern', $pattern);
-
-        if (empty($nb)) {
-            $hb = new Homepage_blacklist();
-            $hb->pattern = $pattern;
-            $hb->created = common_sql_now();
-            $hb->insert();
-            self::blow('homepage_blacklist:patterns');
-        }
-    }
-}
diff --git a/plugins/Blacklist/Nickname_blacklist.php b/plugins/Blacklist/Nickname_blacklist.php
deleted file mode 100644 (file)
index f4f387a..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-<?php
-/**
- * Data class for nickname blacklisting
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for Nickname blacklist
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Nickname_blacklist extends Managed_DataObject
-{
-    public $__table = 'nickname_blacklist'; // table name
-    public $pattern;                        // varchar(255) pattern
-    public $created;                        // datetime not_null
-    public $modified;                       // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'pattern' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'blacklist pattern'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('pattern'),
-        );
-    }
-
-    /**
-     * Return a list of patterns to check
-     *
-     * @return array string patterns to check
-     */
-    static function getPatterns()
-    {
-        $patterns = self::cacheGet('nickname_blacklist:patterns');
-
-        if ($patterns === false) {
-
-            $patterns = array();
-
-            $nb = new Nickname_blacklist();
-
-            $nb->find();
-
-            while ($nb->fetch()) {
-                $patterns[] = $nb->pattern;
-            }
-
-            self::cacheSet('nickname_blacklist:patterns', $patterns);
-        }
-
-        return $patterns;
-    }
-
-    /**
-     * Save new list of patterns
-     *
-     * @return array of patterns to check
-     */
-    static function saveNew($newPatterns)
-    {
-        $oldPatterns = self::getPatterns();
-
-        // Delete stuff that's old that not in new
-        $toDelete = array_diff($oldPatterns, $newPatterns);
-
-        // Insert stuff that's in new and not in old
-        $toInsert = array_diff($newPatterns, $oldPatterns);
-
-        foreach ($toDelete as $pattern) {
-            $nb = Nickname_blacklist::getKV('pattern', $pattern);
-            if (!empty($nb)) {
-                $nb->delete();
-            }
-        }
-
-        foreach ($toInsert as $pattern) {
-            $nb = new Nickname_blacklist();
-            $nb->pattern = $pattern;
-            $nb->created = common_sql_now();
-            $nb->insert();
-        }
-
-        self::blow('nickname_blacklist:patterns');
-    }
-
-    static function ensurePattern($pattern)
-    {
-        $nb = Nickname_blacklist::getKV('pattern', $pattern);
-
-        if (empty($nb)) {
-            $nb = new Nickname_blacklist();
-            $nb->pattern = $pattern;
-            $nb->created = common_sql_now();
-            $nb->insert();
-            self::blow('nickname_blacklist:patterns');
-        }
-    }
-}
diff --git a/plugins/Blacklist/actions/blacklistadminpanel.php b/plugins/Blacklist/actions/blacklistadminpanel.php
new file mode 100644 (file)
index 0000000..ee1c213
--- /dev/null
@@ -0,0 +1,211 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Blacklist administration panel
+ *
+ * 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  Settings
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Administer blacklist
+ *
+ * @category Admin
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link     http://status.net/
+ */
+class BlacklistadminpanelAction extends AdminPanelAction
+{
+    /**
+     * title of the admin panel
+     *
+     * @return string title
+     */
+    function title()
+    {
+        // TRANS: Title of blacklist plugin administration panel.
+        return _m('TITLE','Blacklist');
+    }
+
+    /**
+     * Panel instructions
+     *
+     * @return string instructions
+     */
+    function getInstructions()
+    {
+        // TRANS: Instructions for blacklist plugin administration panel.
+        return _m('Blacklisted URLs and nicknames');
+    }
+
+    /**
+     * Show the actual form
+     *
+     * @return void
+     *
+     * @see BlacklistAdminPanelForm
+     */
+    function showForm()
+    {
+        $form = new BlacklistAdminPanelForm($this);
+        $form->show();
+        return;
+    }
+
+    /**
+     * Save the form settings
+     *
+     * @return void
+     */
+    function saveSettings()
+    {
+        $nickPatterns = $this->splitPatterns($this->trimmed('blacklist-nicknames'));
+        Nickname_blacklist::saveNew($nickPatterns);
+
+        $urlPatterns = $this->splitPatterns($this->trimmed('blacklist-urls'));
+        Homepage_blacklist::saveNew($urlPatterns);
+
+        return;
+    }
+
+    protected function splitPatterns($text)
+    {
+        $patterns = array();
+        foreach (explode("\n", $text) as $raw) {
+            $trimmed = trim($raw);
+            if ($trimmed != '') {
+                $patterns[] = $trimmed;
+            }
+        }
+        return $patterns;
+    }
+
+    /**
+     * Validate the values
+     *
+     * @param array &$values 2d array of values to check
+     *
+     * @return boolean success flag
+     */
+    function validate(&$values)
+    {
+        return true;
+    }
+}
+
+/**
+ * Admin panel form for blacklist panel
+ *
+ * @category Admin
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link     http://status.net/
+ */
+class BlacklistAdminPanelForm extends Form
+{
+    /**
+     * ID of the form
+     *
+     * @return string ID
+     */
+    function id()
+    {
+        return 'blacklistadminpanel';
+    }
+
+    /**
+     * Class of the form
+     *
+     * @return string class
+     */
+    function formClass()
+    {
+        return 'form_settings';
+    }
+
+    /**
+     * Action we post to
+     *
+     * @return string action URL
+     */
+    function action()
+    {
+        return common_local_url('blacklistadminpanel');
+    }
+
+    /**
+     * Show the form controls
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->out->elementStart('li');
+
+        $nickPatterns = Nickname_blacklist::getPatterns();
+
+        // TRANS: Field label in blacklist plugin administration panel.
+        $this->out->textarea('blacklist-nicknames', _m('Nicknames'),
+                             implode("\r\n", $nickPatterns),
+                             // TRANS: Field title in blacklist plugin administration panel.
+                             _m('Patterns of nicknames to block, one per line.'));
+        $this->out->elementEnd('li');
+
+        $urlPatterns = Homepage_blacklist::getPatterns();
+
+        $this->out->elementStart('li');
+        // TRANS: Field label in blacklist plugin administration panel.
+        $this->out->textarea('blacklist-urls', _m('URLs'),
+                             implode("\r\n", $urlPatterns),
+                             // TRANS: Field title in blacklist plugin administration panel.
+                             _m('Patterns of URLs to block, one per line.'));
+        $this->out->elementEnd('li');
+
+        $this->out->elementEnd('ul');
+    }
+
+    /**
+     * Buttons for submitting
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        $this->out->submit('submit',
+                           // TRANS: Button text in blacklist plugin administration panel to save settings.
+                           _m('BUTTON','Save'),
+                           'submit',
+                           null,
+                           // TRANS: Button title in blacklist plugin administration panel to save settings.
+                           _m('Save site settings.'));
+    }
+}
diff --git a/plugins/Blacklist/blacklistadminpanel.php b/plugins/Blacklist/blacklistadminpanel.php
deleted file mode 100644 (file)
index ee1c213..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Blacklist administration panel
- *
- * 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  Settings
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Administer blacklist
- *
- * @category Admin
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
- * @link     http://status.net/
- */
-class BlacklistadminpanelAction extends AdminPanelAction
-{
-    /**
-     * title of the admin panel
-     *
-     * @return string title
-     */
-    function title()
-    {
-        // TRANS: Title of blacklist plugin administration panel.
-        return _m('TITLE','Blacklist');
-    }
-
-    /**
-     * Panel instructions
-     *
-     * @return string instructions
-     */
-    function getInstructions()
-    {
-        // TRANS: Instructions for blacklist plugin administration panel.
-        return _m('Blacklisted URLs and nicknames');
-    }
-
-    /**
-     * Show the actual form
-     *
-     * @return void
-     *
-     * @see BlacklistAdminPanelForm
-     */
-    function showForm()
-    {
-        $form = new BlacklistAdminPanelForm($this);
-        $form->show();
-        return;
-    }
-
-    /**
-     * Save the form settings
-     *
-     * @return void
-     */
-    function saveSettings()
-    {
-        $nickPatterns = $this->splitPatterns($this->trimmed('blacklist-nicknames'));
-        Nickname_blacklist::saveNew($nickPatterns);
-
-        $urlPatterns = $this->splitPatterns($this->trimmed('blacklist-urls'));
-        Homepage_blacklist::saveNew($urlPatterns);
-
-        return;
-    }
-
-    protected function splitPatterns($text)
-    {
-        $patterns = array();
-        foreach (explode("\n", $text) as $raw) {
-            $trimmed = trim($raw);
-            if ($trimmed != '') {
-                $patterns[] = $trimmed;
-            }
-        }
-        return $patterns;
-    }
-
-    /**
-     * Validate the values
-     *
-     * @param array &$values 2d array of values to check
-     *
-     * @return boolean success flag
-     */
-    function validate(&$values)
-    {
-        return true;
-    }
-}
-
-/**
- * Admin panel form for blacklist panel
- *
- * @category Admin
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
- * @link     http://status.net/
- */
-class BlacklistAdminPanelForm extends Form
-{
-    /**
-     * ID of the form
-     *
-     * @return string ID
-     */
-    function id()
-    {
-        return 'blacklistadminpanel';
-    }
-
-    /**
-     * Class of the form
-     *
-     * @return string class
-     */
-    function formClass()
-    {
-        return 'form_settings';
-    }
-
-    /**
-     * Action we post to
-     *
-     * @return string action URL
-     */
-    function action()
-    {
-        return common_local_url('blacklistadminpanel');
-    }
-
-    /**
-     * Show the form controls
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->out->elementStart('li');
-
-        $nickPatterns = Nickname_blacklist::getPatterns();
-
-        // TRANS: Field label in blacklist plugin administration panel.
-        $this->out->textarea('blacklist-nicknames', _m('Nicknames'),
-                             implode("\r\n", $nickPatterns),
-                             // TRANS: Field title in blacklist plugin administration panel.
-                             _m('Patterns of nicknames to block, one per line.'));
-        $this->out->elementEnd('li');
-
-        $urlPatterns = Homepage_blacklist::getPatterns();
-
-        $this->out->elementStart('li');
-        // TRANS: Field label in blacklist plugin administration panel.
-        $this->out->textarea('blacklist-urls', _m('URLs'),
-                             implode("\r\n", $urlPatterns),
-                             // TRANS: Field title in blacklist plugin administration panel.
-                             _m('Patterns of URLs to block, one per line.'));
-        $this->out->elementEnd('li');
-
-        $this->out->elementEnd('ul');
-    }
-
-    /**
-     * Buttons for submitting
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        $this->out->submit('submit',
-                           // TRANS: Button text in blacklist plugin administration panel to save settings.
-                           _m('BUTTON','Save'),
-                           'submit',
-                           null,
-                           // TRANS: Button title in blacklist plugin administration panel to save settings.
-                           _m('Save site settings.'));
-    }
-}
diff --git a/plugins/Blacklist/classes/Homepage_blacklist.php b/plugins/Blacklist/classes/Homepage_blacklist.php
new file mode 100644 (file)
index 0000000..fb2712f
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+/**
+ * Data class for homepage blacklisting
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for Homepage blacklist
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Homepage_blacklist extends Managed_DataObject
+{
+    public $__table = 'homepage_blacklist'; // table name
+    public $pattern;                        // varchar(255) pattern
+    public $created;                        // datetime not_null
+    public $modified;                       // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'pattern' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'blacklist pattern'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('pattern'),
+        );
+    }
+
+    /**
+     * Return a list of patterns to check
+     *
+     * @return array string patterns to check
+     */
+    static function getPatterns()
+    {
+        $patterns = self::cacheGet('homepage_blacklist:patterns');
+
+        if ($patterns === false) {
+
+            $patterns = array();
+
+            $nb = new Homepage_blacklist();
+
+            $nb->find();
+
+            while ($nb->fetch()) {
+                $patterns[] = $nb->pattern;
+            }
+
+            self::cacheSet('homepage_blacklist:patterns', $patterns);
+        }
+
+        return $patterns;
+    }
+
+    /**
+     * Save new list of patterns
+     *
+     * @return array of patterns to check
+     */
+    static function saveNew($newPatterns)
+    {
+        $oldPatterns = self::getPatterns();
+
+        // Delete stuff that's old that not in new
+        $toDelete = array_diff($oldPatterns, $newPatterns);
+
+        // Insert stuff that's in new and not in old
+        $toInsert = array_diff($newPatterns, $oldPatterns);
+
+        foreach ($toDelete as $pattern) {
+            $nb = Homepage_blacklist::getKV('pattern', $pattern);
+            if (!empty($nb)) {
+                $nb->delete();
+            }
+        }
+
+        foreach ($toInsert as $pattern) {
+            $nb = new Homepage_blacklist();
+            $nb->pattern = $pattern;
+            $nb->created = common_sql_now();
+            $nb->insert();
+        }
+
+        self::blow('homepage_blacklist:patterns');
+    }
+
+    static function ensurePattern($pattern)
+    {
+        $hb = Homepage_blacklist::getKV('pattern', $pattern);
+
+        if (empty($nb)) {
+            $hb = new Homepage_blacklist();
+            $hb->pattern = $pattern;
+            $hb->created = common_sql_now();
+            $hb->insert();
+            self::blow('homepage_blacklist:patterns');
+        }
+    }
+}
diff --git a/plugins/Blacklist/classes/Nickname_blacklist.php b/plugins/Blacklist/classes/Nickname_blacklist.php
new file mode 100644 (file)
index 0000000..f4f387a
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+/**
+ * Data class for nickname blacklisting
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for Nickname blacklist
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Nickname_blacklist extends Managed_DataObject
+{
+    public $__table = 'nickname_blacklist'; // table name
+    public $pattern;                        // varchar(255) pattern
+    public $created;                        // datetime not_null
+    public $modified;                       // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'pattern' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'blacklist pattern'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('pattern'),
+        );
+    }
+
+    /**
+     * Return a list of patterns to check
+     *
+     * @return array string patterns to check
+     */
+    static function getPatterns()
+    {
+        $patterns = self::cacheGet('nickname_blacklist:patterns');
+
+        if ($patterns === false) {
+
+            $patterns = array();
+
+            $nb = new Nickname_blacklist();
+
+            $nb->find();
+
+            while ($nb->fetch()) {
+                $patterns[] = $nb->pattern;
+            }
+
+            self::cacheSet('nickname_blacklist:patterns', $patterns);
+        }
+
+        return $patterns;
+    }
+
+    /**
+     * Save new list of patterns
+     *
+     * @return array of patterns to check
+     */
+    static function saveNew($newPatterns)
+    {
+        $oldPatterns = self::getPatterns();
+
+        // Delete stuff that's old that not in new
+        $toDelete = array_diff($oldPatterns, $newPatterns);
+
+        // Insert stuff that's in new and not in old
+        $toInsert = array_diff($newPatterns, $oldPatterns);
+
+        foreach ($toDelete as $pattern) {
+            $nb = Nickname_blacklist::getKV('pattern', $pattern);
+            if (!empty($nb)) {
+                $nb->delete();
+            }
+        }
+
+        foreach ($toInsert as $pattern) {
+            $nb = new Nickname_blacklist();
+            $nb->pattern = $pattern;
+            $nb->created = common_sql_now();
+            $nb->insert();
+        }
+
+        self::blow('nickname_blacklist:patterns');
+    }
+
+    static function ensurePattern($pattern)
+    {
+        $nb = Nickname_blacklist::getKV('pattern', $pattern);
+
+        if (empty($nb)) {
+            $nb = new Nickname_blacklist();
+            $nb->pattern = $pattern;
+            $nb->created = common_sql_now();
+            $nb->insert();
+            self::blow('nickname_blacklist:patterns');
+        }
+    }
+}
index eb1f5833c49eaf754692b935e7fbaf0b5b58a4ec..7fa23461d616e8597643133a6d6cdd1c4ca193f2 100644 (file)
@@ -67,35 +67,6 @@ class BlogPlugin extends MicroAppPlugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'NewblogentryAction':
-        case 'ShowblogentryAction':
-            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'BlogEntryForm':
-        case 'BlogEntryListItem':
-            include_once $dir . '/'.strtolower($cls).'.php';
-            return false;
-        case 'Blog_entry':
-            include_once $dir . '/'.$cls.'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Map URLs to actions
      *
diff --git a/plugins/Blog/Blog_entry.php b/plugins/Blog/Blog_entry.php
deleted file mode 100644 (file)
index b7de821..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Data structure for blog entries
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Blog
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Data structure for blog entries
- *
- * @category  Blog
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class Blog_entry extends Managed_DataObject
-{
-    public $__table = 'blog_entry';
-
-    public $id; // UUID
-    public $profile_id; // int
-    public $title; // varchar(255)
-    public $summary; // text
-    public $content; // text
-    public $uri; // text
-    public $url; // text
-    public $created; // datetime
-    public $modified; // datetime
-
-    const TYPE = ActivityObject::ARTICLE;
-
-    static function schemaDef()
-    {
-        return array(
-            'description' => 'lite blog entry',
-            'fields' => array(
-                'id' => array('type' => 'char',
-                              'length' => 36,
-                              'not null' => true,
-                              'description' => 'Unique ID (UUID)'),
-                'profile_id' => array('type' => 'int',
-                                      'not null' => true,
-                                      'description' => 'Author profile ID'),
-                'title' => array('type' => 'varchar',
-                                 'length' => 255,
-                                 'description' => 'title of the entry'),
-                'summary' => array('type' => 'text',
-                                   'description' => 'initial summary'),
-                'content' => array('type' => 'text',
-                                   'description' => 'HTML content of the entry'),
-                'uri' => array('type' => 'varchar',
-                               'length' => 255,
-                               'description' => 'URI (probably http://) for this entry'),
-                'url' => array('type' => 'varchar',
-                               'length' => 255,
-                               'description' => 'URL (probably http://) for this entry'),
-                'created' => array('type' => 'datetime',
-                                   'not null' => true,
-                                   'description' => 'date this record was created'),
-                'modified' => array('type' => 'datetime',
-                                    'not null' => true,
-                                    'description' => 'date this record was created'),
-            ),
-            'primary key' => array('id'),
-            'unique keys' => array(
-                'blog_entry_uri_key' => array('uri'),
-            ),
-            'foreign keys' => array(
-                'blog_entry_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
-            ),
-            'indexes' => array(
-                'blog_entry_created_idx' => array('created')
-            ),
-        );
-    }
-
-    static function saveNew($profile, $title, $content, $options=null)
-    {
-        if (is_null($options)) {
-            $options = array();
-        }
-
-        $be             = new Blog_entry();
-        $be->id         = (string) new UUID();
-        $be->profile_id = $profile->id;
-        $be->title      = $title; // Note: not HTML-protected
-        $be->content    = self::purify($content);
-
-        if (array_key_exists('summary', $options)) {
-            $be->summary = self::purify($options['summary']);
-        } else {
-            // Already purified
-            $be->summary = self::summarize($be->content);
-        }
-
-        // Don't save an identical summary
-
-        if ($be->summary == $be->content) {
-            $be->summary = null;
-        }
-
-        $url = common_local_url('showblogentry', array('id' => $be->id));
-
-        if (!array_key_exists('uri', $options)) {
-            $options['uri'] = $url;
-        } 
-
-        $be->uri = $options['uri'];
-
-        if (!array_key_exists('url', $options)) {
-            $options['url'] = $url;
-        }
-
-        $be->url = $options['url'];
-
-        if (!array_key_exists('created', $options)) {
-            $be->created = common_sql_now();
-        }
-        
-        $be->created = $options['created'];
-
-        $be->modified = common_sql_now();
-
-        $be->insert();
-
-        // Use user's preferences for short URLs, if possible
-
-        try {
-            $user = $profile->getUser();
-            $shortUrl = File_redirection::makeShort($url,
-                                                    empty($user) ? null : $user);
-        } catch (Exception $e) {
-            // Don't let this stop us.
-            $shortUrl = $url;
-        }
-
-        // XXX: this might be too long.
-
-        if (!empty($be->summary)) {
-            $options['rendered'] = $be->summary . ' ' . 
-                XMLStringer::estring('a', array('href' => $url,
-                                                'class' => 'blog-entry'),
-                                     _('More...'));
-            $text = html_entity_decode(strip_tags($be->summary), ENT_QUOTES, 'UTF-8');
-        } else {
-            $options['rendered'] = $be->content;
-            $text = html_entity_decode(strip_tags($be->content), ENT_QUOTES, 'UTF-8');
-        }
-
-
-        if (Notice::contentTooLong($text)) {
-            $text = substr($text, 0, Notice::maxContent() - mb_strlen($shortUrl) - 2) .
-                '… ' . $shortUrl;
-        }
-
-        // Override this no matter what.
-        
-        $options['object_type'] = self::TYPE;
-
-        $source = array_key_exists('source', $options) ?
-                                    $options['source'] : 'web';
-        
-        $saved = Notice::saveNew($profile->id, $text, $source, $options);
-
-        return $saved;
-    }
-
-    /**
-     * Summarize the contents of a blog post
-     *
-     * We take the first div or paragraph of the blog post if there's a hit;
-     * Otherwise we take the whole thing.
-     * 
-     * @param string $html HTML of full content
-     */
-    static function summarize($html)
-    {
-        if (preg_match('#<p>.*?</p>#s', $html, $matches)) {
-            return $matches[0];
-        } else if (preg_match('#<div>.*?</div>#s', $html, $matches)) {
-            return $matches[0];
-        } else {
-            return $html;
-        }
-    }
-
-    static function fromNotice($notice)
-    {
-        return Blog_entry::getKV('uri', $notice->uri);
-    }
-
-    function getNotice()
-    {
-        return Notice::getKV('uri', $this->uri);
-    }
-
-    function asActivityObject()
-    {
-        $obj = new ActivityObject();
-
-        $obj->id      = $this->uri;
-        $obj->type    = self::TYPE;
-        $obj->title   = $this->title;
-        $obj->summary = $this->summary;
-        $obj->content = $this->content;
-        $obj->link    = $this->url;
-
-        return $obj;
-    }
-
-    /**
-     * Clean up input HTML
-     */
-    static function purify($html)
-    {
-        require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
-
-        $config = array('safe' => 1,
-                        'deny_attribute' => 'id,style,on*');
-        $pure = htmLawed($html, $config);
-
-        return $pure;
-    }
-}
diff --git a/plugins/Blog/actions/newblogentry.php b/plugins/Blog/actions/newblogentry.php
new file mode 100644 (file)
index 0000000..d6421ab
--- /dev/null
@@ -0,0 +1,139 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Save a new blog entry
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Blog
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Save a new blog entry
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class NewblogentryAction extends Action
+{
+    protected $user;
+    protected $title;
+    protected $content;
+    
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        if (!$this->isPost()) {
+            throw new ClientException(_('Must be a POST.'), 405);
+        }
+
+        $this->user = common_current_user();
+
+        if (empty($this->user)) {
+            // TRANS: Client exception thrown when trying to post a blog entry while not logged in.
+            throw new ClientException(_m('Must be logged in to post a blog entry.'),
+                                      403);
+        }
+
+        $this->checkSessionToken();
+        
+        $this->title = $this->trimmed('title');
+
+        if (empty($this->title)) {
+            // TRANS: Client exception thrown when trying to post a blog entry without providing a title.
+            throw new ClientException(_m('Title required.'));
+        }
+
+        $this->content = $this->trimmed('content');
+
+        if (empty($this->content)) {
+            // TRANS: Client exception thrown when trying to post a blog entry without providing content.
+            throw new ClientException(_m('Content required.'));
+        }
+        
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+
+    function handle($argarray=null)
+    {
+        $options = array();
+
+        // Does the heavy-lifting for getting "To:" information
+
+        ToSelector::fillOptions($this, $options);
+
+        $options['source'] = 'web';
+            
+        $profile = $this->user->getProfile();
+
+        $saved = Blog_entry::saveNew($profile,
+                                    $this->title,
+                                    $this->content,
+                                    $options);
+        
+        if ($this->boolean('ajax')) {
+            header('Content-Type: text/xml; charset=utf-8');
+            $this->xw->startDocument('1.0', 'UTF-8');
+            $this->elementStart('html');
+            $this->elementStart('head');
+            // TRANS: Page title after sending a notice.
+            $this->element('title', null, _m('Blog entry saved'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $nli = new NoticeListItem($saved, $this);
+            $nli->show();
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            common_redirect($saved->bestUrl(), 303);
+        }
+    }
+}
diff --git a/plugins/Blog/actions/showblogentry.php b/plugins/Blog/actions/showblogentry.php
new file mode 100644 (file)
index 0000000..b46a65e
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Show a blog entry
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Blog
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Show a blog entry
+ *
+ * @category  Blog
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class ShowblogentryAction extends ShownoticeAction
+{
+    protected $id;
+    protected $entry;
+
+    function getNotice()
+    {
+        $this->id = $this->trimmed('id');
+
+        $this->entry = Blog_entry::getKV('id', $this->id);
+
+        if (empty($this->entry)) {
+            // TRANS: Client exception thrown when referring to a non-existing blog entry.
+            throw new ClientException(_m('No such entry.'), 404);
+        }
+
+        $notice = $this->entry->getNotice();
+
+        if (empty($notice)) {
+            // TRANS: Client exception thrown when referring to a non-existing blog entry.
+            throw new ClientException(_m('No such entry.'), 404);
+        }
+
+        return $notice;
+    }
+
+    /**
+     * Title of the page
+     *
+     * Used by Action class for layout.
+     *
+     * @return string page tile
+     */
+    function title()
+    {
+        // XXX: check for double-encoding
+        // TRANS: Title for a blog entry without a title.
+        return (empty($this->entry->title)) ? _m('Untitled') : $this->entry->title;
+    }
+}
diff --git a/plugins/Blog/blogentryform.php b/plugins/Blog/blogentryform.php
deleted file mode 100644 (file)
index 2da2a13..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Form for creating a blog entry
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Blog
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form for creating a blog entry
- *
- * @category  Blog
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class BlogEntryForm extends Form
-{
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'form_new_blog_entry';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings ajax-notice';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('newblogentry');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('fieldset', array('id' => 'new_blog_entry_data'));
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-        $this->out->input('blog-entry-title',
-                          // TRANS: Field label on blog entry form.
-                          _m('LABEL','Title'),
-                          null,
-                          // TRANS: Field title on blog entry form.
-                          _m('Title of the blog entry.'),
-                          'title');
-        $this->unli();
-
-        $this->li();
-        $this->out->textarea('blog-entry-content',
-                             // TRANS: Field label on blog entry form.
-                             _m('LABEL','Text'),
-                            null,
-                            // TRANS: Field title on blog entry form.
-                            _m('Text of the blog entry.'),
-                            'content');
-        $this->unli();
-
-        $this->out->elementEnd('ul');
-
-        $toWidget = new ToSelector($this->out,
-                                   common_current_user(),
-                                   null);
-        $toWidget->show();
-
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        $this->out->submit('blog-entry-submit',
-                           // TRANS: Button text to save a blog entry.
-                           _m('BUTTON', 'Save'),
-                           'submit',
-                           'submit');
-    }
-}
diff --git a/plugins/Blog/blogentrylistitem.php b/plugins/Blog/blogentrylistitem.php
deleted file mode 100644 (file)
index a89a562..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * NoticeListItem adapter for blog entries
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Blog
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * NoticeListItem adapter for blog entries
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class BlogEntryListItem extends NoticeListItemAdapter
-{
-    function showNotice()
-    {
-        $out = $this->nli->out;
-        $out->elementStart('div', 'entry-title');
-        $this->showAuthor();
-        $this->showContent();
-        $out->elementEnd('div');
-    }
-
-    function showContent()
-    {
-        $notice = $this->nli->notice;
-        $out    = $this->nli->out;
-
-        $entry  = Blog_entry::fromNotice($notice);
-
-        if (empty($entry)) {
-            throw new Exception('BlogEntryListItem used for non-blog notice.');
-        }
-
-        $out->elementStart('h4', array('class' => 'blog-entry-title'));
-        $out->element('a', array('href' => $notice->bestUrl()), $entry->title);
-        $out->elementEnd('h4');
-
-        // XXX: kind of a hack
-
-        $actionName = $out->trimmed('action');
-
-        if ($actionName == 'shownotice' ||
-            $actionName == 'showblogentry' ||
-            $actionName == 'conversation') {
-
-            $out->elementStart('div', 'blog-entry-content');
-            $out->raw($entry->content);
-            $out->elementEnd('div');
-
-        } else {
-
-            if (!empty($entry->summary)) {
-                $out->elementStart('div', 'blog-entry-summary');
-                $out->raw($entry->summary);
-                $out->elementEnd('div');
-            }
-
-            $url = ($entry->url) ? $entry->url : $notice->bestUrl();
-            $out->element('a',
-                          array('href' => $url,
-                                'class' => 'blog-entry-link'),
-                          _('More...'));
-        }
-    }
-}
diff --git a/plugins/Blog/classes/Blog_entry.php b/plugins/Blog/classes/Blog_entry.php
new file mode 100644 (file)
index 0000000..b7de821
--- /dev/null
@@ -0,0 +1,256 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Data structure for blog entries
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Blog
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Data structure for blog entries
+ *
+ * @category  Blog
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class Blog_entry extends Managed_DataObject
+{
+    public $__table = 'blog_entry';
+
+    public $id; // UUID
+    public $profile_id; // int
+    public $title; // varchar(255)
+    public $summary; // text
+    public $content; // text
+    public $uri; // text
+    public $url; // text
+    public $created; // datetime
+    public $modified; // datetime
+
+    const TYPE = ActivityObject::ARTICLE;
+
+    static function schemaDef()
+    {
+        return array(
+            'description' => 'lite blog entry',
+            'fields' => array(
+                'id' => array('type' => 'char',
+                              'length' => 36,
+                              'not null' => true,
+                              'description' => 'Unique ID (UUID)'),
+                'profile_id' => array('type' => 'int',
+                                      'not null' => true,
+                                      'description' => 'Author profile ID'),
+                'title' => array('type' => 'varchar',
+                                 'length' => 255,
+                                 'description' => 'title of the entry'),
+                'summary' => array('type' => 'text',
+                                   'description' => 'initial summary'),
+                'content' => array('type' => 'text',
+                                   'description' => 'HTML content of the entry'),
+                'uri' => array('type' => 'varchar',
+                               'length' => 255,
+                               'description' => 'URI (probably http://) for this entry'),
+                'url' => array('type' => 'varchar',
+                               'length' => 255,
+                               'description' => 'URL (probably http://) for this entry'),
+                'created' => array('type' => 'datetime',
+                                   'not null' => true,
+                                   'description' => 'date this record was created'),
+                'modified' => array('type' => 'datetime',
+                                    'not null' => true,
+                                    'description' => 'date this record was created'),
+            ),
+            'primary key' => array('id'),
+            'unique keys' => array(
+                'blog_entry_uri_key' => array('uri'),
+            ),
+            'foreign keys' => array(
+                'blog_entry_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
+            ),
+            'indexes' => array(
+                'blog_entry_created_idx' => array('created')
+            ),
+        );
+    }
+
+    static function saveNew($profile, $title, $content, $options=null)
+    {
+        if (is_null($options)) {
+            $options = array();
+        }
+
+        $be             = new Blog_entry();
+        $be->id         = (string) new UUID();
+        $be->profile_id = $profile->id;
+        $be->title      = $title; // Note: not HTML-protected
+        $be->content    = self::purify($content);
+
+        if (array_key_exists('summary', $options)) {
+            $be->summary = self::purify($options['summary']);
+        } else {
+            // Already purified
+            $be->summary = self::summarize($be->content);
+        }
+
+        // Don't save an identical summary
+
+        if ($be->summary == $be->content) {
+            $be->summary = null;
+        }
+
+        $url = common_local_url('showblogentry', array('id' => $be->id));
+
+        if (!array_key_exists('uri', $options)) {
+            $options['uri'] = $url;
+        } 
+
+        $be->uri = $options['uri'];
+
+        if (!array_key_exists('url', $options)) {
+            $options['url'] = $url;
+        }
+
+        $be->url = $options['url'];
+
+        if (!array_key_exists('created', $options)) {
+            $be->created = common_sql_now();
+        }
+        
+        $be->created = $options['created'];
+
+        $be->modified = common_sql_now();
+
+        $be->insert();
+
+        // Use user's preferences for short URLs, if possible
+
+        try {
+            $user = $profile->getUser();
+            $shortUrl = File_redirection::makeShort($url,
+                                                    empty($user) ? null : $user);
+        } catch (Exception $e) {
+            // Don't let this stop us.
+            $shortUrl = $url;
+        }
+
+        // XXX: this might be too long.
+
+        if (!empty($be->summary)) {
+            $options['rendered'] = $be->summary . ' ' . 
+                XMLStringer::estring('a', array('href' => $url,
+                                                'class' => 'blog-entry'),
+                                     _('More...'));
+            $text = html_entity_decode(strip_tags($be->summary), ENT_QUOTES, 'UTF-8');
+        } else {
+            $options['rendered'] = $be->content;
+            $text = html_entity_decode(strip_tags($be->content), ENT_QUOTES, 'UTF-8');
+        }
+
+
+        if (Notice::contentTooLong($text)) {
+            $text = substr($text, 0, Notice::maxContent() - mb_strlen($shortUrl) - 2) .
+                '… ' . $shortUrl;
+        }
+
+        // Override this no matter what.
+        
+        $options['object_type'] = self::TYPE;
+
+        $source = array_key_exists('source', $options) ?
+                                    $options['source'] : 'web';
+        
+        $saved = Notice::saveNew($profile->id, $text, $source, $options);
+
+        return $saved;
+    }
+
+    /**
+     * Summarize the contents of a blog post
+     *
+     * We take the first div or paragraph of the blog post if there's a hit;
+     * Otherwise we take the whole thing.
+     * 
+     * @param string $html HTML of full content
+     */
+    static function summarize($html)
+    {
+        if (preg_match('#<p>.*?</p>#s', $html, $matches)) {
+            return $matches[0];
+        } else if (preg_match('#<div>.*?</div>#s', $html, $matches)) {
+            return $matches[0];
+        } else {
+            return $html;
+        }
+    }
+
+    static function fromNotice($notice)
+    {
+        return Blog_entry::getKV('uri', $notice->uri);
+    }
+
+    function getNotice()
+    {
+        return Notice::getKV('uri', $this->uri);
+    }
+
+    function asActivityObject()
+    {
+        $obj = new ActivityObject();
+
+        $obj->id      = $this->uri;
+        $obj->type    = self::TYPE;
+        $obj->title   = $this->title;
+        $obj->summary = $this->summary;
+        $obj->content = $this->content;
+        $obj->link    = $this->url;
+
+        return $obj;
+    }
+
+    /**
+     * Clean up input HTML
+     */
+    static function purify($html)
+    {
+        require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
+
+        $config = array('safe' => 1,
+                        'deny_attribute' => 'id,style,on*');
+        $pure = htmLawed($html, $config);
+
+        return $pure;
+    }
+}
diff --git a/plugins/Blog/forms/blogentry.php b/plugins/Blog/forms/blogentry.php
new file mode 100644 (file)
index 0000000..2da2a13
--- /dev/null
@@ -0,0 +1,132 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Form for creating a blog entry
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Blog
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form for creating a blog entry
+ *
+ * @category  Blog
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class BlogEntryForm extends Form
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'form_new_blog_entry';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings ajax-notice';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('newblogentry');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('fieldset', array('id' => 'new_blog_entry_data'));
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->out->input('blog-entry-title',
+                          // TRANS: Field label on blog entry form.
+                          _m('LABEL','Title'),
+                          null,
+                          // TRANS: Field title on blog entry form.
+                          _m('Title of the blog entry.'),
+                          'title');
+        $this->unli();
+
+        $this->li();
+        $this->out->textarea('blog-entry-content',
+                             // TRANS: Field label on blog entry form.
+                             _m('LABEL','Text'),
+                            null,
+                            // TRANS: Field title on blog entry form.
+                            _m('Text of the blog entry.'),
+                            'content');
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+
+        $toWidget = new ToSelector($this->out,
+                                   common_current_user(),
+                                   null);
+        $toWidget->show();
+
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        $this->out->submit('blog-entry-submit',
+                           // TRANS: Button text to save a blog entry.
+                           _m('BUTTON', 'Save'),
+                           'submit',
+                           'submit');
+    }
+}
diff --git a/plugins/Blog/lib/blogentrylistitem.php b/plugins/Blog/lib/blogentrylistitem.php
new file mode 100644 (file)
index 0000000..a89a562
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * NoticeListItem adapter for blog entries
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Blog
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * NoticeListItem adapter for blog entries
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class BlogEntryListItem extends NoticeListItemAdapter
+{
+    function showNotice()
+    {
+        $out = $this->nli->out;
+        $out->elementStart('div', 'entry-title');
+        $this->showAuthor();
+        $this->showContent();
+        $out->elementEnd('div');
+    }
+
+    function showContent()
+    {
+        $notice = $this->nli->notice;
+        $out    = $this->nli->out;
+
+        $entry  = Blog_entry::fromNotice($notice);
+
+        if (empty($entry)) {
+            throw new Exception('BlogEntryListItem used for non-blog notice.');
+        }
+
+        $out->elementStart('h4', array('class' => 'blog-entry-title'));
+        $out->element('a', array('href' => $notice->bestUrl()), $entry->title);
+        $out->elementEnd('h4');
+
+        // XXX: kind of a hack
+
+        $actionName = $out->trimmed('action');
+
+        if ($actionName == 'shownotice' ||
+            $actionName == 'showblogentry' ||
+            $actionName == 'conversation') {
+
+            $out->elementStart('div', 'blog-entry-content');
+            $out->raw($entry->content);
+            $out->elementEnd('div');
+
+        } else {
+
+            if (!empty($entry->summary)) {
+                $out->elementStart('div', 'blog-entry-summary');
+                $out->raw($entry->summary);
+                $out->elementEnd('div');
+            }
+
+            $url = ($entry->url) ? $entry->url : $notice->bestUrl();
+            $out->element('a',
+                          array('href' => $url,
+                                'class' => 'blog-entry-link'),
+                          _('More...'));
+        }
+    }
+}
diff --git a/plugins/Blog/newblogentry.php b/plugins/Blog/newblogentry.php
deleted file mode 100644 (file)
index d6421ab..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Save a new blog entry
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Blog
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Save a new blog entry
- *
- * @category  Action
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class NewblogentryAction extends Action
-{
-    protected $user;
-    protected $title;
-    protected $content;
-    
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        if (!$this->isPost()) {
-            throw new ClientException(_('Must be a POST.'), 405);
-        }
-
-        $this->user = common_current_user();
-
-        if (empty($this->user)) {
-            // TRANS: Client exception thrown when trying to post a blog entry while not logged in.
-            throw new ClientException(_m('Must be logged in to post a blog entry.'),
-                                      403);
-        }
-
-        $this->checkSessionToken();
-        
-        $this->title = $this->trimmed('title');
-
-        if (empty($this->title)) {
-            // TRANS: Client exception thrown when trying to post a blog entry without providing a title.
-            throw new ClientException(_m('Title required.'));
-        }
-
-        $this->content = $this->trimmed('content');
-
-        if (empty($this->content)) {
-            // TRANS: Client exception thrown when trying to post a blog entry without providing content.
-            throw new ClientException(_m('Content required.'));
-        }
-        
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-
-    function handle($argarray=null)
-    {
-        $options = array();
-
-        // Does the heavy-lifting for getting "To:" information
-
-        ToSelector::fillOptions($this, $options);
-
-        $options['source'] = 'web';
-            
-        $profile = $this->user->getProfile();
-
-        $saved = Blog_entry::saveNew($profile,
-                                    $this->title,
-                                    $this->content,
-                                    $options);
-        
-        if ($this->boolean('ajax')) {
-            header('Content-Type: text/xml; charset=utf-8');
-            $this->xw->startDocument('1.0', 'UTF-8');
-            $this->elementStart('html');
-            $this->elementStart('head');
-            // TRANS: Page title after sending a notice.
-            $this->element('title', null, _m('Blog entry saved'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $nli = new NoticeListItem($saved, $this);
-            $nli->show();
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            common_redirect($saved->bestUrl(), 303);
-        }
-    }
-}
diff --git a/plugins/Blog/showblogentry.php b/plugins/Blog/showblogentry.php
deleted file mode 100644 (file)
index b46a65e..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Show a blog entry
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Blog
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Show a blog entry
- *
- * @category  Blog
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class ShowblogentryAction extends ShownoticeAction
-{
-    protected $id;
-    protected $entry;
-
-    function getNotice()
-    {
-        $this->id = $this->trimmed('id');
-
-        $this->entry = Blog_entry::getKV('id', $this->id);
-
-        if (empty($this->entry)) {
-            // TRANS: Client exception thrown when referring to a non-existing blog entry.
-            throw new ClientException(_m('No such entry.'), 404);
-        }
-
-        $notice = $this->entry->getNotice();
-
-        if (empty($notice)) {
-            // TRANS: Client exception thrown when referring to a non-existing blog entry.
-            throw new ClientException(_m('No such entry.'), 404);
-        }
-
-        return $notice;
-    }
-
-    /**
-     * Title of the page
-     *
-     * Used by Action class for layout.
-     *
-     * @return string page tile
-     */
-    function title()
-    {
-        // XXX: check for double-encoding
-        // TRANS: Title for a blog entry without a title.
-        return (empty($this->entry->title)) ? _m('Untitled') : $this->entry->title;
-    }
-}
diff --git a/plugins/Bookmark/Bookmark.php b/plugins/Bookmark/Bookmark.php
deleted file mode 100644 (file)
index 65c767e..0000000
+++ /dev/null
@@ -1,296 +0,0 @@
-<?php
-/**
- * Data class to mark notices as bookmarks
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * For storing the fact that a notice is a bookmark
- *
- * @category Bookmark
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Bookmark extends Managed_DataObject
-{
-    public $__table = 'bookmark'; // table name
-    public $id;          // char(36) primary_key not_null
-    public $profile_id;  // int(4) not_null
-    public $url;         // varchar(255) not_null
-    public $title;       // varchar(255)
-    public $description; // text
-    public $uri;         // varchar(255)
-    public $created;     // datetime
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'id' => array('type' => 'char',
-                            'length' => 36,
-                            'not null' => true),
-                'profile_id' => array('type' => 'int', 'not null' => true),
-                'uri' => array('type' => 'varchar',
-                            'length' => 255,
-                            'not null' => true),
-                'url' => array('type' => 'varchar',
-                            'length' => 255,
-                            'not null' => true),
-                'title' => array('type' => 'varchar', 'length' => 255),
-                'description' => array('type' => 'text'),
-                'created' => array('type' => 'datetime', 'not null' => true),
-            ),
-            'primary key' => array('id'),
-            'unique keys' => array(
-                'bookmark_uri_key' => array('uri'),
-            ),
-            'foreign keys' => array(
-                'bookmark_profile_id_fkey' => array('profile', array('profile_id' => 'id'))
-            ),
-            'indexes' => array('bookmark_created_idx' => array('created'),
-                            'bookmark_url_idx' => array('url'),
-                            'bookmark_profile_id_idx' => array('profile_id'),
-            ),
-        );
-    }
-
-    /**
-     * Get a bookmark based on a notice
-     *
-     * @param Notice $notice Notice to check for
-     *
-     * @return Bookmark found bookmark or null
-     */
-    static function getByNotice($notice)
-    {
-        return self::getKV('uri', $notice->uri);
-    }
-
-    /**
-     * Get the bookmark that a user made for an URL
-     *
-     * @param Profile $profile Profile to check for
-     * @param string  $url     URL to check for
-     *
-     * @return Bookmark bookmark found or null
-     */
-    static function getByURL($profile, $url)
-    {
-        $nb = new Bookmark();
-
-        $nb->profile_id = $profile->id;
-        $nb->url        = $url;
-
-        if ($nb->find(true)) {
-            return $nb;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Save a new notice bookmark
-     *
-     * @param Profile $profile     To save the bookmark for
-     * @param string  $title       Title of the bookmark
-     * @param string  $url         URL of the bookmark
-     * @param mixed   $rawtags     array of tags or string
-     * @param string  $description Description of the bookmark
-     * @param array   $options     Options for the Notice::saveNew()
-     *
-     * @return Notice saved notice
-     */
-    static function saveNew($profile, $title, $url, $rawtags, $description,
-                            $options=null)
-    {
-        $nb = self::getByURL($profile, $url);
-
-        if (!empty($nb)) {
-            // TRANS: Client exception thrown when trying to save a new bookmark that already exists.
-            throw new ClientException(_m('Bookmark already exists.'));
-        }
-
-        if (empty($options)) {
-            $options = array();
-        }
-
-        if (array_key_exists('uri', $options)) {
-            $other = Bookmark::getKV('uri', $options['uri']);
-            if (!empty($other)) {
-                // TRANS: Client exception thrown when trying to save a new bookmark that already exists.
-                throw new ClientException(_m('Bookmark already exists.'));
-            }
-        }
-
-        if (is_string($rawtags)) {
-            if (empty($rawtags)) {
-                $rawtags = array();
-            } else {
-                $rawtags = preg_split('/[\s,]+/', $rawtags);
-            }
-        }
-
-        $nb = new Bookmark();
-
-        $nb->id          = UUID::gen();
-        $nb->profile_id  = $profile->id;
-        $nb->url         = $url;
-        $nb->title       = $title;
-        $nb->description = $description;
-
-        if (array_key_exists('created', $options)) {
-            $nb->created = $options['created'];
-        } else {
-            $nb->created = common_sql_now();
-        }
-
-        if (array_key_exists('uri', $options)) {
-            $nb->uri = $options['uri'];
-        } else {
-            // FIXME: hacks to work around router bugs in
-            // queue daemons
-
-            $r = Router::get();
-
-            $path = $r->build('showbookmark',
-                              array('id' => $nb->id));
-
-            if (empty($path)) {
-                $nb->uri = common_path('bookmark/'.$nb->id, false, false);
-            } else {
-                $nb->uri = common_local_url('showbookmark',
-                                            array('id' => $nb->id),
-                                            null,
-                                            null,
-                                            false);
-            }
-        }
-
-        $nb->insert();
-
-        $tags    = array();
-        $replies = array();
-
-        // filter "for:nickname" tags
-
-        foreach ($rawtags as $tag) {
-            if (strtolower(mb_substr($tag, 0, 4)) == 'for:') {
-                // skip if done by caller
-                if (!array_key_exists('replies', $options)) {
-                    $nickname = mb_substr($tag, 4);
-                    $other    = common_relative_profile($profile,
-                                                        $nickname);
-                    if (!empty($other)) {
-                        $replies[] = $other->getUri();
-                    }
-                }
-            } else {
-                $tags[] = common_canonical_tag($tag);
-            }
-        }
-
-        $hashtags = array();
-        $taglinks = array();
-
-        foreach ($tags as $tag) {
-            $hashtags[] = '#'.$tag;
-            $attrs      = array('href' => Notice_tag::url($tag),
-                                'rel'  => $tag,
-                                'class' => 'tag');
-            $taglinks[] = XMLStringer::estring('a', $attrs, $tag);
-        }
-
-        // Use user's preferences for short URLs, if possible
-
-        try {
-            $user = User::getKV('id', $profile->id);
-
-            $shortUrl = File_redirection::makeShort($url,
-                                                    empty($user) ? null : $user);
-        } catch (Exception $e) {
-            // Don't let this stop us.
-            $shortUrl = $url;
-        }
-
-        // TRANS: Bookmark content.
-        // TRANS: %1$s is a title, %2$s is a short URL, %3$s is the bookmark description,
-       // TRANS: %4$s is space separated list of hash tags.
-        $content = sprintf(_m('"%1$s" %2$s %3$s %4$s'),
-                           $title,
-                           $shortUrl,
-                           $description,
-                           implode(' ', $hashtags));
-
-        // TRANS: Rendered bookmark content.
-        // TRANS: %1$s is a URL, %2$s the bookmark title, %3$s is the bookmark description,
-       // TRANS: %4$s is space separated list of hash tags.
-        $rendered = sprintf(_m('<span class="xfolkentry">'.
-                              '<a class="taggedlink" href="%1$s">%2$s</a> '.
-                              '<span class="description">%3$s</span> '.
-                              '<span class="meta">%4$s</span>'.
-                              '</span>'),
-                            htmlspecialchars($url),
-                            htmlspecialchars($title),
-                            htmlspecialchars($description),
-                            implode(' ', $taglinks));
-
-        $options = array_merge(array('urls' => array($url),
-                                     'rendered' => $rendered,
-                                     'tags' => $tags,
-                                     'replies' => $replies,
-                                     'object_type' => ActivityObject::BOOKMARK),
-                               $options);
-
-        if (!array_key_exists('uri', $options)) {
-            $options['uri'] = $nb->uri;
-        }
-
-        try {
-            $saved = Notice::saveNew($profile->id,
-                                     $content,
-                                     array_key_exists('source', $options) ?
-                                     $options['source'] : 'web',
-                                     $options);
-        } catch (Exception $e) {
-            $nb->delete();
-            throw $e;
-        }
-
-        if (empty($saved)) {
-            $nb->delete();
-        }
-
-        return $saved;
-    }
-}
index c8408cf9713318a8faefc2bdc2dfa5e64ca48d84..6dacdb085e88a167492d50f64893d07ca1b31950 100644 (file)
@@ -135,44 +135,6 @@ class BookmarkPlugin extends MicroAppPlugin
         $action->script($this->path('js/bookmark.js'));
         return true;
     }
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'BookmarksAction':
-        case 'BookmarksrssAction':
-        case 'ApiTimelineBookmarksAction':
-        case 'ShowbookmarkAction':
-        case 'NewbookmarkAction':
-        case 'BookmarkpopupAction':
-        case 'NoticebyurlAction':
-        case 'BookmarkforurlAction':
-        case 'ImportdeliciousAction':
-            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'Bookmark':
-            include_once $dir.'/'.$cls.'.php';
-            return false;
-        case 'BookmarkListItem':
-        case 'BookmarkForm':
-        case 'InitialBookmarkForm':
-        case 'DeliciousBackupImporter':
-        case 'DeliciousBookmarkImporter':
-            include_once $dir.'/'.strtolower($cls).'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
 
     /**
      * Map URLs to actions
diff --git a/plugins/Bookmark/actions/apitimelinebookmarks.php b/plugins/Bookmark/actions/apitimelinebookmarks.php
new file mode 100644 (file)
index 0000000..34dbfa7
--- /dev/null
@@ -0,0 +1,267 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show a user's favorite notices
+ *
+ * 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  API
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @author    Evan Prodromou <evan@status.net>
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2009-2010 StatusNet, Inc.
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/lib/apibareauth.php';
+
+/**
+ * Returns the 20 most recent favorite notices for the authenticating user or user
+ * specified by the ID parameter in the requested format.
+ *
+ * @category API
+ * @package  StatusNet
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class ApiTimelineBookmarksAction extends ApiBareAuthAction
+{
+    var $notices  = null;
+
+    /**
+     * Take arguments for running
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     */
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $this->user = $this->getTargetUser($this->arg('id'));
+
+        if (empty($this->user)) {
+            // TRANS: Client error displayed when requesting most recent favourite notices by a user for a non-existing user.
+            $this->clientError(_('No such user.'), 404, $this->format);
+            return;
+        }
+
+        $this->notices = $this->getNotices();
+
+        return true;
+    }
+
+    /**
+     * Handle the request
+     *
+     * Just show the notices
+     *
+     * @param array $args $_REQUEST data (unused)
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+        $this->showTimeline();
+    }
+
+    /**
+     * Show the timeline of notices
+     *
+     * @return void
+     */
+    function showTimeline()
+    {
+        $profile  = $this->user->getProfile();
+        $avatar   = $profile->getAvatar(AVATAR_PROFILE_SIZE);
+
+        $sitename = common_config('site', 'name');
+        $title    = sprintf(
+            // TRANS: Title for timeline of most recent favourite notices by a user.
+            // TRANS: %1$s is the StatusNet sitename, %2$s is a user nickname.
+            _('%1$s / Bookmarks from %2$s'),
+            $sitename,
+            $this->user->nickname
+        );
+
+        $taguribase = TagURI::base();
+        $id         = "tag:$taguribase:Bookmarks:" . $this->user->id;
+
+        $subtitle = sprintf(
+            // TRANS: Subtitle for timeline of most recent favourite notices by a user.
+            // TRANS: %1$s is the StatusNet sitename, %2$s is a user's full name,
+            // TRANS: %3$s is a user nickname.
+            _('%1$s updates bookmarked by %2$s / %3$s.'),
+            $sitename,
+            $profile->getBestName(),
+            $this->user->nickname
+        );
+        $logo = !empty($avatar)
+            ? $avatar->displayUrl()
+            : Avatar::defaultImage(AVATAR_PROFILE_SIZE);
+
+        $link = common_local_url(
+            'bookmarks',
+            array('nickname' => $this->user->nickname)
+        );
+
+        $self = $this->getSelfUri();
+
+        switch($this->format) {
+        case 'xml':
+            $this->showXmlTimeline($this->notices);
+            break;
+        case 'rss':
+            $this->showRssTimeline(
+                $this->notices,
+                $title,
+                $link,
+                $subtitle,
+                null,
+                $logo,
+                $self
+            );
+            break;
+        case 'atom':
+            header('Content-Type: application/atom+xml; charset=utf-8');
+
+            $atom = new AtomNoticeFeed($this->auth_user);
+
+            $atom->setId($id);
+            $atom->setTitle($title);
+            $atom->setSubtitle($subtitle);
+            $atom->setLogo($logo);
+            $atom->setUpdated('now');
+
+            $atom->addLink($link);
+            $atom->setSelfLink($self);
+
+            $atom->addEntryFromNotices($this->notices);
+
+            $this->raw($atom->getString());
+            break;
+        case 'json':
+            $this->showJsonTimeline($this->notices);
+            break;
+        case 'as':
+            header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
+            $doc = new ActivityStreamJSONDocument($this->auth_user);
+            $doc->setTitle($title);
+            $doc->addLink($link,'alternate', 'text/html');
+            $doc->addItemsFromNotices($this->notices);
+            $this->raw($doc->asString());
+            break;
+        default:
+            // TRANS: Client error displayed when coming across a non-supported API method.
+            $this->clientError(_('API method not found.'), $code = 404);
+            break;
+        }
+    }
+
+    /**
+     * Get notices
+     *
+     * @return array notices
+     */
+    function getNotices()
+    {
+        $notices = array();
+
+        common_debug("since id = " . $this->since_id . " max id = " . $this->max_id);
+
+        $notice = new BookmarksNoticeStream($this->user->id, true);
+        $notice = $notice->getNotices(
+            ($this->page-1) * $this->count,
+            $this->count,
+            $this->since_id,
+            $this->max_id
+        );
+
+        while ($notice->fetch()) {
+            $notices[] = clone($notice);
+        }
+
+        return $notices;
+    }
+
+    /**
+     * Is this action read only?
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean true
+     */
+    function isReadOnly($args)
+    {
+        return true;
+    }
+
+    /**
+     * When was this feed last modified?
+     *
+     * @return string datestamp of the latest notice in the stream
+     */
+    function lastModified()
+    {
+        if (!empty($this->notices) && (count($this->notices) > 0)) {
+            return strtotime($this->notices[0]->created);
+        }
+
+        return null;
+    }
+
+    /**
+     * An entity tag for this stream
+     *
+     * Returns an Etag based on the action name, language, user ID, and
+     * timestamps of the first and last notice in the timeline
+     *
+     * @return string etag
+     */
+    function etag()
+    {
+        if (!empty($this->notices) && (count($this->notices) > 0)) {
+
+            $last = count($this->notices) - 1;
+
+            return '"' . implode(
+                ':',
+                array($this->arg('action'),
+                      common_user_cache_hash($this->auth_user),
+                      common_language(),
+                      $this->user->id,
+                      strtotime($this->notices[0]->created),
+                      strtotime($this->notices[$last]->created))
+            )
+            . '"';
+        }
+
+        return null;
+    }
+}
diff --git a/plugins/Bookmark/actions/bookmarkforurl.php b/plugins/Bookmark/actions/bookmarkforurl.php
new file mode 100644 (file)
index 0000000..4e6de9b
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Returns a pre-filled bookmark form for a given URL
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Returns a prefilled bookmark form for a given URL
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class BookmarkforurlAction extends Action
+{
+    protected $url        = null;
+    protected $oembed     = null;
+    protected $thumbnail  = null;
+    protected $title      = null;
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $args misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        if (!$this->isPost()) {
+            throw new ClientException(_('POST only'), 405);
+        }
+
+        $this->checkSessionToken();
+        $this->url = $this->trimmed('url');
+
+        if (empty($this->url)) {
+            throw new ClientException(_('URL is required.'), 400);
+        }
+
+        if (!Validate::uri($this->url, array('allowed_schemes' => array('http', 'https')))) {
+            throw new ClientException(_('Invalid URL.'), 400);
+        }
+
+        $f = File::getKV('url', $this->url);
+
+        if (empty($url)) { 
+           $f = File::processNew($this->url);
+        }
+
+        // How about now?
+
+        if (!empty($f)) {
+            $this->oembed    = File_oembed::getKV('file_id', $f->id);
+            if (!empty($this->oembed)) {
+                $this->title = $this->oembed->title;
+            }
+            $this->thumbnail = File_thumbnail::getKV('file_id', $f->id);
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $args is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+
+    function handle($args=null)
+    {
+        $this->startHTML('text/xml;charset=utf-8');
+        $this->elementStart('head');
+        $this->element('title', null, _('Bookmark form'));
+        $this->elementEnd('head');
+        $this->elementStart('body');
+        $bf = new BookmarkForm($this, $this->title, $this->url, null, null, $this->thumbnail);
+        $bf->show();
+        $this->elementEnd('body');
+        $this->elementEnd('html');
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+
+    function isReadOnly($args)
+    {
+        return false;
+    }
+}
diff --git a/plugins/Bookmark/actions/bookmarkpopup.php b/plugins/Bookmark/actions/bookmarkpopup.php
new file mode 100644 (file)
index 0000000..1400c0a
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Post a new bookmark in a popup window
+ *
+ * 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  Bookmark
+ * @package   StatusNet
+ * @author    Sarven Capadisli <csarven@status.net>
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Action for posting a new bookmark
+ *
+ * @category Bookmark
+ * @package  StatusNet
+ * @author   Sarven Capadisli <csarven@status.net>
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link     http://status.net/
+ */
+class BookmarkpopupAction extends NewbookmarkAction
+{
+    /**
+     * Show the title section of the window
+     *
+     * @return void
+     */
+    function showTitle()
+    {
+        $this->element('title',
+                       // TRANS: Title for mini-posting window loaded from bookmarklet.
+                       // TRANS: %s is the StatusNet site name.
+                       null, sprintf(_m('Bookmark on %s'),
+                                     common_config('site', 'name')));
+    }
+
+    /**
+     * Show the header section of the page
+     *
+     * Shows a stub page and the bookmark form.
+     *
+     * @return void
+     */
+    function showHeader()
+    {
+        $this->elementStart('div', array('id' => 'header'));
+        $this->elementStart('address');
+        $this->element('a', array('class' => 'url',
+                                  'href' => common_local_url('top')),
+                         '');
+        $this->elementEnd('address');
+        if (common_logged_in()) {
+            $form = new BookmarkForm($this,
+                                     $this->title,
+                                     $this->url);
+            $form->show();
+        }
+        $this->elementEnd('div');
+    }
+
+    /**
+     * Hide the core section of the page
+     *
+     * @return void
+     */
+    function showCore()
+    {
+    }
+
+    /**
+     * Hide the footer section of the page
+     *
+     * @return void
+     */
+    function showFooter()
+    {
+    }
+
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->script(Plugin::staticPath('Bookmark', 'bookmarkpopup.js'));
+    }
+}
diff --git a/plugins/Bookmark/actions/bookmarks.php b/plugins/Bookmark/actions/bookmarks.php
new file mode 100644 (file)
index 0000000..fce1185
--- /dev/null
@@ -0,0 +1,231 @@
+<?php
+/**
+ * Give a warm greeting to our friendly user
+ *
+ * PHP version 5
+ *
+ * @category Bookmark
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * List currently logged-in user's bookmakrs
+ *
+ * @category Bookmark
+ * @package  StatusNet
+ * @author   Stephane Berube <chimo@chromic.org>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     https://github.com/chimo/BookmarkList
+ */
+class BookmarksAction extends Action
+{
+    var $user = null;
+    var $gc   = null;
+
+    /**
+     * Take arguments for running
+     *
+     * This method is called first, and it lets the action class get
+     * all its arguments and validate them. It's also the time
+     * to fetch any relevant data from the database.
+     *
+     * Action classes should run parent::prepare($args) as the first
+     * line of this method to make sure the default argument-processing
+     * happens.
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     */
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        if (common_config('singleuser', 'enabled')) {
+            $nickname = User::singleUserNickname();
+        } else {
+            // PHP 5.4
+            // $nickname = $this->returnToArgs()[1]['nickname'];
+
+            // PHP < 5.4
+            $nickname = $this->returnToArgs();
+            $nickname = $nickname[1]['nickname'];
+        }
+        
+        $this->user = User::getKV('nickname', $nickname);
+
+        if (!$this->user) {
+            // TRANS: Client error displayed when trying to display bookmarks for a non-existing user.
+            $this->clientError(_('No such user.'));
+            return false;
+        }
+
+        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
+
+        $stream = new BookmarksNoticeStream($this->user->id, true);
+        $this->notices = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
+                                                NOTICES_PER_PAGE + 1); 
+
+        if($this->page > 1 && $this->notices->N == 0) {
+            throw new ClientException(_('No such page.'), 404);
+        }
+
+        return true;
+    }
+
+    /**
+     * Handle request
+     *
+     * This is the main method for handling a request. Note that
+     * most preparation should be done in the prepare() method;
+     * by the time handle() is called the action should be
+     * more or less ready to go.
+     *
+     * @param array $args $_REQUEST args; handled in prepare()
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+        $this->showPage();
+    }
+
+    /**
+     * Title of this page
+     *
+     * Override this method to show a custom title.
+     *
+     * @return string Title of the page
+     */
+    function title()
+    {
+
+        if (empty($this->user)) {
+            // TRANS: Page title for sample plugin.
+            return _m('Log in');
+        } else {
+            // TRANS: Page title for sample plugin. %s is a user nickname.
+            return sprintf(_m('%s\'s bookmarks'), $this->user->nickname);
+        }
+    }
+
+    /**
+     * Feeds for the <head> section
+     *
+     * @return array Feed objects to show
+     */
+    function getFeeds()
+    {
+        return array(new Feed(Feed::JSON,
+                              common_local_url('ApiTimelineBookmarks',
+                                               array(
+                                                    'id' => $this->user->nickname,
+                                                    'format' => 'as')),
+                              // TRANS: Feed link text. %s is a username.
+                              sprintf(_('Feed for favorites of %s (Activity Streams JSON)'),
+                                      $this->user->nickname)),
+                     new Feed(Feed::RSS1,
+                              common_local_url('bookmarksrss',
+                                               array('nickname' => $this->user->nickname)),
+                              // TRANS: Feed link text. %s is a username.
+                              sprintf(_('Feed for favorites of %s (RSS 1.0)'),
+                                      $this->user->nickname)),
+                     new Feed(Feed::RSS2,
+                              common_local_url('ApiTimelineBookmarks',
+                                               array(
+                                                    'id' => $this->user->nickname,
+                                                    'format' => 'rss')),
+                              // TRANS: Feed link text. %s is a username.
+                              sprintf(_('Feed for favorites of %s (RSS 2.0)'),
+                                      $this->user->nickname)),
+                     new Feed(Feed::ATOM,
+                              common_local_url('ApiTimelineBookmarks',
+                                               array(
+                                                    'id' => $this->user->nickname,
+                                                    'format' => 'atom')),
+                              // TRANS: Feed link text. %s is a username.
+                              sprintf(_('Feed for favorites of %s (Atom)'),
+                                      $this->user->nickname)));
+    }
+
+    /**
+     * Show content in the content area
+     *
+     * The default StatusNet page has a lot of decorations: menus,
+     * logos, tabs, all that jazz. This method is used to show
+     * content in the content area of the page; it's the main
+     * thing you want to overload.
+     *
+     * This method also demonstrates use of a plural localized string.
+     *
+     * @return void
+     */
+    function showContent()
+    {
+
+        $nl = new NoticeList($this->notices, $this);
+
+        $cnt = $nl->show();
+
+        if ($cnt == 0) {
+            $this->showEmptyList();
+        }
+
+        $this->pagination($this->page > 1,
+                $cnt > NOTICES_PER_PAGE,
+                $this->page, 'bookmarks',
+                array('nickname' => $this->user->nickname));
+    }
+
+    function showEmptyList() {
+        $message = sprintf(_('This is %1$s\'s bookmark stream, but %1$s hasn\'t bookmarked anything yet.'), $this->user->nickname) . ' ';
+
+        $this->elementStart('div', 'guide');
+        $this->raw(common_markup_to_html($message));
+        $this->elementEnd('div');
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * Some actions only read from the database; others read and write.
+     * The simple database load-balancer built into StatusNet will
+     * direct read-only actions to database mirrors (if they are configured),
+     * and read-write actions to the master database.
+     *
+     * This defaults to false to avoid data integrity issues, but you
+     * should make sure to overload it for performance gains.
+     *
+     * @param array $args other arguments, if RO/RW status depends on them.
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        return true;
+    }
+}
diff --git a/plugins/Bookmark/actions/bookmarksrss.php b/plugins/Bookmark/actions/bookmarksrss.php
new file mode 100644 (file)
index 0000000..c48ab90
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+/**
+ * RSS feed for user bookmarks action class.
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/lib/rssaction.php';
+
+/**
+ * RSS feed for user bookmarks action class.
+ *
+ * Formatting of RSS handled by Rss10Action
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Robin Millette <millette@status.net>
+ * @author   Zach Copley <zach@status.net>
+ * @author   Stephane Berube <chimo@chromic.org> (modified 'favoritesrss.php' to show bookmarks instead)
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class BookmarksrssAction extends Rss10Action
+{
+    /** The user whose bookmarks to display */
+
+    var $user = null;
+
+    /**
+     * Find the user to display by supplied nickname
+     *
+     * @param array $args Arguments from $_REQUEST
+     *
+     * @return boolean success
+     */
+    function prepare($args)
+    {        
+        parent::prepare($args);
+
+        $nickname   = $this->trimmed('nickname');
+        $this->user = User::getKV('nickname', $nickname);
+
+        if (!$this->user) {
+            // TRANS: Client error displayed when trying to get the RSS feed with bookmarks of a user that does not exist.
+            $this->clientError(_('No such user.'));
+            return false;
+        } else {
+            $this->notices = $this->getNotices($this->limit);
+            return true;
+        }
+    }
+
+    /**
+     * Get notices
+     *
+     * @param integer $limit max number of notices to return
+     *
+     * @return array notices
+     */
+    function getNotices($limit=0)
+    {
+        $user    = $this->user;
+
+        $notice = new BookmarksNoticeStream($this->user->id, true);
+        $notice = $notice->getNotices(0, NOTICES_PER_PAGE);
+
+        $notices = array();
+        while ($notice->fetch()) {
+            $notices[] = clone($notice);
+        }
+        return $notices;
+    }
+
+     /**
+     * Get channel.
+     *
+     * @return array associative array on channel information
+     */
+    function getChannel()
+    {
+        $user = $this->user;
+        $c    = array('url' => common_local_url('bookmarksrss',
+                                        array('nickname' =>
+                                        $user->nickname)),
+                   // TRANS: Title of RSS feed with bookmarks of a user.
+                   // TRANS: %s is a user's nickname.
+                   'title' => sprintf(_("%s's bookmarks"), $user->nickname),
+                   'link' => common_local_url('bookmarks',
+                                        array('nickname' =>
+                                        $user->nickname)),
+                   // TRANS: Desciption of RSS feed with bookmarks of a user.
+                   // TRANS: %1$s is a user's nickname, %2$s is the name of the StatusNet site.
+                   'description' => sprintf(_('Bookmarks posted by %1$s on %2$s!'),
+                                        $user->nickname, common_config('site', 'name')));
+        return $c;
+    }
+
+    /**
+     * Get image.
+     *
+     * @return void
+    */
+    function getImage()
+    {
+        return null;
+    }
+
+}
diff --git a/plugins/Bookmark/actions/importdelicious.php b/plugins/Bookmark/actions/importdelicious.php
new file mode 100644 (file)
index 0000000..85a63e8
--- /dev/null
@@ -0,0 +1,349 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Import del.icio.us bookmarks backups
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * UI for importing del.icio.us bookmark backups
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class ImportdeliciousAction extends Action
+{
+    protected $success = false;
+    private $inprogress = false;
+
+    /**
+     * Return the title of the page
+     *
+     * @return string page title
+     */
+    function title()
+    {
+        // TRANS: Title for page to import del.icio.us bookmark backups on.
+        return _m("Import del.icio.us bookmarks");
+    }
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        $cur = common_current_user();
+
+        if (empty($cur)) {
+            // TRANS: Client exception thrown when trying to import bookmarks without being logged in.
+            throw new ClientException(_m('Only logged-in users can '.
+                                        'import del.icio.us backups.'),
+                                      403);
+        }
+
+        if (!$cur->hasRight(BookmarkPlugin::IMPORTDELICIOUS)) {
+            // TRANS: Client exception thrown when trying to import bookmarks without having the rights to do so.
+            throw new ClientException(_m('You may not restore your account.'), 403);
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        parent::handle($argarray);
+
+        if ($this->isPost()) {
+            $this->importDelicious();
+        } else {
+            $this->showPage();
+        }
+        return;
+    }
+
+    /**
+     * Queue a file for importation
+     *
+     * Uses the DeliciousBackupImporter class; may take a long time!
+     *
+     * @return void
+     */
+    function importDelicious()
+    {
+        $this->checkSessionToken();
+
+        if (!isset($_FILES[ImportDeliciousForm::FILEINPUT]['error'])) {
+            // TRANS: Client exception thrown when trying to import bookmarks and upload fails.
+            throw new ClientException(_m('No uploaded file.'));
+        }
+
+        switch ($_FILES[ImportDeliciousForm::FILEINPUT]['error']) {
+        case UPLOAD_ERR_OK: // success, jump out
+            break;
+        case UPLOAD_ERR_INI_SIZE:
+            // TRANS: Client exception thrown when an uploaded file is too large.
+            throw new ClientException(_m('The uploaded file exceeds the ' .
+                'upload_max_filesize directive in php.ini.'));
+            return;
+        case UPLOAD_ERR_FORM_SIZE:
+            throw new ClientException(
+            // TRANS: Client exception thrown when an uploaded file is too large.
+                _m('The uploaded file exceeds the MAX_FILE_SIZE directive' .
+                ' that was specified in the HTML form.'));
+            return;
+        case UPLOAD_ERR_PARTIAL:
+            @unlink($_FILES[ImportDeliciousForm::FILEINPUT]['tmp_name']);
+            // TRANS: Client exception thrown when a file was only partially uploaded.
+            throw new ClientException(_m('The uploaded file was only' .
+                ' partially uploaded.'));
+            return;
+        case UPLOAD_ERR_NO_FILE:
+            // No file; probably just a non-AJAX submission.
+            // TRANS: Client exception thrown when a file upload has failed.
+            throw new ClientException(_m('No uploaded file.'));
+            return;
+        case UPLOAD_ERR_NO_TMP_DIR:
+            // TRANS: Client exception thrown when a temporary folder is not present.
+            throw new ClientException(_m('Missing a temporary folder.'));
+            return;
+        case UPLOAD_ERR_CANT_WRITE:
+            // TRANS: Client exception thrown when writing to disk is not possible.
+            throw new ClientException(_m('Failed to write file to disk.'));
+            return;
+        case UPLOAD_ERR_EXTENSION:
+            // TRANS: Client exception thrown when a file upload has been stopped.
+            throw new ClientException(_m('File upload stopped by extension.'));
+            return;
+        default:
+            common_log(LOG_ERR, __METHOD__ . ": Unknown upload error " .
+                $_FILES[ImportDeliciousForm::FILEINPUT]['error']);
+            // TRANS: Client exception thrown when a file upload operation has failed.
+            throw new ClientException(_m('System error uploading file.'));
+            return;
+        }
+
+        $filename = $_FILES[ImportDeliciousForm::FILEINPUT]['tmp_name'];
+
+        try {
+            if (!file_exists($filename)) {
+                // TRANS: Server exception thrown when a file upload cannot be found.
+                // TRANS: %s is the file that could not be found.
+                throw new ServerException(sprintf(_m('No such file "%s".'),$filename));
+            }
+
+            if (!is_file($filename)) {
+                // TRANS: Server exception thrown when a file upload is incorrect.
+                // TRANS: %s is the irregular file.
+                throw new ServerException(sprintf(_m('Not a regular file: "%s".'),$filename));
+            }
+
+            if (!is_readable($filename)) {
+                // TRANS: Server exception thrown when a file upload is not readable.
+                // TRANS: %s is the file that could not be read.
+                throw new ServerException(sprintf(_m('File "%s" not readable.'),$filename));
+            }
+
+            common_debug(sprintf("Getting backup from file '%s'.", $filename));
+
+            $html = file_get_contents($filename);
+
+            // Enqueue for processing.
+
+            $qm = QueueManager::get();
+            $qm->enqueue(array(common_current_user(), $html), 'dlcsback');
+
+            if ($qm instanceof UnQueueManager) {
+                // No active queuing means we've actually just completed the job!
+                $this->success = true;
+            } else {
+                // We've fed data into background queues, and it's probably still running.
+                $this->inprogress = true;
+            }
+
+            $this->showPage();
+
+        } catch (Exception $e) {
+            // Delete the file and re-throw
+            @unlink($_FILES[ImportDeliciousForm::FILEINPUT]['tmp_name']);
+            throw $e;
+        }
+    }
+
+    /**
+     * Show the content of the page
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        if ($this->success) {
+            $this->element('p', null,
+                           // TRANS: Success message after importing bookmarks.
+                           _m('Bookmarks have been imported. Your bookmarks should now appear in search and your profile page.'));
+        } else if ($this->inprogress) {
+            $this->element('p', null,
+                           // TRANS: Busy message for importing bookmarks.
+                           _m('Bookmarks are being imported. Please wait a few minutes for results.'));
+        } else {
+            $form = new ImportDeliciousForm($this);
+            $form->show();
+        }
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        return !$this->isPost();
+    }
+}
+
+/**
+ * A form for backing up the account.
+ *
+ * @category  Account
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class ImportDeliciousForm extends Form
+{
+    const FILEINPUT = 'deliciousbackupfile';
+
+    /**
+     * Constructor
+     *
+     * Set the encoding type, since this is a file upload.
+     *
+     * @param HTMLOutputter $out output channel
+     *
+     * @return ImportDeliciousForm this
+     */
+    function __construct($out=null)
+    {
+        parent::__construct($out);
+        $this->enctype = 'multipart/form-data';
+    }
+
+    /**
+     * Class of the form.
+     *
+     * @return string the form's class
+     */
+    function formClass()
+    {
+        return 'form_import_delicious';
+    }
+
+    /**
+     * URL the form posts to
+     *
+     * @return string the form's action URL
+     */
+    function action()
+    {
+        return common_local_url('importdelicious');
+    }
+
+    /**
+     * Output form data
+     *
+     * Really, just instructions for doing a backup.
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('p', 'instructions');
+
+        // TRANS: Form instructions for importing bookmarks.
+        $this->out->raw(_m('You can upload a backed-up '.
+                          'delicious.com bookmarks file.'));
+
+        $this->out->elementEnd('p');
+
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->out->elementStart('li', array ('id' => 'settings_attach'));
+        $this->out->element('input', array('name' => self::FILEINPUT,
+                                           'type' => 'file',
+                                           'id' => self::FILEINPUT));
+        $this->out->elementEnd('li');
+
+        $this->out->elementEnd('ul');
+    }
+
+    /**
+     * Buttons for the form
+     *
+     * In this case, a single submit button
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+        $this->out->submit('submit',
+                           // TRANS: Button text on form to import bookmarks.
+                           _m('BUTTON', 'Upload'),
+                           'submit',
+                           null,
+                           // TRANS: Button title on form to import bookmarks.
+                           _m('Upload the file.'));
+    }
+}
diff --git a/plugins/Bookmark/actions/newbookmark.php b/plugins/Bookmark/actions/newbookmark.php
new file mode 100644 (file)
index 0000000..57be783
--- /dev/null
@@ -0,0 +1,245 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Add a new bookmark
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Add a new bookmark
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class NewbookmarkAction extends Action
+{
+    protected $user        = null;
+    protected $error       = null;
+    protected $complete    = null;
+    protected $title       = null;
+    protected $url         = null;
+    protected $tags        = null;
+    protected $description = null;
+
+    /**
+     * Returns the title of the action
+     *
+     * @return string Action title
+     */
+    function title()
+    {
+        // TRANS: Title for action to create a new bookmark.
+        return _m('New bookmark');
+    }
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        if ($this->boolean('ajax')) {
+            StatusNet::setApi(true);
+        }
+
+        $this->user = common_current_user();
+
+        if (empty($this->user)) {
+            // TRANS: Client exception thrown when trying to create a new bookmark while not logged in.
+            throw new ClientException(_m('Must be logged in to post a bookmark.'),
+                                      403);
+        }
+
+        if ($this->isPost()) {
+            $this->checkSessionToken();
+        }
+
+        $this->title       = $this->trimmed('title');
+        $this->url         = $this->trimmed('url');
+        $this->tags        = $this->trimmed('tags');
+        $this->description = $this->trimmed('description');
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        parent::handle($argarray);
+
+        if ($this->isPost()) {
+            $this->newBookmark();
+        } else {
+            $this->showPage();
+        }
+
+        return;
+    }
+
+    /**
+     * Add a new bookmark
+     *
+     * @return void
+     */
+    function newBookmark()
+    {
+        try {
+            if (empty($this->title)) {
+                // TRANS: Client exception thrown when trying to create a new bookmark without a title.
+                throw new ClientException(_m('Bookmark must have a title.'));
+            }
+
+            if (empty($this->url)) {
+                // TRANS: Client exception thrown when trying to create a new bookmark without a URL.
+                throw new ClientException(_m('Bookmark must have an URL.'));
+            }
+
+            $options = array();
+
+            ToSelector::fillOptions($this, $options);
+
+            $saved = Bookmark::saveNew($this->user->getProfile(),
+                                       $this->title,
+                                       $this->url,
+                                       $this->tags,
+                                       $this->description,
+                                       $options);
+
+        } catch (ClientException $ce) {
+            if ($this->boolean('ajax')) {
+                header('Content-Type: text/xml;charset=utf-8');
+                $this->xw->startDocument('1.0', 'UTF-8');
+                $this->elementStart('html');
+                $this->elementStart('head');
+                // TRANS: Page title after an AJAX error occurs
+                $this->element('title', null, _('Ajax Error'));
+                $this->elementEnd('head');
+                $this->elementStart('body');
+                $this->element('p', array('id' => 'error'), $ce->getMessage());
+                $this->elementEnd('body');
+                $this->elementEnd('html');
+                return;
+            } else {
+                $this->error = $ce->getMessage();
+                $this->showPage();
+                return;
+            }
+        }
+
+        if ($this->boolean('ajax')) {
+            header('Content-Type: text/xml;charset=utf-8');
+            $this->xw->startDocument('1.0', 'UTF-8');
+            $this->elementStart('html');
+            $this->elementStart('head');
+            // TRANS: Page title after posting a bookmark.
+            $this->element('title', null, _m('Bookmark posted'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $this->showNotice($saved);
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            common_redirect($saved->bestUrl(), 303);
+        }
+    }
+
+    /**
+     * Output a notice
+     *
+     * Used to generate the notice code for Ajax results.
+     *
+     * @param Notice $notice Notice that was saved
+     *
+     * @return void
+     */
+    function showNotice($notice)
+    {
+        class_exists('NoticeList'); // @fixme hack for autoloader
+        $nli = new NoticeListItem($notice, $this);
+        $nli->show();
+    }
+
+    /**
+     * Show the bookmark form
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        if (!empty($this->error)) {
+            $this->element('p', 'error', $this->error);
+        }
+
+        $form = new BookmarkForm($this,
+                                 $this->title,
+                                 $this->url,
+                                 $this->tags,
+                                 $this->description);
+
+        $form->show();
+
+        return;
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+            $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/plugins/Bookmark/actions/noticebyurl.php b/plugins/Bookmark/actions/noticebyurl.php
new file mode 100644 (file)
index 0000000..d5f9940
--- /dev/null
@@ -0,0 +1,175 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Notice stream of notices with a given attachment
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * List notices that contain/link to/use a given URL
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class NoticebyurlAction extends Action
+{
+    protected $url     = null;
+    protected $file    = null;
+    protected $notices = null;
+    protected $page    = null;
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        $this->file = File::getKV('id', $this->trimmed('id'));
+
+        if (empty($this->file)) {
+            // TRANS: Client exception thrown when an unknown URL is provided.
+            throw new ClientException(_m('Unknown URL.'));
+        }
+
+        $pageArg = $this->trimmed('page');
+
+        $this->page = (empty($pageArg)) ? 1 : intval($pageArg);
+
+        $this->notices = $this->file->stream(($this->page - 1) * NOTICES_PER_PAGE,
+                                             NOTICES_PER_PAGE + 1);
+
+        return true;
+    }
+
+    /**
+     * Title of the page
+     *
+     * @return string page title
+     */
+    function title()
+    {
+        if ($this->page == 1) {
+            // TRANS: Title of notice stream of notices with a given attachment (first page).
+            // TRANS: %s is the URL.
+            return sprintf(_m('Notices linking to %s'), $this->file->url);
+        } else {
+            // TRANS: Title of notice stream of notices with a given attachment (all but first page).
+            // TRANS: %1$s is the URL, %2$s is the page number.
+            return sprintf(_m('Notices linking to %1$s, page %2$d'),
+                           $this->file->url,
+                           $this->page);
+        }
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        $this->showPage();
+    }
+
+    /**
+     * Show main page content.
+     *
+     * Shows a list of the notices that link to the given URL
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        $nl = new NoticeList($this->notices, $this);
+
+        $nl->show();
+
+        $cnt = $nl->show();
+
+        $this->pagination($this->page > 1,
+                          $cnt > NOTICES_PER_PAGE,
+                          $this->page,
+                          'noticebyurl',
+                          array('id' => $this->file->id));
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        return true;
+    }
+
+    /**
+     * Return last modified, if applicable.
+     *
+     * MAY override
+     *
+     * @return string last modified http header
+     */
+    function lastModified()
+    {
+        // For comparison with If-Last-Modified
+        // If not applicable, return null
+        return null;
+    }
+
+    /**
+     * Return etag, if applicable.
+     *
+     * MAY override
+     *
+     * @return string etag http header
+     */
+    function etag()
+    {
+        return null;
+    }
+}
diff --git a/plugins/Bookmark/actions/showbookmark.php b/plugins/Bookmark/actions/showbookmark.php
new file mode 100644 (file)
index 0000000..1c562d5
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Show a single bookmark
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Show a single bookmark, with associated information
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class ShowbookmarkAction extends ShownoticeAction
+{
+    protected $bookmark = null;
+
+    function getNotice()
+    {
+        $this->id = $this->trimmed('id');
+
+        $this->bookmark = Bookmark::getKV('id', $this->id);
+
+        if (empty($this->bookmark)) {
+            // TRANS: Client exception thrown when referring to a non-existing bookmark.
+            throw new ClientException(_m('No such bookmark.'), 404);
+        }
+
+        $notice = Notice::getKV('uri', $this->bookmark->uri);
+
+        if (empty($notice)) {
+            // Did we used to have it, and it got deleted?
+            // TRANS: Client exception thrown when referring to a non-existing bookmark.
+            throw new ClientException(_m('No such bookmark.'), 404);
+        }
+
+        return $notice;
+    }
+
+    /**
+     * Title of the page
+     *
+     * Used by Action class for layout.
+     *
+     * @return string page tile
+     */
+    function title()
+    {
+        // TRANS: Title for bookmark.
+        // TRANS: %1$s is a user nickname, %2$s is a bookmark title.
+        return sprintf(_m('%1$s\'s bookmark for "%2$s"'),
+                       $this->user->nickname,
+                       $this->bookmark->title);
+    }
+
+    /**
+     * Overload page title display to show bookmark link
+     *
+     * @return void
+     */
+    function showPageTitle()
+    {
+        $this->elementStart('h1');
+        $this->element('a',
+                       array('href' => $this->bookmark->url),
+                       $this->bookmark->title);
+        $this->elementEnd('h1');
+    }
+}
diff --git a/plugins/Bookmark/apitimelinebookmarks.php b/plugins/Bookmark/apitimelinebookmarks.php
deleted file mode 100644 (file)
index 1753462..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Show a user's favorite notices
- *
- * 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  API
- * @package   StatusNet
- * @author    Craig Andrews <candrews@integralblue.com>
- * @author    Evan Prodromou <evan@status.net>
- * @author    Zach Copley <zach@status.net>
- * @copyright 2009-2010 StatusNet, Inc.
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/apibareauth.php';
-require_once 'bookmarksnoticestream.php';
-
-/**
- * Returns the 20 most recent favorite notices for the authenticating user or user
- * specified by the ID parameter in the requested format.
- *
- * @category API
- * @package  StatusNet
- * @author   Craig Andrews <candrews@integralblue.com>
- * @author   Evan Prodromou <evan@status.net>
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class ApiTimelineBookmarksAction extends ApiBareAuthAction
-{
-    var $notices  = null;
-
-    /**
-     * Take arguments for running
-     *
-     * @param array $args $_REQUEST args
-     *
-     * @return boolean success flag
-     */
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $this->user = $this->getTargetUser($this->arg('id'));
-
-        if (empty($this->user)) {
-            // TRANS: Client error displayed when requesting most recent favourite notices by a user for a non-existing user.
-            $this->clientError(_('No such user.'), 404, $this->format);
-            return;
-        }
-
-        $this->notices = $this->getNotices();
-
-        return true;
-    }
-
-    /**
-     * Handle the request
-     *
-     * Just show the notices
-     *
-     * @param array $args $_REQUEST data (unused)
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-        $this->showTimeline();
-    }
-
-    /**
-     * Show the timeline of notices
-     *
-     * @return void
-     */
-    function showTimeline()
-    {
-        $profile  = $this->user->getProfile();
-        $avatar   = $profile->getAvatar(AVATAR_PROFILE_SIZE);
-
-        $sitename = common_config('site', 'name');
-        $title    = sprintf(
-            // TRANS: Title for timeline of most recent favourite notices by a user.
-            // TRANS: %1$s is the StatusNet sitename, %2$s is a user nickname.
-            _('%1$s / Bookmarks from %2$s'),
-            $sitename,
-            $this->user->nickname
-        );
-
-        $taguribase = TagURI::base();
-        $id         = "tag:$taguribase:Bookmarks:" . $this->user->id;
-
-        $subtitle = sprintf(
-            // TRANS: Subtitle for timeline of most recent favourite notices by a user.
-            // TRANS: %1$s is the StatusNet sitename, %2$s is a user's full name,
-            // TRANS: %3$s is a user nickname.
-            _('%1$s updates bookmarked by %2$s / %3$s.'),
-            $sitename,
-            $profile->getBestName(),
-            $this->user->nickname
-        );
-        $logo = !empty($avatar)
-            ? $avatar->displayUrl()
-            : Avatar::defaultImage(AVATAR_PROFILE_SIZE);
-
-        $link = common_local_url(
-            'bookmarks',
-            array('nickname' => $this->user->nickname)
-        );
-
-        $self = $this->getSelfUri();
-
-        switch($this->format) {
-        case 'xml':
-            $this->showXmlTimeline($this->notices);
-            break;
-        case 'rss':
-            $this->showRssTimeline(
-                $this->notices,
-                $title,
-                $link,
-                $subtitle,
-                null,
-                $logo,
-                $self
-            );
-            break;
-        case 'atom':
-            header('Content-Type: application/atom+xml; charset=utf-8');
-
-            $atom = new AtomNoticeFeed($this->auth_user);
-
-            $atom->setId($id);
-            $atom->setTitle($title);
-            $atom->setSubtitle($subtitle);
-            $atom->setLogo($logo);
-            $atom->setUpdated('now');
-
-            $atom->addLink($link);
-            $atom->setSelfLink($self);
-
-            $atom->addEntryFromNotices($this->notices);
-
-            $this->raw($atom->getString());
-            break;
-        case 'json':
-            $this->showJsonTimeline($this->notices);
-            break;
-        case 'as':
-            header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
-            $doc = new ActivityStreamJSONDocument($this->auth_user);
-            $doc->setTitle($title);
-            $doc->addLink($link,'alternate', 'text/html');
-            $doc->addItemsFromNotices($this->notices);
-            $this->raw($doc->asString());
-            break;
-        default:
-            // TRANS: Client error displayed when coming across a non-supported API method.
-            $this->clientError(_('API method not found.'), $code = 404);
-            break;
-        }
-    }
-
-    /**
-     * Get notices
-     *
-     * @return array notices
-     */
-    function getNotices()
-    {
-        $notices = array();
-
-        common_debug("since id = " . $this->since_id . " max id = " . $this->max_id);
-
-        $notice = new BookmarksNoticeStream($this->user->id, true);
-        $notice = $notice->getNotices(
-            ($this->page-1) * $this->count,
-            $this->count,
-            $this->since_id,
-            $this->max_id
-        );
-
-        while ($notice->fetch()) {
-            $notices[] = clone($notice);
-        }
-
-        return $notices;
-    }
-
-    /**
-     * Is this action read only?
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean true
-     */
-    function isReadOnly($args)
-    {
-        return true;
-    }
-
-    /**
-     * When was this feed last modified?
-     *
-     * @return string datestamp of the latest notice in the stream
-     */
-    function lastModified()
-    {
-        if (!empty($this->notices) && (count($this->notices) > 0)) {
-            return strtotime($this->notices[0]->created);
-        }
-
-        return null;
-    }
-
-    /**
-     * An entity tag for this stream
-     *
-     * Returns an Etag based on the action name, language, user ID, and
-     * timestamps of the first and last notice in the timeline
-     *
-     * @return string etag
-     */
-    function etag()
-    {
-        if (!empty($this->notices) && (count($this->notices) > 0)) {
-
-            $last = count($this->notices) - 1;
-
-            return '"' . implode(
-                ':',
-                array($this->arg('action'),
-                      common_user_cache_hash($this->auth_user),
-                      common_language(),
-                      $this->user->id,
-                      strtotime($this->notices[0]->created),
-                      strtotime($this->notices[$last]->created))
-            )
-            . '"';
-        }
-
-        return null;
-    }
-}
diff --git a/plugins/Bookmark/bookmarkform.php b/plugins/Bookmark/bookmarkform.php
deleted file mode 100644 (file)
index 20101e1..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * Form for adding a new bookmark
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form to add a new bookmark
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class BookmarkForm extends Form
-{
-    private $_title       = null;
-    private $_url         = null;
-    private $_tags        = null;
-    private $_description = null;
-    private $_thumbnail   = null;
-
-    /**
-     * Construct a bookmark form
-     *
-     * @param HTMLOutputter $out         output channel
-     * @param string        $title       Title of the bookmark
-     * @param string        $url         URL of the bookmark
-     * @param string        $tags        Tags to show
-     * @param string        $description Description of the bookmark
-     *
-     * @return void
-     */
-    function __construct($out=null, $title=null, $url=null, $tags=null,
-                         $description=null, $thumbnail=null)
-    {
-        parent::__construct($out);
-
-        $this->_title       = $title;
-        $this->_url         = $url;
-        $this->_tags        = $tags;
-        $this->_description = $description;
-        $this->_thumbnail   = $thumbnail;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'form_new_bookmark';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings ajax-notice';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('newbookmark');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('fieldset', array('id' => 'new_bookmark_data'));
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-        $this->out->input('bookmark-url',
-                          // TRANS: Field label on form for adding a new bookmark.
-                          _m('LABEL','URL'),
-                          $this->_url,
-                          null,
-                          'url');
-        $this->unli();
-
-        if (!empty($this->_thumbnail)) {
-
-            list($width, $height) = $this->scaleImage($this->_thumbnail->width,
-                                                      $this->_thumbnail->height);
-
-            $this->out->element('img',
-                                array('src' => $this->_thumbnail->url,
-                                      'class' => 'bookmarkform-thumbnail',
-                                      'width' => $width,
-                                      'height' => $height));
-        }
-
-        $this->li();
-        $this->out->input('bookmark-title',
-                          // TRANS: Field label on form for adding a new bookmark.
-                          _m('LABEL','Title'),
-                          $this->_title,
-                          null,
-                          'title');
-        $this->unli();
-
-        $this->li();
-        $this->out->textarea('bookmark-description',
-                             // TRANS: Field label on form for adding a new bookmark.
-                             _m('LABEL','Notes'),
-                             $this->_description,
-                             null,
-                             'description');
-        $this->unli();
-
-        $this->li();
-        $this->out->input('bookmark-tags',
-                          // TRANS: Field label on form for adding a new bookmark.
-                          _m('LABEL','Tags'),
-                          $this->_tags,
-                          // TRANS: Field title on form for adding a new bookmark.
-                          _m('Comma- or space-separated list of tags.'),
-                          'tags');
-        $this->unli();
-
-        $this->out->elementEnd('ul');
-
-        $toWidget = new ToSelector($this->out,
-                                   common_current_user(),
-                                   null);
-        $toWidget->show();
-
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-
-    function formActions()
-    {
-        // TRANS: Button text for action to save a new bookmark.
-        $this->out->submit('bookmark-submit', _m('BUTTON', 'Save'), 'submit', 'submit');
-    }
-
-    function scaleImage($width, $height)
-    {
-        $maxwidth = common_config('attachments', 'thumb_width');
-        $maxheight = common_config('attachments', 'thumb_height');
-
-        if ($width > $height && $width > $maxwidth) {
-            $height = (int) ((((float)$maxwidth)/(float)($width))*(float)$height);
-            $width = $maxwidth;
-        } else if ($height > $maxheight) {
-            $width = (int) ((((float)$maxheight)/(float)($height))*(float)$width);
-            $height = $maxheight;
-        }
-
-        return array($width, $height);
-    }
-}
diff --git a/plugins/Bookmark/bookmarkforurl.php b/plugins/Bookmark/bookmarkforurl.php
deleted file mode 100644 (file)
index 4e6de9b..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Returns a pre-filled bookmark form for a given URL
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Returns a prefilled bookmark form for a given URL
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class BookmarkforurlAction extends Action
-{
-    protected $url        = null;
-    protected $oembed     = null;
-    protected $thumbnail  = null;
-    protected $title      = null;
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $args misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        if (!$this->isPost()) {
-            throw new ClientException(_('POST only'), 405);
-        }
-
-        $this->checkSessionToken();
-        $this->url = $this->trimmed('url');
-
-        if (empty($this->url)) {
-            throw new ClientException(_('URL is required.'), 400);
-        }
-
-        if (!Validate::uri($this->url, array('allowed_schemes' => array('http', 'https')))) {
-            throw new ClientException(_('Invalid URL.'), 400);
-        }
-
-        $f = File::getKV('url', $this->url);
-
-        if (empty($url)) { 
-           $f = File::processNew($this->url);
-        }
-
-        // How about now?
-
-        if (!empty($f)) {
-            $this->oembed    = File_oembed::getKV('file_id', $f->id);
-            if (!empty($this->oembed)) {
-                $this->title = $this->oembed->title;
-            }
-            $this->thumbnail = File_thumbnail::getKV('file_id', $f->id);
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $args is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-
-    function handle($args=null)
-    {
-        $this->startHTML('text/xml;charset=utf-8');
-        $this->elementStart('head');
-        $this->element('title', null, _('Bookmark form'));
-        $this->elementEnd('head');
-        $this->elementStart('body');
-        $bf = new BookmarkForm($this, $this->title, $this->url, null, null, $this->thumbnail);
-        $bf->show();
-        $this->elementEnd('body');
-        $this->elementEnd('html');
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-
-    function isReadOnly($args)
-    {
-        return false;
-    }
-}
diff --git a/plugins/Bookmark/bookmarklistitem.php b/plugins/Bookmark/bookmarklistitem.php
deleted file mode 100644 (file)
index f9613fb..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Adapter to show bookmarks in a nicer way
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * An adapter to show bookmarks in a nicer way
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class BookmarkListItem extends NoticeListItemAdapter
-{
-    function showNotice()
-    {
-        $this->nli->out->elementStart('div', 'entry-title');
-        $this->nli->showAuthor();
-        $this->showContent();
-        $this->nli->out->elementEnd('div');
-    }
-
-    function showContent()
-    {
-        $notice = $this->nli->notice;
-        $out    = $this->nli->out;
-
-        $nb = Bookmark::getByNotice($notice);
-
-        if (empty($nb)) {
-            common_log(LOG_ERR, "No bookmark for notice {$notice->id}");
-            parent::showContent();
-            return;
-        } else if (empty($nb->url)) {
-            common_log(LOG_ERR, "No url for bookmark {$nb->id} for notice {$notice->id}");
-            parent::showContent();
-            return;
-        }
-
-        $profile = $notice->getProfile();
-
-        $out->elementStart('p', array('class' => 'entry-content'));
-
-        // Whether to nofollow
-
-        $attrs = array('href' => $nb->url,
-                       'class' => 'bookmark-title');
-
-        $nf = common_config('nofollow', 'external');
-
-        if ($nf == 'never' || ($nf == 'sometimes' and $out instanceof ShowstreamAction)) {
-            $attrs['rel'] = 'external';
-        } else {
-            $attrs['rel'] = 'nofollow external';
-        }
-
-        $out->elementStart('h3');
-        $out->element('a',
-                      $attrs,
-                      $nb->title);
-        $out->elementEnd('h3');
-
-        // Replies look like "for:" tags
-
-        $replies = $notice->getReplies();
-        $tags = $notice->getTags();
-
-        if (!empty($replies) || !empty($tags)) {
-
-            $out->elementStart('ul', array('class' => 'bookmark-tags'));
-
-            foreach ($replies as $reply) {
-                $other = Profile::getKV('id', $reply);
-                if (!empty($other)) {
-                    $out->elementStart('li');
-                    $out->element('a', array('rel' => 'tag',
-                                             'href' => $other->profileurl,
-                                             'title' => $other->getBestName()),
-                                  sprintf('for:%s', $other->nickname));
-                    $out->elementEnd('li');
-                    $out->text(' ');
-                }
-            }
-
-            foreach ($tags as $tag) {
-                $tag = trim($tag);
-                if (!empty($tag)) {
-                    $out->elementStart('li');
-                    $out->element('a',
-                                  array('rel' => 'tag',
-                                        'href' => Notice_tag::url($tag)),
-                                  $tag);
-                    $out->elementEnd('li');
-                    $out->text(' ');
-                }
-            }
-
-            $out->elementEnd('ul');
-        }
-
-        if (!empty($nb->description)) {
-            $out->element('p',
-                          array('class' => 'bookmark-description'),
-                          $nb->description);
-        }
-
-        $out->elementEnd('p');
-    }
-}
diff --git a/plugins/Bookmark/bookmarkpopup.php b/plugins/Bookmark/bookmarkpopup.php
deleted file mode 100644 (file)
index 1400c0a..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Post a new bookmark in a popup window
- *
- * 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  Bookmark
- * @package   StatusNet
- * @author    Sarven Capadisli <csarven@status.net>
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008-2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Action for posting a new bookmark
- *
- * @category Bookmark
- * @package  StatusNet
- * @author   Sarven Capadisli <csarven@status.net>
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link     http://status.net/
- */
-class BookmarkpopupAction extends NewbookmarkAction
-{
-    /**
-     * Show the title section of the window
-     *
-     * @return void
-     */
-    function showTitle()
-    {
-        $this->element('title',
-                       // TRANS: Title for mini-posting window loaded from bookmarklet.
-                       // TRANS: %s is the StatusNet site name.
-                       null, sprintf(_m('Bookmark on %s'),
-                                     common_config('site', 'name')));
-    }
-
-    /**
-     * Show the header section of the page
-     *
-     * Shows a stub page and the bookmark form.
-     *
-     * @return void
-     */
-    function showHeader()
-    {
-        $this->elementStart('div', array('id' => 'header'));
-        $this->elementStart('address');
-        $this->element('a', array('class' => 'url',
-                                  'href' => common_local_url('top')),
-                         '');
-        $this->elementEnd('address');
-        if (common_logged_in()) {
-            $form = new BookmarkForm($this,
-                                     $this->title,
-                                     $this->url);
-            $form->show();
-        }
-        $this->elementEnd('div');
-    }
-
-    /**
-     * Hide the core section of the page
-     *
-     * @return void
-     */
-    function showCore()
-    {
-    }
-
-    /**
-     * Hide the footer section of the page
-     *
-     * @return void
-     */
-    function showFooter()
-    {
-    }
-
-    function showScripts()
-    {
-        parent::showScripts();
-        $this->script(Plugin::staticPath('Bookmark', 'bookmarkpopup.js'));
-    }
-}
diff --git a/plugins/Bookmark/bookmarks.php b/plugins/Bookmark/bookmarks.php
deleted file mode 100644 (file)
index 8c786d6..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-<?php
-/**
- * Give a warm greeting to our friendly user
- *
- * PHP version 5
- *
- * @category Bookmark
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once 'bookmarksnoticestream.php';
-
-/**
- * List currently logged-in user's bookmakrs
- *
- * @category Bookmark
- * @package  StatusNet
- * @author   Stephane Berube <chimo@chromic.org>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     https://github.com/chimo/BookmarkList
- */
-class BookmarksAction extends Action
-{
-    var $user = null;
-    var $gc   = null;
-
-    /**
-     * Take arguments for running
-     *
-     * This method is called first, and it lets the action class get
-     * all its arguments and validate them. It's also the time
-     * to fetch any relevant data from the database.
-     *
-     * Action classes should run parent::prepare($args) as the first
-     * line of this method to make sure the default argument-processing
-     * happens.
-     *
-     * @param array $args $_REQUEST args
-     *
-     * @return boolean success flag
-     */
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        if (common_config('singleuser', 'enabled')) {
-            $nickname = User::singleUserNickname();
-        } else {
-            // PHP 5.4
-            // $nickname = $this->returnToArgs()[1]['nickname'];
-
-            // PHP < 5.4
-            $nickname = $this->returnToArgs();
-            $nickname = $nickname[1]['nickname'];
-        }
-        
-        $this->user = User::getKV('nickname', $nickname);
-
-        if (!$this->user) {
-            // TRANS: Client error displayed when trying to display bookmarks for a non-existing user.
-            $this->clientError(_('No such user.'));
-            return false;
-        }
-
-        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
-
-        $stream = new BookmarksNoticeStream($this->user->id, true);
-        $this->notices = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
-                                                NOTICES_PER_PAGE + 1); 
-
-        if($this->page > 1 && $this->notices->N == 0) {
-            throw new ClientException(_('No such page.'), 404);
-        }
-
-        return true;
-    }
-
-    /**
-     * Handle request
-     *
-     * This is the main method for handling a request. Note that
-     * most preparation should be done in the prepare() method;
-     * by the time handle() is called the action should be
-     * more or less ready to go.
-     *
-     * @param array $args $_REQUEST args; handled in prepare()
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-        $this->showPage();
-    }
-
-    /**
-     * Title of this page
-     *
-     * Override this method to show a custom title.
-     *
-     * @return string Title of the page
-     */
-    function title()
-    {
-
-        if (empty($this->user)) {
-            // TRANS: Page title for sample plugin.
-            return _m('Log in');
-        } else {
-            // TRANS: Page title for sample plugin. %s is a user nickname.
-            return sprintf(_m('%s\'s bookmarks'), $this->user->nickname);
-        }
-    }
-
-    /**
-     * Feeds for the <head> section
-     *
-     * @return array Feed objects to show
-     */
-    function getFeeds()
-    {
-        return array(new Feed(Feed::JSON,
-                              common_local_url('ApiTimelineBookmarks',
-                                               array(
-                                                    'id' => $this->user->nickname,
-                                                    'format' => 'as')),
-                              // TRANS: Feed link text. %s is a username.
-                              sprintf(_('Feed for favorites of %s (Activity Streams JSON)'),
-                                      $this->user->nickname)),
-                     new Feed(Feed::RSS1,
-                              common_local_url('bookmarksrss',
-                                               array('nickname' => $this->user->nickname)),
-                              // TRANS: Feed link text. %s is a username.
-                              sprintf(_('Feed for favorites of %s (RSS 1.0)'),
-                                      $this->user->nickname)),
-                     new Feed(Feed::RSS2,
-                              common_local_url('ApiTimelineBookmarks',
-                                               array(
-                                                    'id' => $this->user->nickname,
-                                                    'format' => 'rss')),
-                              // TRANS: Feed link text. %s is a username.
-                              sprintf(_('Feed for favorites of %s (RSS 2.0)'),
-                                      $this->user->nickname)),
-                     new Feed(Feed::ATOM,
-                              common_local_url('ApiTimelineBookmarks',
-                                               array(
-                                                    'id' => $this->user->nickname,
-                                                    'format' => 'atom')),
-                              // TRANS: Feed link text. %s is a username.
-                              sprintf(_('Feed for favorites of %s (Atom)'),
-                                      $this->user->nickname)));
-    }
-
-    /**
-     * Show content in the content area
-     *
-     * The default StatusNet page has a lot of decorations: menus,
-     * logos, tabs, all that jazz. This method is used to show
-     * content in the content area of the page; it's the main
-     * thing you want to overload.
-     *
-     * This method also demonstrates use of a plural localized string.
-     *
-     * @return void
-     */
-    function showContent()
-    {
-
-        $nl = new NoticeList($this->notices, $this);
-
-        $cnt = $nl->show();
-
-        if ($cnt == 0) {
-            $this->showEmptyList();
-        }
-
-        $this->pagination($this->page > 1,
-                $cnt > NOTICES_PER_PAGE,
-                $this->page, 'bookmarks',
-                array('nickname' => $this->user->nickname));
-    }
-
-    function showEmptyList() {
-        $message = sprintf(_('This is %1$s\'s bookmark stream, but %1$s hasn\'t bookmarked anything yet.'), $this->user->nickname) . ' ';
-
-        $this->elementStart('div', 'guide');
-        $this->raw(common_markup_to_html($message));
-        $this->elementEnd('div');
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * Some actions only read from the database; others read and write.
-     * The simple database load-balancer built into StatusNet will
-     * direct read-only actions to database mirrors (if they are configured),
-     * and read-write actions to the master database.
-     *
-     * This defaults to false to avoid data integrity issues, but you
-     * should make sure to overload it for performance gains.
-     *
-     * @param array $args other arguments, if RO/RW status depends on them.
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        return true;
-    }
-}
diff --git a/plugins/Bookmark/bookmarksnoticestream.php b/plugins/Bookmark/bookmarksnoticestream.php
deleted file mode 100644 (file)
index a3ac035..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-class RawBookmarksNoticeStream extends NoticeStream
-{
-    protected $user_id;
-    protected $own;
-
-    function __construct($user_id, $own)
-    {
-        $this->user_id = $user_id;
-        $this->own     = $own;
-    }
-
-    function getNoticeIds($offset, $limit, $since_id, $max_id)
-    {
-        $notice = new Notice();
-        $qry = null;
-
-        $qry =  'SELECT notice.* FROM notice ';
-        $qry .= 'INNER JOIN bookmark ON bookmark.uri = notice.uri ';
-        $qry .= 'WHERE bookmark.profile_id = ' . $this->user_id . ' ';
-        $qry .= 'AND notice.is_local != ' . Notice::GATEWAY . ' ';
-
-        if ($since_id != 0) {
-            $qry .= 'AND notice.id > ' . $since_id . ' ';
-        }
-
-        if ($max_id != 0) {
-            $qry .= 'AND notice.id <= ' . $max_id . ' ';
-        }
-
-        // NOTE: we sort by bookmark time, not by notice time!
-        $qry .= 'ORDER BY created DESC ';
-        if (!is_null($offset)) {
-            $qry .= "LIMIT $limit OFFSET $offset";
-        }
-
-        $notice->query($qry);
-        $ids = array();
-        while ($notice->fetch()) {
-            $ids[] = $notice->id;
-        }
-
-        $notice->free();
-        unset($notice);
-        return $ids;
-    }
-}
-
-/**
- * Notice stream for bookmarks
- *
- * @category  Stream
- * @package   StatusNet
- * @author    Stephane Berube <chimo@chromic.org>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class BookmarksNoticeStream extends ScopingNoticeStream
-{
-    function __construct($user_id, $own, $profile = -1)
-    {
-        $stream = new RawBookmarksNoticeStream($user_id, $own);
-
-        if ($own) {
-            $key = 'bookmark:ids_by_user_own:'.$user_id;
-        } else {
-            $key = 'bookmark:ids_by_user:'.$user_id;
-        }
-
-        if (is_int($profile) && $profile == -1) {
-            $profile = Profile::current();
-        }
-
-        parent::__construct(new CachingNoticeStream($stream, $key),
-                            $profile);
-    }
-}
diff --git a/plugins/Bookmark/bookmarksrss.php b/plugins/Bookmark/bookmarksrss.php
deleted file mode 100644 (file)
index a52a0b7..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-<?php
-/**
- * RSS feed for user bookmarks action class.
- *
- * PHP version 5
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @author   Robin Millette <millette@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/rssaction.php';
-require_once 'bookmarksnoticestream.php';
-
-/**
- * RSS feed for user bookmarks action class.
- *
- * Formatting of RSS handled by Rss10Action
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @author   Robin Millette <millette@status.net>
- * @author   Zach Copley <zach@status.net>
- * @author   Stephane Berube <chimo@chromic.org> (modified 'favoritesrss.php' to show bookmarks instead)
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- */
-class BookmarksrssAction extends Rss10Action
-{
-    /** The user whose bookmarks to display */
-
-    var $user = null;
-
-    /**
-     * Find the user to display by supplied nickname
-     *
-     * @param array $args Arguments from $_REQUEST
-     *
-     * @return boolean success
-     */
-    function prepare($args)
-    {        
-        parent::prepare($args);
-
-        $nickname   = $this->trimmed('nickname');
-        $this->user = User::getKV('nickname', $nickname);
-
-        if (!$this->user) {
-            // TRANS: Client error displayed when trying to get the RSS feed with bookmarks of a user that does not exist.
-            $this->clientError(_('No such user.'));
-            return false;
-        } else {
-            $this->notices = $this->getNotices($this->limit);
-            return true;
-        }
-    }
-
-    /**
-     * Get notices
-     *
-     * @param integer $limit max number of notices to return
-     *
-     * @return array notices
-     */
-    function getNotices($limit=0)
-    {
-        $user    = $this->user;
-
-        $notice = new BookmarksNoticeStream($this->user->id, true);
-        $notice = $notice->getNotices(0, NOTICES_PER_PAGE);
-
-        $notices = array();
-        while ($notice->fetch()) {
-            $notices[] = clone($notice);
-        }
-        return $notices;
-    }
-
-     /**
-     * Get channel.
-     *
-     * @return array associative array on channel information
-     */
-    function getChannel()
-    {
-        $user = $this->user;
-        $c    = array('url' => common_local_url('bookmarksrss',
-                                        array('nickname' =>
-                                        $user->nickname)),
-                   // TRANS: Title of RSS feed with bookmarks of a user.
-                   // TRANS: %s is a user's nickname.
-                   'title' => sprintf(_("%s's bookmarks"), $user->nickname),
-                   'link' => common_local_url('bookmarks',
-                                        array('nickname' =>
-                                        $user->nickname)),
-                   // TRANS: Desciption of RSS feed with bookmarks of a user.
-                   // TRANS: %1$s is a user's nickname, %2$s is the name of the StatusNet site.
-                   'description' => sprintf(_('Bookmarks posted by %1$s on %2$s!'),
-                                        $user->nickname, common_config('site', 'name')));
-        return $c;
-    }
-
-    /**
-     * Get image.
-     *
-     * @return void
-    */
-    function getImage()
-    {
-        return null;
-    }
-
-}
diff --git a/plugins/Bookmark/classes/Bookmark.php b/plugins/Bookmark/classes/Bookmark.php
new file mode 100644 (file)
index 0000000..65c767e
--- /dev/null
@@ -0,0 +1,296 @@
+<?php
+/**
+ * Data class to mark notices as bookmarks
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * For storing the fact that a notice is a bookmark
+ *
+ * @category Bookmark
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Bookmark extends Managed_DataObject
+{
+    public $__table = 'bookmark'; // table name
+    public $id;          // char(36) primary_key not_null
+    public $profile_id;  // int(4) not_null
+    public $url;         // varchar(255) not_null
+    public $title;       // varchar(255)
+    public $description; // text
+    public $uri;         // varchar(255)
+    public $created;     // datetime
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'id' => array('type' => 'char',
+                            'length' => 36,
+                            'not null' => true),
+                'profile_id' => array('type' => 'int', 'not null' => true),
+                'uri' => array('type' => 'varchar',
+                            'length' => 255,
+                            'not null' => true),
+                'url' => array('type' => 'varchar',
+                            'length' => 255,
+                            'not null' => true),
+                'title' => array('type' => 'varchar', 'length' => 255),
+                'description' => array('type' => 'text'),
+                'created' => array('type' => 'datetime', 'not null' => true),
+            ),
+            'primary key' => array('id'),
+            'unique keys' => array(
+                'bookmark_uri_key' => array('uri'),
+            ),
+            'foreign keys' => array(
+                'bookmark_profile_id_fkey' => array('profile', array('profile_id' => 'id'))
+            ),
+            'indexes' => array('bookmark_created_idx' => array('created'),
+                            'bookmark_url_idx' => array('url'),
+                            'bookmark_profile_id_idx' => array('profile_id'),
+            ),
+        );
+    }
+
+    /**
+     * Get a bookmark based on a notice
+     *
+     * @param Notice $notice Notice to check for
+     *
+     * @return Bookmark found bookmark or null
+     */
+    static function getByNotice($notice)
+    {
+        return self::getKV('uri', $notice->uri);
+    }
+
+    /**
+     * Get the bookmark that a user made for an URL
+     *
+     * @param Profile $profile Profile to check for
+     * @param string  $url     URL to check for
+     *
+     * @return Bookmark bookmark found or null
+     */
+    static function getByURL($profile, $url)
+    {
+        $nb = new Bookmark();
+
+        $nb->profile_id = $profile->id;
+        $nb->url        = $url;
+
+        if ($nb->find(true)) {
+            return $nb;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Save a new notice bookmark
+     *
+     * @param Profile $profile     To save the bookmark for
+     * @param string  $title       Title of the bookmark
+     * @param string  $url         URL of the bookmark
+     * @param mixed   $rawtags     array of tags or string
+     * @param string  $description Description of the bookmark
+     * @param array   $options     Options for the Notice::saveNew()
+     *
+     * @return Notice saved notice
+     */
+    static function saveNew($profile, $title, $url, $rawtags, $description,
+                            $options=null)
+    {
+        $nb = self::getByURL($profile, $url);
+
+        if (!empty($nb)) {
+            // TRANS: Client exception thrown when trying to save a new bookmark that already exists.
+            throw new ClientException(_m('Bookmark already exists.'));
+        }
+
+        if (empty($options)) {
+            $options = array();
+        }
+
+        if (array_key_exists('uri', $options)) {
+            $other = Bookmark::getKV('uri', $options['uri']);
+            if (!empty($other)) {
+                // TRANS: Client exception thrown when trying to save a new bookmark that already exists.
+                throw new ClientException(_m('Bookmark already exists.'));
+            }
+        }
+
+        if (is_string($rawtags)) {
+            if (empty($rawtags)) {
+                $rawtags = array();
+            } else {
+                $rawtags = preg_split('/[\s,]+/', $rawtags);
+            }
+        }
+
+        $nb = new Bookmark();
+
+        $nb->id          = UUID::gen();
+        $nb->profile_id  = $profile->id;
+        $nb->url         = $url;
+        $nb->title       = $title;
+        $nb->description = $description;
+
+        if (array_key_exists('created', $options)) {
+            $nb->created = $options['created'];
+        } else {
+            $nb->created = common_sql_now();
+        }
+
+        if (array_key_exists('uri', $options)) {
+            $nb->uri = $options['uri'];
+        } else {
+            // FIXME: hacks to work around router bugs in
+            // queue daemons
+
+            $r = Router::get();
+
+            $path = $r->build('showbookmark',
+                              array('id' => $nb->id));
+
+            if (empty($path)) {
+                $nb->uri = common_path('bookmark/'.$nb->id, false, false);
+            } else {
+                $nb->uri = common_local_url('showbookmark',
+                                            array('id' => $nb->id),
+                                            null,
+                                            null,
+                                            false);
+            }
+        }
+
+        $nb->insert();
+
+        $tags    = array();
+        $replies = array();
+
+        // filter "for:nickname" tags
+
+        foreach ($rawtags as $tag) {
+            if (strtolower(mb_substr($tag, 0, 4)) == 'for:') {
+                // skip if done by caller
+                if (!array_key_exists('replies', $options)) {
+                    $nickname = mb_substr($tag, 4);
+                    $other    = common_relative_profile($profile,
+                                                        $nickname);
+                    if (!empty($other)) {
+                        $replies[] = $other->getUri();
+                    }
+                }
+            } else {
+                $tags[] = common_canonical_tag($tag);
+            }
+        }
+
+        $hashtags = array();
+        $taglinks = array();
+
+        foreach ($tags as $tag) {
+            $hashtags[] = '#'.$tag;
+            $attrs      = array('href' => Notice_tag::url($tag),
+                                'rel'  => $tag,
+                                'class' => 'tag');
+            $taglinks[] = XMLStringer::estring('a', $attrs, $tag);
+        }
+
+        // Use user's preferences for short URLs, if possible
+
+        try {
+            $user = User::getKV('id', $profile->id);
+
+            $shortUrl = File_redirection::makeShort($url,
+                                                    empty($user) ? null : $user);
+        } catch (Exception $e) {
+            // Don't let this stop us.
+            $shortUrl = $url;
+        }
+
+        // TRANS: Bookmark content.
+        // TRANS: %1$s is a title, %2$s is a short URL, %3$s is the bookmark description,
+       // TRANS: %4$s is space separated list of hash tags.
+        $content = sprintf(_m('"%1$s" %2$s %3$s %4$s'),
+                           $title,
+                           $shortUrl,
+                           $description,
+                           implode(' ', $hashtags));
+
+        // TRANS: Rendered bookmark content.
+        // TRANS: %1$s is a URL, %2$s the bookmark title, %3$s is the bookmark description,
+       // TRANS: %4$s is space separated list of hash tags.
+        $rendered = sprintf(_m('<span class="xfolkentry">'.
+                              '<a class="taggedlink" href="%1$s">%2$s</a> '.
+                              '<span class="description">%3$s</span> '.
+                              '<span class="meta">%4$s</span>'.
+                              '</span>'),
+                            htmlspecialchars($url),
+                            htmlspecialchars($title),
+                            htmlspecialchars($description),
+                            implode(' ', $taglinks));
+
+        $options = array_merge(array('urls' => array($url),
+                                     'rendered' => $rendered,
+                                     'tags' => $tags,
+                                     'replies' => $replies,
+                                     'object_type' => ActivityObject::BOOKMARK),
+                               $options);
+
+        if (!array_key_exists('uri', $options)) {
+            $options['uri'] = $nb->uri;
+        }
+
+        try {
+            $saved = Notice::saveNew($profile->id,
+                                     $content,
+                                     array_key_exists('source', $options) ?
+                                     $options['source'] : 'web',
+                                     $options);
+        } catch (Exception $e) {
+            $nb->delete();
+            throw $e;
+        }
+
+        if (empty($saved)) {
+            $nb->delete();
+        }
+
+        return $saved;
+    }
+}
diff --git a/plugins/Bookmark/deliciousbackupimporter.php b/plugins/Bookmark/deliciousbackupimporter.php
deleted file mode 100644 (file)
index 0ceba61..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * Importer class for Delicious.com backups
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Importer class for Delicious bookmarks
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class DeliciousBackupImporter extends QueueHandler
-{
-    /**
-     * Transport of the importer
-     *
-     * @return string transport string
-     */
-    function transport()
-    {
-        return 'dlcsback';
-    }
-
-    /**
-     * Import an in-memory bookmark list to a user's account
-     *
-     * Take a delicious.com backup file (same as Netscape bookmarks.html)
-     * and import to StatusNet as Bookmark activities.
-     *
-     * The document format is terrible. It consists of a <dl> with
-     * a bunch of <dt>'s, occasionally with <dd>'s adding descriptions.
-     * There are sometimes <p>'s lost inside.
-     *
-     * @param array $data pair of user, text
-     *
-     * @return boolean success value
-     */
-    function handle($data)
-    {
-        list($user, $body) = $data;
-
-        try {
-            $doc = $this->importHTML($body);
-        } catch (ClientException $cex) {
-            // XXX: message to the user
-            common_log(LOG_WARNING, $cex->getMessage());
-            return true;
-        }
-
-        // If we can't parse it, it's no good
-
-        if (empty($doc)) {
-            return true;
-        }
-
-        $dls = $doc->getElementsByTagName('dl');
-
-        if ($dls->length != 1) {
-            // XXX: message to the user
-            common_log(LOG_WARNING, 'Bad input file');
-            return true;
-        }
-
-        $dl = $dls->item(0);
-
-        $children = $dl->childNodes;
-
-        $dt = null;
-
-        for ($i = 0; $i < $children->length; $i++) {
-            try {
-                $child = $children->item($i);
-                if ($child->nodeType != XML_ELEMENT_NODE) {
-                    continue;
-                }
-                switch (strtolower($child->tagName)) {
-                case 'dt':
-                    // <dt> nodes contain primary information about a bookmark.
-                    // We can't import the current one just yet though, since
-                    // it may be followed by a <dd>.
-                    if (!empty($dt)) {
-                        // No DD provided
-                        $this->importBookmark($user, $dt);
-                        $dt = null;
-                    }
-                    $dt = $child;
-                    break;
-                case 'dd':
-                    $dd = $child;
-
-                    if (!empty($dt)) {
-                        // This <dd> contains a description for the bookmark in
-                        // the preceding <dt> node.
-                        $saved = $this->importBookmark($user, $dt, $dd);
-                    }
-
-                    $dt = null;
-                    $dd = null;
-                    break;
-                case 'p':
-                    common_log(LOG_INFO, 'Skipping the <p> in the <dl>.');
-                    break;
-                default:
-                    common_log(LOG_WARNING,
-                               "Unexpected element $child->tagName ".
-                               " found in import.");
-                }
-            } catch (Exception $e) {
-                common_log(LOG_ERR, $e->getMessage());
-                $dt = $dd = null;
-            }
-        }
-        if (!empty($dt)) {
-            // There was a final bookmark without a description.
-            try {
-                $this->importBookmark($user, $dt);
-            } catch (Exception $e) {
-                common_log(LOG_ERR, $e->getMessage());
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Import a single bookmark
-     *
-     * Takes a <dt>/<dd> pair. The <dt> has a single
-     * <a> in it with some non-standard attributes.
-     *
-     * A <dt><dt><dd> sequence will appear as a <dt> with
-     * anothe <dt> as a child. We handle this case recursively.
-     *
-     * @param User       $user User to import data as
-     * @param DOMElement $dt   <dt> element
-     * @param DOMElement $dd   <dd> element
-     *
-     * @return Notice imported notice
-     */
-    function importBookmark($user, $dt, $dd = null)
-    {
-        $as = $dt->getElementsByTagName('a');
-
-        if ($as->length == 0) {
-            // TRANS: Client exception thrown when a bookmark in an import file is incorrectly formatted.
-            throw new ClientException(_m("No <A> tag in a <DT>."));
-        }
-
-        $a = $as->item(0);
-
-        $private = $a->getAttribute('private');
-
-        if ($private != 0) {
-            // TRANS: Client exception thrown when a bookmark in an import file is private.
-            throw new ClientException(_m('Skipping private bookmark.'));
-        }
-
-        if (!empty($dd)) {
-            $description = $dd->nodeValue;
-        } else {
-            $description = null;
-        }
-        $addDate = $a->getAttribute('add_date');
-
-        $data = array(
-            'profile_id' => $user->id,
-            'title' => $a->nodeValue,
-            'description' => $description,
-            'url' => $a->getAttribute('href'),
-            'tags' => $a->getAttribute('tags'),
-            'created' => common_sql_date(intval($addDate))
-        );
-
-        $qm = QueueManager::get();
-        $qm->enqueue($data, 'dlcsbkmk');
-    }
-
-    /**
-     * Parse some HTML
-     *
-     * Hides the errors that the dom parser returns
-     *
-     * @param string $body Data to import
-     *
-     * @return DOMDocument parsed document
-     */
-
-    function importHTML($body)
-    {
-        // DOMDocument::loadHTML may throw warnings on unrecognized elements,
-        // and notices on unrecognized namespaces.
-        $old = error_reporting(error_reporting() & ~(E_WARNING | E_NOTICE));
-        $dom = new DOMDocument();
-        $ok  = $dom->loadHTML($body);
-        error_reporting($old);
-
-        if ($ok) {
-            foreach ($dom->getElementsByTagName('body') as $node) {
-                $this->fixListsIn($node);
-            }
-            return $dom;
-        } else {
-            return null;
-        }
-    }
-
-
-    function fixListsIn(DOMNode $body) {
-        $toFix = array();
-
-        foreach ($body->childNodes as $node) {
-            if ($node->nodeType == XML_ELEMENT_NODE) {
-                $el = strtolower($node->nodeName);
-                if ($el == 'dl') {
-                    $toFix[] = $node;
-                }
-            }
-        }
-
-        foreach ($toFix as $node) {
-            $this->fixList($node);
-        }
-    }
-
-    function fixList(DOMNode $list) {
-        $toFix = array();
-
-        foreach ($list->childNodes as $node) {
-            if ($node->nodeType == XML_ELEMENT_NODE) {
-                $el = strtolower($node->nodeName);
-                if ($el == 'dt' || $el == 'dd') {
-                    $toFix[] = $node;
-                }
-                if ($el == 'dl') {
-                    // Sublist.
-                    // Technically, these can only appear inside a <dd>...
-                    $this->fixList($node);
-                }
-            }
-        }
-
-        foreach ($toFix as $node) {
-            $this->fixListItem($node);
-        }
-    }
-
-    function fixListItem(DOMNode $item) {
-        // The HTML parser in libxml2 doesn't seem to properly handle
-        // many cases of implied close tags, apparently because it doesn't
-        // understand the nesting rules specified in the HTML DTD.
-        //
-        // This leads to sequences of adjacent <dt>s or <dd>s being incorrectly
-        // interpreted as parent->child trees instead of siblings:
-        //
-        // When parsing this input: "<dt>aaa <dt>bbb"
-        // should be equivalent to: "<dt>aaa </dt><dt>bbb</dt>"
-        // but we're seeing instead: "<dt>aaa <dt>bbb</dt></dt>"
-        //
-        // It does at least know that going from dt to dd, or dd to dt,
-        // should make a break.
-
-        $toMove = array();
-
-        foreach ($item->childNodes as $node) {
-            if ($node->nodeType == XML_ELEMENT_NODE) {
-                $el = strtolower($node->nodeName);
-                if ($el == 'dt' || $el == 'dd') {
-                    // dt & dd cannot contain each other;
-                    // This node was incorrectly placed; move it up a level!
-                    $toMove[] = $node;
-                }
-                if ($el == 'dl') {
-                    // Sublist.
-                    // Technically, these can only appear inside a <dd>.
-                    $this->fixList($node);
-                }
-            }
-        }
-
-        $parent = $item->parentNode;
-        $next = $item->nextSibling;
-        foreach ($toMove as $node) {
-            $item->removeChild($node);
-            $parent->insertBefore($node, $next);
-            $this->fixListItem($node);
-        }
-    }
-}
diff --git a/plugins/Bookmark/deliciousbookmarkimporter.php b/plugins/Bookmark/deliciousbookmarkimporter.php
deleted file mode 100644 (file)
index eca9e96..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * Importer class for Delicious.com bookmarks
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Importer class for Delicious bookmarks
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class DeliciousBookmarkImporter extends QueueHandler
-{
-    /**
-     * Return the transport for this queue handler
-     *
-     * @return string 'dlcsbkmk'
-     */
-    function transport()
-    {
-        return 'dlcsbkmk';
-    }
-
-    /**
-     * Handle the data
-     *
-     * @param array $data associative array of user & bookmark info from DeliciousBackupImporter::importBookmark()
-     *
-     * @return boolean success value
-     */
-    function handle($data)
-    {
-        $profile = Profile::getKV('id', $data['profile_id']);
-
-        try {
-            $saved = Bookmark::saveNew($profile,
-                                       $data['title'],
-                                       $data['url'],
-                                       $data['tags'],
-                                       $data['description'],
-                                       array('created' => $data['created'],
-                                             'distribute' => false));
-        } catch (ClientException $ce) {
-            // Most likely a duplicate -- continue on with the rest!
-            common_log(LOG_ERR, "Error importing delicious bookmark to $data[url]: " . $ce->getMessage());
-            return true;
-        } catch (Exception $ex) {
-            if (preg_match("/DB Error: already exists/", $ex->getMessage())) {
-                common_log(LOG_ERR, "Error importing delicious bookmark to $data[url]: " . $ce->getMessage());
-                return true;
-            } else {
-                throw $ex;
-            }
-        }
-
-        return true;
-    }
-}
diff --git a/plugins/Bookmark/forms/bookmark.php b/plugins/Bookmark/forms/bookmark.php
new file mode 100644 (file)
index 0000000..20101e1
--- /dev/null
@@ -0,0 +1,204 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Form for adding a new bookmark
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form to add a new bookmark
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class BookmarkForm extends Form
+{
+    private $_title       = null;
+    private $_url         = null;
+    private $_tags        = null;
+    private $_description = null;
+    private $_thumbnail   = null;
+
+    /**
+     * Construct a bookmark form
+     *
+     * @param HTMLOutputter $out         output channel
+     * @param string        $title       Title of the bookmark
+     * @param string        $url         URL of the bookmark
+     * @param string        $tags        Tags to show
+     * @param string        $description Description of the bookmark
+     *
+     * @return void
+     */
+    function __construct($out=null, $title=null, $url=null, $tags=null,
+                         $description=null, $thumbnail=null)
+    {
+        parent::__construct($out);
+
+        $this->_title       = $title;
+        $this->_url         = $url;
+        $this->_tags        = $tags;
+        $this->_description = $description;
+        $this->_thumbnail   = $thumbnail;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'form_new_bookmark';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings ajax-notice';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('newbookmark');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('fieldset', array('id' => 'new_bookmark_data'));
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->out->input('bookmark-url',
+                          // TRANS: Field label on form for adding a new bookmark.
+                          _m('LABEL','URL'),
+                          $this->_url,
+                          null,
+                          'url');
+        $this->unli();
+
+        if (!empty($this->_thumbnail)) {
+
+            list($width, $height) = $this->scaleImage($this->_thumbnail->width,
+                                                      $this->_thumbnail->height);
+
+            $this->out->element('img',
+                                array('src' => $this->_thumbnail->url,
+                                      'class' => 'bookmarkform-thumbnail',
+                                      'width' => $width,
+                                      'height' => $height));
+        }
+
+        $this->li();
+        $this->out->input('bookmark-title',
+                          // TRANS: Field label on form for adding a new bookmark.
+                          _m('LABEL','Title'),
+                          $this->_title,
+                          null,
+                          'title');
+        $this->unli();
+
+        $this->li();
+        $this->out->textarea('bookmark-description',
+                             // TRANS: Field label on form for adding a new bookmark.
+                             _m('LABEL','Notes'),
+                             $this->_description,
+                             null,
+                             'description');
+        $this->unli();
+
+        $this->li();
+        $this->out->input('bookmark-tags',
+                          // TRANS: Field label on form for adding a new bookmark.
+                          _m('LABEL','Tags'),
+                          $this->_tags,
+                          // TRANS: Field title on form for adding a new bookmark.
+                          _m('Comma- or space-separated list of tags.'),
+                          'tags');
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+
+        $toWidget = new ToSelector($this->out,
+                                   common_current_user(),
+                                   null);
+        $toWidget->show();
+
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+        // TRANS: Button text for action to save a new bookmark.
+        $this->out->submit('bookmark-submit', _m('BUTTON', 'Save'), 'submit', 'submit');
+    }
+
+    function scaleImage($width, $height)
+    {
+        $maxwidth = common_config('attachments', 'thumb_width');
+        $maxheight = common_config('attachments', 'thumb_height');
+
+        if ($width > $height && $width > $maxwidth) {
+            $height = (int) ((((float)$maxwidth)/(float)($width))*(float)$height);
+            $width = $maxwidth;
+        } else if ($height > $maxheight) {
+            $width = (int) ((((float)$maxheight)/(float)($height))*(float)$width);
+            $height = $maxheight;
+        }
+
+        return array($width, $height);
+    }
+}
diff --git a/plugins/Bookmark/forms/initialbookmark.php b/plugins/Bookmark/forms/initialbookmark.php
new file mode 100644 (file)
index 0000000..a82941b
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * First bookmark form, with just the URL
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Form
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * First bookmark form, with just the URL
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class InitialBookmarkForm extends Form
+{
+    function __construct($out=null)
+    {
+        parent::__construct($out);
+    }
+
+    function id()
+    {
+        return 'form_initial_bookmark';
+    }
+
+    function formClass()
+    {
+        return 'form_settings ajax';
+    }
+
+    function action()
+    {
+        return common_local_url('bookmarkforurl');
+    }
+
+    function formData()
+    {
+        $this->out->elementStart('fieldset', array('id' => 'initial_bookmark_data'));
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->out->input('initial-bookmark-url',
+                          // TRANS: Field label on form for adding a new bookmark.
+                          _m('LABEL','URL'),
+                          null,
+                          null,
+                          'url');
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+        $this->out->elementEnd('fieldset');
+    }
+
+    function formActions()
+    {
+        // TRANS: Button text for action to save a new bookmark.
+        $this->out->submit('initial-bookmark-submit', _m('BUTTON', 'Add'), 'submit', 'submit');
+    }
+}
diff --git a/plugins/Bookmark/importbookmarks.php b/plugins/Bookmark/importbookmarks.php
deleted file mode 100644 (file)
index c47a042..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010 StatusNet, Inc.
- *
- * Import a bookmarks file as notices
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
-
-$shortoptions = 'i:n:f:';
-$longoptions  = array('id=', 'nickname=', 'file=');
-
-$helptext = <<<END_OF_IMPORTBOOKMARKS_HELP
-importbookmarks.php [options]
-Restore a backed-up Delicious.com bookmark file
-
--i --id       ID of user to import bookmarks for
--n --nickname nickname of the user to import for
--f --file     file to read from (STDIN by default)
-END_OF_IMPORTBOOKMARKS_HELP;
-
-require_once INSTALLDIR.'/scripts/commandline.inc';
-
-/**
- * Get the bookmarks file as a string
- *
- * Uses the -f or --file parameter to open and read a
- * a bookmarks file
- *
- * @return string Contents of the file
- */
-
-function getBookmarksFile()
-{
-    $filename = get_option_value('f', 'file');
-
-    if (empty($filename)) {
-        show_help();
-        exit(1);
-    }
-
-    if (!file_exists($filename)) {
-        // TRANS: Exception thrown when a file upload cannot be found.
-        // TRANS: %s is the file that could not be found.
-        throw new Exception(sprintf(_m('No such file "%s".'),$filename));
-    }
-
-    if (!is_file($filename)) {
-        // TRANS: Exception thrown when a file upload is incorrect.
-        // TRANS: %s is the irregular file.
-        throw new Exception(sprintf(_m('Not a regular file: "%s".'),$filename));
-    }
-
-    if (!is_readable($filename)) {
-        // TRANS: Exception thrown when a file upload is not readable.
-        // TRANS: %s is the file that could not be read.
-        throw new Exception(sprintf(_m('File "%s" not readable.'),$filename));
-    }
-
-    // TRANS: %s is the filename that contains a backup for a user.
-    printfv(_m('Getting backup from file "%s".')."\n", $filename);
-
-    $html = file_get_contents($filename);
-
-    return $html;
-}
-
-try {
-    $user = getUser();
-    $html = getBookmarksFile();
-
-    $qm = QueueManager::get();
-
-    $qm->enqueue(array($user, $html), 'dlcsback');
-
-} catch (Exception $e) {
-    print $e->getMessage()."\n";
-    exit(1);
-}
diff --git a/plugins/Bookmark/importdelicious.php b/plugins/Bookmark/importdelicious.php
deleted file mode 100644 (file)
index 85a63e8..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * Import del.icio.us bookmarks backups
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * UI for importing del.icio.us bookmark backups
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class ImportdeliciousAction extends Action
-{
-    protected $success = false;
-    private $inprogress = false;
-
-    /**
-     * Return the title of the page
-     *
-     * @return string page title
-     */
-    function title()
-    {
-        // TRANS: Title for page to import del.icio.us bookmark backups on.
-        return _m("Import del.icio.us bookmarks");
-    }
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        $cur = common_current_user();
-
-        if (empty($cur)) {
-            // TRANS: Client exception thrown when trying to import bookmarks without being logged in.
-            throw new ClientException(_m('Only logged-in users can '.
-                                        'import del.icio.us backups.'),
-                                      403);
-        }
-
-        if (!$cur->hasRight(BookmarkPlugin::IMPORTDELICIOUS)) {
-            // TRANS: Client exception thrown when trying to import bookmarks without having the rights to do so.
-            throw new ClientException(_m('You may not restore your account.'), 403);
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        parent::handle($argarray);
-
-        if ($this->isPost()) {
-            $this->importDelicious();
-        } else {
-            $this->showPage();
-        }
-        return;
-    }
-
-    /**
-     * Queue a file for importation
-     *
-     * Uses the DeliciousBackupImporter class; may take a long time!
-     *
-     * @return void
-     */
-    function importDelicious()
-    {
-        $this->checkSessionToken();
-
-        if (!isset($_FILES[ImportDeliciousForm::FILEINPUT]['error'])) {
-            // TRANS: Client exception thrown when trying to import bookmarks and upload fails.
-            throw new ClientException(_m('No uploaded file.'));
-        }
-
-        switch ($_FILES[ImportDeliciousForm::FILEINPUT]['error']) {
-        case UPLOAD_ERR_OK: // success, jump out
-            break;
-        case UPLOAD_ERR_INI_SIZE:
-            // TRANS: Client exception thrown when an uploaded file is too large.
-            throw new ClientException(_m('The uploaded file exceeds the ' .
-                'upload_max_filesize directive in php.ini.'));
-            return;
-        case UPLOAD_ERR_FORM_SIZE:
-            throw new ClientException(
-            // TRANS: Client exception thrown when an uploaded file is too large.
-                _m('The uploaded file exceeds the MAX_FILE_SIZE directive' .
-                ' that was specified in the HTML form.'));
-            return;
-        case UPLOAD_ERR_PARTIAL:
-            @unlink($_FILES[ImportDeliciousForm::FILEINPUT]['tmp_name']);
-            // TRANS: Client exception thrown when a file was only partially uploaded.
-            throw new ClientException(_m('The uploaded file was only' .
-                ' partially uploaded.'));
-            return;
-        case UPLOAD_ERR_NO_FILE:
-            // No file; probably just a non-AJAX submission.
-            // TRANS: Client exception thrown when a file upload has failed.
-            throw new ClientException(_m('No uploaded file.'));
-            return;
-        case UPLOAD_ERR_NO_TMP_DIR:
-            // TRANS: Client exception thrown when a temporary folder is not present.
-            throw new ClientException(_m('Missing a temporary folder.'));
-            return;
-        case UPLOAD_ERR_CANT_WRITE:
-            // TRANS: Client exception thrown when writing to disk is not possible.
-            throw new ClientException(_m('Failed to write file to disk.'));
-            return;
-        case UPLOAD_ERR_EXTENSION:
-            // TRANS: Client exception thrown when a file upload has been stopped.
-            throw new ClientException(_m('File upload stopped by extension.'));
-            return;
-        default:
-            common_log(LOG_ERR, __METHOD__ . ": Unknown upload error " .
-                $_FILES[ImportDeliciousForm::FILEINPUT]['error']);
-            // TRANS: Client exception thrown when a file upload operation has failed.
-            throw new ClientException(_m('System error uploading file.'));
-            return;
-        }
-
-        $filename = $_FILES[ImportDeliciousForm::FILEINPUT]['tmp_name'];
-
-        try {
-            if (!file_exists($filename)) {
-                // TRANS: Server exception thrown when a file upload cannot be found.
-                // TRANS: %s is the file that could not be found.
-                throw new ServerException(sprintf(_m('No such file "%s".'),$filename));
-            }
-
-            if (!is_file($filename)) {
-                // TRANS: Server exception thrown when a file upload is incorrect.
-                // TRANS: %s is the irregular file.
-                throw new ServerException(sprintf(_m('Not a regular file: "%s".'),$filename));
-            }
-
-            if (!is_readable($filename)) {
-                // TRANS: Server exception thrown when a file upload is not readable.
-                // TRANS: %s is the file that could not be read.
-                throw new ServerException(sprintf(_m('File "%s" not readable.'),$filename));
-            }
-
-            common_debug(sprintf("Getting backup from file '%s'.", $filename));
-
-            $html = file_get_contents($filename);
-
-            // Enqueue for processing.
-
-            $qm = QueueManager::get();
-            $qm->enqueue(array(common_current_user(), $html), 'dlcsback');
-
-            if ($qm instanceof UnQueueManager) {
-                // No active queuing means we've actually just completed the job!
-                $this->success = true;
-            } else {
-                // We've fed data into background queues, and it's probably still running.
-                $this->inprogress = true;
-            }
-
-            $this->showPage();
-
-        } catch (Exception $e) {
-            // Delete the file and re-throw
-            @unlink($_FILES[ImportDeliciousForm::FILEINPUT]['tmp_name']);
-            throw $e;
-        }
-    }
-
-    /**
-     * Show the content of the page
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        if ($this->success) {
-            $this->element('p', null,
-                           // TRANS: Success message after importing bookmarks.
-                           _m('Bookmarks have been imported. Your bookmarks should now appear in search and your profile page.'));
-        } else if ($this->inprogress) {
-            $this->element('p', null,
-                           // TRANS: Busy message for importing bookmarks.
-                           _m('Bookmarks are being imported. Please wait a few minutes for results.'));
-        } else {
-            $form = new ImportDeliciousForm($this);
-            $form->show();
-        }
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        return !$this->isPost();
-    }
-}
-
-/**
- * A form for backing up the account.
- *
- * @category  Account
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class ImportDeliciousForm extends Form
-{
-    const FILEINPUT = 'deliciousbackupfile';
-
-    /**
-     * Constructor
-     *
-     * Set the encoding type, since this is a file upload.
-     *
-     * @param HTMLOutputter $out output channel
-     *
-     * @return ImportDeliciousForm this
-     */
-    function __construct($out=null)
-    {
-        parent::__construct($out);
-        $this->enctype = 'multipart/form-data';
-    }
-
-    /**
-     * Class of the form.
-     *
-     * @return string the form's class
-     */
-    function formClass()
-    {
-        return 'form_import_delicious';
-    }
-
-    /**
-     * URL the form posts to
-     *
-     * @return string the form's action URL
-     */
-    function action()
-    {
-        return common_local_url('importdelicious');
-    }
-
-    /**
-     * Output form data
-     *
-     * Really, just instructions for doing a backup.
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('p', 'instructions');
-
-        // TRANS: Form instructions for importing bookmarks.
-        $this->out->raw(_m('You can upload a backed-up '.
-                          'delicious.com bookmarks file.'));
-
-        $this->out->elementEnd('p');
-
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->out->elementStart('li', array ('id' => 'settings_attach'));
-        $this->out->element('input', array('name' => self::FILEINPUT,
-                                           'type' => 'file',
-                                           'id' => self::FILEINPUT));
-        $this->out->elementEnd('li');
-
-        $this->out->elementEnd('ul');
-    }
-
-    /**
-     * Buttons for the form
-     *
-     * In this case, a single submit button
-     *
-     * @return void
-     */
-
-    function formActions()
-    {
-        $this->out->submit('submit',
-                           // TRANS: Button text on form to import bookmarks.
-                           _m('BUTTON', 'Upload'),
-                           'submit',
-                           null,
-                           // TRANS: Button title on form to import bookmarks.
-                           _m('Upload the file.'));
-    }
-}
diff --git a/plugins/Bookmark/initialbookmarkform.php b/plugins/Bookmark/initialbookmarkform.php
deleted file mode 100644 (file)
index a82941b..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * First bookmark form, with just the URL
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Form
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * First bookmark form, with just the URL
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class InitialBookmarkForm extends Form
-{
-    function __construct($out=null)
-    {
-        parent::__construct($out);
-    }
-
-    function id()
-    {
-        return 'form_initial_bookmark';
-    }
-
-    function formClass()
-    {
-        return 'form_settings ajax';
-    }
-
-    function action()
-    {
-        return common_local_url('bookmarkforurl');
-    }
-
-    function formData()
-    {
-        $this->out->elementStart('fieldset', array('id' => 'initial_bookmark_data'));
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-        $this->out->input('initial-bookmark-url',
-                          // TRANS: Field label on form for adding a new bookmark.
-                          _m('LABEL','URL'),
-                          null,
-                          null,
-                          'url');
-        $this->unli();
-
-        $this->out->elementEnd('ul');
-        $this->out->elementEnd('fieldset');
-    }
-
-    function formActions()
-    {
-        // TRANS: Button text for action to save a new bookmark.
-        $this->out->submit('initial-bookmark-submit', _m('BUTTON', 'Add'), 'submit', 'submit');
-    }
-}
diff --git a/plugins/Bookmark/lib/bookmarklistitem.php b/plugins/Bookmark/lib/bookmarklistitem.php
new file mode 100644 (file)
index 0000000..f9613fb
--- /dev/null
@@ -0,0 +1,144 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Adapter to show bookmarks in a nicer way
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * An adapter to show bookmarks in a nicer way
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class BookmarkListItem extends NoticeListItemAdapter
+{
+    function showNotice()
+    {
+        $this->nli->out->elementStart('div', 'entry-title');
+        $this->nli->showAuthor();
+        $this->showContent();
+        $this->nli->out->elementEnd('div');
+    }
+
+    function showContent()
+    {
+        $notice = $this->nli->notice;
+        $out    = $this->nli->out;
+
+        $nb = Bookmark::getByNotice($notice);
+
+        if (empty($nb)) {
+            common_log(LOG_ERR, "No bookmark for notice {$notice->id}");
+            parent::showContent();
+            return;
+        } else if (empty($nb->url)) {
+            common_log(LOG_ERR, "No url for bookmark {$nb->id} for notice {$notice->id}");
+            parent::showContent();
+            return;
+        }
+
+        $profile = $notice->getProfile();
+
+        $out->elementStart('p', array('class' => 'entry-content'));
+
+        // Whether to nofollow
+
+        $attrs = array('href' => $nb->url,
+                       'class' => 'bookmark-title');
+
+        $nf = common_config('nofollow', 'external');
+
+        if ($nf == 'never' || ($nf == 'sometimes' and $out instanceof ShowstreamAction)) {
+            $attrs['rel'] = 'external';
+        } else {
+            $attrs['rel'] = 'nofollow external';
+        }
+
+        $out->elementStart('h3');
+        $out->element('a',
+                      $attrs,
+                      $nb->title);
+        $out->elementEnd('h3');
+
+        // Replies look like "for:" tags
+
+        $replies = $notice->getReplies();
+        $tags = $notice->getTags();
+
+        if (!empty($replies) || !empty($tags)) {
+
+            $out->elementStart('ul', array('class' => 'bookmark-tags'));
+
+            foreach ($replies as $reply) {
+                $other = Profile::getKV('id', $reply);
+                if (!empty($other)) {
+                    $out->elementStart('li');
+                    $out->element('a', array('rel' => 'tag',
+                                             'href' => $other->profileurl,
+                                             'title' => $other->getBestName()),
+                                  sprintf('for:%s', $other->nickname));
+                    $out->elementEnd('li');
+                    $out->text(' ');
+                }
+            }
+
+            foreach ($tags as $tag) {
+                $tag = trim($tag);
+                if (!empty($tag)) {
+                    $out->elementStart('li');
+                    $out->element('a',
+                                  array('rel' => 'tag',
+                                        'href' => Notice_tag::url($tag)),
+                                  $tag);
+                    $out->elementEnd('li');
+                    $out->text(' ');
+                }
+            }
+
+            $out->elementEnd('ul');
+        }
+
+        if (!empty($nb->description)) {
+            $out->element('p',
+                          array('class' => 'bookmark-description'),
+                          $nb->description);
+        }
+
+        $out->elementEnd('p');
+    }
+}
diff --git a/plugins/Bookmark/lib/bookmarksnoticestream.php b/plugins/Bookmark/lib/bookmarksnoticestream.php
new file mode 100644 (file)
index 0000000..a3ac035
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+
+class RawBookmarksNoticeStream extends NoticeStream
+{
+    protected $user_id;
+    protected $own;
+
+    function __construct($user_id, $own)
+    {
+        $this->user_id = $user_id;
+        $this->own     = $own;
+    }
+
+    function getNoticeIds($offset, $limit, $since_id, $max_id)
+    {
+        $notice = new Notice();
+        $qry = null;
+
+        $qry =  'SELECT notice.* FROM notice ';
+        $qry .= 'INNER JOIN bookmark ON bookmark.uri = notice.uri ';
+        $qry .= 'WHERE bookmark.profile_id = ' . $this->user_id . ' ';
+        $qry .= 'AND notice.is_local != ' . Notice::GATEWAY . ' ';
+
+        if ($since_id != 0) {
+            $qry .= 'AND notice.id > ' . $since_id . ' ';
+        }
+
+        if ($max_id != 0) {
+            $qry .= 'AND notice.id <= ' . $max_id . ' ';
+        }
+
+        // NOTE: we sort by bookmark time, not by notice time!
+        $qry .= 'ORDER BY created DESC ';
+        if (!is_null($offset)) {
+            $qry .= "LIMIT $limit OFFSET $offset";
+        }
+
+        $notice->query($qry);
+        $ids = array();
+        while ($notice->fetch()) {
+            $ids[] = $notice->id;
+        }
+
+        $notice->free();
+        unset($notice);
+        return $ids;
+    }
+}
+
+/**
+ * Notice stream for bookmarks
+ *
+ * @category  Stream
+ * @package   StatusNet
+ * @author    Stephane Berube <chimo@chromic.org>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class BookmarksNoticeStream extends ScopingNoticeStream
+{
+    function __construct($user_id, $own, $profile = -1)
+    {
+        $stream = new RawBookmarksNoticeStream($user_id, $own);
+
+        if ($own) {
+            $key = 'bookmark:ids_by_user_own:'.$user_id;
+        } else {
+            $key = 'bookmark:ids_by_user:'.$user_id;
+        }
+
+        if (is_int($profile) && $profile == -1) {
+            $profile = Profile::current();
+        }
+
+        parent::__construct(new CachingNoticeStream($stream, $key),
+                            $profile);
+    }
+}
diff --git a/plugins/Bookmark/lib/deliciousbackupimporter.php b/plugins/Bookmark/lib/deliciousbackupimporter.php
new file mode 100644 (file)
index 0000000..0ceba61
--- /dev/null
@@ -0,0 +1,323 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Importer class for Delicious.com backups
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Importer class for Delicious bookmarks
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class DeliciousBackupImporter extends QueueHandler
+{
+    /**
+     * Transport of the importer
+     *
+     * @return string transport string
+     */
+    function transport()
+    {
+        return 'dlcsback';
+    }
+
+    /**
+     * Import an in-memory bookmark list to a user's account
+     *
+     * Take a delicious.com backup file (same as Netscape bookmarks.html)
+     * and import to StatusNet as Bookmark activities.
+     *
+     * The document format is terrible. It consists of a <dl> with
+     * a bunch of <dt>'s, occasionally with <dd>'s adding descriptions.
+     * There are sometimes <p>'s lost inside.
+     *
+     * @param array $data pair of user, text
+     *
+     * @return boolean success value
+     */
+    function handle($data)
+    {
+        list($user, $body) = $data;
+
+        try {
+            $doc = $this->importHTML($body);
+        } catch (ClientException $cex) {
+            // XXX: message to the user
+            common_log(LOG_WARNING, $cex->getMessage());
+            return true;
+        }
+
+        // If we can't parse it, it's no good
+
+        if (empty($doc)) {
+            return true;
+        }
+
+        $dls = $doc->getElementsByTagName('dl');
+
+        if ($dls->length != 1) {
+            // XXX: message to the user
+            common_log(LOG_WARNING, 'Bad input file');
+            return true;
+        }
+
+        $dl = $dls->item(0);
+
+        $children = $dl->childNodes;
+
+        $dt = null;
+
+        for ($i = 0; $i < $children->length; $i++) {
+            try {
+                $child = $children->item($i);
+                if ($child->nodeType != XML_ELEMENT_NODE) {
+                    continue;
+                }
+                switch (strtolower($child->tagName)) {
+                case 'dt':
+                    // <dt> nodes contain primary information about a bookmark.
+                    // We can't import the current one just yet though, since
+                    // it may be followed by a <dd>.
+                    if (!empty($dt)) {
+                        // No DD provided
+                        $this->importBookmark($user, $dt);
+                        $dt = null;
+                    }
+                    $dt = $child;
+                    break;
+                case 'dd':
+                    $dd = $child;
+
+                    if (!empty($dt)) {
+                        // This <dd> contains a description for the bookmark in
+                        // the preceding <dt> node.
+                        $saved = $this->importBookmark($user, $dt, $dd);
+                    }
+
+                    $dt = null;
+                    $dd = null;
+                    break;
+                case 'p':
+                    common_log(LOG_INFO, 'Skipping the <p> in the <dl>.');
+                    break;
+                default:
+                    common_log(LOG_WARNING,
+                               "Unexpected element $child->tagName ".
+                               " found in import.");
+                }
+            } catch (Exception $e) {
+                common_log(LOG_ERR, $e->getMessage());
+                $dt = $dd = null;
+            }
+        }
+        if (!empty($dt)) {
+            // There was a final bookmark without a description.
+            try {
+                $this->importBookmark($user, $dt);
+            } catch (Exception $e) {
+                common_log(LOG_ERR, $e->getMessage());
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Import a single bookmark
+     *
+     * Takes a <dt>/<dd> pair. The <dt> has a single
+     * <a> in it with some non-standard attributes.
+     *
+     * A <dt><dt><dd> sequence will appear as a <dt> with
+     * anothe <dt> as a child. We handle this case recursively.
+     *
+     * @param User       $user User to import data as
+     * @param DOMElement $dt   <dt> element
+     * @param DOMElement $dd   <dd> element
+     *
+     * @return Notice imported notice
+     */
+    function importBookmark($user, $dt, $dd = null)
+    {
+        $as = $dt->getElementsByTagName('a');
+
+        if ($as->length == 0) {
+            // TRANS: Client exception thrown when a bookmark in an import file is incorrectly formatted.
+            throw new ClientException(_m("No <A> tag in a <DT>."));
+        }
+
+        $a = $as->item(0);
+
+        $private = $a->getAttribute('private');
+
+        if ($private != 0) {
+            // TRANS: Client exception thrown when a bookmark in an import file is private.
+            throw new ClientException(_m('Skipping private bookmark.'));
+        }
+
+        if (!empty($dd)) {
+            $description = $dd->nodeValue;
+        } else {
+            $description = null;
+        }
+        $addDate = $a->getAttribute('add_date');
+
+        $data = array(
+            'profile_id' => $user->id,
+            'title' => $a->nodeValue,
+            'description' => $description,
+            'url' => $a->getAttribute('href'),
+            'tags' => $a->getAttribute('tags'),
+            'created' => common_sql_date(intval($addDate))
+        );
+
+        $qm = QueueManager::get();
+        $qm->enqueue($data, 'dlcsbkmk');
+    }
+
+    /**
+     * Parse some HTML
+     *
+     * Hides the errors that the dom parser returns
+     *
+     * @param string $body Data to import
+     *
+     * @return DOMDocument parsed document
+     */
+
+    function importHTML($body)
+    {
+        // DOMDocument::loadHTML may throw warnings on unrecognized elements,
+        // and notices on unrecognized namespaces.
+        $old = error_reporting(error_reporting() & ~(E_WARNING | E_NOTICE));
+        $dom = new DOMDocument();
+        $ok  = $dom->loadHTML($body);
+        error_reporting($old);
+
+        if ($ok) {
+            foreach ($dom->getElementsByTagName('body') as $node) {
+                $this->fixListsIn($node);
+            }
+            return $dom;
+        } else {
+            return null;
+        }
+    }
+
+
+    function fixListsIn(DOMNode $body) {
+        $toFix = array();
+
+        foreach ($body->childNodes as $node) {
+            if ($node->nodeType == XML_ELEMENT_NODE) {
+                $el = strtolower($node->nodeName);
+                if ($el == 'dl') {
+                    $toFix[] = $node;
+                }
+            }
+        }
+
+        foreach ($toFix as $node) {
+            $this->fixList($node);
+        }
+    }
+
+    function fixList(DOMNode $list) {
+        $toFix = array();
+
+        foreach ($list->childNodes as $node) {
+            if ($node->nodeType == XML_ELEMENT_NODE) {
+                $el = strtolower($node->nodeName);
+                if ($el == 'dt' || $el == 'dd') {
+                    $toFix[] = $node;
+                }
+                if ($el == 'dl') {
+                    // Sublist.
+                    // Technically, these can only appear inside a <dd>...
+                    $this->fixList($node);
+                }
+            }
+        }
+
+        foreach ($toFix as $node) {
+            $this->fixListItem($node);
+        }
+    }
+
+    function fixListItem(DOMNode $item) {
+        // The HTML parser in libxml2 doesn't seem to properly handle
+        // many cases of implied close tags, apparently because it doesn't
+        // understand the nesting rules specified in the HTML DTD.
+        //
+        // This leads to sequences of adjacent <dt>s or <dd>s being incorrectly
+        // interpreted as parent->child trees instead of siblings:
+        //
+        // When parsing this input: "<dt>aaa <dt>bbb"
+        // should be equivalent to: "<dt>aaa </dt><dt>bbb</dt>"
+        // but we're seeing instead: "<dt>aaa <dt>bbb</dt></dt>"
+        //
+        // It does at least know that going from dt to dd, or dd to dt,
+        // should make a break.
+
+        $toMove = array();
+
+        foreach ($item->childNodes as $node) {
+            if ($node->nodeType == XML_ELEMENT_NODE) {
+                $el = strtolower($node->nodeName);
+                if ($el == 'dt' || $el == 'dd') {
+                    // dt & dd cannot contain each other;
+                    // This node was incorrectly placed; move it up a level!
+                    $toMove[] = $node;
+                }
+                if ($el == 'dl') {
+                    // Sublist.
+                    // Technically, these can only appear inside a <dd>.
+                    $this->fixList($node);
+                }
+            }
+        }
+
+        $parent = $item->parentNode;
+        $next = $item->nextSibling;
+        foreach ($toMove as $node) {
+            $item->removeChild($node);
+            $parent->insertBefore($node, $next);
+            $this->fixListItem($node);
+        }
+    }
+}
diff --git a/plugins/Bookmark/lib/deliciousbookmarkimporter.php b/plugins/Bookmark/lib/deliciousbookmarkimporter.php
new file mode 100644 (file)
index 0000000..eca9e96
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Importer class for Delicious.com bookmarks
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Importer class for Delicious bookmarks
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class DeliciousBookmarkImporter extends QueueHandler
+{
+    /**
+     * Return the transport for this queue handler
+     *
+     * @return string 'dlcsbkmk'
+     */
+    function transport()
+    {
+        return 'dlcsbkmk';
+    }
+
+    /**
+     * Handle the data
+     *
+     * @param array $data associative array of user & bookmark info from DeliciousBackupImporter::importBookmark()
+     *
+     * @return boolean success value
+     */
+    function handle($data)
+    {
+        $profile = Profile::getKV('id', $data['profile_id']);
+
+        try {
+            $saved = Bookmark::saveNew($profile,
+                                       $data['title'],
+                                       $data['url'],
+                                       $data['tags'],
+                                       $data['description'],
+                                       array('created' => $data['created'],
+                                             'distribute' => false));
+        } catch (ClientException $ce) {
+            // Most likely a duplicate -- continue on with the rest!
+            common_log(LOG_ERR, "Error importing delicious bookmark to $data[url]: " . $ce->getMessage());
+            return true;
+        } catch (Exception $ex) {
+            if (preg_match("/DB Error: already exists/", $ex->getMessage())) {
+                common_log(LOG_ERR, "Error importing delicious bookmark to $data[url]: " . $ce->getMessage());
+                return true;
+            } else {
+                throw $ex;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/plugins/Bookmark/newbookmark.php b/plugins/Bookmark/newbookmark.php
deleted file mode 100644 (file)
index 57be783..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * Add a new bookmark
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Add a new bookmark
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class NewbookmarkAction extends Action
-{
-    protected $user        = null;
-    protected $error       = null;
-    protected $complete    = null;
-    protected $title       = null;
-    protected $url         = null;
-    protected $tags        = null;
-    protected $description = null;
-
-    /**
-     * Returns the title of the action
-     *
-     * @return string Action title
-     */
-    function title()
-    {
-        // TRANS: Title for action to create a new bookmark.
-        return _m('New bookmark');
-    }
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        if ($this->boolean('ajax')) {
-            StatusNet::setApi(true);
-        }
-
-        $this->user = common_current_user();
-
-        if (empty($this->user)) {
-            // TRANS: Client exception thrown when trying to create a new bookmark while not logged in.
-            throw new ClientException(_m('Must be logged in to post a bookmark.'),
-                                      403);
-        }
-
-        if ($this->isPost()) {
-            $this->checkSessionToken();
-        }
-
-        $this->title       = $this->trimmed('title');
-        $this->url         = $this->trimmed('url');
-        $this->tags        = $this->trimmed('tags');
-        $this->description = $this->trimmed('description');
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        parent::handle($argarray);
-
-        if ($this->isPost()) {
-            $this->newBookmark();
-        } else {
-            $this->showPage();
-        }
-
-        return;
-    }
-
-    /**
-     * Add a new bookmark
-     *
-     * @return void
-     */
-    function newBookmark()
-    {
-        try {
-            if (empty($this->title)) {
-                // TRANS: Client exception thrown when trying to create a new bookmark without a title.
-                throw new ClientException(_m('Bookmark must have a title.'));
-            }
-
-            if (empty($this->url)) {
-                // TRANS: Client exception thrown when trying to create a new bookmark without a URL.
-                throw new ClientException(_m('Bookmark must have an URL.'));
-            }
-
-            $options = array();
-
-            ToSelector::fillOptions($this, $options);
-
-            $saved = Bookmark::saveNew($this->user->getProfile(),
-                                       $this->title,
-                                       $this->url,
-                                       $this->tags,
-                                       $this->description,
-                                       $options);
-
-        } catch (ClientException $ce) {
-            if ($this->boolean('ajax')) {
-                header('Content-Type: text/xml;charset=utf-8');
-                $this->xw->startDocument('1.0', 'UTF-8');
-                $this->elementStart('html');
-                $this->elementStart('head');
-                // TRANS: Page title after an AJAX error occurs
-                $this->element('title', null, _('Ajax Error'));
-                $this->elementEnd('head');
-                $this->elementStart('body');
-                $this->element('p', array('id' => 'error'), $ce->getMessage());
-                $this->elementEnd('body');
-                $this->elementEnd('html');
-                return;
-            } else {
-                $this->error = $ce->getMessage();
-                $this->showPage();
-                return;
-            }
-        }
-
-        if ($this->boolean('ajax')) {
-            header('Content-Type: text/xml;charset=utf-8');
-            $this->xw->startDocument('1.0', 'UTF-8');
-            $this->elementStart('html');
-            $this->elementStart('head');
-            // TRANS: Page title after posting a bookmark.
-            $this->element('title', null, _m('Bookmark posted'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $this->showNotice($saved);
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            common_redirect($saved->bestUrl(), 303);
-        }
-    }
-
-    /**
-     * Output a notice
-     *
-     * Used to generate the notice code for Ajax results.
-     *
-     * @param Notice $notice Notice that was saved
-     *
-     * @return void
-     */
-    function showNotice($notice)
-    {
-        class_exists('NoticeList'); // @fixme hack for autoloader
-        $nli = new NoticeListItem($notice, $this);
-        $nli->show();
-    }
-
-    /**
-     * Show the bookmark form
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        if (!empty($this->error)) {
-            $this->element('p', 'error', $this->error);
-        }
-
-        $form = new BookmarkForm($this,
-                                 $this->title,
-                                 $this->url,
-                                 $this->tags,
-                                 $this->description);
-
-        $form->show();
-
-        return;
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
-            $_SERVER['REQUEST_METHOD'] == 'HEAD') {
-            return true;
-        } else {
-            return false;
-        }
-    }
-}
diff --git a/plugins/Bookmark/noticebyurl.php b/plugins/Bookmark/noticebyurl.php
deleted file mode 100644 (file)
index d5f9940..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * Notice stream of notices with a given attachment
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * List notices that contain/link to/use a given URL
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class NoticebyurlAction extends Action
-{
-    protected $url     = null;
-    protected $file    = null;
-    protected $notices = null;
-    protected $page    = null;
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        $this->file = File::getKV('id', $this->trimmed('id'));
-
-        if (empty($this->file)) {
-            // TRANS: Client exception thrown when an unknown URL is provided.
-            throw new ClientException(_m('Unknown URL.'));
-        }
-
-        $pageArg = $this->trimmed('page');
-
-        $this->page = (empty($pageArg)) ? 1 : intval($pageArg);
-
-        $this->notices = $this->file->stream(($this->page - 1) * NOTICES_PER_PAGE,
-                                             NOTICES_PER_PAGE + 1);
-
-        return true;
-    }
-
-    /**
-     * Title of the page
-     *
-     * @return string page title
-     */
-    function title()
-    {
-        if ($this->page == 1) {
-            // TRANS: Title of notice stream of notices with a given attachment (first page).
-            // TRANS: %s is the URL.
-            return sprintf(_m('Notices linking to %s'), $this->file->url);
-        } else {
-            // TRANS: Title of notice stream of notices with a given attachment (all but first page).
-            // TRANS: %1$s is the URL, %2$s is the page number.
-            return sprintf(_m('Notices linking to %1$s, page %2$d'),
-                           $this->file->url,
-                           $this->page);
-        }
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        $this->showPage();
-    }
-
-    /**
-     * Show main page content.
-     *
-     * Shows a list of the notices that link to the given URL
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        $nl = new NoticeList($this->notices, $this);
-
-        $nl->show();
-
-        $cnt = $nl->show();
-
-        $this->pagination($this->page > 1,
-                          $cnt > NOTICES_PER_PAGE,
-                          $this->page,
-                          'noticebyurl',
-                          array('id' => $this->file->id));
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        return true;
-    }
-
-    /**
-     * Return last modified, if applicable.
-     *
-     * MAY override
-     *
-     * @return string last modified http header
-     */
-    function lastModified()
-    {
-        // For comparison with If-Last-Modified
-        // If not applicable, return null
-        return null;
-    }
-
-    /**
-     * Return etag, if applicable.
-     *
-     * MAY override
-     *
-     * @return string etag http header
-     */
-    function etag()
-    {
-        return null;
-    }
-}
diff --git a/plugins/Bookmark/scripts/importbookmarks.php b/plugins/Bookmark/scripts/importbookmarks.php
new file mode 100644 (file)
index 0000000..c47a042
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010 StatusNet, Inc.
+ *
+ * Import a bookmarks file as notices
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Bookmark
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
+
+$shortoptions = 'i:n:f:';
+$longoptions  = array('id=', 'nickname=', 'file=');
+
+$helptext = <<<END_OF_IMPORTBOOKMARKS_HELP
+importbookmarks.php [options]
+Restore a backed-up Delicious.com bookmark file
+
+-i --id       ID of user to import bookmarks for
+-n --nickname nickname of the user to import for
+-f --file     file to read from (STDIN by default)
+END_OF_IMPORTBOOKMARKS_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+/**
+ * Get the bookmarks file as a string
+ *
+ * Uses the -f or --file parameter to open and read a
+ * a bookmarks file
+ *
+ * @return string Contents of the file
+ */
+
+function getBookmarksFile()
+{
+    $filename = get_option_value('f', 'file');
+
+    if (empty($filename)) {
+        show_help();
+        exit(1);
+    }
+
+    if (!file_exists($filename)) {
+        // TRANS: Exception thrown when a file upload cannot be found.
+        // TRANS: %s is the file that could not be found.
+        throw new Exception(sprintf(_m('No such file "%s".'),$filename));
+    }
+
+    if (!is_file($filename)) {
+        // TRANS: Exception thrown when a file upload is incorrect.
+        // TRANS: %s is the irregular file.
+        throw new Exception(sprintf(_m('Not a regular file: "%s".'),$filename));
+    }
+
+    if (!is_readable($filename)) {
+        // TRANS: Exception thrown when a file upload is not readable.
+        // TRANS: %s is the file that could not be read.
+        throw new Exception(sprintf(_m('File "%s" not readable.'),$filename));
+    }
+
+    // TRANS: %s is the filename that contains a backup for a user.
+    printfv(_m('Getting backup from file "%s".')."\n", $filename);
+
+    $html = file_get_contents($filename);
+
+    return $html;
+}
+
+try {
+    $user = getUser();
+    $html = getBookmarksFile();
+
+    $qm = QueueManager::get();
+
+    $qm->enqueue(array($user, $html), 'dlcsback');
+
+} catch (Exception $e) {
+    print $e->getMessage()."\n";
+    exit(1);
+}
diff --git a/plugins/Bookmark/showbookmark.php b/plugins/Bookmark/showbookmark.php
deleted file mode 100644 (file)
index 1c562d5..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * Show a single bookmark
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Show a single bookmark, with associated information
- *
- * @category  Bookmark
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class ShowbookmarkAction extends ShownoticeAction
-{
-    protected $bookmark = null;
-
-    function getNotice()
-    {
-        $this->id = $this->trimmed('id');
-
-        $this->bookmark = Bookmark::getKV('id', $this->id);
-
-        if (empty($this->bookmark)) {
-            // TRANS: Client exception thrown when referring to a non-existing bookmark.
-            throw new ClientException(_m('No such bookmark.'), 404);
-        }
-
-        $notice = Notice::getKV('uri', $this->bookmark->uri);
-
-        if (empty($notice)) {
-            // Did we used to have it, and it got deleted?
-            // TRANS: Client exception thrown when referring to a non-existing bookmark.
-            throw new ClientException(_m('No such bookmark.'), 404);
-        }
-
-        return $notice;
-    }
-
-    /**
-     * Title of the page
-     *
-     * Used by Action class for layout.
-     *
-     * @return string page tile
-     */
-    function title()
-    {
-        // TRANS: Title for bookmark.
-        // TRANS: %1$s is a user nickname, %2$s is a bookmark title.
-        return sprintf(_m('%1$s\'s bookmark for "%2$s"'),
-                       $this->user->nickname,
-                       $this->bookmark->title);
-    }
-
-    /**
-     * Overload page title display to show bookmark link
-     *
-     * @return void
-     */
-    function showPageTitle()
-    {
-        $this->elementStart('h1');
-        $this->element('a',
-                       array('href' => $this->bookmark->url),
-                       $this->bookmark->title);
-        $this->elementEnd('h1');
-    }
-}
index ecc642b8568ebac7796cda0c8af532142a334d9d..0efbb7519b5041628059fb4a2506c81666a81aa1 100644 (file)
@@ -54,10 +54,10 @@ class CasAuthenticationPlugin extends AuthenticationPlugin
          case 'phpCAS':
             require_once(INSTALLDIR.'/plugins/CasAuthentication/extlib/CAS.php');
             return false;
-         case 'CasloginAction':
-            require_once(INSTALLDIR.'/plugins/CasAuthentication/' . strtolower(mb_substr($cls, 0, -6)) . '.php');
-            return false;
         }
+
+        // if it's not our exception, try standard places
+        return parent::onAutoload($cls);
     }
 
     function onArgsInitialize(&$args)
diff --git a/plugins/CasAuthentication/actions/caslogin.php b/plugins/CasAuthentication/actions/caslogin.php
new file mode 100644 (file)
index 0000000..0e16427
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+class CasloginAction extends Action
+{
+    function handle($args)
+    {
+        parent::handle($args);
+        if (common_is_real_login()) {
+            // TRANS: Client error displayed when trying to log in while already logged on.
+            $this->clientError(_m('Already logged in.'));
+        } else {
+            global $casSettings;
+            phpCAS::client(CAS_VERSION_2_0,$casSettings['server'],$casSettings['port'],$casSettings['path'],false);
+            phpCAS::setNoCasServerValidation();
+            phpCAS::handleLogoutRequests();
+            phpCAS::forceAuthentication();
+            global $casTempPassword;
+            $casTempPassword = common_good_rand(16);
+            $user = common_check_user(phpCAS::getUser(), $casTempPassword);
+            if (!$user) {
+                // TRANS: Server error displayed when trying to log in with incorrect username or password.
+                $this->serverError(_m('Incorrect username or password.'));
+                return;
+            }
+
+            // success!
+            if (!common_set_user($user)) {
+                // TRANS: Server error displayed when login fails in CAS authentication plugin.
+                $this->serverError(_m('Error setting user. You are probably not authorized.'));
+                return;
+            }
+
+            common_real_login(true);
+
+            $url = common_get_returnto();
+
+            if ($url) {
+                // We don't have to return to it again
+                common_set_returnto(null);
+            } else {
+                if(common_config('site', 'private') && $casSettings['takeOverLogin']) {
+                    //SSO users expect to just go to the URL they entered
+                    //if we don't have a returnto set, the user entered the
+                    //main StatusNet url, so send them there.
+                    $url = common_local_url('public');
+                } else {
+                    //With normal logins (regular form-based username/password),
+                    //the user would expect to go to their home after logging in.
+                    $url = common_local_url('public',
+                                        array('nickname' =>
+                                              $user->nickname));
+                }
+            }
+
+            common_redirect($url, 303);
+        }
+    }
+}
diff --git a/plugins/CasAuthentication/caslogin.php b/plugins/CasAuthentication/caslogin.php
deleted file mode 100644 (file)
index 0e16427..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-class CasloginAction extends Action
-{
-    function handle($args)
-    {
-        parent::handle($args);
-        if (common_is_real_login()) {
-            // TRANS: Client error displayed when trying to log in while already logged on.
-            $this->clientError(_m('Already logged in.'));
-        } else {
-            global $casSettings;
-            phpCAS::client(CAS_VERSION_2_0,$casSettings['server'],$casSettings['port'],$casSettings['path'],false);
-            phpCAS::setNoCasServerValidation();
-            phpCAS::handleLogoutRequests();
-            phpCAS::forceAuthentication();
-            global $casTempPassword;
-            $casTempPassword = common_good_rand(16);
-            $user = common_check_user(phpCAS::getUser(), $casTempPassword);
-            if (!$user) {
-                // TRANS: Server error displayed when trying to log in with incorrect username or password.
-                $this->serverError(_m('Incorrect username or password.'));
-                return;
-            }
-
-            // success!
-            if (!common_set_user($user)) {
-                // TRANS: Server error displayed when login fails in CAS authentication plugin.
-                $this->serverError(_m('Error setting user. You are probably not authorized.'));
-                return;
-            }
-
-            common_real_login(true);
-
-            $url = common_get_returnto();
-
-            if ($url) {
-                // We don't have to return to it again
-                common_set_returnto(null);
-            } else {
-                if(common_config('site', 'private') && $casSettings['takeOverLogin']) {
-                    //SSO users expect to just go to the URL they entered
-                    //if we don't have a returnto set, the user entered the
-                    //main StatusNet url, so send them there.
-                    $url = common_local_url('public');
-                } else {
-                    //With normal logins (regular form-based username/password),
-                    //the user would expect to go to their home after logging in.
-                    $url = common_local_url('public',
-                                        array('nickname' =>
-                                              $user->nickname));
-                }
-            }
-
-            common_redirect($url, 303);
-        }
-    }
-}
index cc109292318a06d3a04ee1191ad649d58380dea7..ba4f05fc20b22be9e820a643812115a42f612e6f 100644 (file)
@@ -31,8 +31,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
     exit(1);
 }
 
-require_once(INSTALLDIR.'/plugins/ClientSideShorten/shorten.php');
-
 class ClientSideShortenPlugin extends Plugin
 {
     function __construct()
@@ -40,16 +38,6 @@ class ClientSideShortenPlugin extends Plugin
         parent::__construct();
     }
 
-    function onAutoload($cls)
-    {
-        switch ($cls)
-        {
-         case 'ShortenAction':
-            require_once(INSTALLDIR.'/plugins/ClientSideShorten/shorten.php');
-            return false;
-        }
-    }
-
     function onEndShowScripts($action){
         if (common_logged_in()) {
             $user = common_current_user();
diff --git a/plugins/ClientSideShorten/actions/shorten.php b/plugins/ClientSideShorten/actions/shorten.php
new file mode 100644 (file)
index 0000000..6840d53
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * List users for autocompletion
+ *
+ * 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  Plugin
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Shorten all URLs in a string
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class ShortenAction extends Action
+{
+    private $text;
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+        $this->groups=array();
+        $this->users=array();
+        $this->text = $this->arg('text');
+        if(is_null($this->text)){
+            // TRANS: Client exception thrown when a text argument is not present.
+            throw new ClientException(_m('"text" argument must be specified.'));
+        }
+        return true;
+    }
+
+    function handle($args=null)
+    {
+        parent::handle($args);
+        header('Content-Type: text/plain');
+        $shortened_text = common_shorten_links($this->text);
+        print $shortened_text;
+    }
+}
diff --git a/plugins/ClientSideShorten/shorten.php b/plugins/ClientSideShorten/shorten.php
deleted file mode 100644 (file)
index 6840d53..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * List users for autocompletion
- *
- * 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  Plugin
- * @package   StatusNet
- * @author    Craig Andrews <candrews@integralblue.com>
- * @copyright 2008-2009 StatusNet, Inc.
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * Shorten all URLs in a string
- *
- * @category Plugin
- * @package  StatusNet
- * @author   Craig Andrews <candrews@integralblue.com>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class ShortenAction extends Action
-{
-    private $text;
-
-    function prepare($args)
-    {
-        parent::prepare($args);
-        $this->groups=array();
-        $this->users=array();
-        $this->text = $this->arg('text');
-        if(is_null($this->text)){
-            // TRANS: Client exception thrown when a text argument is not present.
-            throw new ClientException(_m('"text" argument must be specified.'));
-        }
-        return true;
-    }
-
-    function handle($args=null)
-    {
-        parent::handle($args);
-        header('Content-Type: text/plain');
-        $shortened_text = common_shorten_links($this->text);
-        print $shortened_text;
-    }
-}
index 5979bb0e806107cd49d59d6696d20389fac53730..511b7eb561198930ec4b5bd5b9e43e55837151e3 100644 (file)
@@ -68,41 +68,6 @@ class DirectoryPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing,
-     *         false means stop.
-     */
-    function onAutoload($cls)
-    {
-        // common_debug("class = $cls");
-
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'UserdirectoryAction':
-        case 'GroupdirectoryAction':
-            include_once $dir
-                . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'AlphaNav':
-            include_once $dir
-                . '/lib/' . strtolower($cls) . '.php';
-            return false;
-        case 'SortableSubscriptionList':
-        case 'SortableGroupList':
-            include_once $dir
-                . '/lib/' . strtolower($cls) . '.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Map URLs to actions
      *
index 3364c8ae5c2b5ecab5213038ea2db588f51389f0..732a85f373fc2d4de274d2d639b2ea3d693e41b5 100644 (file)
@@ -91,27 +91,6 @@ class DomainStatusNetworkPlugin extends Plugin
         }
     }
 
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'GlobalregisterAction':
-        case 'GloballoginAction':
-        case 'GlobalrecoverAction':
-            include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'DomainStatusNetworkInstaller':
-        case 'GlobalApiAction':
-        case 'FreeEmail':
-            include_once $dir . '/lib/' . strtolower($cls) . '.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     static function toDomain($raw)
     {
         $parts = explode('@', $raw);
diff --git a/plugins/DomainStatusNetwork/actions/globalapi.php b/plugins/DomainStatusNetwork/actions/globalapi.php
new file mode 100644 (file)
index 0000000..cd0c7f9
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * An action that requires an API key
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  DomainStatusNetwork
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * An action that requires an API key
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class GlobalApiAction extends Action
+{
+    var $email;
+
+    /**
+     * Check for an API key, and throw an exception if it's not set
+     *
+     * @param array $args URL and POST params
+     *
+     * @return boolean continuation flag
+     */
+
+    function prepare($args)
+    {
+        StatusNet::setApi(true); // reduce exception reports to aid in debugging
+
+        parent::prepare($args);
+
+        if (!common_config('globalapi', 'enabled')) {
+            throw new ClientException(_('Global API not enabled.'), 403);
+        }
+
+        $apikey = $this->trimmed('apikey');
+
+        if (empty($apikey)) {
+            throw new ClientException(_('No API key.'), 403);
+        }
+
+        $expected = common_config('globalapi', 'key');
+
+        if ($expected != $apikey) {
+            // FIXME: increment a counter by IP address to prevent brute-force
+            // attacks on the key.
+            throw new ClientException(_('Bad API key.'), 403);
+        }
+
+        $email = common_canonical_email($this->trimmed('email'));
+
+        if (empty($email)) {
+            throw new ClientException(_('No email address.'));
+        }
+
+        if (!Validate::email($email, common_config('email', 'check_domain'))) {
+            throw new ClientException(_('Invalid email address.'));
+        }
+
+        $this->email = $email;
+
+        return true;
+    }
+
+    function showError($message, $code=400)
+    {
+        $this->showOutput(array('error' => $message), $code);
+    }
+
+    function showSuccess($values=null, $code=200)
+    {
+        if (empty($values)) {
+            $values = array();
+        }
+        $values['success'] = 1;
+        $this->showOutput($values, $code);
+    }
+
+    function showOutput($values, $code)
+    {
+        if (array_key_exists($code, ClientErrorAction::$status)) {
+            $status_string = ClientErrorAction::$status[$code];
+        } else if (array_key_exists($code, ServerErrorAction::$status)) {
+            $status_string = ServerErrorAction::$status[$code];
+        } else {
+            // bad code!
+            $code = 500;
+            $status_string = ServerErrorAction::$status[$code];
+        }
+
+        header('HTTP/1.1 '.$code.' '.$status_string);
+
+        header('Content-Type: application/json; charset=utf-8');
+        print(json_encode($values));
+        print("\n");
+    }
+}
diff --git a/plugins/DomainStatusNetwork/lib/globalapiaction.php b/plugins/DomainStatusNetwork/lib/globalapiaction.php
deleted file mode 100644 (file)
index cd0c7f9..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * An action that requires an API key
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  DomainStatusNetwork
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * An action that requires an API key
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class GlobalApiAction extends Action
-{
-    var $email;
-
-    /**
-     * Check for an API key, and throw an exception if it's not set
-     *
-     * @param array $args URL and POST params
-     *
-     * @return boolean continuation flag
-     */
-
-    function prepare($args)
-    {
-        StatusNet::setApi(true); // reduce exception reports to aid in debugging
-
-        parent::prepare($args);
-
-        if (!common_config('globalapi', 'enabled')) {
-            throw new ClientException(_('Global API not enabled.'), 403);
-        }
-
-        $apikey = $this->trimmed('apikey');
-
-        if (empty($apikey)) {
-            throw new ClientException(_('No API key.'), 403);
-        }
-
-        $expected = common_config('globalapi', 'key');
-
-        if ($expected != $apikey) {
-            // FIXME: increment a counter by IP address to prevent brute-force
-            // attacks on the key.
-            throw new ClientException(_('Bad API key.'), 403);
-        }
-
-        $email = common_canonical_email($this->trimmed('email'));
-
-        if (empty($email)) {
-            throw new ClientException(_('No email address.'));
-        }
-
-        if (!Validate::email($email, common_config('email', 'check_domain'))) {
-            throw new ClientException(_('Invalid email address.'));
-        }
-
-        $this->email = $email;
-
-        return true;
-    }
-
-    function showError($message, $code=400)
-    {
-        $this->showOutput(array('error' => $message), $code);
-    }
-
-    function showSuccess($values=null, $code=200)
-    {
-        if (empty($values)) {
-            $values = array();
-        }
-        $values['success'] = 1;
-        $this->showOutput($values, $code);
-    }
-
-    function showOutput($values, $code)
-    {
-        if (array_key_exists($code, ClientErrorAction::$status)) {
-            $status_string = ClientErrorAction::$status[$code];
-        } else if (array_key_exists($code, ServerErrorAction::$status)) {
-            $status_string = ServerErrorAction::$status[$code];
-        } else {
-            // bad code!
-            $code = 500;
-            $status_string = ServerErrorAction::$status[$code];
-        }
-
-        header('HTTP/1.1 '.$code.' '.$status_string);
-
-        header('Content-Type: application/json; charset=utf-8');
-        print(json_encode($values));
-        print("\n");
-    }
-}
index da49338fe7a95a1fa8d1f06779264cec54a5b278..9aa67ab8450d32d08ee9c7ebfd6db2f8acc7c3d5 100644 (file)
@@ -48,32 +48,6 @@ if (!defined('STATUSNET')) {
  */
 class DomainWhitelistPlugin extends Plugin
 {
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false
-     *         means stop.
-     */
-    function onAutoload($cls) {
-        $base = dirname(__FILE__);
-        $lower = strtolower($cls);
-
-        $files = array("$base/classes/$cls.php",
-            "$base/lib/$lower.php");
-        if (substr($lower, -6) == 'action') {
-            $files[] = "$base/actions/" . substr($lower, 0, -6) . ".php";
-        }
-        foreach ($files as $file) {
-            if (file_exists($file)) {
-                include_once $file;
-                return false;
-            }
-        }
-        return true;
-    }
-
     /**
      * Get the path to the plugin's installation directory. Used
      * to link in js files and whatnot.
diff --git a/plugins/DomainWhitelist/forms/whitelistinvite.php b/plugins/DomainWhitelist/forms/whitelistinvite.php
new file mode 100644 (file)
index 0000000..77a48f9
--- /dev/null
@@ -0,0 +1,178 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Fancy form for inviting collegues
+ *
+ * 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  Form
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/lib/form.php';
+
+/**
+ * Form for inviting collegues and friends
+ *
+ * @category Form
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ */
+class WhitelistInviteForm extends Form
+{
+    private $whitelist = null;
+
+    /**
+     * Constructor
+     *
+     * @param Action $out output channel
+     */
+    function __construct($out, $whitelist)
+    {
+        parent::__construct($out);
+        $this->whitelist = $whitelist;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return string ID of the form
+     */
+    function id()
+    {
+       return 'form_invite';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('invite');
+    }
+
+    /**
+     * Name of the form
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+        // TRANS: Form legend.
+        $this->out->element('legend', null, _m('Invite collegues'));
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('ul', 'form_data');
+        for ($i = 0; $i < 3; $i++) {
+            $this->showEmailLI();
+        }
+        $this->out->elementStart('li');
+        $this->out->textarea(
+            // TRANS: Field label for a personal message to send to invitees.
+            'personal', _m('Personal message'),
+            $this->out->trimmed('personal'),
+            // TRANS: Field title for a personal message to send to invitees.
+            _m('Optionally add a personal message to the invitation.')
+        );
+        $this->out->elementEnd('li');
+        $this->out->elementEnd('ul');
+    }
+
+    function showEmailLI()
+    {
+        $this->out->elementStart('li');
+        $this->out->input('username[]', '');
+        $this->out->text('@');
+        if (count($this->whitelist) == 1) {
+            $this->out->element(
+                'span',
+                array('class' => 'email_invite'),
+                $this->whitelist[0]
+           );
+           $this->out->hidden('domain[]', $this->whitelist[0]);
+        } else {
+            $content = array();
+            foreach($this->whitelist as $domain) {
+                $content[$domain] = $domain;
+            }
+            $this->out->dropdown('domain[]', '', $content);
+        }
+        $this->showMultiControls();
+        $this->out->elementEnd('li');
+    }
+
+    function showMultiControls()
+    {
+        $this->out->element(
+            'a',
+            array(
+                'class' => 'remove_row',
+                'href'  => 'javascript://',
+                'style' => 'display: none;'
+            ),
+            '-'
+        );
+
+        $this->out->element(
+            'a',
+            array(
+                'class' => 'add_row',
+                'href'  => 'javascript://',
+                'style' => 'display: none;'
+            ),
+            // TRANS: Link description to action to add another item to a list.
+            _m('Add another item')
+        );
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        $this->out->submit(
+            'send',
+            // TRANS: Send button for inviting friends.
+            _m('BUTTON','Send'), 'submit form_action-primary',
+            'send',
+            // TRANS: Submit button title.
+            _m('Send invitations.')
+        );
+    }
+}
diff --git a/plugins/DomainWhitelist/lib/whitelistinviteform.php b/plugins/DomainWhitelist/lib/whitelistinviteform.php
deleted file mode 100644 (file)
index 77a48f9..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Fancy form for inviting collegues
- *
- * 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  Form
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/lib/form.php';
-
-/**
- * Form for inviting collegues and friends
- *
- * @category Form
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- */
-class WhitelistInviteForm extends Form
-{
-    private $whitelist = null;
-
-    /**
-     * Constructor
-     *
-     * @param Action $out output channel
-     */
-    function __construct($out, $whitelist)
-    {
-        parent::__construct($out);
-        $this->whitelist = $whitelist;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return string ID of the form
-     */
-    function id()
-    {
-       return 'form_invite';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('invite');
-    }
-
-    /**
-     * Name of the form
-     *
-     * @return void
-     */
-    function formLegend()
-    {
-        // TRANS: Form legend.
-        $this->out->element('legend', null, _m('Invite collegues'));
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('ul', 'form_data');
-        for ($i = 0; $i < 3; $i++) {
-            $this->showEmailLI();
-        }
-        $this->out->elementStart('li');
-        $this->out->textarea(
-            // TRANS: Field label for a personal message to send to invitees.
-            'personal', _m('Personal message'),
-            $this->out->trimmed('personal'),
-            // TRANS: Field title for a personal message to send to invitees.
-            _m('Optionally add a personal message to the invitation.')
-        );
-        $this->out->elementEnd('li');
-        $this->out->elementEnd('ul');
-    }
-
-    function showEmailLI()
-    {
-        $this->out->elementStart('li');
-        $this->out->input('username[]', '');
-        $this->out->text('@');
-        if (count($this->whitelist) == 1) {
-            $this->out->element(
-                'span',
-                array('class' => 'email_invite'),
-                $this->whitelist[0]
-           );
-           $this->out->hidden('domain[]', $this->whitelist[0]);
-        } else {
-            $content = array();
-            foreach($this->whitelist as $domain) {
-                $content[$domain] = $domain;
-            }
-            $this->out->dropdown('domain[]', '', $content);
-        }
-        $this->showMultiControls();
-        $this->out->elementEnd('li');
-    }
-
-    function showMultiControls()
-    {
-        $this->out->element(
-            'a',
-            array(
-                'class' => 'remove_row',
-                'href'  => 'javascript://',
-                'style' => 'display: none;'
-            ),
-            '-'
-        );
-
-        $this->out->element(
-            'a',
-            array(
-                'class' => 'add_row',
-                'href'  => 'javascript://',
-                'style' => 'display: none;'
-            ),
-            // TRANS: Link description to action to add another item to a list.
-            _m('Add another item')
-        );
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        $this->out->submit(
-            'send',
-            // TRANS: Send button for inviting friends.
-            _m('BUTTON','Send'), 'submit form_action-primary',
-            'send',
-            // TRANS: Submit button title.
-            _m('Send invitations.')
-        );
-    }
-}
index 2f1116ba0cdaad4a4e8b89a6edc9e4a9de12d6cf..14af35ec118327831a887fe21dcfe61b872b70b5 100644 (file)
@@ -49,24 +49,6 @@ class EmailRegistrationPlugin extends Plugin
 {
     const CONFIRMTYPE = 'register';
 
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'EmailregisterAction':
-            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'EmailRegistrationForm':
-        case 'ConfirmRegistrationForm':
-            include_once $dir . '/' . strtolower($cls) . '.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     function onArgsInitialize(&$args)
     {
         if (array_key_exists('action', $args) && $args['action'] == 'register') {
diff --git a/plugins/EmailRegistration/actions/emailregister.php b/plugins/EmailRegistration/actions/emailregister.php
new file mode 100644 (file)
index 0000000..5eaf84a
--- /dev/null
@@ -0,0 +1,420 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Register a user by their email address
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Email registration
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Email registration
+ *
+ * There are four cases where we're called:
+ *
+ * 1. GET, no arguments. Initial registration; ask for an email address.
+ * 2. POST, email address argument. Initial registration; send an email to confirm.
+ * 3. GET, code argument. Confirming an invitation or a registration; look them up,
+ *    create the relevant user if possible, login as that user, and
+ *    show a password-entry form.
+ * 4. POST, password argument. After confirmation, set the password for the new
+ *    user, and redirect to a registration complete action with some instructions.
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class EmailregisterAction extends Action
+{
+    const NEWEMAIL = 1;
+    const SETPASSWORD = 2;
+    const NEWREGISTER = 3;
+    const CONFIRMINVITE = 4;
+    const CONFIRMREGISTER = 5;
+
+    const CONFIRMTYPE = 'register';
+
+    protected $user;
+    protected $email;
+    protected $code;
+    protected $invitation;
+    protected $confirmation;
+    protected $password1;
+    protected $password2;
+    protected $state;
+    protected $error;
+    protected $complete;
+
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        if (common_config('site', 'closed')) {
+            // TRANS: Client exception trown when registration by e-mail is not allowed.
+            throw new ClientException(_m('Registration not allowed.'), 403);
+        }
+
+        if ($this->isPost()) {
+
+            $this->checkSessionToken();
+
+            $this->email = $this->trimmed('email');
+
+            if (!empty($this->email)) {
+                if (common_config('site', 'inviteonly')) {
+                    // TRANS: Client exception trown when trying to register without an invitation.
+                    throw new ClientException(_m('Sorry, only invited people can register.'), 403);
+                }
+                $this->email = common_canonical_email($this->email);
+                $this->state = self::NEWEMAIL;
+            } else {
+                $this->state = self::SETPASSWORD;
+
+                $this->code = $this->trimmed('code');
+
+                if (empty($this->code)) {
+                    // TRANS: Client exception thrown when no confirmation code was provided.
+                    throw new ClientException(_m('No confirmation code.'));
+                }
+
+                $this->invitation = Invitation::getKV('code', $this->code);
+
+                if (!empty($this->invitation)) {
+                    if (!empty($this->invitation->registered_user_id)) {
+                        // TRANS: Client exception trown when using an invitation multiple times.
+                        throw new ClientException(_m('Invitation already used.'), 403);
+                    }
+                } else {
+
+                    $this->confirmation = Confirm_address::getKV('code', $this->code);
+
+                    if (empty($this->confirmation)) {
+                        // TRANS: Client exception thrown when given confirmation code was not issued.
+                        throw new ClientException(_m('No such confirmation code.'), 403);
+                    }
+                }
+
+                $this->password1 = $this->trimmed('password1');
+                $this->password2 = $this->trimmed('password2');
+
+                $this->tos = $this->boolean('tos');
+            }
+        } else { // GET
+            $this->code = $this->trimmed('code');
+
+            if (empty($this->code)) {
+                if (common_config('site', 'inviteonly')) {
+                    // TRANS: Client exception trown when trying to register without an invitation.
+                    throw new ClientException(_m('Sorry, only invited people can register.'), 403);
+                }
+                $this->state = self::NEWREGISTER;
+            } else {
+                $this->invitation = Invitation::getKV('code', $this->code);
+                if (!empty($this->invitation)) {
+                    if (!empty($this->invitation->registered_user_id)) {
+                        // TRANS: Client exception trown when using an invitation multiple times.
+                        throw new ClientException(_m('Invitation already used.'), 403);
+                    }
+                    $this->state = self::CONFIRMINVITE;
+                } else {
+                    $this->state = self::CONFIRMREGISTER;
+                    $this->confirmation = Confirm_address::getKV('code', $this->code);
+
+                    if (empty($this->confirmation)) {
+                        // TRANS: Client exception thrown when given confirmation code was not issued.
+                        throw new ClientException(_m('No such confirmation code.'), 405);
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    function title()
+    {
+        switch ($this->state) {
+        case self::NEWREGISTER:
+        case self::NEWEMAIL:
+            // TRANS: Title for registration page.
+            return _m('TITLE','Register');
+            break;
+        case self::SETPASSWORD:
+        case self::CONFIRMINVITE:
+        case self::CONFIRMREGISTER:
+            // TRANS: Title for page where to register with a confirmation code.
+            return _m('TITLE','Complete registration');
+            break;
+        }
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        $cur = common_current_user();
+
+        if (!empty($cur)) {
+            common_redirect(common_local_url('all', array('nickname' => $cur->nickname)));
+            return;
+        }
+
+        switch ($this->state) {
+        case self::NEWREGISTER:
+            $this->showRegistrationForm();
+            break;
+        case self::NEWEMAIL:
+            $this->registerUser();
+            break;
+        case self::CONFIRMINVITE:
+            $this->confirmRegistration();
+            break;
+        case self::CONFIRMREGISTER:
+            $this->confirmRegistration();
+            break;
+        case self::SETPASSWORD:
+            $this->setPassword();
+            break;
+        }
+        return;
+    }
+
+    function showRegistrationForm()
+    {
+        $this->form = new EmailRegistrationForm($this, $this->email);
+        $this->showPage();
+    }
+
+    function registerUser()
+    {
+        try {
+            $confirm = EmailRegistrationPlugin::registerEmail($this->email);
+        } catch (ClientException $ce) {
+            $this->error = $ce->getMessage();
+            $this->showRegistrationForm();
+            return;
+        }
+
+        EmailRegistrationPlugin::sendConfirmEmail($confirm);
+
+        // TRANS: Confirmation text after initial registration.
+        // TRANS: %s an e-mail address.
+        $prompt = sprintf(_m('An email was sent to %s to confirm that address. Check your email inbox for instructions.'),
+                          $this->email);
+
+        $this->complete = $prompt;
+
+        $this->showPage();
+    }
+
+    function confirmRegistration()
+    {
+        if (!empty($this->invitation)) {
+            $email = $this->invitation->address;
+        } else if (!empty($this->confirmation)) {
+            $email = $this->confirmation->address;
+        }
+
+        $nickname = $this->nicknameFromEmail($email);
+
+        $this->form = new ConfirmRegistrationForm($this,
+                                                  $nickname,
+                                                  $email,
+                                                  $this->code);
+        $this->showPage();
+    }
+
+    function setPassword()
+    {
+        if (Event::handle('StartRegistrationTry', array($this))) {
+            if (!empty($this->invitation)) {
+                $email = trim($this->invitation->address);
+            } else if (!empty($this->confirmation)) {
+                $email = trim($this->confirmation->address);
+            } else {
+                // TRANS: Client exception trown when trying to set password with an invalid confirmation code.
+                throw new Exception(_m('No confirmation thing.'));
+            }
+
+            if (!$this->tos) {
+                // TRANS: Error text when trying to register without agreeing to the terms.
+                $this->error = _m('You must accept the terms of service and privacy policy to register.');
+            } else if (empty($this->password1)) {
+                // TRANS: Error text when trying to register without a password.
+                $this->error = _m('You must set a password');
+            } else if (strlen($this->password1) < 6) {
+                // TRANS: Error text when trying to register with too short a password.
+                $this->error = _m('Password must be 6 or more characters.');
+            } else if ($this->password1 != $this->password2) {
+                // TRANS: Error text when trying to register without providing the same password twice.
+                $this->error = _m('Passwords do not match.');
+            }
+
+            if (!empty($this->error)) {
+                $nickname = $this->nicknameFromEmail($email);
+                $this->form = new ConfirmRegistrationForm($this, $nickname, $email, $this->code);
+                $this->showPage();
+                return;
+            }
+
+            $nickname = $this->nicknameFromEmail($email);
+
+            try {
+                $fields = array('nickname' => $nickname,
+                                'email' => $email,
+                                'password' => $this->password1,
+                                'email_confirmed' => true);
+
+                if (!empty($this->invitation)) {
+                    $fields['code'] = $this->invitation->code;
+                }
+                $this->user = User::register($fields);
+            } catch (ClientException $e) {
+                $this->error = $e->getMessage();
+                $nickname = $this->nicknameFromEmail($email);
+                $this->form = new ConfirmRegistrationForm($this, $nickname, $email, $this->code);
+                $this->showPage();
+                return;
+            }
+
+            if (empty($this->user)) {
+                // TRANS: Exception trown when using an invitation multiple times.
+                throw new Exception(_m('Failed to register user.'));
+            }
+
+            common_set_user($this->user);
+            // this is a real login
+            common_real_login(true);
+
+            // Re-init language env in case it changed (not yet, but soon)
+            common_init_language();
+
+            if (!empty($this->confirmation)) {
+                $this->confirmation->delete();
+            }
+
+            Event::handle('EndRegistrationTry', array($this));
+        }
+
+        if (Event::handle('StartRegisterSuccess', array($this))) {
+            common_redirect(common_local_url('doc', array('title' => 'welcome')),
+                            303);
+            Event::handle('EndRegisterSuccess', array($this));
+        }
+    }
+
+    function sendConfirmEmail($confirm)
+    {
+        $sitename = common_config('site', 'name');
+
+        $recipients = array($confirm->address);
+
+        $headers['From'] = mail_notify_from();
+        $headers['To'] = trim($confirm->address);
+         // TRANS: Subject for confirmation e-mail.
+         // TRANS: %s is the StatusNet sitename.
+        $headers['Subject'] = sprintf(_m('Confirm your registration on %s'), $sitename);
+
+        $confirmUrl = common_local_url('register', array('code' => $confirm->code));
+
+        // TRANS: Body for confirmation e-mail.
+        // TRANS: %1$s is the StatusNet sitename, %2$s is the confirmation URL.
+        $body = sprintf(_m('Someone (probably you) has requested an account on %1$s using this email address.'.
+                          "\n".
+                          'To confirm the address, click the following URL or copy it into the address bar of your browser.'.
+                          "\n".
+                          '%2$s'.
+                          "\n".
+                          'If it was not you, you can safely ignore this message.'),
+                        $sitename,
+                        $confirmUrl);
+
+        mail_send($recipients, $headers, $body);
+    }
+
+    function showContent()
+    {
+        if ($this->complete) {
+            $this->elementStart('p', 'success');
+            $this->raw($this->complete);
+            $this->elementEnd('p');
+        } else {
+            if ($this->error) {
+                $this->elementStart('p', 'error');
+                $this->raw($this->error);
+                $this->elementEnd('p');
+            }
+
+            if (!empty($this->form)) {
+                $this->form->show();
+            }
+        }
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        return false;
+    }
+
+    function nicknameFromEmail($email)
+    {
+        return EmailRegistrationPlugin::nicknameFromEmail($email);
+    }
+
+    /**
+     * A local menu
+     *
+     * Shows different login/register actions.
+     *
+     * @return void
+     */
+    function showLocalNav()
+    {
+        $nav = new LoginGroupNav($this);
+        $nav->show();
+    }
+}
diff --git a/plugins/EmailRegistration/confirmregistrationform.php b/plugins/EmailRegistration/confirmregistrationform.php
deleted file mode 100644 (file)
index 25e1bb0..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Registration confirmation form
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Email registration
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Registration confirmation form
- *
- * @category  Email registration
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class ConfirmRegistrationForm extends Form
-{
-    protected $code;
-    protected $nickname;
-    protected $email;
-
-    function __construct($out, $nickname, $email, $code)
-    {
-        parent::__construct($out);
-        $this->nickname = $nickname;
-        $this->email = $email;
-        $this->code = $code;
-    }
-
-    function formData()
-    {
-        $this->out->element('p', 'instructions',
-                            // TRANS: Form instructions.
-                            sprintf(_m('Enter a password to confirm your new account.')));
-
-        $this->hidden('code', $this->code);
-
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->elementStart('li');
-
-        // TRANS: Field label in e-mail registration form.
-        $this->element('label', array('for' => 'nickname-ignore'), _m('LABEL','User name'));
-
-        $this->element('input', array('name' => 'nickname-ignore',
-                                      'type' => 'text',
-                                      'id' => 'nickname-ignore',
-                                      'disabled' => 'true',
-                                      'value' => $this->nickname));
-
-        $this->elementEnd('li');
-
-        $this->elementStart('li');
-
-        // TRANS: Field label.
-        $this->element('label', array('for' => 'email-ignore'), _m('Email address'));
-
-        $this->element('input', array('name' => 'email-ignore',
-                                      'type' => 'text',
-                                      'id' => 'email-ignore',
-                                      'disabled' => 'true',
-                                      'value' => $this->email));
-
-        $this->elementEnd('li');
-
-        $this->elementStart('li');
-        // TRANS: Field label on account registration page.
-        $this->password('password1', _m('Password'),
-                        // TRANS: Field title on account registration page.
-                        _m('6 or more characters.'));
-        $this->elementEnd('li');
-        $this->elementStart('li');
-        // TRANS: Field label on account registration page. In this field the password has to be entered a second time.
-        $this->password('password2', _m('PASSWORD','Confirm'),
-                        // TRANS: Field title on account registration page.
-                        _m('Same as password above.'));
-        $this->elementEnd('li');
-
-        $this->elementStart('li');
-
-        $this->element('input', array('name' => 'tos',
-                                      'type' => 'checkbox',
-                                      'class' => 'checkbox',
-                                      'id' => 'tos',
-                                      'value' => 'true'));
-        $this->text(' ');
-
-        $this->elementStart('label', array('class' => 'checkbox',
-                                           'for' => 'tos'));
-
-        // TRANS: Checkbox title for terms of service and privacy policy.
-        $this->raw(sprintf(_m('I agree to the <a href="%1$s">Terms of service</a> and '.
-                             '<a href="%1$s">Privacy policy</a> of this site.'),
-                           common_local_url('doc', array('title' => 'tos')),
-                           common_local_url('doc', array('title' => 'privacy'))));
-
-        $this->elementEnd('label');
-
-        $this->elementEnd('li');
-
-        $this->out->elementEnd('ul');
-    }
-
-    function method()
-    {
-        return 'post';
-    }
-
-    /**
-     * Buttons for form actions
-     *
-     * Submit and cancel buttons (or whatever)
-     * Sub-classes should overload this to show their own buttons.
-     *
-     * @return void
-     */
-
-    function formActions()
-    {
-        // TRANS: Button text for action to register.
-        $this->out->submit('submit', _m('BUTTON', 'Register'));
-    }
-
-    /**
-     * ID of the form
-     *
-     * Should be unique on the page. Sub-classes should overload this
-     * to show their own IDs.
-     *
-     * @return int ID of the form
-     */
-
-    function id()
-    {
-        return 'form_email_registration';
-    }
-
-    /**
-     * Action of the form.
-     *
-     * URL to post to. Should be overloaded by subclasses to give
-     * somewhere to post to.
-     *
-     * @return string URL to post to
-     */
-
-    function action()
-    {
-        return common_local_url('register');
-    }
-
-    function formClass()
-    {
-        return 'form_confirm_registration form_settings';
-    }
-}
diff --git a/plugins/EmailRegistration/emailregister.php b/plugins/EmailRegistration/emailregister.php
deleted file mode 100644 (file)
index 5eaf84a..0000000
+++ /dev/null
@@ -1,420 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Register a user by their email address
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Email registration
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Email registration
- *
- * There are four cases where we're called:
- *
- * 1. GET, no arguments. Initial registration; ask for an email address.
- * 2. POST, email address argument. Initial registration; send an email to confirm.
- * 3. GET, code argument. Confirming an invitation or a registration; look them up,
- *    create the relevant user if possible, login as that user, and
- *    show a password-entry form.
- * 4. POST, password argument. After confirmation, set the password for the new
- *    user, and redirect to a registration complete action with some instructions.
- *
- * @category  Action
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class EmailregisterAction extends Action
-{
-    const NEWEMAIL = 1;
-    const SETPASSWORD = 2;
-    const NEWREGISTER = 3;
-    const CONFIRMINVITE = 4;
-    const CONFIRMREGISTER = 5;
-
-    const CONFIRMTYPE = 'register';
-
-    protected $user;
-    protected $email;
-    protected $code;
-    protected $invitation;
-    protected $confirmation;
-    protected $password1;
-    protected $password2;
-    protected $state;
-    protected $error;
-    protected $complete;
-
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        if (common_config('site', 'closed')) {
-            // TRANS: Client exception trown when registration by e-mail is not allowed.
-            throw new ClientException(_m('Registration not allowed.'), 403);
-        }
-
-        if ($this->isPost()) {
-
-            $this->checkSessionToken();
-
-            $this->email = $this->trimmed('email');
-
-            if (!empty($this->email)) {
-                if (common_config('site', 'inviteonly')) {
-                    // TRANS: Client exception trown when trying to register without an invitation.
-                    throw new ClientException(_m('Sorry, only invited people can register.'), 403);
-                }
-                $this->email = common_canonical_email($this->email);
-                $this->state = self::NEWEMAIL;
-            } else {
-                $this->state = self::SETPASSWORD;
-
-                $this->code = $this->trimmed('code');
-
-                if (empty($this->code)) {
-                    // TRANS: Client exception thrown when no confirmation code was provided.
-                    throw new ClientException(_m('No confirmation code.'));
-                }
-
-                $this->invitation = Invitation::getKV('code', $this->code);
-
-                if (!empty($this->invitation)) {
-                    if (!empty($this->invitation->registered_user_id)) {
-                        // TRANS: Client exception trown when using an invitation multiple times.
-                        throw new ClientException(_m('Invitation already used.'), 403);
-                    }
-                } else {
-
-                    $this->confirmation = Confirm_address::getKV('code', $this->code);
-
-                    if (empty($this->confirmation)) {
-                        // TRANS: Client exception thrown when given confirmation code was not issued.
-                        throw new ClientException(_m('No such confirmation code.'), 403);
-                    }
-                }
-
-                $this->password1 = $this->trimmed('password1');
-                $this->password2 = $this->trimmed('password2');
-
-                $this->tos = $this->boolean('tos');
-            }
-        } else { // GET
-            $this->code = $this->trimmed('code');
-
-            if (empty($this->code)) {
-                if (common_config('site', 'inviteonly')) {
-                    // TRANS: Client exception trown when trying to register without an invitation.
-                    throw new ClientException(_m('Sorry, only invited people can register.'), 403);
-                }
-                $this->state = self::NEWREGISTER;
-            } else {
-                $this->invitation = Invitation::getKV('code', $this->code);
-                if (!empty($this->invitation)) {
-                    if (!empty($this->invitation->registered_user_id)) {
-                        // TRANS: Client exception trown when using an invitation multiple times.
-                        throw new ClientException(_m('Invitation already used.'), 403);
-                    }
-                    $this->state = self::CONFIRMINVITE;
-                } else {
-                    $this->state = self::CONFIRMREGISTER;
-                    $this->confirmation = Confirm_address::getKV('code', $this->code);
-
-                    if (empty($this->confirmation)) {
-                        // TRANS: Client exception thrown when given confirmation code was not issued.
-                        throw new ClientException(_m('No such confirmation code.'), 405);
-                    }
-                }
-            }
-        }
-
-        return true;
-    }
-
-    function title()
-    {
-        switch ($this->state) {
-        case self::NEWREGISTER:
-        case self::NEWEMAIL:
-            // TRANS: Title for registration page.
-            return _m('TITLE','Register');
-            break;
-        case self::SETPASSWORD:
-        case self::CONFIRMINVITE:
-        case self::CONFIRMREGISTER:
-            // TRANS: Title for page where to register with a confirmation code.
-            return _m('TITLE','Complete registration');
-            break;
-        }
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        $cur = common_current_user();
-
-        if (!empty($cur)) {
-            common_redirect(common_local_url('all', array('nickname' => $cur->nickname)));
-            return;
-        }
-
-        switch ($this->state) {
-        case self::NEWREGISTER:
-            $this->showRegistrationForm();
-            break;
-        case self::NEWEMAIL:
-            $this->registerUser();
-            break;
-        case self::CONFIRMINVITE:
-            $this->confirmRegistration();
-            break;
-        case self::CONFIRMREGISTER:
-            $this->confirmRegistration();
-            break;
-        case self::SETPASSWORD:
-            $this->setPassword();
-            break;
-        }
-        return;
-    }
-
-    function showRegistrationForm()
-    {
-        $this->form = new EmailRegistrationForm($this, $this->email);
-        $this->showPage();
-    }
-
-    function registerUser()
-    {
-        try {
-            $confirm = EmailRegistrationPlugin::registerEmail($this->email);
-        } catch (ClientException $ce) {
-            $this->error = $ce->getMessage();
-            $this->showRegistrationForm();
-            return;
-        }
-
-        EmailRegistrationPlugin::sendConfirmEmail($confirm);
-
-        // TRANS: Confirmation text after initial registration.
-        // TRANS: %s an e-mail address.
-        $prompt = sprintf(_m('An email was sent to %s to confirm that address. Check your email inbox for instructions.'),
-                          $this->email);
-
-        $this->complete = $prompt;
-
-        $this->showPage();
-    }
-
-    function confirmRegistration()
-    {
-        if (!empty($this->invitation)) {
-            $email = $this->invitation->address;
-        } else if (!empty($this->confirmation)) {
-            $email = $this->confirmation->address;
-        }
-
-        $nickname = $this->nicknameFromEmail($email);
-
-        $this->form = new ConfirmRegistrationForm($this,
-                                                  $nickname,
-                                                  $email,
-                                                  $this->code);
-        $this->showPage();
-    }
-
-    function setPassword()
-    {
-        if (Event::handle('StartRegistrationTry', array($this))) {
-            if (!empty($this->invitation)) {
-                $email = trim($this->invitation->address);
-            } else if (!empty($this->confirmation)) {
-                $email = trim($this->confirmation->address);
-            } else {
-                // TRANS: Client exception trown when trying to set password with an invalid confirmation code.
-                throw new Exception(_m('No confirmation thing.'));
-            }
-
-            if (!$this->tos) {
-                // TRANS: Error text when trying to register without agreeing to the terms.
-                $this->error = _m('You must accept the terms of service and privacy policy to register.');
-            } else if (empty($this->password1)) {
-                // TRANS: Error text when trying to register without a password.
-                $this->error = _m('You must set a password');
-            } else if (strlen($this->password1) < 6) {
-                // TRANS: Error text when trying to register with too short a password.
-                $this->error = _m('Password must be 6 or more characters.');
-            } else if ($this->password1 != $this->password2) {
-                // TRANS: Error text when trying to register without providing the same password twice.
-                $this->error = _m('Passwords do not match.');
-            }
-
-            if (!empty($this->error)) {
-                $nickname = $this->nicknameFromEmail($email);
-                $this->form = new ConfirmRegistrationForm($this, $nickname, $email, $this->code);
-                $this->showPage();
-                return;
-            }
-
-            $nickname = $this->nicknameFromEmail($email);
-
-            try {
-                $fields = array('nickname' => $nickname,
-                                'email' => $email,
-                                'password' => $this->password1,
-                                'email_confirmed' => true);
-
-                if (!empty($this->invitation)) {
-                    $fields['code'] = $this->invitation->code;
-                }
-                $this->user = User::register($fields);
-            } catch (ClientException $e) {
-                $this->error = $e->getMessage();
-                $nickname = $this->nicknameFromEmail($email);
-                $this->form = new ConfirmRegistrationForm($this, $nickname, $email, $this->code);
-                $this->showPage();
-                return;
-            }
-
-            if (empty($this->user)) {
-                // TRANS: Exception trown when using an invitation multiple times.
-                throw new Exception(_m('Failed to register user.'));
-            }
-
-            common_set_user($this->user);
-            // this is a real login
-            common_real_login(true);
-
-            // Re-init language env in case it changed (not yet, but soon)
-            common_init_language();
-
-            if (!empty($this->confirmation)) {
-                $this->confirmation->delete();
-            }
-
-            Event::handle('EndRegistrationTry', array($this));
-        }
-
-        if (Event::handle('StartRegisterSuccess', array($this))) {
-            common_redirect(common_local_url('doc', array('title' => 'welcome')),
-                            303);
-            Event::handle('EndRegisterSuccess', array($this));
-        }
-    }
-
-    function sendConfirmEmail($confirm)
-    {
-        $sitename = common_config('site', 'name');
-
-        $recipients = array($confirm->address);
-
-        $headers['From'] = mail_notify_from();
-        $headers['To'] = trim($confirm->address);
-         // TRANS: Subject for confirmation e-mail.
-         // TRANS: %s is the StatusNet sitename.
-        $headers['Subject'] = sprintf(_m('Confirm your registration on %s'), $sitename);
-
-        $confirmUrl = common_local_url('register', array('code' => $confirm->code));
-
-        // TRANS: Body for confirmation e-mail.
-        // TRANS: %1$s is the StatusNet sitename, %2$s is the confirmation URL.
-        $body = sprintf(_m('Someone (probably you) has requested an account on %1$s using this email address.'.
-                          "\n".
-                          'To confirm the address, click the following URL or copy it into the address bar of your browser.'.
-                          "\n".
-                          '%2$s'.
-                          "\n".
-                          'If it was not you, you can safely ignore this message.'),
-                        $sitename,
-                        $confirmUrl);
-
-        mail_send($recipients, $headers, $body);
-    }
-
-    function showContent()
-    {
-        if ($this->complete) {
-            $this->elementStart('p', 'success');
-            $this->raw($this->complete);
-            $this->elementEnd('p');
-        } else {
-            if ($this->error) {
-                $this->elementStart('p', 'error');
-                $this->raw($this->error);
-                $this->elementEnd('p');
-            }
-
-            if (!empty($this->form)) {
-                $this->form->show();
-            }
-        }
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        return false;
-    }
-
-    function nicknameFromEmail($email)
-    {
-        return EmailRegistrationPlugin::nicknameFromEmail($email);
-    }
-
-    /**
-     * A local menu
-     *
-     * Shows different login/register actions.
-     *
-     * @return void
-     */
-    function showLocalNav()
-    {
-        $nav = new LoginGroupNav($this);
-        $nav->show();
-    }
-}
diff --git a/plugins/EmailRegistration/emailregistrationform.php b/plugins/EmailRegistration/emailregistrationform.php
deleted file mode 100644 (file)
index d72bfc4..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Email registration form
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Email registration
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Email registration form
- *
- * @category  Email registration
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class EmailRegistrationForm extends Form
-{
-    protected $email;
-
-    function __construct($out, $email)
-    {
-        parent::__construct($out);
-        $this->email = $email;
-    }
-
-    function formData()
-    {
-        $this->out->element('p', 'instructions',
-                            // TRANS: Form instructions.
-                            _m('Enter your email address to register for an account.'));
-
-        $this->out->elementStart('fieldset', array('id' => 'new_bookmark_data'));
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-        $this->out->input('email',
-                          // TRANS: Field label on form for registering an account.
-                          _m('LABEL','E-mail address'),
-                          $this->email);
-        $this->unli();
-
-        $this->out->elementEnd('ul');
-        $this->out->elementEnd('fieldset');
-    }
-
-     function method()
-     {
-         return 'post';
-     }
-
-    /**
-     * Buttons for form actions
-     *
-     * Submit and cancel buttons (or whatever)
-     * Sub-classes should overload this to show their own buttons.
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text for registering an account.
-        $this->out->submit('submit', _m('BUTTON', 'Register'));
-    }
-
-    /**
-     * ID of the form
-     *
-     * Should be unique on the page. Sub-classes should overload this
-     * to show their own IDs.
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'form_email_registration';
-    }
-
-    /**
-     * Action of the form.
-     *
-     * URL to post to. Should be overloaded by subclasses to give
-     * somewhere to post to.
-     *
-     * @return string URL to post to
-     */
-    function action()
-    {
-        return common_local_url('register');
-    }
-
-    function formClass()
-    {
-        return 'form_email_registration form_settings';
-    }
-}
diff --git a/plugins/EmailRegistration/forms/confirmregistration.php b/plugins/EmailRegistration/forms/confirmregistration.php
new file mode 100644 (file)
index 0000000..25e1bb0
--- /dev/null
@@ -0,0 +1,187 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Registration confirmation form
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Email registration
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Registration confirmation form
+ *
+ * @category  Email registration
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class ConfirmRegistrationForm extends Form
+{
+    protected $code;
+    protected $nickname;
+    protected $email;
+
+    function __construct($out, $nickname, $email, $code)
+    {
+        parent::__construct($out);
+        $this->nickname = $nickname;
+        $this->email = $email;
+        $this->code = $code;
+    }
+
+    function formData()
+    {
+        $this->out->element('p', 'instructions',
+                            // TRANS: Form instructions.
+                            sprintf(_m('Enter a password to confirm your new account.')));
+
+        $this->hidden('code', $this->code);
+
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->elementStart('li');
+
+        // TRANS: Field label in e-mail registration form.
+        $this->element('label', array('for' => 'nickname-ignore'), _m('LABEL','User name'));
+
+        $this->element('input', array('name' => 'nickname-ignore',
+                                      'type' => 'text',
+                                      'id' => 'nickname-ignore',
+                                      'disabled' => 'true',
+                                      'value' => $this->nickname));
+
+        $this->elementEnd('li');
+
+        $this->elementStart('li');
+
+        // TRANS: Field label.
+        $this->element('label', array('for' => 'email-ignore'), _m('Email address'));
+
+        $this->element('input', array('name' => 'email-ignore',
+                                      'type' => 'text',
+                                      'id' => 'email-ignore',
+                                      'disabled' => 'true',
+                                      'value' => $this->email));
+
+        $this->elementEnd('li');
+
+        $this->elementStart('li');
+        // TRANS: Field label on account registration page.
+        $this->password('password1', _m('Password'),
+                        // TRANS: Field title on account registration page.
+                        _m('6 or more characters.'));
+        $this->elementEnd('li');
+        $this->elementStart('li');
+        // TRANS: Field label on account registration page. In this field the password has to be entered a second time.
+        $this->password('password2', _m('PASSWORD','Confirm'),
+                        // TRANS: Field title on account registration page.
+                        _m('Same as password above.'));
+        $this->elementEnd('li');
+
+        $this->elementStart('li');
+
+        $this->element('input', array('name' => 'tos',
+                                      'type' => 'checkbox',
+                                      'class' => 'checkbox',
+                                      'id' => 'tos',
+                                      'value' => 'true'));
+        $this->text(' ');
+
+        $this->elementStart('label', array('class' => 'checkbox',
+                                           'for' => 'tos'));
+
+        // TRANS: Checkbox title for terms of service and privacy policy.
+        $this->raw(sprintf(_m('I agree to the <a href="%1$s">Terms of service</a> and '.
+                             '<a href="%1$s">Privacy policy</a> of this site.'),
+                           common_local_url('doc', array('title' => 'tos')),
+                           common_local_url('doc', array('title' => 'privacy'))));
+
+        $this->elementEnd('label');
+
+        $this->elementEnd('li');
+
+        $this->out->elementEnd('ul');
+    }
+
+    function method()
+    {
+        return 'post';
+    }
+
+    /**
+     * Buttons for form actions
+     *
+     * Submit and cancel buttons (or whatever)
+     * Sub-classes should overload this to show their own buttons.
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+        // TRANS: Button text for action to register.
+        $this->out->submit('submit', _m('BUTTON', 'Register'));
+    }
+
+    /**
+     * ID of the form
+     *
+     * Should be unique on the page. Sub-classes should overload this
+     * to show their own IDs.
+     *
+     * @return int ID of the form
+     */
+
+    function id()
+    {
+        return 'form_email_registration';
+    }
+
+    /**
+     * Action of the form.
+     *
+     * URL to post to. Should be overloaded by subclasses to give
+     * somewhere to post to.
+     *
+     * @return string URL to post to
+     */
+
+    function action()
+    {
+        return common_local_url('register');
+    }
+
+    function formClass()
+    {
+        return 'form_confirm_registration form_settings';
+    }
+}
diff --git a/plugins/EmailRegistration/forms/emailregistration.php b/plugins/EmailRegistration/forms/emailregistration.php
new file mode 100644 (file)
index 0000000..d72bfc4
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Email registration form
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Email registration
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Email registration form
+ *
+ * @category  Email registration
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class EmailRegistrationForm extends Form
+{
+    protected $email;
+
+    function __construct($out, $email)
+    {
+        parent::__construct($out);
+        $this->email = $email;
+    }
+
+    function formData()
+    {
+        $this->out->element('p', 'instructions',
+                            // TRANS: Form instructions.
+                            _m('Enter your email address to register for an account.'));
+
+        $this->out->elementStart('fieldset', array('id' => 'new_bookmark_data'));
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->out->input('email',
+                          // TRANS: Field label on form for registering an account.
+                          _m('LABEL','E-mail address'),
+                          $this->email);
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+        $this->out->elementEnd('fieldset');
+    }
+
+     function method()
+     {
+         return 'post';
+     }
+
+    /**
+     * Buttons for form actions
+     *
+     * Submit and cancel buttons (or whatever)
+     * Sub-classes should overload this to show their own buttons.
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text for registering an account.
+        $this->out->submit('submit', _m('BUTTON', 'Register'));
+    }
+
+    /**
+     * ID of the form
+     *
+     * Should be unique on the page. Sub-classes should overload this
+     * to show their own IDs.
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'form_email_registration';
+    }
+
+    /**
+     * Action of the form.
+     *
+     * URL to post to. Should be overloaded by subclasses to give
+     * somewhere to post to.
+     *
+     * @return string URL to post to
+     */
+    function action()
+    {
+        return common_local_url('register');
+    }
+
+    function formClass()
+    {
+        return 'form_email_registration form_settings';
+    }
+}
index 4e144af5217f66b8745e8de3ab015a632659ab49..e55f134a9492b4ee6c487c777777e6014ff5dd88 100644 (file)
@@ -61,32 +61,6 @@ class EmailReminderPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false
-     *         means stop.
-     */
-    function onAutoload($cls) {
-        $base = dirname(__FILE__);
-        $lower = strtolower($cls);
-
-        $files = array("$base/classes/$cls.php",
-            "$base/lib/$lower.php");
-        if (substr($lower, -6) == 'action') {
-            $files[] = "$base/actions/" . substr($lower, 0, -6) . ".php";
-        }
-        foreach ($files as $file) {
-            if (file_exists($file)) {
-                include_once $file;
-                return false;
-            }
-        }
-        return true;
-    }
-
     /**
      * Register our queue handlers
      *
index f99a10bc02c9954709d61e556ae10699c5362da4..0ba80e1e36521a492238772485e43e06ec96d0c7 100644 (file)
@@ -59,32 +59,6 @@ class EmailSummaryPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     *
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-            {
-            case 'SiteEmailSummaryHandler':
-            case 'UserEmailSummaryHandler':
-                include_once $dir . '/'.strtolower($cls).'.php';
-            return false;
-            case 'Email_summary_status':
-                include_once $dir . '/'.$cls.'.php';
-                return false;
-            default:
-                return true;
-            }
-    }
-
     /**
      * Version info for this plugin
      *
diff --git a/plugins/EmailSummary/Email_summary_status.php b/plugins/EmailSummary/Email_summary_status.php
deleted file mode 100644 (file)
index 460053f..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-<?php
-/**
- * Data class for email summary status
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for email summaries
- *
- * Email summary information for users
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Email_summary_status extends Managed_DataObject
-{
-    public $__table = 'email_summary_status'; // table name
-    public $user_id;                         // int(4)  primary_key not_null
-    public $send_summary;                    // tinyint not_null
-    public $last_summary_id;                 // int(4)  null
-    public $created;                         // datetime not_null
-    public $modified;                        // datetime not_null
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id'),
-                'send_summary' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'not null' => true, 'description' => 'whether to send a summary or not'),
-                'last_summary_id' => array('type' => 'int', 'description' => 'last summary id'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),   
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('user_id'),
-            'foreign keys' => array(
-                'email_summary_status_user_id_fkey' => array('user', array('user_id' => 'id')),
-            ),
-        );
-    }
-
-    /**
-     * Helper function
-     *
-     * @param integer $user_id ID of the user to get a count for
-     *
-     * @return int flag for whether to send this user a summary email
-     */
-    static function getSendSummary($user_id)
-    {
-        $ess = Email_summary_status::getKV('user_id', $user_id);
-
-        if (!empty($ess)) {
-            return $ess->send_summary;
-        } else {
-            return 1;
-        }
-    }
-
-    /**
-     * Get email summary status for a user
-     *
-     * @param integer $user_id ID of the user to get a count for
-     *
-     * @return Email_summary_status instance for this user, with count already incremented.
-     */
-    static function getLastSummaryID($user_id)
-    {
-        $ess = Email_summary_status::getKV('user_id', $user_id);
-
-        if (!empty($ess)) {
-            return $ess->last_summary_id;
-        } else {
-            return null;
-        }
-    }
-}
diff --git a/plugins/EmailSummary/classes/Email_summary_status.php b/plugins/EmailSummary/classes/Email_summary_status.php
new file mode 100644 (file)
index 0000000..460053f
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+/**
+ * Data class for email summary status
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for email summaries
+ *
+ * Email summary information for users
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Email_summary_status extends Managed_DataObject
+{
+    public $__table = 'email_summary_status'; // table name
+    public $user_id;                         // int(4)  primary_key not_null
+    public $send_summary;                    // tinyint not_null
+    public $last_summary_id;                 // int(4)  null
+    public $created;                         // datetime not_null
+    public $modified;                        // datetime not_null
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id'),
+                'send_summary' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'not null' => true, 'description' => 'whether to send a summary or not'),
+                'last_summary_id' => array('type' => 'int', 'description' => 'last summary id'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),   
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('user_id'),
+            'foreign keys' => array(
+                'email_summary_status_user_id_fkey' => array('user', array('user_id' => 'id')),
+            ),
+        );
+    }
+
+    /**
+     * Helper function
+     *
+     * @param integer $user_id ID of the user to get a count for
+     *
+     * @return int flag for whether to send this user a summary email
+     */
+    static function getSendSummary($user_id)
+    {
+        $ess = Email_summary_status::getKV('user_id', $user_id);
+
+        if (!empty($ess)) {
+            return $ess->send_summary;
+        } else {
+            return 1;
+        }
+    }
+
+    /**
+     * Get email summary status for a user
+     *
+     * @param integer $user_id ID of the user to get a count for
+     *
+     * @return Email_summary_status instance for this user, with count already incremented.
+     */
+    static function getLastSummaryID($user_id)
+    {
+        $ess = Email_summary_status::getKV('user_id', $user_id);
+
+        if (!empty($ess)) {
+            return $ess->last_summary_id;
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/plugins/EmailSummary/lib/siteemailsummaryhandler.php b/plugins/EmailSummary/lib/siteemailsummaryhandler.php
new file mode 100644 (file)
index 0000000..37e1a22
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ *
+ * Handler for queue items of type 'sitesum', sends email summaries
+ * to all users on the site.
+ *
+ * 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  Sample
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ *
+ * Handler for queue items of type 'sitesum', sends email summaries
+ * to all users on the site.
+ *
+ * @category  Email
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class SiteEmailSummaryHandler extends QueueHandler
+{
+    /**
+     * Return transport keyword which identifies items this queue handler
+     * services; must be defined for all subclasses.
+     *
+     * Must be 8 characters or less to fit in the queue_item database.
+     * ex "email", "jabber", "sms", "irc", ...
+     *
+     * @return string
+     */
+    function transport()
+    {
+        return 'sitesum';
+    }
+
+    /**
+     * Handle the site
+     *
+     * @param mixed $object
+     * @return boolean true on success, false on failure
+     */
+    function handle($object)
+    {
+        $qm = QueueManager::get();
+
+        try {
+            // Enqueue a summary for all users
+
+            $user = new User();
+            $user->find();
+
+            while ($user->fetch()) {
+                try {
+                    $qm->enqueue($user->id, 'usersum');
+                } catch (Exception $e) {
+                    common_log(LOG_WARNING, $e->getMessage());
+                    continue;
+                }
+            }
+        } catch (Exception $e) {
+            common_log(LOG_WARNING, $e->getMessage());
+        }
+
+        return true;
+    }
+}
diff --git a/plugins/EmailSummary/lib/useremailsummaryhandler.php b/plugins/EmailSummary/lib/useremailsummaryhandler.php
new file mode 100644 (file)
index 0000000..46275c3
--- /dev/null
@@ -0,0 +1,245 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ *
+ * Handler for queue items of type 'usersum', sends an email summaries
+ * to a particular user.
+ *
+ * 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  Sample
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Handler for queue items of type 'usersum', sends an email summaries
+ * to a particular user.
+ *
+ * @category  Email
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class UserEmailSummaryHandler extends QueueHandler
+{
+    // Maximum number of notices to include by default. This is probably too much.
+    const MAX_NOTICES = 200;
+
+    /**
+     * Return transport keyword which identifies items this queue handler
+     * services; must be defined for all subclasses.
+     *
+     * Must be 8 characters or less to fit in the queue_item database.
+     * ex "email", "jabber", "sms", "irc", ...
+     *
+     * @return string
+     */
+    function transport()
+    {
+        return 'usersum';
+    }
+
+    /**
+     * Send a summary email to the user
+     *
+     * @param mixed $object
+     * @return boolean true on success, false on failure
+     */
+    function handle($user_id)
+    {
+        // Skip if they've asked not to get summaries
+
+        $ess = Email_summary_status::getKV('user_id', $user_id);
+
+        if (!empty($ess) && !$ess->send_summary) {
+            common_log(LOG_INFO, sprintf('Not sending email summary for user %s by request.', $user_id));
+            return true;
+        }
+
+        $since_id = null;
+
+        if (!empty($ess)) {
+            $since_id = $ess->last_summary_id;
+        }
+
+        $user = User::getKV('id', $user_id);
+
+        if (empty($user)) {
+            common_log(LOG_INFO, sprintf('Not sending email summary for user %s; no such user.', $user_id));
+            return true;
+        }
+
+        if (empty($user->email)) {
+            common_log(LOG_INFO, sprintf('Not sending email summary for user %s; no email address.', $user_id));
+            return true;
+        }
+
+        $profile = $user->getProfile();
+
+        if (empty($profile)) {
+            common_log(LOG_WARNING, sprintf('Not sending email summary for user %s; no profile.', $user_id));
+            return true;
+        }
+
+        $stream = new InboxNoticeStream($user, $user->getProfile());
+
+        $notice = $stream->getNotices(0, self::MAX_NOTICES, $since_id);
+
+        if (empty($notice) || $notice->N == 0) {
+            common_log(LOG_WARNING, sprintf('Not sending email summary for user %s; no notices.', $user_id));
+            return true;
+        }
+
+        // XXX: This is risky fingerpoken in der objektvars, but I didn't feel like
+        // figuring out a better way. -ESP
+
+        $new_top = null;
+
+        if ($notice instanceof ArrayWrapper) {
+            $new_top = $notice->_items[0]->id;
+        }
+
+        // TRANS: Subject for e-mail.
+        $subject = sprintf(_m('Your latest updates from %s'), common_config('site', 'name'));
+
+        $out = new XMLStringer(true);
+
+        $out->elementStart('html');
+        $out->elementStart('head');
+        $out->element('title', null, $subject);
+        $out->elementEnd('head');
+        $out->elementStart('body');
+        $out->elementStart('div', array('width' => '100%',
+                                        'style' => 'background-color: #ffffff; border: 4px solid #4c609a; padding: 10px;'));
+
+        $out->elementStart('div', array('style' => 'color: #ffffff; background-color: #4c609a; font-weight: bold; margin-bottom: 10px; padding: 4px;'));
+        // TRANS: Text in e-mail summary.
+        // TRANS: %1$s is the StatusNet sitename, %2$s is the recipient's profile name.
+        $out->raw(sprintf(_m('Recent updates from %1$s for %2$s:'),
+                          common_config('site', 'name'),
+                          $profile->getBestName()));
+        $out->elementEnd('div');
+
+        $out->elementStart('table', array('width' => '550px',
+                                          'style' => 'border: none; border-collapse: collapse;', 'cellpadding' => '6'));
+
+        while ($notice->fetch()) {
+            $profile = Profile::getKV('id', $notice->profile_id);
+
+            if (empty($profile)) {
+                continue;
+            }
+
+            $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
+
+            $out->elementStart('tr');
+            $out->elementStart('td', array('width' => AVATAR_STREAM_SIZE,
+                                           'height' => AVATAR_STREAM_SIZE,
+                                           'align' => 'left',
+                                           'valign' => 'top',
+                                           'style' => 'border-bottom: 1px dotted #C5CEE3; padding: 10px 6px 10px 6px;'));
+            $out->element('img', array('src' => ($avatar) ?
+                                       $avatar->displayUrl() :
+                                       Avatar::defaultImage(AVATAR_STREAM_SIZE),
+                                       'width' => AVATAR_STREAM_SIZE,
+                                       'height' => AVATAR_STREAM_SIZE,
+                                       'alt' => $profile->getBestName()));
+            $out->elementEnd('td');
+            $out->elementStart('td', array('align' => 'left',
+                                           'valign' => 'top',
+                                           'style' => 'border-bottom: 1px dotted #C5CEE3; padding: 10px 6px 10px 6px;'));
+            $out->element('a', array('href' => $profile->profileurl),
+                          $profile->nickname);
+            $out->text(' ');
+            $out->raw($notice->rendered);
+            $out->elementStart('div', array('style' => 'font-size: 0.8em; padding-top: 4px;'));
+            $noticeurl = $notice->bestUrl();
+            // above should always return an URL
+            assert(!empty($noticeurl));
+            $out->elementStart('a', array('rel' => 'bookmark',
+                                          'href' => $noticeurl));
+            $dt = common_date_iso8601($notice->created);
+            $out->element('abbr', array('style' => 'border-bottom: none;',
+                                        'title' => $dt),
+                          common_date_string($notice->created));
+            $out->elementEnd('a');
+            if ($notice->hasConversation()) {
+                $conv = Conversation::getKV('id', $notice->conversation);
+                $convurl = $conv->uri;
+                if (!empty($convurl)) {
+                    $out->text(' ');
+                    $out->element('a',
+                                  array('href' => $convurl.'#notice-'.$notice->id),
+                                  // TRANS: Link text for link to conversation view.
+                                  _m('in context'));
+                }
+            }
+            $out->elementEnd('div');
+            $out->elementEnd('td');
+            $out->elementEnd('tr');
+        }
+
+        $out->elementEnd('table');
+
+        // TRANS: Link text for link to e-mail settings.
+        // TRANS: %1$s is a link to the e-mail settings, %2$s is the StatusNet sitename.
+        $out->raw("<p>" . sprintf(_m('<a href="%1$s">change your email settings for %2$s</a>'),
+                          common_local_url('emailsettings'),
+                          common_config('site', 'name'))."</p>");
+
+        $out->elementEnd('div');
+        $out->elementEnd('body');
+        $out->elementEnd('html');
+
+        $body = $out->getString();
+
+        // FIXME: do something for people who don't like HTML email
+
+        mail_to_user($user,
+                     $subject,
+                     $body,
+                     array('Content-Type' => 'text/html; charset=utf-8',
+                           'Mime-Version' => '1.0'));
+
+        if (empty($ess)) {
+            $ess = new Email_summary_status();
+
+            $ess->user_id         = $user_id;
+            $ess->created         = common_sql_now();
+            $ess->last_summary_id = $new_top;
+            $ess->modified        = common_sql_now();
+
+            $ess->insert();
+        } else {
+            $orig = clone($ess);
+
+            $ess->last_summary_id = $new_top;
+            $ess->modified        = common_sql_now();
+
+            $ess->update($orig);
+        }
+
+        return true;
+    }
+}
diff --git a/plugins/EmailSummary/scripts/sendemailsummary.php b/plugins/EmailSummary/scripts/sendemailsummary.php
new file mode 100644 (file)
index 0000000..01c11a2
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/*
+ * StatusNet - a distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
+
+$shortoptions = 'i:n:au';
+$longoptions = array('id=', 'nickname=', 'all', 'universe');
+
+$helptext = <<<END_OF_SENDEMAILSUMMARY_HELP
+sendemailsummary.php [options]
+Send an email summary of the inbox to users
+
+ -i --id       ID of user to send summary to
+ -n --nickname nickname of the user to send summary to
+ -a --all      send summary to all users
+ -u --universe send summary to all users on all sites
+
+END_OF_SENDEMAILSUMMARY_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+if (have_option('u', 'universe')) {
+    $sn = new Status_network();
+    if ($sn->find()) {
+        while ($sn->fetch()) {
+            $server = $sn->getServerName();
+            StatusNet::init($server);
+            // Different queue manager, maybe!
+            $qm = QueueManager::get();
+            $qm->enqueue(1, 'sitesum');
+        }
+    }
+} else {
+    $qm = QueueManager::get();
+    // enqueue summary for user or all users
+    try {
+        $user = getUser();
+        $qm->enqueue($user->id, 'usersum');
+    } catch (NoUserArgumentException $nuae) {
+        $qm->enqueue(1, 'sitesum');
+    }
+}
diff --git a/plugins/EmailSummary/sendemailsummary.php b/plugins/EmailSummary/sendemailsummary.php
deleted file mode 100644 (file)
index 01c11a2..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-/*
- * StatusNet - a distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
-
-$shortoptions = 'i:n:au';
-$longoptions = array('id=', 'nickname=', 'all', 'universe');
-
-$helptext = <<<END_OF_SENDEMAILSUMMARY_HELP
-sendemailsummary.php [options]
-Send an email summary of the inbox to users
-
- -i --id       ID of user to send summary to
- -n --nickname nickname of the user to send summary to
- -a --all      send summary to all users
- -u --universe send summary to all users on all sites
-
-END_OF_SENDEMAILSUMMARY_HELP;
-
-require_once INSTALLDIR.'/scripts/commandline.inc';
-
-if (have_option('u', 'universe')) {
-    $sn = new Status_network();
-    if ($sn->find()) {
-        while ($sn->fetch()) {
-            $server = $sn->getServerName();
-            StatusNet::init($server);
-            // Different queue manager, maybe!
-            $qm = QueueManager::get();
-            $qm->enqueue(1, 'sitesum');
-        }
-    }
-} else {
-    $qm = QueueManager::get();
-    // enqueue summary for user or all users
-    try {
-        $user = getUser();
-        $qm->enqueue($user->id, 'usersum');
-    } catch (NoUserArgumentException $nuae) {
-        $qm->enqueue(1, 'sitesum');
-    }
-}
diff --git a/plugins/EmailSummary/siteemailsummaryhandler.php b/plugins/EmailSummary/siteemailsummaryhandler.php
deleted file mode 100644 (file)
index 37e1a22..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- *
- * Handler for queue items of type 'sitesum', sends email summaries
- * to all users on the site.
- *
- * 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  Sample
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- *
- * Handler for queue items of type 'sitesum', sends email summaries
- * to all users on the site.
- *
- * @category  Email
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class SiteEmailSummaryHandler extends QueueHandler
-{
-    /**
-     * Return transport keyword which identifies items this queue handler
-     * services; must be defined for all subclasses.
-     *
-     * Must be 8 characters or less to fit in the queue_item database.
-     * ex "email", "jabber", "sms", "irc", ...
-     *
-     * @return string
-     */
-    function transport()
-    {
-        return 'sitesum';
-    }
-
-    /**
-     * Handle the site
-     *
-     * @param mixed $object
-     * @return boolean true on success, false on failure
-     */
-    function handle($object)
-    {
-        $qm = QueueManager::get();
-
-        try {
-            // Enqueue a summary for all users
-
-            $user = new User();
-            $user->find();
-
-            while ($user->fetch()) {
-                try {
-                    $qm->enqueue($user->id, 'usersum');
-                } catch (Exception $e) {
-                    common_log(LOG_WARNING, $e->getMessage());
-                    continue;
-                }
-            }
-        } catch (Exception $e) {
-            common_log(LOG_WARNING, $e->getMessage());
-        }
-
-        return true;
-    }
-}
diff --git a/plugins/EmailSummary/useremailsummaryhandler.php b/plugins/EmailSummary/useremailsummaryhandler.php
deleted file mode 100644 (file)
index 46275c3..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- *
- * Handler for queue items of type 'usersum', sends an email summaries
- * to a particular user.
- *
- * 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  Sample
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Handler for queue items of type 'usersum', sends an email summaries
- * to a particular user.
- *
- * @category  Email
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class UserEmailSummaryHandler extends QueueHandler
-{
-    // Maximum number of notices to include by default. This is probably too much.
-    const MAX_NOTICES = 200;
-
-    /**
-     * Return transport keyword which identifies items this queue handler
-     * services; must be defined for all subclasses.
-     *
-     * Must be 8 characters or less to fit in the queue_item database.
-     * ex "email", "jabber", "sms", "irc", ...
-     *
-     * @return string
-     */
-    function transport()
-    {
-        return 'usersum';
-    }
-
-    /**
-     * Send a summary email to the user
-     *
-     * @param mixed $object
-     * @return boolean true on success, false on failure
-     */
-    function handle($user_id)
-    {
-        // Skip if they've asked not to get summaries
-
-        $ess = Email_summary_status::getKV('user_id', $user_id);
-
-        if (!empty($ess) && !$ess->send_summary) {
-            common_log(LOG_INFO, sprintf('Not sending email summary for user %s by request.', $user_id));
-            return true;
-        }
-
-        $since_id = null;
-
-        if (!empty($ess)) {
-            $since_id = $ess->last_summary_id;
-        }
-
-        $user = User::getKV('id', $user_id);
-
-        if (empty($user)) {
-            common_log(LOG_INFO, sprintf('Not sending email summary for user %s; no such user.', $user_id));
-            return true;
-        }
-
-        if (empty($user->email)) {
-            common_log(LOG_INFO, sprintf('Not sending email summary for user %s; no email address.', $user_id));
-            return true;
-        }
-
-        $profile = $user->getProfile();
-
-        if (empty($profile)) {
-            common_log(LOG_WARNING, sprintf('Not sending email summary for user %s; no profile.', $user_id));
-            return true;
-        }
-
-        $stream = new InboxNoticeStream($user, $user->getProfile());
-
-        $notice = $stream->getNotices(0, self::MAX_NOTICES, $since_id);
-
-        if (empty($notice) || $notice->N == 0) {
-            common_log(LOG_WARNING, sprintf('Not sending email summary for user %s; no notices.', $user_id));
-            return true;
-        }
-
-        // XXX: This is risky fingerpoken in der objektvars, but I didn't feel like
-        // figuring out a better way. -ESP
-
-        $new_top = null;
-
-        if ($notice instanceof ArrayWrapper) {
-            $new_top = $notice->_items[0]->id;
-        }
-
-        // TRANS: Subject for e-mail.
-        $subject = sprintf(_m('Your latest updates from %s'), common_config('site', 'name'));
-
-        $out = new XMLStringer(true);
-
-        $out->elementStart('html');
-        $out->elementStart('head');
-        $out->element('title', null, $subject);
-        $out->elementEnd('head');
-        $out->elementStart('body');
-        $out->elementStart('div', array('width' => '100%',
-                                        'style' => 'background-color: #ffffff; border: 4px solid #4c609a; padding: 10px;'));
-
-        $out->elementStart('div', array('style' => 'color: #ffffff; background-color: #4c609a; font-weight: bold; margin-bottom: 10px; padding: 4px;'));
-        // TRANS: Text in e-mail summary.
-        // TRANS: %1$s is the StatusNet sitename, %2$s is the recipient's profile name.
-        $out->raw(sprintf(_m('Recent updates from %1$s for %2$s:'),
-                          common_config('site', 'name'),
-                          $profile->getBestName()));
-        $out->elementEnd('div');
-
-        $out->elementStart('table', array('width' => '550px',
-                                          'style' => 'border: none; border-collapse: collapse;', 'cellpadding' => '6'));
-
-        while ($notice->fetch()) {
-            $profile = Profile::getKV('id', $notice->profile_id);
-
-            if (empty($profile)) {
-                continue;
-            }
-
-            $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
-
-            $out->elementStart('tr');
-            $out->elementStart('td', array('width' => AVATAR_STREAM_SIZE,
-                                           'height' => AVATAR_STREAM_SIZE,
-                                           'align' => 'left',
-                                           'valign' => 'top',
-                                           'style' => 'border-bottom: 1px dotted #C5CEE3; padding: 10px 6px 10px 6px;'));
-            $out->element('img', array('src' => ($avatar) ?
-                                       $avatar->displayUrl() :
-                                       Avatar::defaultImage(AVATAR_STREAM_SIZE),
-                                       'width' => AVATAR_STREAM_SIZE,
-                                       'height' => AVATAR_STREAM_SIZE,
-                                       'alt' => $profile->getBestName()));
-            $out->elementEnd('td');
-            $out->elementStart('td', array('align' => 'left',
-                                           'valign' => 'top',
-                                           'style' => 'border-bottom: 1px dotted #C5CEE3; padding: 10px 6px 10px 6px;'));
-            $out->element('a', array('href' => $profile->profileurl),
-                          $profile->nickname);
-            $out->text(' ');
-            $out->raw($notice->rendered);
-            $out->elementStart('div', array('style' => 'font-size: 0.8em; padding-top: 4px;'));
-            $noticeurl = $notice->bestUrl();
-            // above should always return an URL
-            assert(!empty($noticeurl));
-            $out->elementStart('a', array('rel' => 'bookmark',
-                                          'href' => $noticeurl));
-            $dt = common_date_iso8601($notice->created);
-            $out->element('abbr', array('style' => 'border-bottom: none;',
-                                        'title' => $dt),
-                          common_date_string($notice->created));
-            $out->elementEnd('a');
-            if ($notice->hasConversation()) {
-                $conv = Conversation::getKV('id', $notice->conversation);
-                $convurl = $conv->uri;
-                if (!empty($convurl)) {
-                    $out->text(' ');
-                    $out->element('a',
-                                  array('href' => $convurl.'#notice-'.$notice->id),
-                                  // TRANS: Link text for link to conversation view.
-                                  _m('in context'));
-                }
-            }
-            $out->elementEnd('div');
-            $out->elementEnd('td');
-            $out->elementEnd('tr');
-        }
-
-        $out->elementEnd('table');
-
-        // TRANS: Link text for link to e-mail settings.
-        // TRANS: %1$s is a link to the e-mail settings, %2$s is the StatusNet sitename.
-        $out->raw("<p>" . sprintf(_m('<a href="%1$s">change your email settings for %2$s</a>'),
-                          common_local_url('emailsettings'),
-                          common_config('site', 'name'))."</p>");
-
-        $out->elementEnd('div');
-        $out->elementEnd('body');
-        $out->elementEnd('html');
-
-        $body = $out->getString();
-
-        // FIXME: do something for people who don't like HTML email
-
-        mail_to_user($user,
-                     $subject,
-                     $body,
-                     array('Content-Type' => 'text/html; charset=utf-8',
-                           'Mime-Version' => '1.0'));
-
-        if (empty($ess)) {
-            $ess = new Email_summary_status();
-
-            $ess->user_id         = $user_id;
-            $ess->created         = common_sql_now();
-            $ess->last_summary_id = $new_top;
-            $ess->modified        = common_sql_now();
-
-            $ess->insert();
-        } else {
-            $orig = clone($ess);
-
-            $ess->last_summary_id = $new_top;
-            $ess->modified        = common_sql_now();
-
-            $ess->update($orig);
-        }
-
-        return true;
-    }
-}
index 7676a25ac01a2a29d0abfe33d7e6318eb918a39e..2a8a078c747e717b294dbf0218b4928aeaaa60a5 100644 (file)
@@ -64,44 +64,6 @@ class EventPlugin extends MicroappPlugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'NeweventAction':
-        case 'NewrsvpAction':
-        case 'CancelrsvpAction':
-        case 'ShoweventAction':
-        case 'ShowrsvpAction':
-        case 'TimelistAction':
-            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'EventListItem':
-        case 'RSVPListItem':
-        case 'EventForm':
-        case 'RSVPForm':
-        case 'CancelRSVPForm':
-        case 'EventTimeList':
-            include_once $dir . '/'.strtolower($cls).'.php';
-            break;
-        case 'Happening':
-        case 'RSVP':
-            include_once $dir . '/'.$cls.'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Map URLs to actions
      *
diff --git a/plugins/Event/Happening.php b/plugins/Event/Happening.php
deleted file mode 100644 (file)
index 2b097e9..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-<?php
-/**
- * Data class for happenings
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Data class for happenings
- *
- * There's already an Event class in lib/event.php, so we couldn't
- * call this an Event without causing a hole in space-time.
- *
- * "Happening" seemed good enough.
- *
- * @category Event
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      Managed_DataObject
- */
-class Happening extends Managed_DataObject
-{
-    const OBJECT_TYPE = 'http://activitystrea.ms/schema/1.0/event';
-
-    public $__table = 'happening'; // table name
-    public $id;                    // varchar(36) UUID
-    public $uri;                   // varchar(255)
-    public $profile_id;            // int
-    public $start_time;            // datetime
-    public $end_time;              // datetime
-    public $title;                 // varchar(255)
-    public $location;              // varchar(255)
-    public $url;                   // varchar(255)
-    public $description;           // text
-    public $created;               // datetime
-
-    /**
-     * The One True Thingy that must be defined and declared.
-     */
-    public static function schemaDef()
-    {
-        return array(
-            'description' => 'A real-world happening',
-            'fields' => array(
-                'id' => array('type' => 'char',
-                              'length' => 36,
-                              'not null' => true,
-                              'description' => 'UUID'),
-                'uri' => array('type' => 'varchar',
-                               'length' => 255,
-                               'not null' => true),
-                'profile_id' => array('type' => 'int', 'not null' => true),
-                'start_time' => array('type' => 'datetime', 'not null' => true),
-                'end_time' => array('type' => 'datetime', 'not null' => true),
-                'title' => array('type' => 'varchar',
-                                 'length' => 255,
-                                 'not null' => true),
-                'location' => array('type' => 'varchar',
-                                    'length' => 255),
-                'url' => array('type' => 'varchar',
-                               'length' => 255),
-                'description' => array('type' => 'text'),
-                'created' => array('type' => 'datetime',
-                                   'not null' => true),
-            ),
-            'primary key' => array('id'),
-            'unique keys' => array(
-                'happening_uri_key' => array('uri'),
-            ),
-            'foreign keys' => array('happening_profile_id__key' => array('profile', array('profile_id' => 'id'))),
-            'indexes' => array('happening_created_idx' => array('created'),
-                               'happening_start_end_idx' => array('start_time', 'end_time')),
-        );
-    }
-
-    static function saveNew($profile, $start_time, $end_time, $title, $location, $description, $url, $options=array())
-    {
-        if (array_key_exists('uri', $options)) {
-            $other = Happening::getKV('uri', $options['uri']);
-            if (!empty($other)) {
-                // TRANS: Client exception thrown when trying to create an event that already exists.
-                throw new ClientException(_m('Event already exists.'));
-            }
-        }
-
-        $ev = new Happening();
-
-        $ev->id          = UUID::gen();
-        $ev->profile_id  = $profile->id;
-        $ev->start_time  = common_sql_date($start_time);
-        $ev->end_time    = common_sql_date($end_time);
-        $ev->title       = $title;
-        $ev->location    = $location;
-        $ev->description = $description;
-        $ev->url         = $url;
-
-        if (array_key_exists('created', $options)) {
-            $ev->created = $options['created'];
-        } else {
-            $ev->created = common_sql_now();
-        }
-
-        if (array_key_exists('uri', $options)) {
-            $ev->uri = $options['uri'];
-        } else {
-            $ev->uri = common_local_url('showevent',
-                                        array('id' => $ev->id));
-        }
-
-        $ev->insert();
-
-        // XXX: does this get truncated?
-
-        // TRANS: Event description. %1$s is a title, %2$s is start time, %3$s is end time,
-        // TRANS: %4$s is location, %5$s is a description.
-        $content = sprintf(_m('"%1$s" %2$s - %3$s (%4$s): %5$s'),
-                           $title,
-                           common_exact_date($ev->start_time),
-                           common_exact_date($ev->end_time),
-                           $location,
-                           $description);
-
-        // TRANS: Rendered event description. %1$s is a title, %2$s is start time, %3$s is start time,
-        // TRANS: %4$s is end time, %5$s is end time, %6$s is location, %7$s is description.
-        // TRANS: Class names should not be translated.
-        $rendered = sprintf(_m('<span class="vevent">'.
-                              '<span class="summary">%1$s</span> '.
-                              '<abbr class="dtstart" title="%2$s">%3$s</a> - '.
-                              '<abbr class="dtend" title="%4$s">%5$s</a> '.
-                              '(<span class="location">%6$s</span>): '.
-                              '<span class="description">%7$s</span> '.
-                              '</span>'),
-                            htmlspecialchars($title),
-                            htmlspecialchars(common_date_iso8601($ev->start_time)),
-                            htmlspecialchars(common_exact_date($ev->start_time)),
-                            htmlspecialchars(common_date_iso8601($ev->end_time)),
-                            htmlspecialchars(common_exact_date($ev->end_time)),
-                            htmlspecialchars($location),
-                            htmlspecialchars($description));
-
-        $options = array_merge(array('object_type' => Happening::OBJECT_TYPE),
-                               $options);
-
-        if (!array_key_exists('uri', $options)) {
-            $options['uri'] = $ev->uri;
-        }
-
-        if (!empty($url)) {
-            $options['urls'] = array($url);
-        }
-
-        $saved = Notice::saveNew($profile->id,
-                                 $content,
-                                 array_key_exists('source', $options) ?
-                                 $options['source'] : 'web',
-                                 $options);
-
-        return $saved;
-    }
-
-    function getNotice()
-    {
-        return Notice::getKV('uri', $this->uri);
-    }
-
-    static function fromNotice($notice)
-    {
-        return Happening::getKV('uri', $notice->uri);
-    }
-
-    function getRSVPs()
-    {
-        return RSVP::forEvent($this);
-    }
-
-    function getRSVP($profile)
-    {
-        return RSVP::pkeyGet(array('profile_id' => $profile->id,
-                                   'event_id' => $this->id));
-    }
-}
diff --git a/plugins/Event/RSVP.php b/plugins/Event/RSVP.php
deleted file mode 100644 (file)
index cea916c..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-<?php
-/**
- * Data class for event RSVPs
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Data class for event RSVPs
- *
- * @category Event
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      Managed_DataObject
- */
-class RSVP extends Managed_DataObject
-{
-    const POSITIVE = 'http://activitystrea.ms/schema/1.0/rsvp-yes';
-    const POSSIBLE = 'http://activitystrea.ms/schema/1.0/rsvp-maybe';
-    const NEGATIVE = 'http://activitystrea.ms/schema/1.0/rsvp-no';
-
-    public $__table = 'rsvp'; // table name
-    public $id;                // varchar(36) UUID
-    public $uri;               // varchar(255)
-    public $profile_id;        // int
-    public $event_id;          // varchar(36) UUID
-    public $response;            // tinyint
-    public $created;           // datetime
-
-    /**
-     * Add the compound profile_id/event_id index to our cache keys
-     * since the DB_DataObject stuff doesn't understand compound keys
-     * except for the primary.
-     *
-     * @return array
-     */
-    function _allCacheKeys() {
-        $keys = parent::_allCacheKeys();
-        $keys[] = self::multicacheKey('RSVP', array('profile_id' => $this->profile_id,
-                                                    'event_id' => $this->event_id));
-        return $keys;
-    }
-
-    /**
-     * The One True Thingy that must be defined and declared.
-     */
-    public static function schemaDef()
-    {
-        return array(
-            'description' => 'Plan to attend event',
-            'fields' => array(
-                'id' => array('type' => 'char',
-                              'length' => 36,
-                              'not null' => true,
-                              'description' => 'UUID'),
-                'uri' => array('type' => 'varchar',
-                               'length' => 255,
-                               'not null' => true),
-                'profile_id' => array('type' => 'int'),
-                'event_id' => array('type' => 'char',
-                              'length' => 36,
-                              'not null' => true,
-                              'description' => 'UUID'),
-                'response' => array('type' => 'char',
-                                  'length' => '1',
-                                  'description' => 'Y, N, or ? for three-state yes, no, maybe'),
-                'created' => array('type' => 'datetime',
-                                   'not null' => true),
-            ),
-            'primary key' => array('id'),
-            'unique keys' => array(
-                'rsvp_uri_key' => array('uri'),
-                'rsvp_profile_event_key' => array('profile_id', 'event_id'),
-            ),
-            'foreign keys' => array('rsvp_event_id_key' => array('event', array('event_id' => 'id')),
-                                    'rsvp_profile_id__key' => array('profile', array('profile_id' => 'id'))),
-            'indexes' => array('rsvp_created_idx' => array('created')),
-        );
-    }
-
-    function saveNew($profile, $event, $verb, $options=array())
-    {
-        if (array_key_exists('uri', $options)) {
-            $other = RSVP::getKV('uri', $options['uri']);
-            if (!empty($other)) {
-                // TRANS: Client exception thrown when trying to save an already existing RSVP ("please respond").
-                throw new ClientException(_m('RSVP already exists.'));
-            }
-        }
-
-        $other = RSVP::pkeyGet(array('profile_id' => $profile->id,
-                                     'event_id' => $event->id));
-
-        if (!empty($other)) {
-            // TRANS: Client exception thrown when trying to save an already existing RSVP ("please respond").
-            throw new ClientException(_m('RSVP already exists.'));
-        }
-
-        $rsvp = new RSVP();
-
-        $rsvp->id          = UUID::gen();
-        $rsvp->profile_id  = $profile->id;
-        $rsvp->event_id    = $event->id;
-        $rsvp->response      = self::codeFor($verb);
-
-        if (array_key_exists('created', $options)) {
-            $rsvp->created = $options['created'];
-        } else {
-            $rsvp->created = common_sql_now();
-        }
-
-        if (array_key_exists('uri', $options)) {
-            $rsvp->uri = $options['uri'];
-        } else {
-            $rsvp->uri = common_local_url('showrsvp',
-                                        array('id' => $rsvp->id));
-        }
-
-        $rsvp->insert();
-
-        self::blow('rsvp:for-event:%s', $event->id);
-
-        // XXX: come up with something sexier
-
-        $content = $rsvp->asString();
-
-        $rendered = $rsvp->asHTML();
-
-        $options = array_merge(array('object_type' => $verb),
-                               $options);
-
-        if (!array_key_exists('uri', $options)) {
-            $options['uri'] = $rsvp->uri;
-        }
-
-        $eventNotice = $event->getNotice();
-
-        if (!empty($eventNotice)) {
-            $options['reply_to'] = $eventNotice->id;
-        }
-
-        $saved = Notice::saveNew($profile->id,
-                                 $content,
-                                 array_key_exists('source', $options) ?
-                                 $options['source'] : 'web',
-                                 $options);
-
-        return $saved;
-    }
-
-    function codeFor($verb)
-    {
-        switch ($verb) {
-        case RSVP::POSITIVE:
-            return 'Y';
-            break;
-        case RSVP::NEGATIVE:
-            return 'N';
-            break;
-        case RSVP::POSSIBLE:
-            return '?';
-            break;
-        default:
-            // TRANS: Exception thrown when requesting an undefined verb for RSVP.
-            throw new Exception(sprintf(_m('Unknown verb "%s".'),$verb));
-        }
-    }
-
-    static function verbFor($code)
-    {
-        switch ($code) {
-        case 'Y':
-            return RSVP::POSITIVE;
-            break;
-        case 'N':
-            return RSVP::NEGATIVE;
-            break;
-        case '?':
-            return RSVP::POSSIBLE;
-            break;
-        default:
-            // TRANS: Exception thrown when requesting an undefined code for RSVP.
-            throw new Exception(sprintf(_m('Unknown code "%s".'),$code));
-        }
-    }
-
-    function getNotice()
-    {
-        $notice = Notice::getKV('uri', $this->uri);
-        if (empty($notice)) {
-            // TRANS: Server exception thrown when requesting a non-exsting notice for an RSVP ("please respond").
-            // TRANS: %s is the RSVP with the missing notice.
-            throw new ServerException(sprintf(_m('RSVP %s does not correspond to a notice in the database.'),$this->id));
-        }
-        return $notice;
-    }
-
-    static function fromNotice($notice)
-    {
-        return RSVP::getKV('uri', $notice->uri);
-    }
-
-    static function forEvent($event)
-    {
-        $keypart = sprintf('rsvp:for-event:%s', $event->id);
-
-        $idstr = self::cacheGet($keypart);
-
-        if ($idstr !== false) {
-            $ids = explode(',', $idstr);
-        } else {
-            $ids = array();
-
-            $rsvp = new RSVP();
-
-            $rsvp->selectAdd();
-            $rsvp->selectAdd('id');
-
-            $rsvp->event_id = $event->id;
-
-            if ($rsvp->find()) {
-                while ($rsvp->fetch()) {
-                    $ids[] = $rsvp->id;
-                }
-            }
-            self::cacheSet($keypart, implode(',', $ids));
-        }
-
-        $rsvps = array(RSVP::POSITIVE => array(),
-                       RSVP::NEGATIVE => array(),
-                       RSVP::POSSIBLE => array());
-
-        foreach ($ids as $id) {
-            $rsvp = RSVP::getKV('id', $id);
-            if (!empty($rsvp)) {
-                $verb = self::verbFor($rsvp->response);
-                $rsvps[$verb][] = $rsvp;
-            }
-        }
-
-        return $rsvps;
-    }
-
-    function getProfile()
-    {
-        $profile = Profile::getKV('id', $this->profile_id);
-        if (empty($profile)) {
-            // TRANS: Exception thrown when requesting a non-existing profile.
-            // TRANS: %s is the ID of the non-existing profile.
-            throw new Exception(sprintf(_m('No profile with ID %s.'),$this->profile_id));
-        }
-        return $profile;
-    }
-
-    function getEvent()
-    {
-        $event = Happening::getKV('id', $this->event_id);
-        if (empty($event)) {
-            // TRANS: Exception thrown when requesting a non-existing event.
-            // TRANS: %s is the ID of the non-existing event.
-            throw new Exception(sprintf(_m('No event with ID %s.'),$this->event_id));
-        }
-        return $event;
-    }
-
-    function asHTML()
-    {
-        $event = Happening::getKV('id', $this->event_id);
-
-        return self::toHTML($this->getProfile(),
-                            $event,
-                            $this->response);
-    }
-
-    function asString()
-    {
-        $event = Happening::getKV('id', $this->event_id);
-
-        return self::toString($this->getProfile(),
-                              $event,
-                              $this->response);
-    }
-
-    static function toHTML($profile, $event, $response)
-    {
-        $fmt = null;
-
-        switch ($response) {
-        case 'Y':
-            // TRANS: HTML version of an RSVP ("please respond") status for a user.
-            // TRANS: %1$s is a profile URL, %2$s a profile name,
-            // TRANS: %3$s is an event URL, %4$s an event title.
-            $fmt = _m("<span class='automatic event-rsvp'><a href='%1\$s'>%2\$s</a> is attending <a href='%3\$s'>%4\$s</a>.</span>");
-            break;
-        case 'N':
-            // TRANS: HTML version of an RSVP ("please respond") status for a user.
-            // TRANS: %1$s is a profile URL, %2$s a profile name,
-            // TRANS: %3$s is an event URL, %4$s an event title.
-            $fmt = _m("<span class='automatic event-rsvp'><a href='%1\$s'>%2\$s</a> is not attending <a href='%3\$s'>%4\$s</a>.</span>");
-            break;
-        case '?':
-            // TRANS: HTML version of an RSVP ("please respond") status for a user.
-            // TRANS: %1$s is a profile URL, %2$s a profile name,
-            // TRANS: %3$s is an event URL, %4$s an event title.
-            $fmt = _m("<span class='automatic event-rsvp'><a href='%1\$s'>%2\$s</a> might attend <a href='%3\$s'>%4\$s</a>.</span>");
-            break;
-        default:
-            // TRANS: Exception thrown when requesting a user's RSVP status for a non-existing response code.
-            // TRANS: %s is the non-existing response code.
-            throw new Exception(sprintf(_m('Unknown response code %s.'),$response));
-            break;
-        }
-
-        if (empty($event)) {
-            $eventUrl = '#';
-            // TRANS: Used as event title when not event title is available.
-            // TRANS: Used as: Username [is [not ] attending|might attend] an unknown event.
-            $eventTitle = _m('an unknown event');
-        } else {
-            $notice = $event->getNotice();
-            $eventUrl = $notice->bestUrl();
-            $eventTitle = $event->title;
-        }
-
-        return sprintf($fmt,
-                       htmlspecialchars($profile->profileurl),
-                       htmlspecialchars($profile->getBestName()),
-                       htmlspecialchars($eventUrl),
-                       htmlspecialchars($eventTitle));
-    }
-
-    static function toString($profile, $event, $response)
-    {
-        $fmt = null;
-
-        switch ($response) {
-        case 'Y':
-            // TRANS: Plain text version of an RSVP ("please respond") status for a user.
-            // TRANS: %1$s is a profile name, %2$s is an event title.
-            $fmt = _m('%1$s is attending %2$s.');
-            break;
-        case 'N':
-            // TRANS: Plain text version of an RSVP ("please respond") status for a user.
-            // TRANS: %1$s is a profile name, %2$s is an event title.
-            $fmt = _m('%1$s is not attending %2$s.');
-            break;
-        case '?':
-            // TRANS: Plain text version of an RSVP ("please respond") status for a user.
-            // TRANS: %1$s is a profile name, %2$s is an event title.
-            $fmt = _m('%1$s might attend %2$s.');
-            break;
-        default:
-            // TRANS: Exception thrown when requesting a user's RSVP status for a non-existing response code.
-            // TRANS: %s is the non-existing response code.
-            throw new Exception(sprintf(_m('Unknown response code %s.'),$response));
-            break;
-        }
-
-        if (empty($event)) {
-            // TRANS: Used as event title when not event title is available.
-            // TRANS: Used as: Username [is [not ] attending|might attend] an unknown event.
-            $eventTitle = _m('an unknown event');
-        } else {
-            $notice = $event->getNotice();
-            $eventTitle = $event->title;
-        }
-
-        return sprintf($fmt,
-                       $profile->getBestName(),
-                       $eventTitle);
-    }
-
-    function delete()
-    {
-        self::blow('rsvp:for-event:%s', $event->id);
-        parent::delete();
-    }
-}
diff --git a/plugins/Event/actions/cancelrsvp.php b/plugins/Event/actions/cancelrsvp.php
new file mode 100644 (file)
index 0000000..ab6cc50
--- /dev/null
@@ -0,0 +1,206 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Cancel the RSVP for an event
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * RSVP for an event
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class CancelrsvpAction extends Action
+{
+    protected $user  = null;
+    protected $rsvp  = null;
+    protected $event = null;
+
+    /**
+     * Returns the title of the action
+     *
+     * @return string Action title
+     */
+    function title()
+    {
+        // TRANS: Title for RSVP ("please respond") action.
+        return _m('TITLE','Cancel RSVP');
+    }
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+        if ($this->boolean('ajax')) {
+            StatusNet::setApi(true); // short error results!
+        }
+
+        $rsvpId = $this->trimmed('rsvp');
+
+        if (empty($rsvpId)) {
+            // TRANS: Client exception thrown when referring to a non-existing RSVP ("please respond") item.
+            throw new ClientException(_m('No such RSVP.'));
+        }
+
+        $this->rsvp = RSVP::getKV('id', $rsvpId);
+
+        if (empty($this->rsvp)) {
+            // TRANS: Client exception thrown when referring to a non-existing RSVP ("please respond") item.
+            throw new ClientException(_m('No such RSVP.'));
+        }
+
+        $this->event = Happening::getKV('id', $this->rsvp->event_id);
+
+        if (empty($this->event)) {
+            // TRANS: Client exception thrown when referring to a non-existing event.
+            throw new ClientException(_m('No such event.'));
+        }
+
+        $this->user = common_current_user();
+
+        if (empty($this->user)) {
+            // TRANS: Client exception thrown when trying tp RSVP ("please respond") while not logged in.
+            throw new ClientException(_m('You must be logged in to RSVP for an event.'));
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        parent::handle($argarray);
+
+        if ($this->isPost()) {
+            $this->cancelRSVP();
+        } else {
+            $this->showPage();
+        }
+
+        return;
+    }
+
+    /**
+     * Add a new event
+     *
+     * @return void
+     */
+    function cancelRSVP()
+    {
+        try {
+            $notice = $this->rsvp->getNotice();
+            // NB: this will delete the rsvp, too
+            if (!empty($notice)) {
+                common_log(LOG_DEBUG, "Deleting notice...");
+                $notice->delete();
+            } else {
+                common_log(LOG_DEBUG, "Deleting RSVP alone...");
+                $this->rsvp->delete();
+            }
+        } catch (ClientException $ce) {
+            $this->error = $ce->getMessage();
+            $this->showPage();
+            return;
+        }
+
+        if ($this->boolean('ajax')) {
+            header('Content-Type: text/xml;charset=utf-8');
+            $this->xw->startDocument('1.0', 'UTF-8');
+            $this->elementStart('html');
+            $this->elementStart('head');
+            // TRANS: Page title after sending a notice.
+            $this->element('title', null, _m('Event saved'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $this->elementStart('body');
+            $form = new RSVPForm($this->event, $this);
+            $form->show();
+            $this->elementEnd('body');
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        }
+    }
+
+    /**
+     * Show the event form
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        if (!empty($this->error)) {
+            $this->element('p', 'error', $this->error);
+        }
+
+        $form = new CancelRSVPForm($this->rsvp, $this);
+
+        $form->show();
+
+        return;
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+            $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/plugins/Event/actions/newevent.php b/plugins/Event/actions/newevent.php
new file mode 100644 (file)
index 0000000..4c2157e
--- /dev/null
@@ -0,0 +1,332 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Add a new event
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Add a new event
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class NeweventAction extends Action
+{
+    protected $user        = null;
+    protected $error       = null;
+    protected $complete    = null;
+    protected $title       = null;
+    protected $location    = null;
+    protected $description = null;
+    protected $startTime   = null;
+    protected $endTime     = null;
+
+    /**
+     * Returns the title of the action
+     *
+     * @return string Action title
+     */
+    function title()
+    {
+        // TRANS: Title for new event form.
+        return _m('TITLE','New event');
+    }
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        $this->user = common_current_user();
+
+        if (empty($this->user)) {
+            // TRANS: Client exception thrown when trying to post an event while not logged in.
+            throw new ClientException(_m('Must be logged in to post a event.'),
+                                      403);
+        }
+
+        if ($this->isPost()) {
+            $this->checkSessionToken();
+        }
+
+        try {
+
+            $this->title = $this->trimmed('title');
+
+            if (empty($this->title)) {
+                // TRANS: Client exception thrown when trying to post an event without providing a title.
+                throw new ClientException(_m('Title required.'));
+            }
+
+            $this->location    = $this->trimmed('location');
+            $this->url         = $this->trimmed('url');
+            $this->description = $this->trimmed('description');
+            $tz                = $this->trimmed('tz');
+
+            $startDate = $this->trimmed('startdate');
+
+            if (empty($startDate)) {
+                // TRANS: Client exception thrown when trying to post an event without providing a start date.
+                throw new ClientException(_m('Start date required.'));
+            }
+
+            $startTime = $this->trimmed('event-starttime');
+
+            if (empty($startTime)) {
+                $startTime = '00:00';
+            }
+
+            $endDate   = $this->trimmed('enddate');
+
+            if (empty($endDate)) {
+                // TRANS: Client exception thrown when trying to post an event without providing an end date.
+                throw new ClientException(_m('End date required.'));
+            }
+
+            $endTime   = $this->trimmed('event-endtime');
+
+            if (empty($endTime)) {
+                $endTime = '00:00';
+            }
+
+            $start = $startDate . ' ' . $startTime . ' ' . $tz;
+            $end   = $endDate . ' ' . $endTime . ' ' . $tz;
+
+            $this->startTime = strtotime($start);
+            $this->endTime   = strtotime($end);
+
+            if ($this->startTime == 0) {
+                // TRANS: Client exception thrown when trying to post an event with a date that cannot be processed.
+                // TRANS: %s is the data that could not be processed.
+                throw new ClientException(sprintf(_m('Could not parse date "%s".'),
+                                            $start));
+            }
+
+            if ($this->endTime == 0) {
+                // TRANS: Client exception thrown when trying to post an event with a date that cannot be processed.
+                // TRANS: %s is the data that could not be processed.
+                throw new ClientException(sprintf(_m('Could not parse date "%s".'),
+                                            $end));
+            }
+        } catch (ClientException $ce) {
+            if ($this->boolean('ajax')) {
+                $this->outputAjaxError($ce->getMessage());
+                return false;
+            } else {
+                $this->error = $ce->getMessage();
+                $this->showPage();
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        parent::handle($argarray);
+
+        if ($this->isPost()) {
+            $this->newEvent();
+        } else {
+            $this->showPage();
+        }
+
+        return;
+    }
+
+    /**
+     * Add a new event
+     *
+     * @return void
+     */
+    function newEvent()
+    {
+        try {
+
+            if (empty($this->title)) {
+                // TRANS: Client exception thrown when trying to post an event without providing a title.
+                throw new ClientException(_m('Event must have a title.'));
+            }
+
+            if (empty($this->startTime)) {
+                // TRANS: Client exception thrown when trying to post an event without providing a start time.
+                throw new ClientException(_m('Event must have a start time.'));
+            }
+
+            if (empty($this->endTime)) {
+                // TRANS: Client exception thrown when trying to post an event without providing an end time.
+                throw new ClientException(_m('Event must have an end time.'));
+            }
+
+            if (!empty($this->url) && Validate::uri($this->url) === false) {
+                // TRANS: Client exception thrown when trying to post an event with an invalid URL.
+                throw new ClientException(_m('URL must be valid.'));
+            }
+
+            $options = array();
+
+            // Does the heavy-lifting for getting "To:" information
+
+            ToSelector::fillOptions($this, $options);
+
+            $profile = $this->user->getProfile();
+
+            $saved = Happening::saveNew($profile,
+                                        $this->startTime,
+                                        $this->endTime,
+                                        $this->title,
+                                        $this->location,
+                                        $this->description,
+                                        $this->url,
+                                        $options);
+
+            $event = Happening::fromNotice($saved);
+
+            RSVP::saveNew($profile, $event, RSVP::POSITIVE);
+
+        } catch (ClientException $ce) {
+            if ($this->boolean('ajax')) {
+                $this->outputAjaxError($ce->getMessage());
+                return;
+            } else {
+                $this->error = $ce->getMessage();
+                $this->showPage();
+                return;
+            }
+        }
+
+        if ($this->boolean('ajax')) {
+            header('Content-Type: text/xml;charset=utf-8');
+            $this->xw->startDocument('1.0', 'UTF-8');
+            $this->elementStart('html');
+            $this->elementStart('head');
+            // TRANS: Page title after sending a notice.
+            $this->element('title', null, _m('Event saved'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $this->showNotice($saved);
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            common_redirect($saved->bestUrl(), 303);
+        }
+    }
+
+    // @todo factor this out into a base class
+    function outputAjaxError($msg)
+    {
+        header('Content-Type: text/xml;charset=utf-8');
+        $this->xw->startDocument('1.0', 'UTF-8');
+        $this->elementStart('html');
+        $this->elementStart('head');
+        // TRANS: Page title after an AJAX error occurs
+        $this->element('title', null, _('Ajax Error'));
+        $this->elementEnd('head');
+        $this->elementStart('body');
+        $this->element('p', array('id' => 'error'), $msg);
+        $this->elementEnd('body');
+        $this->elementEnd('html');
+        return;
+    }
+
+    /**
+     * Show the event form
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        if (!empty($this->error)) {
+            $this->element('p', 'error', $this->error);
+        }
+
+        $form = new EventForm($this);
+
+        $form->show();
+
+        return;
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+            $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+
+    /**
+     * Output a notice
+     *
+     * Used to generate the notice code for Ajax results.
+     *
+     * @param Notice $notice Notice that was saved
+     *
+     * @return void
+     */
+    function showNotice($notice)
+    {
+        $nli = new NoticeListItem($notice, $this);
+        $nli->show();
+    }
+}
diff --git a/plugins/Event/actions/newrsvp.php b/plugins/Event/actions/newrsvp.php
new file mode 100644 (file)
index 0000000..272c6f0
--- /dev/null
@@ -0,0 +1,213 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * RSVP for an event
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * RSVP for an event
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class NewrsvpAction extends Action
+{
+    protected $user  = null;
+    protected $event = null;
+    protected $verb  = null;
+
+    /**
+     * Returns the title of the action
+     *
+     * @return string Action title
+     */
+    function title()
+    {
+        // TRANS: Title for RSVP ("please respond") action.
+        return _m('TITLE','New RSVP');
+    }
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+        if ($this->boolean('ajax')) {
+            StatusNet::setApi(true); // short error results!
+        }
+
+        $eventId = $this->trimmed('event');
+
+        if (empty($eventId)) {
+            // TRANS: Client exception thrown when referring to a non-existing event.
+            throw new ClientException(_m('No such event.'));
+        }
+
+        $this->event = Happening::getKV('id', $eventId);
+
+        if (empty($this->event)) {
+            // TRANS: Client exception thrown when referring to a non-existing event.
+            throw new ClientException(_m('No such event.'));
+        }
+
+        $this->user = common_current_user();
+
+        if (empty($this->user)) {
+            // TRANS: Client exception thrown when trying to RSVP ("please respond") while not logged in.
+            throw new ClientException(_m('You must be logged in to RSVP for an event.'));
+        }
+
+        common_debug(print_r($this->args, true));
+
+        switch (strtolower($this->trimmed('submitvalue'))) {
+        case 'yes':
+            $this->verb = RSVP::POSITIVE;
+            break;
+        case 'no':
+            $this->verb = RSVP::NEGATIVE;
+            break;
+        case 'maybe':
+            $this->verb = RSVP::POSSIBLE;
+            break;
+        default:
+            // TRANS: Client exception thrown when using an invalid value for RSVP ("please respond").
+            throw new ClientException(_m('Unknown submit value.'));
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        parent::handle($argarray);
+
+        if ($this->isPost()) {
+            $this->newRSVP();
+        } else {
+            $this->showPage();
+        }
+
+        return;
+    }
+
+    /**
+     * Add a new event
+     *
+     * @return void
+     */
+    function newRSVP()
+    {
+        try {
+            $saved = RSVP::saveNew($this->user->getProfile(),
+                                   $this->event,
+                                   $this->verb);
+        } catch (ClientException $ce) {
+            $this->error = $ce->getMessage();
+            $this->showPage();
+            return;
+        }
+
+        if ($this->boolean('ajax')) {
+            $rsvp = RSVP::fromNotice($saved);
+            header('Content-Type: text/xml;charset=utf-8');
+            $this->xw->startDocument('1.0', 'UTF-8');
+            $this->elementStart('html');
+            $this->elementStart('head');
+            // TRANS: Page title after creating an event.
+            $this->element('title', null, _m('Event saved'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $this->elementStart('body');
+            $cancel = new CancelRSVPForm($rsvp, $this);
+            $cancel->show();
+            $this->elementEnd('body');
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            common_redirect($saved->bestUrl(), 303);
+        }
+    }
+
+    /**
+     * Show the event form
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        if (!empty($this->error)) {
+            $this->element('p', 'error', $this->error);
+        }
+
+        $form = new RSVPForm($this->event, $this);
+
+        $form->show();
+
+        return;
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+            $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/plugins/Event/actions/showevent.php b/plugins/Event/actions/showevent.php
new file mode 100644 (file)
index 0000000..f1d8c6a
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Show a single event
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Show a single event, with associated information
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class ShoweventAction extends ShownoticeAction
+{
+    protected $id    = null;
+    protected $event = null;
+
+    function getNotice()
+    {
+        $this->id = $this->trimmed('id');
+
+        $this->event = Happening::getKV('id', $this->id);
+
+        if (empty($this->event)) {
+            // TRANS: Client exception thrown when referring to a non-existing event.
+            throw new ClientException(_m('No such event.'), 404);
+        }
+
+        $notice = $this->event->getNotice();
+
+        if (empty($notice)) {
+            // Did we used to have it, and it got deleted?
+            // TRANS: Client exception thrown when referring to a non-existing event.
+            throw new ClientException(_m('No such event.'), 404);
+        }
+
+        return $notice;
+    }
+
+    /**
+     * Title of the page
+     *
+     * Used by Action class for layout.
+     *
+     * @return string page tile
+     */
+    function title()
+    {
+        return $this->event->title;
+    }
+}
diff --git a/plugins/Event/actions/showrsvp.php b/plugins/Event/actions/showrsvp.php
new file mode 100644 (file)
index 0000000..697bda9
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Show a single RSVP
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  RSVP
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Show a single RSVP, with associated information
+ *
+ * @category  RSVP
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class ShowrsvpAction extends ShownoticeAction
+{
+    protected $rsvp = null;
+    protected $event = null;
+
+    function getNotice()
+    {
+        $this->id = $this->trimmed('id');
+
+        $this->rsvp = RSVP::getKV('id', $this->id);
+
+        if (empty($this->rsvp)) {
+            // TRANS: Client exception thrown when referring to a non-existing RSVP.
+            // TRANS: RSVP stands for "Please reply".
+            throw new ClientException(_m('No such RSVP.'), 404);
+        }
+
+        $this->event = $this->rsvp->getEvent();
+
+        if (empty($this->event)) {
+            // TRANS: Client exception thrown when referring to a non-existing event.
+            throw new ClientException(_m('No such event.'), 404);
+        }
+
+        $notice = $this->rsvp->getNotice();
+
+        if (empty($notice)) {
+            // Did we used to have it, and it got deleted?
+            // TRANS: Client exception thrown when referring to a non-existing RSVP.
+            // TRANS: RSVP stands for "Please reply".
+            throw new ClientException(_m('No such RSVP.'), 404);
+        }
+
+        return $notice;
+    }
+
+    /**
+     * Title of the page
+     *
+     * Used by Action class for layout.
+     *
+     * @return string page tile
+     */
+    function title()
+    {
+        // TRANS: Title for event.
+       // TRANS: %1$s is a user nickname, %2$s is an event title.
+        return sprintf(_m('%1$s\'s RSVP for "%2$s"'),
+                       $this->user->nickname,
+                       $this->event->title);
+    }
+}
diff --git a/plugins/Event/actions/timelist.php b/plugins/Event/actions/timelist.php
new file mode 100644 (file)
index 0000000..cb8efb3
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Callback handler to populate end time dropdown
+ */
+class TimelistAction extends Action {
+    private $start;
+    private $duration;
+
+    /**
+     * Get ready
+     *
+     * @param array $args misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($args) {
+        parent::prepare($args);
+        $this->start = $this->arg('start');
+        $this->duration = $this->boolean('duration', false);
+        return true;
+    }
+
+    /**
+     * Handle input and ouput something
+     *
+     * @param array $args $_REQUEST arguments
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+
+        if (!common_logged_in()) {
+            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
+            $this->clientError(_m('Not logged in.'));
+            return;
+        }
+
+        if (!empty($this->start)) {
+            $times = EventTimeList::getTimes($this->start, $this->duration);
+        } else {
+            // TRANS: Client error when submitting a form with unexpected information.
+            $this->clientError(_m('Unexpected form submission.'));
+            return;
+        }
+
+        if ($this->boolean('ajax')) {
+            header('Content-Type: application/json; charset=utf-8');
+            print json_encode($times);
+        } else {
+            // TRANS: Client error displayed when using an action in a non-AJAX way.
+            $this->clientError(_m('This action is AJAX only.'));
+        }
+    }
+
+    /**
+     * Override the regular error handler to show something more
+     * ajaxy
+     *
+     * @param string $msg   error message
+     * @param int    $code  error code
+     */
+    function clientError($msg, $code = 400) {
+        if ($this->boolean('ajax')) {
+            header('Content-Type: application/json; charset=utf-8');
+            print json_encode(
+                array(
+                    'success' => false,
+                    'code'    => $code,
+                    'message' => $msg
+                )
+            );
+        } else {
+            parent::clientError($msg, $code);
+        }
+    }
+}
diff --git a/plugins/Event/cancelrsvp.php b/plugins/Event/cancelrsvp.php
deleted file mode 100644 (file)
index ab6cc50..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Cancel the RSVP for an event
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * RSVP for an event
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class CancelrsvpAction extends Action
-{
-    protected $user  = null;
-    protected $rsvp  = null;
-    protected $event = null;
-
-    /**
-     * Returns the title of the action
-     *
-     * @return string Action title
-     */
-    function title()
-    {
-        // TRANS: Title for RSVP ("please respond") action.
-        return _m('TITLE','Cancel RSVP');
-    }
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-        if ($this->boolean('ajax')) {
-            StatusNet::setApi(true); // short error results!
-        }
-
-        $rsvpId = $this->trimmed('rsvp');
-
-        if (empty($rsvpId)) {
-            // TRANS: Client exception thrown when referring to a non-existing RSVP ("please respond") item.
-            throw new ClientException(_m('No such RSVP.'));
-        }
-
-        $this->rsvp = RSVP::getKV('id', $rsvpId);
-
-        if (empty($this->rsvp)) {
-            // TRANS: Client exception thrown when referring to a non-existing RSVP ("please respond") item.
-            throw new ClientException(_m('No such RSVP.'));
-        }
-
-        $this->event = Happening::getKV('id', $this->rsvp->event_id);
-
-        if (empty($this->event)) {
-            // TRANS: Client exception thrown when referring to a non-existing event.
-            throw new ClientException(_m('No such event.'));
-        }
-
-        $this->user = common_current_user();
-
-        if (empty($this->user)) {
-            // TRANS: Client exception thrown when trying tp RSVP ("please respond") while not logged in.
-            throw new ClientException(_m('You must be logged in to RSVP for an event.'));
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        parent::handle($argarray);
-
-        if ($this->isPost()) {
-            $this->cancelRSVP();
-        } else {
-            $this->showPage();
-        }
-
-        return;
-    }
-
-    /**
-     * Add a new event
-     *
-     * @return void
-     */
-    function cancelRSVP()
-    {
-        try {
-            $notice = $this->rsvp->getNotice();
-            // NB: this will delete the rsvp, too
-            if (!empty($notice)) {
-                common_log(LOG_DEBUG, "Deleting notice...");
-                $notice->delete();
-            } else {
-                common_log(LOG_DEBUG, "Deleting RSVP alone...");
-                $this->rsvp->delete();
-            }
-        } catch (ClientException $ce) {
-            $this->error = $ce->getMessage();
-            $this->showPage();
-            return;
-        }
-
-        if ($this->boolean('ajax')) {
-            header('Content-Type: text/xml;charset=utf-8');
-            $this->xw->startDocument('1.0', 'UTF-8');
-            $this->elementStart('html');
-            $this->elementStart('head');
-            // TRANS: Page title after sending a notice.
-            $this->element('title', null, _m('Event saved'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $this->elementStart('body');
-            $form = new RSVPForm($this->event, $this);
-            $form->show();
-            $this->elementEnd('body');
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        }
-    }
-
-    /**
-     * Show the event form
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        if (!empty($this->error)) {
-            $this->element('p', 'error', $this->error);
-        }
-
-        $form = new CancelRSVPForm($this->rsvp, $this);
-
-        $form->show();
-
-        return;
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
-            $_SERVER['REQUEST_METHOD'] == 'HEAD') {
-            return true;
-        } else {
-            return false;
-        }
-    }
-}
diff --git a/plugins/Event/cancelrsvpform.php b/plugins/Event/cancelrsvpform.php
deleted file mode 100644 (file)
index 3047c57..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Form to RSVP for an event
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * A form to RSVP for an event
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class CancelRSVPForm extends Form
-{
-    protected $rsvp = null;
-
-    function __construct($rsvp, $out=null)
-    {
-        parent::__construct($out);
-        $this->rsvp = $rsvp;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'form_event_rsvp';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'ajax';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('cancelrsvp');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('fieldset', array('id' => 'new_rsvp_data'));
-
-        $this->out->hidden('rsvp-id', $this->rsvp->id, 'rsvp');
-
-        switch (RSVP::verbFor($this->rsvp->response)) {
-        case RSVP::POSITIVE:
-            // TRANS: Possible status for RSVP ("please respond") item.
-            $this->out->text(_m('You will attend this event.'));
-            break;
-        case RSVP::NEGATIVE:
-            // TRANS: Possible status for RSVP ("please respond") item.
-            $this->out->text(_m('You will not attend this event.'));
-            break;
-        case RSVP::POSSIBLE:
-            // TRANS: Possible status for RSVP ("please respond") item.
-            $this->out->text(_m('You might attend this event.'));
-            break;
-        }
-
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text to cancel responding to an RSVP ("please respond") item.
-        $this->out->submit('rsvp-cancel', _m('BUTTON', 'Cancel'));
-    }
-}
diff --git a/plugins/Event/classes/Happening.php b/plugins/Event/classes/Happening.php
new file mode 100644 (file)
index 0000000..2b097e9
--- /dev/null
@@ -0,0 +1,210 @@
+<?php
+/**
+ * Data class for happenings
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Data class for happenings
+ *
+ * There's already an Event class in lib/event.php, so we couldn't
+ * call this an Event without causing a hole in space-time.
+ *
+ * "Happening" seemed good enough.
+ *
+ * @category Event
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      Managed_DataObject
+ */
+class Happening extends Managed_DataObject
+{
+    const OBJECT_TYPE = 'http://activitystrea.ms/schema/1.0/event';
+
+    public $__table = 'happening'; // table name
+    public $id;                    // varchar(36) UUID
+    public $uri;                   // varchar(255)
+    public $profile_id;            // int
+    public $start_time;            // datetime
+    public $end_time;              // datetime
+    public $title;                 // varchar(255)
+    public $location;              // varchar(255)
+    public $url;                   // varchar(255)
+    public $description;           // text
+    public $created;               // datetime
+
+    /**
+     * The One True Thingy that must be defined and declared.
+     */
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'A real-world happening',
+            'fields' => array(
+                'id' => array('type' => 'char',
+                              'length' => 36,
+                              'not null' => true,
+                              'description' => 'UUID'),
+                'uri' => array('type' => 'varchar',
+                               'length' => 255,
+                               'not null' => true),
+                'profile_id' => array('type' => 'int', 'not null' => true),
+                'start_time' => array('type' => 'datetime', 'not null' => true),
+                'end_time' => array('type' => 'datetime', 'not null' => true),
+                'title' => array('type' => 'varchar',
+                                 'length' => 255,
+                                 'not null' => true),
+                'location' => array('type' => 'varchar',
+                                    'length' => 255),
+                'url' => array('type' => 'varchar',
+                               'length' => 255),
+                'description' => array('type' => 'text'),
+                'created' => array('type' => 'datetime',
+                                   'not null' => true),
+            ),
+            'primary key' => array('id'),
+            'unique keys' => array(
+                'happening_uri_key' => array('uri'),
+            ),
+            'foreign keys' => array('happening_profile_id__key' => array('profile', array('profile_id' => 'id'))),
+            'indexes' => array('happening_created_idx' => array('created'),
+                               'happening_start_end_idx' => array('start_time', 'end_time')),
+        );
+    }
+
+    static function saveNew($profile, $start_time, $end_time, $title, $location, $description, $url, $options=array())
+    {
+        if (array_key_exists('uri', $options)) {
+            $other = Happening::getKV('uri', $options['uri']);
+            if (!empty($other)) {
+                // TRANS: Client exception thrown when trying to create an event that already exists.
+                throw new ClientException(_m('Event already exists.'));
+            }
+        }
+
+        $ev = new Happening();
+
+        $ev->id          = UUID::gen();
+        $ev->profile_id  = $profile->id;
+        $ev->start_time  = common_sql_date($start_time);
+        $ev->end_time    = common_sql_date($end_time);
+        $ev->title       = $title;
+        $ev->location    = $location;
+        $ev->description = $description;
+        $ev->url         = $url;
+
+        if (array_key_exists('created', $options)) {
+            $ev->created = $options['created'];
+        } else {
+            $ev->created = common_sql_now();
+        }
+
+        if (array_key_exists('uri', $options)) {
+            $ev->uri = $options['uri'];
+        } else {
+            $ev->uri = common_local_url('showevent',
+                                        array('id' => $ev->id));
+        }
+
+        $ev->insert();
+
+        // XXX: does this get truncated?
+
+        // TRANS: Event description. %1$s is a title, %2$s is start time, %3$s is end time,
+        // TRANS: %4$s is location, %5$s is a description.
+        $content = sprintf(_m('"%1$s" %2$s - %3$s (%4$s): %5$s'),
+                           $title,
+                           common_exact_date($ev->start_time),
+                           common_exact_date($ev->end_time),
+                           $location,
+                           $description);
+
+        // TRANS: Rendered event description. %1$s is a title, %2$s is start time, %3$s is start time,
+        // TRANS: %4$s is end time, %5$s is end time, %6$s is location, %7$s is description.
+        // TRANS: Class names should not be translated.
+        $rendered = sprintf(_m('<span class="vevent">'.
+                              '<span class="summary">%1$s</span> '.
+                              '<abbr class="dtstart" title="%2$s">%3$s</a> - '.
+                              '<abbr class="dtend" title="%4$s">%5$s</a> '.
+                              '(<span class="location">%6$s</span>): '.
+                              '<span class="description">%7$s</span> '.
+                              '</span>'),
+                            htmlspecialchars($title),
+                            htmlspecialchars(common_date_iso8601($ev->start_time)),
+                            htmlspecialchars(common_exact_date($ev->start_time)),
+                            htmlspecialchars(common_date_iso8601($ev->end_time)),
+                            htmlspecialchars(common_exact_date($ev->end_time)),
+                            htmlspecialchars($location),
+                            htmlspecialchars($description));
+
+        $options = array_merge(array('object_type' => Happening::OBJECT_TYPE),
+                               $options);
+
+        if (!array_key_exists('uri', $options)) {
+            $options['uri'] = $ev->uri;
+        }
+
+        if (!empty($url)) {
+            $options['urls'] = array($url);
+        }
+
+        $saved = Notice::saveNew($profile->id,
+                                 $content,
+                                 array_key_exists('source', $options) ?
+                                 $options['source'] : 'web',
+                                 $options);
+
+        return $saved;
+    }
+
+    function getNotice()
+    {
+        return Notice::getKV('uri', $this->uri);
+    }
+
+    static function fromNotice($notice)
+    {
+        return Happening::getKV('uri', $notice->uri);
+    }
+
+    function getRSVPs()
+    {
+        return RSVP::forEvent($this);
+    }
+
+    function getRSVP($profile)
+    {
+        return RSVP::pkeyGet(array('profile_id' => $profile->id,
+                                   'event_id' => $this->id));
+    }
+}
diff --git a/plugins/Event/classes/RSVP.php b/plugins/Event/classes/RSVP.php
new file mode 100644 (file)
index 0000000..cea916c
--- /dev/null
@@ -0,0 +1,407 @@
+<?php
+/**
+ * Data class for event RSVPs
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Data class for event RSVPs
+ *
+ * @category Event
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      Managed_DataObject
+ */
+class RSVP extends Managed_DataObject
+{
+    const POSITIVE = 'http://activitystrea.ms/schema/1.0/rsvp-yes';
+    const POSSIBLE = 'http://activitystrea.ms/schema/1.0/rsvp-maybe';
+    const NEGATIVE = 'http://activitystrea.ms/schema/1.0/rsvp-no';
+
+    public $__table = 'rsvp'; // table name
+    public $id;                // varchar(36) UUID
+    public $uri;               // varchar(255)
+    public $profile_id;        // int
+    public $event_id;          // varchar(36) UUID
+    public $response;            // tinyint
+    public $created;           // datetime
+
+    /**
+     * Add the compound profile_id/event_id index to our cache keys
+     * since the DB_DataObject stuff doesn't understand compound keys
+     * except for the primary.
+     *
+     * @return array
+     */
+    function _allCacheKeys() {
+        $keys = parent::_allCacheKeys();
+        $keys[] = self::multicacheKey('RSVP', array('profile_id' => $this->profile_id,
+                                                    'event_id' => $this->event_id));
+        return $keys;
+    }
+
+    /**
+     * The One True Thingy that must be defined and declared.
+     */
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'Plan to attend event',
+            'fields' => array(
+                'id' => array('type' => 'char',
+                              'length' => 36,
+                              'not null' => true,
+                              'description' => 'UUID'),
+                'uri' => array('type' => 'varchar',
+                               'length' => 255,
+                               'not null' => true),
+                'profile_id' => array('type' => 'int'),
+                'event_id' => array('type' => 'char',
+                              'length' => 36,
+                              'not null' => true,
+                              'description' => 'UUID'),
+                'response' => array('type' => 'char',
+                                  'length' => '1',
+                                  'description' => 'Y, N, or ? for three-state yes, no, maybe'),
+                'created' => array('type' => 'datetime',
+                                   'not null' => true),
+            ),
+            'primary key' => array('id'),
+            'unique keys' => array(
+                'rsvp_uri_key' => array('uri'),
+                'rsvp_profile_event_key' => array('profile_id', 'event_id'),
+            ),
+            'foreign keys' => array('rsvp_event_id_key' => array('event', array('event_id' => 'id')),
+                                    'rsvp_profile_id__key' => array('profile', array('profile_id' => 'id'))),
+            'indexes' => array('rsvp_created_idx' => array('created')),
+        );
+    }
+
+    function saveNew($profile, $event, $verb, $options=array())
+    {
+        if (array_key_exists('uri', $options)) {
+            $other = RSVP::getKV('uri', $options['uri']);
+            if (!empty($other)) {
+                // TRANS: Client exception thrown when trying to save an already existing RSVP ("please respond").
+                throw new ClientException(_m('RSVP already exists.'));
+            }
+        }
+
+        $other = RSVP::pkeyGet(array('profile_id' => $profile->id,
+                                     'event_id' => $event->id));
+
+        if (!empty($other)) {
+            // TRANS: Client exception thrown when trying to save an already existing RSVP ("please respond").
+            throw new ClientException(_m('RSVP already exists.'));
+        }
+
+        $rsvp = new RSVP();
+
+        $rsvp->id          = UUID::gen();
+        $rsvp->profile_id  = $profile->id;
+        $rsvp->event_id    = $event->id;
+        $rsvp->response      = self::codeFor($verb);
+
+        if (array_key_exists('created', $options)) {
+            $rsvp->created = $options['created'];
+        } else {
+            $rsvp->created = common_sql_now();
+        }
+
+        if (array_key_exists('uri', $options)) {
+            $rsvp->uri = $options['uri'];
+        } else {
+            $rsvp->uri = common_local_url('showrsvp',
+                                        array('id' => $rsvp->id));
+        }
+
+        $rsvp->insert();
+
+        self::blow('rsvp:for-event:%s', $event->id);
+
+        // XXX: come up with something sexier
+
+        $content = $rsvp->asString();
+
+        $rendered = $rsvp->asHTML();
+
+        $options = array_merge(array('object_type' => $verb),
+                               $options);
+
+        if (!array_key_exists('uri', $options)) {
+            $options['uri'] = $rsvp->uri;
+        }
+
+        $eventNotice = $event->getNotice();
+
+        if (!empty($eventNotice)) {
+            $options['reply_to'] = $eventNotice->id;
+        }
+
+        $saved = Notice::saveNew($profile->id,
+                                 $content,
+                                 array_key_exists('source', $options) ?
+                                 $options['source'] : 'web',
+                                 $options);
+
+        return $saved;
+    }
+
+    function codeFor($verb)
+    {
+        switch ($verb) {
+        case RSVP::POSITIVE:
+            return 'Y';
+            break;
+        case RSVP::NEGATIVE:
+            return 'N';
+            break;
+        case RSVP::POSSIBLE:
+            return '?';
+            break;
+        default:
+            // TRANS: Exception thrown when requesting an undefined verb for RSVP.
+            throw new Exception(sprintf(_m('Unknown verb "%s".'),$verb));
+        }
+    }
+
+    static function verbFor($code)
+    {
+        switch ($code) {
+        case 'Y':
+            return RSVP::POSITIVE;
+            break;
+        case 'N':
+            return RSVP::NEGATIVE;
+            break;
+        case '?':
+            return RSVP::POSSIBLE;
+            break;
+        default:
+            // TRANS: Exception thrown when requesting an undefined code for RSVP.
+            throw new Exception(sprintf(_m('Unknown code "%s".'),$code));
+        }
+    }
+
+    function getNotice()
+    {
+        $notice = Notice::getKV('uri', $this->uri);
+        if (empty($notice)) {
+            // TRANS: Server exception thrown when requesting a non-exsting notice for an RSVP ("please respond").
+            // TRANS: %s is the RSVP with the missing notice.
+            throw new ServerException(sprintf(_m('RSVP %s does not correspond to a notice in the database.'),$this->id));
+        }
+        return $notice;
+    }
+
+    static function fromNotice($notice)
+    {
+        return RSVP::getKV('uri', $notice->uri);
+    }
+
+    static function forEvent($event)
+    {
+        $keypart = sprintf('rsvp:for-event:%s', $event->id);
+
+        $idstr = self::cacheGet($keypart);
+
+        if ($idstr !== false) {
+            $ids = explode(',', $idstr);
+        } else {
+            $ids = array();
+
+            $rsvp = new RSVP();
+
+            $rsvp->selectAdd();
+            $rsvp->selectAdd('id');
+
+            $rsvp->event_id = $event->id;
+
+            if ($rsvp->find()) {
+                while ($rsvp->fetch()) {
+                    $ids[] = $rsvp->id;
+                }
+            }
+            self::cacheSet($keypart, implode(',', $ids));
+        }
+
+        $rsvps = array(RSVP::POSITIVE => array(),
+                       RSVP::NEGATIVE => array(),
+                       RSVP::POSSIBLE => array());
+
+        foreach ($ids as $id) {
+            $rsvp = RSVP::getKV('id', $id);
+            if (!empty($rsvp)) {
+                $verb = self::verbFor($rsvp->response);
+                $rsvps[$verb][] = $rsvp;
+            }
+        }
+
+        return $rsvps;
+    }
+
+    function getProfile()
+    {
+        $profile = Profile::getKV('id', $this->profile_id);
+        if (empty($profile)) {
+            // TRANS: Exception thrown when requesting a non-existing profile.
+            // TRANS: %s is the ID of the non-existing profile.
+            throw new Exception(sprintf(_m('No profile with ID %s.'),$this->profile_id));
+        }
+        return $profile;
+    }
+
+    function getEvent()
+    {
+        $event = Happening::getKV('id', $this->event_id);
+        if (empty($event)) {
+            // TRANS: Exception thrown when requesting a non-existing event.
+            // TRANS: %s is the ID of the non-existing event.
+            throw new Exception(sprintf(_m('No event with ID %s.'),$this->event_id));
+        }
+        return $event;
+    }
+
+    function asHTML()
+    {
+        $event = Happening::getKV('id', $this->event_id);
+
+        return self::toHTML($this->getProfile(),
+                            $event,
+                            $this->response);
+    }
+
+    function asString()
+    {
+        $event = Happening::getKV('id', $this->event_id);
+
+        return self::toString($this->getProfile(),
+                              $event,
+                              $this->response);
+    }
+
+    static function toHTML($profile, $event, $response)
+    {
+        $fmt = null;
+
+        switch ($response) {
+        case 'Y':
+            // TRANS: HTML version of an RSVP ("please respond") status for a user.
+            // TRANS: %1$s is a profile URL, %2$s a profile name,
+            // TRANS: %3$s is an event URL, %4$s an event title.
+            $fmt = _m("<span class='automatic event-rsvp'><a href='%1\$s'>%2\$s</a> is attending <a href='%3\$s'>%4\$s</a>.</span>");
+            break;
+        case 'N':
+            // TRANS: HTML version of an RSVP ("please respond") status for a user.
+            // TRANS: %1$s is a profile URL, %2$s a profile name,
+            // TRANS: %3$s is an event URL, %4$s an event title.
+            $fmt = _m("<span class='automatic event-rsvp'><a href='%1\$s'>%2\$s</a> is not attending <a href='%3\$s'>%4\$s</a>.</span>");
+            break;
+        case '?':
+            // TRANS: HTML version of an RSVP ("please respond") status for a user.
+            // TRANS: %1$s is a profile URL, %2$s a profile name,
+            // TRANS: %3$s is an event URL, %4$s an event title.
+            $fmt = _m("<span class='automatic event-rsvp'><a href='%1\$s'>%2\$s</a> might attend <a href='%3\$s'>%4\$s</a>.</span>");
+            break;
+        default:
+            // TRANS: Exception thrown when requesting a user's RSVP status for a non-existing response code.
+            // TRANS: %s is the non-existing response code.
+            throw new Exception(sprintf(_m('Unknown response code %s.'),$response));
+            break;
+        }
+
+        if (empty($event)) {
+            $eventUrl = '#';
+            // TRANS: Used as event title when not event title is available.
+            // TRANS: Used as: Username [is [not ] attending|might attend] an unknown event.
+            $eventTitle = _m('an unknown event');
+        } else {
+            $notice = $event->getNotice();
+            $eventUrl = $notice->bestUrl();
+            $eventTitle = $event->title;
+        }
+
+        return sprintf($fmt,
+                       htmlspecialchars($profile->profileurl),
+                       htmlspecialchars($profile->getBestName()),
+                       htmlspecialchars($eventUrl),
+                       htmlspecialchars($eventTitle));
+    }
+
+    static function toString($profile, $event, $response)
+    {
+        $fmt = null;
+
+        switch ($response) {
+        case 'Y':
+            // TRANS: Plain text version of an RSVP ("please respond") status for a user.
+            // TRANS: %1$s is a profile name, %2$s is an event title.
+            $fmt = _m('%1$s is attending %2$s.');
+            break;
+        case 'N':
+            // TRANS: Plain text version of an RSVP ("please respond") status for a user.
+            // TRANS: %1$s is a profile name, %2$s is an event title.
+            $fmt = _m('%1$s is not attending %2$s.');
+            break;
+        case '?':
+            // TRANS: Plain text version of an RSVP ("please respond") status for a user.
+            // TRANS: %1$s is a profile name, %2$s is an event title.
+            $fmt = _m('%1$s might attend %2$s.');
+            break;
+        default:
+            // TRANS: Exception thrown when requesting a user's RSVP status for a non-existing response code.
+            // TRANS: %s is the non-existing response code.
+            throw new Exception(sprintf(_m('Unknown response code %s.'),$response));
+            break;
+        }
+
+        if (empty($event)) {
+            // TRANS: Used as event title when not event title is available.
+            // TRANS: Used as: Username [is [not ] attending|might attend] an unknown event.
+            $eventTitle = _m('an unknown event');
+        } else {
+            $notice = $event->getNotice();
+            $eventTitle = $event->title;
+        }
+
+        return sprintf($fmt,
+                       $profile->getBestName(),
+                       $eventTitle);
+    }
+
+    function delete()
+    {
+        self::blow('rsvp:for-event:%s', $event->id);
+        parent::delete();
+    }
+}
diff --git a/plugins/Event/eventform.php b/plugins/Event/eventform.php
deleted file mode 100644 (file)
index 8100e59..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Form for entering an event
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form for adding an event
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class EventForm extends Form
-{
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'form_new_event';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings ajax-notice';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('newevent');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('fieldset', array('id' => 'new_event_data'));
-
-        // Passing in the URL of the Ajax action that the .js for this form hits
-        // when selecting event start and end times. JavaScript will try to
-        // use a relative path, unless explicitely told where an action is,
-        // and that's a bit difficult to calculate since the event form is on
-        // so many pages with different paths. It might be worth solving this
-        // globally by putting the base site path in the Identifier-URL meta tag
-        // or something similar, so it would be easy to calculate the exact path
-        // for actions and other things in JavaScripts. -z
-        $this->out->hidden('timelist_action_url', common_local_url('timelist'));
-
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-        $this->out->input('event-title',
-                          // TRANS: Field label on event form.
-                          _m('LABEL','Title'),
-                          null,
-                          // TRANS: Field title on event form.
-                          _m('Title of the event.'),
-                          'title');
-        $this->unli();
-
-        $this->li();
-
-        $today = new DateTime('now');
-        $today->setTimezone(new DateTimeZone(common_timezone()));
-
-        $this->out->input('event-startdate',
-                          // TRANS: Field label on event form.
-                          _m('LABEL','Start date'),
-                          $today->format('m/d/Y'),
-                          // TRANS: Field title on event form.
-                          _m('Date the event starts.'),
-                          'startdate');
-        $this->unli();
-
-        $this->li();
-
-        $times = EventTimeList::getTimes($today->format('m/d/Y 12:00') . ' am ' . $today->format('T'));
-        $start = EventTimeList::nearestHalfHour($today->format('c'));
-        $start->setTimezone(new DateTimeZone(common_timezone()));
-
-        $this->out->dropdown(
-            'event-starttime',
-            // TRANS: Field label on event form.
-            _m('LABEL','Start time'),
-            $times,
-            // TRANS: Field title on event form. %s is the abbreviated timezone
-            sprintf(_m("Time the event starts (%s)."), $today->format('T')),
-            false,
-            $start->format('g:ia')
-        );
-
-        // Need to keep JavaScript TZ in sync with PHP TZ
-        $this->out->hidden('tz', $today->format('T'));
-        $this->out->hidden('now', $today->format('F d, Y H:i:s T'));
-
-        $this->unli();
-
-        $this->li();
-        $this->out->input('event-enddate',
-                          // TRANS: Field label on event form.
-                          _m('LABEL','End date'),
-                          $today->format('m/d/Y'),
-                          // TRANS: Field title on event form.
-                          _m('Date the event ends.'),
-                          'enddate');
-        $this->unli();
-
-        $this->li();
-
-        $this->out->dropdown(
-            'event-endtime',
-            // TRANS: Field label on event form.
-            _m('LABEL','End time'),
-            EventTimeList::getTimes($start->format('c'), true),
-            // TRANS: Field title on event form.
-            _m('Time the event ends.'),
-            false,
-            null
-        );
-        $this->unli();
-
-        $this->li();
-        $this->out->input('event-location',
-                          // TRANS: Field label on event form.
-                          _m('LABEL','Where?'),
-                          null,
-                          // TRANS: Field title on event form.
-                          _m('Event location.'),
-                          'location');
-        $this->unli();
-
-        $this->li();
-        $this->out->input('event-url',
-                          // TRANS: Field label on event form.
-                          _m('LABEL','URL'),
-                          null,
-                          // TRANS: Field title on event form.
-                          _m('URL for more information.'),
-                          'url');
-        $this->unli();
-
-        $this->li();
-        $this->out->input('event-description',
-                          // TRANS: Field label on event form.
-                          _m('LABEL','Description'),
-                          null,
-                          // TRANS: Field title on event form.
-                          _m('Description of the event.'),
-                          'description');
-        $this->unli();
-
-        $this->out->elementEnd('ul');
-
-        $toWidget = new ToSelector($this->out,
-                                   common_current_user(),
-                                   null);
-        $toWidget->show();
-
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text to save an event..
-        $this->out->submit('event-submit', _m('BUTTON', 'Save'), 'submit', 'submit');
-    }
-}
diff --git a/plugins/Event/eventlistitem.php b/plugins/Event/eventlistitem.php
deleted file mode 100644 (file)
index b563e22..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Notice-list representation of an event
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Notice-list representation of an event
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class EventListItem extends NoticeListItemAdapter
-{
-    function showNotice()
-    {
-        $this->nli->out->elementStart('div', 'entry-title');
-        $this->nli->showAuthor();
-        $this->showContent();
-        $this->nli->out->elementEnd('div');
-    }
-
-    function showContent()
-    {
-        $notice = $this->nli->notice;
-        $out    = $this->nli->out;
-
-        $profile = $notice->getProfile();
-        $event   = Happening::fromNotice($notice);
-
-        if (empty($event)) {
-            // TRANS: Content for a deleted RSVP list item (RSVP stands for "please respond").
-            $out->element('p', null, _m('Deleted.'));
-            return;
-        }
-
-        $out->elementStart('div', 'vevent event'); // VEVENT IN
-
-        $out->elementStart('h3');  // VEVENT/H3 IN
-
-        if (!empty($event->url)) {
-            $out->element('a',
-                          array('href' => $event->url,
-                                'class' => 'event-title entry-title summary'),
-                          $event->title);
-        } else {
-            $out->text($event->title);
-        }
-
-        $out->elementEnd('h3'); // VEVENT/H3 OUT
-
-        $now       = new DateTime();
-        $startDate = new DateTime($event->start_time);
-        $endDate   = new DateTime($event->end_time);
-        $userTz    = new DateTimeZone(common_timezone());
-
-        // Localize the time for the observer
-        $now->setTimeZone($userTz);
-        $startDate->setTimezone($userTz);
-        $endDate->setTimezone($userTz);
-
-        $thisYear  = $now->format('Y');
-        $startYear = $startDate->format('Y');
-        $endYear   = $endDate->format('Y');
-
-        $dateFmt = 'D, F j, '; // e.g.: Mon, Aug 31
-
-        if ($startYear != $thisYear || $endYear != $thisYear) {
-            $dateFmt .= 'Y,'; // append year if we need to think about years
-        }
-
-        $startDateStr = $startDate->format($dateFmt);
-        $endDateStr = $endDate->format($dateFmt);
-
-        $timeFmt = 'g:ia';
-
-        $startTimeStr = $startDate->format($timeFmt);
-        $endTimeStr = $endDate->format("{$timeFmt} (T)");
-
-        $out->elementStart('div', 'event-times'); // VEVENT/EVENT-TIMES IN
-
-        // TRANS: Field label for event description.
-        $out->element('strong', null, _m('Time:'));
-
-        $out->element('abbr', array('class' => 'dtstart',
-                                    'title' => common_date_iso8601($event->start_time)),
-                      $startDateStr . ' ' . $startTimeStr);
-        $out->text(' â€“ ');
-        if ($startDateStr == $endDateStr) {
-            $out->element('span', array('class' => 'dtend',
-                                        'title' => common_date_iso8601($event->end_time)),
-                          $endTimeStr);
-        } else {
-            $out->element('span', array('class' => 'dtend',
-                                        'title' => common_date_iso8601($event->end_time)),
-                          $endDateStr . ' ' . $endTimeStr);
-        }
-
-        $out->elementEnd('div'); // VEVENT/EVENT-TIMES OUT
-
-        if (!empty($event->location)) {
-            $out->elementStart('div', 'event-location');
-            // TRANS: Field label for event description.
-            $out->element('strong', null, _m('Location:'));
-            $out->element('span', 'location', $event->location);
-            $out->elementEnd('div');
-        }
-
-        if (!empty($event->description)) {
-            $out->elementStart('div', 'event-description');
-            // TRANS: Field label for event description.
-            $out->element('strong', null, _m('Description:'));
-            $out->element('span', 'description', $event->description);
-            $out->elementEnd('div');
-        }
-
-        $rsvps = $event->getRSVPs();
-
-        $out->elementStart('div', 'event-rsvps');
-        // TRANS: Field label for event description.
-
-        $out->text(_('Attending:'));
-        $out->elementStart('ul', 'attending-list');
-
-        foreach ($rsvps as $verb => $responses) {
-            $out->elementStart('li', 'rsvp-list');
-            switch ($verb)
-            {
-            case RSVP::POSITIVE:
-                $out->text(_('Yes:'));
-                break;
-            case RSVP::NEGATIVE:
-                $out->text(_('No:'));
-                break;
-            case RSVP::POSSIBLE:
-                $out->text(_('Maybe:'));
-                break;
-            }
-            $ids = array();
-            foreach ($responses as $response) {
-                $ids[] = $response->profile_id;
-            }
-            $ids = array_slice($ids, 0, ProfileMiniList::MAX_PROFILES + 1);
-            $profiles = Profile::pivotGet('id', $ids);
-            $profile  = new ArrayWrapper(array_values($profiles));
-            $minilist = new ProfileMiniList($profile, $out);
-            $minilist->show();
-
-            $out->elementEnd('li');
-        }
-
-        $out->elementEnd('ul');
-        $out->elementEnd('div');
-
-        $user = common_current_user();
-
-        if (!empty($user)) {
-            $rsvp = $event->getRSVP($user->getProfile());
-
-            if (empty($rsvp)) {
-                $form = new RSVPForm($event, $out);
-            } else {
-                $form = new CancelRSVPForm($rsvp, $out);
-            }
-
-            $form->show();
-        }
-
-        $out->elementEnd('div'); // vevent out
-    }
-}
diff --git a/plugins/Event/eventtimelist.php b/plugins/Event/eventtimelist.php
deleted file mode 100644 (file)
index 2436a4f..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-<?php
-/**
- * Helper class for calculating and displaying event times
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- *  Class to get fancy times for the dropdowns on the new event form
- */
-class EventTimeList {
-    /**
-     * Round up to the nearest half hour
-     *
-     * @param string $time the time to round (date/time string)
-     * @return DateTime    the rounded time
-     */
-    public static function nearestHalfHour($time)
-    {
-        $startd = new DateTime($time);
-
-        $minutes = $startd->format('i');
-        $hour    = $startd->format('H');
-
-        if ($minutes >= 30) {
-            $minutes = '00';
-            $hour++;
-        } else {
-            $minutes = '30';
-        }
-
-        $startd->setTime($hour, $minutes, 0);
-
-        return $startd;
-    }
-
-    /**
-     * Output a list of times in half-hour intervals
-     *
-     * @param string  $start       Time to start with (date string, usually a ts)
-     * @param boolean $duration    Whether to include the duration of the event
-     *                             (from the start)
-     * @return array  $times (localized 24 hour time string => fancy time string)
-     */
-    public static function getTimes($start = 'now', $duration = false)
-    {
-        $newTime = new DateTime($start);
-        $times   = array();
-        $len     = 0;
-
-        $newTime->setTimezone(new DateTimeZone(common_timezone()));
-
-        for ($i = 0; $i < 47; $i++) {
-
-            $localTime = $newTime->format("g:ia");
-
-            // pretty up the end-time option list a bit
-            if ($duration) {
-                $hours = $len / 60;
-                switch ($hours) {
-                case 0:
-                    // TRANS: 0 minutes abbreviated. Used in a list.
-                    $total = ' ' . _m('(0 min)');
-                    break;
-                case .5:
-                    // TRANS: 30 minutes abbreviated. Used in a list.
-                    $total = ' ' . _m('(30 min)');
-                    break;
-                case 1:
-                    // TRANS: 1 hour. Used in a list.
-                    $total = ' ' . _m('(1 hour)');
-                    break;
-                default:
-                    // TRANS: Number of hours (%.1f and %d). Used in a list.
-                    $format = is_float($hours) 
-                        ? _m('(%.1f hours)')
-                        : _m('(%d hours)');
-                    $total = ' ' . sprintf($format, $hours);
-                    break;
-                }
-                $localTime .= $total;
-                $len += 30;
-            }
-
-            $times[$newTime->format('g:ia')] = $localTime;
-            $newTime->modify('+30min'); // 30 min intervals
-        }
-
-        return $times;
-    }
-}
diff --git a/plugins/Event/forms/cancelrsvp.php b/plugins/Event/forms/cancelrsvp.php
new file mode 100644 (file)
index 0000000..3047c57
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Form to RSVP for an event
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * A form to RSVP for an event
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class CancelRSVPForm extends Form
+{
+    protected $rsvp = null;
+
+    function __construct($rsvp, $out=null)
+    {
+        parent::__construct($out);
+        $this->rsvp = $rsvp;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'form_event_rsvp';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'ajax';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('cancelrsvp');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('fieldset', array('id' => 'new_rsvp_data'));
+
+        $this->out->hidden('rsvp-id', $this->rsvp->id, 'rsvp');
+
+        switch (RSVP::verbFor($this->rsvp->response)) {
+        case RSVP::POSITIVE:
+            // TRANS: Possible status for RSVP ("please respond") item.
+            $this->out->text(_m('You will attend this event.'));
+            break;
+        case RSVP::NEGATIVE:
+            // TRANS: Possible status for RSVP ("please respond") item.
+            $this->out->text(_m('You will not attend this event.'));
+            break;
+        case RSVP::POSSIBLE:
+            // TRANS: Possible status for RSVP ("please respond") item.
+            $this->out->text(_m('You might attend this event.'));
+            break;
+        }
+
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text to cancel responding to an RSVP ("please respond") item.
+        $this->out->submit('rsvp-cancel', _m('BUTTON', 'Cancel'));
+    }
+}
diff --git a/plugins/Event/forms/event.php b/plugins/Event/forms/event.php
new file mode 100644 (file)
index 0000000..8100e59
--- /dev/null
@@ -0,0 +1,221 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Form for entering an event
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form for adding an event
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class EventForm extends Form
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'form_new_event';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings ajax-notice';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('newevent');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('fieldset', array('id' => 'new_event_data'));
+
+        // Passing in the URL of the Ajax action that the .js for this form hits
+        // when selecting event start and end times. JavaScript will try to
+        // use a relative path, unless explicitely told where an action is,
+        // and that's a bit difficult to calculate since the event form is on
+        // so many pages with different paths. It might be worth solving this
+        // globally by putting the base site path in the Identifier-URL meta tag
+        // or something similar, so it would be easy to calculate the exact path
+        // for actions and other things in JavaScripts. -z
+        $this->out->hidden('timelist_action_url', common_local_url('timelist'));
+
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->out->input('event-title',
+                          // TRANS: Field label on event form.
+                          _m('LABEL','Title'),
+                          null,
+                          // TRANS: Field title on event form.
+                          _m('Title of the event.'),
+                          'title');
+        $this->unli();
+
+        $this->li();
+
+        $today = new DateTime('now');
+        $today->setTimezone(new DateTimeZone(common_timezone()));
+
+        $this->out->input('event-startdate',
+                          // TRANS: Field label on event form.
+                          _m('LABEL','Start date'),
+                          $today->format('m/d/Y'),
+                          // TRANS: Field title on event form.
+                          _m('Date the event starts.'),
+                          'startdate');
+        $this->unli();
+
+        $this->li();
+
+        $times = EventTimeList::getTimes($today->format('m/d/Y 12:00') . ' am ' . $today->format('T'));
+        $start = EventTimeList::nearestHalfHour($today->format('c'));
+        $start->setTimezone(new DateTimeZone(common_timezone()));
+
+        $this->out->dropdown(
+            'event-starttime',
+            // TRANS: Field label on event form.
+            _m('LABEL','Start time'),
+            $times,
+            // TRANS: Field title on event form. %s is the abbreviated timezone
+            sprintf(_m("Time the event starts (%s)."), $today->format('T')),
+            false,
+            $start->format('g:ia')
+        );
+
+        // Need to keep JavaScript TZ in sync with PHP TZ
+        $this->out->hidden('tz', $today->format('T'));
+        $this->out->hidden('now', $today->format('F d, Y H:i:s T'));
+
+        $this->unli();
+
+        $this->li();
+        $this->out->input('event-enddate',
+                          // TRANS: Field label on event form.
+                          _m('LABEL','End date'),
+                          $today->format('m/d/Y'),
+                          // TRANS: Field title on event form.
+                          _m('Date the event ends.'),
+                          'enddate');
+        $this->unli();
+
+        $this->li();
+
+        $this->out->dropdown(
+            'event-endtime',
+            // TRANS: Field label on event form.
+            _m('LABEL','End time'),
+            EventTimeList::getTimes($start->format('c'), true),
+            // TRANS: Field title on event form.
+            _m('Time the event ends.'),
+            false,
+            null
+        );
+        $this->unli();
+
+        $this->li();
+        $this->out->input('event-location',
+                          // TRANS: Field label on event form.
+                          _m('LABEL','Where?'),
+                          null,
+                          // TRANS: Field title on event form.
+                          _m('Event location.'),
+                          'location');
+        $this->unli();
+
+        $this->li();
+        $this->out->input('event-url',
+                          // TRANS: Field label on event form.
+                          _m('LABEL','URL'),
+                          null,
+                          // TRANS: Field title on event form.
+                          _m('URL for more information.'),
+                          'url');
+        $this->unli();
+
+        $this->li();
+        $this->out->input('event-description',
+                          // TRANS: Field label on event form.
+                          _m('LABEL','Description'),
+                          null,
+                          // TRANS: Field title on event form.
+                          _m('Description of the event.'),
+                          'description');
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+
+        $toWidget = new ToSelector($this->out,
+                                   common_current_user(),
+                                   null);
+        $toWidget->show();
+
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text to save an event..
+        $this->out->submit('event-submit', _m('BUTTON', 'Save'), 'submit', 'submit');
+    }
+}
diff --git a/plugins/Event/forms/rsvp.php b/plugins/Event/forms/rsvp.php
new file mode 100644 (file)
index 0000000..7bb9260
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Form to RSVP for an event
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * A form to RSVP for an event
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class RSVPForm extends Form
+{
+    protected $event = null;
+
+    function __construct($event, $out=null)
+    {
+        parent::__construct($out);
+        $this->event = $event;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'form_event_rsvp';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'ajax';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('newrsvp');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('fieldset', array('id' => 'new_rsvp_data'));
+
+        // TRANS: Field label on form to RSVP ("please respond") for an event.
+        $this->out->text(_m('RSVP:'));
+
+        $this->out->hidden('event-id', $this->event->id, 'event');
+        $this->out->hidden('submitvalue', '');
+
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text for RSVP ("please respond") reply to confirm attendence.
+        $this->submitButton('yes', _m('BUTTON', 'Yes'));
+        // TRANS: Button text for RSVP ("please respond") reply to deny attendence.
+        $this->submitButton('no', _m('BUTTON', 'No'));
+        // TRANS: Button text for RSVP ("please respond") reply to indicate one might attend.
+        $this->submitButton('maybe', _m('BUTTON', 'Maybe'));
+    }
+
+    function submitButton($id, $label)
+    {
+        $this->out->element(
+            'input',
+                array(
+                    'type'    => 'submit',
+                    'id'      => 'rsvp-submit',
+                    'name'    => $id,
+                    'class'   => 'submit',
+                    'value'   => $label,
+                    'title'   => $label,
+                    'onClick' => 'this.form.submitvalue.value = this.name; return true;'
+            )
+        );
+    }
+}
diff --git a/plugins/Event/lib/eventlistitem.php b/plugins/Event/lib/eventlistitem.php
new file mode 100644 (file)
index 0000000..b563e22
--- /dev/null
@@ -0,0 +1,205 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Notice-list representation of an event
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Event
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Notice-list representation of an event
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class EventListItem extends NoticeListItemAdapter
+{
+    function showNotice()
+    {
+        $this->nli->out->elementStart('div', 'entry-title');
+        $this->nli->showAuthor();
+        $this->showContent();
+        $this->nli->out->elementEnd('div');
+    }
+
+    function showContent()
+    {
+        $notice = $this->nli->notice;
+        $out    = $this->nli->out;
+
+        $profile = $notice->getProfile();
+        $event   = Happening::fromNotice($notice);
+
+        if (empty($event)) {
+            // TRANS: Content for a deleted RSVP list item (RSVP stands for "please respond").
+            $out->element('p', null, _m('Deleted.'));
+            return;
+        }
+
+        $out->elementStart('div', 'vevent event'); // VEVENT IN
+
+        $out->elementStart('h3');  // VEVENT/H3 IN
+
+        if (!empty($event->url)) {
+            $out->element('a',
+                          array('href' => $event->url,
+                                'class' => 'event-title entry-title summary'),
+                          $event->title);
+        } else {
+            $out->text($event->title);
+        }
+
+        $out->elementEnd('h3'); // VEVENT/H3 OUT
+
+        $now       = new DateTime();
+        $startDate = new DateTime($event->start_time);
+        $endDate   = new DateTime($event->end_time);
+        $userTz    = new DateTimeZone(common_timezone());
+
+        // Localize the time for the observer
+        $now->setTimeZone($userTz);
+        $startDate->setTimezone($userTz);
+        $endDate->setTimezone($userTz);
+
+        $thisYear  = $now->format('Y');
+        $startYear = $startDate->format('Y');
+        $endYear   = $endDate->format('Y');
+
+        $dateFmt = 'D, F j, '; // e.g.: Mon, Aug 31
+
+        if ($startYear != $thisYear || $endYear != $thisYear) {
+            $dateFmt .= 'Y,'; // append year if we need to think about years
+        }
+
+        $startDateStr = $startDate->format($dateFmt);
+        $endDateStr = $endDate->format($dateFmt);
+
+        $timeFmt = 'g:ia';
+
+        $startTimeStr = $startDate->format($timeFmt);
+        $endTimeStr = $endDate->format("{$timeFmt} (T)");
+
+        $out->elementStart('div', 'event-times'); // VEVENT/EVENT-TIMES IN
+
+        // TRANS: Field label for event description.
+        $out->element('strong', null, _m('Time:'));
+
+        $out->element('abbr', array('class' => 'dtstart',
+                                    'title' => common_date_iso8601($event->start_time)),
+                      $startDateStr . ' ' . $startTimeStr);
+        $out->text(' â€“ ');
+        if ($startDateStr == $endDateStr) {
+            $out->element('span', array('class' => 'dtend',
+                                        'title' => common_date_iso8601($event->end_time)),
+                          $endTimeStr);
+        } else {
+            $out->element('span', array('class' => 'dtend',
+                                        'title' => common_date_iso8601($event->end_time)),
+                          $endDateStr . ' ' . $endTimeStr);
+        }
+
+        $out->elementEnd('div'); // VEVENT/EVENT-TIMES OUT
+
+        if (!empty($event->location)) {
+            $out->elementStart('div', 'event-location');
+            // TRANS: Field label for event description.
+            $out->element('strong', null, _m('Location:'));
+            $out->element('span', 'location', $event->location);
+            $out->elementEnd('div');
+        }
+
+        if (!empty($event->description)) {
+            $out->elementStart('div', 'event-description');
+            // TRANS: Field label for event description.
+            $out->element('strong', null, _m('Description:'));
+            $out->element('span', 'description', $event->description);
+            $out->elementEnd('div');
+        }
+
+        $rsvps = $event->getRSVPs();
+
+        $out->elementStart('div', 'event-rsvps');
+        // TRANS: Field label for event description.
+
+        $out->text(_('Attending:'));
+        $out->elementStart('ul', 'attending-list');
+
+        foreach ($rsvps as $verb => $responses) {
+            $out->elementStart('li', 'rsvp-list');
+            switch ($verb)
+            {
+            case RSVP::POSITIVE:
+                $out->text(_('Yes:'));
+                break;
+            case RSVP::NEGATIVE:
+                $out->text(_('No:'));
+                break;
+            case RSVP::POSSIBLE:
+                $out->text(_('Maybe:'));
+                break;
+            }
+            $ids = array();
+            foreach ($responses as $response) {
+                $ids[] = $response->profile_id;
+            }
+            $ids = array_slice($ids, 0, ProfileMiniList::MAX_PROFILES + 1);
+            $profiles = Profile::pivotGet('id', $ids);
+            $profile  = new ArrayWrapper(array_values($profiles));
+            $minilist = new ProfileMiniList($profile, $out);
+            $minilist->show();
+
+            $out->elementEnd('li');
+        }
+
+        $out->elementEnd('ul');
+        $out->elementEnd('div');
+
+        $user = common_current_user();
+
+        if (!empty($user)) {
+            $rsvp = $event->getRSVP($user->getProfile());
+
+            if (empty($rsvp)) {
+                $form = new RSVPForm($event, $out);
+            } else {
+                $form = new CancelRSVPForm($rsvp, $out);
+            }
+
+            $form->show();
+        }
+
+        $out->elementEnd('div'); // vevent out
+    }
+}
diff --git a/plugins/Event/lib/eventtimelist.php b/plugins/Event/lib/eventtimelist.php
new file mode 100644 (file)
index 0000000..2436a4f
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+/**
+ * Helper class for calculating and displaying event times
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ *  Class to get fancy times for the dropdowns on the new event form
+ */
+class EventTimeList {
+    /**
+     * Round up to the nearest half hour
+     *
+     * @param string $time the time to round (date/time string)
+     * @return DateTime    the rounded time
+     */
+    public static function nearestHalfHour($time)
+    {
+        $startd = new DateTime($time);
+
+        $minutes = $startd->format('i');
+        $hour    = $startd->format('H');
+
+        if ($minutes >= 30) {
+            $minutes = '00';
+            $hour++;
+        } else {
+            $minutes = '30';
+        }
+
+        $startd->setTime($hour, $minutes, 0);
+
+        return $startd;
+    }
+
+    /**
+     * Output a list of times in half-hour intervals
+     *
+     * @param string  $start       Time to start with (date string, usually a ts)
+     * @param boolean $duration    Whether to include the duration of the event
+     *                             (from the start)
+     * @return array  $times (localized 24 hour time string => fancy time string)
+     */
+    public static function getTimes($start = 'now', $duration = false)
+    {
+        $newTime = new DateTime($start);
+        $times   = array();
+        $len     = 0;
+
+        $newTime->setTimezone(new DateTimeZone(common_timezone()));
+
+        for ($i = 0; $i < 47; $i++) {
+
+            $localTime = $newTime->format("g:ia");
+
+            // pretty up the end-time option list a bit
+            if ($duration) {
+                $hours = $len / 60;
+                switch ($hours) {
+                case 0:
+                    // TRANS: 0 minutes abbreviated. Used in a list.
+                    $total = ' ' . _m('(0 min)');
+                    break;
+                case .5:
+                    // TRANS: 30 minutes abbreviated. Used in a list.
+                    $total = ' ' . _m('(30 min)');
+                    break;
+                case 1:
+                    // TRANS: 1 hour. Used in a list.
+                    $total = ' ' . _m('(1 hour)');
+                    break;
+                default:
+                    // TRANS: Number of hours (%.1f and %d). Used in a list.
+                    $format = is_float($hours) 
+                        ? _m('(%.1f hours)')
+                        : _m('(%d hours)');
+                    $total = ' ' . sprintf($format, $hours);
+                    break;
+                }
+                $localTime .= $total;
+                $len += 30;
+            }
+
+            $times[$newTime->format('g:ia')] = $localTime;
+            $newTime->modify('+30min'); // 30 min intervals
+        }
+
+        return $times;
+    }
+}
diff --git a/plugins/Event/lib/rsvplistitem.php b/plugins/Event/lib/rsvplistitem.php
new file mode 100644 (file)
index 0000000..fef1c9d
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Title of module
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Cache
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Class comment
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class RSVPListItem extends NoticeListItemAdapter
+{
+    function showNotice()
+    {
+        $this->nli->out->elementStart('div', 'entry-title');
+        $this->nli->showAuthor();
+        $this->showContent();
+        $this->nli->out->elementEnd('div');
+    }
+
+    function showContent()
+    {
+        $notice = $this->nli->notice;
+        $out    = $this->nli->out;
+
+        $rsvp = RSVP::fromNotice($notice);
+
+        if (empty($rsvp)) {
+            // TRANS: Content for a deleted RSVP list item (RSVP stands for "please respond").
+            $out->element('p', null, _m('Deleted.'));
+            return;
+        }
+
+        $out->elementStart('div', 'rsvp');
+        $out->raw($rsvp->asHTML());
+        $out->elementEnd('div');
+        return;
+    }
+}
diff --git a/plugins/Event/newevent.php b/plugins/Event/newevent.php
deleted file mode 100644 (file)
index 4c2157e..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Add a new event
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Add a new event
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class NeweventAction extends Action
-{
-    protected $user        = null;
-    protected $error       = null;
-    protected $complete    = null;
-    protected $title       = null;
-    protected $location    = null;
-    protected $description = null;
-    protected $startTime   = null;
-    protected $endTime     = null;
-
-    /**
-     * Returns the title of the action
-     *
-     * @return string Action title
-     */
-    function title()
-    {
-        // TRANS: Title for new event form.
-        return _m('TITLE','New event');
-    }
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        $this->user = common_current_user();
-
-        if (empty($this->user)) {
-            // TRANS: Client exception thrown when trying to post an event while not logged in.
-            throw new ClientException(_m('Must be logged in to post a event.'),
-                                      403);
-        }
-
-        if ($this->isPost()) {
-            $this->checkSessionToken();
-        }
-
-        try {
-
-            $this->title = $this->trimmed('title');
-
-            if (empty($this->title)) {
-                // TRANS: Client exception thrown when trying to post an event without providing a title.
-                throw new ClientException(_m('Title required.'));
-            }
-
-            $this->location    = $this->trimmed('location');
-            $this->url         = $this->trimmed('url');
-            $this->description = $this->trimmed('description');
-            $tz                = $this->trimmed('tz');
-
-            $startDate = $this->trimmed('startdate');
-
-            if (empty($startDate)) {
-                // TRANS: Client exception thrown when trying to post an event without providing a start date.
-                throw new ClientException(_m('Start date required.'));
-            }
-
-            $startTime = $this->trimmed('event-starttime');
-
-            if (empty($startTime)) {
-                $startTime = '00:00';
-            }
-
-            $endDate   = $this->trimmed('enddate');
-
-            if (empty($endDate)) {
-                // TRANS: Client exception thrown when trying to post an event without providing an end date.
-                throw new ClientException(_m('End date required.'));
-            }
-
-            $endTime   = $this->trimmed('event-endtime');
-
-            if (empty($endTime)) {
-                $endTime = '00:00';
-            }
-
-            $start = $startDate . ' ' . $startTime . ' ' . $tz;
-            $end   = $endDate . ' ' . $endTime . ' ' . $tz;
-
-            $this->startTime = strtotime($start);
-            $this->endTime   = strtotime($end);
-
-            if ($this->startTime == 0) {
-                // TRANS: Client exception thrown when trying to post an event with a date that cannot be processed.
-                // TRANS: %s is the data that could not be processed.
-                throw new ClientException(sprintf(_m('Could not parse date "%s".'),
-                                            $start));
-            }
-
-            if ($this->endTime == 0) {
-                // TRANS: Client exception thrown when trying to post an event with a date that cannot be processed.
-                // TRANS: %s is the data that could not be processed.
-                throw new ClientException(sprintf(_m('Could not parse date "%s".'),
-                                            $end));
-            }
-        } catch (ClientException $ce) {
-            if ($this->boolean('ajax')) {
-                $this->outputAjaxError($ce->getMessage());
-                return false;
-            } else {
-                $this->error = $ce->getMessage();
-                $this->showPage();
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        parent::handle($argarray);
-
-        if ($this->isPost()) {
-            $this->newEvent();
-        } else {
-            $this->showPage();
-        }
-
-        return;
-    }
-
-    /**
-     * Add a new event
-     *
-     * @return void
-     */
-    function newEvent()
-    {
-        try {
-
-            if (empty($this->title)) {
-                // TRANS: Client exception thrown when trying to post an event without providing a title.
-                throw new ClientException(_m('Event must have a title.'));
-            }
-
-            if (empty($this->startTime)) {
-                // TRANS: Client exception thrown when trying to post an event without providing a start time.
-                throw new ClientException(_m('Event must have a start time.'));
-            }
-
-            if (empty($this->endTime)) {
-                // TRANS: Client exception thrown when trying to post an event without providing an end time.
-                throw new ClientException(_m('Event must have an end time.'));
-            }
-
-            if (!empty($this->url) && Validate::uri($this->url) === false) {
-                // TRANS: Client exception thrown when trying to post an event with an invalid URL.
-                throw new ClientException(_m('URL must be valid.'));
-            }
-
-            $options = array();
-
-            // Does the heavy-lifting for getting "To:" information
-
-            ToSelector::fillOptions($this, $options);
-
-            $profile = $this->user->getProfile();
-
-            $saved = Happening::saveNew($profile,
-                                        $this->startTime,
-                                        $this->endTime,
-                                        $this->title,
-                                        $this->location,
-                                        $this->description,
-                                        $this->url,
-                                        $options);
-
-            $event = Happening::fromNotice($saved);
-
-            RSVP::saveNew($profile, $event, RSVP::POSITIVE);
-
-        } catch (ClientException $ce) {
-            if ($this->boolean('ajax')) {
-                $this->outputAjaxError($ce->getMessage());
-                return;
-            } else {
-                $this->error = $ce->getMessage();
-                $this->showPage();
-                return;
-            }
-        }
-
-        if ($this->boolean('ajax')) {
-            header('Content-Type: text/xml;charset=utf-8');
-            $this->xw->startDocument('1.0', 'UTF-8');
-            $this->elementStart('html');
-            $this->elementStart('head');
-            // TRANS: Page title after sending a notice.
-            $this->element('title', null, _m('Event saved'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $this->showNotice($saved);
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            common_redirect($saved->bestUrl(), 303);
-        }
-    }
-
-    // @todo factor this out into a base class
-    function outputAjaxError($msg)
-    {
-        header('Content-Type: text/xml;charset=utf-8');
-        $this->xw->startDocument('1.0', 'UTF-8');
-        $this->elementStart('html');
-        $this->elementStart('head');
-        // TRANS: Page title after an AJAX error occurs
-        $this->element('title', null, _('Ajax Error'));
-        $this->elementEnd('head');
-        $this->elementStart('body');
-        $this->element('p', array('id' => 'error'), $msg);
-        $this->elementEnd('body');
-        $this->elementEnd('html');
-        return;
-    }
-
-    /**
-     * Show the event form
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        if (!empty($this->error)) {
-            $this->element('p', 'error', $this->error);
-        }
-
-        $form = new EventForm($this);
-
-        $form->show();
-
-        return;
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
-            $_SERVER['REQUEST_METHOD'] == 'HEAD') {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-
-    /**
-     * Output a notice
-     *
-     * Used to generate the notice code for Ajax results.
-     *
-     * @param Notice $notice Notice that was saved
-     *
-     * @return void
-     */
-    function showNotice($notice)
-    {
-        $nli = new NoticeListItem($notice, $this);
-        $nli->show();
-    }
-}
diff --git a/plugins/Event/newrsvp.php b/plugins/Event/newrsvp.php
deleted file mode 100644 (file)
index 272c6f0..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * RSVP for an event
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * RSVP for an event
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class NewrsvpAction extends Action
-{
-    protected $user  = null;
-    protected $event = null;
-    protected $verb  = null;
-
-    /**
-     * Returns the title of the action
-     *
-     * @return string Action title
-     */
-    function title()
-    {
-        // TRANS: Title for RSVP ("please respond") action.
-        return _m('TITLE','New RSVP');
-    }
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-        if ($this->boolean('ajax')) {
-            StatusNet::setApi(true); // short error results!
-        }
-
-        $eventId = $this->trimmed('event');
-
-        if (empty($eventId)) {
-            // TRANS: Client exception thrown when referring to a non-existing event.
-            throw new ClientException(_m('No such event.'));
-        }
-
-        $this->event = Happening::getKV('id', $eventId);
-
-        if (empty($this->event)) {
-            // TRANS: Client exception thrown when referring to a non-existing event.
-            throw new ClientException(_m('No such event.'));
-        }
-
-        $this->user = common_current_user();
-
-        if (empty($this->user)) {
-            // TRANS: Client exception thrown when trying to RSVP ("please respond") while not logged in.
-            throw new ClientException(_m('You must be logged in to RSVP for an event.'));
-        }
-
-        common_debug(print_r($this->args, true));
-
-        switch (strtolower($this->trimmed('submitvalue'))) {
-        case 'yes':
-            $this->verb = RSVP::POSITIVE;
-            break;
-        case 'no':
-            $this->verb = RSVP::NEGATIVE;
-            break;
-        case 'maybe':
-            $this->verb = RSVP::POSSIBLE;
-            break;
-        default:
-            // TRANS: Client exception thrown when using an invalid value for RSVP ("please respond").
-            throw new ClientException(_m('Unknown submit value.'));
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        parent::handle($argarray);
-
-        if ($this->isPost()) {
-            $this->newRSVP();
-        } else {
-            $this->showPage();
-        }
-
-        return;
-    }
-
-    /**
-     * Add a new event
-     *
-     * @return void
-     */
-    function newRSVP()
-    {
-        try {
-            $saved = RSVP::saveNew($this->user->getProfile(),
-                                   $this->event,
-                                   $this->verb);
-        } catch (ClientException $ce) {
-            $this->error = $ce->getMessage();
-            $this->showPage();
-            return;
-        }
-
-        if ($this->boolean('ajax')) {
-            $rsvp = RSVP::fromNotice($saved);
-            header('Content-Type: text/xml;charset=utf-8');
-            $this->xw->startDocument('1.0', 'UTF-8');
-            $this->elementStart('html');
-            $this->elementStart('head');
-            // TRANS: Page title after creating an event.
-            $this->element('title', null, _m('Event saved'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $this->elementStart('body');
-            $cancel = new CancelRSVPForm($rsvp, $this);
-            $cancel->show();
-            $this->elementEnd('body');
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            common_redirect($saved->bestUrl(), 303);
-        }
-    }
-
-    /**
-     * Show the event form
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        if (!empty($this->error)) {
-            $this->element('p', 'error', $this->error);
-        }
-
-        $form = new RSVPForm($this->event, $this);
-
-        $form->show();
-
-        return;
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
-            $_SERVER['REQUEST_METHOD'] == 'HEAD') {
-            return true;
-        } else {
-            return false;
-        }
-    }
-}
diff --git a/plugins/Event/rsvpform.php b/plugins/Event/rsvpform.php
deleted file mode 100644 (file)
index 7bb9260..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Form to RSVP for an event
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * A form to RSVP for an event
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class RSVPForm extends Form
-{
-    protected $event = null;
-
-    function __construct($event, $out=null)
-    {
-        parent::__construct($out);
-        $this->event = $event;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'form_event_rsvp';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'ajax';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('newrsvp');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('fieldset', array('id' => 'new_rsvp_data'));
-
-        // TRANS: Field label on form to RSVP ("please respond") for an event.
-        $this->out->text(_m('RSVP:'));
-
-        $this->out->hidden('event-id', $this->event->id, 'event');
-        $this->out->hidden('submitvalue', '');
-
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text for RSVP ("please respond") reply to confirm attendence.
-        $this->submitButton('yes', _m('BUTTON', 'Yes'));
-        // TRANS: Button text for RSVP ("please respond") reply to deny attendence.
-        $this->submitButton('no', _m('BUTTON', 'No'));
-        // TRANS: Button text for RSVP ("please respond") reply to indicate one might attend.
-        $this->submitButton('maybe', _m('BUTTON', 'Maybe'));
-    }
-
-    function submitButton($id, $label)
-    {
-        $this->out->element(
-            'input',
-                array(
-                    'type'    => 'submit',
-                    'id'      => 'rsvp-submit',
-                    'name'    => $id,
-                    'class'   => 'submit',
-                    'value'   => $label,
-                    'title'   => $label,
-                    'onClick' => 'this.form.submitvalue.value = this.name; return true;'
-            )
-        );
-    }
-}
diff --git a/plugins/Event/rsvplistitem.php b/plugins/Event/rsvplistitem.php
deleted file mode 100644 (file)
index fef1c9d..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Title of module
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Cache
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Class comment
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class RSVPListItem extends NoticeListItemAdapter
-{
-    function showNotice()
-    {
-        $this->nli->out->elementStart('div', 'entry-title');
-        $this->nli->showAuthor();
-        $this->showContent();
-        $this->nli->out->elementEnd('div');
-    }
-
-    function showContent()
-    {
-        $notice = $this->nli->notice;
-        $out    = $this->nli->out;
-
-        $rsvp = RSVP::fromNotice($notice);
-
-        if (empty($rsvp)) {
-            // TRANS: Content for a deleted RSVP list item (RSVP stands for "please respond").
-            $out->element('p', null, _m('Deleted.'));
-            return;
-        }
-
-        $out->elementStart('div', 'rsvp');
-        $out->raw($rsvp->asHTML());
-        $out->elementEnd('div');
-        return;
-    }
-}
diff --git a/plugins/Event/showevent.php b/plugins/Event/showevent.php
deleted file mode 100644 (file)
index f1d8c6a..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Show a single event
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Show a single event, with associated information
- *
- * @category  Event
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class ShoweventAction extends ShownoticeAction
-{
-    protected $id    = null;
-    protected $event = null;
-
-    function getNotice()
-    {
-        $this->id = $this->trimmed('id');
-
-        $this->event = Happening::getKV('id', $this->id);
-
-        if (empty($this->event)) {
-            // TRANS: Client exception thrown when referring to a non-existing event.
-            throw new ClientException(_m('No such event.'), 404);
-        }
-
-        $notice = $this->event->getNotice();
-
-        if (empty($notice)) {
-            // Did we used to have it, and it got deleted?
-            // TRANS: Client exception thrown when referring to a non-existing event.
-            throw new ClientException(_m('No such event.'), 404);
-        }
-
-        return $notice;
-    }
-
-    /**
-     * Title of the page
-     *
-     * Used by Action class for layout.
-     *
-     * @return string page tile
-     */
-    function title()
-    {
-        return $this->event->title;
-    }
-}
diff --git a/plugins/Event/showrsvp.php b/plugins/Event/showrsvp.php
deleted file mode 100644 (file)
index 697bda9..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * Show a single RSVP
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  RSVP
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Show a single RSVP, with associated information
- *
- * @category  RSVP
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class ShowrsvpAction extends ShownoticeAction
-{
-    protected $rsvp = null;
-    protected $event = null;
-
-    function getNotice()
-    {
-        $this->id = $this->trimmed('id');
-
-        $this->rsvp = RSVP::getKV('id', $this->id);
-
-        if (empty($this->rsvp)) {
-            // TRANS: Client exception thrown when referring to a non-existing RSVP.
-            // TRANS: RSVP stands for "Please reply".
-            throw new ClientException(_m('No such RSVP.'), 404);
-        }
-
-        $this->event = $this->rsvp->getEvent();
-
-        if (empty($this->event)) {
-            // TRANS: Client exception thrown when referring to a non-existing event.
-            throw new ClientException(_m('No such event.'), 404);
-        }
-
-        $notice = $this->rsvp->getNotice();
-
-        if (empty($notice)) {
-            // Did we used to have it, and it got deleted?
-            // TRANS: Client exception thrown when referring to a non-existing RSVP.
-            // TRANS: RSVP stands for "Please reply".
-            throw new ClientException(_m('No such RSVP.'), 404);
-        }
-
-        return $notice;
-    }
-
-    /**
-     * Title of the page
-     *
-     * Used by Action class for layout.
-     *
-     * @return string page tile
-     */
-    function title()
-    {
-        // TRANS: Title for event.
-       // TRANS: %1$s is a user nickname, %2$s is an event title.
-        return sprintf(_m('%1$s\'s RSVP for "%2$s"'),
-                       $this->user->nickname,
-                       $this->event->title);
-    }
-}
diff --git a/plugins/Event/timelist.php b/plugins/Event/timelist.php
deleted file mode 100644 (file)
index cb8efb3..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Event
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Callback handler to populate end time dropdown
- */
-class TimelistAction extends Action {
-    private $start;
-    private $duration;
-
-    /**
-     * Get ready
-     *
-     * @param array $args misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($args) {
-        parent::prepare($args);
-        $this->start = $this->arg('start');
-        $this->duration = $this->boolean('duration', false);
-        return true;
-    }
-
-    /**
-     * Handle input and ouput something
-     *
-     * @param array $args $_REQUEST arguments
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-
-        if (!common_logged_in()) {
-            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
-            $this->clientError(_m('Not logged in.'));
-            return;
-        }
-
-        if (!empty($this->start)) {
-            $times = EventTimeList::getTimes($this->start, $this->duration);
-        } else {
-            // TRANS: Client error when submitting a form with unexpected information.
-            $this->clientError(_m('Unexpected form submission.'));
-            return;
-        }
-
-        if ($this->boolean('ajax')) {
-            header('Content-Type: application/json; charset=utf-8');
-            print json_encode($times);
-        } else {
-            // TRANS: Client error displayed when using an action in a non-AJAX way.
-            $this->clientError(_m('This action is AJAX only.'));
-        }
-    }
-
-    /**
-     * Override the regular error handler to show something more
-     * ajaxy
-     *
-     * @param string $msg   error message
-     * @param int    $code  error code
-     */
-    function clientError($msg, $code = 400) {
-        if ($this->boolean('ajax')) {
-            header('Content-Type: application/json; charset=utf-8');
-            print json_encode(
-                array(
-                    'success' => false,
-                    'code'    => $code,
-                    'message' => $msg
-                )
-            );
-        } else {
-            parent::clientError($msg, $code);
-        }
-    }
-}
index 8436275b78f231d2b962b75d59b8e12f46f3e814..578825aa34e8e8a3fcb5743fb19136f056d37a18 100644 (file)
@@ -43,42 +43,6 @@ class ExtendedProfilePlugin extends Plugin
         return true;
     }
 
-    /**
-     * Autoloader
-     *
-     * Loads our classes if they're requested.
-     *
-     * @param string $cls Class requested
-     *
-     * @return boolean hook return
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch (strtolower($cls))
-        {
-        case 'profiledetailaction':
-        case 'profiledetailsettingsaction':
-        case 'userautocompleteaction':
-            include_once $dir . '/actions/'
-                . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-            break; // Safety first!
-        case 'extendedprofile':
-        case 'extendedprofilewidget':
-            include_once $dir . '/lib/' . strtolower($cls) . '.php';
-            return false;
-            break;
-        case 'profile_detail':
-            include_once $dir . '/classes/' . ucfirst($cls) . '.php';
-            return false;
-            break;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Add paths to the router table
      *
index 07a149785cf5db86aa1909dd0ad9af8def2c3c5f..3bace7341b2b99aa614cb7abb3ae314012b7dcc5 100644 (file)
@@ -109,23 +109,9 @@ class FacebookBridgePlugin extends Plugin
             include_once $dir . '/extlib/base_facebook.php';
             include_once $dir . '/extlib/facebook.php';
             return false;
-        case 'FacebookloginAction':
-        case 'FacebookfinishloginAction':
-        case 'FacebookadminpanelAction':
-        case 'FacebooksettingsAction':
-        case 'FacebookdeauthorizeAction':
-            include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'Facebookclient':
-        case 'FacebookQueueHandler':
-            include_once $dir . '/lib/' . strtolower($cls) . '.php';
-            return false;
-        case 'Notice_to_item':
-            include_once $dir . '/classes/' . $cls . '.php';
-            return false;
-        default:
-            return true;
         }
+
+        return parent::onAutoload($cls);
     }
 
     /**
index 0bea8d9aa46ed93041fd824cd5b38188a06b7eeb..167b4315336acec99a02be4d06b4bd7900ec3ae9 100644 (file)
@@ -119,27 +119,6 @@ class FollowEveryonePlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'User_followeveryone_prefs':
-            include_once $dir . '/'.$cls.'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Show a checkbox on the profile form to ask whether to follow everyone
      *
diff --git a/plugins/FollowEveryone/User_followeveryone_prefs.php b/plugins/FollowEveryone/User_followeveryone_prefs.php
deleted file mode 100644 (file)
index c1a654d..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-/**
- * Data class for counting greetings
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for counting greetings
- *
- * We use the DB_DataObject framework for data classes in StatusNet. Each
- * table maps to a particular data class, making it easier to manipulate
- * data.
- *
- * Data classes should extend Memcached_DataObject, the (slightly misnamed)
- * extension of DB_DataObject that provides caching, internationalization,
- * and other bits of good functionality to StatusNet-specific data classes.
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class User_followeveryone_prefs extends Managed_DataObject
-{
-    public $__table = 'user_followeveryone_prefs'; // table name
-    public $user_id;                               // int(4)  primary_key not_null
-    public $followeveryone;                        // tinyint(1)
-    public $created;                               // datetime()   not_null
-    public $modified;                              // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id'),
-                'followeveryone' => array('type' => 'int', 'default' => 1, 'size' => 'tiny', 'description' => 'whether to follow everyone'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('user_id'),
-            'foreign keys' => array(
-                'user_followeveryone_prefs_user_id_fkey' => array('user', array('user_id' => 'id')),
-            ),
-        );
-    }
-
-    static function followEveryone($user_id)
-    {
-        $ufep = self::getKV('user_id', $user_id);
-
-        if (empty($ufep)) {
-            return true;
-        } else {
-            return (bool)$ufep->followeveryone;
-        }
-    }
-
-    static function savePref($user_id, $followEveryone)
-    {
-        $ufep = self::getKV('user_id', $user_id);
-
-        if (empty($ufep)) {
-            $ufep = new User_followeveryone_prefs();
-            $ufep->user_id = $user_id;
-            $ufep->followeveryone = $followEveryone;
-            $ufep->insert();
-        } else {
-            $orig = clone($ufep);
-            $ufep->followeveryone = $followEveryone;
-            $ufep->update();
-        }
-
-        return true;
-    }
-}
diff --git a/plugins/FollowEveryone/classes/User_followeveryone_prefs.php b/plugins/FollowEveryone/classes/User_followeveryone_prefs.php
new file mode 100644 (file)
index 0000000..c1a654d
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Data class for counting greetings
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for counting greetings
+ *
+ * We use the DB_DataObject framework for data classes in StatusNet. Each
+ * table maps to a particular data class, making it easier to manipulate
+ * data.
+ *
+ * Data classes should extend Memcached_DataObject, the (slightly misnamed)
+ * extension of DB_DataObject that provides caching, internationalization,
+ * and other bits of good functionality to StatusNet-specific data classes.
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class User_followeveryone_prefs extends Managed_DataObject
+{
+    public $__table = 'user_followeveryone_prefs'; // table name
+    public $user_id;                               // int(4)  primary_key not_null
+    public $followeveryone;                        // tinyint(1)
+    public $created;                               // datetime()   not_null
+    public $modified;                              // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id'),
+                'followeveryone' => array('type' => 'int', 'default' => 1, 'size' => 'tiny', 'description' => 'whether to follow everyone'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('user_id'),
+            'foreign keys' => array(
+                'user_followeveryone_prefs_user_id_fkey' => array('user', array('user_id' => 'id')),
+            ),
+        );
+    }
+
+    static function followEveryone($user_id)
+    {
+        $ufep = self::getKV('user_id', $user_id);
+
+        if (empty($ufep)) {
+            return true;
+        } else {
+            return (bool)$ufep->followeveryone;
+        }
+    }
+
+    static function savePref($user_id, $followEveryone)
+    {
+        $ufep = self::getKV('user_id', $user_id);
+
+        if (empty($ufep)) {
+            $ufep = new User_followeveryone_prefs();
+            $ufep->user_id = $user_id;
+            $ufep->followeveryone = $followEveryone;
+            $ufep->insert();
+        } else {
+            $orig = clone($ufep);
+            $ufep->followeveryone = $followEveryone;
+            $ufep->update();
+        }
+
+        return true;
+    }
+}
index 27a32855582966a5e4f72b4f46d8c609f46c7ce6..cb6ee97e8970a6bbfb78f5e8c6b463ea9e2f7666 100644 (file)
@@ -42,26 +42,6 @@ class GNUsocialPhotoPlugin extends MicroAppPlugin
         return true;
     }
 
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-        switch($cls)
-        {
-        case 'Photo':
-            include_once $dir . '/Photo.php';
-            break;
-        case 'NewPhotoForm':
-            include_once $dir . '/newphotoform.php';
-            break;
-        case 'NewphotoAction':
-            include_once $dir . '/newphoto.php';
-            break;
-        default:
-            break;
-        }
-        return true;
-    }
-
     function onRouterInitialized($m)
     {
         $m->connect('main/photo/new', array('action' => 'newphoto'));
diff --git a/plugins/GNUsocialPhoto/Photo.php b/plugins/GNUsocialPhoto/Photo.php
deleted file mode 100644 (file)
index be35492..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-<?php
-/**
- * GNU Social
- * Copyright (C) 2011, Free Software Foundation, Inc.
- *
- * PHP version 5
- *
- * LICENCE:
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   GNU Social
- * @author    Ian Denhardt <ian@zenhack.net>
- * @copyright 2011 Free Software Foundation, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- */
-
-if(!defined('STATUSNET')){
-    exit(1);
-}
-
-/**
- * Data class for photos.
- */
-
-class Photo extends Managed_DataObject
-{
-    const OBJECT_TYPE = 'http://activitystrea.ms/schema/1.0/photo';
-
-    public $__table = 'photo'; // table name
-    public $id;                // char (36) // UUID
-    public $uri;               // varchar (255)  // This is the corresponding notice's uri.
-    public $photo_uri;         // varchar (255)
-    public $thumb_uri;         // varchar (255)
-    public $title;             // varchar (255)
-    public $description;       // text
-    public $profile_id;        // int
-
-    public static function getByNotice($notice)
-    {
-        return self::getKV('uri', $notice->uri);
-    }
-
-    public function getNotice()
-    {
-        return Notice::getKV('uri', $this->uri);
-    }
-
-    public static function schemaDef()
-    {
-        return array(
-            'description' => 'A photograph',
-            'fields' => array(
-                'id' => array('type' => 'char',
-                              'length' => 36,
-                              'not null' => true,
-                              'description' => 'UUID'),
-                'uri' => array('type' => 'varchar',
-                               'length' => 255,
-                               'not null' => true),
-                'photo_uri' => array('type' => 'varchar',
-                               'length' => 255,
-                               'not null' => true),
-                'photo_uri' => array('type' => 'varchar',
-                               'length' => 255,
-                               'not null' => true),
-                'profile_id' => array('type' => 'int', 'not null' => true),
-            ),
-            'primary key' => array('id'),
-            'foreign keys' => array('photo_profile_id__key' => array('profile' => array('profile_id' => 'id'))),
-        );
-    }
-
-    function saveNew($profile, $photo_uri, $thumb_uri, $title, $description, $options=array())
-    {
-        $photo = new Photo();
-
-        $photo->id =  UUID::gen();
-        $photo->profile_id = $profile->id;
-        $photo->photo_uri = $photo_uri;
-        $photo->thumb_uri = $thumb_uri;
-
-
-        $options['object_type'] = Photo::OBJECT_TYPE;
-
-        if (!array_key_exists('uri', $options)) { 
-            $options['uri'] = common_local_url('showphoto', array('id' => $photo->id));
-        }
-
-        if (!array_key_exists('rendered', $options)) {
-            $options['rendered'] = sprintf("<img src=\"%s\" alt=\"%s\"></img>", $photo_uri,
-                $title);
-        }
-
-        $photo->uri = $options['uri'];
-        
-        $photo->insert();
-
-        return Notice::saveNew($profile->id,
-                               '',
-                               'web',
-                               $options);
-
-    }
-}
diff --git a/plugins/GNUsocialPhoto/actions/newphoto.php b/plugins/GNUsocialPhoto/actions/newphoto.php
new file mode 100644 (file)
index 0000000..18ae552
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+/**
+ * GNU Social
+ * Copyright (C) 2011, Free Software Foundation, Inc.
+ *
+ * PHP version 5
+ *
+ * LICENCE:
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   GNU Social
+ * @author    Ian Denhardt <ian@zenhack.net>
+ * @copyright 2011 Free Software Foundation, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ */
+
+if(!defined('STATUSNET')){
+    exit(1);
+}
+
+class NewphotoAction extends Action
+{
+    var $user = null;
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+        $this->user = common_current_user();
+
+        if(empty($this->user)){
+            throw new ClientException(_('Must be logged in to post a photo'),
+                403);
+        }
+
+        if($this->isPost()){
+            $this->checkSessionToken();
+        }
+
+        return true;
+    }
+
+    function handle($args)
+    {
+        parent::handle($args);
+
+        if ($this->isPost()) {
+            $this->handlePost($args);
+        } else {
+            $this->showPage();
+        }
+    }
+    
+    function handlePost($args)
+    {
+
+        /*
+        // Workaround for PHP returning empty $_POST and $_FILES when POST
+        // length > post_max_size in php.ini
+        if (empty($_FILES)
+            && empty($_POST)
+            && ($_SERVER['CONTENT_LENGTH'] > 0)
+        ) {
+            $msg = _('The server was unable to handle that much POST ' .
+                'data (%s bytes) due to its current configuration.');
+            $this->showForm(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
+            return;
+        } */
+
+        $profile = $this->user->getProfile();
+
+        $options = array();
+        
+        ToSelector::fillOptions($this, $options);
+
+        try {
+            $this->handleUpload();
+        } catch (Exception $e) {
+            $this->showForm($e->getMessage());
+            return;
+        }
+        
+
+        common_redirect($photo->uri, 303);
+    }
+
+    function getUpload()
+    {
+            $imagefile = ImageFile::fromUpload('photo_upload');
+
+            if($imagefile === null) {
+                throw new Exception(_('No file uploaded'));
+            }
+
+            $title = $this->trimmed('title');
+            $description = $this->trimmed('description');
+
+            $new_filename = UUID::gen() . image_type_to_extension($imagefile->type);
+            move_uploaded_file($imagefile->filepath, INSTALLDIR . '/file/' . $new_filename);
+           
+            // XXX: we should be using https where we can. TODO: detect whether the server
+            // supports this.
+            $photo_uri = 'http://' . common_config('site', 'server') . '/file/'
+                . $new_filename;
+            $thumb_uri = $photo_uri;
+
+            $photo = Photo::saveNew($profile, $photo_uri, $thumb_uri, $title,
+                $description, $options);
+        
+    }
+
+    function showContent()
+    {
+        $form = new NewPhotoForm();
+        $form->show();
+    }
+}
+
+
diff --git a/plugins/GNUsocialPhoto/actions/showphoto.php b/plugins/GNUsocialPhoto/actions/showphoto.php
new file mode 100644 (file)
index 0000000..b97a056
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+/**
+ * GNU Social
+ * Copyright (C) 2011, Free Software Foundation, Inc.
+ *
+ * PHP version 5
+ *
+ * LICENCE:
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   GNU Social
+ * @author    Ian Denhardt <ian@zenhack.net>
+ * @copyright 2011 Free Software Foundation, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ */
+
+if(!defined('STATUSNET')){
+    exit(1);
+}
+
+
diff --git a/plugins/GNUsocialPhoto/classes/Photo.php b/plugins/GNUsocialPhoto/classes/Photo.php
new file mode 100644 (file)
index 0000000..be35492
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * GNU Social
+ * Copyright (C) 2011, Free Software Foundation, Inc.
+ *
+ * PHP version 5
+ *
+ * LICENCE:
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   GNU Social
+ * @author    Ian Denhardt <ian@zenhack.net>
+ * @copyright 2011 Free Software Foundation, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ */
+
+if(!defined('STATUSNET')){
+    exit(1);
+}
+
+/**
+ * Data class for photos.
+ */
+
+class Photo extends Managed_DataObject
+{
+    const OBJECT_TYPE = 'http://activitystrea.ms/schema/1.0/photo';
+
+    public $__table = 'photo'; // table name
+    public $id;                // char (36) // UUID
+    public $uri;               // varchar (255)  // This is the corresponding notice's uri.
+    public $photo_uri;         // varchar (255)
+    public $thumb_uri;         // varchar (255)
+    public $title;             // varchar (255)
+    public $description;       // text
+    public $profile_id;        // int
+
+    public static function getByNotice($notice)
+    {
+        return self::getKV('uri', $notice->uri);
+    }
+
+    public function getNotice()
+    {
+        return Notice::getKV('uri', $this->uri);
+    }
+
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'A photograph',
+            'fields' => array(
+                'id' => array('type' => 'char',
+                              'length' => 36,
+                              'not null' => true,
+                              'description' => 'UUID'),
+                'uri' => array('type' => 'varchar',
+                               'length' => 255,
+                               'not null' => true),
+                'photo_uri' => array('type' => 'varchar',
+                               'length' => 255,
+                               'not null' => true),
+                'photo_uri' => array('type' => 'varchar',
+                               'length' => 255,
+                               'not null' => true),
+                'profile_id' => array('type' => 'int', 'not null' => true),
+            ),
+            'primary key' => array('id'),
+            'foreign keys' => array('photo_profile_id__key' => array('profile' => array('profile_id' => 'id'))),
+        );
+    }
+
+    function saveNew($profile, $photo_uri, $thumb_uri, $title, $description, $options=array())
+    {
+        $photo = new Photo();
+
+        $photo->id =  UUID::gen();
+        $photo->profile_id = $profile->id;
+        $photo->photo_uri = $photo_uri;
+        $photo->thumb_uri = $thumb_uri;
+
+
+        $options['object_type'] = Photo::OBJECT_TYPE;
+
+        if (!array_key_exists('uri', $options)) { 
+            $options['uri'] = common_local_url('showphoto', array('id' => $photo->id));
+        }
+
+        if (!array_key_exists('rendered', $options)) {
+            $options['rendered'] = sprintf("<img src=\"%s\" alt=\"%s\"></img>", $photo_uri,
+                $title);
+        }
+
+        $photo->uri = $options['uri'];
+        
+        $photo->insert();
+
+        return Notice::saveNew($profile->id,
+                               '',
+                               'web',
+                               $options);
+
+    }
+}
diff --git a/plugins/GNUsocialPhoto/forms/newphoto.php b/plugins/GNUsocialPhoto/forms/newphoto.php
new file mode 100644 (file)
index 0000000..4ef51ca
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+/**
+ * GNU Social
+ * Copyright (C) 2011, Free Software Foundation, Inc.
+ *
+ * PHP version 5
+ *
+ * LICENCE:
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   GNU Social
+ * @author    Ian Denhardt <ian@zenhack.net>
+ * @copyright 2011 Free Software Foundation, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ */
+
+if(!defined('STATUSNET')){
+    exit(1);
+}
+
+class NewPhotoForm extends Form
+{
+    function id()
+    {
+        return "form_new_photo";
+    }
+
+    function action()
+    {
+        return common_local_url('newphoto');
+    }
+
+    function formClass()
+    {
+        return 'form_settings ajax-notice';
+    }
+
+    function formData()
+    {
+        $this->out->elementStart('fieldset', array('id' => 'new_photo_data'));
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->out->input('title', _('Title'), null, _('Photo title (optional).'));
+        $this->unli();
+
+        $this->li();
+        $this->out->element('input', array('name' => 'photo_upload',
+                                           'type' => 'file',
+                                           'id' => 'photo_upload'));
+        $this->unli();
+
+        $this->li();
+        $this->textarea('description', _('Description'), null, _('Description of the photo (optional).'));
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+
+        $toWidget = new ToSelector($this->out,
+                                   common_current_user(),
+                                   null);
+        $toWidget->show();
+
+        $this->out->elementEnd('fieldset');
+    }
+
+    function formActions()
+    {
+        $this->out->submit('photo-submit', _m('BUTTON', 'Save'));
+    }
+}
diff --git a/plugins/GNUsocialPhoto/newphoto.php b/plugins/GNUsocialPhoto/newphoto.php
deleted file mode 100644 (file)
index 18ae552..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-<?php
-/**
- * GNU Social
- * Copyright (C) 2011, Free Software Foundation, Inc.
- *
- * PHP version 5
- *
- * LICENCE:
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   GNU Social
- * @author    Ian Denhardt <ian@zenhack.net>
- * @copyright 2011 Free Software Foundation, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- */
-
-if(!defined('STATUSNET')){
-    exit(1);
-}
-
-class NewphotoAction extends Action
-{
-    var $user = null;
-
-    function prepare($args)
-    {
-        parent::prepare($args);
-        $this->user = common_current_user();
-
-        if(empty($this->user)){
-            throw new ClientException(_('Must be logged in to post a photo'),
-                403);
-        }
-
-        if($this->isPost()){
-            $this->checkSessionToken();
-        }
-
-        return true;
-    }
-
-    function handle($args)
-    {
-        parent::handle($args);
-
-        if ($this->isPost()) {
-            $this->handlePost($args);
-        } else {
-            $this->showPage();
-        }
-    }
-    
-    function handlePost($args)
-    {
-
-        /*
-        // Workaround for PHP returning empty $_POST and $_FILES when POST
-        // length > post_max_size in php.ini
-        if (empty($_FILES)
-            && empty($_POST)
-            && ($_SERVER['CONTENT_LENGTH'] > 0)
-        ) {
-            $msg = _('The server was unable to handle that much POST ' .
-                'data (%s bytes) due to its current configuration.');
-            $this->showForm(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
-            return;
-        } */
-
-        $profile = $this->user->getProfile();
-
-        $options = array();
-        
-        ToSelector::fillOptions($this, $options);
-
-        try {
-            $this->handleUpload();
-        } catch (Exception $e) {
-            $this->showForm($e->getMessage());
-            return;
-        }
-        
-
-        common_redirect($photo->uri, 303);
-    }
-
-    function getUpload()
-    {
-            $imagefile = ImageFile::fromUpload('photo_upload');
-
-            if($imagefile === null) {
-                throw new Exception(_('No file uploaded'));
-            }
-
-            $title = $this->trimmed('title');
-            $description = $this->trimmed('description');
-
-            $new_filename = UUID::gen() . image_type_to_extension($imagefile->type);
-            move_uploaded_file($imagefile->filepath, INSTALLDIR . '/file/' . $new_filename);
-           
-            // XXX: we should be using https where we can. TODO: detect whether the server
-            // supports this.
-            $photo_uri = 'http://' . common_config('site', 'server') . '/file/'
-                . $new_filename;
-            $thumb_uri = $photo_uri;
-
-            $photo = Photo::saveNew($profile, $photo_uri, $thumb_uri, $title,
-                $description, $options);
-        
-    }
-
-    function showContent()
-    {
-        $form = new NewPhotoForm();
-        $form->show();
-    }
-}
-
-
diff --git a/plugins/GNUsocialPhoto/newphotoform.php b/plugins/GNUsocialPhoto/newphotoform.php
deleted file mode 100644 (file)
index 4ef51ca..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-/**
- * GNU Social
- * Copyright (C) 2011, Free Software Foundation, Inc.
- *
- * PHP version 5
- *
- * LICENCE:
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   GNU Social
- * @author    Ian Denhardt <ian@zenhack.net>
- * @copyright 2011 Free Software Foundation, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- */
-
-if(!defined('STATUSNET')){
-    exit(1);
-}
-
-class NewPhotoForm extends Form
-{
-    function id()
-    {
-        return "form_new_photo";
-    }
-
-    function action()
-    {
-        return common_local_url('newphoto');
-    }
-
-    function formClass()
-    {
-        return 'form_settings ajax-notice';
-    }
-
-    function formData()
-    {
-        $this->out->elementStart('fieldset', array('id' => 'new_photo_data'));
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-        $this->out->input('title', _('Title'), null, _('Photo title (optional).'));
-        $this->unli();
-
-        $this->li();
-        $this->out->element('input', array('name' => 'photo_upload',
-                                           'type' => 'file',
-                                           'id' => 'photo_upload'));
-        $this->unli();
-
-        $this->li();
-        $this->textarea('description', _('Description'), null, _('Description of the photo (optional).'));
-        $this->unli();
-
-        $this->out->elementEnd('ul');
-
-        $toWidget = new ToSelector($this->out,
-                                   common_current_user(),
-                                   null);
-        $toWidget->show();
-
-        $this->out->elementEnd('fieldset');
-    }
-
-    function formActions()
-    {
-        $this->out->submit('photo-submit', _m('BUTTON', 'Save'));
-    }
-}
diff --git a/plugins/GNUsocialPhoto/showphoto.php b/plugins/GNUsocialPhoto/showphoto.php
deleted file mode 100644 (file)
index b97a056..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-/**
- * GNU Social
- * Copyright (C) 2011, Free Software Foundation, Inc.
- *
- * PHP version 5
- *
- * LICENCE:
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   GNU Social
- * @author    Ian Denhardt <ian@zenhack.net>
- * @copyright 2011 Free Software Foundation, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- */
-
-if(!defined('STATUSNET')){
-    exit(1);
-}
-
-
index 3a7e8822364fe200a3671931fd9d9cf6c842dd8c..235155192780191ff80e794cbb2546439882c248 100644 (file)
@@ -33,41 +33,10 @@ if (!defined('STATUSNET')) {
     exit(1);
 }
 
+include_once $dir . '/lib/photolib.php';
+
 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();
diff --git a/plugins/GNUsocialPhotos/lib/gnusocialphotonav.php b/plugins/GNUsocialPhotos/lib/gnusocialphotonav.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/gnusocialphototemp.php b/plugins/GNUsocialPhotos/lib/gnusocialphototemp.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/lib/photonav.php b/plugins/GNUsocialPhotos/lib/photonav.php
deleted file mode 100644 (file)
index 180f0ad..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-<?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
deleted file mode 100644 (file)
index 119c301..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<?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; 
-}
index e6c961f86528960da44bcee2dfcbc170a6e48e86..bfdcf3e42db05315c2be9c9d8122db1703e2097f 100644 (file)
@@ -30,32 +30,11 @@ if (!defined('STATUSNET')) {
     exit(1);
 }
 
+include_once $dir . '/lib/profiletools.php';
+
 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();
index 296b45122310c01b51133831e68df648ddf0e278..7d1b98a63f33fb84543ff58724774891298fe133 100644 (file)
@@ -42,29 +42,6 @@ class GNUsocialVideoPlugin extends MicroAppPlugin
         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'));
diff --git a/plugins/GNUsocialVideo/Video.php b/plugins/GNUsocialVideo/Video.php
deleted file mode 100644 (file)
index 0311ee5..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-/**
- * GNU Social
- * Copyright (C) 2011, Free Software Foundation, Inc.
- *
- * PHP version 5
- *
- * LICENCE:
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   GNU Social
- * @author    Ian Denhardt <ian@zenhack.net>
- * @copyright 2011 Free Software Foundation, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- */
-
-if(!defined('STATUSNET')){
-    exit(1);
-}
-
-/**
- * Data class for videos.
- */
-
-class Video extends Managed_DataObject
-{
-    const OBJECT_TYPE = 'http://activitystrea.ms/schema/1.0/video';
-
-    public $__table = 'video'; // table name
-    public $id;                // char (36) // UUID
-    public $uri;               // varchar (255)  // This is the corresponding notice's uri.
-    public $url;               // varchar (255)
-    public $profile_id;        // int
-
-    public static function getByNotice($notice)
-    {
-        return self::getKV('uri', $notice->uri);
-    }
-
-    public function getNotice()
-    {
-        return Notice::getKV('uri', $this->uri);
-    }
-
-    public static function schemaDef()
-    {
-        return array(
-            'description' => 'A video clip',
-            'fields' => array(
-                'id' => array('type' => 'char',
-                              'length' => 36,
-                              'not null' => true,
-                              'description' => 'UUID'),
-                'uri' => array('type' => 'varchar',
-                               'length' => 255,
-                               'not null' => true),
-                'url' => array('type' => 'varchar',
-                               'length' => 255,
-                               'not null' => true),
-                'profile_id' => array('type' => 'int', 'not null' => true),
-            ),
-            'primary key' => array('id'),
-            'foreign keys' => array('video_profile_id__key' => array('profile' => array('profile_id' => 'id'))),
-        );
-    }
-
-    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/showvideo.php b/plugins/GNUsocialVideo/actions/showvideo.php
new file mode 100644 (file)
index 0000000..628d086
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**
+ * GNU Social
+ * Copyright (C) 2011, Free Software Foundation, Inc.
+ *
+ * PHP version 5
+ *
+ * LICENCE:
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   GNU Social
+ * @author    Ian Denhardt <ian@zenhack.net>
+ * @copyright 2011 Free Software Foundation, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ */
+
+if(!defined('STATUSNET')){
+    exit(1);
+}
+
+class ShowvideoAction extends ShownoticeAction
+{
+    protected $id = null;
+    protected $vid = null;
+
+    function prepare($args)
+    {
+        OwnerDesignAction::prepare($args);
+        $this->id = $this->trimmed('id');
+        $this->vid = Video::getKV('id', $this->id);
+
+        if (empty($this->vid)) {
+            throw new ClientException(_('No such video.'), 404);
+        }
+
+        $this->notice = $this->vid->getNotice();
+
+        if (empty($this->notice)) {
+            throw new ClientException(_('No such video'), 404);
+        }
+
+        $this->user = User::getKV('id', $this->vid->profile_id);
+
+        if (empty($this->user)) {
+            throw new ClientException(_('No such user.'), 404);
+        }
+
+        $this->profile = $this->user->getProfile();
+
+        if (empty($this->profile)) {
+            throw new ServerException(_('User without a profile.'));
+        }
+
+        return true;
+    }
+}
diff --git a/plugins/GNUsocialVideo/classes/Video.php b/plugins/GNUsocialVideo/classes/Video.php
new file mode 100644 (file)
index 0000000..0311ee5
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+/**
+ * GNU Social
+ * Copyright (C) 2011, Free Software Foundation, Inc.
+ *
+ * PHP version 5
+ *
+ * LICENCE:
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   GNU Social
+ * @author    Ian Denhardt <ian@zenhack.net>
+ * @copyright 2011 Free Software Foundation, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ */
+
+if(!defined('STATUSNET')){
+    exit(1);
+}
+
+/**
+ * Data class for videos.
+ */
+
+class Video extends Managed_DataObject
+{
+    const OBJECT_TYPE = 'http://activitystrea.ms/schema/1.0/video';
+
+    public $__table = 'video'; // table name
+    public $id;                // char (36) // UUID
+    public $uri;               // varchar (255)  // This is the corresponding notice's uri.
+    public $url;               // varchar (255)
+    public $profile_id;        // int
+
+    public static function getByNotice($notice)
+    {
+        return self::getKV('uri', $notice->uri);
+    }
+
+    public function getNotice()
+    {
+        return Notice::getKV('uri', $this->uri);
+    }
+
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'A video clip',
+            'fields' => array(
+                'id' => array('type' => 'char',
+                              'length' => 36,
+                              'not null' => true,
+                              'description' => 'UUID'),
+                'uri' => array('type' => 'varchar',
+                               'length' => 255,
+                               'not null' => true),
+                'url' => array('type' => 'varchar',
+                               'length' => 255,
+                               'not null' => true),
+                'profile_id' => array('type' => 'int', 'not null' => true),
+            ),
+            'primary key' => array('id'),
+            'foreign keys' => array('video_profile_id__key' => array('profile' => array('profile_id' => 'id'))),
+        );
+    }
+
+    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/forms/video.php b/plugins/GNUsocialVideo/forms/video.php
new file mode 100644 (file)
index 0000000..12f4563
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+/**
+ * GNU Social
+ * Copyright (C) 2011, Free Software Foundation, Inc.
+ *
+ * PHP version 5
+ *
+ * LICENCE:
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   GNU Social
+ * @author    Ian Denhardt <ian@zenhack.net>
+ * @copyright 2011 Free Software Foundation, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ */
+
+if(!defined('STATUSNET')){
+    exit(1);
+}
+
+class VideoForm extends Form
+{
+    function id()
+    {
+        return "form_new_video";
+    }
+
+    function action()
+    {
+        return common_local_url('postvideo');
+    }
+
+    function formClass()
+    {
+        return 'form_settings ajax-notice';
+    }
+
+    function formData()
+    {
+        $this->out->elementStart('fieldset', array('id' => 'new_video_data'));
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->out->input('url', _('URL'), null, _('URL of the video'));
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+
+        $toWidget = new ToSelector($this->out,
+                                   common_current_user(),
+                                   null);
+        $toWidget->show();
+
+        $this->out->elementEnd('fieldset');
+    }
+
+    function formActions()
+    {
+        $this->out->submit('submit', _m('BUTTON', 'Save'));
+    }
+}
diff --git a/plugins/GNUsocialVideo/showvideo.php b/plugins/GNUsocialVideo/showvideo.php
deleted file mode 100644 (file)
index 628d086..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/**
- * GNU Social
- * Copyright (C) 2011, Free Software Foundation, Inc.
- *
- * PHP version 5
- *
- * LICENCE:
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   GNU Social
- * @author    Ian Denhardt <ian@zenhack.net>
- * @copyright 2011 Free Software Foundation, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- */
-
-if(!defined('STATUSNET')){
-    exit(1);
-}
-
-class ShowvideoAction extends ShownoticeAction
-{
-    protected $id = null;
-    protected $vid = null;
-
-    function prepare($args)
-    {
-        OwnerDesignAction::prepare($args);
-        $this->id = $this->trimmed('id');
-        $this->vid = Video::getKV('id', $this->id);
-
-        if (empty($this->vid)) {
-            throw new ClientException(_('No such video.'), 404);
-        }
-
-        $this->notice = $this->vid->getNotice();
-
-        if (empty($this->notice)) {
-            throw new ClientException(_('No such video'), 404);
-        }
-
-        $this->user = User::getKV('id', $this->vid->profile_id);
-
-        if (empty($this->user)) {
-            throw new ClientException(_('No such user.'), 404);
-        }
-
-        $this->profile = $this->user->getProfile();
-
-        if (empty($this->profile)) {
-            throw new ServerException(_('User without a profile.'));
-        }
-
-        return true;
-    }
-}
diff --git a/plugins/GNUsocialVideo/videoform.php b/plugins/GNUsocialVideo/videoform.php
deleted file mode 100644 (file)
index 12f4563..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-/**
- * GNU Social
- * Copyright (C) 2011, Free Software Foundation, Inc.
- *
- * PHP version 5
- *
- * LICENCE:
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   GNU Social
- * @author    Ian Denhardt <ian@zenhack.net>
- * @copyright 2011 Free Software Foundation, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- */
-
-if(!defined('STATUSNET')){
-    exit(1);
-}
-
-class VideoForm extends Form
-{
-    function id()
-    {
-        return "form_new_video";
-    }
-
-    function action()
-    {
-        return common_local_url('postvideo');
-    }
-
-    function formClass()
-    {
-        return 'form_settings ajax-notice';
-    }
-
-    function formData()
-    {
-        $this->out->elementStart('fieldset', array('id' => 'new_video_data'));
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-        $this->out->input('url', _('URL'), null, _('URL of the video'));
-        $this->unli();
-
-        $this->out->elementEnd('ul');
-
-        $toWidget = new ToSelector($this->out,
-                                   common_current_user(),
-                                   null);
-        $toWidget->show();
-
-        $this->out->elementEnd('fieldset');
-    }
-
-    function formActions()
-    {
-        $this->out->submit('submit', _m('BUTTON', 'Save'));
-    }
-}
index 27ce289c2e4f9242a87898490abe8886c751ab70..ee541f2dc04d140ca00d2a3be00583bd37f9230b 100644 (file)
@@ -41,27 +41,6 @@ class GroupFavoritedPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Automatically load the actions and libraries used by the plugin
-     *
-     * @param Class $cls the class
-     *
-     * @return boolean hook return
-     *
-     */
-    function onAutoload($cls)
-    {
-        $base = dirname(__FILE__);
-        $lower = strtolower($cls);
-        switch ($lower) {
-        case 'groupfavoritedaction':
-            require_once "$base/$lower.php";
-            return false;
-        default:
-            return true;
-        }
-    }
-
     function onEndGroupGroupNav(GroupNav $nav)
     {
         $action_name = $nav->action->trimmed('action');
diff --git a/plugins/GroupFavorited/actions/groupfavorited.php b/plugins/GroupFavorited/actions/groupfavorited.php
new file mode 100644 (file)
index 0000000..dcbf7d0
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * List of popular notices
+ *
+ * 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  Public
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+class GroupFavoritedAction extends ShowgroupAction
+{
+    /**
+     * Title of the page
+     *
+     * @return string page title, with page number
+     */
+    function title()
+    {
+        $base = $this->group->getFancyName();
+
+        if ($this->page == 1) {
+            // TRANS: %s is a group name.
+            return sprintf(_m('Popular posts in %s group'), $base);
+        } else {
+            // TRANS: %1$s is a group name, %2$s is a group number.
+            return sprintf(_m('Popular posts in %1$s group, page %2$d'),
+                           $base,
+                           $this->page);
+        }
+    }
+
+    /**
+     * Content area
+     *
+     * Shows the list of popular notices
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        $groupId = intval($this->group->id);
+        $weightexpr = common_sql_weight('fave.modified', common_config('popular', 'dropoff'));
+        $cutoff = sprintf("fave.modified > '%s'",
+                          common_sql_date(time() - common_config('popular', 'cutoff')));
+
+        $qry = 'SELECT notice.*, '.
+          $weightexpr . ' as weight ' .
+          'FROM notice ' .
+          "JOIN group_inbox ON notice.id = group_inbox.notice_id " .
+          'JOIN fave ON notice.id = fave.notice_id ' .
+          "WHERE $cutoff AND group_id = $groupId " .
+          'GROUP BY id,profile_id,uri,content,rendered,url,created,notice.modified,reply_to,is_local,source,notice.conversation ' .
+          'ORDER BY weight DESC';
+
+        $offset = ($this->page - 1) * NOTICES_PER_PAGE;
+        $limit  = NOTICES_PER_PAGE + 1;
+
+        if (common_config('db', 'type') == 'pgsql') {
+            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
+        } else {
+            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
+        }
+
+        $notice = Memcached_DataObject::cachedQuery('Notice',
+                                                    $qry,
+                                                    600);
+
+        $nl = new NoticeList($notice, $this);
+
+        $cnt = $nl->show();
+
+        if ($cnt == 0) {
+            //$this->showEmptyList();
+        }
+
+        $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
+                          $this->page, 'groupfavorited',
+                          array('nickname' => $this->group->nickname));
+    }
+}
diff --git a/plugins/GroupFavorited/groupfavoritedaction.php b/plugins/GroupFavorited/groupfavoritedaction.php
deleted file mode 100644 (file)
index dcbf7d0..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * List of popular notices
- *
- * 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  Public
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-class GroupFavoritedAction extends ShowgroupAction
-{
-    /**
-     * Title of the page
-     *
-     * @return string page title, with page number
-     */
-    function title()
-    {
-        $base = $this->group->getFancyName();
-
-        if ($this->page == 1) {
-            // TRANS: %s is a group name.
-            return sprintf(_m('Popular posts in %s group'), $base);
-        } else {
-            // TRANS: %1$s is a group name, %2$s is a group number.
-            return sprintf(_m('Popular posts in %1$s group, page %2$d'),
-                           $base,
-                           $this->page);
-        }
-    }
-
-    /**
-     * Content area
-     *
-     * Shows the list of popular notices
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        $groupId = intval($this->group->id);
-        $weightexpr = common_sql_weight('fave.modified', common_config('popular', 'dropoff'));
-        $cutoff = sprintf("fave.modified > '%s'",
-                          common_sql_date(time() - common_config('popular', 'cutoff')));
-
-        $qry = 'SELECT notice.*, '.
-          $weightexpr . ' as weight ' .
-          'FROM notice ' .
-          "JOIN group_inbox ON notice.id = group_inbox.notice_id " .
-          'JOIN fave ON notice.id = fave.notice_id ' .
-          "WHERE $cutoff AND group_id = $groupId " .
-          'GROUP BY id,profile_id,uri,content,rendered,url,created,notice.modified,reply_to,is_local,source,notice.conversation ' .
-          'ORDER BY weight DESC';
-
-        $offset = ($this->page - 1) * NOTICES_PER_PAGE;
-        $limit  = NOTICES_PER_PAGE + 1;
-
-        if (common_config('db', 'type') == 'pgsql') {
-            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
-        } else {
-            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
-        }
-
-        $notice = Memcached_DataObject::cachedQuery('Notice',
-                                                    $qry,
-                                                    600);
-
-        $nl = new NoticeList($notice, $this);
-
-        $cnt = $nl->show();
-
-        if ($cnt == 0) {
-            //$this->showEmptyList();
-        }
-
-        $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
-                          $this->page, 'groupfavorited',
-                          array('nickname' => $this->group->nickname));
-    }
-}
index 467f384f4d60ccd07743c67323e835e3c15d191a..1cbf3c31f1f557606e27152b791e18685787b0ed 100644 (file)
@@ -67,40 +67,6 @@ class GroupPrivateMessagePlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'GroupinboxAction':
-        case 'ShowgroupmessageAction':
-        case 'NewgroupmessageAction':
-            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'Group_privacy_settings':
-        case 'Group_message':
-        case 'Group_message_profile':
-            include_once $dir . '/'.$cls.'.php';
-            return false;
-        case 'GroupMessageCommand':
-        case 'GroupMessageList':
-        case 'GroupMessageListItem':
-        case 'GroupMessageForm':
-            include_once $dir . '/'.strtolower($cls).'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Map URLs to actions
      *
diff --git a/plugins/GroupPrivateMessage/Group_message.php b/plugins/GroupPrivateMessage/Group_message.php
deleted file mode 100644 (file)
index 7e825d4..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-<?php
-/**
- * Data class for group direct messages
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for group direct messages
- *
- * @category GroupPrivateMessage
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Group_message extends Managed_DataObject
-{
-    public $__table = 'group_message'; // table name
-    public $id;                        // char(36)  primary_key not_null
-    public $uri;                       // varchar(255)
-    public $from_profile;              // int
-    public $to_group;                  // int
-    public $content;
-    public $rendered;
-    public $url;
-    public $created;                   // datetime()   not_null
-    public $modified;                  // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'id' => array('type' => 'char', 'not null' => true, 'length' => 36, 'description' => 'message uuid'),
-                'uri' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'message uri'),
-                'url' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'representation url'),
-                'from_profile' => array('type' => 'int', 'not null' => true, 'description' => 'sending profile ID'),
-                'to_group' => array('type' => 'int', 'not null' => true, 'description' => 'receiving group ID'),
-                'content' => array('type' => 'text', 'not null' => true, 'description' => 'message content'),
-                'rendered' => array('type' => 'text', 'not null' => true, 'description' => 'rendered message'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('id'),
-            'unique keys' => array(
-                'group_message_uri_key' => array('uri'),
-            ),
-            'foreign keys' => array(
-                'group_message_from_profile_fkey' => array('profile', array('from_profile' => 'id')),
-                'group_message_to_group_fkey' => array('user_group', array('to_group' => 'id')),
-            ),
-            'indexes' => array(
-                'group_message_from_profile_idx' => array('from_profile'),
-                'group_message_to_group_idx' => array('to_group'),
-                'group_message_url_idx' => array('url'),
-            ),
-        );
-    }
-
-    static function send($user, $group, $text)
-    {
-        if (!$user->hasRight(Right::NEWMESSAGE)) {
-            // XXX: maybe break this out into a separate right
-            // TRANS: Exception thrown when trying to send group private message without having the right to do that.
-            // TRANS: %s is a user nickname.
-            throw new Exception(sprintf(_m('User %s is not allowed to send private messages.'),
-                                        $user->nickname));
-        }
-
-        Group_privacy_settings::ensurePost($user, $group);
-
-        $text = $user->shortenLinks($text);
-
-        // We use the same limits as for 'regular' private messages.
-
-        if (Message::contentTooLong($text)) {
-            // TRANS: Exception thrown when trying to send group private message that is too long.
-            // TRANS: %d is the maximum meggage length.
-            throw new Exception(sprintf(_m('That\'s too long. Maximum message size is %d character.',
-                                           'That\'s too long. Maximum message size is %d characters.',
-                                           Message::maxContent()),
-                                        Message::maxContent()));
-        }
-
-        // Valid! Let's do this thing!
-
-        $gm = new Group_message();
-
-        $gm->id           = UUID::gen();
-        $gm->uri          = common_local_url('showgroupmessage', array('id' => $gm->id));
-        $gm->from_profile = $user->id;
-        $gm->to_group     = $group->id;
-        $gm->content      = $text; // XXX: is this cool?!
-        $gm->rendered     = common_render_text($text);
-        $gm->url          = $gm->uri;
-        $gm->created      = common_sql_now();
-
-        // This throws a conniption if there's a problem
-
-        $gm->insert();
-
-        $gm->distribute();
-
-        return $gm;
-    }
-
-    function distribute()
-    {
-        $group = User_group::getKV('id', $this->to_group);
-
-        $member = $group->getMembers();
-
-        while ($member->fetch()) {
-            Group_message_profile::send($this, $member);
-        }
-    }
-
-    function getGroup()
-    {
-        $group = User_group::getKV('id', $this->to_group);
-        if (empty($group)) {
-            // TRANS: Exception thrown when trying to send group private message to a non-existing group.
-            throw new ServerException(_m('No group for group message.'));
-        }
-        return $group;
-    }
-
-    function getSender()
-    {
-        $sender = Profile::getKV('id', $this->from_profile);
-        if (empty($sender)) {
-            // TRANS: Exception thrown when trying to send group private message without having a sender.
-            throw new ServerException(_m('No sender for group message.'));
-        }
-        return $sender;
-    }
-
-    static function forGroup($group, $offset, $limit)
-    {
-        // XXX: cache
-        $gm = new Group_message();
-
-        $gm->to_group = $group->id;
-        $gm->orderBy('created DESC');
-        $gm->limit($offset, $limit);
-
-        $gm->find();
-
-        return $gm;
-    }
-}
diff --git a/plugins/GroupPrivateMessage/Group_message_profile.php b/plugins/GroupPrivateMessage/Group_message_profile.php
deleted file mode 100644 (file)
index ad465b9..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-/**
- * Who received a group message
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for group direct messages for users
- *
- * @category GroupPrivateMessage
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Group_message_profile extends Managed_DataObject
-{
-    public $__table = 'group_message_profile'; // table name
-    public $to_profile;                        // int
-    public $group_message_id;                  // varchar(36)  primary_key not_null
-    public $created;                           // datetime()   not_null
-    public $modified;                          // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'to_profile' => array('type' => 'int', 'not null' => true, 'description' => 'id of group direct message'),
-                'group_message_id' => array('type' => 'char', 'not null' => true, 'length' => 36, 'description' => 'related group message uuid'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('to_profile', 'group_message_id'),
-            'foreign keys' => array(
-                'group_message_profile_to_profile_fkey' => array('profile', array('to_profile' => 'id')),
-                'group_message_profile_group_message_id_fkey' => array('group_message', array('group_message_id' => 'id')),
-            ),
-        );
-    }
-
-    function send($gm, $profile)
-    {
-        $gmp = new Group_message_profile();
-
-        $gmp->group_message_id = $gm->id;
-        $gmp->to_profile       = $profile->id;
-        $gmp->created          = common_sql_now();
-
-        $gmp->insert();
-
-        // If it's not for the author, send email notification
-        if ($gm->from_profile != $profile->id) {
-            $gmp->notify();
-        }
-
-        return $gmp;
-    }
-
-    function notify()
-    {
-        // XXX: add more here
-        $this->notifyByMail();
-    }
-
-    function notifyByMail()
-    {
-        $to = User::getKV('id', $this->to_profile);
-
-        if (empty($to) || is_null($to->email) || !$to->emailnotifymsg) {
-            return true;
-        }
-
-        $gm = Group_message::getKV('id', $this->group_message_id);
-
-        $from_profile = Profile::getKV('id', $gm->from_profile);
-
-        $group = $gm->getGroup();
-
-        common_switch_locale($to->language);
-
-        // TRANS: Subject for direct-message notification email.
-        // TRANS: %1$s is the sending user's nickname, %2$s is the group nickname.
-        $subject = sprintf(_m('New private message from %1$s to group %2$s'), $from_profile->nickname, $group->nickname);
-
-        // TRANS: Body for direct-message notification email.
-        // TRANS: %1$s is the sending user's long name, %2$s is the sending user's nickname,
-        // TRANS: %3$s is the message content, %4$s a URL to the message,
-        // TRANS: %5$s is the StatusNet sitename.
-        $body = sprintf(_m("%1\$s (%2\$s) sent a private message to group %3\$s:\n\n".
-                           "------------------------------------------------------\n".
-                           "%4\$s\n".
-                           "------------------------------------------------------\n\n".
-                           "You can reply to their message here:\n\n".
-                           "%5\$s\n\n".
-                           "Do not reply to this email; it will not get to them.\n\n".
-                           "With kind regards,\n".
-                           "%6\$s"),
-                        $from_profile->getBestName(),
-                        $from_profile->nickname,
-                        $group->nickname,
-                        $gm->content,
-                        common_local_url('newmessage', array('to' => $from_profile->id)),
-                        common_config('site', 'name')) . "\n";
-
-        $headers = _mail_prepare_headers('message', $to->nickname, $from_profile->nickname);
-
-        common_switch_locale();
-
-        return mail_to_user($to, $subject, $body, $headers);
-    }
-}
diff --git a/plugins/GroupPrivateMessage/Group_privacy_settings.php b/plugins/GroupPrivateMessage/Group_privacy_settings.php
deleted file mode 100644 (file)
index 9e983ba..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-<?php
-/**
- * Data class for group privacy settings
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Data class for group privacy
- *
- * Stores admin preferences about the group.
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Group_privacy_settings extends Managed_DataObject
-{
-    public $__table = 'group_privacy_settings';
-    /** ID of the group. */
-    public $group_id;
-    /** When to allow privacy: always, sometimes, or never. */
-    public $allow_privacy;
-    /** Who can send private messages: everyone, member, admin */
-    public $allow_sender;
-    /** row creation timestamp */
-    public $created;
-    /** Last-modified timestamp */
-    public $modified;
-
-    /** NEVER is */
-
-    const SOMETIMES = -1;
-    const NEVER  = 0;
-    const ALWAYS = 1;
-
-    /** These are bit-mappy, as a hedge against the future. */
-
-    const EVERYONE = 1;
-    const MEMBER   = 2;
-    const ADMIN    = 4;
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'group_id' => array('type' => 'int', 'not null' => true, 'description' => 'group_privacy_settings'),
-                'allow_privacy' => array('type' => 'int', 'not null' => true, 'description' => 'sometimes=-1, never=0, always=1'),
-                'allow_sender' => array('type' => 'int', 'not null' => true, 'description' => 'list of bit-mappy values in source code'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('group_id'),
-            'foreign keys' => array(
-                'group_privacy_settings_group_id_fkey' => array('user_group', array('group_id' => 'id')),
-            ),
-        );
-    }
-
-    function forGroup($group)
-    {
-        $gps = Group_privacy_settings::getKV('group_id', $group->id);
-
-        if (empty($gps)) {
-            // make a fake one with defaults
-            $gps = new Group_privacy_settings();
-            $gps->allow_privacy = Group_privacy_settings::SOMETIMES;
-            $gps->allow_sender  = Group_privacy_settings::MEMBER;
-        }
-
-        return $gps;
-    }
-
-    function ensurePost($user, $group)
-    {
-        $gps = self::forGroup($group);
-
-        if ($gps->allow_privacy == Group_privacy_settings::NEVER) {
-            // TRANS: Exception thrown when trying to set group privacy setting if group %s does not allow private messages.
-            throw new Exception(sprintf(_m('Group %s does not allow private messages.'),
-                                        $group->nickname));
-        }
-
-        switch ($gps->allow_sender) {
-        case Group_privacy_settings::EVERYONE:
-            $profile = $user->getProfile();
-            if (Group_block::isBlocked($group, $profile)) {
-                // TRANS: Exception thrown when trying to send group private message while blocked from that group.
-                // TRANS: %1$s is a user nickname, %2$s is a group nickname.
-                throw new Exception(sprintf(_m('User %1$s is blocked from group %2$s.'),
-                                            $user->nickname,
-                                            $group->nickname));
-            }
-            break;
-        case Group_privacy_settings::MEMBER:
-            if (!$user->isMember($group)) {
-                // TRANS: Exception thrown when trying to send group private message while not a member.
-                // TRANS: %1$s is a user nickname, %2$s is a group nickname.
-                throw new Exception(sprintf(_m('User %1$s is not a member of group %2$s.'),
-                                            $user->nickname,
-                                            $group->nickname));
-            }
-            break;
-        case Group_privacy_settings::ADMIN:
-            if (!$user->isAdmin($group)) {
-                // TRANS: Exception thrown when trying to send group private message while not a group administrator.
-                // TRANS: %1$s is a user nickname, %2$s is a group nickname.
-                throw new Exception(sprintf(_m('User %1$s is not an administrator of group %2$s.'),
-                                            $user->nickname,
-                                            $group->nickname));
-            }
-            break;
-        default:
-            // TRANS: Exception thrown when encountering undefined group privacy settings.
-            // TRANS: %s is a group nickname.
-            throw new Exception(sprintf(_m('Unknown privacy settings for group %s.'),
-                                        $group->nickname));
-        }
-
-        return true;
-    }
-}
diff --git a/plugins/GroupPrivateMessage/actions/groupinbox.php b/plugins/GroupPrivateMessage/actions/groupinbox.php
new file mode 100644 (file)
index 0000000..84952d0
--- /dev/null
@@ -0,0 +1,212 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * List of private messages to this group
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  GroupPrivateMessage
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Show a list of private messages to this group
+ *
+ * @category  GroupPrivateMessage
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class GroupinboxAction extends GroupAction
+{
+    var $gm;
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        $cur = common_current_user();
+
+        if (empty($cur)) {
+            // TRANS: Client exception thrown when trying to view group inbox while not logged in.
+            throw new ClientException(_m('Only for logged-in users.'), 403);
+        }
+
+        $nicknameArg = $this->trimmed('nickname');
+
+        $nickname = common_canonical_nickname($nicknameArg);
+
+        if ($nickname != $nicknameArg) {
+            $url = common_local_url('groupinbox', array('nickname' => $nickname));
+            common_redirect($url);
+            return false;
+        }
+
+        $localGroup = Local_group::getKV('nickname', $nickname);
+
+        if (empty($localGroup)) {
+            // TRANS: Client exception thrown when trying to view group inbox for non-existing group.
+            throw new ClientException(_m('No such group.'), 404);
+        }
+
+        $this->group = User_group::getKV('id', $localGroup->group_id);
+
+        if (empty($this->group)) {
+            // TRANS: Client exception thrown when trying to view group inbox for non-existing group.
+            throw new ClientException(_m('No such group.'), 404);
+        }
+
+        if (!$cur->isMember($this->group)) {
+            // TRANS: Client exception thrown when trying to view group inbox while not a member.
+            throw new ClientException(_m('Only for members.'), 403);
+        }
+
+        $this->page = $this->trimmed('page');
+
+        if (!$this->page) {
+            $this->page = 1;
+        }
+
+        $this->gm = Group_message::forGroup($this->group,
+                                            ($this->page - 1) * MESSAGES_PER_PAGE,
+                                            MESSAGES_PER_PAGE + 1);
+        return true;
+    }
+
+    function showLocalNav()
+    {
+        $nav = new GroupNav($this, $this->group);
+        $nav->show();
+    }
+
+    function showNoticeForm()
+    {
+        $form = new GroupMessageForm($this, $this->group);
+        $form->show();
+    }
+
+    function showContent()
+    {
+        $gml = new GroupMessageList($this, $this->gm);
+        $cnt = $gml->show();
+
+        if ($cnt == 0) {
+            // TRANS: Text of group inbox if no private messages were sent to it.
+            $this->element('p', 'guide', _m('This group has not received any private messages.'));
+        }
+        $this->pagination($this->page > 1,
+                          $cnt > MESSAGES_PER_PAGE,
+                          $this->page,
+                          'groupinbox',
+                          array('nickname' => $this->group->nickname));
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        $this->showPage();
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        return true;
+    }
+
+    /**
+     * Title of the page
+     *
+     * @return string page title, with page number
+     */
+    function title()
+    {
+        $base = $this->group->getFancyName();
+
+        if ($this->page == 1) {
+            // TRANS: Title of inbox for group %s.
+            return sprintf(_m('%s group inbox'), $base);
+        } else {
+            // TRANS: Page title for any but first group page.
+            // TRANS: %1$s is a group name, $2$s is a page number.
+            return sprintf(_m('%1$s group inbox, page %2$d'),
+                           $base,
+                           $this->page);
+        }
+    }
+
+    /**
+     * Show the page notice
+     *
+     * Shows instructions for the page
+     *
+     * @return void
+     */
+    function showPageNotice()
+    {
+        $instr  = $this->getInstructions();
+        $output = common_markup_to_html($instr);
+
+        $this->elementStart('div', 'instructions');
+        $this->raw($output);
+        $this->elementEnd('div');
+    }
+
+    /**
+     * Instructions for using this page
+     *
+     * @return string localised instructions for using the page
+     */
+    function getInstructions()
+    {
+        // TRANS: Instructions for user inbox page.
+        return _m('This is the group inbox, which lists all incoming private messages for this group.');
+    }
+}
diff --git a/plugins/GroupPrivateMessage/actions/newgroupmessage.php b/plugins/GroupPrivateMessage/actions/newgroupmessage.php
new file mode 100644 (file)
index 0000000..37738a3
--- /dev/null
@@ -0,0 +1,163 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Action for adding a new group message
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Cache
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Action for adding a new group message
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class NewgroupmessageAction extends Action
+{
+    var $group;
+    var $user;
+    var $text;
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        $this->user = common_current_user();
+
+        if (empty($this->user)) {
+            // TRANS: Client exception thrown when trying to send a private group message while not logged in.
+            throw new ClientException(_m('Must be logged in.'), 403);
+        }
+
+        if (!$this->user->hasRight(Right::NEWMESSAGE)) {
+            // TRANS: Exception thrown when user %s is not allowed to send a private group message.
+            throw new Exception(sprintf(_m('User %s is not allowed to send private messages.'),
+                                        $this->user->nickname));
+        }
+
+        $nicknameArg = $this->trimmed('nickname');
+
+        $nickname = common_canonical_nickname($nicknameArg);
+
+        if ($nickname != $nicknameArg) {
+            $url = common_local_url('newgroupmessage', array('nickname' => $nickname));
+            common_redirect($url, 301);
+            return false;
+        }
+
+        $localGroup = Local_group::getKV('nickname', $nickname);
+
+        if (empty($localGroup)) {
+            // TRANS: Client exception thrown when trying to send a private group message to a non-existing group.
+            throw new ClientException(_m('No such group.'), 404);
+        }
+
+        $this->group = User_group::getKV('id', $localGroup->group_id);
+
+        if (empty($this->group)) {
+            // TRANS: Client exception thrown when trying to send a private group message to a non-existing group.
+            throw new ClientException(_m('No such group.'), 404);
+        }
+
+        // This throws an exception on error
+        Group_privacy_settings::ensurePost($this->user, $this->group);
+
+        // If we're posted to, check session token and get text
+        if ($this->isPost()) {
+            $this->checkSessionToken();
+            $this->text = $this->trimmed('content');
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        if ($this->isPost()) {
+            $this->sendNewMessage();
+        } else {
+            $this->showPage();
+        }
+    }
+
+    function showNoticeForm()
+    {
+        $form = new GroupMessageForm($this, $this->group);
+        $form->show();
+    }
+
+    function sendNewMessage()
+    {
+        $gm = Group_message::send($this->user, $this->group, $this->text);
+
+        if ($this->boolean('ajax')) {
+            $this->startHTML('text/xml;charset=utf-8');
+            $this->elementStart('head');
+            // TRANS: Title after sending a private group message.
+            $this->element('title', null, _m('Message sent'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $this->element('p',
+                           array('id' => 'command_result'),
+                           // TRANS: Succes text after sending a direct message to group %s.
+                           sprintf(_m('Direct message to %s sent.'),
+                                   $this->group->nickname));
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            common_redirect($gm->url, 303);
+        }
+    }
+
+    function title()
+    {
+        // TRANS: Title of form for new private group message.
+        return sprintf(_m('New message to group %s'), $this->group->nickname);
+    }
+}
diff --git a/plugins/GroupPrivateMessage/actions/showgroupmessage.php b/plugins/GroupPrivateMessage/actions/showgroupmessage.php
new file mode 100644 (file)
index 0000000..8b99ece
--- /dev/null
@@ -0,0 +1,189 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Show a single group message
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  GroupPrivateMessage
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Show a single private group message
+ *
+ * @category  GroupPrivateMessage
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class ShowgroupmessageAction extends Action
+{
+    var $gm;
+    var $group;
+    var $sender;
+    var $user;
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        $this->user = common_current_user();
+
+        if (empty($this->user)) {
+            // TRANS: Client exception thrown when trying to view group private messages without being logged in.
+            throw new ClientException(_m('Only logged-in users can view private messages.'),
+                                      403);
+        }
+
+        $id = $this->trimmed('id');
+
+        $this->gm = Group_message::getKV('id', $id);
+
+        if (empty($this->gm)) {
+            // TRANS: Client exception thrown when trying to view a non-existing group private message.
+            throw new ClientException(_m('No such message.'), 404);
+        }
+
+        $this->group = User_group::getKV('id', $this->gm->to_group);
+
+        if (empty($this->group)) {
+            // TRANS: Server exception thrown when trying to view group private messages for a non-exsting group.
+            throw new ServerException(_m('Group not found.'));
+        }
+
+        if (!$this->user->isMember($this->group)) {
+            // TRANS: Client exception thrown when trying to view a group private message without being a group member.
+            throw new ClientException(_m('Cannot read message.'), 403);
+        }
+
+        $this->sender = Profile::getKV('id', $this->gm->from_profile);
+
+        if (empty($this->sender)) {
+            // TRANS: Server exception thrown when trying to view a group private message without a sender.
+            throw new ServerException(_m('No sender found.'));
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        $this->showPage();
+    }
+
+    /**
+     * Title of the page
+     */
+    function title()
+    {
+        // TRANS: Title for private group message.
+        // TRANS: %1$s is the sender name, %2$s is the group name, %3$s is a timestamp.
+        return sprintf(_m('Message from %1$s to group %2$s on %3$s'),
+                       $this->sender->nickname,
+                       $this->group->nickname,
+                       common_exact_date($this->gm->created));
+    }
+
+    /**
+     * Show the content area.
+     */
+    function showContent()
+    {
+        $this->elementStart('ul', 'notices messages');
+        $gmli = new GroupMessageListItem($this, $this->gm);
+        $gmli->show();
+        $this->elementEnd('ul');
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        return true;
+    }
+
+    /**
+     * Return last modified, if applicable.
+     *
+     * MAY override
+     *
+     * @return string last modified http header
+     */
+    function lastModified()
+    {
+        return max(strtotime($this->group->modified),
+                   strtotime($this->sender->modified),
+                   strtotime($this->gm->modified));
+    }
+
+    /**
+     * Return etag, if applicable.
+     *
+     * MAY override
+     *
+     * @return string etag http header
+     */
+    function etag()
+    {
+        $avatar = $this->sender->getAvatar(AVATAR_STREAM_SIZE);
+
+        $avtime = ($avatar) ? strtotime($avatar->modified) : 0;
+
+        return 'W/"' . implode(':', array($this->arg('action'),
+                                          common_user_cache_hash(),
+                                          common_language(),
+                                          $this->gm->id,
+                                          strtotime($this->sender->modified),
+                                          strtotime($this->group->modified),
+                                          $avtime)) . '"';
+    }
+}
diff --git a/plugins/GroupPrivateMessage/classes/Group_message.php b/plugins/GroupPrivateMessage/classes/Group_message.php
new file mode 100644 (file)
index 0000000..7e825d4
--- /dev/null
@@ -0,0 +1,181 @@
+<?php
+/**
+ * Data class for group direct messages
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for group direct messages
+ *
+ * @category GroupPrivateMessage
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Group_message extends Managed_DataObject
+{
+    public $__table = 'group_message'; // table name
+    public $id;                        // char(36)  primary_key not_null
+    public $uri;                       // varchar(255)
+    public $from_profile;              // int
+    public $to_group;                  // int
+    public $content;
+    public $rendered;
+    public $url;
+    public $created;                   // datetime()   not_null
+    public $modified;                  // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'id' => array('type' => 'char', 'not null' => true, 'length' => 36, 'description' => 'message uuid'),
+                'uri' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'message uri'),
+                'url' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'representation url'),
+                'from_profile' => array('type' => 'int', 'not null' => true, 'description' => 'sending profile ID'),
+                'to_group' => array('type' => 'int', 'not null' => true, 'description' => 'receiving group ID'),
+                'content' => array('type' => 'text', 'not null' => true, 'description' => 'message content'),
+                'rendered' => array('type' => 'text', 'not null' => true, 'description' => 'rendered message'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('id'),
+            'unique keys' => array(
+                'group_message_uri_key' => array('uri'),
+            ),
+            'foreign keys' => array(
+                'group_message_from_profile_fkey' => array('profile', array('from_profile' => 'id')),
+                'group_message_to_group_fkey' => array('user_group', array('to_group' => 'id')),
+            ),
+            'indexes' => array(
+                'group_message_from_profile_idx' => array('from_profile'),
+                'group_message_to_group_idx' => array('to_group'),
+                'group_message_url_idx' => array('url'),
+            ),
+        );
+    }
+
+    static function send($user, $group, $text)
+    {
+        if (!$user->hasRight(Right::NEWMESSAGE)) {
+            // XXX: maybe break this out into a separate right
+            // TRANS: Exception thrown when trying to send group private message without having the right to do that.
+            // TRANS: %s is a user nickname.
+            throw new Exception(sprintf(_m('User %s is not allowed to send private messages.'),
+                                        $user->nickname));
+        }
+
+        Group_privacy_settings::ensurePost($user, $group);
+
+        $text = $user->shortenLinks($text);
+
+        // We use the same limits as for 'regular' private messages.
+
+        if (Message::contentTooLong($text)) {
+            // TRANS: Exception thrown when trying to send group private message that is too long.
+            // TRANS: %d is the maximum meggage length.
+            throw new Exception(sprintf(_m('That\'s too long. Maximum message size is %d character.',
+                                           'That\'s too long. Maximum message size is %d characters.',
+                                           Message::maxContent()),
+                                        Message::maxContent()));
+        }
+
+        // Valid! Let's do this thing!
+
+        $gm = new Group_message();
+
+        $gm->id           = UUID::gen();
+        $gm->uri          = common_local_url('showgroupmessage', array('id' => $gm->id));
+        $gm->from_profile = $user->id;
+        $gm->to_group     = $group->id;
+        $gm->content      = $text; // XXX: is this cool?!
+        $gm->rendered     = common_render_text($text);
+        $gm->url          = $gm->uri;
+        $gm->created      = common_sql_now();
+
+        // This throws a conniption if there's a problem
+
+        $gm->insert();
+
+        $gm->distribute();
+
+        return $gm;
+    }
+
+    function distribute()
+    {
+        $group = User_group::getKV('id', $this->to_group);
+
+        $member = $group->getMembers();
+
+        while ($member->fetch()) {
+            Group_message_profile::send($this, $member);
+        }
+    }
+
+    function getGroup()
+    {
+        $group = User_group::getKV('id', $this->to_group);
+        if (empty($group)) {
+            // TRANS: Exception thrown when trying to send group private message to a non-existing group.
+            throw new ServerException(_m('No group for group message.'));
+        }
+        return $group;
+    }
+
+    function getSender()
+    {
+        $sender = Profile::getKV('id', $this->from_profile);
+        if (empty($sender)) {
+            // TRANS: Exception thrown when trying to send group private message without having a sender.
+            throw new ServerException(_m('No sender for group message.'));
+        }
+        return $sender;
+    }
+
+    static function forGroup($group, $offset, $limit)
+    {
+        // XXX: cache
+        $gm = new Group_message();
+
+        $gm->to_group = $group->id;
+        $gm->orderBy('created DESC');
+        $gm->limit($offset, $limit);
+
+        $gm->find();
+
+        return $gm;
+    }
+}
diff --git a/plugins/GroupPrivateMessage/classes/Group_message_profile.php b/plugins/GroupPrivateMessage/classes/Group_message_profile.php
new file mode 100644 (file)
index 0000000..ad465b9
--- /dev/null
@@ -0,0 +1,142 @@
+<?php
+/**
+ * Who received a group message
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for group direct messages for users
+ *
+ * @category GroupPrivateMessage
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Group_message_profile extends Managed_DataObject
+{
+    public $__table = 'group_message_profile'; // table name
+    public $to_profile;                        // int
+    public $group_message_id;                  // varchar(36)  primary_key not_null
+    public $created;                           // datetime()   not_null
+    public $modified;                          // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'to_profile' => array('type' => 'int', 'not null' => true, 'description' => 'id of group direct message'),
+                'group_message_id' => array('type' => 'char', 'not null' => true, 'length' => 36, 'description' => 'related group message uuid'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('to_profile', 'group_message_id'),
+            'foreign keys' => array(
+                'group_message_profile_to_profile_fkey' => array('profile', array('to_profile' => 'id')),
+                'group_message_profile_group_message_id_fkey' => array('group_message', array('group_message_id' => 'id')),
+            ),
+        );
+    }
+
+    function send($gm, $profile)
+    {
+        $gmp = new Group_message_profile();
+
+        $gmp->group_message_id = $gm->id;
+        $gmp->to_profile       = $profile->id;
+        $gmp->created          = common_sql_now();
+
+        $gmp->insert();
+
+        // If it's not for the author, send email notification
+        if ($gm->from_profile != $profile->id) {
+            $gmp->notify();
+        }
+
+        return $gmp;
+    }
+
+    function notify()
+    {
+        // XXX: add more here
+        $this->notifyByMail();
+    }
+
+    function notifyByMail()
+    {
+        $to = User::getKV('id', $this->to_profile);
+
+        if (empty($to) || is_null($to->email) || !$to->emailnotifymsg) {
+            return true;
+        }
+
+        $gm = Group_message::getKV('id', $this->group_message_id);
+
+        $from_profile = Profile::getKV('id', $gm->from_profile);
+
+        $group = $gm->getGroup();
+
+        common_switch_locale($to->language);
+
+        // TRANS: Subject for direct-message notification email.
+        // TRANS: %1$s is the sending user's nickname, %2$s is the group nickname.
+        $subject = sprintf(_m('New private message from %1$s to group %2$s'), $from_profile->nickname, $group->nickname);
+
+        // TRANS: Body for direct-message notification email.
+        // TRANS: %1$s is the sending user's long name, %2$s is the sending user's nickname,
+        // TRANS: %3$s is the message content, %4$s a URL to the message,
+        // TRANS: %5$s is the StatusNet sitename.
+        $body = sprintf(_m("%1\$s (%2\$s) sent a private message to group %3\$s:\n\n".
+                           "------------------------------------------------------\n".
+                           "%4\$s\n".
+                           "------------------------------------------------------\n\n".
+                           "You can reply to their message here:\n\n".
+                           "%5\$s\n\n".
+                           "Do not reply to this email; it will not get to them.\n\n".
+                           "With kind regards,\n".
+                           "%6\$s"),
+                        $from_profile->getBestName(),
+                        $from_profile->nickname,
+                        $group->nickname,
+                        $gm->content,
+                        common_local_url('newmessage', array('to' => $from_profile->id)),
+                        common_config('site', 'name')) . "\n";
+
+        $headers = _mail_prepare_headers('message', $to->nickname, $from_profile->nickname);
+
+        common_switch_locale();
+
+        return mail_to_user($to, $subject, $body, $headers);
+    }
+}
diff --git a/plugins/GroupPrivateMessage/classes/Group_privacy_settings.php b/plugins/GroupPrivateMessage/classes/Group_privacy_settings.php
new file mode 100644 (file)
index 0000000..9e983ba
--- /dev/null
@@ -0,0 +1,152 @@
+<?php
+/**
+ * Data class for group privacy settings
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Data class for group privacy
+ *
+ * Stores admin preferences about the group.
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Group_privacy_settings extends Managed_DataObject
+{
+    public $__table = 'group_privacy_settings';
+    /** ID of the group. */
+    public $group_id;
+    /** When to allow privacy: always, sometimes, or never. */
+    public $allow_privacy;
+    /** Who can send private messages: everyone, member, admin */
+    public $allow_sender;
+    /** row creation timestamp */
+    public $created;
+    /** Last-modified timestamp */
+    public $modified;
+
+    /** NEVER is */
+
+    const SOMETIMES = -1;
+    const NEVER  = 0;
+    const ALWAYS = 1;
+
+    /** These are bit-mappy, as a hedge against the future. */
+
+    const EVERYONE = 1;
+    const MEMBER   = 2;
+    const ADMIN    = 4;
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'group_id' => array('type' => 'int', 'not null' => true, 'description' => 'group_privacy_settings'),
+                'allow_privacy' => array('type' => 'int', 'not null' => true, 'description' => 'sometimes=-1, never=0, always=1'),
+                'allow_sender' => array('type' => 'int', 'not null' => true, 'description' => 'list of bit-mappy values in source code'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('group_id'),
+            'foreign keys' => array(
+                'group_privacy_settings_group_id_fkey' => array('user_group', array('group_id' => 'id')),
+            ),
+        );
+    }
+
+    function forGroup($group)
+    {
+        $gps = Group_privacy_settings::getKV('group_id', $group->id);
+
+        if (empty($gps)) {
+            // make a fake one with defaults
+            $gps = new Group_privacy_settings();
+            $gps->allow_privacy = Group_privacy_settings::SOMETIMES;
+            $gps->allow_sender  = Group_privacy_settings::MEMBER;
+        }
+
+        return $gps;
+    }
+
+    function ensurePost($user, $group)
+    {
+        $gps = self::forGroup($group);
+
+        if ($gps->allow_privacy == Group_privacy_settings::NEVER) {
+            // TRANS: Exception thrown when trying to set group privacy setting if group %s does not allow private messages.
+            throw new Exception(sprintf(_m('Group %s does not allow private messages.'),
+                                        $group->nickname));
+        }
+
+        switch ($gps->allow_sender) {
+        case Group_privacy_settings::EVERYONE:
+            $profile = $user->getProfile();
+            if (Group_block::isBlocked($group, $profile)) {
+                // TRANS: Exception thrown when trying to send group private message while blocked from that group.
+                // TRANS: %1$s is a user nickname, %2$s is a group nickname.
+                throw new Exception(sprintf(_m('User %1$s is blocked from group %2$s.'),
+                                            $user->nickname,
+                                            $group->nickname));
+            }
+            break;
+        case Group_privacy_settings::MEMBER:
+            if (!$user->isMember($group)) {
+                // TRANS: Exception thrown when trying to send group private message while not a member.
+                // TRANS: %1$s is a user nickname, %2$s is a group nickname.
+                throw new Exception(sprintf(_m('User %1$s is not a member of group %2$s.'),
+                                            $user->nickname,
+                                            $group->nickname));
+            }
+            break;
+        case Group_privacy_settings::ADMIN:
+            if (!$user->isAdmin($group)) {
+                // TRANS: Exception thrown when trying to send group private message while not a group administrator.
+                // TRANS: %1$s is a user nickname, %2$s is a group nickname.
+                throw new Exception(sprintf(_m('User %1$s is not an administrator of group %2$s.'),
+                                            $user->nickname,
+                                            $group->nickname));
+            }
+            break;
+        default:
+            // TRANS: Exception thrown when encountering undefined group privacy settings.
+            // TRANS: %s is a group nickname.
+            throw new Exception(sprintf(_m('Unknown privacy settings for group %s.'),
+                                        $group->nickname));
+        }
+
+        return true;
+    }
+}
diff --git a/plugins/GroupPrivateMessage/forms/groupmessage.php b/plugins/GroupPrivateMessage/forms/groupmessage.php
new file mode 100644 (file)
index 0000000..c33e4b2
--- /dev/null
@@ -0,0 +1,164 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Form for posting a group message
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  GroupPrivateMessage
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form for posting a group message
+ *
+ * @category  GroupPrivateMessage
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class GroupMessageForm extends Form
+{
+    var $group;
+    var $content;
+
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out   Output context
+     * @param User_group    $group Group to post to
+     *
+     * @todo add a drop-down list to post to any group
+     */
+    function __construct($out, $group, $content=null)
+    {
+        parent::__construct($out);
+
+        $this->group   = $group;
+        $this->content = $content;
+    }
+
+    /**
+     * Action for the form
+     */
+    function action()
+    {
+        return common_local_url('newgroupmessage',
+                                array('nickname' => $this->group->nickname));
+    }
+
+    /**
+     * Legend for the form
+     *
+     * @param
+     *
+     * @return
+     */
+    function formLegend()
+    {
+        $this->out->element('legend',
+                            null,
+                            // TRANS: Form legend for sending private message to group %s.
+                            sprintf(_m('Message to %s'), $this->group->nickname));
+    }
+
+    /**
+     * id for the form
+     *
+     * @param
+     *
+     * @return
+     */
+    function id()
+    {
+        return 'form_notice-group-message';
+    }
+
+    /**
+     * class for the form
+     *
+     * @param
+     *
+     * @return
+     */
+    function formClass()
+    {
+        return 'form_notice';
+    }
+
+    /**
+     * Entry data
+     *
+     * @param
+     *
+     * @return
+     */
+    function formData()
+    {
+        $this->out->element('label', array('for' => 'notice_data-text',
+                                           'id' => 'notice_data-text-label'),
+                            // TRANS: Field label for private group message to group %s.
+                            sprintf(_m('Direct message to %s'), $this->group->nickname));
+
+        $this->out->element('textarea', array('id' => 'notice_data-text',
+                                              'cols' => 35,
+                                              'rows' => 4,
+                                              'name' => 'content'),
+                            ($this->content) ? $this->content : '');
+
+        $contentLimit = Message::maxContent();
+
+        if ($contentLimit > 0) {
+            $this->out->elementStart('dl', 'form_note');
+            // TRANS: Indicator for number of chatacters still available for notice.
+            $this->out->element('dt', null, _m('Available characters'));
+            $this->out->element('dd', array('class' => 'count'),
+                                $contentLimit);
+            $this->out->elementEnd('dl');
+        }
+    }
+
+    /**
+     * Legend for the form
+     *
+     * @param
+     *
+     * @return
+     */
+    function formActions()
+    {
+        $this->out->element('input', array('id' => 'notice_action-submit',
+                                           'class' => 'submit',
+                                           'name' => 'message_send',
+                                           'type' => 'submit',
+                                           // TRANS: Send button text for sending private group notice.
+                                           'value' => _m('Send button for sending notice', 'Send')));
+    }
+}
diff --git a/plugins/GroupPrivateMessage/groupinbox.php b/plugins/GroupPrivateMessage/groupinbox.php
deleted file mode 100644 (file)
index 84952d0..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * List of private messages to this group
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  GroupPrivateMessage
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Show a list of private messages to this group
- *
- * @category  GroupPrivateMessage
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class GroupinboxAction extends GroupAction
-{
-    var $gm;
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        $cur = common_current_user();
-
-        if (empty($cur)) {
-            // TRANS: Client exception thrown when trying to view group inbox while not logged in.
-            throw new ClientException(_m('Only for logged-in users.'), 403);
-        }
-
-        $nicknameArg = $this->trimmed('nickname');
-
-        $nickname = common_canonical_nickname($nicknameArg);
-
-        if ($nickname != $nicknameArg) {
-            $url = common_local_url('groupinbox', array('nickname' => $nickname));
-            common_redirect($url);
-            return false;
-        }
-
-        $localGroup = Local_group::getKV('nickname', $nickname);
-
-        if (empty($localGroup)) {
-            // TRANS: Client exception thrown when trying to view group inbox for non-existing group.
-            throw new ClientException(_m('No such group.'), 404);
-        }
-
-        $this->group = User_group::getKV('id', $localGroup->group_id);
-
-        if (empty($this->group)) {
-            // TRANS: Client exception thrown when trying to view group inbox for non-existing group.
-            throw new ClientException(_m('No such group.'), 404);
-        }
-
-        if (!$cur->isMember($this->group)) {
-            // TRANS: Client exception thrown when trying to view group inbox while not a member.
-            throw new ClientException(_m('Only for members.'), 403);
-        }
-
-        $this->page = $this->trimmed('page');
-
-        if (!$this->page) {
-            $this->page = 1;
-        }
-
-        $this->gm = Group_message::forGroup($this->group,
-                                            ($this->page - 1) * MESSAGES_PER_PAGE,
-                                            MESSAGES_PER_PAGE + 1);
-        return true;
-    }
-
-    function showLocalNav()
-    {
-        $nav = new GroupNav($this, $this->group);
-        $nav->show();
-    }
-
-    function showNoticeForm()
-    {
-        $form = new GroupMessageForm($this, $this->group);
-        $form->show();
-    }
-
-    function showContent()
-    {
-        $gml = new GroupMessageList($this, $this->gm);
-        $cnt = $gml->show();
-
-        if ($cnt == 0) {
-            // TRANS: Text of group inbox if no private messages were sent to it.
-            $this->element('p', 'guide', _m('This group has not received any private messages.'));
-        }
-        $this->pagination($this->page > 1,
-                          $cnt > MESSAGES_PER_PAGE,
-                          $this->page,
-                          'groupinbox',
-                          array('nickname' => $this->group->nickname));
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        $this->showPage();
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        return true;
-    }
-
-    /**
-     * Title of the page
-     *
-     * @return string page title, with page number
-     */
-    function title()
-    {
-        $base = $this->group->getFancyName();
-
-        if ($this->page == 1) {
-            // TRANS: Title of inbox for group %s.
-            return sprintf(_m('%s group inbox'), $base);
-        } else {
-            // TRANS: Page title for any but first group page.
-            // TRANS: %1$s is a group name, $2$s is a page number.
-            return sprintf(_m('%1$s group inbox, page %2$d'),
-                           $base,
-                           $this->page);
-        }
-    }
-
-    /**
-     * Show the page notice
-     *
-     * Shows instructions for the page
-     *
-     * @return void
-     */
-    function showPageNotice()
-    {
-        $instr  = $this->getInstructions();
-        $output = common_markup_to_html($instr);
-
-        $this->elementStart('div', 'instructions');
-        $this->raw($output);
-        $this->elementEnd('div');
-    }
-
-    /**
-     * Instructions for using this page
-     *
-     * @return string localised instructions for using the page
-     */
-    function getInstructions()
-    {
-        // TRANS: Instructions for user inbox page.
-        return _m('This is the group inbox, which lists all incoming private messages for this group.');
-    }
-}
diff --git a/plugins/GroupPrivateMessage/groupmessagecommand.php b/plugins/GroupPrivateMessage/groupmessagecommand.php
deleted file mode 100644 (file)
index 2df45b1..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Command object for messages to groups
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Command
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Command object for messages to groups
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class GroupMessageCommand extends Command
-{
-    /** User sending the message. */
-    var $user;
-    /** Nickname of the group they're sending to. */
-    var $nickname;
-    /** Text of the message. */
-    var $text;
-
-    /**
-     * Constructor
-     *
-     * @param User   $user     User sending the message
-     * @param string $nickname Nickname of the group
-     * @param string $text     Text of message
-     */
-    function __construct($user, $nickname, $text)
-    {
-        $this->user     = $user;
-        $this->nickname = $nickname;
-        $this->text     = $text;
-    }
-
-    function handle($channel)
-    {
-        // Throws a command exception if group not found
-        $group = $this->getGroup($this->nickname);
-
-        $gm = Group_message::send($this->user, $group, $this->text);
-
-        $channel->output($this->user,
-                         // TRANS: Succes message after sending private group message to group %s.
-                         sprintf(_m('Direct message to group %s sent.'),
-                                 $group->nickname));
-
-        return true;
-    }
-}
diff --git a/plugins/GroupPrivateMessage/groupmessageform.php b/plugins/GroupPrivateMessage/groupmessageform.php
deleted file mode 100644 (file)
index c33e4b2..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Form for posting a group message
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  GroupPrivateMessage
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form for posting a group message
- *
- * @category  GroupPrivateMessage
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class GroupMessageForm extends Form
-{
-    var $group;
-    var $content;
-
-    /**
-     * Constructor
-     *
-     * @param HTMLOutputter $out   Output context
-     * @param User_group    $group Group to post to
-     *
-     * @todo add a drop-down list to post to any group
-     */
-    function __construct($out, $group, $content=null)
-    {
-        parent::__construct($out);
-
-        $this->group   = $group;
-        $this->content = $content;
-    }
-
-    /**
-     * Action for the form
-     */
-    function action()
-    {
-        return common_local_url('newgroupmessage',
-                                array('nickname' => $this->group->nickname));
-    }
-
-    /**
-     * Legend for the form
-     *
-     * @param
-     *
-     * @return
-     */
-    function formLegend()
-    {
-        $this->out->element('legend',
-                            null,
-                            // TRANS: Form legend for sending private message to group %s.
-                            sprintf(_m('Message to %s'), $this->group->nickname));
-    }
-
-    /**
-     * id for the form
-     *
-     * @param
-     *
-     * @return
-     */
-    function id()
-    {
-        return 'form_notice-group-message';
-    }
-
-    /**
-     * class for the form
-     *
-     * @param
-     *
-     * @return
-     */
-    function formClass()
-    {
-        return 'form_notice';
-    }
-
-    /**
-     * Entry data
-     *
-     * @param
-     *
-     * @return
-     */
-    function formData()
-    {
-        $this->out->element('label', array('for' => 'notice_data-text',
-                                           'id' => 'notice_data-text-label'),
-                            // TRANS: Field label for private group message to group %s.
-                            sprintf(_m('Direct message to %s'), $this->group->nickname));
-
-        $this->out->element('textarea', array('id' => 'notice_data-text',
-                                              'cols' => 35,
-                                              'rows' => 4,
-                                              'name' => 'content'),
-                            ($this->content) ? $this->content : '');
-
-        $contentLimit = Message::maxContent();
-
-        if ($contentLimit > 0) {
-            $this->out->elementStart('dl', 'form_note');
-            // TRANS: Indicator for number of chatacters still available for notice.
-            $this->out->element('dt', null, _m('Available characters'));
-            $this->out->element('dd', array('class' => 'count'),
-                                $contentLimit);
-            $this->out->elementEnd('dl');
-        }
-    }
-
-    /**
-     * Legend for the form
-     *
-     * @param
-     *
-     * @return
-     */
-    function formActions()
-    {
-        $this->out->element('input', array('id' => 'notice_action-submit',
-                                           'class' => 'submit',
-                                           'name' => 'message_send',
-                                           'type' => 'submit',
-                                           // TRANS: Send button text for sending private group notice.
-                                           'value' => _m('Send button for sending notice', 'Send')));
-    }
-}
diff --git a/plugins/GroupPrivateMessage/groupmessagelist.php b/plugins/GroupPrivateMessage/groupmessagelist.php
deleted file mode 100644 (file)
index 61ea3b2..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Widget for showing list of group messages
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  GroupPrivateMessage
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Widget for showing list of group messages
- *
- * @category  GroupPrivateMessage
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class GroupMessageList extends Widget
-{
-    var $gm;
-
-    /**
-     * Constructor
-     *
-     * @param HTMLOutputter $out output context
-     * @param Group_message $gm  Group message stream
-     */
-    function __construct($out, $gm)
-    {
-        parent::__construct($out);
-        $this->gm = $gm;
-    }
-
-    /**
-     * Show the list
-     *
-     * @return void
-     */
-    function show()
-    {
-        $this->out->elementStart('ul', 'notices messages group-messages');
-
-        $cnt = 0;
-
-        while ($this->gm->fetch() && $cnt <= MESSAGES_PER_PAGE) {
-
-            $cnt++;
-
-            if ($cnt > MESSAGES_PER_PAGE) {
-                break;
-            }
-
-            $gmli = new GroupMessageListItem($this->out, $this->gm);
-            $gmli->show();
-        }
-
-        $this->out->elementEnd('ul');
-
-        return $cnt;
-    }
-}
diff --git a/plugins/GroupPrivateMessage/groupmessagelistitem.php b/plugins/GroupPrivateMessage/groupmessagelistitem.php
deleted file mode 100644 (file)
index cb9c6a5..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Widget for showing an individual group message
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  GroupPrivateMessage
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Widget for showing a single group message
- *
- * @category  GroupPrivateMessage
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class GroupMessageListItem extends Widget
-{
-    var $gm;
-
-    /**
-     * Constructor
-     *
-     * @param HTMLOutputter $out output context
-     * @param Group_message $gm  Group message
-     */
-    function __construct($out, $gm)
-    {
-        parent::__construct($out);
-        $this->gm = $gm;
-    }
-
-    /**
-     * Show the item
-     *
-     * @return void
-     */
-    function show()
-    {
-        $group  = $this->gm->getGroup();
-        $sender = $this->gm->getSender();
-
-        $this->out->elementStart('li', array('class' => 'hentry notice message group-message',
-                                         'id' => 'message-' . $this->gm->id));
-
-        $this->out->elementStart('div', 'entry-title');
-        $this->out->elementStart('span', 'vcard author');
-        $this->out->elementStart('a',
-                                 array('href' => $sender->profileurl,
-                                       'class' => 'url'));
-        $avatar = $sender->getAvatar(AVATAR_STREAM_SIZE);
-        $this->out->element('img', array('src' => ($avatar) ?
-                                    $avatar->displayUrl() :
-                                    Avatar::defaultImage(AVATAR_STREAM_SIZE),
-                                    'width' => AVATAR_STREAM_SIZE,
-                                    'height' => AVATAR_STREAM_SIZE,
-                                    'class' => 'photo avatar',
-                                    'alt' => $sender->getBestName()));
-        $this->out->element('span',
-                            array('class' => 'nickname fn'),
-                            $sender->nickname);
-        $this->out->elementEnd('a');
-        $this->out->elementEnd('span');
-
-        $this->out->elementStart('p', array('class' => 'entry-content message-content'));
-        $this->out->raw($this->gm->rendered);
-        $this->out->elementEnd('p');
-        $this->out->elementEnd('div');
-
-        $this->out->elementStart('div', 'entry-content');
-        $this->out->elementStart('a', array('rel' => 'bookmark',
-                                            'class' => 'timestamp',
-                                            'href' => $this->gm->url));
-        $dt = common_date_iso8601($this->gm->created);
-        $this->out->element('abbr', array('class' => 'published',
-                                          'title' => $dt),
-                            common_date_string($this->gm->created));
-        $this->out->elementEnd('a');
-        $this->out->elementEnd('div');
-
-        $this->out->elementEnd('li');
-    }
-}
diff --git a/plugins/GroupPrivateMessage/lib/groupmessagecommand.php b/plugins/GroupPrivateMessage/lib/groupmessagecommand.php
new file mode 100644 (file)
index 0000000..2df45b1
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Command object for messages to groups
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Command
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Command object for messages to groups
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class GroupMessageCommand extends Command
+{
+    /** User sending the message. */
+    var $user;
+    /** Nickname of the group they're sending to. */
+    var $nickname;
+    /** Text of the message. */
+    var $text;
+
+    /**
+     * Constructor
+     *
+     * @param User   $user     User sending the message
+     * @param string $nickname Nickname of the group
+     * @param string $text     Text of message
+     */
+    function __construct($user, $nickname, $text)
+    {
+        $this->user     = $user;
+        $this->nickname = $nickname;
+        $this->text     = $text;
+    }
+
+    function handle($channel)
+    {
+        // Throws a command exception if group not found
+        $group = $this->getGroup($this->nickname);
+
+        $gm = Group_message::send($this->user, $group, $this->text);
+
+        $channel->output($this->user,
+                         // TRANS: Succes message after sending private group message to group %s.
+                         sprintf(_m('Direct message to group %s sent.'),
+                                 $group->nickname));
+
+        return true;
+    }
+}
diff --git a/plugins/GroupPrivateMessage/lib/groupmessagelist.php b/plugins/GroupPrivateMessage/lib/groupmessagelist.php
new file mode 100644 (file)
index 0000000..61ea3b2
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Widget for showing list of group messages
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  GroupPrivateMessage
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Widget for showing list of group messages
+ *
+ * @category  GroupPrivateMessage
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class GroupMessageList extends Widget
+{
+    var $gm;
+
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out output context
+     * @param Group_message $gm  Group message stream
+     */
+    function __construct($out, $gm)
+    {
+        parent::__construct($out);
+        $this->gm = $gm;
+    }
+
+    /**
+     * Show the list
+     *
+     * @return void
+     */
+    function show()
+    {
+        $this->out->elementStart('ul', 'notices messages group-messages');
+
+        $cnt = 0;
+
+        while ($this->gm->fetch() && $cnt <= MESSAGES_PER_PAGE) {
+
+            $cnt++;
+
+            if ($cnt > MESSAGES_PER_PAGE) {
+                break;
+            }
+
+            $gmli = new GroupMessageListItem($this->out, $this->gm);
+            $gmli->show();
+        }
+
+        $this->out->elementEnd('ul');
+
+        return $cnt;
+    }
+}
diff --git a/plugins/GroupPrivateMessage/lib/groupmessagelistitem.php b/plugins/GroupPrivateMessage/lib/groupmessagelistitem.php
new file mode 100644 (file)
index 0000000..cb9c6a5
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Widget for showing an individual group message
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  GroupPrivateMessage
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Widget for showing a single group message
+ *
+ * @category  GroupPrivateMessage
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class GroupMessageListItem extends Widget
+{
+    var $gm;
+
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out output context
+     * @param Group_message $gm  Group message
+     */
+    function __construct($out, $gm)
+    {
+        parent::__construct($out);
+        $this->gm = $gm;
+    }
+
+    /**
+     * Show the item
+     *
+     * @return void
+     */
+    function show()
+    {
+        $group  = $this->gm->getGroup();
+        $sender = $this->gm->getSender();
+
+        $this->out->elementStart('li', array('class' => 'hentry notice message group-message',
+                                         'id' => 'message-' . $this->gm->id));
+
+        $this->out->elementStart('div', 'entry-title');
+        $this->out->elementStart('span', 'vcard author');
+        $this->out->elementStart('a',
+                                 array('href' => $sender->profileurl,
+                                       'class' => 'url'));
+        $avatar = $sender->getAvatar(AVATAR_STREAM_SIZE);
+        $this->out->element('img', array('src' => ($avatar) ?
+                                    $avatar->displayUrl() :
+                                    Avatar::defaultImage(AVATAR_STREAM_SIZE),
+                                    'width' => AVATAR_STREAM_SIZE,
+                                    'height' => AVATAR_STREAM_SIZE,
+                                    'class' => 'photo avatar',
+                                    'alt' => $sender->getBestName()));
+        $this->out->element('span',
+                            array('class' => 'nickname fn'),
+                            $sender->nickname);
+        $this->out->elementEnd('a');
+        $this->out->elementEnd('span');
+
+        $this->out->elementStart('p', array('class' => 'entry-content message-content'));
+        $this->out->raw($this->gm->rendered);
+        $this->out->elementEnd('p');
+        $this->out->elementEnd('div');
+
+        $this->out->elementStart('div', 'entry-content');
+        $this->out->elementStart('a', array('rel' => 'bookmark',
+                                            'class' => 'timestamp',
+                                            'href' => $this->gm->url));
+        $dt = common_date_iso8601($this->gm->created);
+        $this->out->element('abbr', array('class' => 'published',
+                                          'title' => $dt),
+                            common_date_string($this->gm->created));
+        $this->out->elementEnd('a');
+        $this->out->elementEnd('div');
+
+        $this->out->elementEnd('li');
+    }
+}
diff --git a/plugins/GroupPrivateMessage/newgroupmessage.php b/plugins/GroupPrivateMessage/newgroupmessage.php
deleted file mode 100644 (file)
index 37738a3..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Action for adding a new group message
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Cache
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Action for adding a new group message
- *
- * @category  Action
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class NewgroupmessageAction extends Action
-{
-    var $group;
-    var $user;
-    var $text;
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        $this->user = common_current_user();
-
-        if (empty($this->user)) {
-            // TRANS: Client exception thrown when trying to send a private group message while not logged in.
-            throw new ClientException(_m('Must be logged in.'), 403);
-        }
-
-        if (!$this->user->hasRight(Right::NEWMESSAGE)) {
-            // TRANS: Exception thrown when user %s is not allowed to send a private group message.
-            throw new Exception(sprintf(_m('User %s is not allowed to send private messages.'),
-                                        $this->user->nickname));
-        }
-
-        $nicknameArg = $this->trimmed('nickname');
-
-        $nickname = common_canonical_nickname($nicknameArg);
-
-        if ($nickname != $nicknameArg) {
-            $url = common_local_url('newgroupmessage', array('nickname' => $nickname));
-            common_redirect($url, 301);
-            return false;
-        }
-
-        $localGroup = Local_group::getKV('nickname', $nickname);
-
-        if (empty($localGroup)) {
-            // TRANS: Client exception thrown when trying to send a private group message to a non-existing group.
-            throw new ClientException(_m('No such group.'), 404);
-        }
-
-        $this->group = User_group::getKV('id', $localGroup->group_id);
-
-        if (empty($this->group)) {
-            // TRANS: Client exception thrown when trying to send a private group message to a non-existing group.
-            throw new ClientException(_m('No such group.'), 404);
-        }
-
-        // This throws an exception on error
-        Group_privacy_settings::ensurePost($this->user, $this->group);
-
-        // If we're posted to, check session token and get text
-        if ($this->isPost()) {
-            $this->checkSessionToken();
-            $this->text = $this->trimmed('content');
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        if ($this->isPost()) {
-            $this->sendNewMessage();
-        } else {
-            $this->showPage();
-        }
-    }
-
-    function showNoticeForm()
-    {
-        $form = new GroupMessageForm($this, $this->group);
-        $form->show();
-    }
-
-    function sendNewMessage()
-    {
-        $gm = Group_message::send($this->user, $this->group, $this->text);
-
-        if ($this->boolean('ajax')) {
-            $this->startHTML('text/xml;charset=utf-8');
-            $this->elementStart('head');
-            // TRANS: Title after sending a private group message.
-            $this->element('title', null, _m('Message sent'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $this->element('p',
-                           array('id' => 'command_result'),
-                           // TRANS: Succes text after sending a direct message to group %s.
-                           sprintf(_m('Direct message to %s sent.'),
-                                   $this->group->nickname));
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            common_redirect($gm->url, 303);
-        }
-    }
-
-    function title()
-    {
-        // TRANS: Title of form for new private group message.
-        return sprintf(_m('New message to group %s'), $this->group->nickname);
-    }
-}
diff --git a/plugins/GroupPrivateMessage/showgroupmessage.php b/plugins/GroupPrivateMessage/showgroupmessage.php
deleted file mode 100644 (file)
index 8b99ece..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Show a single group message
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  GroupPrivateMessage
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Show a single private group message
- *
- * @category  GroupPrivateMessage
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class ShowgroupmessageAction extends Action
-{
-    var $gm;
-    var $group;
-    var $sender;
-    var $user;
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        $this->user = common_current_user();
-
-        if (empty($this->user)) {
-            // TRANS: Client exception thrown when trying to view group private messages without being logged in.
-            throw new ClientException(_m('Only logged-in users can view private messages.'),
-                                      403);
-        }
-
-        $id = $this->trimmed('id');
-
-        $this->gm = Group_message::getKV('id', $id);
-
-        if (empty($this->gm)) {
-            // TRANS: Client exception thrown when trying to view a non-existing group private message.
-            throw new ClientException(_m('No such message.'), 404);
-        }
-
-        $this->group = User_group::getKV('id', $this->gm->to_group);
-
-        if (empty($this->group)) {
-            // TRANS: Server exception thrown when trying to view group private messages for a non-exsting group.
-            throw new ServerException(_m('Group not found.'));
-        }
-
-        if (!$this->user->isMember($this->group)) {
-            // TRANS: Client exception thrown when trying to view a group private message without being a group member.
-            throw new ClientException(_m('Cannot read message.'), 403);
-        }
-
-        $this->sender = Profile::getKV('id', $this->gm->from_profile);
-
-        if (empty($this->sender)) {
-            // TRANS: Server exception thrown when trying to view a group private message without a sender.
-            throw new ServerException(_m('No sender found.'));
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        $this->showPage();
-    }
-
-    /**
-     * Title of the page
-     */
-    function title()
-    {
-        // TRANS: Title for private group message.
-        // TRANS: %1$s is the sender name, %2$s is the group name, %3$s is a timestamp.
-        return sprintf(_m('Message from %1$s to group %2$s on %3$s'),
-                       $this->sender->nickname,
-                       $this->group->nickname,
-                       common_exact_date($this->gm->created));
-    }
-
-    /**
-     * Show the content area.
-     */
-    function showContent()
-    {
-        $this->elementStart('ul', 'notices messages');
-        $gmli = new GroupMessageListItem($this, $this->gm);
-        $gmli->show();
-        $this->elementEnd('ul');
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        return true;
-    }
-
-    /**
-     * Return last modified, if applicable.
-     *
-     * MAY override
-     *
-     * @return string last modified http header
-     */
-    function lastModified()
-    {
-        return max(strtotime($this->group->modified),
-                   strtotime($this->sender->modified),
-                   strtotime($this->gm->modified));
-    }
-
-    /**
-     * Return etag, if applicable.
-     *
-     * MAY override
-     *
-     * @return string etag http header
-     */
-    function etag()
-    {
-        $avatar = $this->sender->getAvatar(AVATAR_STREAM_SIZE);
-
-        $avtime = ($avatar) ? strtotime($avatar->modified) : 0;
-
-        return 'W/"' . implode(':', array($this->arg('action'),
-                                          common_user_cache_hash(),
-                                          common_language(),
-                                          $this->gm->id,
-                                          strtotime($this->sender->modified),
-                                          strtotime($this->group->modified),
-                                          $avtime)) . '"';
-    }
-}
index dbaa904c6e5b0efba91fd09f838c0c2606237fdd..1d23e384a74a2016abd453df0dbb0121ea7db157 100644 (file)
@@ -71,28 +71,6 @@ class ImapPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'ImapManager':
-        case 'IMAPMailHandler':
-            include_once $dir . '/'.strtolower($cls).'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     function onStartQueueDaemonIoManagers(&$classes)
     {
         $classes[] = new ImapManager($this);
diff --git a/plugins/Imap/imapmailhandler.php b/plugins/Imap/imapmailhandler.php
deleted file mode 100644 (file)
index e287523..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-class IMAPMailHandler extends MailHandler
-{
-    function error($from, $msg)
-    {
-        $this->log(LOG_INFO, "Error: $from $msg");
-        $headers['To'] = $from;
-        // TRANS: E-mail subject in case of an error.
-        $headers['Subject'] = _m('Error');
-
-        return mail_send(array($from), $headers, $msg);
-    }
-}
diff --git a/plugins/Imap/imapmanager.php b/plugins/Imap/imapmanager.php
deleted file mode 100644 (file)
index b731b29..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * IMAP IO Manager
- *
- * 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  Plugin
- * @package   StatusNet
- * @author    Craig Andrews <candrews@integralblue.com>
- * @copyright 2009-2010 StatusNet, Inc.
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @maintainer Craig Andrews <candrews@integralblue.com>
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-class ImapManager extends IoManager
-{
-    protected $conn = null;
-
-    function __construct($plugin)
-    {
-        $this->plugin = $plugin;
-    }
-
-    /**
-     * Fetch the singleton manager for the current site.
-     * @return mixed ImapManager, or false if unneeded
-     */
-    public static function get()
-    {
-        // TRANS: Exception thrown when the ImapManager is used incorrectly in the code.
-        throw new Exception(_m('ImapManager should be created using its constructor, not using the static "get()" method.'));
-    }
-
-    /**
-     * Lists the IM connection socket to allow i/o master to wake
-     * when input comes in here as well as from the queue source.
-     *
-     * @return array of resources
-     */
-    public function getSockets()
-    {
-        return array();
-    }
-
-    /**
-     * Tell the i/o master we need one instance globally.
-     * Since this is a plugin manager, the plugin class itself will
-     * create one instance per site. This prevents the IoMaster from
-     * making more instances.
-     */
-    public static function multiSite()
-    {
-        return IoManager::GLOBAL_SINGLE_ONLY;
-    }
-
-    /**
-     * Initialize connection to server.
-     * @return boolean true on success
-     */
-    public function start($master)
-    {
-        if(parent::start($master))
-        {
-            $this->conn = $this->connect();
-            return true;
-        }else{
-            return false;
-        }
-    }
-
-    public function handleInput($socket)
-    {
-        $this->check_mailbox();
-        return true;
-    }
-
-    public function poll()
-    {
-        return $this->check_mailbox() > 0;
-    }
-
-    function pollInterval()
-    {
-        return $this->plugin->poll_frequency;
-    }
-
-    protected function connect()
-    {
-        $this->conn = imap_open($this->plugin->mailbox, $this->plugin->user, $this->plugin->password);
-        if($this->conn){
-            common_log(LOG_INFO, "Connected");
-            return $this->conn;
-        }else{
-            common_log(LOG_INFO, "Failed to connect: " . imap_last_error());
-            return $this->conn;
-        }
-    }
-
-    protected function check_mailbox()
-    {
-        imap_ping($this->conn);
-        $count = imap_num_msg($this->conn);
-        common_log(LOG_INFO, "Found $count messages");
-        if($count > 0){
-            $handler = new IMAPMailHandler();
-            for($i=1; $i <= $count; $i++)
-            {
-                $rawmessage = imap_fetchheader($this->conn, $count, FT_PREFETCHTEXT) . imap_body($this->conn, $i);
-                $handler->handle_message($rawmessage);
-                imap_delete($this->conn, $i);
-            }
-            imap_expunge($this->conn);
-            common_log(LOG_INFO, "Finished processing messages");
-        }
-        return $count;
-    }
-}
diff --git a/plugins/Imap/lib/imapmailhandler.php b/plugins/Imap/lib/imapmailhandler.php
new file mode 100644 (file)
index 0000000..e287523
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+class IMAPMailHandler extends MailHandler
+{
+    function error($from, $msg)
+    {
+        $this->log(LOG_INFO, "Error: $from $msg");
+        $headers['To'] = $from;
+        // TRANS: E-mail subject in case of an error.
+        $headers['Subject'] = _m('Error');
+
+        return mail_send(array($from), $headers, $msg);
+    }
+}
diff --git a/plugins/Imap/lib/imapmanager.php b/plugins/Imap/lib/imapmanager.php
new file mode 100644 (file)
index 0000000..b731b29
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * IMAP IO Manager
+ *
+ * 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  Plugin
+ * @package   StatusNet
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009-2010 StatusNet, Inc.
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @maintainer Craig Andrews <candrews@integralblue.com>
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+class ImapManager extends IoManager
+{
+    protected $conn = null;
+
+    function __construct($plugin)
+    {
+        $this->plugin = $plugin;
+    }
+
+    /**
+     * Fetch the singleton manager for the current site.
+     * @return mixed ImapManager, or false if unneeded
+     */
+    public static function get()
+    {
+        // TRANS: Exception thrown when the ImapManager is used incorrectly in the code.
+        throw new Exception(_m('ImapManager should be created using its constructor, not using the static "get()" method.'));
+    }
+
+    /**
+     * Lists the IM connection socket to allow i/o master to wake
+     * when input comes in here as well as from the queue source.
+     *
+     * @return array of resources
+     */
+    public function getSockets()
+    {
+        return array();
+    }
+
+    /**
+     * Tell the i/o master we need one instance globally.
+     * Since this is a plugin manager, the plugin class itself will
+     * create one instance per site. This prevents the IoMaster from
+     * making more instances.
+     */
+    public static function multiSite()
+    {
+        return IoManager::GLOBAL_SINGLE_ONLY;
+    }
+
+    /**
+     * Initialize connection to server.
+     * @return boolean true on success
+     */
+    public function start($master)
+    {
+        if(parent::start($master))
+        {
+            $this->conn = $this->connect();
+            return true;
+        }else{
+            return false;
+        }
+    }
+
+    public function handleInput($socket)
+    {
+        $this->check_mailbox();
+        return true;
+    }
+
+    public function poll()
+    {
+        return $this->check_mailbox() > 0;
+    }
+
+    function pollInterval()
+    {
+        return $this->plugin->poll_frequency;
+    }
+
+    protected function connect()
+    {
+        $this->conn = imap_open($this->plugin->mailbox, $this->plugin->user, $this->plugin->password);
+        if($this->conn){
+            common_log(LOG_INFO, "Connected");
+            return $this->conn;
+        }else{
+            common_log(LOG_INFO, "Failed to connect: " . imap_last_error());
+            return $this->conn;
+        }
+    }
+
+    protected function check_mailbox()
+    {
+        imap_ping($this->conn);
+        $count = imap_num_msg($this->conn);
+        common_log(LOG_INFO, "Found $count messages");
+        if($count > 0){
+            $handler = new IMAPMailHandler();
+            for($i=1; $i <= $count; $i++)
+            {
+                $rawmessage = imap_fetchheader($this->conn, $count, FT_PREFETCHTEXT) . imap_body($this->conn, $i);
+                $handler->handle_message($rawmessage);
+                imap_delete($this->conn, $i);
+            }
+            imap_expunge($this->conn);
+            common_log(LOG_INFO, "Finished processing messages");
+        }
+        return $count;
+    }
+}
diff --git a/plugins/Irc/ChannelResponseChannel.php b/plugins/Irc/ChannelResponseChannel.php
deleted file mode 100644 (file)
index 3ddad28..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php\r
-/**\r
- * StatusNet, the distributed open-source microblogging tool\r
- *\r
- * Extend the IMChannel class to allow commands to send messages\r
- * to a channel instead of PMing a user\r
- *\r
- * PHP version 5\r
- *\r
- * LICENCE: This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU Affero General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU Affero General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Affero General Public License\r
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- * @category  Network\r
- * @package   StatusNet\r
- * @author    Luke Fitzgerald <lw.fitzgerald@googlemail.com>\r
- * @copyright 2010 StatusNet, Inc.\r
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0\r
- * @link      http://status.net/\r
- */\r
-\r
-if (!defined('STATUSNET') && !defined('LACONICA')) {\r
-    exit(1);\r
-}\r
-\r
-class ChannelResponseChannel extends IMChannel {\r
-    protected $ircChannel;\r
-\r
-    /**\r
-    * Construct a ChannelResponseChannel\r
-    *\r
-    * @param IMplugin $imPlugin IMPlugin\r
-    * @param string $ircChannel IRC Channel to reply to\r
-    * @return ChannelResponseChannel\r
-    */\r
-    public function __construct($imPlugin, $ircChannel) {\r
-        $this->ircChannel = $ircChannel;\r
-        parent::__construct($imPlugin);\r
-    }\r
-\r
-    /**\r
-    * Send a message using the plugin\r
-    *\r
-    * @param User $user User\r
-    * @param string $text Message text\r
-    * @return void\r
-    */\r
-    public function output($user, $text) {\r
-        $text = $user->nickname.': ['.common_config('site', 'name') . '] ' . $text;\r
-        $this->imPlugin->sendMessage($this->ircChannel, $text);\r
-    }\r
-}\r
diff --git a/plugins/Irc/Fake_Irc.php b/plugins/Irc/Fake_Irc.php
deleted file mode 100644 (file)
index 81b8676..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Instead of sending IRC messages, retrieve the raw data that would be sent
- *
- * 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  Network
- * @package   StatusNet
- * @author    Luke Fitzgerald <lw.fitzgerald@googlemail.com>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-class Fake_Irc extends Phergie_Driver_Streams {
-    public $would_be_sent = null;
-
-    /**
-    * Store the components for sending a command
-    *
-    * @param string $command Command
-    * @param array $args Arguments
-    * @return void
-    */
-    protected function send($command, $args = '') {
-        $this->would_be_sent = array('command' => $command, 'args' => $args);
-    }
-}
index aeefded4b005413f6aa3aea2608f112c15d926ca..18d1a0afe1b1f719d4dd007d7009fcb0e59c000a 100644 (file)
@@ -120,24 +120,13 @@ class IrcPlugin extends ImPlugin {
      * @return boolean hook value; true means continue processing, false means stop.
      */
     public function onAutoload($cls) {
-        $dir = dirname(__FILE__);
-
-        switch ($cls) {
-            case 'IrcManager':
-                include_once $dir . '/'.strtolower($cls).'.php';
-                return false;
-            case 'Fake_Irc':
-            case 'Irc_waiting_message':
-            case 'ChannelResponseChannel':
-                include_once $dir . '/'. $cls .'.php';
-                return false;
-            default:
-                if (substr($cls, 0, 7) == 'Phergie') {
-                    include_once str_replace('_', DIRECTORY_SEPARATOR, $cls) . '.php';
-                    return false;
-                }
-                return true;
+        // in the beginning of this file, we have added an include path
+        if (substr($cls, 0, 7) == 'Phergie') {
+            include_once str_replace('_', DIRECTORY_SEPARATOR, $cls) . '.php';
+            return false;
         }
+
+        return parent::onAutoload($cls);
     }
 
     /*
diff --git a/plugins/Irc/Irc_waiting_message.php b/plugins/Irc/Irc_waiting_message.php
deleted file mode 100644 (file)
index eefd904..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php\r
-/**\r
- * Table Definition for irc_waiting_message\r
- */\r
-\r
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';\r
-\r
-class Irc_waiting_message extends Managed_DataObject {\r
-\r
-    public $__table = 'irc_waiting_message'; // table name\r
-    public $id;                              // int primary_key not_null auto_increment\r
-    public $data;                            // blob not_null\r
-    public $prioritise;                      // tinyint(1) not_null\r
-    public $attempts;                        // int not_null\r
-    public $claimed;                         // datetime()\r
-    public $created;                         // datetime()   not_null\r
-    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP\r
-\r
-    public static function schemaDef()\r
-    {\r
-        return array(\r
-            'fields' => array(\r
-                'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique ID for entry'),\r
-                'data' => array('type' => 'blob', 'not null' => true, 'description' => 'data blob'),\r
-                'prioritise' => array('type' => 'int', 'size' => 'tiny', 'description' => 'tinyint priority value'),\r
-                'attempts' => array('type' => 'int', 'not null' => true, 'description' => 'attempts count'),\r
-                'claimed' => array('type' => 'datetime', 'description' => 'date this irc message was claimed'),\r
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),\r
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),\r
-            ),\r
-            'primary key' => array('id'),\r
-            'indexes' => array(\r
-                'irc_waiting_message_prioritise_idx' => array('prioritise'),\r
-            ),\r
-        );\r
-    }\r
-\r
-    /**\r
-     * Get the next item in the queue\r
-     *\r
-     * @return Irc_waiting_message Next message if there is one\r
-     */\r
-    public static function top() {\r
-        $wm = new Irc_waiting_message();\r
-\r
-        $wm->orderBy('prioritise DESC, created');\r
-        $wm->whereAdd('claimed is null');\r
-\r
-        $wm->limit(1);\r
-\r
-        $cnt = $wm->find(true);\r
-\r
-        if ($cnt) {\r
-            // XXX: potential race condition\r
-            // can we force it to only update if claimed is still null\r
-            // (or old)?\r
-            common_log(LOG_INFO, 'claiming IRC waiting message id = ' . $wm->id);\r
-            $orig = clone($wm);\r
-            $wm->claimed = common_sql_now();\r
-            $result = $wm->update($orig);\r
-            if ($result) {\r
-                common_log(LOG_INFO, 'claim succeeded.');\r
-                return $wm;\r
-            } else {\r
-                common_log(LOG_INFO, 'claim failed.');\r
-            }\r
-        }\r
-        $wm = null;\r
-        return null;\r
-    }\r
-\r
-    /**\r
-    * Increment the attempts count\r
-    *\r
-    * @return void\r
-    * @throws Exception\r
-    */\r
-    public function incAttempts() {\r
-        $orig = clone($this);\r
-        $this->attempts++;\r
-        $result = $this->update($orig);\r
-\r
-        if (!$result) {\r
-            // TRANS: Exception thrown when an IRC attempts count could not be updated.\r
-            // TRANS: %d is the object ID for which the count could not be updated.\r
-            throw Exception(sprintf(_m('Could not increment attempts count for %d.'), $this->id));\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Release a claimed item.\r
-     */\r
-    public function releaseClaim() {\r
-        // DB_DataObject doesn't let us save nulls right now\r
-        $sql = sprintf("UPDATE irc_waiting_message SET claimed=NULL WHERE id=%d", $this->id);\r
-        $this->query($sql);\r
-\r
-        $this->claimed = null;\r
-        $this->encache();\r
-    }\r
-}\r
diff --git a/plugins/Irc/classes/Irc_waiting_message.php b/plugins/Irc/classes/Irc_waiting_message.php
new file mode 100644 (file)
index 0000000..eefd904
--- /dev/null
@@ -0,0 +1,101 @@
+<?php\r
+/**\r
+ * Table Definition for irc_waiting_message\r
+ */\r
+\r
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';\r
+\r
+class Irc_waiting_message extends Managed_DataObject {\r
+\r
+    public $__table = 'irc_waiting_message'; // table name\r
+    public $id;                              // int primary_key not_null auto_increment\r
+    public $data;                            // blob not_null\r
+    public $prioritise;                      // tinyint(1) not_null\r
+    public $attempts;                        // int not_null\r
+    public $claimed;                         // datetime()\r
+    public $created;                         // datetime()   not_null\r
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP\r
+\r
+    public static function schemaDef()\r
+    {\r
+        return array(\r
+            'fields' => array(\r
+                'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique ID for entry'),\r
+                'data' => array('type' => 'blob', 'not null' => true, 'description' => 'data blob'),\r
+                'prioritise' => array('type' => 'int', 'size' => 'tiny', 'description' => 'tinyint priority value'),\r
+                'attempts' => array('type' => 'int', 'not null' => true, 'description' => 'attempts count'),\r
+                'claimed' => array('type' => 'datetime', 'description' => 'date this irc message was claimed'),\r
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),\r
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),\r
+            ),\r
+            'primary key' => array('id'),\r
+            'indexes' => array(\r
+                'irc_waiting_message_prioritise_idx' => array('prioritise'),\r
+            ),\r
+        );\r
+    }\r
+\r
+    /**\r
+     * Get the next item in the queue\r
+     *\r
+     * @return Irc_waiting_message Next message if there is one\r
+     */\r
+    public static function top() {\r
+        $wm = new Irc_waiting_message();\r
+\r
+        $wm->orderBy('prioritise DESC, created');\r
+        $wm->whereAdd('claimed is null');\r
+\r
+        $wm->limit(1);\r
+\r
+        $cnt = $wm->find(true);\r
+\r
+        if ($cnt) {\r
+            // XXX: potential race condition\r
+            // can we force it to only update if claimed is still null\r
+            // (or old)?\r
+            common_log(LOG_INFO, 'claiming IRC waiting message id = ' . $wm->id);\r
+            $orig = clone($wm);\r
+            $wm->claimed = common_sql_now();\r
+            $result = $wm->update($orig);\r
+            if ($result) {\r
+                common_log(LOG_INFO, 'claim succeeded.');\r
+                return $wm;\r
+            } else {\r
+                common_log(LOG_INFO, 'claim failed.');\r
+            }\r
+        }\r
+        $wm = null;\r
+        return null;\r
+    }\r
+\r
+    /**\r
+    * Increment the attempts count\r
+    *\r
+    * @return void\r
+    * @throws Exception\r
+    */\r
+    public function incAttempts() {\r
+        $orig = clone($this);\r
+        $this->attempts++;\r
+        $result = $this->update($orig);\r
+\r
+        if (!$result) {\r
+            // TRANS: Exception thrown when an IRC attempts count could not be updated.\r
+            // TRANS: %d is the object ID for which the count could not be updated.\r
+            throw Exception(sprintf(_m('Could not increment attempts count for %d.'), $this->id));\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Release a claimed item.\r
+     */\r
+    public function releaseClaim() {\r
+        // DB_DataObject doesn't let us save nulls right now\r
+        $sql = sprintf("UPDATE irc_waiting_message SET claimed=NULL WHERE id=%d", $this->id);\r
+        $this->query($sql);\r
+\r
+        $this->claimed = null;\r
+        $this->encache();\r
+    }\r
+}\r
diff --git a/plugins/Irc/ircmanager.php b/plugins/Irc/ircmanager.php
deleted file mode 100644 (file)
index 3c0a504..0000000
+++ /dev/null
@@ -1,358 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-/**
- * IRC background connection manager for IRC-using queue handlers,
- * allowing them to send outgoing messages on the right connection.
- *
- * Input is handled during socket select loop, Any incoming messages will be handled.
- *
- * In a multi-site queuedaemon.php run, one connection will be instantiated
- * for each site being handled by the current process that has IRC enabled.
- */
-class IrcManager extends ImManager {
-    protected $conn = null;
-    protected $lastPing = null;
-    protected $messageWaiting = true;
-    protected $lastMessage = null;
-
-    protected $regChecks = array();
-    protected $regChecksLookup = array();
-
-    protected $connected = false;
-
-    /**
-     * Initialize connection to server.
-     *
-     * @return boolean true on success
-     */
-    public function start($master) {
-        if (parent::start($master)) {
-            $this->connect();
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-    * Return any open sockets that the run loop should listen
-    * for input on.
-    *
-    * @return array Array of socket resources
-    */
-    public function getSockets() {
-        $this->connect();
-        if ($this->conn) {
-            return $this->conn->getSockets();
-        } else {
-            return array();
-        }
-    }
-
-    /**
-     * Request a maximum timeout for listeners before the next idle period.
-     *
-     * @return integer Maximum timeout
-     */
-    public function timeout() {
-        if ($this->messageWaiting) {
-            return 1;
-        } else {
-            return $this->plugin->pinginterval;
-        }
-    }
-
-    /**
-     * Idle processing for io manager's execution loop.
-     *
-     * @return void
-     */
-    public function idle() {
-        // Send a ping if necessary
-        if (empty($this->lastPing) || time() - $this->lastPing > $this->plugin->pinginterval) {
-            $this->sendPing();
-        }
-
-        if ($this->connected) {
-            // Send a waiting message if appropriate
-            if ($this->messageWaiting && time() - $this->lastMessage > 1) {
-                $wm = Irc_waiting_message::top();
-                if ($wm === NULL) {
-                    $this->messageWaiting = false;
-                    return;
-                }
-
-                $data = unserialize($wm->data);
-                $wm->incAttempts();
-
-                if ($this->send_raw_message($data)) {
-                    $wm->delete();
-                } else {
-                    if ($wm->attempts <= common_config('queue', 'max_retries')) {
-                        // Try again next idle
-                        $wm->releaseClaim();
-                    } else {
-                        // Exceeded the maximum number of retries
-                        $wm->delete();
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Process IRC events that have come in over the wire.
-     *
-     * @param resource $socket Socket to handle input on
-     * @return void
-     */
-    public function handleInput($socket) {
-        common_log(LOG_DEBUG, 'Servicing the IRC queue.');
-        $this->stats('irc_process');
-
-        try {
-            $this->conn->handleEvents();
-        } catch (Phergie_Driver_Exception $e) {
-            $this->connected = false;
-            $this->conn->reconnect();
-        }
-    }
-
-    /**
-    * Initiate connection
-    *
-    * @return void
-    */
-    public function connect() {
-        if (!$this->conn) {
-            $this->conn = new Phergie_StatusnetBot;
-
-            $config = new Phergie_Config;
-            $config->readArray(
-                array(
-                    'connections' => array(
-                        array(
-                            'host' => $this->plugin->host,
-                            'port' => $this->plugin->port,
-                            'username' => $this->plugin->username,
-                            'realname' => $this->plugin->realname,
-                            'nick' => $this->plugin->nick,
-                            'password' => $this->plugin->password,
-                            'transport' => $this->plugin->transporttype,
-                            'encoding' => $this->plugin->encoding
-                        )
-                    ),
-
-                    'driver' => 'statusnet',
-
-                    'processor' => 'async',
-                    'processor.options' => array('sec' => 0, 'usec' => 0),
-
-                    'plugins' => array(
-                        'Pong',
-                        'NickServ',
-                        'AutoJoin',
-                        'Statusnet',
-                    ),
-
-                    'plugins.autoload' => true,
-
-                    // Uncomment to enable debugging output
-                    //'ui.enabled' => true,
-
-                    'nickserv.password' => $this->plugin->nickservpassword,
-                    'nickserv.identify_message' => $this->plugin->nickservidentifyregexp,
-
-                    'autojoin.channels' => $this->plugin->channels,
-
-                    'statusnet.messagecallback' => array($this, 'handle_irc_message'),
-                    'statusnet.regcallback' => array($this, 'handle_reg_response'),
-                    'statusnet.connectedcallback' => array($this, 'handle_connected'),
-                    'statusnet.unregregexp' => $this->plugin->unregregexp,
-                    'statusnet.regregexp' => $this->plugin->regregexp
-                )
-            );
-
-            $this->conn->setConfig($config);
-            $this->conn->connect();
-            $this->lastPing = time();
-            $this->lastMessage = time();
-        }
-        return $this->conn;
-    }
-
-    /**
-    * Called via a callback when a message is received
-    * Passes it back to the queuing system
-    *
-    * @param array $data Data
-    * @return boolean
-    */
-    public function handle_irc_message($data) {
-        $this->plugin->enqueueIncomingRaw($data);
-        return true;
-    }
-
-    /**
-    * Called via a callback when NickServ responds to
-    * the bots query asking if a nick is registered
-    *
-    * @param array $data Data
-    * @return void
-    */
-    public function handle_reg_response($data) {
-        // Retrieve data
-        $screenname = $data['screenname'];
-        $nickdata = $this->regChecks[$screenname];
-        $usernick = $nickdata['user']->nickname;
-
-        if (isset($this->regChecksLookup[$usernick])) {
-            if ($data['registered']) {
-                // Send message
-                $this->plugin->sendConfirmationCode($screenname, $nickdata['code'], $nickdata['user'], true);
-            } else {
-                // TRANS: Message given when using an unregistered IRC nickname.
-                $this->plugin->sendMessage($screenname, _m('Your nickname is not registered so IRC connectivity cannot be enabled.'));
-
-                $confirm = new Confirm_address();
-
-                $confirm->user_id      = $user->id;
-                $confirm->address_type = $this->plugin->transport;
-
-                if ($confirm->find(true)) {
-                    $result = $confirm->delete();
-
-                    if (!$result) {
-                        common_log_db_error($confirm, 'DELETE', __FILE__);
-                        // TRANS: Server error thrown on database error when deleting IRC nickname confirmation.
-                        $this->serverError(_m('Could not delete confirmation.'));
-                        return;
-                    }
-                }
-            }
-
-            // Unset lookup value
-            unset($this->regChecksLookup[$usernick]);
-
-            // Unset data
-            unset($this->regChecks[$screename]);
-        }
-    }
-
-    /**
-    * Called when the connection is established
-    *
-    * @return void
-    */
-    public function handle_connected() {
-        $this->connected = true;
-    }
-
-    /**
-    * Enters a message into the database for sending when ready
-    *
-    * @param string $command Command
-    * @param array $args Arguments
-    * @return boolean
-    */
-    protected function enqueue_waiting_message($data) {
-        $wm = new Irc_waiting_message();
-
-        $wm->data       = serialize($data);
-        $wm->prioritise = $data['prioritise'];
-        $wm->attempts   = 0;
-        $wm->created    = common_sql_now();
-        $result         = $wm->insert();
-
-        if (!$result) {
-            common_log_db_error($wm, 'INSERT', __FILE__);
-            // TRANS: Server exception thrown when an IRC waiting queue item could not be added to the database.
-            throw new ServerException(_m('Database error inserting IRC waiting queue item.'));
-        }
-
-        return true;
-    }
-
-    /**
-     * Send a message using the daemon
-     *
-     * @param $data Message data
-     * @return boolean true on success
-     */
-    public function send_raw_message($data) {
-        $this->connect();
-        if (!$this->conn) {
-            return false;
-        }
-
-        if ($data['type'] != 'delayedmessage') {
-            if ($data['type'] != 'message') {
-                // Nick checking
-                $nickdata = $data['nickdata'];
-                $usernick = $nickdata['user']->nickname;
-                $screenname = $nickdata['screenname'];
-
-                // Cancel any existing checks for this user
-                if (isset($this->regChecksLookup[$usernick])) {
-                    unset($this->regChecks[$this->regChecksLookup[$usernick]]);
-                }
-
-                $this->regChecks[$screenname] = $nickdata;
-                $this->regChecksLookup[$usernick] = $screenname;
-            }
-
-            // If there is a backlog or we need to wait, queue the message
-            if ($this->messageWaiting || time() - $this->lastMessage < 1) {
-                $this->enqueue_waiting_message(
-                    array(
-                        'type' => 'delayedmessage',
-                        'prioritise' => $data['prioritise'],
-                        'data' => $data['data']
-                    )
-                );
-                $this->messageWaiting = true;
-                return true;
-            }
-        }
-
-        try {
-            $this->conn->send($data['data']['command'], $data['data']['args']);
-        } catch (Phergie_Driver_Exception $e) {
-            $this->connected = false;
-            $this->conn->reconnect();
-            return false;
-        }
-
-        $this->lastMessage = time();
-        return true;
-    }
-
-    /**
-    * Sends a ping
-    *
-    * @return void
-    */
-    protected function sendPing() {
-        $this->lastPing = time();
-        $this->conn->send('PING', $this->lastPing);
-    }
-}
diff --git a/plugins/Irc/lib/channelresponsechannel.php b/plugins/Irc/lib/channelresponsechannel.php
new file mode 100644 (file)
index 0000000..3ddad28
--- /dev/null
@@ -0,0 +1,61 @@
+<?php\r
+/**\r
+ * StatusNet, the distributed open-source microblogging tool\r
+ *\r
+ * Extend the IMChannel class to allow commands to send messages\r
+ * to a channel instead of PMing a user\r
+ *\r
+ * PHP version 5\r
+ *\r
+ * LICENCE: This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU Affero General Public License as published by\r
+ * the Free Software Foundation, either version 3 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU Affero General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Affero General Public License\r
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ * @category  Network\r
+ * @package   StatusNet\r
+ * @author    Luke Fitzgerald <lw.fitzgerald@googlemail.com>\r
+ * @copyright 2010 StatusNet, Inc.\r
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0\r
+ * @link      http://status.net/\r
+ */\r
+\r
+if (!defined('STATUSNET') && !defined('LACONICA')) {\r
+    exit(1);\r
+}\r
+\r
+class ChannelResponseChannel extends IMChannel {\r
+    protected $ircChannel;\r
+\r
+    /**\r
+    * Construct a ChannelResponseChannel\r
+    *\r
+    * @param IMplugin $imPlugin IMPlugin\r
+    * @param string $ircChannel IRC Channel to reply to\r
+    * @return ChannelResponseChannel\r
+    */\r
+    public function __construct($imPlugin, $ircChannel) {\r
+        $this->ircChannel = $ircChannel;\r
+        parent::__construct($imPlugin);\r
+    }\r
+\r
+    /**\r
+    * Send a message using the plugin\r
+    *\r
+    * @param User $user User\r
+    * @param string $text Message text\r
+    * @return void\r
+    */\r
+    public function output($user, $text) {\r
+        $text = $user->nickname.': ['.common_config('site', 'name') . '] ' . $text;\r
+        $this->imPlugin->sendMessage($this->ircChannel, $text);\r
+    }\r
+}\r
diff --git a/plugins/Irc/lib/fake_irc.php b/plugins/Irc/lib/fake_irc.php
new file mode 100644 (file)
index 0000000..81b8676
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Instead of sending IRC messages, retrieve the raw data that would be sent
+ *
+ * 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  Network
+ * @package   StatusNet
+ * @author    Luke Fitzgerald <lw.fitzgerald@googlemail.com>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+class Fake_Irc extends Phergie_Driver_Streams {
+    public $would_be_sent = null;
+
+    /**
+    * Store the components for sending a command
+    *
+    * @param string $command Command
+    * @param array $args Arguments
+    * @return void
+    */
+    protected function send($command, $args = '') {
+        $this->would_be_sent = array('command' => $command, 'args' => $args);
+    }
+}
diff --git a/plugins/Irc/lib/ircmanager.php b/plugins/Irc/lib/ircmanager.php
new file mode 100644 (file)
index 0000000..3c0a504
--- /dev/null
@@ -0,0 +1,358 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+/**
+ * IRC background connection manager for IRC-using queue handlers,
+ * allowing them to send outgoing messages on the right connection.
+ *
+ * Input is handled during socket select loop, Any incoming messages will be handled.
+ *
+ * In a multi-site queuedaemon.php run, one connection will be instantiated
+ * for each site being handled by the current process that has IRC enabled.
+ */
+class IrcManager extends ImManager {
+    protected $conn = null;
+    protected $lastPing = null;
+    protected $messageWaiting = true;
+    protected $lastMessage = null;
+
+    protected $regChecks = array();
+    protected $regChecksLookup = array();
+
+    protected $connected = false;
+
+    /**
+     * Initialize connection to server.
+     *
+     * @return boolean true on success
+     */
+    public function start($master) {
+        if (parent::start($master)) {
+            $this->connect();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+    * Return any open sockets that the run loop should listen
+    * for input on.
+    *
+    * @return array Array of socket resources
+    */
+    public function getSockets() {
+        $this->connect();
+        if ($this->conn) {
+            return $this->conn->getSockets();
+        } else {
+            return array();
+        }
+    }
+
+    /**
+     * Request a maximum timeout for listeners before the next idle period.
+     *
+     * @return integer Maximum timeout
+     */
+    public function timeout() {
+        if ($this->messageWaiting) {
+            return 1;
+        } else {
+            return $this->plugin->pinginterval;
+        }
+    }
+
+    /**
+     * Idle processing for io manager's execution loop.
+     *
+     * @return void
+     */
+    public function idle() {
+        // Send a ping if necessary
+        if (empty($this->lastPing) || time() - $this->lastPing > $this->plugin->pinginterval) {
+            $this->sendPing();
+        }
+
+        if ($this->connected) {
+            // Send a waiting message if appropriate
+            if ($this->messageWaiting && time() - $this->lastMessage > 1) {
+                $wm = Irc_waiting_message::top();
+                if ($wm === NULL) {
+                    $this->messageWaiting = false;
+                    return;
+                }
+
+                $data = unserialize($wm->data);
+                $wm->incAttempts();
+
+                if ($this->send_raw_message($data)) {
+                    $wm->delete();
+                } else {
+                    if ($wm->attempts <= common_config('queue', 'max_retries')) {
+                        // Try again next idle
+                        $wm->releaseClaim();
+                    } else {
+                        // Exceeded the maximum number of retries
+                        $wm->delete();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Process IRC events that have come in over the wire.
+     *
+     * @param resource $socket Socket to handle input on
+     * @return void
+     */
+    public function handleInput($socket) {
+        common_log(LOG_DEBUG, 'Servicing the IRC queue.');
+        $this->stats('irc_process');
+
+        try {
+            $this->conn->handleEvents();
+        } catch (Phergie_Driver_Exception $e) {
+            $this->connected = false;
+            $this->conn->reconnect();
+        }
+    }
+
+    /**
+    * Initiate connection
+    *
+    * @return void
+    */
+    public function connect() {
+        if (!$this->conn) {
+            $this->conn = new Phergie_StatusnetBot;
+
+            $config = new Phergie_Config;
+            $config->readArray(
+                array(
+                    'connections' => array(
+                        array(
+                            'host' => $this->plugin->host,
+                            'port' => $this->plugin->port,
+                            'username' => $this->plugin->username,
+                            'realname' => $this->plugin->realname,
+                            'nick' => $this->plugin->nick,
+                            'password' => $this->plugin->password,
+                            'transport' => $this->plugin->transporttype,
+                            'encoding' => $this->plugin->encoding
+                        )
+                    ),
+
+                    'driver' => 'statusnet',
+
+                    'processor' => 'async',
+                    'processor.options' => array('sec' => 0, 'usec' => 0),
+
+                    'plugins' => array(
+                        'Pong',
+                        'NickServ',
+                        'AutoJoin',
+                        'Statusnet',
+                    ),
+
+                    'plugins.autoload' => true,
+
+                    // Uncomment to enable debugging output
+                    //'ui.enabled' => true,
+
+                    'nickserv.password' => $this->plugin->nickservpassword,
+                    'nickserv.identify_message' => $this->plugin->nickservidentifyregexp,
+
+                    'autojoin.channels' => $this->plugin->channels,
+
+                    'statusnet.messagecallback' => array($this, 'handle_irc_message'),
+                    'statusnet.regcallback' => array($this, 'handle_reg_response'),
+                    'statusnet.connectedcallback' => array($this, 'handle_connected'),
+                    'statusnet.unregregexp' => $this->plugin->unregregexp,
+                    'statusnet.regregexp' => $this->plugin->regregexp
+                )
+            );
+
+            $this->conn->setConfig($config);
+            $this->conn->connect();
+            $this->lastPing = time();
+            $this->lastMessage = time();
+        }
+        return $this->conn;
+    }
+
+    /**
+    * Called via a callback when a message is received
+    * Passes it back to the queuing system
+    *
+    * @param array $data Data
+    * @return boolean
+    */
+    public function handle_irc_message($data) {
+        $this->plugin->enqueueIncomingRaw($data);
+        return true;
+    }
+
+    /**
+    * Called via a callback when NickServ responds to
+    * the bots query asking if a nick is registered
+    *
+    * @param array $data Data
+    * @return void
+    */
+    public function handle_reg_response($data) {
+        // Retrieve data
+        $screenname = $data['screenname'];
+        $nickdata = $this->regChecks[$screenname];
+        $usernick = $nickdata['user']->nickname;
+
+        if (isset($this->regChecksLookup[$usernick])) {
+            if ($data['registered']) {
+                // Send message
+                $this->plugin->sendConfirmationCode($screenname, $nickdata['code'], $nickdata['user'], true);
+            } else {
+                // TRANS: Message given when using an unregistered IRC nickname.
+                $this->plugin->sendMessage($screenname, _m('Your nickname is not registered so IRC connectivity cannot be enabled.'));
+
+                $confirm = new Confirm_address();
+
+                $confirm->user_id      = $user->id;
+                $confirm->address_type = $this->plugin->transport;
+
+                if ($confirm->find(true)) {
+                    $result = $confirm->delete();
+
+                    if (!$result) {
+                        common_log_db_error($confirm, 'DELETE', __FILE__);
+                        // TRANS: Server error thrown on database error when deleting IRC nickname confirmation.
+                        $this->serverError(_m('Could not delete confirmation.'));
+                        return;
+                    }
+                }
+            }
+
+            // Unset lookup value
+            unset($this->regChecksLookup[$usernick]);
+
+            // Unset data
+            unset($this->regChecks[$screename]);
+        }
+    }
+
+    /**
+    * Called when the connection is established
+    *
+    * @return void
+    */
+    public function handle_connected() {
+        $this->connected = true;
+    }
+
+    /**
+    * Enters a message into the database for sending when ready
+    *
+    * @param string $command Command
+    * @param array $args Arguments
+    * @return boolean
+    */
+    protected function enqueue_waiting_message($data) {
+        $wm = new Irc_waiting_message();
+
+        $wm->data       = serialize($data);
+        $wm->prioritise = $data['prioritise'];
+        $wm->attempts   = 0;
+        $wm->created    = common_sql_now();
+        $result         = $wm->insert();
+
+        if (!$result) {
+            common_log_db_error($wm, 'INSERT', __FILE__);
+            // TRANS: Server exception thrown when an IRC waiting queue item could not be added to the database.
+            throw new ServerException(_m('Database error inserting IRC waiting queue item.'));
+        }
+
+        return true;
+    }
+
+    /**
+     * Send a message using the daemon
+     *
+     * @param $data Message data
+     * @return boolean true on success
+     */
+    public function send_raw_message($data) {
+        $this->connect();
+        if (!$this->conn) {
+            return false;
+        }
+
+        if ($data['type'] != 'delayedmessage') {
+            if ($data['type'] != 'message') {
+                // Nick checking
+                $nickdata = $data['nickdata'];
+                $usernick = $nickdata['user']->nickname;
+                $screenname = $nickdata['screenname'];
+
+                // Cancel any existing checks for this user
+                if (isset($this->regChecksLookup[$usernick])) {
+                    unset($this->regChecks[$this->regChecksLookup[$usernick]]);
+                }
+
+                $this->regChecks[$screenname] = $nickdata;
+                $this->regChecksLookup[$usernick] = $screenname;
+            }
+
+            // If there is a backlog or we need to wait, queue the message
+            if ($this->messageWaiting || time() - $this->lastMessage < 1) {
+                $this->enqueue_waiting_message(
+                    array(
+                        'type' => 'delayedmessage',
+                        'prioritise' => $data['prioritise'],
+                        'data' => $data['data']
+                    )
+                );
+                $this->messageWaiting = true;
+                return true;
+            }
+        }
+
+        try {
+            $this->conn->send($data['data']['command'], $data['data']['args']);
+        } catch (Phergie_Driver_Exception $e) {
+            $this->connected = false;
+            $this->conn->reconnect();
+            return false;
+        }
+
+        $this->lastMessage = time();
+        return true;
+    }
+
+    /**
+    * Sends a ping
+    *
+    * @return void
+    */
+    protected function sendPing() {
+        $this->lastPing = time();
+        $this->conn->send('PING', $this->lastPing);
+    }
+}
index 38ea6e6592356664bb8289cfa6297d3d186baeb3..76df45369cabe3dfd21a4deca4eddf5d20b68ae0 100644 (file)
@@ -54,6 +54,8 @@ class LdapAuthenticationPlugin extends AuthenticationPlugin
             require_once(INSTALLDIR.'/plugins/LdapCommon/LdapCommon.php');
             return false;
         }
+
+        return parent::onAutoload($cls);
     }
 
     function onEndShowPageNotice($action)
index 1049c5610aa918057d777cc5f51e808213d6bfd8..6c8f1253c53e19dec6675f2889966c3648fcadc6 100644 (file)
@@ -56,6 +56,8 @@ class LdapAuthorizationPlugin extends AuthorizationPlugin
             require_once(INSTALLDIR.'/plugins/LdapCommon/LdapCommon.php');
             return false;
         }
+
+        return parent::onAutoload($cls);
     }
 
     //---interface implementation---//
index afc61abf7222633c7a5078c0b8555da256b5738f..ef31313b8fc796d8c8245b87cf40c1d4883b7674 100644 (file)
@@ -75,6 +75,7 @@ class LdapCommon
 
     function onAutoload($cls)
     {
+        // we've added an extra include-path in the beginning of this file
         switch ($cls)
         {
          case 'MemcacheSchemaCache':
@@ -93,6 +94,8 @@ class LdapCommon
             require_once 'Net/LDAP2/Entry.php';
             return false;
         }
+
+        return parent::onAutoload($cls);
     }
 
     function get_ldap_config(){
index 09b3a2af645d05900ba4cded2ac8255827c1cd1a..652afdbdf961351cf01e0f8eab9f777d9700a927 100644 (file)
@@ -68,28 +68,6 @@ class LinkPreviewPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Autoloader
-     *
-     * Loads our classes if they're requested.
-     *
-     * @param string $cls Class requested
-     *
-     * @return boolean hook return
-     */
-    function onAutoload($cls)
-    {
-        $lower = strtolower($cls);
-        switch ($lower)
-        {
-        case 'oembedproxyaction':
-            require_once dirname(__FILE__) . '/' . $lower . '.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Hook for RouterInitialized event.
      *
diff --git a/plugins/LinkPreview/actions/oembedproxy.php b/plugins/LinkPreview/actions/oembedproxy.php
new file mode 100644 (file)
index 0000000..7e54aca
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * StatusNet-only extensions to the Twitter-like API
+ *
+ * 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   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Oembed proxy implementation
+ *
+ * This class provides an interface for our JS-side code to pull info on
+ * links from other sites, using either native oEmbed, our own custom
+ * handlers, or the noembed.com offsite proxy service as configured.
+ *
+ * @category  oEmbed
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+class OembedproxyAction extends OembedAction
+{
+    function handle($args)
+    {
+        // Trigger short error responses; not a human-readable web page.
+        StatusNet::setApi(true);
+
+        // We're not a general oEmbed proxy service; limit to valid sessions.
+        $token = $this->trimmed('token');
+        if (!$token || $token != common_session_token()) {
+            // TRANS: Client error displayed when the session token does not match or is not given.
+            $this->clientError(_m('There was a problem with your session token. '.
+                                 'Try again, please.'));
+        }
+
+        $format = $this->arg('format');
+        if ($format && $format != 'json') {
+            // TRANS: Client exception thrown when requesting a different format than JSON.
+            throw new ClientException(_m('Invalid format; only JSON supported.'));
+        }
+
+        $url = $this->arg('url');
+        if (!common_valid_http_url($url)) {
+            // TRANS: Client exception thrown when not providing a valid URL.
+            throw new ClientException(_m('Invalid URL.'));
+        }
+
+        $params = array();
+        if ($this->arg('maxwidth')) {
+            $params['maxwidth'] = $this->arg('maxwidth');
+        }
+        if ($this->arg('maxheight')) {
+            $params['maxheight'] = $this->arg('maxheight');
+        }
+
+        $data = oEmbedHelper::getObject($url, $params);
+
+        $this->init_document('json');
+        print json_encode($data);
+    }
+}
diff --git a/plugins/LinkPreview/oembedproxyaction.php b/plugins/LinkPreview/oembedproxyaction.php
deleted file mode 100644 (file)
index 7e54aca..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * StatusNet-only extensions to the Twitter-like API
- *
- * 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   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * Oembed proxy implementation
- *
- * This class provides an interface for our JS-side code to pull info on
- * links from other sites, using either native oEmbed, our own custom
- * handlers, or the noembed.com offsite proxy service as configured.
- *
- * @category  oEmbed
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-class OembedproxyAction extends OembedAction
-{
-    function handle($args)
-    {
-        // Trigger short error responses; not a human-readable web page.
-        StatusNet::setApi(true);
-
-        // We're not a general oEmbed proxy service; limit to valid sessions.
-        $token = $this->trimmed('token');
-        if (!$token || $token != common_session_token()) {
-            // TRANS: Client error displayed when the session token does not match or is not given.
-            $this->clientError(_m('There was a problem with your session token. '.
-                                 'Try again, please.'));
-        }
-
-        $format = $this->arg('format');
-        if ($format && $format != 'json') {
-            // TRANS: Client exception thrown when requesting a different format than JSON.
-            throw new ClientException(_m('Invalid format; only JSON supported.'));
-        }
-
-        $url = $this->arg('url');
-        if (!common_valid_http_url($url)) {
-            // TRANS: Client exception thrown when not providing a valid URL.
-            throw new ClientException(_m('Invalid URL.'));
-        }
-
-        $params = array();
-        if ($this->arg('maxwidth')) {
-            $params['maxwidth'] = $this->arg('maxwidth');
-        }
-        if ($this->arg('maxheight')) {
-            $params['maxheight'] = $this->arg('maxheight');
-        }
-
-        $data = oEmbedHelper::getObject($url, $params);
-
-        $this->init_document('json');
-        print json_encode($data);
-    }
-}
index f51b6073097b0a4c57d494717bfef175270a3b60..9883650206810d0dc82ea80008a17233a42651d7 100644 (file)
@@ -74,29 +74,6 @@ class MapstractionPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Hook for autoloading classes
-     *
-     * This makes sure our classes get autoloaded from our directory
-     *
-     * @param string $cls name of class being used
-     *
-     * @return boolean event handler return
-     */
-    function onAutoload($cls)
-    {
-        switch ($cls)
-        {
-        case 'AllmapAction':
-        case 'UsermapAction':
-        case 'MapAction':
-            include_once INSTALLDIR.'/plugins/Mapstraction/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Hook for adding extra JavaScript
      *
diff --git a/plugins/Mapstraction/actions/allmap.php b/plugins/Mapstraction/actions/allmap.php
new file mode 100644 (file)
index 0000000..d1a9fdd
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show a map of user's friends' notices
+ *
+ * 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  Mapstraction
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Show a map of user's notices
+ *
+ * @category Mapstraction
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class AllmapAction extends MapAction
+{
+    function prepare($args)
+    {
+        if (parent::prepare($args)) {
+            $cur = common_current_user();
+            $stream = new InboxNoticeStream($this->user, $cur->getProfile());
+            $this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
+                                                NOTICES_PER_PAGE + 1,
+                                                null,
+                                                null);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    function title()
+    {
+        $base = $this->profile->getFancyName();
+
+        if ($this->page == 1) {
+            // TRANS: Page title.
+            // TRANS: %s is a user nickname.
+            return sprintf(_m("%s friends map"),
+                           $base);
+        } else {
+            // @todo CHECKME: does this even happen? May not be needed.
+            // TRANS: Page title.
+            // TRANS: %1$s is a user nickname, %2$d is a page number.
+            return sprintf(_m('%1$s friends map, page %2$d'),
+                           $base,
+                           $this->page);
+        }
+    }
+}
diff --git a/plugins/Mapstraction/actions/map.php b/plugins/Mapstraction/actions/map.php
new file mode 100644 (file)
index 0000000..15f3b94
--- /dev/null
@@ -0,0 +1,166 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show a map of user's notices
+ *
+ * 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  Mapstraction
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009-2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Show a map of notices
+ *
+ * @category Mapstraction
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class MapAction extends Action
+{
+    var $profile = null;
+    var $page    = null;
+    var $notices = null;
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $nickname_arg = $this->arg('nickname');
+        $nickname     = Nickname::normalize($nickname_arg);
+
+        // Permanent redirect on non-canonical nickname
+
+        if ($nickname_arg != $nickname) {
+            $args = array('nickname' => $nickname);
+            if ($this->arg('page') && $this->arg('page') != 1) {
+                $args['page'] = $this->arg['page'];
+            }
+            common_redirect(common_local_url($this->trimmed('action'), $args), 301);
+            return false;
+        }
+
+        $this->user = User::getKV('nickname', $nickname);
+
+        if (!$this->user) {
+            // TRANS: Client error displayed when referring to a non-existing user.
+            $this->clientError(_m('No such user.'), 404);
+            return false;
+        }
+
+        $this->profile = $this->user->getProfile();
+
+        if (!$this->profile) {
+            // TRANS: Error message displayed when referring to a user without a profile.
+            $this->serverError(_m('User has no profile.'));
+            return false;
+        }
+
+        $page = $this->trimmed('page');
+
+        if (!empty($page) && Validate::number($page)) {
+            $this->page = $page+0;
+        } else {
+            $this->page = 1;
+        }
+
+        $this->notices = empty($this->tag)
+          ? $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1)
+            : $this->user->getTaggedNotices($this->tag, ($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1, 0, 0, null);
+
+        return true;
+    }
+
+    function handle($args)
+    {
+        parent::handle($args);
+        $this->showPage();
+    }
+
+    function showContent()
+    {
+        $this->element('div', array('id' => 'map_canvas',
+                                    'class' => 'gray smallmap',
+                                    'style' => "width: 100%; height: 400px"));
+    }
+
+    /**
+     * Hook for adding extra JavaScript
+     *
+     * @param Action $action Action object for the page
+     *
+     * @return boolean event handler return
+     */
+    function showScripts()
+    {
+        parent::showScripts();
+        $jsonArray = array();
+
+        while ($this->notice->fetch()) {
+            if (!empty($this->notice->lat) && !empty($this->notice->lon)) {
+                $jsonNotice = $this->noticeAsJson($this->notice);
+                $jsonArray[] = $jsonNotice;
+            }
+        }
+
+        $this->inlineScript('$(document).ready(function() { '.
+                            ' var _notices = ' . json_encode($jsonArray).'; ' .
+                            'showMapstraction($("#map_canvas"), _notices); });');
+
+        return true;
+    }
+
+    function noticeAsJson($notice)
+    {
+        // FIXME: this code should be abstracted to a neutral third
+        // party, like Notice::asJson(). I'm not sure of the ethics
+        // of refactoring from within a plugin, so I'm just abusing
+        // the ApiAction method. Don't do this unless you're me!
+
+        $act = new ApiAction('/dev/null');
+
+        $arr = $act->twitterStatusArray($notice, true);
+        $arr['url'] = $notice->bestUrl();
+        $arr['html'] = $notice->rendered;
+        $arr['source'] = $arr['source'];
+
+        if (!empty($notice->reply_to)) {
+            $reply_to = Notice::getKV('id', $notice->reply_to);
+            if (!empty($reply_to)) {
+                $arr['in_reply_to_status_url'] = $reply_to->bestUrl();
+            }
+            $reply_to = null;
+        }
+
+        $profile = $notice->getProfile();
+        $arr['user']['profile_url'] = $profile->profileurl;
+
+        return $arr;
+    }
+}
diff --git a/plugins/Mapstraction/actions/usermap.php b/plugins/Mapstraction/actions/usermap.php
new file mode 100644 (file)
index 0000000..99a43e5
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show a map of user's notices
+ *
+ * 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  Mapstraction
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Show a map of user's notices
+ *
+ * @category Mapstraction
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class UsermapAction extends MapAction
+{
+    function prepare($args)
+    {
+        if(parent::prepare($args)) {
+            $this->notice = empty($this->tag)
+              ? $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1)
+                : $this->user->getTaggedNotices($this->tag, ($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1, 0, 0, null);
+            return true;
+        }else{
+            return false;
+        }
+    }
+
+    function title()
+    {
+        $base = $this->profile->getFancyName();
+
+        if ($this->page == 1) {
+            // TRANS: Title for map widget.
+            // TRANS: %s is a user name.
+            return sprintf(_m('%s map'),$base);
+        } else {
+            // @todo CHECKME: Is the part ", page %2$d" relevant here?
+            // TRANS: Title for map widget.
+            // TRANS: %1$s is a user name, %2$d is a page nember.
+            return sprintf(_m("%1$s map, page %2$d"),
+                           $base,
+                           $this->page);
+        }
+    }
+}
diff --git a/plugins/Mapstraction/allmap.php b/plugins/Mapstraction/allmap.php
deleted file mode 100644 (file)
index d1a9fdd..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Show a map of user's friends' notices
- *
- * 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  Mapstraction
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Show a map of user's notices
- *
- * @category Mapstraction
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @author   Craig Andrews <candrews@integralblue.com>
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class AllmapAction extends MapAction
-{
-    function prepare($args)
-    {
-        if (parent::prepare($args)) {
-            $cur = common_current_user();
-            $stream = new InboxNoticeStream($this->user, $cur->getProfile());
-            $this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
-                                                NOTICES_PER_PAGE + 1,
-                                                null,
-                                                null);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    function title()
-    {
-        $base = $this->profile->getFancyName();
-
-        if ($this->page == 1) {
-            // TRANS: Page title.
-            // TRANS: %s is a user nickname.
-            return sprintf(_m("%s friends map"),
-                           $base);
-        } else {
-            // @todo CHECKME: does this even happen? May not be needed.
-            // TRANS: Page title.
-            // TRANS: %1$s is a user nickname, %2$d is a page number.
-            return sprintf(_m('%1$s friends map, page %2$d'),
-                           $base,
-                           $this->page);
-        }
-    }
-}
diff --git a/plugins/Mapstraction/map.php b/plugins/Mapstraction/map.php
deleted file mode 100644 (file)
index 15f3b94..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Show a map of user's notices
- *
- * 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  Mapstraction
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2009-2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Show a map of notices
- *
- * @category Mapstraction
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @author   Craig Andrews <candrews@integralblue.com>
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class MapAction extends Action
-{
-    var $profile = null;
-    var $page    = null;
-    var $notices = null;
-
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $nickname_arg = $this->arg('nickname');
-        $nickname     = Nickname::normalize($nickname_arg);
-
-        // Permanent redirect on non-canonical nickname
-
-        if ($nickname_arg != $nickname) {
-            $args = array('nickname' => $nickname);
-            if ($this->arg('page') && $this->arg('page') != 1) {
-                $args['page'] = $this->arg['page'];
-            }
-            common_redirect(common_local_url($this->trimmed('action'), $args), 301);
-            return false;
-        }
-
-        $this->user = User::getKV('nickname', $nickname);
-
-        if (!$this->user) {
-            // TRANS: Client error displayed when referring to a non-existing user.
-            $this->clientError(_m('No such user.'), 404);
-            return false;
-        }
-
-        $this->profile = $this->user->getProfile();
-
-        if (!$this->profile) {
-            // TRANS: Error message displayed when referring to a user without a profile.
-            $this->serverError(_m('User has no profile.'));
-            return false;
-        }
-
-        $page = $this->trimmed('page');
-
-        if (!empty($page) && Validate::number($page)) {
-            $this->page = $page+0;
-        } else {
-            $this->page = 1;
-        }
-
-        $this->notices = empty($this->tag)
-          ? $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1)
-            : $this->user->getTaggedNotices($this->tag, ($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1, 0, 0, null);
-
-        return true;
-    }
-
-    function handle($args)
-    {
-        parent::handle($args);
-        $this->showPage();
-    }
-
-    function showContent()
-    {
-        $this->element('div', array('id' => 'map_canvas',
-                                    'class' => 'gray smallmap',
-                                    'style' => "width: 100%; height: 400px"));
-    }
-
-    /**
-     * Hook for adding extra JavaScript
-     *
-     * @param Action $action Action object for the page
-     *
-     * @return boolean event handler return
-     */
-    function showScripts()
-    {
-        parent::showScripts();
-        $jsonArray = array();
-
-        while ($this->notice->fetch()) {
-            if (!empty($this->notice->lat) && !empty($this->notice->lon)) {
-                $jsonNotice = $this->noticeAsJson($this->notice);
-                $jsonArray[] = $jsonNotice;
-            }
-        }
-
-        $this->inlineScript('$(document).ready(function() { '.
-                            ' var _notices = ' . json_encode($jsonArray).'; ' .
-                            'showMapstraction($("#map_canvas"), _notices); });');
-
-        return true;
-    }
-
-    function noticeAsJson($notice)
-    {
-        // FIXME: this code should be abstracted to a neutral third
-        // party, like Notice::asJson(). I'm not sure of the ethics
-        // of refactoring from within a plugin, so I'm just abusing
-        // the ApiAction method. Don't do this unless you're me!
-
-        $act = new ApiAction('/dev/null');
-
-        $arr = $act->twitterStatusArray($notice, true);
-        $arr['url'] = $notice->bestUrl();
-        $arr['html'] = $notice->rendered;
-        $arr['source'] = $arr['source'];
-
-        if (!empty($notice->reply_to)) {
-            $reply_to = Notice::getKV('id', $notice->reply_to);
-            if (!empty($reply_to)) {
-                $arr['in_reply_to_status_url'] = $reply_to->bestUrl();
-            }
-            $reply_to = null;
-        }
-
-        $profile = $notice->getProfile();
-        $arr['user']['profile_url'] = $profile->profileurl;
-
-        return $arr;
-    }
-}
diff --git a/plugins/Mapstraction/usermap.php b/plugins/Mapstraction/usermap.php
deleted file mode 100644 (file)
index 99a43e5..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Show a map of user's notices
- *
- * 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  Mapstraction
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Show a map of user's notices
- *
- * @category Mapstraction
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @author   Craig Andrews <candrews@integralblue.com>
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class UsermapAction extends MapAction
-{
-    function prepare($args)
-    {
-        if(parent::prepare($args)) {
-            $this->notice = empty($this->tag)
-              ? $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1)
-                : $this->user->getTaggedNotices($this->tag, ($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1, 0, 0, null);
-            return true;
-        }else{
-            return false;
-        }
-    }
-
-    function title()
-    {
-        $base = $this->profile->getFancyName();
-
-        if ($this->page == 1) {
-            // TRANS: Title for map widget.
-            // TRANS: %s is a user name.
-            return sprintf(_m('%s map'),$base);
-        } else {
-            // @todo CHECKME: Is the part ", page %2$d" relevant here?
-            // TRANS: Title for map widget.
-            // TRANS: %1$s is a user name, %2$d is a page nember.
-            return sprintf(_m("%1$s map, page %2$d"),
-                           $base,
-                           $this->page);
-        }
-    }
-}
index 4041b228aee41f02ed7e71587dc102ce68648cfa..9a1f4110d1c12fe6a53fab2f7b61d015cfdf5dd7 100644 (file)
@@ -58,18 +58,6 @@ class MinifyPlugin extends Plugin
         return true;
     }
 
-    function onAutoload($cls)
-    {
-        switch ($cls)
-        {
-         case 'MinifyAction':
-            require_once(INSTALLDIR.'/plugins/Minify/' . strtolower(mb_substr($cls, 0, -6)) . '.php');
-            return false;
-         default:
-            return true;
-        }
-    }
-
     function onLoginAction($action, &$login)
     {
         switch ($action)
diff --git a/plugins/Minify/actions/minify.php b/plugins/Minify/actions/minify.php
new file mode 100644 (file)
index 0000000..b7bee7c
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+class MinifyAction extends Action
+{
+    const TYPE_CSS = 'text/css';
+    const TYPE_HTML = 'text/html';
+    // there is some debate over the ideal JS Content-Type, but this is the
+    // Apache default and what Yahoo! uses..
+    const TYPE_JS = 'application/x-javascript';
+
+    var $file;
+    var $v;
+
+    function isReadOnly($args)
+    {
+        return true;
+    }
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+        $this->v = $args['v'];
+
+        $f = $this->arg('f');
+        if(isset($f)) {
+            $this->file = INSTALLDIR.'/'.$f;
+            if(file_exists($this->file)) {
+                return true;
+            } else {
+                // TRANS: Client error displayed when not providing a valid path in parameter "f".
+                $this->clientError(_m('The parameter "f" is not a valid path.'),404);
+                return false;
+            }
+        }else{
+            // TRANS: Client error displayed when not providing parameter "f".
+            $this->clientError(_m('The parameter "f" is required but missing.'),500);
+            return false;
+        }
+    }
+
+    function etag()
+    {
+        if(isset($this->v)) {
+            return "\"" . crc32($this->file . $this->v) . "\"";
+        }else{
+            $stat = stat($this->file);
+            return '"' . $stat['ino'] . '-' . $stat['size'] . '-' . $stat['mtime'] . '"';
+        }
+    }
+
+    function lastModified()
+    {
+        return filemtime($this->file);
+    }
+
+    function handle($args)
+    {
+        parent::handle($args);
+
+        $c = Cache::instance();
+        if (!empty($c)) {
+            $cacheKey = Cache::key(MinifyPlugin::cacheKey . ':' . $this->file . '?v=' . empty($this->v)?'':$this->v);
+            $out = $c->get($cacheKey);
+        }
+        if(empty($out)) {
+            $out = $this->minify($this->file);
+        }
+        if (!empty($c)) {
+            $c->set($cacheKey, $out);
+        }
+
+        $sec = session_cache_expire() * 60;
+        header('Cache-Control: public, max-age=' . $sec);
+        header('Pragma: public');
+        $this->raw($out);
+    }
+
+    function minify($file)
+    {
+        $info = pathinfo($file);
+        switch(strtolower($info['extension'])){
+            case 'js':
+                $out = MinifyPlugin::minifyJs(file_get_contents($file));
+                header('Content-Type: ' . self::TYPE_JS);
+                break;
+            case 'css':
+                $options = array();
+                $options['currentDir'] = dirname($file);
+                $options['docRoot'] = INSTALLDIR;
+                $out = MinifyPlugin::minifyCss(file_get_contents($file),$options);
+                header('Content-Type: ' . self::TYPE_CSS);
+                break;
+            default:
+                // TRANS: Client error displayed when trying to minify an unsupported file type.
+                $this->clientError(_m('File type not supported.'),500);
+                return false;
+        }
+        return $out;
+    }
+}
diff --git a/plugins/Minify/minify.php b/plugins/Minify/minify.php
deleted file mode 100644 (file)
index b7bee7c..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-class MinifyAction extends Action
-{
-    const TYPE_CSS = 'text/css';
-    const TYPE_HTML = 'text/html';
-    // there is some debate over the ideal JS Content-Type, but this is the
-    // Apache default and what Yahoo! uses..
-    const TYPE_JS = 'application/x-javascript';
-
-    var $file;
-    var $v;
-
-    function isReadOnly($args)
-    {
-        return true;
-    }
-
-    function prepare($args)
-    {
-        parent::prepare($args);
-        $this->v = $args['v'];
-
-        $f = $this->arg('f');
-        if(isset($f)) {
-            $this->file = INSTALLDIR.'/'.$f;
-            if(file_exists($this->file)) {
-                return true;
-            } else {
-                // TRANS: Client error displayed when not providing a valid path in parameter "f".
-                $this->clientError(_m('The parameter "f" is not a valid path.'),404);
-                return false;
-            }
-        }else{
-            // TRANS: Client error displayed when not providing parameter "f".
-            $this->clientError(_m('The parameter "f" is required but missing.'),500);
-            return false;
-        }
-    }
-
-    function etag()
-    {
-        if(isset($this->v)) {
-            return "\"" . crc32($this->file . $this->v) . "\"";
-        }else{
-            $stat = stat($this->file);
-            return '"' . $stat['ino'] . '-' . $stat['size'] . '-' . $stat['mtime'] . '"';
-        }
-    }
-
-    function lastModified()
-    {
-        return filemtime($this->file);
-    }
-
-    function handle($args)
-    {
-        parent::handle($args);
-
-        $c = Cache::instance();
-        if (!empty($c)) {
-            $cacheKey = Cache::key(MinifyPlugin::cacheKey . ':' . $this->file . '?v=' . empty($this->v)?'':$this->v);
-            $out = $c->get($cacheKey);
-        }
-        if(empty($out)) {
-            $out = $this->minify($this->file);
-        }
-        if (!empty($c)) {
-            $c->set($cacheKey, $out);
-        }
-
-        $sec = session_cache_expire() * 60;
-        header('Cache-Control: public, max-age=' . $sec);
-        header('Pragma: public');
-        $this->raw($out);
-    }
-
-    function minify($file)
-    {
-        $info = pathinfo($file);
-        switch(strtolower($info['extension'])){
-            case 'js':
-                $out = MinifyPlugin::minifyJs(file_get_contents($file));
-                header('Content-Type: ' . self::TYPE_JS);
-                break;
-            case 'css':
-                $options = array();
-                $options['currentDir'] = dirname($file);
-                $options['docRoot'] = INSTALLDIR;
-                $out = MinifyPlugin::minifyCss(file_get_contents($file),$options);
-                header('Content-Type: ' . self::TYPE_CSS);
-                break;
-            default:
-                // TRANS: Client error displayed when trying to minify an unsupported file type.
-                $this->clientError(_m('File type not supported.'),500);
-                return false;
-        }
-        return $out;
-    }
-}
diff --git a/plugins/ModLog/ModLog.php b/plugins/ModLog/ModLog.php
deleted file mode 100644 (file)
index 606b786..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2012, StatusNet, Inc.
- *
- * ModLog.php -- data object to store moderation logs
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Moderation
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2012 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Class comment here
- *
- * @category Category here
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-
-class ModLog extends Managed_DataObject
-{
-    public $__table = 'mod_log'; // table name
-
-    public $id;           // UUID
-    public $profile_id;   // profile id
-    public $moderator_id; // profile id
-    public $role;         // the role
-    public $grant;        // 1 = grant, 0 = revoke
-    public $created;      // datetime
-
-    /**
-     * The One True Thingy that must be defined and declared.
-     */
-    public static function schemaDef()
-    {
-        return array('description' => 'Log of moderation events',
-                     'fields' => array(
-                                       'id' => array('type' => 'varchar',
-                                                     'length' => 36,
-                                                     'not null' => true,
-                                                     'description' => 'unique event ID'),
-                                       'profile_id' => array('type' => 'int',
-                                                             'not null' => true,
-                                                             'description' => 'profile getting the role'),
-                                       'moderator_id' => array('type' => 'int',
-                                                               'description' => 'profile granting or revoking the role'),
-                                       'role' => array('type' => 'varchar',
-                                                       'length' => 32,
-                                                       'not null' => true,
-                                                       'description' => 'role granted or revoked'),
-                                       'is_grant' => array('type' => 'int',
-                                                           'size' => 'tiny',
-                                                           'default' => 1,
-                                                           'description' => 'Was this a grant or revocation of a role'),
-                                       'created' => array('type' => 'datetime',
-                                                          'not null' => true,
-                                                          'description' => 'date this record was created')
-                                       ),
-                     'primary key' => array('id'),
-                     'foreign keys' => array(
-                                             'mod_log_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
-                                             'mod_log_moderator_id_fkey' => array('user', array('user_id' => 'id'))
-                                             ),
-                     'indexes' => array(
-                                        'mod_log_profile_id_created_idx' => array('profile_id', 'created'),
-                                        ),
-                     );
-    }
-}
index 48229232f27f7a52356f1a68a95e45454ba48882..89f71fe866030537d5935a481356f65b6284ce3c 100644 (file)
@@ -71,28 +71,6 @@ class ModLogPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'ModLog':
-            include_once $dir . '/'.$cls.'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     function onEndGrantRole($profile, $role)
     {
         $modlog = new ModLog();
diff --git a/plugins/ModLog/classes/ModLog.php b/plugins/ModLog/classes/ModLog.php
new file mode 100644 (file)
index 0000000..606b786
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2012, StatusNet, Inc.
+ *
+ * ModLog.php -- data object to store moderation logs
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Moderation
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Class comment here
+ *
+ * @category Category here
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+
+class ModLog extends Managed_DataObject
+{
+    public $__table = 'mod_log'; // table name
+
+    public $id;           // UUID
+    public $profile_id;   // profile id
+    public $moderator_id; // profile id
+    public $role;         // the role
+    public $grant;        // 1 = grant, 0 = revoke
+    public $created;      // datetime
+
+    /**
+     * The One True Thingy that must be defined and declared.
+     */
+    public static function schemaDef()
+    {
+        return array('description' => 'Log of moderation events',
+                     'fields' => array(
+                                       'id' => array('type' => 'varchar',
+                                                     'length' => 36,
+                                                     'not null' => true,
+                                                     'description' => 'unique event ID'),
+                                       'profile_id' => array('type' => 'int',
+                                                             'not null' => true,
+                                                             'description' => 'profile getting the role'),
+                                       'moderator_id' => array('type' => 'int',
+                                                               'description' => 'profile granting or revoking the role'),
+                                       'role' => array('type' => 'varchar',
+                                                       'length' => 32,
+                                                       'not null' => true,
+                                                       'description' => 'role granted or revoked'),
+                                       'is_grant' => array('type' => 'int',
+                                                           'size' => 'tiny',
+                                                           'default' => 1,
+                                                           'description' => 'Was this a grant or revocation of a role'),
+                                       'created' => array('type' => 'datetime',
+                                                          'not null' => true,
+                                                          'description' => 'date this record was created')
+                                       ),
+                     'primary key' => array('id'),
+                     'foreign keys' => array(
+                                             'mod_log_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
+                                             'mod_log_moderator_id_fkey' => array('user', array('user_id' => 'id'))
+                                             ),
+                     'indexes' => array(
+                                        'mod_log_profile_id_created_idx' => array('profile_id', 'created'),
+                                        ),
+                     );
+    }
+}
index c8c88c9d7a391bc5095797cfaa30a7726a68d09c..d66ec6054a629724df0d7816bee66b96fdecc0e0 100644 (file)
@@ -62,28 +62,6 @@ class ModPlusPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Autoloader
-     *
-     * Loads our classes if they're requested.
-     *
-     * @param string $cls Class requested
-     *
-     * @return boolean hook return
-     */
-    function onAutoload($cls)
-    {
-        switch ($cls)
-        {
-        case 'RemoteprofileAction':
-        case 'RemoteProfileAction':
-            require_once dirname(__FILE__) . '/remoteprofileaction.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Add ModPlus-related paths to the router table
      *
diff --git a/plugins/ModPlus/actions/remoteprofile.php b/plugins/ModPlus/actions/remoteprofile.php
new file mode 100644 (file)
index 0000000..bfcc5a8
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+class RemoteProfileAction extends ShowstreamAction
+{
+    function prepare($args)
+    {
+        Action::prepare($args); // skip the ProfileAction code and replace it...
+
+        $id = $this->arg('id');
+        $this->user = false;
+        $this->profile = Profile::getKV('id', $id);
+
+        if (!$this->profile) {
+            // TRANS: Error message displayed when referring to a user without a profile.
+            $this->serverError(_m('User has no profile.'));
+            return false;
+        }
+
+        $user = User::getKV('id', $this->profile->id);
+        if ($user) {
+            // This is a local user -- send to their regular profile.
+            $url = common_local_url('showstream', array('nickname' => $user->nickname));
+            common_redirect($url);
+            return false;
+        }
+
+        $this->tag = $this->trimmed('tag');
+        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
+        common_set_returnto($this->selfUrl());
+
+        $p = Profile::current();
+        if (empty($this->tag)) {
+            $stream = new ProfileNoticeStream($this->profile, $p);
+        } else {
+            $stream = new TaggedProfileNoticeStream($this->profile, $this->tag, $p);
+        }
+        $this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
+
+        return true;
+    }
+
+    function handle($args)
+    {
+        // skip yadis thingy
+        $this->showPage();
+    }
+
+    function title()
+    {
+        $base = $this->profile->getBestName();
+        $host = parse_url($this->profile->profileurl, PHP_URL_HOST);
+        // TRANS: Remote profile action page title.
+        // TRANS: %1$s is a username, %2$s is a hostname.
+        return sprintf(_m('%1$s on %2$s'), $base, $host);
+    }
+
+    /**
+     * Instead of showing notices, link to the original offsite profile.
+     */
+    function showNotices()
+    {
+        $url = $this->profile->profileurl;
+        $host = parse_url($url, PHP_URL_HOST);
+        $markdown = sprintf(
+                // TRANS: Message on remote profile page.
+                // TRANS: This message contains Markdown links in the form [description](link).
+                // TRANS: %1$s is a profile nickname, %2$s is a hostname, %3$s is a URL.
+                _m('This remote profile is registered on another site; see [%1$s\'s original profile page on %2$s](%3$s).'),
+                $this->profile->nickname,
+                $host,
+                $url);
+        $html = common_markup_to_html($markdown);
+        $this->raw($html);
+
+        if ($this->profile->hasRole(Profile_role::SILENCED)) {
+            // TRANS: Message on blocked remote profile page.
+            $markdown = _m('Site moderators have silenced this profile, which prevents delivery of new messages to any users on this site.');
+            $this->raw(common_markup_to_html($markdown));
+        }else{
+
+            $pnl = null;
+            if (Event::handle('ShowStreamNoticeList', array($this->notice, $this, &$pnl))) {
+                $pnl = new ProfileNoticeList($this->notice, $this);
+            }
+            $cnt = $pnl->show();
+            if (0 == $cnt) {
+                $this->showEmptyListMessage();
+            }
+
+            $args = array('id' => $this->profile->id);
+            if (!empty($this->tag))
+            {
+                $args['tag'] = $this->tag;
+            }
+            $this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page,
+                              'remoteprofile', $args);
+
+        }
+    }
+
+    function getFeeds()
+    {
+        // none
+    }
+
+    /**
+     * Don't do various extra stuff, and also trim some things to avoid crawlers.
+     */
+    function extraHead()
+    {
+        $this->element('meta', array('name' => 'robots',
+                                     'content' => 'noindex,nofollow'));
+    }
+
+    function showLocalNav()
+    {
+        // skip
+    }
+
+    function showSections()
+    {
+        // skip
+    }
+
+    function showStatistics()
+    {
+        // skip
+    }
+}
diff --git a/plugins/ModPlus/remoteprofileaction.php b/plugins/ModPlus/remoteprofileaction.php
deleted file mode 100644 (file)
index bfcc5a8..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-class RemoteProfileAction extends ShowstreamAction
-{
-    function prepare($args)
-    {
-        Action::prepare($args); // skip the ProfileAction code and replace it...
-
-        $id = $this->arg('id');
-        $this->user = false;
-        $this->profile = Profile::getKV('id', $id);
-
-        if (!$this->profile) {
-            // TRANS: Error message displayed when referring to a user without a profile.
-            $this->serverError(_m('User has no profile.'));
-            return false;
-        }
-
-        $user = User::getKV('id', $this->profile->id);
-        if ($user) {
-            // This is a local user -- send to their regular profile.
-            $url = common_local_url('showstream', array('nickname' => $user->nickname));
-            common_redirect($url);
-            return false;
-        }
-
-        $this->tag = $this->trimmed('tag');
-        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
-        common_set_returnto($this->selfUrl());
-
-        $p = Profile::current();
-        if (empty($this->tag)) {
-            $stream = new ProfileNoticeStream($this->profile, $p);
-        } else {
-            $stream = new TaggedProfileNoticeStream($this->profile, $this->tag, $p);
-        }
-        $this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
-
-        return true;
-    }
-
-    function handle($args)
-    {
-        // skip yadis thingy
-        $this->showPage();
-    }
-
-    function title()
-    {
-        $base = $this->profile->getBestName();
-        $host = parse_url($this->profile->profileurl, PHP_URL_HOST);
-        // TRANS: Remote profile action page title.
-        // TRANS: %1$s is a username, %2$s is a hostname.
-        return sprintf(_m('%1$s on %2$s'), $base, $host);
-    }
-
-    /**
-     * Instead of showing notices, link to the original offsite profile.
-     */
-    function showNotices()
-    {
-        $url = $this->profile->profileurl;
-        $host = parse_url($url, PHP_URL_HOST);
-        $markdown = sprintf(
-                // TRANS: Message on remote profile page.
-                // TRANS: This message contains Markdown links in the form [description](link).
-                // TRANS: %1$s is a profile nickname, %2$s is a hostname, %3$s is a URL.
-                _m('This remote profile is registered on another site; see [%1$s\'s original profile page on %2$s](%3$s).'),
-                $this->profile->nickname,
-                $host,
-                $url);
-        $html = common_markup_to_html($markdown);
-        $this->raw($html);
-
-        if ($this->profile->hasRole(Profile_role::SILENCED)) {
-            // TRANS: Message on blocked remote profile page.
-            $markdown = _m('Site moderators have silenced this profile, which prevents delivery of new messages to any users on this site.');
-            $this->raw(common_markup_to_html($markdown));
-        }else{
-
-            $pnl = null;
-            if (Event::handle('ShowStreamNoticeList', array($this->notice, $this, &$pnl))) {
-                $pnl = new ProfileNoticeList($this->notice, $this);
-            }
-            $cnt = $pnl->show();
-            if (0 == $cnt) {
-                $this->showEmptyListMessage();
-            }
-
-            $args = array('id' => $this->profile->id);
-            if (!empty($this->tag))
-            {
-                $args['tag'] = $this->tag;
-            }
-            $this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page,
-                              'remoteprofile', $args);
-
-        }
-    }
-
-    function getFeeds()
-    {
-        // none
-    }
-
-    /**
-     * Don't do various extra stuff, and also trim some things to avoid crawlers.
-     */
-    function extraHead()
-    {
-        $this->element('meta', array('name' => 'robots',
-                                     'content' => 'noindex,nofollow'));
-    }
-
-    function showLocalNav()
-    {
-        // skip
-    }
-
-    function showSections()
-    {
-        // skip
-    }
-
-    function showStatistics()
-    {
-        // skip
-    }
-}
index 38602668b9b5047f2f92edfbb4838a11885a73b5..91c67f5f799d2f669b49a4699f04dc258f5b25a9 100644 (file)
@@ -105,13 +105,9 @@ class MsnPlugin extends ImPlugin {
             case 'MSN':\r
                 require_once(INSTALLDIR.'/plugins/Msn/extlib/phpmsnclass/msn.class.php');\r
                 return false;\r
-            case 'MsnManager':\r
-            case 'Msn_waiting_message':\r
-                include_once $dir . '/'.strtolower($cls).'.php';\r
-                return false;\r
-            default:\r
-                return true;\r
         }\r
+\r
+        return parent::onAutoload($cls);\r
     }\r
 \r
     /*\r
diff --git a/plugins/Msn/classes/msn_waiting_message.php b/plugins/Msn/classes/msn_waiting_message.php
new file mode 100644 (file)
index 0000000..cfb69de
--- /dev/null
@@ -0,0 +1,87 @@
+<?php\r
+/**\r
+ * Table Definition for msn_waiting_message\r
+ */\r
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';\r
+\r
+class Msn_waiting_message extends Managed_DataObject {\r
+\r
+    public $__table = 'msn_waiting_message'; // table name\r
+    public $id;                              // int primary_key not_null auto_increment\r
+    public $screenname;                      // varchar(255) not_null\r
+    public $message;                         // text not_null\r
+    public $claimed;                         // datetime()\r
+    public $created;                         // datetime()   not_null\r
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP\r
+\r
+    public static function schemaDef()\r
+    {\r
+        return array(\r
+            'fields' => array(\r
+                'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique ID for entry'),\r
+                'screenname' => array('type' => 'varchar', 'length' => 255, 'description' => 'from screenname'),\r
+                'message' => array('type' => 'text', 'not null' => true, 'description' => 'MSN message text'),\r
+                'claimed' => array('type' => 'datetime', 'description' => 'date this irc message was claimed'),\r
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),\r
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),\r
+            ),\r
+            'primary key' => array('id'),\r
+            'indexes' => array(\r
+                'msn_waiting_message_prioritise_idx' => array('screenname'),\r
+            ),\r
+        );\r
+    }\r
+\r
+    /**\r
+     * @param string $screenname screenname or array of screennames to pull from\r
+     *                          If not specified, checks all queues in the system.\r
+     */\r
+    public static function top($screenname = null) {\r
+        $wm = new Msn_waiting_message();\r
+        if ($screenname) {\r
+            if (is_array($screenname)) {\r
+                // @fixme use safer escaping\r
+                $list = implode("','", array_map('addslashes', $screenname));\r
+                $wm->whereAdd("screenname in ('$list')");\r
+            } else {\r
+                $wm->screenname = $screenname;\r
+            }\r
+        }\r
+        $wm->orderBy('created');\r
+        $wm->whereAdd('claimed is null');\r
+\r
+        $wm->limit(1);\r
+\r
+        $cnt = $wm->find(true);\r
+\r
+        if ($cnt) {\r
+            // XXX: potential race condition\r
+            // can we force it to only update if claimed is still null\r
+            // (or old)?\r
+            common_log(LOG_INFO, 'claiming msn waiting message id = ' . $wm->id);\r
+            $orig = clone($wm);\r
+            $wm->claimed = common_sql_now();\r
+            $result = $wm->update($orig);\r
+            if ($result) {\r
+                common_log(LOG_INFO, 'claim succeeded.');\r
+                return $wm;\r
+            } else {\r
+                common_log(LOG_INFO, 'claim failed.');\r
+            }\r
+        }\r
+        $wm = null;\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * Release a claimed item.\r
+     */\r
+    public function releaseClaim() {\r
+        // DB_DataObject doesn't let us save nulls right now\r
+        $sql = sprintf("UPDATE msn_waiting_message SET claimed=NULL WHERE id=%d", $this->id);\r
+        $this->query($sql);\r
+\r
+        $this->claimed = null;\r
+        $this->encache();\r
+    }\r
+}\r
diff --git a/plugins/Msn/lib/msnmanager.php b/plugins/Msn/lib/msnmanager.php
new file mode 100644 (file)
index 0000000..82d40d6
--- /dev/null
@@ -0,0 +1,276 @@
+<?php\r
+/*\r
+ * StatusNet - the distributed open-source microblogging tool\r
+ * Copyright (C) 2008, 2009, StatusNet, Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU Affero General Public License as published by\r
+ * the Free Software Foundation, either version 3 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU Affero General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Affero General Public License\r
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
+ */\r
+\r
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\r
+\r
+/**\r
+ * MSN background connection manager for MSN-using queue handlers,\r
+ * allowing them to send outgoing messages on the right connection.\r
+ *\r
+ * Input is handled during socket select loop, keepalive pings during idle.\r
+ * Any incoming messages will be handled.\r
+ *\r
+ * In a multi-site queuedaemon.php run, one connection will be instantiated\r
+ * for each site being handled by the current process that has MSN enabled.\r
+ */\r
+class MsnManager extends ImManager {\r
+    public $conn = null;\r
+    protected $lastPing = null;\r
+    protected $pingInterval;\r
+\r
+    /**\r
+     * Initialise connection to server.\r
+     *\r
+     * @return boolean true on success\r
+     */\r
+    public function start($master) {\r
+        if (parent::start($master)) {\r
+            $this->requeue_waiting_messages();\r
+            $this->connect();\r
+            return true;\r
+        } else {\r
+            return false;\r
+        }\r
+    }\r
+\r
+    /**\r
+    * Return any open sockets that the run loop should listen\r
+    * for input on.\r
+    *\r
+    * @return array Array of socket resources\r
+    */\r
+    public function getSockets() {\r
+        $this->connect();\r
+        if ($this->conn) {\r
+            return $this->conn->getSockets();\r
+        } else {\r
+            return array();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Idle processing for io manager's execution loop.\r
+     * Send keepalive pings to server.\r
+     *\r
+     * @return void\r
+     */\r
+    public function idle($timeout = 0) {\r
+        if (empty($this->lastPing) || time() - $this->lastPing > $this->pingInterval) {\r
+            $this->send_ping();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Message pump is triggered on socket input, so we only need an idle()\r
+     * call often enough to trigger our outgoing pings.\r
+     */\r
+    public function timeout() {\r
+        return $this->pingInterval;\r
+    }\r
+\r
+    /**\r
+     * Process MSN events that have come in over the wire.\r
+     *\r
+     * @param resource $socket Socket ready\r
+     * @return void\r
+     */\r
+    public function handleInput($socket) {\r
+        common_log(LOG_DEBUG, 'Servicing the MSN queue.');\r
+        $this->stats('msn_process');\r
+        $this->conn->receive();\r
+    }\r
+\r
+    /**\r
+    * Initiate connection\r
+    *\r
+    * @return void\r
+    */\r
+    public function connect() {\r
+        if (!$this->conn) {\r
+            $this->conn = new MSN(\r
+                array(\r
+                    'user' => $this->plugin->user,\r
+                    'password' => $this->plugin->password,\r
+                    'alias' => $this->plugin->nickname,\r
+                    // TRANS: MSN bot status message.\r
+                    'psm' => _m('Send me a message to post a notice'),\r
+                    'debug' => false\r
+                )\r
+            );\r
+            $this->conn->registerHandler('IMin', array($this, 'handle_msn_message'));\r
+            $this->conn->registerHandler('SessionReady', array($this, 'handle_session_ready'));\r
+            $this->conn->registerHandler('Pong', array($this, 'update_ping_time'));\r
+            $this->conn->registerHandler('ConnectFailed', array($this, 'handle_connect_failed'));\r
+            $this->conn->registerHandler('Reconnect', array($this, 'handle_reconnect'));\r
+            $this->conn->signon();\r
+            $this->lastPing = time();\r
+        }\r
+        return $this->conn;\r
+    }\r
+\r
+    /**\r
+    * Called by the idle process to send a ping\r
+    * when necessary\r
+    *\r
+    * @return void\r
+    */\r
+    protected function send_ping() {\r
+        $this->connect();\r
+        if (!$this->conn) {\r
+            return false;\r
+        }\r
+\r
+        $this->conn->sendPing();\r
+        $this->lastPing = time();\r
+        $this->pingInterval = 50;\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Update the time till the next ping\r
+     *\r
+     * @param $data Time till next ping\r
+     * @return void\r
+     */\r
+    public function update_ping_time($data) {\r
+        $this->pingInterval = $data;\r
+    }\r
+\r
+    /**\r
+    * Called via a callback when a message is received\r
+    *\r
+    * Passes it back to the queuing system\r
+    *\r
+    * @param array $data Data\r
+    * @return boolean\r
+    */\r
+    public function handle_msn_message($data) {\r
+        $this->plugin->enqueueIncomingRaw($data);\r
+        return true;\r
+    }\r
+\r
+    /**\r
+    * Called via a callback when a session becomes ready\r
+    *\r
+    * @param array $data Data\r
+    */\r
+    public function handle_session_ready($data) {\r
+        $sessionFailed = false;\r
+        $wm = Msn_waiting_message::top($data['to']);\r
+        while ($wm != NULL) {\r
+            if ($sessionFailed) {\r
+                $this->plugin->sendMessage($wm->screenname, $wm->message);\r
+                $sessionFailed = true;\r
+            } elseif (!$this->conn->sendMessage($wm->screenname, $wm->message, $ignore)) {\r
+                $this->plugin->sendMessage($wm->screenname, $wm->message);\r
+            }\r
+\r
+            $wm->delete();\r
+            $wm = Msn_waiting_message::top($data['to']);\r
+        }\r
+    }\r
+\r
+    /**\r
+    * Requeue messages from the waiting table so we try\r
+    * to send them again\r
+    *\r
+    * @return void\r
+    */\r
+    protected function requeue_waiting_messages() {\r
+        $wm = Msn_waiting_message::top();\r
+        while ($wm != NULL) {\r
+            $this->plugin->sendMessage($wm->screenname, $wm->message);\r
+            $wm->delete();\r
+            $wm = Msn_waiting_message::top();\r
+        }\r
+    }\r
+\r
+    /**\r
+    * Called by callback to log failure during connect\r
+    *\r
+    * @param string $message error message reported\r
+    * @return void\r
+    */\r
+    public function handle_connect_failed($message) {\r
+        common_log(LOG_NOTICE, 'MSN connect failed, retrying: ' . $message);\r
+    }\r
+\r
+    /**\r
+    * Called by callback to log reconnection\r
+    *\r
+    * @param void $data Not used (there to keep callback happy)\r
+    * @return void\r
+    */\r
+    public function handle_reconnect($data) {\r
+        common_log(LOG_NOTICE, 'MSN reconnecting');\r
+        // Requeue messages waiting in the DB\r
+        $this->requeue_waiting_messages();\r
+    }\r
+\r
+    /**\r
+    * Enters a message into the database for sending via a callback\r
+    * when the session is established\r
+    *\r
+    * @param string $to Intended recipient\r
+    * @param string $message Message\r
+    */\r
+    protected function enqueue_waiting_message($to, $message) {\r
+        $wm = new Msn_waiting_message();\r
+\r
+        $wm->screenname = $to;\r
+        $wm->message    = $message;\r
+        $wm->created    = common_sql_now();\r
+        $result         = $wm->insert();\r
+\r
+        if (!$result) {\r
+            common_log_db_error($wm, 'INSERT', __FILE__);\r
+            // TRANS: Server exception thrown when a message to be sent through MSN cannot be added to the database queue.\r
+            throw new ServerException(_m('Database error inserting queue item.'));\r
+        }\r
+\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Send a message using the daemon\r
+     *\r
+     * @param $data Message data\r
+     * @return boolean true on success\r
+     */\r
+    public function send_raw_message($data) {\r
+        $this->connect();\r
+        if (!$this->conn) {\r
+            return false;\r
+        }\r
+\r
+        $waitForSession = false;\r
+        if (!$this->conn->sendMessage($data['to'], $data['message'], $waitForSession)) {\r
+            if ($waitForSession) {\r
+                $this->enqueue_waiting_message($data['to'], $data['message']);\r
+            } else {\r
+                return false;\r
+            }\r
+        }\r
+\r
+        // Sending a command updates the time till next ping\r
+        $this->lastPing = time();\r
+        $this->pingInterval = 50;\r
+        return true;\r
+    }\r
+}\r
diff --git a/plugins/Msn/msn_waiting_message.php b/plugins/Msn/msn_waiting_message.php
deleted file mode 100644 (file)
index cfb69de..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php\r
-/**\r
- * Table Definition for msn_waiting_message\r
- */\r
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';\r
-\r
-class Msn_waiting_message extends Managed_DataObject {\r
-\r
-    public $__table = 'msn_waiting_message'; // table name\r
-    public $id;                              // int primary_key not_null auto_increment\r
-    public $screenname;                      // varchar(255) not_null\r
-    public $message;                         // text not_null\r
-    public $claimed;                         // datetime()\r
-    public $created;                         // datetime()   not_null\r
-    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP\r
-\r
-    public static function schemaDef()\r
-    {\r
-        return array(\r
-            'fields' => array(\r
-                'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique ID for entry'),\r
-                'screenname' => array('type' => 'varchar', 'length' => 255, 'description' => 'from screenname'),\r
-                'message' => array('type' => 'text', 'not null' => true, 'description' => 'MSN message text'),\r
-                'claimed' => array('type' => 'datetime', 'description' => 'date this irc message was claimed'),\r
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),\r
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),\r
-            ),\r
-            'primary key' => array('id'),\r
-            'indexes' => array(\r
-                'msn_waiting_message_prioritise_idx' => array('screenname'),\r
-            ),\r
-        );\r
-    }\r
-\r
-    /**\r
-     * @param string $screenname screenname or array of screennames to pull from\r
-     *                          If not specified, checks all queues in the system.\r
-     */\r
-    public static function top($screenname = null) {\r
-        $wm = new Msn_waiting_message();\r
-        if ($screenname) {\r
-            if (is_array($screenname)) {\r
-                // @fixme use safer escaping\r
-                $list = implode("','", array_map('addslashes', $screenname));\r
-                $wm->whereAdd("screenname in ('$list')");\r
-            } else {\r
-                $wm->screenname = $screenname;\r
-            }\r
-        }\r
-        $wm->orderBy('created');\r
-        $wm->whereAdd('claimed is null');\r
-\r
-        $wm->limit(1);\r
-\r
-        $cnt = $wm->find(true);\r
-\r
-        if ($cnt) {\r
-            // XXX: potential race condition\r
-            // can we force it to only update if claimed is still null\r
-            // (or old)?\r
-            common_log(LOG_INFO, 'claiming msn waiting message id = ' . $wm->id);\r
-            $orig = clone($wm);\r
-            $wm->claimed = common_sql_now();\r
-            $result = $wm->update($orig);\r
-            if ($result) {\r
-                common_log(LOG_INFO, 'claim succeeded.');\r
-                return $wm;\r
-            } else {\r
-                common_log(LOG_INFO, 'claim failed.');\r
-            }\r
-        }\r
-        $wm = null;\r
-        return null;\r
-    }\r
-\r
-    /**\r
-     * Release a claimed item.\r
-     */\r
-    public function releaseClaim() {\r
-        // DB_DataObject doesn't let us save nulls right now\r
-        $sql = sprintf("UPDATE msn_waiting_message SET claimed=NULL WHERE id=%d", $this->id);\r
-        $this->query($sql);\r
-\r
-        $this->claimed = null;\r
-        $this->encache();\r
-    }\r
-}\r
diff --git a/plugins/Msn/msnmanager.php b/plugins/Msn/msnmanager.php
deleted file mode 100644 (file)
index 82d40d6..0000000
+++ /dev/null
@@ -1,276 +0,0 @@
-<?php\r
-/*\r
- * StatusNet - the distributed open-source microblogging tool\r
- * Copyright (C) 2008, 2009, StatusNet, Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU Affero General Public License as published by\r
- * the Free Software Foundation, either version 3 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU Affero General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Affero General Public License\r
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- */\r
-\r
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\r
-\r
-/**\r
- * MSN background connection manager for MSN-using queue handlers,\r
- * allowing them to send outgoing messages on the right connection.\r
- *\r
- * Input is handled during socket select loop, keepalive pings during idle.\r
- * Any incoming messages will be handled.\r
- *\r
- * In a multi-site queuedaemon.php run, one connection will be instantiated\r
- * for each site being handled by the current process that has MSN enabled.\r
- */\r
-class MsnManager extends ImManager {\r
-    public $conn = null;\r
-    protected $lastPing = null;\r
-    protected $pingInterval;\r
-\r
-    /**\r
-     * Initialise connection to server.\r
-     *\r
-     * @return boolean true on success\r
-     */\r
-    public function start($master) {\r
-        if (parent::start($master)) {\r
-            $this->requeue_waiting_messages();\r
-            $this->connect();\r
-            return true;\r
-        } else {\r
-            return false;\r
-        }\r
-    }\r
-\r
-    /**\r
-    * Return any open sockets that the run loop should listen\r
-    * for input on.\r
-    *\r
-    * @return array Array of socket resources\r
-    */\r
-    public function getSockets() {\r
-        $this->connect();\r
-        if ($this->conn) {\r
-            return $this->conn->getSockets();\r
-        } else {\r
-            return array();\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Idle processing for io manager's execution loop.\r
-     * Send keepalive pings to server.\r
-     *\r
-     * @return void\r
-     */\r
-    public function idle($timeout = 0) {\r
-        if (empty($this->lastPing) || time() - $this->lastPing > $this->pingInterval) {\r
-            $this->send_ping();\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Message pump is triggered on socket input, so we only need an idle()\r
-     * call often enough to trigger our outgoing pings.\r
-     */\r
-    public function timeout() {\r
-        return $this->pingInterval;\r
-    }\r
-\r
-    /**\r
-     * Process MSN events that have come in over the wire.\r
-     *\r
-     * @param resource $socket Socket ready\r
-     * @return void\r
-     */\r
-    public function handleInput($socket) {\r
-        common_log(LOG_DEBUG, 'Servicing the MSN queue.');\r
-        $this->stats('msn_process');\r
-        $this->conn->receive();\r
-    }\r
-\r
-    /**\r
-    * Initiate connection\r
-    *\r
-    * @return void\r
-    */\r
-    public function connect() {\r
-        if (!$this->conn) {\r
-            $this->conn = new MSN(\r
-                array(\r
-                    'user' => $this->plugin->user,\r
-                    'password' => $this->plugin->password,\r
-                    'alias' => $this->plugin->nickname,\r
-                    // TRANS: MSN bot status message.\r
-                    'psm' => _m('Send me a message to post a notice'),\r
-                    'debug' => false\r
-                )\r
-            );\r
-            $this->conn->registerHandler('IMin', array($this, 'handle_msn_message'));\r
-            $this->conn->registerHandler('SessionReady', array($this, 'handle_session_ready'));\r
-            $this->conn->registerHandler('Pong', array($this, 'update_ping_time'));\r
-            $this->conn->registerHandler('ConnectFailed', array($this, 'handle_connect_failed'));\r
-            $this->conn->registerHandler('Reconnect', array($this, 'handle_reconnect'));\r
-            $this->conn->signon();\r
-            $this->lastPing = time();\r
-        }\r
-        return $this->conn;\r
-    }\r
-\r
-    /**\r
-    * Called by the idle process to send a ping\r
-    * when necessary\r
-    *\r
-    * @return void\r
-    */\r
-    protected function send_ping() {\r
-        $this->connect();\r
-        if (!$this->conn) {\r
-            return false;\r
-        }\r
-\r
-        $this->conn->sendPing();\r
-        $this->lastPing = time();\r
-        $this->pingInterval = 50;\r
-        return true;\r
-    }\r
-\r
-    /**\r
-     * Update the time till the next ping\r
-     *\r
-     * @param $data Time till next ping\r
-     * @return void\r
-     */\r
-    public function update_ping_time($data) {\r
-        $this->pingInterval = $data;\r
-    }\r
-\r
-    /**\r
-    * Called via a callback when a message is received\r
-    *\r
-    * Passes it back to the queuing system\r
-    *\r
-    * @param array $data Data\r
-    * @return boolean\r
-    */\r
-    public function handle_msn_message($data) {\r
-        $this->plugin->enqueueIncomingRaw($data);\r
-        return true;\r
-    }\r
-\r
-    /**\r
-    * Called via a callback when a session becomes ready\r
-    *\r
-    * @param array $data Data\r
-    */\r
-    public function handle_session_ready($data) {\r
-        $sessionFailed = false;\r
-        $wm = Msn_waiting_message::top($data['to']);\r
-        while ($wm != NULL) {\r
-            if ($sessionFailed) {\r
-                $this->plugin->sendMessage($wm->screenname, $wm->message);\r
-                $sessionFailed = true;\r
-            } elseif (!$this->conn->sendMessage($wm->screenname, $wm->message, $ignore)) {\r
-                $this->plugin->sendMessage($wm->screenname, $wm->message);\r
-            }\r
-\r
-            $wm->delete();\r
-            $wm = Msn_waiting_message::top($data['to']);\r
-        }\r
-    }\r
-\r
-    /**\r
-    * Requeue messages from the waiting table so we try\r
-    * to send them again\r
-    *\r
-    * @return void\r
-    */\r
-    protected function requeue_waiting_messages() {\r
-        $wm = Msn_waiting_message::top();\r
-        while ($wm != NULL) {\r
-            $this->plugin->sendMessage($wm->screenname, $wm->message);\r
-            $wm->delete();\r
-            $wm = Msn_waiting_message::top();\r
-        }\r
-    }\r
-\r
-    /**\r
-    * Called by callback to log failure during connect\r
-    *\r
-    * @param string $message error message reported\r
-    * @return void\r
-    */\r
-    public function handle_connect_failed($message) {\r
-        common_log(LOG_NOTICE, 'MSN connect failed, retrying: ' . $message);\r
-    }\r
-\r
-    /**\r
-    * Called by callback to log reconnection\r
-    *\r
-    * @param void $data Not used (there to keep callback happy)\r
-    * @return void\r
-    */\r
-    public function handle_reconnect($data) {\r
-        common_log(LOG_NOTICE, 'MSN reconnecting');\r
-        // Requeue messages waiting in the DB\r
-        $this->requeue_waiting_messages();\r
-    }\r
-\r
-    /**\r
-    * Enters a message into the database for sending via a callback\r
-    * when the session is established\r
-    *\r
-    * @param string $to Intended recipient\r
-    * @param string $message Message\r
-    */\r
-    protected function enqueue_waiting_message($to, $message) {\r
-        $wm = new Msn_waiting_message();\r
-\r
-        $wm->screenname = $to;\r
-        $wm->message    = $message;\r
-        $wm->created    = common_sql_now();\r
-        $result         = $wm->insert();\r
-\r
-        if (!$result) {\r
-            common_log_db_error($wm, 'INSERT', __FILE__);\r
-            // TRANS: Server exception thrown when a message to be sent through MSN cannot be added to the database queue.\r
-            throw new ServerException(_m('Database error inserting queue item.'));\r
-        }\r
-\r
-        return true;\r
-    }\r
-\r
-    /**\r
-     * Send a message using the daemon\r
-     *\r
-     * @param $data Message data\r
-     * @return boolean true on success\r
-     */\r
-    public function send_raw_message($data) {\r
-        $this->connect();\r
-        if (!$this->conn) {\r
-            return false;\r
-        }\r
-\r
-        $waitForSession = false;\r
-        if (!$this->conn->sendMessage($data['to'], $data['message'], $waitForSession)) {\r
-            if ($waitForSession) {\r
-                $this->enqueue_waiting_message($data['to'], $data['message']);\r
-            } else {\r
-                return false;\r
-            }\r
-        }\r
-\r
-        // Sending a command updates the time till next ping\r
-        $this->lastPing = time();\r
-        $this->pingInterval = 50;\r
-        return true;\r
-    }\r
-}\r
index d73da51d588848696ea4b7032ab8fe3c73e6b0de..c97acfe7b28c54c548452367838e3463ef518d91 100644 (file)
@@ -75,27 +75,6 @@ class NoticeTitlePlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'Notice_title':
-            include_once $dir . '/'.$cls.'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Provide plugin version information.
      *
diff --git a/plugins/NoticeTitle/Notice_title.php b/plugins/NoticeTitle/Notice_title.php
deleted file mode 100644 (file)
index 610e32f..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-/**
- * Data class for notice titles
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for notice titles
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Notice_title extends Managed_DataObject
-{
-    const MAXCHARS = 255;
-
-    public $__table = 'notice_title'; // table name
-    public $notice_id;                         // int(11)  primary_key not_null
-    public $title;                             // varchar(255)
-    public $created;                           // datetime()   not_null
-    public $modified;                          // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice id this title relates to'),
-                'title' => array('type' => 'varchar', 'length' => Notice_title::MAXCHARS, 'description' => 'title to notice'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('notice_id'),
-            'foreign keys' => array(
-                'notice_title_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
-            ),
-        );
-    }
-
-    /**
-     * Get a notice title based on the notice
-     *
-     * @param Notice $notice Notice to fetch a title for
-     *
-     * @return string title of the notice, or null if none
-     */
-    static function fromNotice($notice)
-    {
-        $nt = Notice_title::getKV('notice_id', $notice->id);
-        if (empty($nt)) {
-            return null;
-        } else {
-            return $nt->title;
-        }
-    }
-}
diff --git a/plugins/NoticeTitle/classes/Notice_title.php b/plugins/NoticeTitle/classes/Notice_title.php
new file mode 100644 (file)
index 0000000..610e32f
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/**
+ * Data class for notice titles
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for notice titles
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Notice_title extends Managed_DataObject
+{
+    const MAXCHARS = 255;
+
+    public $__table = 'notice_title'; // table name
+    public $notice_id;                         // int(11)  primary_key not_null
+    public $title;                             // varchar(255)
+    public $created;                           // datetime()   not_null
+    public $modified;                          // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice id this title relates to'),
+                'title' => array('type' => 'varchar', 'length' => Notice_title::MAXCHARS, 'description' => 'title to notice'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('notice_id'),
+            'foreign keys' => array(
+                'notice_title_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
+            ),
+        );
+    }
+
+    /**
+     * Get a notice title based on the notice
+     *
+     * @param Notice $notice Notice to fetch a title for
+     *
+     * @return string title of the notice, or null if none
+     */
+    static function fromNotice($notice)
+    {
+        $nt = Notice_title::getKV('notice_id', $notice->id);
+        if (empty($nt)) {
+            return null;
+        } else {
+            return $nt->title;
+        }
+    }
+}
index fe4510f4a702f99ebfdf1c5f102edc5eb66b5faa..e2552e8e0881be6b189de1d3b1d34402b25abe69 100644 (file)
@@ -54,40 +54,6 @@ if (!defined('STATUSNET')) {
  */
 class OMBPlugin extends Plugin
 {
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'Requesttokenaction':
-        case 'Accesstokenaction':
-        case 'Userauthorizationaction':
-        case 'Postnoticeaction':
-        case 'Updateprofileaction':
-        case 'Finishremotesubscribeaction':
-        case 'Remotesubscribeaction':
-        case 'XrdsAction':
-            include_once $dir . '/action/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-            break;
-        case 'OmbQueueHandler':
-        case 'ProfileQueueHandler':
-            include_once $dir . '/lib/' . strtolower($cls) . '.php';
-            return false;
-        case 'OMBOAuthDataStore':
-            include_once $dir . '/lib/omboauthstore.php';
-        default:
-            return true;
-        }
-    }
 
     /**
      * Map URLs to actions
index a2c3d7c45ed0a790b5fe06c6b42e7c5f0fc3359d..4731440a311ee6a51a2697752d75565e26aa02ff 100644 (file)
@@ -21,7 +21,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
 $dir = dirname(__FILE__);
 
-require_once $dir . '/omboauthstore.php';
 require_once $dir . '/../extlib/libomb/constants.php';
 require_once $dir . '/../extlib/libomb/service_consumer.php';
 require_once $dir . '/../extlib/libomb/notice.php';
diff --git a/plugins/OMB/lib/omboauthdatastore.php b/plugins/OMB/lib/omboauthdatastore.php
new file mode 100644 (file)
index 0000000..857748b
--- /dev/null
@@ -0,0 +1,509 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008-2011 StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+require_once dirname(__FILE__) . '/../extlib/libomb/datastore.php';
+
+// @todo FIXME: Class documentation missing.
+class OMBOAuthDataStore extends OAuthDataStore
+{
+    // We keep a record of who's contacted us
+    function lookup_consumer($consumer_key)
+    {
+        $con = Consumer::getKV('consumer_key', $consumer_key);
+        if (!$con) {
+            $con = new Consumer();
+            $con->consumer_key = $consumer_key;
+            $con->seed = common_good_rand(16);
+            $con->created = DB_DataObject_Cast::dateTime();
+            if (!$con->insert()) {
+                return null;
+            }
+        }
+        return new OAuthConsumer($con->consumer_key, '');
+    }
+
+    function lookup_token($consumer, $token_type, $token_key)
+    {
+        $t = new Token();
+        if (!is_null($consumer)) {
+            $t->consumer_key = $consumer->key;
+        }
+        $t->tok = $token_key;
+        $t->type = ($token_type == 'access') ? 1 : 0;
+        if ($t->find(true)) {
+            return new OAuthToken($t->tok, $t->secret);
+        } else {
+            return null;
+        }
+    }
+
+    // http://oauth.net/core/1.0/#nonce
+    // "The Consumer SHALL then generate a Nonce value that is unique for
+    // all requests with that timestamp."
+    // XXX: It's not clear why the token is here
+    function lookup_nonce($consumer, $token, $nonce, $timestamp)
+    {
+        $n = new Nonce();
+        $n->consumer_key = $consumer->key;
+        $n->ts = common_sql_date($timestamp);
+        $n->nonce = $nonce;
+        if ($n->find(true)) {
+            return true;
+        } else {
+            $n->created = DB_DataObject_Cast::dateTime();
+            $n->insert();
+            return false;
+        }
+    }
+
+    function new_request_token($consumer)
+    {
+        $t = new Token();
+        $t->consumer_key = $consumer->key;
+        $t->tok = common_good_rand(16);
+        $t->secret = common_good_rand(16);
+        $t->type = 0; // request
+        $t->state = 0; // unauthorized
+        $t->created = DB_DataObject_Cast::dateTime();
+        if (!$t->insert()) {
+            return null;
+        } else {
+            return new OAuthToken($t->tok, $t->secret);
+        }
+    }
+
+    // defined in OAuthDataStore, but not implemented anywhere
+    function fetch_request_token($consumer)
+    {
+        return $this->new_request_token($consumer);
+    }
+
+    function new_access_token($token, $consumer)
+    {
+        common_debug('new_access_token("'.$token->key.'","'.$consumer->key.'")', __FILE__);
+        $rt = new Token();
+        $rt->consumer_key = $consumer->key;
+        $rt->tok = $token->key;
+        $rt->type = 0; // request
+        if ($rt->find(true) && $rt->state == 1) { // authorized
+            common_debug('request token found.', __FILE__);
+            $at = new Token();
+            $at->consumer_key = $consumer->key;
+            $at->tok = common_good_rand(16);
+            $at->secret = common_good_rand(16);
+            $at->type = 1; // access
+            $at->created = DB_DataObject_Cast::dateTime();
+            if (!$at->insert()) {
+                $e = $at->_lastError;
+                common_debug('access token "'.$at->tok.'" not inserted: "'.$e->message.'"', __FILE__);
+                return null;
+            } else {
+                common_debug('access token "'.$at->tok.'" inserted', __FILE__);
+                // burn the old one
+                $orig_rt = clone($rt);
+                $rt->state = 2; // used
+                if (!$rt->update($orig_rt)) {
+                    return null;
+                }
+                common_debug('request token "'.$rt->tok.'" updated', __FILE__);
+                // Update subscription
+                // XXX: mixing levels here
+                $sub = Subscription::getKV('token', $rt->tok);
+                if (!$sub) {
+                    return null;
+                }
+                common_debug('subscription for request token found', __FILE__);
+                $orig_sub = clone($sub);
+                $sub->token = $at->tok;
+                $sub->secret = $at->secret;
+                if (!$sub->update($orig_sub)) {
+                    return null;
+                } else {
+                    common_debug('subscription updated to use access token', __FILE__);
+                    return new OAuthToken($at->tok, $at->secret);
+                }
+            }
+        } else {
+            return null;
+        }
+    }
+
+    // defined in OAuthDataStore, but not implemented anywhere
+    function fetch_access_token($consumer)
+    {
+        return $this->new_access_token($consumer);
+    }
+
+    /**
+     * Revoke specified OAuth token
+     *
+     * Revokes the authorization token specified by $token_key.
+     * Throws exceptions in case of error.
+     *
+     * @param string $token_key The token to be revoked
+     *
+     * @access public
+     **/
+    public function revoke_token($token_key) {
+        $rt = new Token();
+        $rt->tok = $token_key;
+        $rt->type = 0;
+        $rt->state = 0;
+        if (!$rt->find(true)) {
+            throw new Exception('Tried to revoke unknown token');
+        }
+        if (!$rt->delete()) {
+            throw new Exception('Failed to delete revoked token');
+        }
+    }
+
+    /**
+     * Authorize specified OAuth token
+     *
+     * Authorizes the authorization token specified by $token_key.
+     * Throws exceptions in case of error.
+     *
+     * @param string $token_key The token to be authorized
+     *
+     * @access public
+     **/
+    public function authorize_token($token_key) {
+        $rt = new Token();
+        $rt->tok = $token_key;
+        $rt->type = 0;
+        $rt->state = 0;
+        if (!$rt->find(true)) {
+            throw new Exception('Tried to authorize unknown token');
+        }
+        $orig_rt = clone($rt);
+        $rt->state = 1; # Authorized but not used
+        if (!$rt->update($orig_rt)) {
+            throw new Exception('Failed to authorize token');
+        }
+    }
+
+    /**
+     * Get profile by identifying URI
+     *
+     * Returns an OMB_Profile object representing the OMB profile identified by
+     * $identifier_uri.
+     * Returns null if there is no such OMB profile.
+     * Throws exceptions in case of other error.
+     *
+     * @param string $identifier_uri The OMB identifier URI specifying the
+     *                               requested profile
+     *
+     * @access public
+     *
+     * @return OMB_Profile The corresponding profile
+     **/
+    public function getProfile($identifier_uri) {
+        /* getProfile is only used for remote profiles by libomb.
+           @TODO: Make it work with local ones anyway. */
+        $remote = Remote_profile::getKV('uri', $identifier_uri);
+        if (!$remote) throw new Exception('No such remote profile');
+        $profile = Profile::getKV('id', $remote->id);
+        if (!$profile) throw new Exception('No profile for remote user');
+
+        require_once dirname(__FILE__) . '/omb.php';
+        return profile_to_omb_profile($identifier_uri, $profile);
+    }
+
+    /**
+     * Save passed profile
+     *
+     * Stores the OMB profile $profile. Overwrites an existing entry.
+     * Throws exceptions in case of error.
+     *
+     * @param OMB_Profile $profile   The OMB profile which should be saved
+     *
+     * @access public
+     **/
+    public function saveProfile($omb_profile) {
+        if (common_profile_url($omb_profile->getNickname()) ==
+                                                $omb_profile->getProfileURL()) {
+            throw new Exception('Not implemented');
+        } else {
+            $remote = Remote_profile::getKV('uri', $omb_profile->getIdentifierURI());
+
+            if ($remote) {
+                $exists = true;
+                $profile = Profile::getKV($remote->id);
+                $orig_remote = clone($remote);
+                $orig_profile = clone($profile);
+                // XXX: compare current postNotice and updateProfile URLs to the ones
+                // stored in the DB to avoid (possibly...) above attack
+            } else {
+                $exists = false;
+                $remote = new Remote_profile();
+                $remote->uri = $omb_profile->getIdentifierURI();
+                $profile = new Profile();
+            }
+
+            $profile->nickname = $omb_profile->getNickname();
+            $profile->profileurl = $omb_profile->getProfileURL();
+
+            $fullname = $omb_profile->getFullname();
+            $profile->fullname = is_null($fullname) ? '' : $fullname;
+            $homepage = $omb_profile->getHomepage();
+            $profile->homepage = is_null($homepage) ? '' : $homepage;
+            $bio = $omb_profile->getBio();
+            $profile->bio = is_null($bio) ? '' : $bio;
+            $location = $omb_profile->getLocation();
+            $profile->location = is_null($location) ? '' : $location;
+
+            if ($exists) {
+                $profile->update($orig_profile);
+            } else {
+                $profile->created = DB_DataObject_Cast::dateTime(); # current time
+                $id = $profile->insert();
+                if (!$id) {
+                    // TRANS: Exception thrown when creating a new profile fails in OAuth store.
+                    throw new Exception(_('Error inserting new profile.'));
+                }
+                $remote->id = $id;
+            }
+
+            $avatar_url = $omb_profile->getAvatarURL();
+            if ($avatar_url) {
+                if (!$this->add_avatar($profile, $avatar_url)) {
+                    // TRANS: Exception thrown when creating a new avatar fails in OAuth store.
+                    throw new Exception(_('Error inserting avatar.'));
+                }
+            } else {
+                $avatar = $profile->getOriginalAvatar();
+                if($avatar) $avatar->delete();
+                $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
+                if($avatar) $avatar->delete();
+                $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
+                if($avatar) $avatar->delete();
+                $avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
+                if($avatar) $avatar->delete();
+            }
+
+            if ($exists) {
+                if (!$remote->update($orig_remote)) {
+                    // TRANS: Exception thrown when updating a remote profile fails in OAuth store.
+                    throw new Exception(_('Error updating remote profile.'));
+                }
+            } else {
+                $remote->created = DB_DataObject_Cast::dateTime(); # current time
+                if (!$remote->insert()) {
+                    // TRANS: Exception thrown when creating a remote profile fails in OAuth store.
+                    throw new Exception(_('Error inserting remote profile.'));
+                }
+            }
+        }
+    }
+
+    function add_avatar($profile, $url)
+    {
+        $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
+        try {
+            copy($url, $temp_filename);
+            $imagefile = new ImageFile($profile->id, $temp_filename);
+            $filename = Avatar::filename($profile->id,
+                                         image_type_to_extension($imagefile->type),
+                                         null,
+                                         common_timestamp());
+            rename($temp_filename, Avatar::path($filename));
+        } catch (Exception $e) {
+            unlink($temp_filename);
+            throw $e;
+        }
+        return $profile->setOriginal($filename);
+    }
+
+    /**
+     * Save passed notice
+     *
+     * Stores the OMB notice $notice. The datastore may change the passed notice.
+     * This might by neccessary for URIs depending on a database key. Note that
+     * it is the user’s duty to present a mechanism for his OMB_Datastore to
+     * appropriately change his OMB_Notice.
+     * Throws exceptions in case of error.
+     *
+     * @param OMB_Notice $notice The OMB notice which should be saved
+     *
+     * @access public
+     **/
+    public function saveNotice(&$omb_notice) {
+        if (Notice::getKV('uri', $omb_notice->getIdentifierURI())) {
+            // TRANS: Exception thrown when a notice is denied because it has been sent before.
+            throw new Exception(_('Duplicate notice.'));
+        }
+        $author_uri = $omb_notice->getAuthor()->getIdentifierURI();
+        common_log(LOG_DEBUG, $author_uri, __FILE__);
+        $author = Remote_profile::getKV('uri', $author_uri);
+        if (!$author) {
+            $author = User::getKV('uri', $author_uri);
+        }
+        if (!$author) {
+            throw new Exception('No such user.');
+        }
+
+        common_log(LOG_DEBUG, print_r($author, true), __FILE__);
+
+        $notice = Notice::saveNew($author->id,
+                                  $omb_notice->getContent(),
+                                  'omb',
+                                  array('is_local' => Notice::REMOTE,
+                                        'uri' => $omb_notice->getIdentifierURI()));
+
+    }
+
+    /**
+     * Get subscriptions of a given profile
+     *
+     * Returns an array containing subscription informations for the specified
+     * profile. Every array entry should in turn be an array with keys
+     *   'uri´: The identifier URI of the subscriber
+     *   'token´: The subscribe token
+     *   'secret´: The secret token
+     * Throws exceptions in case of error.
+     *
+     * @param string $subscribed_user_uri The OMB identifier URI specifying the
+     *                                    subscribed profile
+     *
+     * @access public
+     *
+     * @return mixed An array containing the subscriptions or 0 if no
+     *               subscription has been found.
+     **/
+    public function getSubscriptions($subscribed_user_uri) {
+        $sub = new Subscription();
+
+        $user = $this->_getAnyProfile($subscribed_user_uri);
+
+        $sub->subscribed = $user->id;
+
+        if (!$sub->find(true)) {
+            return array();
+        }
+
+        /* Since we do not use OMB_Service_Provider’s action methods, there
+           is no need to actually return the subscriptions. */
+        return 1;
+    }
+
+    private function _getAnyProfile($uri)
+    {
+        $user = Remote_profile::getKV('uri', $uri);
+        if (!$user) {
+            $user = User::getKV('uri', $uri);
+        }
+        if (!$user) {
+            throw new Exception('No such user.');
+        }
+        return $user;
+    }
+
+    /**
+     * Delete a subscription
+     *
+     * Deletes the subscription from $subscriber_uri to $subscribed_user_uri.
+     * Throws exceptions in case of error.
+     *
+     * @param string $subscriber_uri      The OMB identifier URI specifying the
+     *                                    subscribing profile
+     *
+     * @param string $subscribed_user_uri The OMB identifier URI specifying the
+     *                                    subscribed profile
+     *
+     * @access public
+     **/
+    public function deleteSubscription($subscriber_uri, $subscribed_user_uri)
+    {
+        $sub = new Subscription();
+
+        $subscribed = $this->_getAnyProfile($subscribed_user_uri);
+        $subscriber = $this->_getAnyProfile($subscriber_uri);
+
+        $sub->subscribed = $subscribed->id;
+        $sub->subscriber = $subscriber->id;
+
+        $sub->delete();
+    }
+
+    /**
+     * Save a subscription
+     *
+     * Saves the subscription from $subscriber_uri to $subscribed_user_uri.
+     * Throws exceptions in case of error.
+     *
+     * @param string     $subscriber_uri      The OMB identifier URI specifying
+     *                                        the subscribing profile
+     *
+     * @param string     $subscribed_user_uri The OMB identifier URI specifying
+     *                                        the subscribed profile
+     * @param OAuthToken $token               The access token
+     *
+     * @access public
+     **/
+    public function saveSubscription($subscriber_uri, $subscribed_user_uri,
+                                                                       $token)
+    {
+        $sub = new Subscription();
+
+        $subscribed = $this->_getAnyProfile($subscribed_user_uri);
+        $subscriber = $this->_getAnyProfile($subscriber_uri);
+
+        if (!$subscriber->hasRight(Right::SUBSCRIBE)) {
+            common_log(LOG_INFO, __METHOD__ . ": remote subscriber banned ($subscriber_uri subbing to $subscribed_user_uri)");
+            // TRANS: Error message displayed to a banned user when they try to subscribe.
+            return _('You have been banned from subscribing.');
+        }
+
+        $sub->subscribed = $subscribed->id;
+        $sub->subscriber = $subscriber->id;
+
+        $sub_exists = $sub->find(true);
+
+        if ($sub_exists) {
+            $orig_sub = clone($sub);
+        } else {
+            $sub->created = DB_DataObject_Cast::dateTime();
+        }
+
+        $sub->token  = $token->key;
+        $sub->secret = $token->secret;
+
+        if ($sub_exists) {
+            $result = $sub->update($orig_sub);
+        } else {
+            $result = $sub->insert();
+        }
+
+        if (!$result) {
+            common_log_db_error($sub, ($sub_exists) ? 'UPDATE' : 'INSERT', __FILE__);
+            // TRANS: Exception thrown when creating a new subscription fails in OAuth store.
+            throw new Exception(_('Could not insert new subscription.'));
+            return;
+        }
+
+        /* Notify user, if necessary. */
+
+        if ($subscribed instanceof User) {
+            mail_subscribe_notify_profile($subscribed,
+                                          Profile::getKV($subscriber->id));
+        }
+    }
+}
diff --git a/plugins/OMB/lib/omboauthstore.php b/plugins/OMB/lib/omboauthstore.php
deleted file mode 100644 (file)
index 857748b..0000000
+++ /dev/null
@@ -1,509 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008-2011 StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-require_once dirname(__FILE__) . '/../extlib/libomb/datastore.php';
-
-// @todo FIXME: Class documentation missing.
-class OMBOAuthDataStore extends OAuthDataStore
-{
-    // We keep a record of who's contacted us
-    function lookup_consumer($consumer_key)
-    {
-        $con = Consumer::getKV('consumer_key', $consumer_key);
-        if (!$con) {
-            $con = new Consumer();
-            $con->consumer_key = $consumer_key;
-            $con->seed = common_good_rand(16);
-            $con->created = DB_DataObject_Cast::dateTime();
-            if (!$con->insert()) {
-                return null;
-            }
-        }
-        return new OAuthConsumer($con->consumer_key, '');
-    }
-
-    function lookup_token($consumer, $token_type, $token_key)
-    {
-        $t = new Token();
-        if (!is_null($consumer)) {
-            $t->consumer_key = $consumer->key;
-        }
-        $t->tok = $token_key;
-        $t->type = ($token_type == 'access') ? 1 : 0;
-        if ($t->find(true)) {
-            return new OAuthToken($t->tok, $t->secret);
-        } else {
-            return null;
-        }
-    }
-
-    // http://oauth.net/core/1.0/#nonce
-    // "The Consumer SHALL then generate a Nonce value that is unique for
-    // all requests with that timestamp."
-    // XXX: It's not clear why the token is here
-    function lookup_nonce($consumer, $token, $nonce, $timestamp)
-    {
-        $n = new Nonce();
-        $n->consumer_key = $consumer->key;
-        $n->ts = common_sql_date($timestamp);
-        $n->nonce = $nonce;
-        if ($n->find(true)) {
-            return true;
-        } else {
-            $n->created = DB_DataObject_Cast::dateTime();
-            $n->insert();
-            return false;
-        }
-    }
-
-    function new_request_token($consumer)
-    {
-        $t = new Token();
-        $t->consumer_key = $consumer->key;
-        $t->tok = common_good_rand(16);
-        $t->secret = common_good_rand(16);
-        $t->type = 0; // request
-        $t->state = 0; // unauthorized
-        $t->created = DB_DataObject_Cast::dateTime();
-        if (!$t->insert()) {
-            return null;
-        } else {
-            return new OAuthToken($t->tok, $t->secret);
-        }
-    }
-
-    // defined in OAuthDataStore, but not implemented anywhere
-    function fetch_request_token($consumer)
-    {
-        return $this->new_request_token($consumer);
-    }
-
-    function new_access_token($token, $consumer)
-    {
-        common_debug('new_access_token("'.$token->key.'","'.$consumer->key.'")', __FILE__);
-        $rt = new Token();
-        $rt->consumer_key = $consumer->key;
-        $rt->tok = $token->key;
-        $rt->type = 0; // request
-        if ($rt->find(true) && $rt->state == 1) { // authorized
-            common_debug('request token found.', __FILE__);
-            $at = new Token();
-            $at->consumer_key = $consumer->key;
-            $at->tok = common_good_rand(16);
-            $at->secret = common_good_rand(16);
-            $at->type = 1; // access
-            $at->created = DB_DataObject_Cast::dateTime();
-            if (!$at->insert()) {
-                $e = $at->_lastError;
-                common_debug('access token "'.$at->tok.'" not inserted: "'.$e->message.'"', __FILE__);
-                return null;
-            } else {
-                common_debug('access token "'.$at->tok.'" inserted', __FILE__);
-                // burn the old one
-                $orig_rt = clone($rt);
-                $rt->state = 2; // used
-                if (!$rt->update($orig_rt)) {
-                    return null;
-                }
-                common_debug('request token "'.$rt->tok.'" updated', __FILE__);
-                // Update subscription
-                // XXX: mixing levels here
-                $sub = Subscription::getKV('token', $rt->tok);
-                if (!$sub) {
-                    return null;
-                }
-                common_debug('subscription for request token found', __FILE__);
-                $orig_sub = clone($sub);
-                $sub->token = $at->tok;
-                $sub->secret = $at->secret;
-                if (!$sub->update($orig_sub)) {
-                    return null;
-                } else {
-                    common_debug('subscription updated to use access token', __FILE__);
-                    return new OAuthToken($at->tok, $at->secret);
-                }
-            }
-        } else {
-            return null;
-        }
-    }
-
-    // defined in OAuthDataStore, but not implemented anywhere
-    function fetch_access_token($consumer)
-    {
-        return $this->new_access_token($consumer);
-    }
-
-    /**
-     * Revoke specified OAuth token
-     *
-     * Revokes the authorization token specified by $token_key.
-     * Throws exceptions in case of error.
-     *
-     * @param string $token_key The token to be revoked
-     *
-     * @access public
-     **/
-    public function revoke_token($token_key) {
-        $rt = new Token();
-        $rt->tok = $token_key;
-        $rt->type = 0;
-        $rt->state = 0;
-        if (!$rt->find(true)) {
-            throw new Exception('Tried to revoke unknown token');
-        }
-        if (!$rt->delete()) {
-            throw new Exception('Failed to delete revoked token');
-        }
-    }
-
-    /**
-     * Authorize specified OAuth token
-     *
-     * Authorizes the authorization token specified by $token_key.
-     * Throws exceptions in case of error.
-     *
-     * @param string $token_key The token to be authorized
-     *
-     * @access public
-     **/
-    public function authorize_token($token_key) {
-        $rt = new Token();
-        $rt->tok = $token_key;
-        $rt->type = 0;
-        $rt->state = 0;
-        if (!$rt->find(true)) {
-            throw new Exception('Tried to authorize unknown token');
-        }
-        $orig_rt = clone($rt);
-        $rt->state = 1; # Authorized but not used
-        if (!$rt->update($orig_rt)) {
-            throw new Exception('Failed to authorize token');
-        }
-    }
-
-    /**
-     * Get profile by identifying URI
-     *
-     * Returns an OMB_Profile object representing the OMB profile identified by
-     * $identifier_uri.
-     * Returns null if there is no such OMB profile.
-     * Throws exceptions in case of other error.
-     *
-     * @param string $identifier_uri The OMB identifier URI specifying the
-     *                               requested profile
-     *
-     * @access public
-     *
-     * @return OMB_Profile The corresponding profile
-     **/
-    public function getProfile($identifier_uri) {
-        /* getProfile is only used for remote profiles by libomb.
-           @TODO: Make it work with local ones anyway. */
-        $remote = Remote_profile::getKV('uri', $identifier_uri);
-        if (!$remote) throw new Exception('No such remote profile');
-        $profile = Profile::getKV('id', $remote->id);
-        if (!$profile) throw new Exception('No profile for remote user');
-
-        require_once dirname(__FILE__) . '/omb.php';
-        return profile_to_omb_profile($identifier_uri, $profile);
-    }
-
-    /**
-     * Save passed profile
-     *
-     * Stores the OMB profile $profile. Overwrites an existing entry.
-     * Throws exceptions in case of error.
-     *
-     * @param OMB_Profile $profile   The OMB profile which should be saved
-     *
-     * @access public
-     **/
-    public function saveProfile($omb_profile) {
-        if (common_profile_url($omb_profile->getNickname()) ==
-                                                $omb_profile->getProfileURL()) {
-            throw new Exception('Not implemented');
-        } else {
-            $remote = Remote_profile::getKV('uri', $omb_profile->getIdentifierURI());
-
-            if ($remote) {
-                $exists = true;
-                $profile = Profile::getKV($remote->id);
-                $orig_remote = clone($remote);
-                $orig_profile = clone($profile);
-                // XXX: compare current postNotice and updateProfile URLs to the ones
-                // stored in the DB to avoid (possibly...) above attack
-            } else {
-                $exists = false;
-                $remote = new Remote_profile();
-                $remote->uri = $omb_profile->getIdentifierURI();
-                $profile = new Profile();
-            }
-
-            $profile->nickname = $omb_profile->getNickname();
-            $profile->profileurl = $omb_profile->getProfileURL();
-
-            $fullname = $omb_profile->getFullname();
-            $profile->fullname = is_null($fullname) ? '' : $fullname;
-            $homepage = $omb_profile->getHomepage();
-            $profile->homepage = is_null($homepage) ? '' : $homepage;
-            $bio = $omb_profile->getBio();
-            $profile->bio = is_null($bio) ? '' : $bio;
-            $location = $omb_profile->getLocation();
-            $profile->location = is_null($location) ? '' : $location;
-
-            if ($exists) {
-                $profile->update($orig_profile);
-            } else {
-                $profile->created = DB_DataObject_Cast::dateTime(); # current time
-                $id = $profile->insert();
-                if (!$id) {
-                    // TRANS: Exception thrown when creating a new profile fails in OAuth store.
-                    throw new Exception(_('Error inserting new profile.'));
-                }
-                $remote->id = $id;
-            }
-
-            $avatar_url = $omb_profile->getAvatarURL();
-            if ($avatar_url) {
-                if (!$this->add_avatar($profile, $avatar_url)) {
-                    // TRANS: Exception thrown when creating a new avatar fails in OAuth store.
-                    throw new Exception(_('Error inserting avatar.'));
-                }
-            } else {
-                $avatar = $profile->getOriginalAvatar();
-                if($avatar) $avatar->delete();
-                $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
-                if($avatar) $avatar->delete();
-                $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
-                if($avatar) $avatar->delete();
-                $avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
-                if($avatar) $avatar->delete();
-            }
-
-            if ($exists) {
-                if (!$remote->update($orig_remote)) {
-                    // TRANS: Exception thrown when updating a remote profile fails in OAuth store.
-                    throw new Exception(_('Error updating remote profile.'));
-                }
-            } else {
-                $remote->created = DB_DataObject_Cast::dateTime(); # current time
-                if (!$remote->insert()) {
-                    // TRANS: Exception thrown when creating a remote profile fails in OAuth store.
-                    throw new Exception(_('Error inserting remote profile.'));
-                }
-            }
-        }
-    }
-
-    function add_avatar($profile, $url)
-    {
-        $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
-        try {
-            copy($url, $temp_filename);
-            $imagefile = new ImageFile($profile->id, $temp_filename);
-            $filename = Avatar::filename($profile->id,
-                                         image_type_to_extension($imagefile->type),
-                                         null,
-                                         common_timestamp());
-            rename($temp_filename, Avatar::path($filename));
-        } catch (Exception $e) {
-            unlink($temp_filename);
-            throw $e;
-        }
-        return $profile->setOriginal($filename);
-    }
-
-    /**
-     * Save passed notice
-     *
-     * Stores the OMB notice $notice. The datastore may change the passed notice.
-     * This might by neccessary for URIs depending on a database key. Note that
-     * it is the user’s duty to present a mechanism for his OMB_Datastore to
-     * appropriately change his OMB_Notice.
-     * Throws exceptions in case of error.
-     *
-     * @param OMB_Notice $notice The OMB notice which should be saved
-     *
-     * @access public
-     **/
-    public function saveNotice(&$omb_notice) {
-        if (Notice::getKV('uri', $omb_notice->getIdentifierURI())) {
-            // TRANS: Exception thrown when a notice is denied because it has been sent before.
-            throw new Exception(_('Duplicate notice.'));
-        }
-        $author_uri = $omb_notice->getAuthor()->getIdentifierURI();
-        common_log(LOG_DEBUG, $author_uri, __FILE__);
-        $author = Remote_profile::getKV('uri', $author_uri);
-        if (!$author) {
-            $author = User::getKV('uri', $author_uri);
-        }
-        if (!$author) {
-            throw new Exception('No such user.');
-        }
-
-        common_log(LOG_DEBUG, print_r($author, true), __FILE__);
-
-        $notice = Notice::saveNew($author->id,
-                                  $omb_notice->getContent(),
-                                  'omb',
-                                  array('is_local' => Notice::REMOTE,
-                                        'uri' => $omb_notice->getIdentifierURI()));
-
-    }
-
-    /**
-     * Get subscriptions of a given profile
-     *
-     * Returns an array containing subscription informations for the specified
-     * profile. Every array entry should in turn be an array with keys
-     *   'uri´: The identifier URI of the subscriber
-     *   'token´: The subscribe token
-     *   'secret´: The secret token
-     * Throws exceptions in case of error.
-     *
-     * @param string $subscribed_user_uri The OMB identifier URI specifying the
-     *                                    subscribed profile
-     *
-     * @access public
-     *
-     * @return mixed An array containing the subscriptions or 0 if no
-     *               subscription has been found.
-     **/
-    public function getSubscriptions($subscribed_user_uri) {
-        $sub = new Subscription();
-
-        $user = $this->_getAnyProfile($subscribed_user_uri);
-
-        $sub->subscribed = $user->id;
-
-        if (!$sub->find(true)) {
-            return array();
-        }
-
-        /* Since we do not use OMB_Service_Provider’s action methods, there
-           is no need to actually return the subscriptions. */
-        return 1;
-    }
-
-    private function _getAnyProfile($uri)
-    {
-        $user = Remote_profile::getKV('uri', $uri);
-        if (!$user) {
-            $user = User::getKV('uri', $uri);
-        }
-        if (!$user) {
-            throw new Exception('No such user.');
-        }
-        return $user;
-    }
-
-    /**
-     * Delete a subscription
-     *
-     * Deletes the subscription from $subscriber_uri to $subscribed_user_uri.
-     * Throws exceptions in case of error.
-     *
-     * @param string $subscriber_uri      The OMB identifier URI specifying the
-     *                                    subscribing profile
-     *
-     * @param string $subscribed_user_uri The OMB identifier URI specifying the
-     *                                    subscribed profile
-     *
-     * @access public
-     **/
-    public function deleteSubscription($subscriber_uri, $subscribed_user_uri)
-    {
-        $sub = new Subscription();
-
-        $subscribed = $this->_getAnyProfile($subscribed_user_uri);
-        $subscriber = $this->_getAnyProfile($subscriber_uri);
-
-        $sub->subscribed = $subscribed->id;
-        $sub->subscriber = $subscriber->id;
-
-        $sub->delete();
-    }
-
-    /**
-     * Save a subscription
-     *
-     * Saves the subscription from $subscriber_uri to $subscribed_user_uri.
-     * Throws exceptions in case of error.
-     *
-     * @param string     $subscriber_uri      The OMB identifier URI specifying
-     *                                        the subscribing profile
-     *
-     * @param string     $subscribed_user_uri The OMB identifier URI specifying
-     *                                        the subscribed profile
-     * @param OAuthToken $token               The access token
-     *
-     * @access public
-     **/
-    public function saveSubscription($subscriber_uri, $subscribed_user_uri,
-                                                                       $token)
-    {
-        $sub = new Subscription();
-
-        $subscribed = $this->_getAnyProfile($subscribed_user_uri);
-        $subscriber = $this->_getAnyProfile($subscriber_uri);
-
-        if (!$subscriber->hasRight(Right::SUBSCRIBE)) {
-            common_log(LOG_INFO, __METHOD__ . ": remote subscriber banned ($subscriber_uri subbing to $subscribed_user_uri)");
-            // TRANS: Error message displayed to a banned user when they try to subscribe.
-            return _('You have been banned from subscribing.');
-        }
-
-        $sub->subscribed = $subscribed->id;
-        $sub->subscriber = $subscriber->id;
-
-        $sub_exists = $sub->find(true);
-
-        if ($sub_exists) {
-            $orig_sub = clone($sub);
-        } else {
-            $sub->created = DB_DataObject_Cast::dateTime();
-        }
-
-        $sub->token  = $token->key;
-        $sub->secret = $token->secret;
-
-        if ($sub_exists) {
-            $result = $sub->update($orig_sub);
-        } else {
-            $result = $sub->insert();
-        }
-
-        if (!$result) {
-            common_log_db_error($sub, ($sub_exists) ? 'UPDATE' : 'INSERT', __FILE__);
-            // TRANS: Exception thrown when creating a new subscription fails in OAuth store.
-            throw new Exception(_('Could not insert new subscription.'));
-            return;
-        }
-
-        /* Notify user, if necessary. */
-
-        if ($subscribed instanceof User) {
-            mail_subscribe_notify_profile($subscribed,
-                                          Profile::getKV($subscriber->id));
-        }
-    }
-}
index 567989c11e85bc8cdeacc9aa78f898abdfc03892..bcb8a3098c0041a5adfb5e8387ed00419436cc06 100644 (file)
@@ -200,38 +200,6 @@ class OStatusPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Automatically load the actions and libraries used by the plugin
-     *
-     * @param Class $cls the class
-     *
-     * @return boolean hook return
-     *
-     */
-    function onAutoload($cls)
-    {
-        $base = dirname(__FILE__);
-        $lower = strtolower($cls);
-        $map = array('activityverb' => 'activity',
-                     'activityobject' => 'activity',
-                     'activityutils' => 'activity');
-        if (isset($map[$lower])) {
-            $lower = $map[$lower];
-        }
-        $files = array("$base/classes/$cls.php",
-                       "$base/lib/$lower.php");
-        if (substr($lower, -6) == 'action') {
-            $files[] = "$base/actions/" . substr($lower, 0, -6) . ".php";
-        }
-        foreach ($files as $file) {
-            if (file_exists($file)) {
-                include_once $file;
-                return false;
-            }
-        }
-        return true;
-    }
-
     /**
      * Add in an OStatus subscribe button
      */
diff --git a/plugins/OStatus/actions/salmon.php b/plugins/OStatus/actions/salmon.php
new file mode 100644 (file)
index 0000000..7cb4ac2
--- /dev/null
@@ -0,0 +1,229 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @package OStatusPlugin
+ * @author James Walker <james@status.net>
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+class SalmonAction extends Action
+{
+    var $xml      = null;
+    var $activity = null;
+    var $target   = null;
+
+    function prepare($args)
+    {
+        StatusNet::setApi(true); // Send smaller error pages
+
+        parent::prepare($args);
+
+        if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+            // TRANS: Client error. POST is a HTTP command. It should not be translated.
+            $this->clientError(_m('This method requires a POST.'));
+        }
+
+        if (empty($_SERVER['CONTENT_TYPE']) || $_SERVER['CONTENT_TYPE'] != 'application/magic-envelope+xml') {
+            // TRANS: Client error. Do not translate "application/magic-envelope+xml".
+            $this->clientError(_m('Salmon requires "application/magic-envelope+xml".'));
+        }
+
+        $xml = file_get_contents('php://input');
+
+        // Check the signature
+        $salmon = new Salmon;
+        if (!$salmon->verifyMagicEnv($xml)) {
+            common_log(LOG_DEBUG, "Salmon signature verification failed.");
+            // TRANS: Client error.
+            $this->clientError(_m('Salmon signature verification failed.'));
+        } else {
+            $magic_env = new MagicEnvelope();
+            $env = $magic_env->parse($xml);
+            $xml = $magic_env->unfold($env);
+        }
+
+        $dom = DOMDocument::loadXML($xml);
+        if ($dom->documentElement->namespaceURI != Activity::ATOM ||
+            $dom->documentElement->localName != 'entry') {
+            common_log(LOG_DEBUG, "Got invalid Salmon post: $xml");
+            // TRANS: Client error.
+            $this->clientError(_m('Salmon post must be an Atom entry.'));
+        }
+
+        $this->activity = new Activity($dom->documentElement);
+        return true;
+    }
+
+    /**
+     * Check the posted activity type and break out to appropriate processing.
+     */
+
+    function handle($args)
+    {
+        StatusNet::setApi(true); // Send smaller error pages
+
+        common_log(LOG_DEBUG, "Got a " . $this->activity->verb);
+        if (Event::handle('StartHandleSalmonTarget', array($this->activity, $this->target)) &&
+            Event::handle('StartHandleSalmon', array($this->activity))) {
+            switch ($this->activity->verb)
+            {
+            case ActivityVerb::POST:
+                $this->handlePost();
+                break;
+            case ActivityVerb::SHARE:
+                $this->handleShare();
+                break;
+            case ActivityVerb::FAVORITE:
+                $this->handleFavorite();
+                break;
+            case ActivityVerb::UNFAVORITE:
+                $this->handleUnfavorite();
+                break;
+            case ActivityVerb::FOLLOW:
+            case ActivityVerb::FRIEND:
+                $this->handleFollow();
+                break;
+            case ActivityVerb::UNFOLLOW:
+                $this->handleUnfollow();
+                break;
+            case ActivityVerb::JOIN:
+                $this->handleJoin();
+                break;
+            case ActivityVerb::LEAVE:
+                $this->handleLeave();
+                break;
+            case ActivityVerb::TAG:
+                $this->handleTag();
+                break;
+            case ActivityVerb::UNTAG:
+                $this->handleUntag();
+                break;
+            case ActivityVerb::UPDATE_PROFILE:
+                $this->handleUpdateProfile();
+                break;
+            default:
+                // TRANS: Client exception.
+                throw new ClientException(_m('Unrecognized activity type.'));
+            }
+            Event::handle('EndHandleSalmon', array($this->activity));
+            Event::handle('EndHandleSalmonTarget', array($this->activity, $this->target));
+        }
+    }
+
+    function handlePost()
+    {
+        // TRANS: Client exception.
+        throw new ClientException(_m('This target does not understand posts.'));
+    }
+
+    function handleFollow()
+    {
+        // TRANS: Client exception.
+        throw new ClientException(_m('This target does not understand follows.'));
+    }
+
+    function handleUnfollow()
+    {
+        // TRANS: Client exception.
+        throw new ClientException(_m('This target does not understand unfollows.'));
+    }
+
+    function handleFavorite()
+    {
+        // TRANS: Client exception.
+        throw new ClientException(_m('This target does not understand favorites.'));
+    }
+
+    function handleUnfavorite()
+    {
+        // TRANS: Client exception.
+        throw new ClientException(_m('This target does not understand unfavorites.'));
+    }
+
+    function handleShare()
+    {
+        // TRANS: Client exception.
+        throw new ClientException(_m('This target does not understand share events.'));
+    }
+
+    function handleJoin()
+    {
+        // TRANS: Client exception.
+        throw new ClientException(_m('This target does not understand joins.'));
+    }
+
+    function handleLeave()
+    {
+        // TRANS: Client exception.
+        throw new ClientException(_m('This target does not understand leave events.'));
+    }
+
+    function handleTag()
+    {
+        // TRANS: Client exception.
+        throw new ClientException(_m('This target does not understand list events.'));
+    }
+
+    function handleUntag()
+    {
+        // TRANS: Client exception.
+        throw new ClientException(_m('This target does not understand unlist events.'));
+    }
+
+    /**
+     * Remote user sent us an update to their profile.
+     * If we already know them, accept the updates.
+     */
+    function handleUpdateProfile()
+    {
+        $oprofile = Ostatus_profile::getActorProfile($this->activity);
+        if ($oprofile) {
+            common_log(LOG_INFO, "Got a profile-update ping from $oprofile->uri");
+            $oprofile->updateFromActivityObject($this->activity->actor);
+        } else {
+            common_log(LOG_INFO, "Ignoring profile-update ping from unknown " . $this->activity->actor->id);
+        }
+    }
+
+    /**
+     * @return Ostatus_profile
+     */
+    function ensureProfile()
+    {
+        $actor = $this->activity->actor;
+        if (empty($actor->id)) {
+            common_log(LOG_ERR, "broken actor: " . var_export($actor, true));
+            common_log(LOG_ERR, "activity with no actor: " . var_export($this->activity, true));
+            // TRANS: Exception.
+            throw new Exception(_m('Received a salmon slap from unidentified actor.'));
+        }
+
+        return Ostatus_profile::ensureActivityObjectProfile($actor);
+    }
+
+    function saveNotice()
+    {
+        $oprofile = $this->ensureProfile();
+        return $oprofile->processPost($this->activity, 'salmon');
+    }
+}
diff --git a/plugins/OStatus/actions/xrd.php b/plugins/OStatus/actions/xrd.php
new file mode 100644 (file)
index 0000000..779ce4d
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @package OStatusPlugin
+ * @maintainer James Walker <james@status.net>
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+class XrdAction extends Action
+{
+    public $uri;
+
+    public $user;
+
+    public $xrd;
+
+    function handle()
+    {
+        $nick    = $this->user->nickname;
+        $profile = $this->user->getProfile();
+
+        if (empty($this->xrd)) {
+            $xrd = new XRD();
+        } else {
+            $xrd = $this->xrd;
+        }
+
+        if (empty($xrd->subject)) {
+            $xrd->subject = Discovery::normalize($this->uri);
+        }
+
+        // Possible aliases for the user
+
+        $uris = array($this->user->uri, $profile->profileurl);
+
+        // FIXME: Webfinger generation code should live somewhere on its own
+
+        $path = common_config('site', 'path');
+
+        if (empty($path)) {
+            $uris[] = sprintf('acct:%s@%s', $nick, common_config('site', 'server'));
+        }
+
+        foreach ($uris as $uri) {
+            if ($uri != $xrd->subject) {
+                $xrd->alias[] = $uri;
+            }
+        }
+
+        $xrd->links[] = array('rel' => Discovery::PROFILEPAGE,
+                              'type' => 'text/html',
+                              'href' => $profile->profileurl);
+
+        $xrd->links[] = array('rel' => Discovery::UPDATESFROM,
+                              'href' => common_local_url('ApiTimelineUser',
+                                                         array('id' => $this->user->id,
+                                                               'format' => 'atom')),
+                              'type' => 'application/atom+xml');
+
+        // XFN
+        $xrd->links[] = array('rel' => 'http://gmpg.org/xfn/11',
+                              'type' => 'text/html',
+                              'href' => $profile->profileurl);
+        // FOAF
+        $xrd->links[] = array('rel' => 'describedby',
+                              'type' => 'application/rdf+xml',
+                              'href' => common_local_url('foaf',
+                                                         array('nickname' => $nick)));
+
+        // Salmon
+        $salmon_url = common_local_url('usersalmon',
+                                       array('id' => $this->user->id));
+
+        $xrd->links[] = array('rel' => Salmon::REL_SALMON,
+                              'href' => $salmon_url);
+        // XXX : Deprecated - to be removed.
+        $xrd->links[] = array('rel' => Salmon::NS_REPLIES,
+                              'href' => $salmon_url);
+
+        $xrd->links[] = array('rel' => Salmon::NS_MENTIONS,
+                              'href' => $salmon_url);
+
+        // Get this user's keypair
+        $magickey = Magicsig::getKV('user_id', $this->user->id);
+        if (!$magickey) {
+            // No keypair yet, let's generate one.
+            $magickey = new Magicsig();
+            $magickey->generate($this->user->id);
+        }
+
+        $xrd->links[] = array('rel' => Magicsig::PUBLICKEYREL,
+                              'href' => 'data:application/magic-public-key,'. $magickey->toString(false));
+
+        // TODO - finalize where the redirect should go on the publisher
+        $url = common_local_url('ostatussub') . '?profile={uri}';
+        $xrd->links[] = array('rel' => 'http://ostatus.org/schema/1.0/subscribe',
+                              'template' => $url );
+
+        $url = common_local_url('tagprofile') . '?uri={uri}';
+        $xrd->links[] = array('rel' => 'http://ostatus.org/schema/1.0/tag',
+                              'template' => $url );
+
+        header('Content-type: application/xrd+xml');
+        print $xrd->toXML();
+    }
+}
diff --git a/plugins/OStatus/lib/salmonaction.php b/plugins/OStatus/lib/salmonaction.php
deleted file mode 100644 (file)
index 7cb4ac2..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * @package OStatusPlugin
- * @author James Walker <james@status.net>
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-class SalmonAction extends Action
-{
-    var $xml      = null;
-    var $activity = null;
-    var $target   = null;
-
-    function prepare($args)
-    {
-        StatusNet::setApi(true); // Send smaller error pages
-
-        parent::prepare($args);
-
-        if ($_SERVER['REQUEST_METHOD'] != 'POST') {
-            // TRANS: Client error. POST is a HTTP command. It should not be translated.
-            $this->clientError(_m('This method requires a POST.'));
-        }
-
-        if (empty($_SERVER['CONTENT_TYPE']) || $_SERVER['CONTENT_TYPE'] != 'application/magic-envelope+xml') {
-            // TRANS: Client error. Do not translate "application/magic-envelope+xml".
-            $this->clientError(_m('Salmon requires "application/magic-envelope+xml".'));
-        }
-
-        $xml = file_get_contents('php://input');
-
-        // Check the signature
-        $salmon = new Salmon;
-        if (!$salmon->verifyMagicEnv($xml)) {
-            common_log(LOG_DEBUG, "Salmon signature verification failed.");
-            // TRANS: Client error.
-            $this->clientError(_m('Salmon signature verification failed.'));
-        } else {
-            $magic_env = new MagicEnvelope();
-            $env = $magic_env->parse($xml);
-            $xml = $magic_env->unfold($env);
-        }
-
-        $dom = DOMDocument::loadXML($xml);
-        if ($dom->documentElement->namespaceURI != Activity::ATOM ||
-            $dom->documentElement->localName != 'entry') {
-            common_log(LOG_DEBUG, "Got invalid Salmon post: $xml");
-            // TRANS: Client error.
-            $this->clientError(_m('Salmon post must be an Atom entry.'));
-        }
-
-        $this->activity = new Activity($dom->documentElement);
-        return true;
-    }
-
-    /**
-     * Check the posted activity type and break out to appropriate processing.
-     */
-
-    function handle($args)
-    {
-        StatusNet::setApi(true); // Send smaller error pages
-
-        common_log(LOG_DEBUG, "Got a " . $this->activity->verb);
-        if (Event::handle('StartHandleSalmonTarget', array($this->activity, $this->target)) &&
-            Event::handle('StartHandleSalmon', array($this->activity))) {
-            switch ($this->activity->verb)
-            {
-            case ActivityVerb::POST:
-                $this->handlePost();
-                break;
-            case ActivityVerb::SHARE:
-                $this->handleShare();
-                break;
-            case ActivityVerb::FAVORITE:
-                $this->handleFavorite();
-                break;
-            case ActivityVerb::UNFAVORITE:
-                $this->handleUnfavorite();
-                break;
-            case ActivityVerb::FOLLOW:
-            case ActivityVerb::FRIEND:
-                $this->handleFollow();
-                break;
-            case ActivityVerb::UNFOLLOW:
-                $this->handleUnfollow();
-                break;
-            case ActivityVerb::JOIN:
-                $this->handleJoin();
-                break;
-            case ActivityVerb::LEAVE:
-                $this->handleLeave();
-                break;
-            case ActivityVerb::TAG:
-                $this->handleTag();
-                break;
-            case ActivityVerb::UNTAG:
-                $this->handleUntag();
-                break;
-            case ActivityVerb::UPDATE_PROFILE:
-                $this->handleUpdateProfile();
-                break;
-            default:
-                // TRANS: Client exception.
-                throw new ClientException(_m('Unrecognized activity type.'));
-            }
-            Event::handle('EndHandleSalmon', array($this->activity));
-            Event::handle('EndHandleSalmonTarget', array($this->activity, $this->target));
-        }
-    }
-
-    function handlePost()
-    {
-        // TRANS: Client exception.
-        throw new ClientException(_m('This target does not understand posts.'));
-    }
-
-    function handleFollow()
-    {
-        // TRANS: Client exception.
-        throw new ClientException(_m('This target does not understand follows.'));
-    }
-
-    function handleUnfollow()
-    {
-        // TRANS: Client exception.
-        throw new ClientException(_m('This target does not understand unfollows.'));
-    }
-
-    function handleFavorite()
-    {
-        // TRANS: Client exception.
-        throw new ClientException(_m('This target does not understand favorites.'));
-    }
-
-    function handleUnfavorite()
-    {
-        // TRANS: Client exception.
-        throw new ClientException(_m('This target does not understand unfavorites.'));
-    }
-
-    function handleShare()
-    {
-        // TRANS: Client exception.
-        throw new ClientException(_m('This target does not understand share events.'));
-    }
-
-    function handleJoin()
-    {
-        // TRANS: Client exception.
-        throw new ClientException(_m('This target does not understand joins.'));
-    }
-
-    function handleLeave()
-    {
-        // TRANS: Client exception.
-        throw new ClientException(_m('This target does not understand leave events.'));
-    }
-
-    function handleTag()
-    {
-        // TRANS: Client exception.
-        throw new ClientException(_m('This target does not understand list events.'));
-    }
-
-    function handleUntag()
-    {
-        // TRANS: Client exception.
-        throw new ClientException(_m('This target does not understand unlist events.'));
-    }
-
-    /**
-     * Remote user sent us an update to their profile.
-     * If we already know them, accept the updates.
-     */
-    function handleUpdateProfile()
-    {
-        $oprofile = Ostatus_profile::getActorProfile($this->activity);
-        if ($oprofile) {
-            common_log(LOG_INFO, "Got a profile-update ping from $oprofile->uri");
-            $oprofile->updateFromActivityObject($this->activity->actor);
-        } else {
-            common_log(LOG_INFO, "Ignoring profile-update ping from unknown " . $this->activity->actor->id);
-        }
-    }
-
-    /**
-     * @return Ostatus_profile
-     */
-    function ensureProfile()
-    {
-        $actor = $this->activity->actor;
-        if (empty($actor->id)) {
-            common_log(LOG_ERR, "broken actor: " . var_export($actor, true));
-            common_log(LOG_ERR, "activity with no actor: " . var_export($this->activity, true));
-            // TRANS: Exception.
-            throw new Exception(_m('Received a salmon slap from unidentified actor.'));
-        }
-
-        return Ostatus_profile::ensureActivityObjectProfile($actor);
-    }
-
-    function saveNotice()
-    {
-        $oprofile = $this->ensureProfile();
-        return $oprofile->processPost($this->activity, 'salmon');
-    }
-}
diff --git a/plugins/OStatus/lib/xrdaction.php b/plugins/OStatus/lib/xrdaction.php
deleted file mode 100644 (file)
index 779ce4d..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * @package OStatusPlugin
- * @maintainer James Walker <james@status.net>
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-class XrdAction extends Action
-{
-    public $uri;
-
-    public $user;
-
-    public $xrd;
-
-    function handle()
-    {
-        $nick    = $this->user->nickname;
-        $profile = $this->user->getProfile();
-
-        if (empty($this->xrd)) {
-            $xrd = new XRD();
-        } else {
-            $xrd = $this->xrd;
-        }
-
-        if (empty($xrd->subject)) {
-            $xrd->subject = Discovery::normalize($this->uri);
-        }
-
-        // Possible aliases for the user
-
-        $uris = array($this->user->uri, $profile->profileurl);
-
-        // FIXME: Webfinger generation code should live somewhere on its own
-
-        $path = common_config('site', 'path');
-
-        if (empty($path)) {
-            $uris[] = sprintf('acct:%s@%s', $nick, common_config('site', 'server'));
-        }
-
-        foreach ($uris as $uri) {
-            if ($uri != $xrd->subject) {
-                $xrd->alias[] = $uri;
-            }
-        }
-
-        $xrd->links[] = array('rel' => Discovery::PROFILEPAGE,
-                              'type' => 'text/html',
-                              'href' => $profile->profileurl);
-
-        $xrd->links[] = array('rel' => Discovery::UPDATESFROM,
-                              'href' => common_local_url('ApiTimelineUser',
-                                                         array('id' => $this->user->id,
-                                                               'format' => 'atom')),
-                              'type' => 'application/atom+xml');
-
-        // XFN
-        $xrd->links[] = array('rel' => 'http://gmpg.org/xfn/11',
-                              'type' => 'text/html',
-                              'href' => $profile->profileurl);
-        // FOAF
-        $xrd->links[] = array('rel' => 'describedby',
-                              'type' => 'application/rdf+xml',
-                              'href' => common_local_url('foaf',
-                                                         array('nickname' => $nick)));
-
-        // Salmon
-        $salmon_url = common_local_url('usersalmon',
-                                       array('id' => $this->user->id));
-
-        $xrd->links[] = array('rel' => Salmon::REL_SALMON,
-                              'href' => $salmon_url);
-        // XXX : Deprecated - to be removed.
-        $xrd->links[] = array('rel' => Salmon::NS_REPLIES,
-                              'href' => $salmon_url);
-
-        $xrd->links[] = array('rel' => Salmon::NS_MENTIONS,
-                              'href' => $salmon_url);
-
-        // Get this user's keypair
-        $magickey = Magicsig::getKV('user_id', $this->user->id);
-        if (!$magickey) {
-            // No keypair yet, let's generate one.
-            $magickey = new Magicsig();
-            $magickey->generate($this->user->id);
-        }
-
-        $xrd->links[] = array('rel' => Magicsig::PUBLICKEYREL,
-                              'href' => 'data:application/magic-public-key,'. $magickey->toString(false));
-
-        // TODO - finalize where the redirect should go on the publisher
-        $url = common_local_url('ostatussub') . '?profile={uri}';
-        $xrd->links[] = array('rel' => 'http://ostatus.org/schema/1.0/subscribe',
-                              'template' => $url );
-
-        $url = common_local_url('tagprofile') . '?uri={uri}';
-        $xrd->links[] = array('rel' => 'http://ostatus.org/schema/1.0/tag',
-                              'template' => $url );
-
-        header('Content-type: application/xrd+xml');
-        print $xrd->toXML();
-    }
-}
index a7bede2875d23d10a3354b55c4340beeaddf2a18..1dd636930ef36517ae12f0afd18b48d34f1ac3ac 100644 (file)
@@ -49,22 +49,6 @@ if (!defined('STATUSNET')) {
 
 class OfflineBackupPlugin extends Plugin
 {
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'OfflinebackupAction':
-            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'OfflineBackupQueueHandler':
-            include_once $dir . '/'.strtolower($cls).'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
 
     function onRouterInitialized($m)
     {
diff --git a/plugins/OfflineBackup/actions/offlinebackup.php b/plugins/OfflineBackup/actions/offlinebackup.php
new file mode 100644 (file)
index 0000000..a9c64ca
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Initiate an offline backup
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Offline backup
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Action to initiate an offline backup
+ *
+ * @category  Offline backup
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class OfflinebackupAction extends BackupaccountAction
+{
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+
+    function handle($argarray=null)
+    {
+        if ($this->isPost()) {
+            $this->queueBackup();
+        } else {
+            $this->showPage();
+        }
+        return;
+    }
+
+    function queueBackup()
+    {
+        $cur = common_current_user();
+
+        $qm = QueueManager::get();
+
+        $qm->enqueue($cur->id, 'backoff');
+
+        $this->showPage();
+    }
+
+    function showContent()
+    {
+        if ($this->isPost()) {
+            $this->text(_('Backup queued. You will get a notification by email when your backup is ready to download.'));
+        } else {
+            parent::showContent();
+        }
+    }
+}
diff --git a/plugins/OfflineBackup/lib/offlinebackupqueuehandler.php b/plugins/OfflineBackup/lib/offlinebackupqueuehandler.php
new file mode 100644 (file)
index 0000000..ba95912
--- /dev/null
@@ -0,0 +1,327 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Offline backup queue handler
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Offline backup
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Offline backup queue handler
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class OfflineBackupQueueHandler extends QueueHandler
+{
+    function transport()
+    {
+        return 'backoff';
+    }
+
+    function handle($object)
+    {
+        $userId = $object;
+
+        $user = User::getKV($userId);
+
+        common_log(LOG_INFO, "Making backup file for user ".$user->nickname);
+
+        $fileName = $this->makeBackupFile($user);
+
+        common_log(LOG_INFO, "Notifying user ".$user->nickname . " of their new backup file.");
+
+        $this->notifyBackupFile($user, $fileName);
+
+        return true;
+    }
+
+    function makeBackupFile($user)
+    {
+        // XXX: this is pretty lose-y;  try another way
+
+        $tmpdir = sys_get_temp_dir() . '/offline-backup/' . $user->nickname . '/' . common_date_iso8601(common_sql_now());
+
+        common_log(LOG_INFO, 'Writing backup data to ' . $tmpdir . ' for ' . $user->nickname);
+
+        mkdir($tmpdir, 0700, true);
+
+        $this->dumpNotices($user, $tmpdir);
+        $this->dumpFaves($user, $tmpdir);
+        $this->dumpSubscriptions($user, $tmpdir);
+        $this->dumpSubscribers($user, $tmpdir);
+        $this->dumpGroups($user, $tmpdir);
+
+        $fileName = File::filename($user->getProfile(), "backup", "application/atom+xml");
+        $fullPath = File::path($fileName);
+
+        $this->makeActivityFeed($user, $tmpdir, $fullPath);
+
+        $this->delTree($tmpdir);
+
+        return $fileName;
+    }
+
+    function notifyBackupFile($user, $fileName)
+    {
+        $fileUrl = File::url($fileName);
+
+        $body = sprintf(_m("The backup file you requested is ready for download.\n\n".
+                           "%s\n".
+                           "Thanks for your time,\n",
+                           "%s\n"),
+                        $fileUrl,
+                        common_config('site', 'name'));
+
+        $headers = _mail_prepare_headers('offlinebackup', $user->nickname, $user->nickname);
+
+        mail_to_user($user, _('Backup file ready for download'), $body, $headers);
+    }
+
+    function dumpNotices($user, $dir)
+    {
+        common_log(LOG_INFO, 'dumping notices by ' . $user->nickname . ' to directory ' . $dir);
+
+        $profile = $user->getProfile();
+
+        $stream = new ProfileNoticeStream($profile, $profile);
+
+        $page = 1;
+
+        do {
+
+            $notice = $stream->getNotices(($page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
+
+            while ($notice->fetch()) {
+                try {
+                    $fname = $dir . '/'. common_date_iso8601($notice->created) . '-notice-' . $notice->id . '.atom'; 
+                    $data  = $notice->asAtomEntry(false, false, false, null);
+                    common_log(LOG_INFO, 'dumping notice ' . $notice->id . ' to file ' . $fname);
+                    file_put_contents($fname, $data);
+                    $data  = null;
+                } catch (Exception $e) {
+                    common_log(LOG_ERR, "Error backing up notice " . $notice->id . ": " . $e->getMessage());
+                    continue;
+                }
+            }
+
+            $page++;
+
+        } while ($notice->N > NOTICES_PER_PAGE);
+    }
+
+    function dumpFaves($user, $dir)
+    {
+        common_log(LOG_INFO, 'dumping faves by ' . $user->nickname . ' to directory ' . $dir);
+        
+        $page = 1;
+
+        do {
+            $fave = Fave::byProfile($user->id, ($page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
+
+            while ($fave->fetch()) {
+                try {
+                    $fname = $dir . '/'. common_date_iso8601($fave->modified) . '-fave-' . $fave->notice_id . '.atom'; 
+                    $act   = $fave->asActivity();
+                    $data  = $act->asString(false, false, false);
+                    common_log(LOG_INFO, 'dumping fave of ' . $fave->notice_id . ' to file ' . $fname);
+                    file_put_contents($fname, $data);
+                    $data  = null;
+                } catch (Exception $e) {
+                    common_log(LOG_ERR, "Error backing up fave of " . $fave->notice_id . ": " . $e->getMessage());
+                    continue;
+                }
+            }
+            
+            $page++;
+
+        } while ($fave->N > NOTICES_PER_PAGE);
+    }
+
+    function dumpSubscriptions($user, $dir)
+    {
+        common_log(LOG_INFO, 'dumping subscriptions by ' . $user->nickname . ' to directory ' . $dir);
+        
+        $page = 1;
+
+        do {
+            $sub = Subscription::bySubscriber($user->id, ($page-1)*PROFILES_PER_PAGE, PROFILES_PER_PAGE + 1);
+
+            while ($sub->fetch()) {
+                try {
+                    if ($sub->subscribed == $user->id) {
+                        continue;
+                    }
+                    $fname = $dir . '/'. common_date_iso8601($sub->created) . '-subscription-' . $sub->subscribed . '.atom'; 
+                    $act   = $sub->asActivity();
+                    $data  = $act->asString(false, false, false);
+                    common_log(LOG_INFO, 'dumping sub of ' . $sub->subscribed . ' to file ' . $fname);
+                    file_put_contents($fname, $data);
+                    $data  = null;
+                } catch (Exception $e) {
+                    common_log(LOG_ERR, "Error backing up subscription to " . $sub->subscribed . ": " . $e->getMessage());
+                    continue;
+                }
+            }
+
+            $page++;
+
+        } while ($sub->N > PROFILES_PER_PAGE);
+    }
+
+    function dumpSubscribers($user, $dir)
+    {
+        common_log(LOG_INFO, 'dumping subscribers to ' . $user->nickname . ' to directory ' . $dir);
+        
+        $page = 1;
+
+        do {
+            $sub = Subscription::bySubscribed($user->id, ($page-1)*PROFILES_PER_PAGE, PROFILES_PER_PAGE + 1);
+
+            while ($sub->fetch()) {
+                try {
+                    if ($sub->subscriber == $user->id) {
+                        continue;
+                    }
+                    $fname = $dir . '/'. common_date_iso8601($sub->created) . '-subscriber-' . $sub->subscriber . '.atom'; 
+                    $act   = $sub->asActivity();
+                    $data  = $act->asString(false, true, false);
+                    common_log(LOG_INFO, 'dumping sub by ' . $sub->subscriber . ' to file ' . $fname);
+                    file_put_contents($fname, $data);
+                    $data  = null;
+                } catch (Exception $e) {
+                    common_log(LOG_ERR, "Error backing up subscription from " . $sub->subscriber . ": " . $e->getMessage());
+                    continue;
+                }
+            }
+
+            $page++;
+
+        } while ($sub->N > PROFILES_PER_PAGE);
+    }
+
+    function dumpGroups($user, $dir)
+    {
+        common_log(LOG_INFO, 'dumping memberships of ' . $user->nickname . ' to directory ' . $dir);
+        
+        $page = 1;
+
+        do {
+
+            $mem = Group_member::byMember($user->id, ($page-1)*GROUPS_PER_PAGE, GROUPS_PER_PAGE + 1);
+
+            while ($mem->fetch()) {
+                try {
+                    $fname = $dir . '/'. common_date_iso8601($mem->created) . '-membership-' . $mem->group_id . '.atom'; 
+                    $act   = $mem->asActivity();
+                    $data  = $act->asString(false, false, false);
+                    common_log(LOG_INFO, 'dumping membership in ' . $mem->group_id . ' to file ' . $fname);
+                    file_put_contents($fname, $data);
+                    $data  = null;
+                } catch (Exception $e) {
+                    common_log(LOG_ERR, "Error backing up membership in " . $mem->group_id . ": " . $e->getMessage());
+                    continue;
+                }
+            }
+
+            $page++;
+
+        } while ($mem->N > GROUPS_PER_PAGE);
+    }
+
+    function makeActivityFeed($user, $tmpdir, $fullPath)
+    {
+        $handle = fopen($fullPath, 'c');
+
+        $this->writeFeedHeader($user, $handle);
+
+        $objects = scandir($tmpdir);
+
+        rsort($objects);
+
+        foreach ($objects as $object) {
+            $objFull = $tmpdir . '/' . $object;
+            if (!is_dir($objFull)) {
+                $entry = file_get_contents($objFull);
+                fwrite($handle, $entry);
+                $entry = null;
+            }
+        }
+
+        $this->writeFeedFooter($user, $handle);
+        fclose($handle);
+    }
+
+    function writeFeedHeader($user, $handle)
+    {
+        fwrite($handle, '<?xml version="1.0" encoding="UTF-8"?>');
+        fwrite($handle, "\n");
+        fwrite($handle, '<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:georss="http://www.georss.org/georss" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:media="http://purl.org/syndication/atommedia" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:statusnet="http://status.net/schema/api/1/">');
+        fwrite($handle, "\n");
+
+        $profile = $user->getProfile();
+
+        $author = ActivityObject::fromProfile($profile);
+
+        $xs = new XMLStringer();
+        $author->outputTo($xs, 'author');
+        fwrite($handle, $xs->getString());
+        fwrite($handle, "\n");
+    }
+
+    function writeFeedFooter($user, $handle)
+    {
+        fwrite($handle, '</feed>');
+    }
+
+    function delTree($dir)
+    {
+        if (is_dir($dir)) {
+            $objects = scandir($dir);
+            foreach ($objects as $object) {
+                if ($object != "." && $object != "..") {
+                    if (filetype($dir."/".$object) == "dir") {
+                        $this->delTree($dir."/".$object);
+                    } else {
+                        unlink($dir."/".$object);
+                    }
+                }
+            }
+            reset($objects);
+            rmdir($dir);
+        }
+    }
+}
diff --git a/plugins/OfflineBackup/offlinebackup.php b/plugins/OfflineBackup/offlinebackup.php
deleted file mode 100644 (file)
index a9c64ca..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Initiate an offline backup
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Offline backup
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Action to initiate an offline backup
- *
- * @category  Offline backup
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class OfflinebackupAction extends BackupaccountAction
-{
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-
-    function handle($argarray=null)
-    {
-        if ($this->isPost()) {
-            $this->queueBackup();
-        } else {
-            $this->showPage();
-        }
-        return;
-    }
-
-    function queueBackup()
-    {
-        $cur = common_current_user();
-
-        $qm = QueueManager::get();
-
-        $qm->enqueue($cur->id, 'backoff');
-
-        $this->showPage();
-    }
-
-    function showContent()
-    {
-        if ($this->isPost()) {
-            $this->text(_('Backup queued. You will get a notification by email when your backup is ready to download.'));
-        } else {
-            parent::showContent();
-        }
-    }
-}
diff --git a/plugins/OfflineBackup/offlinebackupqueuehandler.php b/plugins/OfflineBackup/offlinebackupqueuehandler.php
deleted file mode 100644 (file)
index ba95912..0000000
+++ /dev/null
@@ -1,327 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Offline backup queue handler
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Offline backup
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Offline backup queue handler
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class OfflineBackupQueueHandler extends QueueHandler
-{
-    function transport()
-    {
-        return 'backoff';
-    }
-
-    function handle($object)
-    {
-        $userId = $object;
-
-        $user = User::getKV($userId);
-
-        common_log(LOG_INFO, "Making backup file for user ".$user->nickname);
-
-        $fileName = $this->makeBackupFile($user);
-
-        common_log(LOG_INFO, "Notifying user ".$user->nickname . " of their new backup file.");
-
-        $this->notifyBackupFile($user, $fileName);
-
-        return true;
-    }
-
-    function makeBackupFile($user)
-    {
-        // XXX: this is pretty lose-y;  try another way
-
-        $tmpdir = sys_get_temp_dir() . '/offline-backup/' . $user->nickname . '/' . common_date_iso8601(common_sql_now());
-
-        common_log(LOG_INFO, 'Writing backup data to ' . $tmpdir . ' for ' . $user->nickname);
-
-        mkdir($tmpdir, 0700, true);
-
-        $this->dumpNotices($user, $tmpdir);
-        $this->dumpFaves($user, $tmpdir);
-        $this->dumpSubscriptions($user, $tmpdir);
-        $this->dumpSubscribers($user, $tmpdir);
-        $this->dumpGroups($user, $tmpdir);
-
-        $fileName = File::filename($user->getProfile(), "backup", "application/atom+xml");
-        $fullPath = File::path($fileName);
-
-        $this->makeActivityFeed($user, $tmpdir, $fullPath);
-
-        $this->delTree($tmpdir);
-
-        return $fileName;
-    }
-
-    function notifyBackupFile($user, $fileName)
-    {
-        $fileUrl = File::url($fileName);
-
-        $body = sprintf(_m("The backup file you requested is ready for download.\n\n".
-                           "%s\n".
-                           "Thanks for your time,\n",
-                           "%s\n"),
-                        $fileUrl,
-                        common_config('site', 'name'));
-
-        $headers = _mail_prepare_headers('offlinebackup', $user->nickname, $user->nickname);
-
-        mail_to_user($user, _('Backup file ready for download'), $body, $headers);
-    }
-
-    function dumpNotices($user, $dir)
-    {
-        common_log(LOG_INFO, 'dumping notices by ' . $user->nickname . ' to directory ' . $dir);
-
-        $profile = $user->getProfile();
-
-        $stream = new ProfileNoticeStream($profile, $profile);
-
-        $page = 1;
-
-        do {
-
-            $notice = $stream->getNotices(($page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
-
-            while ($notice->fetch()) {
-                try {
-                    $fname = $dir . '/'. common_date_iso8601($notice->created) . '-notice-' . $notice->id . '.atom'; 
-                    $data  = $notice->asAtomEntry(false, false, false, null);
-                    common_log(LOG_INFO, 'dumping notice ' . $notice->id . ' to file ' . $fname);
-                    file_put_contents($fname, $data);
-                    $data  = null;
-                } catch (Exception $e) {
-                    common_log(LOG_ERR, "Error backing up notice " . $notice->id . ": " . $e->getMessage());
-                    continue;
-                }
-            }
-
-            $page++;
-
-        } while ($notice->N > NOTICES_PER_PAGE);
-    }
-
-    function dumpFaves($user, $dir)
-    {
-        common_log(LOG_INFO, 'dumping faves by ' . $user->nickname . ' to directory ' . $dir);
-        
-        $page = 1;
-
-        do {
-            $fave = Fave::byProfile($user->id, ($page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
-
-            while ($fave->fetch()) {
-                try {
-                    $fname = $dir . '/'. common_date_iso8601($fave->modified) . '-fave-' . $fave->notice_id . '.atom'; 
-                    $act   = $fave->asActivity();
-                    $data  = $act->asString(false, false, false);
-                    common_log(LOG_INFO, 'dumping fave of ' . $fave->notice_id . ' to file ' . $fname);
-                    file_put_contents($fname, $data);
-                    $data  = null;
-                } catch (Exception $e) {
-                    common_log(LOG_ERR, "Error backing up fave of " . $fave->notice_id . ": " . $e->getMessage());
-                    continue;
-                }
-            }
-            
-            $page++;
-
-        } while ($fave->N > NOTICES_PER_PAGE);
-    }
-
-    function dumpSubscriptions($user, $dir)
-    {
-        common_log(LOG_INFO, 'dumping subscriptions by ' . $user->nickname . ' to directory ' . $dir);
-        
-        $page = 1;
-
-        do {
-            $sub = Subscription::bySubscriber($user->id, ($page-1)*PROFILES_PER_PAGE, PROFILES_PER_PAGE + 1);
-
-            while ($sub->fetch()) {
-                try {
-                    if ($sub->subscribed == $user->id) {
-                        continue;
-                    }
-                    $fname = $dir . '/'. common_date_iso8601($sub->created) . '-subscription-' . $sub->subscribed . '.atom'; 
-                    $act   = $sub->asActivity();
-                    $data  = $act->asString(false, false, false);
-                    common_log(LOG_INFO, 'dumping sub of ' . $sub->subscribed . ' to file ' . $fname);
-                    file_put_contents($fname, $data);
-                    $data  = null;
-                } catch (Exception $e) {
-                    common_log(LOG_ERR, "Error backing up subscription to " . $sub->subscribed . ": " . $e->getMessage());
-                    continue;
-                }
-            }
-
-            $page++;
-
-        } while ($sub->N > PROFILES_PER_PAGE);
-    }
-
-    function dumpSubscribers($user, $dir)
-    {
-        common_log(LOG_INFO, 'dumping subscribers to ' . $user->nickname . ' to directory ' . $dir);
-        
-        $page = 1;
-
-        do {
-            $sub = Subscription::bySubscribed($user->id, ($page-1)*PROFILES_PER_PAGE, PROFILES_PER_PAGE + 1);
-
-            while ($sub->fetch()) {
-                try {
-                    if ($sub->subscriber == $user->id) {
-                        continue;
-                    }
-                    $fname = $dir . '/'. common_date_iso8601($sub->created) . '-subscriber-' . $sub->subscriber . '.atom'; 
-                    $act   = $sub->asActivity();
-                    $data  = $act->asString(false, true, false);
-                    common_log(LOG_INFO, 'dumping sub by ' . $sub->subscriber . ' to file ' . $fname);
-                    file_put_contents($fname, $data);
-                    $data  = null;
-                } catch (Exception $e) {
-                    common_log(LOG_ERR, "Error backing up subscription from " . $sub->subscriber . ": " . $e->getMessage());
-                    continue;
-                }
-            }
-
-            $page++;
-
-        } while ($sub->N > PROFILES_PER_PAGE);
-    }
-
-    function dumpGroups($user, $dir)
-    {
-        common_log(LOG_INFO, 'dumping memberships of ' . $user->nickname . ' to directory ' . $dir);
-        
-        $page = 1;
-
-        do {
-
-            $mem = Group_member::byMember($user->id, ($page-1)*GROUPS_PER_PAGE, GROUPS_PER_PAGE + 1);
-
-            while ($mem->fetch()) {
-                try {
-                    $fname = $dir . '/'. common_date_iso8601($mem->created) . '-membership-' . $mem->group_id . '.atom'; 
-                    $act   = $mem->asActivity();
-                    $data  = $act->asString(false, false, false);
-                    common_log(LOG_INFO, 'dumping membership in ' . $mem->group_id . ' to file ' . $fname);
-                    file_put_contents($fname, $data);
-                    $data  = null;
-                } catch (Exception $e) {
-                    common_log(LOG_ERR, "Error backing up membership in " . $mem->group_id . ": " . $e->getMessage());
-                    continue;
-                }
-            }
-
-            $page++;
-
-        } while ($mem->N > GROUPS_PER_PAGE);
-    }
-
-    function makeActivityFeed($user, $tmpdir, $fullPath)
-    {
-        $handle = fopen($fullPath, 'c');
-
-        $this->writeFeedHeader($user, $handle);
-
-        $objects = scandir($tmpdir);
-
-        rsort($objects);
-
-        foreach ($objects as $object) {
-            $objFull = $tmpdir . '/' . $object;
-            if (!is_dir($objFull)) {
-                $entry = file_get_contents($objFull);
-                fwrite($handle, $entry);
-                $entry = null;
-            }
-        }
-
-        $this->writeFeedFooter($user, $handle);
-        fclose($handle);
-    }
-
-    function writeFeedHeader($user, $handle)
-    {
-        fwrite($handle, '<?xml version="1.0" encoding="UTF-8"?>');
-        fwrite($handle, "\n");
-        fwrite($handle, '<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:georss="http://www.georss.org/georss" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:media="http://purl.org/syndication/atommedia" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:statusnet="http://status.net/schema/api/1/">');
-        fwrite($handle, "\n");
-
-        $profile = $user->getProfile();
-
-        $author = ActivityObject::fromProfile($profile);
-
-        $xs = new XMLStringer();
-        $author->outputTo($xs, 'author');
-        fwrite($handle, $xs->getString());
-        fwrite($handle, "\n");
-    }
-
-    function writeFeedFooter($user, $handle)
-    {
-        fwrite($handle, '</feed>');
-    }
-
-    function delTree($dir)
-    {
-        if (is_dir($dir)) {
-            $objects = scandir($dir);
-            foreach ($objects as $object) {
-                if ($object != "." && $object != "..") {
-                    if (filetype($dir."/".$object) == "dir") {
-                        $this->delTree($dir."/".$object);
-                    } else {
-                        unlink($dir."/".$object);
-                    }
-                }
-            }
-            reset($objects);
-            rmdir($dir);
-        }
-    }
-}
index f23311873977b8c477a6f0f4e8d84fff05c0281b..f4a9e061a1be6a2ff7fb0697240eefbfe9d41627 100644 (file)
@@ -350,30 +350,14 @@ class OpenIDPlugin extends Plugin
     {
         switch ($cls)
         {
-        case 'OpenidloginAction':
-        case 'FinishopenidloginAction':
-        case 'FinishaddopenidAction':
-        case 'XrdsAction':
-        case 'PublicxrdsAction':
-        case 'OpenidsettingsAction':
-        case 'OpenidserverAction':
-        case 'OpenidtrustAction':
-        case 'OpenidadminpanelAction':
-            require_once dirname(__FILE__) . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'User_openid':
-        case 'User_openid_prefs':
-        case 'User_openid_trustroot':
-            require_once dirname(__FILE__) . '/' . $cls . '.php';
-            return false;
         case 'Auth_OpenID_TeamsExtension':
         case 'Auth_OpenID_TeamsRequest':
         case 'Auth_OpenID_TeamsResponse':
             require_once dirname(__FILE__) . '/extlib/teams-extension.php';
             return false;
-        default:
-            return true;
         }
+
+        return parent::onAutoload($cls);
     }
 
     /**
diff --git a/plugins/OpenID/User_openid.php b/plugins/OpenID/User_openid.php
deleted file mode 100644 (file)
index baff5cd..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-/**
- * Table Definition for user_openid
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-class User_openid extends Managed_DataObject
-{
-    ###START_AUTOCODE
-    /* the code below is auto generated do not remove the above tag */
-
-    public $__table = 'user_openid';                     // table name
-    public $canonical;                       // varchar(255)  primary_key not_null
-    public $display;                         // varchar(255)  unique_key not_null
-    public $user_id;                         // int(4)   not_null
-    public $created;                         // datetime()   not_null
-    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    /* the code above is auto generated do not remove the tag below */
-    ###END_AUTOCODE
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'canonical' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'OpenID canonical string'),
-                'display' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'OpenID display string'),
-                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'User ID for OpenID owner'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('canonical'),
-            'unique keys' => array(
-                'user_openid_display_key' => array('display'),
-            ),
-            'indexes' => array(
-                'user_openid_user_id_idx' => array('user_id'),
-            ),
-            'foreign keys' => array(
-                'user_openid_user_id_fkey' => array('user', array('user_id' => 'id')),
-            ),
-        );
-    }
-
-    static function hasOpenID($user_id)
-    {
-        $oid = new User_openid();
-
-        $oid->user_id = $user_id;
-
-        $cnt = $oid->find();
-
-        return ($cnt > 0);
-    }
-}
diff --git a/plugins/OpenID/User_openid_prefs.php b/plugins/OpenID/User_openid_prefs.php
deleted file mode 100644 (file)
index fcd88f4..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2012, StatusNet, Inc.
- *
- * User_openid_prefs.php
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  OpenID
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2012 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Store preferences for OpenID use in StatusNet
- *
- * @category OpenID
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-
-class User_openid_prefs extends Managed_DataObject
-{
-    public $__table = 'user_openid_prefs'; // table name
-
-    public $user_id;            // The User with the prefs
-    public $hide_profile_link;  // Hide the link on the profile block?
-    public $created;            // datetime
-    public $modified;           // datetime
-
-    /**
-     * The One True Thingy that must be defined and declared.
-     */
-
-    public static function schemaDef()
-    {
-        return array(
-                     'description' => 'Per-user preferences for OpenID display',
-                     'fields' => array('user_id' => array('type' => 'integer',
-                                                          'not null' => true,
-                                                          'description' => 'User whose prefs we are saving'),
-                                       'hide_profile_link' => array('type' => 'int',
-                                                                    'not null' => true,
-                                                                    'default' => 0,
-                                                                    'description' => 'Whether to hide profile links from profile block'),
-                                       'created' => array('type' => 'datetime',
-                                                          'not null' => true,
-                                                          'description' => 'date this record was created'),
-                                       'modified' => array('type' => 'datetime',
-                                                           'not null' => true,
-                                                           'description' => 'date this record was modified'),
-                                       ),
-                     'primary key' => array('user_id'),
-                     'foreign keys' => array('user_openid_prefs_user_id_fkey' => array('user', array('user_id' => 'id')),
-                                             ),
-                     'indexes' => array(),
-                     );
-    }
-}
diff --git a/plugins/OpenID/User_openid_trustroot.php b/plugins/OpenID/User_openid_trustroot.php
deleted file mode 100644 (file)
index ec09e17..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-/**
- * Table Definition for user_openid_trustroot
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-class User_openid_trustroot extends Managed_DataObject
-{
-    ###START_AUTOCODE
-    /* the code below is auto generated do not remove the above tag */
-
-    public $__table = 'user_openid_trustroot';                     // table name
-    public $trustroot;                         // varchar(255) primary_key not_null
-    public $user_id;                         // int(4)  primary_key not_null
-    public $created;                         // datetime()   not_null
-    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    /* the code above is auto generated do not remove the tag below */
-    ###END_AUTOCODE
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'trustroot' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'OpenID trustroot string'),
-                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'User ID for OpenID trustroot owner'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('trustroot', 'user_id'),
-        );
-    }
-}
diff --git a/plugins/OpenID/actions/finishaddopenid.php b/plugins/OpenID/actions/finishaddopenid.php
new file mode 100644 (file)
index 0000000..5182e50
--- /dev/null
@@ -0,0 +1,197 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Complete adding an OpenID
+ *
+ * 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  Settings
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
+
+/**
+ * Complete adding an OpenID
+ *
+ * Handle the return from an OpenID verification
+ *
+ * @category Settings
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class FinishaddopenidAction extends Action
+{
+    var $msg = null;
+
+    /**
+     * Handle the redirect back from OpenID confirmation
+     *
+     * Check to see if the user's logged in, and then try
+     * to use the OpenID login system.
+     *
+     * @param array $args $_REQUEST arguments
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+        if (!common_logged_in()) {
+            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
+            $this->clientError(_m('Not logged in.'));
+        } else {
+            $this->tryLogin();
+        }
+    }
+
+    /**
+     * Try to log in using OpenID
+     *
+     * Check the OpenID for validity; potentially store it.
+     *
+     * @return void
+     */
+    function tryLogin()
+    {
+        $consumer = oid_consumer();
+
+        $response = $consumer->complete(common_local_url('finishaddopenid'));
+
+        if ($response->status == Auth_OpenID_CANCEL) {
+            // TRANS: Status message in case the response from the OpenID provider is that the logon attempt was cancelled.
+            $this->message(_m('OpenID authentication cancelled.'));
+            return;
+        } else if ($response->status == Auth_OpenID_FAILURE) {
+            // TRANS: OpenID authentication failed; display the error message.
+            // TRANS: %s is the error message.
+            $this->message(sprintf(_m('OpenID authentication failed: %s.'),
+                                   $response->message));
+        } else if ($response->status == Auth_OpenID_SUCCESS) {
+
+            $display   = $response->getDisplayIdentifier();
+            $canonical = ($response->endpoint && $response->endpoint->canonicalID) ?
+              $response->endpoint->canonicalID : $display;
+
+            $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
+
+            if ($sreg_resp) {
+                $sreg = $sreg_resp->contents();
+            }
+
+            // Launchpad teams extension
+            if (!oid_check_teams($response)) {
+                // TRANS: OpenID authentication error.
+                $this->message(_m('OpenID authentication aborted: You are not allowed to login to this site.'));
+                return;
+            }
+
+            $cur = common_current_user();
+
+            $other = oid_get_user($canonical);
+
+            if ($other) {
+                if ($other->id == $cur->id) {
+                    // TRANS: Message in case a user tries to add an OpenID that is already connected to them.
+                    $this->message(_m('You already have this OpenID!'));
+                } else {
+                    // TRANS: Message in case a user tries to add an OpenID that is already used by another user.
+                    $this->message(_m('Someone else already has this OpenID.'));
+                }
+                return;
+            }
+
+            // start a transaction
+
+            $cur->query('BEGIN');
+
+            $result = oid_link_user($cur->id, $canonical, $display);
+
+            if (!$result) {
+                // TRANS: Message in case the OpenID object cannot be connected to the user.
+                $this->message(_m('Error connecting user.'));
+                return;
+            }
+            if (Event::handle('StartOpenIDUpdateUser', array($cur, $canonical, &$sreg))) {
+                if ($sreg) {
+                    if (!oid_update_user($cur, $sreg)) {
+                        // TRANS: Message in case the user or the user profile cannot be saved in StatusNet.
+                        $this->message(_m('Error updating profile.'));
+                        return;
+                    }
+                }
+            }
+            Event::handle('EndOpenIDUpdateUser', array($cur, $canonical, $sreg));
+
+            // success!
+
+            $cur->query('COMMIT');
+
+            oid_set_last($display);
+
+            common_redirect(common_local_url('openidsettings'), 303);
+        }
+    }
+
+    /**
+     * Show a failure message
+     *
+     * Something went wrong. Save the message, and show the page.
+     *
+     * @param string $msg Error message to show
+     *
+     * @return void
+     */
+    function message($msg)
+    {
+        $this->message = $msg;
+        $this->showPage();
+    }
+
+    /**
+     * Title of the page
+     *
+     * @return string title
+     */
+    function title()
+    {
+        // TRANS: Title after getting the status of the OpenID authorisation request.
+        return _m('OpenID Login');
+    }
+
+    /**
+     * Show error message
+     *
+     * @return void
+     */
+    function showPageNotice()
+    {
+        if ($this->message) {
+            $this->element('p', 'error', $this->message);
+        }
+    }
+}
diff --git a/plugins/OpenID/actions/finishopenidlogin.php b/plugins/OpenID/actions/finishopenidlogin.php
new file mode 100644 (file)
index 0000000..4326f19
--- /dev/null
@@ -0,0 +1,591 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
+
+class FinishopenidloginAction extends Action
+{
+    var $error = null;
+    var $username = null;
+    var $message = null;
+
+    function handle($args)
+    {
+        parent::handle($args);
+        if (common_is_real_login()) {
+            // TRANS: Client error message trying to log on with OpenID while already logged on.
+            $this->clientError(_m('Already logged in.'));
+        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $token = $this->trimmed('token');
+            if (!$token || $token != common_session_token()) {
+                // TRANS: Message given when there is a problem with the user's session token.
+                $this->showForm(_m('There was a problem with your session token. Try again, please.'));
+                return;
+            }
+            if ($this->arg('create')) {
+                if (!$this->boolean('license')) {
+                    // TRANS: Message given if user does not agree with the site's license.
+                    $this->showForm(_m('You cannot register if you do not agree to the license.'),
+                                    $this->trimmed('newname'));
+                    return;
+                }
+                $this->createNewUser();
+            } else if ($this->arg('connect')) {
+                $this->connectUser();
+            } else {
+                // TRANS: Messag given on an unknown error.
+                $this->showForm(_m('An unknown error has occured.'),
+                                $this->trimmed('newname'));
+            }
+        } else {
+            $this->tryLogin();
+        }
+    }
+
+    function showPageNotice()
+    {
+        if ($this->error) {
+            $this->element('div', array('class' => 'error'), $this->error);
+        } else {
+            $this->element('div', 'instructions',
+                           // TRANS: Instructions given after a first successful logon using OpenID.
+                           // TRANS: %s is the site name.
+                           sprintf(_m('This is the first time you have logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
+        }
+    }
+
+    function title()
+    {
+        // TRANS: Title
+        return _m('TITLE','OpenID Account Setup');
+    }
+
+    function showForm($error=null, $username=null)
+    {
+        $this->error = $error;
+        $this->username = $username;
+
+        $this->showPage();
+    }
+
+    /**
+     * @fixme much of this duplicates core code, which is very fragile.
+     * Should probably be replaced with an extensible mini version of
+     * the core registration form.
+     */
+    function showContent()
+    {
+        if (!empty($this->message_text)) {
+            $this->element('div', array('class' => 'error'), $this->message_text);
+            return;
+        }
+
+        // We don't recognize this OpenID, so we're going to give the user
+        // two options, each in its own mini-form.
+        //
+        // First, they can create a new account using their OpenID auth
+        // info. The profile will be pre-populated with whatever name,
+        // email, and location we can get from the OpenID provider, so
+        // all we ask for is the license confirmation.
+        $this->elementStart('form', array('method' => 'post',
+                                          'id' => 'account_create',
+                                          'class' => 'form_settings',
+                                          'action' => common_local_url('finishopenidlogin')));
+        $this->hidden('token', common_session_token());
+        $this->elementStart('fieldset', array('id' => 'form_openid_createaccount'));
+        $this->element('legend', null,
+                       // TRANS: Fieldset legend.
+                       _m('Create new account'));
+        $this->element('p', null,
+                       // TRANS: Form guide.
+                       _m('Create a new user with this nickname.'));
+        $this->elementStart('ul', 'form_data');
+
+        // Hook point for captcha etc
+        Event::handle('StartRegistrationFormData', array($this));
+
+        $this->elementStart('li');
+        // TRANS: Field label.
+        $this->input('newname', _m('New nickname'),
+                     ($this->username) ? $this->username : '',
+                     // TRANS: Field title.
+                     _m('1-64 lowercase letters or numbers, no punctuation or spaces.'));
+        $this->elementEnd('li');
+        $this->elementStart('li');
+        // TRANS: Field label.
+        $this->input('email', _m('Email'), $this->getEmail(),
+                     // TRANS: Field title.
+                     _m('Used only for updates, announcements, '.
+                       'and password recovery.'));
+        $this->elementEnd('li');
+
+        // Hook point for captcha etc
+        Event::handle('EndRegistrationFormData', array($this));
+
+        $this->elementStart('li');
+        $this->element('input', array('type' => 'checkbox',
+                                      'id' => 'license',
+                                      'class' => 'checkbox',
+                                      'name' => 'license',
+                                      'value' => 'true'));
+        $this->elementStart('label', array('for' => 'license',
+                                          'class' => 'checkbox'));
+        // TRANS: OpenID plugin link text.
+        // TRANS: %s is a link to a license with the license name as link text.
+        $message = _m('My text and files are available under %s ' .
+                     'except this private data: password, ' .
+                     'email address, IM address, and phone number.');
+        $link = '<a href="' .
+                htmlspecialchars(common_config('license', 'url')) .
+                '">' .
+                htmlspecialchars(common_config('license', 'title')) .
+                '</a>';
+        $this->raw(sprintf(htmlspecialchars($message), $link));
+        $this->elementEnd('label');
+        $this->elementEnd('li');
+        $this->elementEnd('ul');
+        // TRANS: Button label in form in which to create a new user on the site for an OpenID.
+        $this->submit('create', _m('BUTTON', 'Create'));
+        $this->elementEnd('fieldset');
+        $this->elementEnd('form');
+
+        // The second option is to attach this OpenID to an existing account
+        // on the local system, which they need to provide a password for.
+        $this->elementStart('form', array('method' => 'post',
+                                          'id' => 'account_connect',
+                                          'class' => 'form_settings',
+                                          'action' => common_local_url('finishopenidlogin')));
+        $this->hidden('token', common_session_token());
+        $this->elementStart('fieldset', array('id' => 'form_openid_createaccount'));
+        $this->element('legend', null,
+                       // TRANS: Used as form legend for form in which to connect an OpenID to an existing user on the site.
+                       _m('Connect existing account'));
+        $this->element('p', null,
+                       // TRANS: User instructions for form in which to connect an OpenID to an existing user on the site.
+                       _m('If you already have an account, login with your username and password to connect it to your OpenID.'));
+        $this->elementStart('ul', 'form_data');
+        $this->elementStart('li');
+        // TRANS: Field label in form in which to connect an OpenID to an existing user on the site.
+        $this->input('nickname', _m('Existing nickname'));
+        $this->elementEnd('li');
+        $this->elementStart('li');
+        // TRANS: Field label in form in which to connect an OpenID to an existing user on the site.
+        $this->password('password', _m('Password'));
+        $this->elementEnd('li');
+        $this->elementEnd('ul');
+        // TRANS: Button text in form in which to connect an OpenID to an existing user on the site.
+        $this->submit('connect', _m('BUTTON', 'Connect'));
+        $this->elementEnd('fieldset');
+        $this->elementEnd('form');
+    }
+
+    /**
+     * Get specified e-mail from the form, or the OpenID sreg info, or the
+     * invite code.
+     *
+     * @return string
+     */
+    function getEmail()
+    {
+        $email = $this->trimmed('email');
+        if (!empty($email)) {
+            return $email;
+        }
+
+        // Pull from openid thingy
+        list($display, $canonical, $sreg) = $this->getSavedValues();
+        if (!empty($sreg['email'])) {
+            return $sreg['email'];
+        }
+
+        // Terrible hack for invites...
+        if (common_config('site', 'inviteonly')) {
+            $code = $_SESSION['invitecode'];
+            if ($code) {
+                $invite = Invitation::getKV($code);
+
+                if ($invite && $invite->address_type == 'email') {
+                    return $invite->address;
+                }
+            }
+        }
+        return '';
+    }
+
+    function tryLogin()
+    {
+        $consumer = oid_consumer();
+
+        $response = $consumer->complete(common_local_url('finishopenidlogin'));
+
+        if ($response->status == Auth_OpenID_CANCEL) {
+            // TRANS: Status message in case the response from the OpenID provider is that the logon attempt was cancelled.
+            $this->message(_m('OpenID authentication cancelled.'));
+            return;
+        } else if ($response->status == Auth_OpenID_FAILURE) {
+            // TRANS: OpenID authentication failed; display the error message. %s is the error message.
+            $this->message(sprintf(_m('OpenID authentication failed: %s.'), $response->message));
+        } else if ($response->status == Auth_OpenID_SUCCESS) {
+            // This means the authentication succeeded; extract the
+            // identity URL and Simple Registration data (if it was
+            // returned).
+            $display = $response->getDisplayIdentifier();
+            $canonical = ($response->endpoint->canonicalID) ?
+              $response->endpoint->canonicalID : $response->getDisplayIdentifier();
+
+            oid_assert_allowed($display);
+            oid_assert_allowed($canonical);
+
+            $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
+
+            if ($sreg_resp) {
+                $sreg = $sreg_resp->contents();
+            }
+
+            // Launchpad teams extension
+            if (!oid_check_teams($response)) {
+                // TRANS: Message displayed when OpenID authentication is aborted.
+                $this->message(_m('OpenID authentication aborted: You are not allowed to login to this site.'));
+                return;
+            }
+
+            $user = oid_get_user($canonical);
+
+            if ($user) {
+                oid_set_last($display);
+                // XXX: commented out at @edd's request until better
+                // control over how data flows from OpenID provider.
+                // oid_update_user($user, $sreg);
+                common_set_user($user);
+                common_real_login(true);
+                if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
+                    common_rememberme($user);
+                }
+                unset($_SESSION['openid_rememberme']);
+                $this->goHome($user->nickname);
+            } else {
+                $this->saveValues($display, $canonical, $sreg);
+                $this->showForm(null, $this->bestNewNickname($display, $sreg));
+            }
+        }
+    }
+
+    function message($msg)
+    {
+        $this->message_text = $msg;
+        $this->showPage();
+    }
+
+    function saveValues($display, $canonical, $sreg)
+    {
+        common_ensure_session();
+        $_SESSION['openid_display'] = $display;
+        $_SESSION['openid_canonical'] = $canonical;
+        $_SESSION['openid_sreg'] = $sreg;
+    }
+
+    function getSavedValues()
+    {
+        return array($_SESSION['openid_display'],
+                     $_SESSION['openid_canonical'],
+                     $_SESSION['openid_sreg']);
+    }
+
+    function createNewUser()
+    {
+        // FIXME: save invite code before redirect, and check here
+
+        if (!Event::handle('StartRegistrationTry', array($this))) {
+            return;
+        }
+
+        if (common_config('site', 'closed')) {
+            // TRANS: OpenID plugin message. No new user registration is allowed on the site.
+            $this->clientError(_m('Registration not allowed.'));
+            return;
+        }
+
+        $invite = null;
+
+        if (common_config('site', 'inviteonly')) {
+            $code = $_SESSION['invitecode'];
+            if (empty($code)) {
+                // TRANS: OpenID plugin message. No new user registration is allowed on the site without an invitation code, and none was provided.
+                $this->clientError(_m('Registration not allowed.'));
+                return;
+            }
+
+            $invite = Invitation::getKV($code);
+
+            if (empty($invite)) {
+                // TRANS: OpenID plugin message. No new user registration is allowed on the site without an invitation code, and the one provided was not valid.
+                $this->clientError(_m('Not a valid invitation code.'));
+                return;
+            }
+        }
+
+        try {
+            $nickname = Nickname::normalize($this->trimmed('newname'));
+        } catch (NicknameException $e) {
+            $this->showForm($e->getMessage());
+            return;
+        }
+
+        if (!User::allowed_nickname($nickname)) {
+            // TRANS: OpenID plugin message. The entered new user name is blacklisted.
+            $this->showForm(_m('Nickname not allowed.'));
+            return;
+        }
+
+        if (User::getKV('nickname', $nickname)) {
+            // TRANS: OpenID plugin message. The entered new user name is already used.
+            $this->showForm(_m('Nickname already in use. Try another one.'));
+            return;
+        }
+
+        list($display, $canonical, $sreg) = $this->getSavedValues();
+
+        if (!$display || !$canonical) {
+            // TRANS: OpenID plugin server error. A stored OpenID cannot be retrieved.
+            $this->serverError(_m('Stored OpenID not found.'));
+            return;
+        }
+
+        // Possible race condition... let's be paranoid
+
+        $other = oid_get_user($canonical);
+
+        if ($other) {
+            // TRANS: OpenID plugin server error.
+            $this->serverError(_m('Creating new account for OpenID that already has a user.'));
+            return;
+        }
+
+        Event::handle('StartOpenIDCreateNewUser', array($canonical, &$sreg));
+
+        $location = '';
+        if (!empty($sreg['country'])) {
+            if ($sreg['postcode']) {
+                // XXX: use postcode to get city and region
+                // XXX: also, store postcode somewhere -- it's valuable!
+                $location = $sreg['postcode'] . ', ' . $sreg['country'];
+            } else {
+                $location = $sreg['country'];
+            }
+        }
+
+        if (!empty($sreg['fullname']) && mb_strlen($sreg['fullname']) <= 255) {
+            $fullname = $sreg['fullname'];
+        } else {
+            $fullname = '';
+        }
+
+        $email = $this->getEmail();
+
+        // XXX: add language
+        // XXX: add timezone
+
+        $args = array('nickname' => $nickname,
+                      'email' => $email,
+                      'fullname' => $fullname,
+                      'location' => $location);
+
+        if (!empty($invite)) {
+            $args['code'] = $invite->code;
+        }
+
+        $user = User::register($args);
+
+        $result = oid_link_user($user->id, $canonical, $display);
+
+        Event::handle('EndOpenIDCreateNewUser', array($user, $canonical, $sreg));
+
+        oid_set_last($display);
+        common_set_user($user);
+        common_real_login(true);
+        if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
+            common_rememberme($user);
+        }
+        unset($_SESSION['openid_rememberme']);
+
+        Event::handle('EndRegistrationTry', array($this));
+
+        common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)),
+                        303);
+    }
+
+    function connectUser()
+    {
+        $nickname = $this->trimmed('nickname');
+        $password = $this->trimmed('password');
+
+        if (!common_check_user($nickname, $password)) {
+            // TRANS: OpenID plugin message.
+            $this->showForm(_m('Invalid username or password.'));
+            return;
+        }
+
+        // They're legit!
+
+        $user = User::getKV('nickname', $nickname);
+
+        list($display, $canonical, $sreg) = $this->getSavedValues();
+
+        if (!$display || !$canonical) {
+            // TRANS: OpenID plugin server error. A stored OpenID cannot be found.
+            $this->serverError(_m('Stored OpenID not found.'));
+            return;
+        }
+
+        $result = oid_link_user($user->id, $canonical, $display);
+
+        if (!$result) {
+            // TRANS: OpenID plugin server error. The user or user profile could not be saved.
+            $this->serverError(_m('Error connecting user to OpenID.'));
+            return;
+        }
+
+        if (Event::handle('StartOpenIDUpdateUser', array($user, $canonical, &$sreg))) {
+            oid_update_user($user, $sreg);
+        }
+        Event::handle('EndOpenIDUpdateUser', array($user, $canonical, $sreg));
+
+        oid_set_last($display);
+        common_set_user($user);
+        common_real_login(true);
+        if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
+            common_rememberme($user);
+        }
+        unset($_SESSION['openid_rememberme']);
+        $this->goHome($user->nickname);
+    }
+
+    function goHome($nickname)
+    {
+        $url = common_get_returnto();
+        if ($url) {
+            // We don't have to return to it again
+            common_set_returnto(null);
+           $url = common_inject_session($url);
+        } else {
+            $url = common_local_url('all',
+                                    array('nickname' =>
+                                          $nickname));
+        }
+        common_redirect($url, 303);
+    }
+
+    function bestNewNickname($display, $sreg)
+    {
+        // Try the passed-in nickname
+
+        if (!empty($sreg['nickname'])) {
+            $nickname = $this->nicknamize($sreg['nickname']);
+            if ($this->isNewNickname($nickname)) {
+                return $nickname;
+            }
+        }
+
+        // Try the full name
+
+        if (!empty($sreg['fullname'])) {
+            $fullname = $this->nicknamize($sreg['fullname']);
+            if ($this->isNewNickname($fullname)) {
+                return $fullname;
+            }
+        }
+
+        // Try the URL
+
+        $from_url = $this->openidToNickname($display);
+
+        if ($from_url && $this->isNewNickname($from_url)) {
+            return $from_url;
+        }
+
+        // XXX: others?
+
+        return null;
+    }
+
+    function isNewNickname($str)
+    {
+        if (!Nickname::isValid($str)) {
+            return false;
+        }
+        if (!User::allowed_nickname($str)) {
+            return false;
+        }
+        if (User::getKV('nickname', $str)) {
+            return false;
+        }
+        return true;
+    }
+
+    function openidToNickname($openid)
+    {
+        if (Auth_Yadis_identifierScheme($openid) == 'XRI') {
+            return $this->xriToNickname($openid);
+        } else {
+            return $this->urlToNickname($openid);
+        }
+    }
+
+    // We try to use an OpenID URL as a legal StatusNet user name in this order
+    // 1. Plain hostname, like http://evanp.myopenid.com/
+    // 2. One element in path, like http://profile.typekey.com/EvanProdromou/
+    //    or http://getopenid.com/evanprodromou
+    function urlToNickname($openid)
+    {
+        return common_url_to_nickname($openid);
+    }
+
+    function xriToNickname($xri)
+    {
+        $base = $this->xriBase($xri);
+
+        if (!$base) {
+            return null;
+        } else {
+            // =evan.prodromou
+            // or @gratis*evan.prodromou
+            $parts = explode('*', substr($base, 1));
+            return $this->nicknamize(array_pop($parts));
+        }
+    }
+
+    function xriBase($xri)
+    {
+        if (substr($xri, 0, 6) == 'xri://') {
+            return substr($xri, 6);
+        } else {
+            return $xri;
+        }
+    }
+
+    // Given a string, try to make it work as a nickname
+    function nicknamize($str)
+    {
+        return common_nicknamize($str);
+    }
+}
diff --git a/plugins/OpenID/actions/openidadminpanel.php b/plugins/OpenID/actions/openidadminpanel.php
new file mode 100644 (file)
index 0000000..c447a16
--- /dev/null
@@ -0,0 +1,288 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * OpenID bridge administration panel
+ *
+ * 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  Settings
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Administer global OpenID settings
+ *
+ * @category Admin
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class OpenidadminpanelAction extends AdminPanelAction
+{
+    /**
+     * Returns the page title
+     *
+     * @return string page title
+     */
+    function title()
+    {
+        // TRANS: Title for OpenID bridge administration page.
+        return _m('TITLE','OpenID Settings');
+    }
+
+    /**
+     * Instructions for using this form.
+     *
+     * @return string instructions
+     */
+    function getInstructions()
+    {
+        // TRANS: Page instructions.
+        return _m('OpenID settings');
+    }
+
+    /**
+     * Show the OpenID admin panel form
+     *
+     * @return void
+     */
+    function showForm()
+    {
+        $form = new OpenIDAdminPanelForm($this);
+        $form->show();
+        return;
+    }
+
+    /**
+     * Save settings from the form
+     *
+     * @return void
+     */
+    function saveSettings()
+    {
+        static $settings = array(
+            'openid' => array('trusted_provider', 'required_team')
+        );
+
+        static $booleans = array(
+            'openid' => array('append_username'),
+            'site' => array('openidonly')
+        );
+
+        $values = array();
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                $values[$section][$setting]
+                    = $this->trimmed($setting);
+            }
+        }
+
+        foreach ($booleans as $section => $parts) {
+            foreach ($parts as $setting) {
+                $values[$section][$setting]
+                    = ($this->boolean($setting)) ? 1 : 0;
+            }
+        }
+
+        // This throws an exception on validation errors
+
+        $this->validate($values);
+
+        // assert(all values are valid);
+
+        $config = new Config();
+
+        $config->query('BEGIN');
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                Config::save($section, $setting, $values[$section][$setting]);
+            }
+        }
+
+        foreach ($booleans as $section => $parts) {
+            foreach ($parts as $setting) {
+                Config::save($section, $setting, $values[$section][$setting]);
+            }
+        }
+
+        $config->query('COMMIT');
+
+        return;
+    }
+
+    function validate(&$values)
+    {
+        // Validate consumer key and secret (can't be too long)
+
+        if (mb_strlen($values['openid']['trusted_provider']) > 255) {
+            $this->clientError(
+                // TRANS: Client error displayed when OpenID provider URL is too long.
+                _m('Invalid provider URL. Maximum length is 255 characters.')
+            );
+        }
+
+        if (mb_strlen($values['openid']['required_team']) > 255) {
+            $this->clientError(
+                // TRANS: Client error displayed when Launchpad team name is too long.
+                _m('Invalid team name. Maximum length is 255 characters.')
+            );
+        }
+    }
+}
+
+class OpenIDAdminPanelForm extends AdminForm
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'openidadminpanel';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('openidadminpanel');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     *
+     * @todo Some of the options could prevent users from logging in again.
+     *       Make sure that the acting administrator has a valid OpenID matching,
+     *       or more carefully warn folks.
+     */
+    function formData()
+    {
+        $this->out->elementStart(
+            'fieldset',
+            array('id' => 'settings_openid')
+        );
+        // TRANS: Fieldset legend.
+        $this->out->element('legend', null, _m('LEGEND','Trusted provider'));
+        $this->out->element('p', 'form_guide',
+            // TRANS: Form guide.
+            _m('By default, users are allowed to authenticate with any OpenID provider. ' .
+               'If you are using your own OpenID service for shared sign-in, ' .
+               'you can restrict access to only your own users here.'));
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->input(
+            'trusted_provider',
+            // TRANS: Field label.
+            _m('Provider URL'),
+            // TRANS: Field title.
+            _m('All OpenID logins will be sent to this URL; other providers may not be used.'),
+            'openid'
+        );
+        $this->unli();
+
+        $this->li();
+        $this->out->checkbox(
+            // TRANS: Checkbox label.
+            'append_username', _m('Append a username to base URL'),
+            (bool) $this->value('append_username', 'openid'),
+            // TRANS: Checkbox title.
+            _m('Login form will show the base URL and prompt for a username to add at the end. Use when OpenID provider URL should be the profile page for individual users.'),
+            'true'
+        );
+        $this->unli();
+
+        $this->li();
+        $this->input(
+            'required_team',
+            // TRANS: Field label.
+            _m('Required team'),
+            // TRANS: Field title.
+            _m('Only allow logins from users in the given team (Launchpad extension).'),
+            'openid'
+        );
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+        $this->out->elementEnd('fieldset');
+
+        $this->out->elementStart(
+            'fieldset',
+            array('id' => 'settings_openid-options')
+        );
+        // TRANS: Fieldset legend.
+        $this->out->element('legend', null, _m('LEGEND','Options'));
+
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+
+        $this->out->checkbox(
+            // TRANS: Checkbox label.
+            'openidonly', _m('Enable OpenID-only mode'),
+            (bool) $this->value('openidonly', 'site'),
+            // TRANS: Checkbox title.
+            _m('Require all users to login via OpenID. Warning: disables password authentication for all users!'),
+            'true'
+        );
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text to save OpenID settings.
+        $this->out->submit('submit', _m('BUTTON','Save'), 'submit', null,
+                           // TRANS: Button title to save OpenID settings.
+                           _m('Save OpenID settings.'));
+    }
+}
diff --git a/plugins/OpenID/actions/openidlogin.php b/plugins/OpenID/actions/openidlogin.php
new file mode 100644 (file)
index 0000000..86ebcae
--- /dev/null
@@ -0,0 +1,188 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
+
+class OpenidloginAction extends Action
+{
+    function handle($args)
+    {
+        parent::handle($args);
+        if (common_is_real_login()) {
+            // TRANS: Client error message trying to log on with OpenID while already logged on.
+            $this->clientError(_m('Already logged in.'));
+        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $provider = common_config('openid', 'trusted_provider');
+            if ($provider) {
+                $openid_url = $provider;
+                if (common_config('openid', 'append_username')) {
+                    $openid_url .= $this->trimmed('openid_username');
+                }
+            } else {
+                $openid_url = $this->trimmed('openid_url');
+            }
+
+            oid_assert_allowed($openid_url);
+
+            $rememberme = $this->boolean('rememberme');
+
+            common_ensure_session();
+
+            $_SESSION['openid_rememberme'] = $rememberme;
+
+            $result = oid_authenticate($openid_url,
+                                       'finishopenidlogin');
+
+            if (is_string($result)) { # error message
+                unset($_SESSION['openid_rememberme']);
+                $this->showForm($result, $openid_url);
+            }
+        } else {
+            $openid_url = oid_get_last();
+            $this->showForm(null, $openid_url);
+        }
+    }
+
+    function getInstructions()
+    {
+        if (common_logged_in() && !common_is_real_login() &&
+            common_get_returnto()) {
+            // rememberme logins have to reauthenticate before
+            // changing any profile settings (cookie-stealing protection)
+            // TRANS: OpenID plugin message. Rememberme logins have to reauthenticate before changing any profile settings.
+            // TRANS: "OpenID" is the display text for a link with URL "(%%doc.openid%%)".
+            return _m('For security reasons, please re-login with your ' .
+                     '[OpenID](%%doc.openid%%) ' .
+                     'before changing your settings.');
+        } else {
+            // TRANS: OpenID plugin message.
+            // TRANS: "OpenID" is the display text for a link with URL "(%%doc.openid%%)".
+            return _m('Login with an [OpenID](%%doc.openid%%) account.');
+        }
+    }
+
+    function showPageNotice()
+    {
+        if ($this->error) {
+            $this->element('div', array('class' => 'error'), $this->error);
+        } else {
+            $instr = $this->getInstructions();
+            $output = common_markup_to_html($instr);
+            $this->elementStart('div', 'instructions');
+            $this->raw($output);
+            $this->elementEnd('div');
+        }
+    }
+
+    function showScripts()
+    {
+        parent::showScripts();
+        if (common_config('openid', 'trusted_provider')) {
+            if (common_config('openid', 'append_username')) {
+                $this->autofocus('openid_username');
+            } else {
+                $this->autofocus('rememberme');
+            }
+        } else {
+            $this->autofocus('openid_url');
+        }
+    }
+
+    function title()
+    {
+        // TRANS: OpenID plugin message. Title.
+        return _m('TITLE','OpenID Login');
+    }
+
+    function showForm($error=null, $openid_url)
+    {
+        $this->error = $error;
+        $this->openid_url = $openid_url;
+        $this->showPage();
+    }
+
+    function showContent() {
+        $formaction = common_local_url('openidlogin');
+        $this->elementStart('form', array('method' => 'post',
+                                           'id' => 'form_openid_login',
+                                           'class' => 'form_settings',
+                                           'action' => $formaction));
+        $this->elementStart('fieldset');
+        // TRANS: OpenID plugin logon form legend.
+        $this->element('legend', null, _m('LEGEND','OpenID login'));
+
+        $this->elementStart('ul', 'form_data');
+        $this->elementStart('li');
+        $provider = common_config('openid', 'trusted_provider');
+        $appendUsername = common_config('openid', 'append_username');
+        if ($provider) {
+            // TRANS: Field label.
+            $this->element('label', array(), _m('LABEL','OpenID provider'));
+            $this->element('span', array(), $provider);
+            if ($appendUsername) {
+                $this->element('input', array('id' => 'openid_username',
+                                              'name' => 'openid_username',
+                                              'style' => 'float: none'));
+            }
+            $this->element('p', 'form_guide',
+                           // TRANS: Form guide.
+                           ($appendUsername ? _m('Enter your username.') . ' ' : '') .
+                           // TRANS: Form guide.
+                           _m('You will be sent to the provider\'s site for authentication.'));
+            $this->hidden('openid_url', $provider);
+        } else {
+            // TRANS: OpenID plugin logon form field label.
+            $this->input('openid_url', _m('OpenID URL'),
+                         $this->openid_url,
+                        // TRANS: OpenID plugin logon form field title.
+                         _m('Your OpenID URL.'));
+        }
+        $this->elementEnd('li');
+        $this->elementStart('li', array('id' => 'settings_rememberme'));
+        // TRANS: OpenID plugin logon form checkbox label for setting to put the OpenID information in a cookie.
+        $this->checkbox('rememberme', _m('Remember me'), false,
+                        // TRANS: OpenID plugin logon form field title.
+                        _m('Automatically login in the future; ' .
+                           'not for shared computers!'));
+        $this->elementEnd('li');
+        $this->elementEnd('ul');
+        // TRANS: OpenID plugin logon form button label to start logon with the data provided in the logon form.
+        $this->submit('submit', _m('BUTTON', 'Login'));
+        $this->elementEnd('fieldset');
+        $this->elementEnd('form');
+    }
+
+    function showLocalNav()
+    {
+        $nav = new LoginGroupNav($this);
+        $nav->show();
+    }
+
+    function showNoticeForm()
+    {
+    }
+
+    function showProfileBlock()
+    {
+    }
+}
diff --git a/plugins/OpenID/actions/openidserver.php b/plugins/OpenID/actions/openidserver.php
new file mode 100644 (file)
index 0000000..847eb48
--- /dev/null
@@ -0,0 +1,163 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Settings for OpenID
+ *
+ * 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  Settings
+ * @package   StatusNet
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
+
+/**
+ * Settings for OpenID
+ *
+ * Lets users add, edit and delete OpenIDs from their account
+ *
+ * @category Settings
+ * @package  StatusNet
+ * @author   Craig Andrews <candrews@integralblue.com>
+ * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class OpenidserverAction extends Action
+{
+    var $oserver;
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+        $this->oserver = oid_server();
+        return true;
+    }
+
+    function handle($args)
+    {
+        parent::handle($args);
+        $request = $this->oserver->decodeRequest();
+        if (in_array($request->mode, array('checkid_immediate',
+            'checkid_setup'))) {
+            $user = common_current_user();
+            if(!$user){
+                if($request->immediate){
+                    //cannot prompt the user to login in immediate mode, so answer false
+                    $response = $this->generateDenyResponse($request);
+                }else{
+                    // Go log in, and then come back.
+                    //
+                    // Note: 303 redirect rather than 307 to avoid
+                    // prompting user for form resubmission if we
+                    // were POSTed here.
+                    common_set_returnto($_SERVER['REQUEST_URI']);
+                    common_redirect(common_local_url('login'), 303);
+                    return;
+                }
+            }else if(common_profile_url($user->nickname) == $request->identity || $request->idSelect()){
+                $user_openid_trustroot = User_openid_trustroot::pkeyGet(
+                                                array('user_id'=>$user->id, 'trustroot'=>$request->trust_root));
+                if(empty($user_openid_trustroot)){
+                    if($request->immediate){
+                        //cannot prompt the user to trust this trust root in immediate mode, so answer false
+                        $response = $this->generateDenyResponse($request);
+                    }else{
+                        common_ensure_session();
+                        $_SESSION['openid_trust_root'] = $request->trust_root;
+                        $allowResponse = $this->generateAllowResponse($request, $user);
+                        $this->oserver->encodeResponse($allowResponse); //sign the response
+                        $denyResponse = $this->generateDenyResponse($request);
+                        $this->oserver->encodeResponse($denyResponse); //sign the response
+                        $_SESSION['openid_allow_url'] = $allowResponse->encodeToUrl();
+                        $_SESSION['openid_deny_url'] = $denyResponse->encodeToUrl();
+
+                        // Ask the user to trust this trust root...
+                        //
+                        // Note: 303 redirect rather than 307 to avoid
+                        // prompting user for form resubmission if we
+                        // were POSTed here.
+                        common_redirect(common_local_url('openidtrust'), 303);
+                        return;
+                    }
+                }else{
+                    //user has previously authorized this trust root
+                    $response = $this->generateAllowResponse($request, $user);
+                    //$response = $request->answer(true, null, common_profile_url($user->nickname));
+                }
+            } else if ($request->immediate) {
+                $response = $this->generateDenyResponse($request);
+            } else {
+                //invalid
+                // TRANS: OpenID plugin client error given trying to add an unauthorised OpenID to a user (403).
+                // TRANS: %s is a request identity.
+                $this->clientError(sprintf(_m('You are not authorized to use the identity %s.'),$request->identity),$code=403);
+            }
+        } else {
+            $response = $this->oserver->handleRequest($request);
+        }
+
+        if($response){
+            $response = $this->oserver->encodeResponse($response);
+            if ($response->code != AUTH_OPENID_HTTP_OK) {
+                header(sprintf("HTTP/1.1 %d ", $response->code),
+                       true, $response->code);
+            }
+
+            if($response->headers){
+                foreach ($response->headers as $k => $v) {
+                    header("$k: $v");
+                }
+            }
+            $this->raw($response->body);
+        }else{
+            // TRANS: OpenID plugin client error given when not getting a response for a given OpenID provider (500).
+            $this->clientError(_m('Just an OpenID provider. Nothing to see here, move along...'),$code=500);
+        }
+    }
+
+    function generateAllowResponse($request, $user){
+        $response = $request->answer(true, null, common_profile_url($user->nickname));
+
+        $profile = $user->getProfile();
+        $sreg_data = array(
+            'fullname' => $profile->fullname,
+            'nickname' => $user->nickname,
+            'email' => $user->email,
+            'language' => $user->language,
+            'timezone' => $user->timezone);
+        $sreg_request = Auth_OpenID_SRegRequest::fromOpenIDRequest($request);
+        $sreg_response = Auth_OpenID_SRegResponse::extractResponse(
+                              $sreg_request, $sreg_data);
+        $sreg_response->toMessage($response->fields);
+        return $response;
+    }
+
+    function generateDenyResponse($request){
+        $response = $request->answer(false);
+        return $response;
+    }
+}
diff --git a/plugins/OpenID/actions/openidsettings.php b/plugins/OpenID/actions/openidsettings.php
new file mode 100644 (file)
index 0000000..7bef69b
--- /dev/null
@@ -0,0 +1,386 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Settings for OpenID
+ *
+ * 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  Settings
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
+
+/**
+ * Settings for OpenID
+ *
+ * Lets users add, edit and delete OpenIDs from their account
+ *
+ * @category Settings
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class OpenidsettingsAction extends SettingsAction
+{
+    /**
+     * Title of the page
+     *
+     * @return string Page title
+     */
+    function title()
+    {
+        // TRANS: Title of OpenID settings page for a user.
+        return _m('TITLE','OpenID settings');
+    }
+
+    /**
+     * Instructions for use
+     *
+     * @return string Instructions for use
+     */
+    function getInstructions()
+    {
+        // TRANS: Form instructions for OpenID settings.
+        // TRANS: This message contains Markdown links in the form [description](link).
+        return _m('[OpenID](%%doc.openid%%) lets you log into many sites ' .
+                 'with the same user account. '.
+                 'Manage your associated OpenIDs from here.');
+    }
+
+    function showScripts()
+    {
+        parent::showScripts();
+        $this->autofocus('openid_url');
+    }
+
+    /**
+     * Show the form for OpenID management
+     *
+     * We have one form with a few different submit buttons to do different things.
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        $user = common_current_user();
+
+        if (!common_config('openid', 'trusted_provider')) {
+            $this->elementStart('form', array('method' => 'post',
+                                              'id' => 'form_settings_openid_add',
+                                              'class' => 'form_settings',
+                                              'action' =>
+                                              common_local_url('openidsettings')));
+            $this->elementStart('fieldset', array('id' => 'settings_openid_add'));
+    
+            // TRANS: Fieldset legend.
+            $this->element('legend', null, _m('LEGEND','Add OpenID'));
+            $this->hidden('token', common_session_token());
+            $this->element('p', 'form_guide',
+                           // TRANS: Form guide.
+                           _m('If you want to add an OpenID to your account, ' .
+                             'enter it in the box below and click "Add".'));
+            $this->elementStart('ul', 'form_data');
+            $this->elementStart('li');
+            $this->element('label', array('for' => 'openid_url'),
+                           // TRANS: Field label.
+                           _m('OpenID URL'));
+            $this->element('input', array('name' => 'openid_url',
+                                          'type' => 'text',
+                                          'id' => 'openid_url'));
+            $this->elementEnd('li');
+            $this->elementEnd('ul');
+            $this->element('input', array('type' => 'submit',
+                                          'id' => 'settings_openid_add_action-submit',
+                                          'name' => 'add',
+                                          'class' => 'submit',
+                                          // TRANS: Button text for adding an OpenID URL.
+                                          'value' => _m('BUTTON','Add')));
+            $this->elementEnd('fieldset');
+            $this->elementEnd('form');
+        }
+        $oid = new User_openid();
+
+        $oid->user_id = $user->id;
+
+        $cnt = $oid->find();
+
+        if ($cnt > 0) {
+            // TRANS: Header on OpenID settings page.
+            $this->element('h2', null, _m('HEADER','Remove OpenID'));
+
+            if ($cnt == 1 && !$user->password) {
+
+                $this->element('p', 'form_guide',
+                               // TRANS: Form guide.
+                               _m('Removing your only OpenID '.
+                                 'would make it impossible to log in! ' .
+                                 'If you need to remove it, '.
+                                 'add another OpenID first.'));
+
+                if ($oid->fetch()) {
+                    $this->elementStart('p');
+                    $this->element('a', array('href' => $oid->canonical),
+                                   $oid->display);
+                    $this->elementEnd('p');
+                }
+
+            } else {
+
+                $this->element('p', 'form_guide',
+                               // TRANS: Form guide.
+                               _m('You can remove an OpenID from your account '.
+                                 'by clicking the button marked "Remove".'));
+                $idx = 0;
+
+                while ($oid->fetch()) {
+                    $this->elementStart('form',
+                                        array('method' => 'POST',
+                                              'id' => 'form_settings_openid_delete' . $idx,
+                                              'class' => 'form_settings',
+                                              'action' =>
+                                              common_local_url('openidsettings')));
+                    $this->elementStart('fieldset');
+                    $this->hidden('token', common_session_token());
+                    $this->element('a', array('href' => $oid->canonical),
+                                   $oid->display);
+                    $this->element('input', array('type' => 'hidden',
+                                                  'id' => 'openid_url'.$idx,
+                                                  'name' => 'openid_url',
+                                                  'value' => $oid->canonical));
+                    $this->element('input', array('type' => 'submit',
+                                                  'id' => 'remove'.$idx,
+                                                  'name' => 'remove',
+                                                  'class' => 'submit remove',
+                                                  // TRANS: Button text to remove an OpenID.
+                                                  'value' => _m('BUTTON','Remove')));
+                    $this->elementEnd('fieldset');
+                    $this->elementEnd('form');
+                    $idx++;
+                }
+            }
+        }
+
+        $this->elementStart('form', array('method' => 'post',
+                                          'id' => 'form_settings_openid_trustroots',
+                                          'class' => 'form_settings',
+                                          'action' =>
+                                          common_local_url('openidsettings')));
+        $this->elementStart('fieldset', array('id' => 'settings_openid_trustroots'));
+        // TRANS: Fieldset legend.
+        $this->element('legend', null, _m('OpenID Trusted Sites'));
+        $this->hidden('token', common_session_token());
+        $this->element('p', 'form_guide',
+                       // TRANS: Form guide.
+                       _m('The following sites are allowed to access your ' .
+                       'identity and log you in. You can remove a site from ' .
+                       'this list to deny it access to your OpenID.'));
+        $this->elementStart('ul', 'form_data');
+        $user_openid_trustroot = new User_openid_trustroot();
+        $user_openid_trustroot->user_id=$user->id;
+        if($user_openid_trustroot->find()) {
+            while($user_openid_trustroot->fetch()) {
+                $this->elementStart('li');
+                $this->element('input', array('name' => 'openid_trustroot[]',
+                                              'type' => 'checkbox',
+                                              'class' => 'checkbox',
+                                              'value' => $user_openid_trustroot->trustroot,
+                                              'id' => 'openid_trustroot_' . crc32($user_openid_trustroot->trustroot)));
+                $this->element('label', array('class'=>'checkbox', 'for' => 'openid_trustroot_' . crc32($user_openid_trustroot->trustroot)),
+                               $user_openid_trustroot->trustroot);
+                $this->elementEnd('li');
+            }
+        }
+        $this->elementEnd('ul');
+        $this->element('input', array('type' => 'submit',
+                                      'id' => 'settings_openid_trustroots_action-submit',
+                                      'name' => 'remove_trustroots',
+                                      'class' => 'submit',
+                                      // TRANS: Button text to remove an OpenID trustroot.
+                                      'value' => _m('BUTTON','Remove')));
+        $this->elementEnd('fieldset');
+        
+        $prefs = User_openid_prefs::getKV('user_id', $user->id);
+
+        $this->elementStart('fieldset');
+        $this->element('legend', null, _m('LEGEND','Preferences'));
+        $this->elementStart('ul', 'form_data');
+        $this->checkBox('hide_profile_link', "Hide OpenID links from my profile", !empty($prefs) && $prefs->hide_profile_link);
+        $this->element('input', array('type' => 'submit',
+                                      'id' => 'settings_openid_prefs_save',
+                                      'name' => 'save_prefs',
+                                      'class' => 'submit',
+                                      // TRANS: Button text to save OpenID prefs
+                                      'value' => _m('BUTTON','Save')));
+        $this->elementEnd('ul');
+        $this->elementEnd('fieldset');
+
+        $this->elementEnd('form');
+    }
+
+    /**
+     * Handle a POST request
+     *
+     * Muxes to different sub-functions based on which button was pushed
+     *
+     * @return void
+     */
+    function handlePost()
+    {
+        // CSRF protection
+        $token = $this->trimmed('token');
+        if (!$token || $token != common_session_token()) {
+            // TRANS: Client error displayed when the session token does not match or is not given.
+            $this->showForm(_m('There was a problem with your session token. '.
+                              'Try again, please.'));
+            return;
+        }
+
+        if ($this->arg('add')) {
+            if (common_config('openid', 'trusted_provider')) {
+                // TRANS: Form validation error if no OpenID providers can be added.
+                $this->showForm(_m('Cannot add new providers.'));
+            } else {
+                $result = oid_authenticate($this->trimmed('openid_url'),
+                                           'finishaddopenid');
+                if (is_string($result)) { // error message
+                    $this->showForm($result);
+                }
+            }
+        } else if ($this->arg('remove')) {
+            $this->removeOpenid();
+        } else if($this->arg('remove_trustroots')) {
+            $this->removeTrustroots();
+        } else if($this->arg('save_prefs')) {
+            $this->savePrefs();
+        } else {
+            // TRANS: Unexpected form validation error.
+            $this->showForm(_m('Something weird happened.'));
+        }
+    }
+
+    /**
+     * Handles a request to remove OpenID trustroots from the user's account
+     *
+     * Validates input and, if everything is OK, deletes the trustroots.
+     * Reloads the form with a success or error notification.
+     *
+     * @return void
+     */
+    function removeTrustroots()
+    {
+        $user = common_current_user();
+        $trustroots = $this->arg('openid_trustroot');
+        if($trustroots) {
+            foreach($trustroots as $trustroot) {
+                $user_openid_trustroot = User_openid_trustroot::pkeyGet(
+                                                array('user_id'=>$user->id, 'trustroot'=>$trustroot));
+                if($user_openid_trustroot) {
+                    $user_openid_trustroot->delete();
+                } else {
+                    // TRANS: Form validation error when trying to remove a non-existing trustroot.
+                    $this->showForm(_m('No such OpenID trustroot.'));
+                    return;
+                }
+            }
+            // TRANS: Success message after removing trustroots.
+            $this->showForm(_m('Trustroots removed.'), true);
+        } else {
+            $this->showForm();
+        }
+        return;
+    }
+
+    /**
+     * Handles a request to remove an OpenID from the user's account
+     *
+     * Validates input and, if everything is OK, deletes the OpenID.
+     * Reloads the form with a success or error notification.
+     *
+     * @return void
+     */
+    function removeOpenid()
+    {
+        $openid_url = $this->trimmed('openid_url');
+
+        $oid = User_openid::getKV('canonical', $openid_url);
+
+        if (!$oid) {
+            // TRANS: Form validation error for a non-existing OpenID.
+            $this->showForm(_m('No such OpenID.'));
+            return;
+        }
+        $cur = common_current_user();
+        if (!$cur || $oid->user_id != $cur->id) {
+            // TRANS: Form validation error if OpenID is connected to another user.
+            $this->showForm(_m('That OpenID does not belong to you.'));
+            return;
+        }
+        $oid->delete();
+        // TRANS: Success message after removing an OpenID.
+        $this->showForm(_m('OpenID removed.'), true);
+        return;
+    }
+
+    /**
+     * Handles a request to save preferences
+     *
+     * Validates input and, if everything is OK, deletes the OpenID.
+     * Reloads the form with a success or error notification.
+     *
+     * @return void
+     */
+    function savePrefs()
+    {
+        $cur = common_current_user();
+
+        if (empty($cur)) {
+            throw new ClientException(_("Not logged in."));
+        }
+
+        $orig  = null;
+        $prefs = User_openid_prefs::getKV('user_id', $cur->id);
+
+        if (empty($prefs)) {
+            $prefs          = new User_openid_prefs();
+            $prefs->user_id = $cur->id;
+            $prefs->created = common_sql_now();
+        } else {
+            $orig = clone($prefs);
+        }
+
+        $prefs->hide_profile_link = $this->boolean('hide_profile_link');
+
+        if (empty($orig)) {
+            $prefs->insert();
+        } else {
+            $prefs->update($orig);
+        }
+
+        $this->showForm(_m('OpenID preferences saved.'), true);
+        return;
+    }
+}
diff --git a/plugins/OpenID/actions/openidtrust.php b/plugins/OpenID/actions/openidtrust.php
new file mode 100644 (file)
index 0000000..d39d854
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/plugins/OpenID/openid.php';
+
+class OpenidtrustAction extends Action
+{
+    var $trust_root;
+    var $allowUrl;
+    var $denyUrl;
+    var $user;
+
+    /**
+     * Is this a read-only action?
+     *
+     * @return boolean false
+     */
+    function isReadOnly($args)
+    {
+        return false;
+    }
+
+    /**
+     * Title of the page
+     *
+     * @return string title of the page
+     */
+    function title()
+    {
+        // TRANS: Title for identity verification page.
+        return _m('OpenID Identity Verification');
+    }
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+        common_ensure_session();
+        $this->user = common_current_user();
+        if(empty($this->user)){
+            /* Go log in, and then come back. */
+            common_set_returnto($_SERVER['REQUEST_URI']);
+            common_redirect(common_local_url('login'));
+            return;
+        }
+        $this->trust_root = $_SESSION['openid_trust_root'];
+        $this->allowUrl = $_SESSION['openid_allow_url'];
+        $this->denyUrl = $_SESSION['openid_deny_url'];
+        if(empty($this->trust_root) || empty($this->allowUrl) || empty($this->denyUrl)){
+            // TRANS: Client error when visiting page directly.
+            $this->clientError(_m('This page should only be reached during OpenID processing, not directly.'));
+            return;
+        }
+        return true;
+    }
+
+    function handle($args)
+    {
+        parent::handle($args);
+        if($_SERVER['REQUEST_METHOD'] == 'POST'){
+            $this->handleSubmit();
+        }else{
+            $this->showPage();
+        }
+    }
+
+    function handleSubmit()
+    {
+        unset($_SESSION['openid_trust_root']);
+        unset($_SESSION['openid_allow_url']);
+        unset($_SESSION['openid_deny_url']);
+        if($this->arg('allow'))
+        {
+            //save to database
+            $user_openid_trustroot = new User_openid_trustroot();
+            $user_openid_trustroot->user_id = $this->user->id;
+            $user_openid_trustroot->trustroot = $this->trust_root;
+            $user_openid_trustroot->created = DB_DataObject_Cast::dateTime();
+            if (!$user_openid_trustroot->insert()) {
+                $err = PEAR::getStaticProperty('DB_DataObject','lastError');
+            }
+            common_redirect($this->allowUrl, $code=302);
+        }else{
+            common_redirect($this->denyUrl, $code=302);
+        }
+    }
+
+    /**
+     * Show page notice
+     *
+     * Display a notice for how to use the page, or the
+     * error if it exists.
+     *
+     * @return void
+     */
+    function showPageNotice()
+    {
+        // TRANS: Page notice. %s is a trustroot name.
+        $this->element('p',null,sprintf(_m('%s has asked to verify your identity. Click Continue to verify your identity and login without creating a new password.'),$this->trust_root));
+    }
+
+    /**
+     * Core of the display code
+     *
+     * Shows the login form.
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        $this->elementStart('form', array('method' => 'post',
+                                   'id' => 'form_openidtrust',
+                                   'class' => 'form_settings',
+                                   'action' => common_local_url('openidtrust')));
+        $this->elementStart('fieldset');
+        // TRANS: Button text to continue OpenID identity verification.
+        $this->submit('allow', _m('BUTTON','Continue'));
+        // TRANS: Button text to cancel OpenID identity verification.
+        $this->submit('deny', _m('BUTTON','Cancel'));
+
+        $this->elementEnd('fieldset');
+        $this->elementEnd('form');
+    }
+}
diff --git a/plugins/OpenID/classes/User_openid.php b/plugins/OpenID/classes/User_openid.php
new file mode 100644 (file)
index 0000000..baff5cd
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Table Definition for user_openid
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+class User_openid extends Managed_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'user_openid';                     // table name
+    public $canonical;                       // varchar(255)  primary_key not_null
+    public $display;                         // varchar(255)  unique_key not_null
+    public $user_id;                         // int(4)   not_null
+    public $created;                         // datetime()   not_null
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'canonical' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'OpenID canonical string'),
+                'display' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'OpenID display string'),
+                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'User ID for OpenID owner'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('canonical'),
+            'unique keys' => array(
+                'user_openid_display_key' => array('display'),
+            ),
+            'indexes' => array(
+                'user_openid_user_id_idx' => array('user_id'),
+            ),
+            'foreign keys' => array(
+                'user_openid_user_id_fkey' => array('user', array('user_id' => 'id')),
+            ),
+        );
+    }
+
+    static function hasOpenID($user_id)
+    {
+        $oid = new User_openid();
+
+        $oid->user_id = $user_id;
+
+        $cnt = $oid->find();
+
+        return ($cnt > 0);
+    }
+}
diff --git a/plugins/OpenID/classes/User_openid_prefs.php b/plugins/OpenID/classes/User_openid_prefs.php
new file mode 100644 (file)
index 0000000..fcd88f4
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2012, StatusNet, Inc.
+ *
+ * User_openid_prefs.php
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  OpenID
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Store preferences for OpenID use in StatusNet
+ *
+ * @category OpenID
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+
+class User_openid_prefs extends Managed_DataObject
+{
+    public $__table = 'user_openid_prefs'; // table name
+
+    public $user_id;            // The User with the prefs
+    public $hide_profile_link;  // Hide the link on the profile block?
+    public $created;            // datetime
+    public $modified;           // datetime
+
+    /**
+     * The One True Thingy that must be defined and declared.
+     */
+
+    public static function schemaDef()
+    {
+        return array(
+                     'description' => 'Per-user preferences for OpenID display',
+                     'fields' => array('user_id' => array('type' => 'integer',
+                                                          'not null' => true,
+                                                          'description' => 'User whose prefs we are saving'),
+                                       'hide_profile_link' => array('type' => 'int',
+                                                                    'not null' => true,
+                                                                    'default' => 0,
+                                                                    'description' => 'Whether to hide profile links from profile block'),
+                                       'created' => array('type' => 'datetime',
+                                                          'not null' => true,
+                                                          'description' => 'date this record was created'),
+                                       'modified' => array('type' => 'datetime',
+                                                           'not null' => true,
+                                                           'description' => 'date this record was modified'),
+                                       ),
+                     'primary key' => array('user_id'),
+                     'foreign keys' => array('user_openid_prefs_user_id_fkey' => array('user', array('user_id' => 'id')),
+                                             ),
+                     'indexes' => array(),
+                     );
+    }
+}
diff --git a/plugins/OpenID/classes/User_openid_trustroot.php b/plugins/OpenID/classes/User_openid_trustroot.php
new file mode 100644 (file)
index 0000000..ec09e17
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Table Definition for user_openid_trustroot
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+class User_openid_trustroot extends Managed_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'user_openid_trustroot';                     // table name
+    public $trustroot;                         // varchar(255) primary_key not_null
+    public $user_id;                         // int(4)  primary_key not_null
+    public $created;                         // datetime()   not_null
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'trustroot' => array('type' => 'varchar', 'not null' => true, 'length' => 255, 'description' => 'OpenID trustroot string'),
+                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'User ID for OpenID trustroot owner'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('trustroot', 'user_id'),
+        );
+    }
+}
diff --git a/plugins/OpenID/finishaddopenid.php b/plugins/OpenID/finishaddopenid.php
deleted file mode 100644 (file)
index 5182e50..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Complete adding an OpenID
- *
- * 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  Settings
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/plugins/OpenID/openid.php';
-
-/**
- * Complete adding an OpenID
- *
- * Handle the return from an OpenID verification
- *
- * @category Settings
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class FinishaddopenidAction extends Action
-{
-    var $msg = null;
-
-    /**
-     * Handle the redirect back from OpenID confirmation
-     *
-     * Check to see if the user's logged in, and then try
-     * to use the OpenID login system.
-     *
-     * @param array $args $_REQUEST arguments
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-        if (!common_logged_in()) {
-            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
-            $this->clientError(_m('Not logged in.'));
-        } else {
-            $this->tryLogin();
-        }
-    }
-
-    /**
-     * Try to log in using OpenID
-     *
-     * Check the OpenID for validity; potentially store it.
-     *
-     * @return void
-     */
-    function tryLogin()
-    {
-        $consumer = oid_consumer();
-
-        $response = $consumer->complete(common_local_url('finishaddopenid'));
-
-        if ($response->status == Auth_OpenID_CANCEL) {
-            // TRANS: Status message in case the response from the OpenID provider is that the logon attempt was cancelled.
-            $this->message(_m('OpenID authentication cancelled.'));
-            return;
-        } else if ($response->status == Auth_OpenID_FAILURE) {
-            // TRANS: OpenID authentication failed; display the error message.
-            // TRANS: %s is the error message.
-            $this->message(sprintf(_m('OpenID authentication failed: %s.'),
-                                   $response->message));
-        } else if ($response->status == Auth_OpenID_SUCCESS) {
-
-            $display   = $response->getDisplayIdentifier();
-            $canonical = ($response->endpoint && $response->endpoint->canonicalID) ?
-              $response->endpoint->canonicalID : $display;
-
-            $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
-
-            if ($sreg_resp) {
-                $sreg = $sreg_resp->contents();
-            }
-
-            // Launchpad teams extension
-            if (!oid_check_teams($response)) {
-                // TRANS: OpenID authentication error.
-                $this->message(_m('OpenID authentication aborted: You are not allowed to login to this site.'));
-                return;
-            }
-
-            $cur = common_current_user();
-
-            $other = oid_get_user($canonical);
-
-            if ($other) {
-                if ($other->id == $cur->id) {
-                    // TRANS: Message in case a user tries to add an OpenID that is already connected to them.
-                    $this->message(_m('You already have this OpenID!'));
-                } else {
-                    // TRANS: Message in case a user tries to add an OpenID that is already used by another user.
-                    $this->message(_m('Someone else already has this OpenID.'));
-                }
-                return;
-            }
-
-            // start a transaction
-
-            $cur->query('BEGIN');
-
-            $result = oid_link_user($cur->id, $canonical, $display);
-
-            if (!$result) {
-                // TRANS: Message in case the OpenID object cannot be connected to the user.
-                $this->message(_m('Error connecting user.'));
-                return;
-            }
-            if (Event::handle('StartOpenIDUpdateUser', array($cur, $canonical, &$sreg))) {
-                if ($sreg) {
-                    if (!oid_update_user($cur, $sreg)) {
-                        // TRANS: Message in case the user or the user profile cannot be saved in StatusNet.
-                        $this->message(_m('Error updating profile.'));
-                        return;
-                    }
-                }
-            }
-            Event::handle('EndOpenIDUpdateUser', array($cur, $canonical, $sreg));
-
-            // success!
-
-            $cur->query('COMMIT');
-
-            oid_set_last($display);
-
-            common_redirect(common_local_url('openidsettings'), 303);
-        }
-    }
-
-    /**
-     * Show a failure message
-     *
-     * Something went wrong. Save the message, and show the page.
-     *
-     * @param string $msg Error message to show
-     *
-     * @return void
-     */
-    function message($msg)
-    {
-        $this->message = $msg;
-        $this->showPage();
-    }
-
-    /**
-     * Title of the page
-     *
-     * @return string title
-     */
-    function title()
-    {
-        // TRANS: Title after getting the status of the OpenID authorisation request.
-        return _m('OpenID Login');
-    }
-
-    /**
-     * Show error message
-     *
-     * @return void
-     */
-    function showPageNotice()
-    {
-        if ($this->message) {
-            $this->element('p', 'error', $this->message);
-        }
-    }
-}
diff --git a/plugins/OpenID/finishopenidlogin.php b/plugins/OpenID/finishopenidlogin.php
deleted file mode 100644 (file)
index 4326f19..0000000
+++ /dev/null
@@ -1,591 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/plugins/OpenID/openid.php';
-
-class FinishopenidloginAction extends Action
-{
-    var $error = null;
-    var $username = null;
-    var $message = null;
-
-    function handle($args)
-    {
-        parent::handle($args);
-        if (common_is_real_login()) {
-            // TRANS: Client error message trying to log on with OpenID while already logged on.
-            $this->clientError(_m('Already logged in.'));
-        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-            $token = $this->trimmed('token');
-            if (!$token || $token != common_session_token()) {
-                // TRANS: Message given when there is a problem with the user's session token.
-                $this->showForm(_m('There was a problem with your session token. Try again, please.'));
-                return;
-            }
-            if ($this->arg('create')) {
-                if (!$this->boolean('license')) {
-                    // TRANS: Message given if user does not agree with the site's license.
-                    $this->showForm(_m('You cannot register if you do not agree to the license.'),
-                                    $this->trimmed('newname'));
-                    return;
-                }
-                $this->createNewUser();
-            } else if ($this->arg('connect')) {
-                $this->connectUser();
-            } else {
-                // TRANS: Messag given on an unknown error.
-                $this->showForm(_m('An unknown error has occured.'),
-                                $this->trimmed('newname'));
-            }
-        } else {
-            $this->tryLogin();
-        }
-    }
-
-    function showPageNotice()
-    {
-        if ($this->error) {
-            $this->element('div', array('class' => 'error'), $this->error);
-        } else {
-            $this->element('div', 'instructions',
-                           // TRANS: Instructions given after a first successful logon using OpenID.
-                           // TRANS: %s is the site name.
-                           sprintf(_m('This is the first time you have logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
-        }
-    }
-
-    function title()
-    {
-        // TRANS: Title
-        return _m('TITLE','OpenID Account Setup');
-    }
-
-    function showForm($error=null, $username=null)
-    {
-        $this->error = $error;
-        $this->username = $username;
-
-        $this->showPage();
-    }
-
-    /**
-     * @fixme much of this duplicates core code, which is very fragile.
-     * Should probably be replaced with an extensible mini version of
-     * the core registration form.
-     */
-    function showContent()
-    {
-        if (!empty($this->message_text)) {
-            $this->element('div', array('class' => 'error'), $this->message_text);
-            return;
-        }
-
-        // We don't recognize this OpenID, so we're going to give the user
-        // two options, each in its own mini-form.
-        //
-        // First, they can create a new account using their OpenID auth
-        // info. The profile will be pre-populated with whatever name,
-        // email, and location we can get from the OpenID provider, so
-        // all we ask for is the license confirmation.
-        $this->elementStart('form', array('method' => 'post',
-                                          'id' => 'account_create',
-                                          'class' => 'form_settings',
-                                          'action' => common_local_url('finishopenidlogin')));
-        $this->hidden('token', common_session_token());
-        $this->elementStart('fieldset', array('id' => 'form_openid_createaccount'));
-        $this->element('legend', null,
-                       // TRANS: Fieldset legend.
-                       _m('Create new account'));
-        $this->element('p', null,
-                       // TRANS: Form guide.
-                       _m('Create a new user with this nickname.'));
-        $this->elementStart('ul', 'form_data');
-
-        // Hook point for captcha etc
-        Event::handle('StartRegistrationFormData', array($this));
-
-        $this->elementStart('li');
-        // TRANS: Field label.
-        $this->input('newname', _m('New nickname'),
-                     ($this->username) ? $this->username : '',
-                     // TRANS: Field title.
-                     _m('1-64 lowercase letters or numbers, no punctuation or spaces.'));
-        $this->elementEnd('li');
-        $this->elementStart('li');
-        // TRANS: Field label.
-        $this->input('email', _m('Email'), $this->getEmail(),
-                     // TRANS: Field title.
-                     _m('Used only for updates, announcements, '.
-                       'and password recovery.'));
-        $this->elementEnd('li');
-
-        // Hook point for captcha etc
-        Event::handle('EndRegistrationFormData', array($this));
-
-        $this->elementStart('li');
-        $this->element('input', array('type' => 'checkbox',
-                                      'id' => 'license',
-                                      'class' => 'checkbox',
-                                      'name' => 'license',
-                                      'value' => 'true'));
-        $this->elementStart('label', array('for' => 'license',
-                                          'class' => 'checkbox'));
-        // TRANS: OpenID plugin link text.
-        // TRANS: %s is a link to a license with the license name as link text.
-        $message = _m('My text and files are available under %s ' .
-                     'except this private data: password, ' .
-                     'email address, IM address, and phone number.');
-        $link = '<a href="' .
-                htmlspecialchars(common_config('license', 'url')) .
-                '">' .
-                htmlspecialchars(common_config('license', 'title')) .
-                '</a>';
-        $this->raw(sprintf(htmlspecialchars($message), $link));
-        $this->elementEnd('label');
-        $this->elementEnd('li');
-        $this->elementEnd('ul');
-        // TRANS: Button label in form in which to create a new user on the site for an OpenID.
-        $this->submit('create', _m('BUTTON', 'Create'));
-        $this->elementEnd('fieldset');
-        $this->elementEnd('form');
-
-        // The second option is to attach this OpenID to an existing account
-        // on the local system, which they need to provide a password for.
-        $this->elementStart('form', array('method' => 'post',
-                                          'id' => 'account_connect',
-                                          'class' => 'form_settings',
-                                          'action' => common_local_url('finishopenidlogin')));
-        $this->hidden('token', common_session_token());
-        $this->elementStart('fieldset', array('id' => 'form_openid_createaccount'));
-        $this->element('legend', null,
-                       // TRANS: Used as form legend for form in which to connect an OpenID to an existing user on the site.
-                       _m('Connect existing account'));
-        $this->element('p', null,
-                       // TRANS: User instructions for form in which to connect an OpenID to an existing user on the site.
-                       _m('If you already have an account, login with your username and password to connect it to your OpenID.'));
-        $this->elementStart('ul', 'form_data');
-        $this->elementStart('li');
-        // TRANS: Field label in form in which to connect an OpenID to an existing user on the site.
-        $this->input('nickname', _m('Existing nickname'));
-        $this->elementEnd('li');
-        $this->elementStart('li');
-        // TRANS: Field label in form in which to connect an OpenID to an existing user on the site.
-        $this->password('password', _m('Password'));
-        $this->elementEnd('li');
-        $this->elementEnd('ul');
-        // TRANS: Button text in form in which to connect an OpenID to an existing user on the site.
-        $this->submit('connect', _m('BUTTON', 'Connect'));
-        $this->elementEnd('fieldset');
-        $this->elementEnd('form');
-    }
-
-    /**
-     * Get specified e-mail from the form, or the OpenID sreg info, or the
-     * invite code.
-     *
-     * @return string
-     */
-    function getEmail()
-    {
-        $email = $this->trimmed('email');
-        if (!empty($email)) {
-            return $email;
-        }
-
-        // Pull from openid thingy
-        list($display, $canonical, $sreg) = $this->getSavedValues();
-        if (!empty($sreg['email'])) {
-            return $sreg['email'];
-        }
-
-        // Terrible hack for invites...
-        if (common_config('site', 'inviteonly')) {
-            $code = $_SESSION['invitecode'];
-            if ($code) {
-                $invite = Invitation::getKV($code);
-
-                if ($invite && $invite->address_type == 'email') {
-                    return $invite->address;
-                }
-            }
-        }
-        return '';
-    }
-
-    function tryLogin()
-    {
-        $consumer = oid_consumer();
-
-        $response = $consumer->complete(common_local_url('finishopenidlogin'));
-
-        if ($response->status == Auth_OpenID_CANCEL) {
-            // TRANS: Status message in case the response from the OpenID provider is that the logon attempt was cancelled.
-            $this->message(_m('OpenID authentication cancelled.'));
-            return;
-        } else if ($response->status == Auth_OpenID_FAILURE) {
-            // TRANS: OpenID authentication failed; display the error message. %s is the error message.
-            $this->message(sprintf(_m('OpenID authentication failed: %s.'), $response->message));
-        } else if ($response->status == Auth_OpenID_SUCCESS) {
-            // This means the authentication succeeded; extract the
-            // identity URL and Simple Registration data (if it was
-            // returned).
-            $display = $response->getDisplayIdentifier();
-            $canonical = ($response->endpoint->canonicalID) ?
-              $response->endpoint->canonicalID : $response->getDisplayIdentifier();
-
-            oid_assert_allowed($display);
-            oid_assert_allowed($canonical);
-
-            $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
-
-            if ($sreg_resp) {
-                $sreg = $sreg_resp->contents();
-            }
-
-            // Launchpad teams extension
-            if (!oid_check_teams($response)) {
-                // TRANS: Message displayed when OpenID authentication is aborted.
-                $this->message(_m('OpenID authentication aborted: You are not allowed to login to this site.'));
-                return;
-            }
-
-            $user = oid_get_user($canonical);
-
-            if ($user) {
-                oid_set_last($display);
-                // XXX: commented out at @edd's request until better
-                // control over how data flows from OpenID provider.
-                // oid_update_user($user, $sreg);
-                common_set_user($user);
-                common_real_login(true);
-                if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
-                    common_rememberme($user);
-                }
-                unset($_SESSION['openid_rememberme']);
-                $this->goHome($user->nickname);
-            } else {
-                $this->saveValues($display, $canonical, $sreg);
-                $this->showForm(null, $this->bestNewNickname($display, $sreg));
-            }
-        }
-    }
-
-    function message($msg)
-    {
-        $this->message_text = $msg;
-        $this->showPage();
-    }
-
-    function saveValues($display, $canonical, $sreg)
-    {
-        common_ensure_session();
-        $_SESSION['openid_display'] = $display;
-        $_SESSION['openid_canonical'] = $canonical;
-        $_SESSION['openid_sreg'] = $sreg;
-    }
-
-    function getSavedValues()
-    {
-        return array($_SESSION['openid_display'],
-                     $_SESSION['openid_canonical'],
-                     $_SESSION['openid_sreg']);
-    }
-
-    function createNewUser()
-    {
-        // FIXME: save invite code before redirect, and check here
-
-        if (!Event::handle('StartRegistrationTry', array($this))) {
-            return;
-        }
-
-        if (common_config('site', 'closed')) {
-            // TRANS: OpenID plugin message. No new user registration is allowed on the site.
-            $this->clientError(_m('Registration not allowed.'));
-            return;
-        }
-
-        $invite = null;
-
-        if (common_config('site', 'inviteonly')) {
-            $code = $_SESSION['invitecode'];
-            if (empty($code)) {
-                // TRANS: OpenID plugin message. No new user registration is allowed on the site without an invitation code, and none was provided.
-                $this->clientError(_m('Registration not allowed.'));
-                return;
-            }
-
-            $invite = Invitation::getKV($code);
-
-            if (empty($invite)) {
-                // TRANS: OpenID plugin message. No new user registration is allowed on the site without an invitation code, and the one provided was not valid.
-                $this->clientError(_m('Not a valid invitation code.'));
-                return;
-            }
-        }
-
-        try {
-            $nickname = Nickname::normalize($this->trimmed('newname'));
-        } catch (NicknameException $e) {
-            $this->showForm($e->getMessage());
-            return;
-        }
-
-        if (!User::allowed_nickname($nickname)) {
-            // TRANS: OpenID plugin message. The entered new user name is blacklisted.
-            $this->showForm(_m('Nickname not allowed.'));
-            return;
-        }
-
-        if (User::getKV('nickname', $nickname)) {
-            // TRANS: OpenID plugin message. The entered new user name is already used.
-            $this->showForm(_m('Nickname already in use. Try another one.'));
-            return;
-        }
-
-        list($display, $canonical, $sreg) = $this->getSavedValues();
-
-        if (!$display || !$canonical) {
-            // TRANS: OpenID plugin server error. A stored OpenID cannot be retrieved.
-            $this->serverError(_m('Stored OpenID not found.'));
-            return;
-        }
-
-        // Possible race condition... let's be paranoid
-
-        $other = oid_get_user($canonical);
-
-        if ($other) {
-            // TRANS: OpenID plugin server error.
-            $this->serverError(_m('Creating new account for OpenID that already has a user.'));
-            return;
-        }
-
-        Event::handle('StartOpenIDCreateNewUser', array($canonical, &$sreg));
-
-        $location = '';
-        if (!empty($sreg['country'])) {
-            if ($sreg['postcode']) {
-                // XXX: use postcode to get city and region
-                // XXX: also, store postcode somewhere -- it's valuable!
-                $location = $sreg['postcode'] . ', ' . $sreg['country'];
-            } else {
-                $location = $sreg['country'];
-            }
-        }
-
-        if (!empty($sreg['fullname']) && mb_strlen($sreg['fullname']) <= 255) {
-            $fullname = $sreg['fullname'];
-        } else {
-            $fullname = '';
-        }
-
-        $email = $this->getEmail();
-
-        // XXX: add language
-        // XXX: add timezone
-
-        $args = array('nickname' => $nickname,
-                      'email' => $email,
-                      'fullname' => $fullname,
-                      'location' => $location);
-
-        if (!empty($invite)) {
-            $args['code'] = $invite->code;
-        }
-
-        $user = User::register($args);
-
-        $result = oid_link_user($user->id, $canonical, $display);
-
-        Event::handle('EndOpenIDCreateNewUser', array($user, $canonical, $sreg));
-
-        oid_set_last($display);
-        common_set_user($user);
-        common_real_login(true);
-        if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
-            common_rememberme($user);
-        }
-        unset($_SESSION['openid_rememberme']);
-
-        Event::handle('EndRegistrationTry', array($this));
-
-        common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)),
-                        303);
-    }
-
-    function connectUser()
-    {
-        $nickname = $this->trimmed('nickname');
-        $password = $this->trimmed('password');
-
-        if (!common_check_user($nickname, $password)) {
-            // TRANS: OpenID plugin message.
-            $this->showForm(_m('Invalid username or password.'));
-            return;
-        }
-
-        // They're legit!
-
-        $user = User::getKV('nickname', $nickname);
-
-        list($display, $canonical, $sreg) = $this->getSavedValues();
-
-        if (!$display || !$canonical) {
-            // TRANS: OpenID plugin server error. A stored OpenID cannot be found.
-            $this->serverError(_m('Stored OpenID not found.'));
-            return;
-        }
-
-        $result = oid_link_user($user->id, $canonical, $display);
-
-        if (!$result) {
-            // TRANS: OpenID plugin server error. The user or user profile could not be saved.
-            $this->serverError(_m('Error connecting user to OpenID.'));
-            return;
-        }
-
-        if (Event::handle('StartOpenIDUpdateUser', array($user, $canonical, &$sreg))) {
-            oid_update_user($user, $sreg);
-        }
-        Event::handle('EndOpenIDUpdateUser', array($user, $canonical, $sreg));
-
-        oid_set_last($display);
-        common_set_user($user);
-        common_real_login(true);
-        if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
-            common_rememberme($user);
-        }
-        unset($_SESSION['openid_rememberme']);
-        $this->goHome($user->nickname);
-    }
-
-    function goHome($nickname)
-    {
-        $url = common_get_returnto();
-        if ($url) {
-            // We don't have to return to it again
-            common_set_returnto(null);
-           $url = common_inject_session($url);
-        } else {
-            $url = common_local_url('all',
-                                    array('nickname' =>
-                                          $nickname));
-        }
-        common_redirect($url, 303);
-    }
-
-    function bestNewNickname($display, $sreg)
-    {
-        // Try the passed-in nickname
-
-        if (!empty($sreg['nickname'])) {
-            $nickname = $this->nicknamize($sreg['nickname']);
-            if ($this->isNewNickname($nickname)) {
-                return $nickname;
-            }
-        }
-
-        // Try the full name
-
-        if (!empty($sreg['fullname'])) {
-            $fullname = $this->nicknamize($sreg['fullname']);
-            if ($this->isNewNickname($fullname)) {
-                return $fullname;
-            }
-        }
-
-        // Try the URL
-
-        $from_url = $this->openidToNickname($display);
-
-        if ($from_url && $this->isNewNickname($from_url)) {
-            return $from_url;
-        }
-
-        // XXX: others?
-
-        return null;
-    }
-
-    function isNewNickname($str)
-    {
-        if (!Nickname::isValid($str)) {
-            return false;
-        }
-        if (!User::allowed_nickname($str)) {
-            return false;
-        }
-        if (User::getKV('nickname', $str)) {
-            return false;
-        }
-        return true;
-    }
-
-    function openidToNickname($openid)
-    {
-        if (Auth_Yadis_identifierScheme($openid) == 'XRI') {
-            return $this->xriToNickname($openid);
-        } else {
-            return $this->urlToNickname($openid);
-        }
-    }
-
-    // We try to use an OpenID URL as a legal StatusNet user name in this order
-    // 1. Plain hostname, like http://evanp.myopenid.com/
-    // 2. One element in path, like http://profile.typekey.com/EvanProdromou/
-    //    or http://getopenid.com/evanprodromou
-    function urlToNickname($openid)
-    {
-        return common_url_to_nickname($openid);
-    }
-
-    function xriToNickname($xri)
-    {
-        $base = $this->xriBase($xri);
-
-        if (!$base) {
-            return null;
-        } else {
-            // =evan.prodromou
-            // or @gratis*evan.prodromou
-            $parts = explode('*', substr($base, 1));
-            return $this->nicknamize(array_pop($parts));
-        }
-    }
-
-    function xriBase($xri)
-    {
-        if (substr($xri, 0, 6) == 'xri://') {
-            return substr($xri, 6);
-        } else {
-            return $xri;
-        }
-    }
-
-    // Given a string, try to make it work as a nickname
-    function nicknamize($str)
-    {
-        return common_nicknamize($str);
-    }
-}
diff --git a/plugins/OpenID/openidadminpanel.php b/plugins/OpenID/openidadminpanel.php
deleted file mode 100644 (file)
index c447a16..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * OpenID bridge administration panel
- *
- * 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  Settings
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Administer global OpenID settings
- *
- * @category Admin
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class OpenidadminpanelAction extends AdminPanelAction
-{
-    /**
-     * Returns the page title
-     *
-     * @return string page title
-     */
-    function title()
-    {
-        // TRANS: Title for OpenID bridge administration page.
-        return _m('TITLE','OpenID Settings');
-    }
-
-    /**
-     * Instructions for using this form.
-     *
-     * @return string instructions
-     */
-    function getInstructions()
-    {
-        // TRANS: Page instructions.
-        return _m('OpenID settings');
-    }
-
-    /**
-     * Show the OpenID admin panel form
-     *
-     * @return void
-     */
-    function showForm()
-    {
-        $form = new OpenIDAdminPanelForm($this);
-        $form->show();
-        return;
-    }
-
-    /**
-     * Save settings from the form
-     *
-     * @return void
-     */
-    function saveSettings()
-    {
-        static $settings = array(
-            'openid' => array('trusted_provider', 'required_team')
-        );
-
-        static $booleans = array(
-            'openid' => array('append_username'),
-            'site' => array('openidonly')
-        );
-
-        $values = array();
-
-        foreach ($settings as $section => $parts) {
-            foreach ($parts as $setting) {
-                $values[$section][$setting]
-                    = $this->trimmed($setting);
-            }
-        }
-
-        foreach ($booleans as $section => $parts) {
-            foreach ($parts as $setting) {
-                $values[$section][$setting]
-                    = ($this->boolean($setting)) ? 1 : 0;
-            }
-        }
-
-        // This throws an exception on validation errors
-
-        $this->validate($values);
-
-        // assert(all values are valid);
-
-        $config = new Config();
-
-        $config->query('BEGIN');
-
-        foreach ($settings as $section => $parts) {
-            foreach ($parts as $setting) {
-                Config::save($section, $setting, $values[$section][$setting]);
-            }
-        }
-
-        foreach ($booleans as $section => $parts) {
-            foreach ($parts as $setting) {
-                Config::save($section, $setting, $values[$section][$setting]);
-            }
-        }
-
-        $config->query('COMMIT');
-
-        return;
-    }
-
-    function validate(&$values)
-    {
-        // Validate consumer key and secret (can't be too long)
-
-        if (mb_strlen($values['openid']['trusted_provider']) > 255) {
-            $this->clientError(
-                // TRANS: Client error displayed when OpenID provider URL is too long.
-                _m('Invalid provider URL. Maximum length is 255 characters.')
-            );
-        }
-
-        if (mb_strlen($values['openid']['required_team']) > 255) {
-            $this->clientError(
-                // TRANS: Client error displayed when Launchpad team name is too long.
-                _m('Invalid team name. Maximum length is 255 characters.')
-            );
-        }
-    }
-}
-
-class OpenIDAdminPanelForm extends AdminForm
-{
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'openidadminpanel';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('openidadminpanel');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     *
-     * @todo Some of the options could prevent users from logging in again.
-     *       Make sure that the acting administrator has a valid OpenID matching,
-     *       or more carefully warn folks.
-     */
-    function formData()
-    {
-        $this->out->elementStart(
-            'fieldset',
-            array('id' => 'settings_openid')
-        );
-        // TRANS: Fieldset legend.
-        $this->out->element('legend', null, _m('LEGEND','Trusted provider'));
-        $this->out->element('p', 'form_guide',
-            // TRANS: Form guide.
-            _m('By default, users are allowed to authenticate with any OpenID provider. ' .
-               'If you are using your own OpenID service for shared sign-in, ' .
-               'you can restrict access to only your own users here.'));
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-        $this->input(
-            'trusted_provider',
-            // TRANS: Field label.
-            _m('Provider URL'),
-            // TRANS: Field title.
-            _m('All OpenID logins will be sent to this URL; other providers may not be used.'),
-            'openid'
-        );
-        $this->unli();
-
-        $this->li();
-        $this->out->checkbox(
-            // TRANS: Checkbox label.
-            'append_username', _m('Append a username to base URL'),
-            (bool) $this->value('append_username', 'openid'),
-            // TRANS: Checkbox title.
-            _m('Login form will show the base URL and prompt for a username to add at the end. Use when OpenID provider URL should be the profile page for individual users.'),
-            'true'
-        );
-        $this->unli();
-
-        $this->li();
-        $this->input(
-            'required_team',
-            // TRANS: Field label.
-            _m('Required team'),
-            // TRANS: Field title.
-            _m('Only allow logins from users in the given team (Launchpad extension).'),
-            'openid'
-        );
-        $this->unli();
-
-        $this->out->elementEnd('ul');
-        $this->out->elementEnd('fieldset');
-
-        $this->out->elementStart(
-            'fieldset',
-            array('id' => 'settings_openid-options')
-        );
-        // TRANS: Fieldset legend.
-        $this->out->element('legend', null, _m('LEGEND','Options'));
-
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-
-        $this->out->checkbox(
-            // TRANS: Checkbox label.
-            'openidonly', _m('Enable OpenID-only mode'),
-            (bool) $this->value('openidonly', 'site'),
-            // TRANS: Checkbox title.
-            _m('Require all users to login via OpenID. Warning: disables password authentication for all users!'),
-            'true'
-        );
-        $this->unli();
-
-        $this->out->elementEnd('ul');
-
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text to save OpenID settings.
-        $this->out->submit('submit', _m('BUTTON','Save'), 'submit', null,
-                           // TRANS: Button title to save OpenID settings.
-                           _m('Save OpenID settings.'));
-    }
-}
diff --git a/plugins/OpenID/openidlogin.php b/plugins/OpenID/openidlogin.php
deleted file mode 100644 (file)
index 86ebcae..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/plugins/OpenID/openid.php';
-
-class OpenidloginAction extends Action
-{
-    function handle($args)
-    {
-        parent::handle($args);
-        if (common_is_real_login()) {
-            // TRANS: Client error message trying to log on with OpenID while already logged on.
-            $this->clientError(_m('Already logged in.'));
-        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-            $provider = common_config('openid', 'trusted_provider');
-            if ($provider) {
-                $openid_url = $provider;
-                if (common_config('openid', 'append_username')) {
-                    $openid_url .= $this->trimmed('openid_username');
-                }
-            } else {
-                $openid_url = $this->trimmed('openid_url');
-            }
-
-            oid_assert_allowed($openid_url);
-
-            $rememberme = $this->boolean('rememberme');
-
-            common_ensure_session();
-
-            $_SESSION['openid_rememberme'] = $rememberme;
-
-            $result = oid_authenticate($openid_url,
-                                       'finishopenidlogin');
-
-            if (is_string($result)) { # error message
-                unset($_SESSION['openid_rememberme']);
-                $this->showForm($result, $openid_url);
-            }
-        } else {
-            $openid_url = oid_get_last();
-            $this->showForm(null, $openid_url);
-        }
-    }
-
-    function getInstructions()
-    {
-        if (common_logged_in() && !common_is_real_login() &&
-            common_get_returnto()) {
-            // rememberme logins have to reauthenticate before
-            // changing any profile settings (cookie-stealing protection)
-            // TRANS: OpenID plugin message. Rememberme logins have to reauthenticate before changing any profile settings.
-            // TRANS: "OpenID" is the display text for a link with URL "(%%doc.openid%%)".
-            return _m('For security reasons, please re-login with your ' .
-                     '[OpenID](%%doc.openid%%) ' .
-                     'before changing your settings.');
-        } else {
-            // TRANS: OpenID plugin message.
-            // TRANS: "OpenID" is the display text for a link with URL "(%%doc.openid%%)".
-            return _m('Login with an [OpenID](%%doc.openid%%) account.');
-        }
-    }
-
-    function showPageNotice()
-    {
-        if ($this->error) {
-            $this->element('div', array('class' => 'error'), $this->error);
-        } else {
-            $instr = $this->getInstructions();
-            $output = common_markup_to_html($instr);
-            $this->elementStart('div', 'instructions');
-            $this->raw($output);
-            $this->elementEnd('div');
-        }
-    }
-
-    function showScripts()
-    {
-        parent::showScripts();
-        if (common_config('openid', 'trusted_provider')) {
-            if (common_config('openid', 'append_username')) {
-                $this->autofocus('openid_username');
-            } else {
-                $this->autofocus('rememberme');
-            }
-        } else {
-            $this->autofocus('openid_url');
-        }
-    }
-
-    function title()
-    {
-        // TRANS: OpenID plugin message. Title.
-        return _m('TITLE','OpenID Login');
-    }
-
-    function showForm($error=null, $openid_url)
-    {
-        $this->error = $error;
-        $this->openid_url = $openid_url;
-        $this->showPage();
-    }
-
-    function showContent() {
-        $formaction = common_local_url('openidlogin');
-        $this->elementStart('form', array('method' => 'post',
-                                           'id' => 'form_openid_login',
-                                           'class' => 'form_settings',
-                                           'action' => $formaction));
-        $this->elementStart('fieldset');
-        // TRANS: OpenID plugin logon form legend.
-        $this->element('legend', null, _m('LEGEND','OpenID login'));
-
-        $this->elementStart('ul', 'form_data');
-        $this->elementStart('li');
-        $provider = common_config('openid', 'trusted_provider');
-        $appendUsername = common_config('openid', 'append_username');
-        if ($provider) {
-            // TRANS: Field label.
-            $this->element('label', array(), _m('LABEL','OpenID provider'));
-            $this->element('span', array(), $provider);
-            if ($appendUsername) {
-                $this->element('input', array('id' => 'openid_username',
-                                              'name' => 'openid_username',
-                                              'style' => 'float: none'));
-            }
-            $this->element('p', 'form_guide',
-                           // TRANS: Form guide.
-                           ($appendUsername ? _m('Enter your username.') . ' ' : '') .
-                           // TRANS: Form guide.
-                           _m('You will be sent to the provider\'s site for authentication.'));
-            $this->hidden('openid_url', $provider);
-        } else {
-            // TRANS: OpenID plugin logon form field label.
-            $this->input('openid_url', _m('OpenID URL'),
-                         $this->openid_url,
-                        // TRANS: OpenID plugin logon form field title.
-                         _m('Your OpenID URL.'));
-        }
-        $this->elementEnd('li');
-        $this->elementStart('li', array('id' => 'settings_rememberme'));
-        // TRANS: OpenID plugin logon form checkbox label for setting to put the OpenID information in a cookie.
-        $this->checkbox('rememberme', _m('Remember me'), false,
-                        // TRANS: OpenID plugin logon form field title.
-                        _m('Automatically login in the future; ' .
-                           'not for shared computers!'));
-        $this->elementEnd('li');
-        $this->elementEnd('ul');
-        // TRANS: OpenID plugin logon form button label to start logon with the data provided in the logon form.
-        $this->submit('submit', _m('BUTTON', 'Login'));
-        $this->elementEnd('fieldset');
-        $this->elementEnd('form');
-    }
-
-    function showLocalNav()
-    {
-        $nav = new LoginGroupNav($this);
-        $nav->show();
-    }
-
-    function showNoticeForm()
-    {
-    }
-
-    function showProfileBlock()
-    {
-    }
-}
diff --git a/plugins/OpenID/openidserver.php b/plugins/OpenID/openidserver.php
deleted file mode 100644 (file)
index 847eb48..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Settings for OpenID
- *
- * 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  Settings
- * @package   StatusNet
- * @author   Craig Andrews <candrews@integralblue.com>
- * @copyright 2008-2009 StatusNet, Inc.
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/plugins/OpenID/openid.php';
-
-/**
- * Settings for OpenID
- *
- * Lets users add, edit and delete OpenIDs from their account
- *
- * @category Settings
- * @package  StatusNet
- * @author   Craig Andrews <candrews@integralblue.com>
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class OpenidserverAction extends Action
-{
-    var $oserver;
-
-    function prepare($args)
-    {
-        parent::prepare($args);
-        $this->oserver = oid_server();
-        return true;
-    }
-
-    function handle($args)
-    {
-        parent::handle($args);
-        $request = $this->oserver->decodeRequest();
-        if (in_array($request->mode, array('checkid_immediate',
-            'checkid_setup'))) {
-            $user = common_current_user();
-            if(!$user){
-                if($request->immediate){
-                    //cannot prompt the user to login in immediate mode, so answer false
-                    $response = $this->generateDenyResponse($request);
-                }else{
-                    // Go log in, and then come back.
-                    //
-                    // Note: 303 redirect rather than 307 to avoid
-                    // prompting user for form resubmission if we
-                    // were POSTed here.
-                    common_set_returnto($_SERVER['REQUEST_URI']);
-                    common_redirect(common_local_url('login'), 303);
-                    return;
-                }
-            }else if(common_profile_url($user->nickname) == $request->identity || $request->idSelect()){
-                $user_openid_trustroot = User_openid_trustroot::pkeyGet(
-                                                array('user_id'=>$user->id, 'trustroot'=>$request->trust_root));
-                if(empty($user_openid_trustroot)){
-                    if($request->immediate){
-                        //cannot prompt the user to trust this trust root in immediate mode, so answer false
-                        $response = $this->generateDenyResponse($request);
-                    }else{
-                        common_ensure_session();
-                        $_SESSION['openid_trust_root'] = $request->trust_root;
-                        $allowResponse = $this->generateAllowResponse($request, $user);
-                        $this->oserver->encodeResponse($allowResponse); //sign the response
-                        $denyResponse = $this->generateDenyResponse($request);
-                        $this->oserver->encodeResponse($denyResponse); //sign the response
-                        $_SESSION['openid_allow_url'] = $allowResponse->encodeToUrl();
-                        $_SESSION['openid_deny_url'] = $denyResponse->encodeToUrl();
-
-                        // Ask the user to trust this trust root...
-                        //
-                        // Note: 303 redirect rather than 307 to avoid
-                        // prompting user for form resubmission if we
-                        // were POSTed here.
-                        common_redirect(common_local_url('openidtrust'), 303);
-                        return;
-                    }
-                }else{
-                    //user has previously authorized this trust root
-                    $response = $this->generateAllowResponse($request, $user);
-                    //$response = $request->answer(true, null, common_profile_url($user->nickname));
-                }
-            } else if ($request->immediate) {
-                $response = $this->generateDenyResponse($request);
-            } else {
-                //invalid
-                // TRANS: OpenID plugin client error given trying to add an unauthorised OpenID to a user (403).
-                // TRANS: %s is a request identity.
-                $this->clientError(sprintf(_m('You are not authorized to use the identity %s.'),$request->identity),$code=403);
-            }
-        } else {
-            $response = $this->oserver->handleRequest($request);
-        }
-
-        if($response){
-            $response = $this->oserver->encodeResponse($response);
-            if ($response->code != AUTH_OPENID_HTTP_OK) {
-                header(sprintf("HTTP/1.1 %d ", $response->code),
-                       true, $response->code);
-            }
-
-            if($response->headers){
-                foreach ($response->headers as $k => $v) {
-                    header("$k: $v");
-                }
-            }
-            $this->raw($response->body);
-        }else{
-            // TRANS: OpenID plugin client error given when not getting a response for a given OpenID provider (500).
-            $this->clientError(_m('Just an OpenID provider. Nothing to see here, move along...'),$code=500);
-        }
-    }
-
-    function generateAllowResponse($request, $user){
-        $response = $request->answer(true, null, common_profile_url($user->nickname));
-
-        $profile = $user->getProfile();
-        $sreg_data = array(
-            'fullname' => $profile->fullname,
-            'nickname' => $user->nickname,
-            'email' => $user->email,
-            'language' => $user->language,
-            'timezone' => $user->timezone);
-        $sreg_request = Auth_OpenID_SRegRequest::fromOpenIDRequest($request);
-        $sreg_response = Auth_OpenID_SRegResponse::extractResponse(
-                              $sreg_request, $sreg_data);
-        $sreg_response->toMessage($response->fields);
-        return $response;
-    }
-
-    function generateDenyResponse($request){
-        $response = $request->answer(false);
-        return $response;
-    }
-}
diff --git a/plugins/OpenID/openidsettings.php b/plugins/OpenID/openidsettings.php
deleted file mode 100644 (file)
index 7bef69b..0000000
+++ /dev/null
@@ -1,386 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Settings for OpenID
- *
- * 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  Settings
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/plugins/OpenID/openid.php';
-
-/**
- * Settings for OpenID
- *
- * Lets users add, edit and delete OpenIDs from their account
- *
- * @category Settings
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class OpenidsettingsAction extends SettingsAction
-{
-    /**
-     * Title of the page
-     *
-     * @return string Page title
-     */
-    function title()
-    {
-        // TRANS: Title of OpenID settings page for a user.
-        return _m('TITLE','OpenID settings');
-    }
-
-    /**
-     * Instructions for use
-     *
-     * @return string Instructions for use
-     */
-    function getInstructions()
-    {
-        // TRANS: Form instructions for OpenID settings.
-        // TRANS: This message contains Markdown links in the form [description](link).
-        return _m('[OpenID](%%doc.openid%%) lets you log into many sites ' .
-                 'with the same user account. '.
-                 'Manage your associated OpenIDs from here.');
-    }
-
-    function showScripts()
-    {
-        parent::showScripts();
-        $this->autofocus('openid_url');
-    }
-
-    /**
-     * Show the form for OpenID management
-     *
-     * We have one form with a few different submit buttons to do different things.
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        $user = common_current_user();
-
-        if (!common_config('openid', 'trusted_provider')) {
-            $this->elementStart('form', array('method' => 'post',
-                                              'id' => 'form_settings_openid_add',
-                                              'class' => 'form_settings',
-                                              'action' =>
-                                              common_local_url('openidsettings')));
-            $this->elementStart('fieldset', array('id' => 'settings_openid_add'));
-    
-            // TRANS: Fieldset legend.
-            $this->element('legend', null, _m('LEGEND','Add OpenID'));
-            $this->hidden('token', common_session_token());
-            $this->element('p', 'form_guide',
-                           // TRANS: Form guide.
-                           _m('If you want to add an OpenID to your account, ' .
-                             'enter it in the box below and click "Add".'));
-            $this->elementStart('ul', 'form_data');
-            $this->elementStart('li');
-            $this->element('label', array('for' => 'openid_url'),
-                           // TRANS: Field label.
-                           _m('OpenID URL'));
-            $this->element('input', array('name' => 'openid_url',
-                                          'type' => 'text',
-                                          'id' => 'openid_url'));
-            $this->elementEnd('li');
-            $this->elementEnd('ul');
-            $this->element('input', array('type' => 'submit',
-                                          'id' => 'settings_openid_add_action-submit',
-                                          'name' => 'add',
-                                          'class' => 'submit',
-                                          // TRANS: Button text for adding an OpenID URL.
-                                          'value' => _m('BUTTON','Add')));
-            $this->elementEnd('fieldset');
-            $this->elementEnd('form');
-        }
-        $oid = new User_openid();
-
-        $oid->user_id = $user->id;
-
-        $cnt = $oid->find();
-
-        if ($cnt > 0) {
-            // TRANS: Header on OpenID settings page.
-            $this->element('h2', null, _m('HEADER','Remove OpenID'));
-
-            if ($cnt == 1 && !$user->password) {
-
-                $this->element('p', 'form_guide',
-                               // TRANS: Form guide.
-                               _m('Removing your only OpenID '.
-                                 'would make it impossible to log in! ' .
-                                 'If you need to remove it, '.
-                                 'add another OpenID first.'));
-
-                if ($oid->fetch()) {
-                    $this->elementStart('p');
-                    $this->element('a', array('href' => $oid->canonical),
-                                   $oid->display);
-                    $this->elementEnd('p');
-                }
-
-            } else {
-
-                $this->element('p', 'form_guide',
-                               // TRANS: Form guide.
-                               _m('You can remove an OpenID from your account '.
-                                 'by clicking the button marked "Remove".'));
-                $idx = 0;
-
-                while ($oid->fetch()) {
-                    $this->elementStart('form',
-                                        array('method' => 'POST',
-                                              'id' => 'form_settings_openid_delete' . $idx,
-                                              'class' => 'form_settings',
-                                              'action' =>
-                                              common_local_url('openidsettings')));
-                    $this->elementStart('fieldset');
-                    $this->hidden('token', common_session_token());
-                    $this->element('a', array('href' => $oid->canonical),
-                                   $oid->display);
-                    $this->element('input', array('type' => 'hidden',
-                                                  'id' => 'openid_url'.$idx,
-                                                  'name' => 'openid_url',
-                                                  'value' => $oid->canonical));
-                    $this->element('input', array('type' => 'submit',
-                                                  'id' => 'remove'.$idx,
-                                                  'name' => 'remove',
-                                                  'class' => 'submit remove',
-                                                  // TRANS: Button text to remove an OpenID.
-                                                  'value' => _m('BUTTON','Remove')));
-                    $this->elementEnd('fieldset');
-                    $this->elementEnd('form');
-                    $idx++;
-                }
-            }
-        }
-
-        $this->elementStart('form', array('method' => 'post',
-                                          'id' => 'form_settings_openid_trustroots',
-                                          'class' => 'form_settings',
-                                          'action' =>
-                                          common_local_url('openidsettings')));
-        $this->elementStart('fieldset', array('id' => 'settings_openid_trustroots'));
-        // TRANS: Fieldset legend.
-        $this->element('legend', null, _m('OpenID Trusted Sites'));
-        $this->hidden('token', common_session_token());
-        $this->element('p', 'form_guide',
-                       // TRANS: Form guide.
-                       _m('The following sites are allowed to access your ' .
-                       'identity and log you in. You can remove a site from ' .
-                       'this list to deny it access to your OpenID.'));
-        $this->elementStart('ul', 'form_data');
-        $user_openid_trustroot = new User_openid_trustroot();
-        $user_openid_trustroot->user_id=$user->id;
-        if($user_openid_trustroot->find()) {
-            while($user_openid_trustroot->fetch()) {
-                $this->elementStart('li');
-                $this->element('input', array('name' => 'openid_trustroot[]',
-                                              'type' => 'checkbox',
-                                              'class' => 'checkbox',
-                                              'value' => $user_openid_trustroot->trustroot,
-                                              'id' => 'openid_trustroot_' . crc32($user_openid_trustroot->trustroot)));
-                $this->element('label', array('class'=>'checkbox', 'for' => 'openid_trustroot_' . crc32($user_openid_trustroot->trustroot)),
-                               $user_openid_trustroot->trustroot);
-                $this->elementEnd('li');
-            }
-        }
-        $this->elementEnd('ul');
-        $this->element('input', array('type' => 'submit',
-                                      'id' => 'settings_openid_trustroots_action-submit',
-                                      'name' => 'remove_trustroots',
-                                      'class' => 'submit',
-                                      // TRANS: Button text to remove an OpenID trustroot.
-                                      'value' => _m('BUTTON','Remove')));
-        $this->elementEnd('fieldset');
-        
-        $prefs = User_openid_prefs::getKV('user_id', $user->id);
-
-        $this->elementStart('fieldset');
-        $this->element('legend', null, _m('LEGEND','Preferences'));
-        $this->elementStart('ul', 'form_data');
-        $this->checkBox('hide_profile_link', "Hide OpenID links from my profile", !empty($prefs) && $prefs->hide_profile_link);
-        $this->element('input', array('type' => 'submit',
-                                      'id' => 'settings_openid_prefs_save',
-                                      'name' => 'save_prefs',
-                                      'class' => 'submit',
-                                      // TRANS: Button text to save OpenID prefs
-                                      'value' => _m('BUTTON','Save')));
-        $this->elementEnd('ul');
-        $this->elementEnd('fieldset');
-
-        $this->elementEnd('form');
-    }
-
-    /**
-     * Handle a POST request
-     *
-     * Muxes to different sub-functions based on which button was pushed
-     *
-     * @return void
-     */
-    function handlePost()
-    {
-        // CSRF protection
-        $token = $this->trimmed('token');
-        if (!$token || $token != common_session_token()) {
-            // TRANS: Client error displayed when the session token does not match or is not given.
-            $this->showForm(_m('There was a problem with your session token. '.
-                              'Try again, please.'));
-            return;
-        }
-
-        if ($this->arg('add')) {
-            if (common_config('openid', 'trusted_provider')) {
-                // TRANS: Form validation error if no OpenID providers can be added.
-                $this->showForm(_m('Cannot add new providers.'));
-            } else {
-                $result = oid_authenticate($this->trimmed('openid_url'),
-                                           'finishaddopenid');
-                if (is_string($result)) { // error message
-                    $this->showForm($result);
-                }
-            }
-        } else if ($this->arg('remove')) {
-            $this->removeOpenid();
-        } else if($this->arg('remove_trustroots')) {
-            $this->removeTrustroots();
-        } else if($this->arg('save_prefs')) {
-            $this->savePrefs();
-        } else {
-            // TRANS: Unexpected form validation error.
-            $this->showForm(_m('Something weird happened.'));
-        }
-    }
-
-    /**
-     * Handles a request to remove OpenID trustroots from the user's account
-     *
-     * Validates input and, if everything is OK, deletes the trustroots.
-     * Reloads the form with a success or error notification.
-     *
-     * @return void
-     */
-    function removeTrustroots()
-    {
-        $user = common_current_user();
-        $trustroots = $this->arg('openid_trustroot');
-        if($trustroots) {
-            foreach($trustroots as $trustroot) {
-                $user_openid_trustroot = User_openid_trustroot::pkeyGet(
-                                                array('user_id'=>$user->id, 'trustroot'=>$trustroot));
-                if($user_openid_trustroot) {
-                    $user_openid_trustroot->delete();
-                } else {
-                    // TRANS: Form validation error when trying to remove a non-existing trustroot.
-                    $this->showForm(_m('No such OpenID trustroot.'));
-                    return;
-                }
-            }
-            // TRANS: Success message after removing trustroots.
-            $this->showForm(_m('Trustroots removed.'), true);
-        } else {
-            $this->showForm();
-        }
-        return;
-    }
-
-    /**
-     * Handles a request to remove an OpenID from the user's account
-     *
-     * Validates input and, if everything is OK, deletes the OpenID.
-     * Reloads the form with a success or error notification.
-     *
-     * @return void
-     */
-    function removeOpenid()
-    {
-        $openid_url = $this->trimmed('openid_url');
-
-        $oid = User_openid::getKV('canonical', $openid_url);
-
-        if (!$oid) {
-            // TRANS: Form validation error for a non-existing OpenID.
-            $this->showForm(_m('No such OpenID.'));
-            return;
-        }
-        $cur = common_current_user();
-        if (!$cur || $oid->user_id != $cur->id) {
-            // TRANS: Form validation error if OpenID is connected to another user.
-            $this->showForm(_m('That OpenID does not belong to you.'));
-            return;
-        }
-        $oid->delete();
-        // TRANS: Success message after removing an OpenID.
-        $this->showForm(_m('OpenID removed.'), true);
-        return;
-    }
-
-    /**
-     * Handles a request to save preferences
-     *
-     * Validates input and, if everything is OK, deletes the OpenID.
-     * Reloads the form with a success or error notification.
-     *
-     * @return void
-     */
-    function savePrefs()
-    {
-        $cur = common_current_user();
-
-        if (empty($cur)) {
-            throw new ClientException(_("Not logged in."));
-        }
-
-        $orig  = null;
-        $prefs = User_openid_prefs::getKV('user_id', $cur->id);
-
-        if (empty($prefs)) {
-            $prefs          = new User_openid_prefs();
-            $prefs->user_id = $cur->id;
-            $prefs->created = common_sql_now();
-        } else {
-            $orig = clone($prefs);
-        }
-
-        $prefs->hide_profile_link = $this->boolean('hide_profile_link');
-
-        if (empty($orig)) {
-            $prefs->insert();
-        } else {
-            $prefs->update($orig);
-        }
-
-        $this->showForm(_m('OpenID preferences saved.'), true);
-        return;
-    }
-}
diff --git a/plugins/OpenID/openidtrust.php b/plugins/OpenID/openidtrust.php
deleted file mode 100644 (file)
index d39d854..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/plugins/OpenID/openid.php';
-
-class OpenidtrustAction extends Action
-{
-    var $trust_root;
-    var $allowUrl;
-    var $denyUrl;
-    var $user;
-
-    /**
-     * Is this a read-only action?
-     *
-     * @return boolean false
-     */
-    function isReadOnly($args)
-    {
-        return false;
-    }
-
-    /**
-     * Title of the page
-     *
-     * @return string title of the page
-     */
-    function title()
-    {
-        // TRANS: Title for identity verification page.
-        return _m('OpenID Identity Verification');
-    }
-
-    function prepare($args)
-    {
-        parent::prepare($args);
-        common_ensure_session();
-        $this->user = common_current_user();
-        if(empty($this->user)){
-            /* Go log in, and then come back. */
-            common_set_returnto($_SERVER['REQUEST_URI']);
-            common_redirect(common_local_url('login'));
-            return;
-        }
-        $this->trust_root = $_SESSION['openid_trust_root'];
-        $this->allowUrl = $_SESSION['openid_allow_url'];
-        $this->denyUrl = $_SESSION['openid_deny_url'];
-        if(empty($this->trust_root) || empty($this->allowUrl) || empty($this->denyUrl)){
-            // TRANS: Client error when visiting page directly.
-            $this->clientError(_m('This page should only be reached during OpenID processing, not directly.'));
-            return;
-        }
-        return true;
-    }
-
-    function handle($args)
-    {
-        parent::handle($args);
-        if($_SERVER['REQUEST_METHOD'] == 'POST'){
-            $this->handleSubmit();
-        }else{
-            $this->showPage();
-        }
-    }
-
-    function handleSubmit()
-    {
-        unset($_SESSION['openid_trust_root']);
-        unset($_SESSION['openid_allow_url']);
-        unset($_SESSION['openid_deny_url']);
-        if($this->arg('allow'))
-        {
-            //save to database
-            $user_openid_trustroot = new User_openid_trustroot();
-            $user_openid_trustroot->user_id = $this->user->id;
-            $user_openid_trustroot->trustroot = $this->trust_root;
-            $user_openid_trustroot->created = DB_DataObject_Cast::dateTime();
-            if (!$user_openid_trustroot->insert()) {
-                $err = PEAR::getStaticProperty('DB_DataObject','lastError');
-            }
-            common_redirect($this->allowUrl, $code=302);
-        }else{
-            common_redirect($this->denyUrl, $code=302);
-        }
-    }
-
-    /**
-     * Show page notice
-     *
-     * Display a notice for how to use the page, or the
-     * error if it exists.
-     *
-     * @return void
-     */
-    function showPageNotice()
-    {
-        // TRANS: Page notice. %s is a trustroot name.
-        $this->element('p',null,sprintf(_m('%s has asked to verify your identity. Click Continue to verify your identity and login without creating a new password.'),$this->trust_root));
-    }
-
-    /**
-     * Core of the display code
-     *
-     * Shows the login form.
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        $this->elementStart('form', array('method' => 'post',
-                                   'id' => 'form_openidtrust',
-                                   'class' => 'form_settings',
-                                   'action' => common_local_url('openidtrust')));
-        $this->elementStart('fieldset');
-        // TRANS: Button text to continue OpenID identity verification.
-        $this->submit('allow', _m('BUTTON','Continue'));
-        // TRANS: Button text to cancel OpenID identity verification.
-        $this->submit('deny', _m('BUTTON','Cancel'));
-
-        $this->elementEnd('fieldset');
-        $this->elementEnd('form');
-    }
-}
index d288363b21c323e2f06ece4b7effd14b4af33bf7..7285cf1ca836f2b530780b34850e56c073b48357 100644 (file)
@@ -181,20 +181,6 @@ ENDOFSCRIPT;
         return true;
     }
 
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'OpenxadminpanelAction':
-            require_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     function onEndAdminPanelNav($menu) {
         if (AdminPanelAction::canAdmin('openx')) {
             // TRANS: Menu item title.
diff --git a/plugins/OpenX/actions/openxadminpanel.php b/plugins/OpenX/actions/openxadminpanel.php
new file mode 100644 (file)
index 0000000..bf2001a
--- /dev/null
@@ -0,0 +1,222 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * OpenX administration panel
+ *
+ * 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  OpenX
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Administer openx settings
+ *
+ * @category OpenX
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class OpenXadminpanelAction extends AdminPanelAction
+{
+    /**
+     * Returns the page title
+     *
+     * @return string page title
+     */
+    function title()
+    {
+        // TRANS: Page title for OpenX admin panel.
+        return _m('TITLE', 'OpenX');
+    }
+
+    /**
+     * Instructions for using this form.
+     *
+     * @return string instructions
+     */
+    function getInstructions()
+    {
+        // TRANS: Instructions for OpenX admin panel.
+        return _m('OpenX settings for this StatusNet site');
+    }
+
+    /**
+     * Show the site admin panel form
+     *
+     * @return void
+     */
+    function showForm()
+    {
+        $form = new OpenXAdminPanelForm($this);
+        $form->show();
+        return;
+    }
+
+    /**
+     * Save settings from the form
+     *
+     * @return void
+     */
+    function saveSettings()
+    {
+        static $settings = array('openx' => array('adScript', 'mediumRectangle', 'rectangle', 'leaderboard', 'wideSkyscraper'));
+
+        $values = array();
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                $values[$section][$setting] = $this->trimmed($setting);
+            }
+        }
+
+        // This throws an exception on validation errors
+        $this->validate($values);
+
+        // assert(all values are valid);
+        $config = new Config();
+
+        $config->query('BEGIN');
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                Config::save($section, $setting, $values[$section][$setting]);
+            }
+        }
+
+        $config->query('COMMIT');
+
+        return;
+    }
+
+    function validate(&$values)
+    {
+    }
+}
+
+/**
+ * Form for the openx admin panel
+ */
+class OpenXAdminPanelForm extends AdminForm
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'form_openx_admin_panel';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_openx';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('openxadminpanel');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('fieldset', array('id' => 'openx_admin'));
+        $this->out->elementStart('ul', 'form_data');
+        $this->li();
+        $this->input('adScript',
+                     // TRANS: Form label in OpenX admin panel.
+                     _m('Ad script URL'),
+                     // TRANS: Tooltip for form label in OpenX admin panel.
+                     _m('Script URL'),
+                     'openx');
+        $this->unli();
+        $this->li();
+        $this->input('mediumRectangle',
+                     // TRANS: Form label in OpenX admin panel. Refers to advertisement format.
+                     _m('Medium rectangle'),
+                     // TRANS: Tooltip for form label in OpenX admin panel. Refers to advertisement format.
+                     _m('Medium rectangle zone'),
+                     'openx');
+        $this->unli();
+        $this->li();
+        $this->input('rectangle',
+                     // TRANS: Form label in OpenX admin panel. Refers to advertisement format.
+                     _m('Rectangle'),
+                     // TRANS: Tooltip for form label in OpenX admin panel. Refers to advertisement format.
+                     _m('Rectangle zone'),
+                     'openx');
+        $this->unli();
+        $this->li();
+        $this->input('leaderboard',
+                     // TRANS: Form label in OpenX admin panel. Refers to advertisement format.
+                     _m('Leaderboard'),
+                     // TRANS: Tooltip for form label in OpenX admin panel. Refers to advertisement format.
+                     _m('Leaderboard zone'),
+                     'openx');
+        $this->unli();
+        $this->li();
+        $this->input('wideSkyscraper',
+                     // TRANS: Form label in OpenX admin panel. Refers to advertisement format.
+                     _m('Skyscraper'),
+                     // TRANS: Tooltip for form label in OpenX admin panel. Refers to advertisement format.
+                     _m('Wide skyscraper zone'),
+                     'openx');
+        $this->unli();
+        $this->out->elementEnd('ul');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        $this->out->submit('submit',
+                    // TRANS: Submit button text in OpenX admin panel.
+                    _m('BUTTON','Save'),
+                    'submit',
+                    null,
+                    // TRANS: Submit button title in OpenX admin panel.
+                    _m('Save OpenX settings.'));
+    }
+}
diff --git a/plugins/OpenX/openxadminpanel.php b/plugins/OpenX/openxadminpanel.php
deleted file mode 100644 (file)
index bf2001a..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * OpenX administration panel
- *
- * 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  OpenX
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Administer openx settings
- *
- * @category OpenX
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class OpenXadminpanelAction extends AdminPanelAction
-{
-    /**
-     * Returns the page title
-     *
-     * @return string page title
-     */
-    function title()
-    {
-        // TRANS: Page title for OpenX admin panel.
-        return _m('TITLE', 'OpenX');
-    }
-
-    /**
-     * Instructions for using this form.
-     *
-     * @return string instructions
-     */
-    function getInstructions()
-    {
-        // TRANS: Instructions for OpenX admin panel.
-        return _m('OpenX settings for this StatusNet site');
-    }
-
-    /**
-     * Show the site admin panel form
-     *
-     * @return void
-     */
-    function showForm()
-    {
-        $form = new OpenXAdminPanelForm($this);
-        $form->show();
-        return;
-    }
-
-    /**
-     * Save settings from the form
-     *
-     * @return void
-     */
-    function saveSettings()
-    {
-        static $settings = array('openx' => array('adScript', 'mediumRectangle', 'rectangle', 'leaderboard', 'wideSkyscraper'));
-
-        $values = array();
-
-        foreach ($settings as $section => $parts) {
-            foreach ($parts as $setting) {
-                $values[$section][$setting] = $this->trimmed($setting);
-            }
-        }
-
-        // This throws an exception on validation errors
-        $this->validate($values);
-
-        // assert(all values are valid);
-        $config = new Config();
-
-        $config->query('BEGIN');
-
-        foreach ($settings as $section => $parts) {
-            foreach ($parts as $setting) {
-                Config::save($section, $setting, $values[$section][$setting]);
-            }
-        }
-
-        $config->query('COMMIT');
-
-        return;
-    }
-
-    function validate(&$values)
-    {
-    }
-}
-
-/**
- * Form for the openx admin panel
- */
-class OpenXAdminPanelForm extends AdminForm
-{
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'form_openx_admin_panel';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_openx';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('openxadminpanel');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('fieldset', array('id' => 'openx_admin'));
-        $this->out->elementStart('ul', 'form_data');
-        $this->li();
-        $this->input('adScript',
-                     // TRANS: Form label in OpenX admin panel.
-                     _m('Ad script URL'),
-                     // TRANS: Tooltip for form label in OpenX admin panel.
-                     _m('Script URL'),
-                     'openx');
-        $this->unli();
-        $this->li();
-        $this->input('mediumRectangle',
-                     // TRANS: Form label in OpenX admin panel. Refers to advertisement format.
-                     _m('Medium rectangle'),
-                     // TRANS: Tooltip for form label in OpenX admin panel. Refers to advertisement format.
-                     _m('Medium rectangle zone'),
-                     'openx');
-        $this->unli();
-        $this->li();
-        $this->input('rectangle',
-                     // TRANS: Form label in OpenX admin panel. Refers to advertisement format.
-                     _m('Rectangle'),
-                     // TRANS: Tooltip for form label in OpenX admin panel. Refers to advertisement format.
-                     _m('Rectangle zone'),
-                     'openx');
-        $this->unli();
-        $this->li();
-        $this->input('leaderboard',
-                     // TRANS: Form label in OpenX admin panel. Refers to advertisement format.
-                     _m('Leaderboard'),
-                     // TRANS: Tooltip for form label in OpenX admin panel. Refers to advertisement format.
-                     _m('Leaderboard zone'),
-                     'openx');
-        $this->unli();
-        $this->li();
-        $this->input('wideSkyscraper',
-                     // TRANS: Form label in OpenX admin panel. Refers to advertisement format.
-                     _m('Skyscraper'),
-                     // TRANS: Tooltip for form label in OpenX admin panel. Refers to advertisement format.
-                     _m('Wide skyscraper zone'),
-                     'openx');
-        $this->unli();
-        $this->out->elementEnd('ul');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        $this->out->submit('submit',
-                    // TRANS: Submit button text in OpenX admin panel.
-                    _m('BUTTON','Save'),
-                    'submit',
-                    null,
-                    // TRANS: Submit button title in OpenX admin panel.
-                    _m('Save OpenX settings.'));
-    }
-}
diff --git a/plugins/Poll/Poll.php b/plugins/Poll/Poll.php
deleted file mode 100644 (file)
index 213bab4..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-<?php
-/**
- * Data class to mark notices as bookmarks
- *
- * PHP version 5
- *
- * @category PollPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * For storing the poll options and such
- *
- * @category PollPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-
-class Poll extends Managed_DataObject
-{
-    public $__table = 'poll'; // table name
-    public $id;          // char(36) primary key not null -> UUID
-    public $uri;
-    public $profile_id;  // int -> profile.id
-    public $question;    // text
-    public $options;     // text; newline(?)-delimited
-    public $created;     // datetime
-
-    /**
-     * The One True Thingy that must be defined and declared.
-     */
-    public static function schemaDef()
-    {
-        return array(
-            'description' => 'Per-notice poll data for Poll plugin',
-            'fields' => array(
-                'id' => array('type' => 'char', 'length' => 36, 'not null' => true, 'description' => 'UUID'),
-                'uri' => array('type' => 'varchar', 'length' => 255, 'not null' => true),
-                'profile_id' => array('type' => 'int'),
-                'question' => array('type' => 'text'),
-                'options' => array('type' => 'text'),
-                'created' => array('type' => 'datetime', 'not null' => true),
-            ),
-            'primary key' => array('id'),
-            'unique keys' => array(
-                'poll_uri_key' => array('uri'),
-            ),
-        );
-    }
-
-    /**
-     * Get a bookmark based on a notice
-     *
-     * @param Notice $notice Notice to check for
-     *
-     * @return Poll found poll or null
-     */
-    static function getByNotice($notice)
-    {
-        return self::getKV('uri', $notice->uri);
-    }
-
-    function getOptions()
-    {
-        return explode("\n", $this->options);
-    }
-
-    /**
-     * Is this a valid selection index?
-     *
-     * @param numeric $selection (1-based)
-     * @return boolean
-     */
-    function isValidSelection($selection)
-    {
-        if ($selection != intval($selection)) {
-            return false;
-        }
-        if ($selection < 1 || $selection > count($this->getOptions())) {
-            return false;
-        }
-        return true;
-    }
-
-    function getNotice()
-    {
-        return Notice::getKV('uri', $this->uri);
-    }
-
-    function bestUrl()
-    {
-        return $this->getNotice()->bestUrl();
-    }
-
-    /**
-     * Get the response of a particular user to this poll, if any.
-     *
-     * @param Profile $profile
-     * @return Poll_response object or null
-     */
-    function getResponse(Profile $profile)
-    {
-       $pr = Poll_response::pkeyGet(array('poll_id' => $this->id,
-                                                                          'profile_id' => $profile->id));
-       return $pr;
-    }
-
-    function countResponses()
-    {
-        $pr = new Poll_response();
-        $pr->poll_id = $this->id;
-        $pr->groupBy('selection');
-        $pr->selectAdd('count(profile_id) as votes');
-        $pr->find();
-
-        $raw = array();
-        while ($pr->fetch()) {
-            // Votes list 1-based
-            // Array stores 0-based
-            $raw[$pr->selection - 1] = $pr->votes;
-        }
-
-        $counts = array();
-        foreach (array_keys($this->getOptions()) as $key) {
-            if (isset($raw[$key])) {
-                $counts[$key] = $raw[$key];
-            } else {
-                $counts[$key] = 0;
-            }
-        }
-        return $counts;
-    }
-
-    /**
-     * Save a new poll notice
-     *
-     * @param Profile $profile
-     * @param string  $question
-     * @param array   $opts (poll responses)
-     *
-     * @return Notice saved notice
-     */
-    static function saveNew($profile, $question, $opts, $options=null)
-    {
-        if (empty($options)) {
-            $options = array();
-        }
-
-        $p = new Poll();
-
-        $p->id          = UUID::gen();
-        $p->profile_id  = $profile->id;
-        $p->question    = $question;
-        $p->options     = implode("\n", $opts);
-
-        if (array_key_exists('created', $options)) {
-            $p->created = $options['created'];
-        } else {
-            $p->created = common_sql_now();
-        }
-
-        if (array_key_exists('uri', $options)) {
-            $p->uri = $options['uri'];
-        } else {
-            $p->uri = common_local_url('showpoll',
-                                        array('id' => $p->id));
-        }
-
-        common_log(LOG_DEBUG, "Saving poll: $p->id $p->uri");
-        $p->insert();
-
-        // TRANS: Notice content creating a poll.
-        // TRANS: %1$s is the poll question, %2$s is a link to the poll.
-        $content  = sprintf(_m('Poll: %1$s %2$s'),
-                            $question,
-                            $p->uri);
-        $link = '<a href="' . htmlspecialchars($p->uri) . '">' . htmlspecialchars($question) . '</a>';
-        // TRANS: Rendered version of the notice content creating a poll.
-        // TRANS: %s is a link to the poll with the question as link description.
-        $rendered = sprintf(_m('Poll: %s'), $link);
-
-        $tags    = array('poll');
-        $replies = array();
-
-        $options = array_merge(array('urls' => array(),
-                                     'rendered' => $rendered,
-                                     'tags' => $tags,
-                                     'replies' => $replies,
-                                     'object_type' => PollPlugin::POLL_OBJECT),
-                               $options);
-
-        if (!array_key_exists('uri', $options)) {
-            $options['uri'] = $p->uri;
-        }
-
-        $saved = Notice::saveNew($profile->id,
-                                 $content,
-                                 array_key_exists('source', $options) ?
-                                 $options['source'] : 'web',
-                                 $options);
-
-        return $saved;
-    }
-}
index d09d827bd1f5be281a4e0eb96333c041683ef430..a6292032c51b7cdc41205ff31bec7cd7c10d1bae 100644 (file)
@@ -81,40 +81,6 @@ class PollPlugin extends MicroAppPlugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'ShowpollAction':
-        case 'NewpollAction':
-        case 'RespondpollAction':
-        case 'PollsettingsAction':
-            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'Poll':
-        case 'Poll_response':
-        case 'User_poll_prefs':
-            include_once $dir.'/'.$cls.'.php';
-            return false;
-        case 'NewPollForm':
-        case 'PollResponseForm':
-        case 'PollResultForm':
-            include_once $dir.'/'.strtolower($cls).'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Map URLs to actions
      *
diff --git a/plugins/Poll/Poll_response.php b/plugins/Poll/Poll_response.php
deleted file mode 100644 (file)
index 21b390d..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-<?php
-/**
- * Data class to record responses to polls
- *
- * PHP version 5
- *
- * @category PollPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * For storing the poll options and such
- *
- * @category PollPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Poll_response extends Managed_DataObject
-{
-    public $__table = 'poll_response'; // table name
-    public $id;          // char(36) primary key not null -> UUID
-    public $uri;          // varchar(255)
-    public $poll_id;     // char(36) -> poll.id UUID
-    public $profile_id;  // int -> profile.id
-    public $selection;   // int -> choice #
-    public $created;     // datetime
-
-    /**
-     * The One True Thingy that must be defined and declared.
-     */
-    public static function schemaDef()
-    {
-        return array(
-            'description' => 'Record of responses to polls',
-            'fields' => array(
-                'id' => array('type' => 'char', 'length' => 36, 'not null' => true, 'description' => 'UUID of the response'),
-                'uri' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'UUID to the response notice'),
-                'poll_id' => array('type' => 'char', 'length' => 36, 'not null' => true, 'description' => 'UUID of poll being responded to'),
-                'profile_id' => array('type' => 'int'),
-                'selection' => array('type' => 'int'),
-                'created' => array('type' => 'datetime', 'not null' => true),
-            ),
-            'primary key' => array('id'),
-            'unique keys' => array(
-                'poll_uri_key' => array('uri'),
-                'poll_response_poll_id_profile_id_key' => array('poll_id', 'profile_id'),
-            ),
-            'indexes' => array(
-                'poll_response_profile_id_poll_id_index' => array('profile_id', 'poll_id'),
-            )
-        );
-    }
-
-    /**
-     * Get a poll response based on a notice
-     *
-     * @param Notice $notice Notice to check for
-     *
-     * @return Poll_response found response or null
-     */
-    static function getByNotice($notice)
-    {
-        return self::getKV('uri', $notice->uri);
-    }
-
-    /**
-     * Get the notice that belongs to this response...
-     *
-     * @return Notice
-     */
-    function getNotice()
-    {
-        return Notice::getKV('uri', $this->uri);
-    }
-
-    function bestUrl()
-    {
-        return $this->getNotice()->bestUrl();
-    }
-
-    /**
-     *
-     * @return Poll
-     */
-    function getPoll()
-    {
-        return Poll::getKV('id', $this->poll_id);
-    }
-    /**
-     * Save a new poll notice
-     *
-     * @param Profile $profile
-     * @param Poll    $poll the poll being responded to
-     * @param int     $selection (1-based)
-     * @param array   $opts (poll responses)
-     *
-     * @return Notice saved notice
-     */
-    static function saveNew($profile, $poll, $selection, $options=null)
-    {
-        if (empty($options)) {
-            $options = array();
-        }
-
-        if (!$poll->isValidSelection($selection)) {
-            // TRANS: Client exception thrown when responding to a poll with an invalid option.
-            throw new ClientException(_m('Invalid poll selection.'));
-        }
-        $opts = $poll->getOptions();
-        $answer = $opts[$selection - 1];
-
-        $pr = new Poll_response();
-        $pr->id          = UUID::gen();
-        $pr->profile_id  = $profile->id;
-        $pr->poll_id     = $poll->id;
-        $pr->selection   = $selection;
-
-        if (array_key_exists('created', $options)) {
-            $pr->created = $options['created'];
-        } else {
-            $pr->created = common_sql_now();
-        }
-
-        if (array_key_exists('uri', $options)) {
-            $pr->uri = $options['uri'];
-        } else {
-            $pr->uri = common_local_url('showpollresponse',
-                                        array('id' => $pr->id));
-        }
-
-        common_log(LOG_DEBUG, "Saving poll response: $pr->id $pr->uri");
-        $pr->insert();
-
-        // TRANS: Notice content voting for a poll.
-        // TRANS: %s is the chosen option in the poll.
-        $content  = sprintf(_m('voted for "%s"'),
-                            $answer);
-        $link = '<a href="' . htmlspecialchars($poll->uri) . '">' . htmlspecialchars($answer) . '</a>';
-        // TRANS: Rendered version of the notice content voting for a poll.
-        // TRANS: %s a link to the poll with the chosen option as link description.
-        $rendered = sprintf(_m('voted for "%s"'), $link);
-
-        $tags    = array();
-
-        $options = array_merge(array('urls' => array(),
-                                     'rendered' => $rendered,
-                                     'tags' => $tags,
-                                     'reply_to' => $poll->getNotice()->id,
-                                     'object_type' => PollPlugin::POLL_RESPONSE_OBJECT),
-                               $options);
-
-        if (!array_key_exists('uri', $options)) {
-            $options['uri'] = $pr->uri;
-        }
-
-        $saved = Notice::saveNew($profile->id,
-                                 $content,
-                                 array_key_exists('source', $options) ?
-                                 $options['source'] : 'web',
-                                 $options);
-
-        return $saved;
-    }
-}
diff --git a/plugins/Poll/User_poll_prefs.php b/plugins/Poll/User_poll_prefs.php
deleted file mode 100644 (file)
index 450a6b9..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-/**
- * Data class to record user prefs for polls
- *
- * PHP version 5
- *
- * @category PollPlugin
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2012, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * For storing the poll prefs
- *
- * @category PollPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class User_poll_prefs extends Managed_DataObject
-{
-    public $__table = 'user_poll_prefs'; // table name
-    public $user_id;          // int id
-    public $hide_responses;   // boolean
-    public $created;          // datetime
-    public $modified;         // datetime
-
-    /**
-     * The One True Thingy that must be defined and declared.
-     */
-    public static function schemaDef()
-    {
-        return array(
-            'description' => 'Record of user preferences for polls',
-            'fields' => array(
-                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id'),
-                'hide_responses' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'Hide all poll responses'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('user_id')
-        );
-    }
-}
diff --git a/plugins/Poll/actions/newpoll.php b/plugins/Poll/actions/newpoll.php
new file mode 100644 (file)
index 0000000..e7d5d3d
--- /dev/null
@@ -0,0 +1,231 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Add a new Poll
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Poll
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Add a new Poll
+ *
+ * @category  Poll
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class NewPollAction extends Action
+{
+    protected $user        = null;
+    protected $error       = null;
+    protected $complete    = null;
+
+    protected $question    = null;
+    protected $options     = array();
+
+    /**
+     * Returns the title of the action
+     *
+     * @return string Action title
+     */
+    function title()
+    {
+        // TRANS: Title for poll page.
+        return _m('New poll');
+    }
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        $this->user = common_current_user();
+
+        if (empty($this->user)) {
+            // TRANS: Client exception thrown trying to create a poll while not logged in.
+            throw new ClientException(_m('You must be logged in to post a poll.'),
+                                      403);
+        }
+
+        if ($this->isPost()) {
+            $this->checkSessionToken();
+        }
+
+        $this->question = $this->trimmed('question');
+        for ($i = 1; $i < 20; $i++) {
+            $opt = $this->trimmed('option' . $i);
+            if ($opt != '') {
+                $this->options[] = $opt;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        parent::handle($argarray);
+
+        if ($this->isPost()) {
+            $this->newPoll();
+        } else {
+            $this->showPage();
+        }
+
+        return;
+    }
+
+    /**
+     * Add a new Poll
+     *
+     * @return void
+     */
+    function newPoll()
+    {
+        if ($this->boolean('ajax')) {
+            StatusNet::setApi(true);
+        }
+        try {
+            if (empty($this->question)) {
+            // TRANS: Client exception thrown trying to create a poll without a question.
+                throw new ClientException(_m('Poll must have a question.'));
+            }
+
+            if (count($this->options) < 2) {
+                // TRANS: Client exception thrown trying to create a poll with fewer than two options.
+                throw new ClientException(_m('Poll must have at least two options.'));
+            }
+
+            // Notice options; distinct from choices for the poll
+
+            $options = array();
+
+            // Does the heavy-lifting for getting "To:" information
+
+            ToSelector::fillOptions($this, $options);
+
+            $saved = Poll::saveNew($this->user->getProfile(),
+                                   $this->question,
+                                   $this->options,
+                                   $options);
+
+        } catch (ClientException $ce) {
+            $this->error = $ce->getMessage();
+            $this->showPage();
+            return;
+        }
+
+        if ($this->boolean('ajax')) {
+            header('Content-Type: text/xml;charset=utf-8');
+            $this->xw->startDocument('1.0', 'UTF-8');
+            $this->elementStart('html');
+            $this->elementStart('head');
+            // TRANS: Page title after sending a notice.
+            $this->element('title', null, _m('Notice posted'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $this->showNotice($saved);
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            common_redirect($saved->bestUrl(), 303);
+        }
+    }
+
+    /**
+     * Output a notice
+     *
+     * Used to generate the notice code for Ajax results.
+     *
+     * @param Notice $notice Notice that was saved
+     *
+     * @return void
+     */
+    function showNotice($notice)
+    {
+        class_exists('NoticeList'); // @fixme hack for autoloader
+        $nli = new NoticeListItem($notice, $this);
+        $nli->show();
+    }
+
+    /**
+     * Show the Poll form
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        if (!empty($this->error)) {
+            $this->element('p', 'error', $this->error);
+        }
+
+        $form = new NewPollForm($this,
+                                 $this->question,
+                                 $this->options);
+
+        $form->show();
+
+        return;
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+            $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/plugins/Poll/actions/pollsettings.php b/plugins/Poll/actions/pollsettings.php
new file mode 100644 (file)
index 0000000..b390812
--- /dev/null
@@ -0,0 +1,198 @@
+<?php
+/**
+ * Form to set your personal poll settings
+ *
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugins
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2012 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+class PollSettingsAction extends SettingsAction
+{
+    /**
+     * Title of the page
+     *
+     * @return string Page title
+     */
+    function title()
+    {
+        // TRANS: Page title.
+        return _m('Poll settings');
+    }
+
+    /**
+     * Instructions for use
+     *
+     * @return string Instructions for use
+     */
+
+    function getInstructions()
+    {
+        // TRANS: Page instructions.
+        return _m('Set your poll preferences');
+    }
+
+    /**
+     * Show the form for Poll
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        $user = common_current_user();
+
+        $prefs = User_poll_prefs::getKV('user_id', $user->id);
+
+        $form = new PollPrefsForm($this, $prefs);
+
+        $form->show();
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+
+    function handlePost()
+    {
+        $user = common_current_user();
+
+        $upp = User_poll_prefs::getKV('user_id', $user->id);
+        $orig = null;
+
+        if (!empty($upp)) {
+            $orig = clone($upp);
+        } else {
+            $upp = new User_poll_prefs();
+            $upp->user_id = $user->id;
+            $upp->created = common_sql_now();
+        }
+
+        $upp->hide_responses = $this->boolean('hide_responses');
+        $upp->modified       = common_sql_now();
+
+        if (!empty($orig)) {
+            $upp->update($orig);
+        } else {
+            $upp->insert();
+        }
+
+        // TRANS: Confirmation shown when user profile settings are saved.
+        $this->showForm(_('Settings saved.'), true);
+
+        return;
+    }
+}
+
+class PollPrefsForm extends Form
+{
+    var $prefs;
+
+    function __construct($out, $prefs)
+    {
+        parent::__construct($out);
+        $this->prefs = $prefs;
+    }
+
+    /**
+     * Visible or invisible data elements
+     *
+     * Display the form fields that make up the data of the form.
+     * Sub-classes should overload this to show their data.
+     *
+     * @return void
+     */
+
+    function formData()
+    {
+        $this->elementStart('fieldset');
+        $this->elementStart('ul', 'form_data');
+        $this->elementStart('li');
+        $this->checkbox('hide_responses',
+                        _('Do not deliver poll responses to my home timeline'),
+                        (!empty($this->prefs) && $this->prefs->hide_responses));
+        $this->elementEnd('li');
+        $this->elementEnd('ul');
+        $this->elementEnd('fieldset');
+    }
+
+    /**
+     * Buttons for form actions
+     *
+     * Submit and cancel buttons (or whatever)
+     * Sub-classes should overload this to show their own buttons.
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+        $this->submit('submit', _('Save'));
+    }
+
+    /**
+     * ID of the form
+     *
+     * Should be unique on the page. Sub-classes should overload this
+     * to show their own IDs.
+     *
+     * @return int ID of the form
+     */
+
+    function id()
+    {
+        return 'form_poll_prefs';
+    }
+
+    /**
+     * Action of the form.
+     *
+     * URL to post to. Should be overloaded by subclasses to give
+     * somewhere to post to.
+     *
+     * @return string URL to post to
+     */
+
+    function action()
+    {
+        return common_local_url('pollsettings');
+    }
+
+    /**
+     * Class of the form. May include space-separated list of multiple classes.
+     *
+     * @return string the form's class
+     */
+
+    function formClass()
+    {
+        return 'form_settings';
+    }
+}
diff --git a/plugins/Poll/actions/respondpoll.php b/plugins/Poll/actions/respondpoll.php
new file mode 100644 (file)
index 0000000..df3f68e
--- /dev/null
@@ -0,0 +1,200 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Respond to a Poll
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Poll
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Respond to a Poll
+ *
+ * @category  Poll
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class RespondPollAction extends Action
+{
+    protected $user        = null;
+    protected $error       = null;
+    protected $complete    = null;
+
+    protected $poll        = null;
+    protected $selection   = null;
+
+    /**
+     * Returns the title of the action
+     *
+     * @return string Action title
+     */
+    function title()
+    {
+        // TRANS: Page title for poll response.
+        return _m('Poll response');
+    }
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+        if ($this->boolean('ajax')) {
+            StatusNet::setApi(true);
+        }
+
+        $this->user = common_current_user();
+
+        if (empty($this->user)) {
+            // TRANS: Client exception thrown trying to respond to a poll while not logged in.
+            throw new ClientException(_m('You must be logged in to respond to a poll.'),
+                                      403);
+        }
+
+        if ($this->isPost()) {
+            $this->checkSessionToken();
+        }
+
+        $id = $this->trimmed('id');
+        $this->poll = Poll::getKV('id', $id);
+        if (empty($this->poll)) {
+            // TRANS: Client exception thrown trying to respond to a non-existing poll.
+            throw new ClientException(_m('Invalid or missing poll.'), 404);
+        }
+
+        $selection = intval($this->trimmed('pollselection'));
+        if ($selection < 1 || $selection > count($this->poll->getOptions())) {
+            // TRANS: Client exception thrown responding to a poll with an invalid answer.
+            throw new ClientException(_m('Invalid poll selection.'));
+        }
+        $this->selection = $selection;
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        parent::handle($argarray);
+
+        if ($this->isPost()) {
+            $this->respondPoll();
+        } else {
+            $this->showPage();
+        }
+
+        return;
+    }
+
+    /**
+     * Add a new Poll
+     *
+     * @return void
+     */
+    function respondPoll()
+    {
+        try {
+            $notice = Poll_response::saveNew($this->user->getProfile(),
+                                             $this->poll,
+                                             $this->selection);
+        } catch (ClientException $ce) {
+            $this->error = $ce->getMessage();
+            $this->showPage();
+            return;
+        }
+
+        if ($this->boolean('ajax')) {
+            header('Content-Type: text/xml;charset=utf-8');
+            $this->xw->startDocument('1.0', 'UTF-8');
+            $this->elementStart('html');
+            $this->elementStart('head');
+            // TRANS: Page title after sending a poll response.
+            $this->element('title', null, _m('Poll results'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $form = new PollResultForm($this->poll, $this);
+            $form->show();
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            common_redirect($this->poll->bestUrl(), 303);
+        }
+    }
+
+    /**
+     * Show the Poll form
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        if (!empty($this->error)) {
+            $this->element('p', 'error', $this->error);
+        }
+
+        $form = new PollResponseForm($this->poll, $this);
+
+        $form->show();
+
+        return;
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+            $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/plugins/Poll/actions/showpoll.php b/plugins/Poll/actions/showpoll.php
new file mode 100644 (file)
index 0000000..91f661e
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Show a single Poll
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  PollPlugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Show a single Poll, with associated information
+ *
+ * @category  PollPlugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class ShowPollAction extends ShownoticeAction
+{
+    protected $poll = null;
+
+    function getNotice()
+    {
+        $this->id = $this->trimmed('id');
+
+        $this->poll = Poll::getKV('id', $this->id);
+
+        if (empty($this->poll)) {
+            // TRANS: Client exception thrown trying to view a non-existing poll.
+            throw new ClientException(_m('No such poll.'), 404);
+        }
+
+        $notice = $this->poll->getNotice();
+
+        if (empty($notice)) {
+            // Did we used to have it, and it got deleted?
+            // TRANS: Client exception thrown trying to view a non-existing poll notice.
+            throw new ClientException(_m('No such poll notice.'), 404);
+        }
+
+        return $notice;
+    }
+
+    /**
+     * Title of the page
+     *
+     * Used by Action class for layout.
+     *
+     * @return string page tile
+     */
+    function title()
+    {
+        // TRANS: Page title for a poll.
+        // TRANS: %1$s is the nickname of the user that created the poll, %2$s is the poll question.
+        return sprintf(_m('%1$s\'s poll: %2$s'),
+                       $this->user->nickname,
+                       $this->poll->question);
+    }
+
+    /**
+     * @fixme combine the notice time with poll update time
+     */
+    function lastModified()
+    {
+        return Action::lastModified();
+    }
+
+
+    /**
+     * @fixme combine the notice time with poll update time
+     */
+    function etag()
+    {
+        return Action::etag();
+    }
+}
diff --git a/plugins/Poll/classes/Poll.php b/plugins/Poll/classes/Poll.php
new file mode 100644 (file)
index 0000000..213bab4
--- /dev/null
@@ -0,0 +1,231 @@
+<?php
+/**
+ * Data class to mark notices as bookmarks
+ *
+ * PHP version 5
+ *
+ * @category PollPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * For storing the poll options and such
+ *
+ * @category PollPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+
+class Poll extends Managed_DataObject
+{
+    public $__table = 'poll'; // table name
+    public $id;          // char(36) primary key not null -> UUID
+    public $uri;
+    public $profile_id;  // int -> profile.id
+    public $question;    // text
+    public $options;     // text; newline(?)-delimited
+    public $created;     // datetime
+
+    /**
+     * The One True Thingy that must be defined and declared.
+     */
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'Per-notice poll data for Poll plugin',
+            'fields' => array(
+                'id' => array('type' => 'char', 'length' => 36, 'not null' => true, 'description' => 'UUID'),
+                'uri' => array('type' => 'varchar', 'length' => 255, 'not null' => true),
+                'profile_id' => array('type' => 'int'),
+                'question' => array('type' => 'text'),
+                'options' => array('type' => 'text'),
+                'created' => array('type' => 'datetime', 'not null' => true),
+            ),
+            'primary key' => array('id'),
+            'unique keys' => array(
+                'poll_uri_key' => array('uri'),
+            ),
+        );
+    }
+
+    /**
+     * Get a bookmark based on a notice
+     *
+     * @param Notice $notice Notice to check for
+     *
+     * @return Poll found poll or null
+     */
+    static function getByNotice($notice)
+    {
+        return self::getKV('uri', $notice->uri);
+    }
+
+    function getOptions()
+    {
+        return explode("\n", $this->options);
+    }
+
+    /**
+     * Is this a valid selection index?
+     *
+     * @param numeric $selection (1-based)
+     * @return boolean
+     */
+    function isValidSelection($selection)
+    {
+        if ($selection != intval($selection)) {
+            return false;
+        }
+        if ($selection < 1 || $selection > count($this->getOptions())) {
+            return false;
+        }
+        return true;
+    }
+
+    function getNotice()
+    {
+        return Notice::getKV('uri', $this->uri);
+    }
+
+    function bestUrl()
+    {
+        return $this->getNotice()->bestUrl();
+    }
+
+    /**
+     * Get the response of a particular user to this poll, if any.
+     *
+     * @param Profile $profile
+     * @return Poll_response object or null
+     */
+    function getResponse(Profile $profile)
+    {
+       $pr = Poll_response::pkeyGet(array('poll_id' => $this->id,
+                                                                          'profile_id' => $profile->id));
+       return $pr;
+    }
+
+    function countResponses()
+    {
+        $pr = new Poll_response();
+        $pr->poll_id = $this->id;
+        $pr->groupBy('selection');
+        $pr->selectAdd('count(profile_id) as votes');
+        $pr->find();
+
+        $raw = array();
+        while ($pr->fetch()) {
+            // Votes list 1-based
+            // Array stores 0-based
+            $raw[$pr->selection - 1] = $pr->votes;
+        }
+
+        $counts = array();
+        foreach (array_keys($this->getOptions()) as $key) {
+            if (isset($raw[$key])) {
+                $counts[$key] = $raw[$key];
+            } else {
+                $counts[$key] = 0;
+            }
+        }
+        return $counts;
+    }
+
+    /**
+     * Save a new poll notice
+     *
+     * @param Profile $profile
+     * @param string  $question
+     * @param array   $opts (poll responses)
+     *
+     * @return Notice saved notice
+     */
+    static function saveNew($profile, $question, $opts, $options=null)
+    {
+        if (empty($options)) {
+            $options = array();
+        }
+
+        $p = new Poll();
+
+        $p->id          = UUID::gen();
+        $p->profile_id  = $profile->id;
+        $p->question    = $question;
+        $p->options     = implode("\n", $opts);
+
+        if (array_key_exists('created', $options)) {
+            $p->created = $options['created'];
+        } else {
+            $p->created = common_sql_now();
+        }
+
+        if (array_key_exists('uri', $options)) {
+            $p->uri = $options['uri'];
+        } else {
+            $p->uri = common_local_url('showpoll',
+                                        array('id' => $p->id));
+        }
+
+        common_log(LOG_DEBUG, "Saving poll: $p->id $p->uri");
+        $p->insert();
+
+        // TRANS: Notice content creating a poll.
+        // TRANS: %1$s is the poll question, %2$s is a link to the poll.
+        $content  = sprintf(_m('Poll: %1$s %2$s'),
+                            $question,
+                            $p->uri);
+        $link = '<a href="' . htmlspecialchars($p->uri) . '">' . htmlspecialchars($question) . '</a>';
+        // TRANS: Rendered version of the notice content creating a poll.
+        // TRANS: %s is a link to the poll with the question as link description.
+        $rendered = sprintf(_m('Poll: %s'), $link);
+
+        $tags    = array('poll');
+        $replies = array();
+
+        $options = array_merge(array('urls' => array(),
+                                     'rendered' => $rendered,
+                                     'tags' => $tags,
+                                     'replies' => $replies,
+                                     'object_type' => PollPlugin::POLL_OBJECT),
+                               $options);
+
+        if (!array_key_exists('uri', $options)) {
+            $options['uri'] = $p->uri;
+        }
+
+        $saved = Notice::saveNew($profile->id,
+                                 $content,
+                                 array_key_exists('source', $options) ?
+                                 $options['source'] : 'web',
+                                 $options);
+
+        return $saved;
+    }
+}
diff --git a/plugins/Poll/classes/Poll_response.php b/plugins/Poll/classes/Poll_response.php
new file mode 100644 (file)
index 0000000..21b390d
--- /dev/null
@@ -0,0 +1,191 @@
+<?php
+/**
+ * Data class to record responses to polls
+ *
+ * PHP version 5
+ *
+ * @category PollPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * For storing the poll options and such
+ *
+ * @category PollPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Poll_response extends Managed_DataObject
+{
+    public $__table = 'poll_response'; // table name
+    public $id;          // char(36) primary key not null -> UUID
+    public $uri;          // varchar(255)
+    public $poll_id;     // char(36) -> poll.id UUID
+    public $profile_id;  // int -> profile.id
+    public $selection;   // int -> choice #
+    public $created;     // datetime
+
+    /**
+     * The One True Thingy that must be defined and declared.
+     */
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'Record of responses to polls',
+            'fields' => array(
+                'id' => array('type' => 'char', 'length' => 36, 'not null' => true, 'description' => 'UUID of the response'),
+                'uri' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'UUID to the response notice'),
+                'poll_id' => array('type' => 'char', 'length' => 36, 'not null' => true, 'description' => 'UUID of poll being responded to'),
+                'profile_id' => array('type' => 'int'),
+                'selection' => array('type' => 'int'),
+                'created' => array('type' => 'datetime', 'not null' => true),
+            ),
+            'primary key' => array('id'),
+            'unique keys' => array(
+                'poll_uri_key' => array('uri'),
+                'poll_response_poll_id_profile_id_key' => array('poll_id', 'profile_id'),
+            ),
+            'indexes' => array(
+                'poll_response_profile_id_poll_id_index' => array('profile_id', 'poll_id'),
+            )
+        );
+    }
+
+    /**
+     * Get a poll response based on a notice
+     *
+     * @param Notice $notice Notice to check for
+     *
+     * @return Poll_response found response or null
+     */
+    static function getByNotice($notice)
+    {
+        return self::getKV('uri', $notice->uri);
+    }
+
+    /**
+     * Get the notice that belongs to this response...
+     *
+     * @return Notice
+     */
+    function getNotice()
+    {
+        return Notice::getKV('uri', $this->uri);
+    }
+
+    function bestUrl()
+    {
+        return $this->getNotice()->bestUrl();
+    }
+
+    /**
+     *
+     * @return Poll
+     */
+    function getPoll()
+    {
+        return Poll::getKV('id', $this->poll_id);
+    }
+    /**
+     * Save a new poll notice
+     *
+     * @param Profile $profile
+     * @param Poll    $poll the poll being responded to
+     * @param int     $selection (1-based)
+     * @param array   $opts (poll responses)
+     *
+     * @return Notice saved notice
+     */
+    static function saveNew($profile, $poll, $selection, $options=null)
+    {
+        if (empty($options)) {
+            $options = array();
+        }
+
+        if (!$poll->isValidSelection($selection)) {
+            // TRANS: Client exception thrown when responding to a poll with an invalid option.
+            throw new ClientException(_m('Invalid poll selection.'));
+        }
+        $opts = $poll->getOptions();
+        $answer = $opts[$selection - 1];
+
+        $pr = new Poll_response();
+        $pr->id          = UUID::gen();
+        $pr->profile_id  = $profile->id;
+        $pr->poll_id     = $poll->id;
+        $pr->selection   = $selection;
+
+        if (array_key_exists('created', $options)) {
+            $pr->created = $options['created'];
+        } else {
+            $pr->created = common_sql_now();
+        }
+
+        if (array_key_exists('uri', $options)) {
+            $pr->uri = $options['uri'];
+        } else {
+            $pr->uri = common_local_url('showpollresponse',
+                                        array('id' => $pr->id));
+        }
+
+        common_log(LOG_DEBUG, "Saving poll response: $pr->id $pr->uri");
+        $pr->insert();
+
+        // TRANS: Notice content voting for a poll.
+        // TRANS: %s is the chosen option in the poll.
+        $content  = sprintf(_m('voted for "%s"'),
+                            $answer);
+        $link = '<a href="' . htmlspecialchars($poll->uri) . '">' . htmlspecialchars($answer) . '</a>';
+        // TRANS: Rendered version of the notice content voting for a poll.
+        // TRANS: %s a link to the poll with the chosen option as link description.
+        $rendered = sprintf(_m('voted for "%s"'), $link);
+
+        $tags    = array();
+
+        $options = array_merge(array('urls' => array(),
+                                     'rendered' => $rendered,
+                                     'tags' => $tags,
+                                     'reply_to' => $poll->getNotice()->id,
+                                     'object_type' => PollPlugin::POLL_RESPONSE_OBJECT),
+                               $options);
+
+        if (!array_key_exists('uri', $options)) {
+            $options['uri'] = $pr->uri;
+        }
+
+        $saved = Notice::saveNew($profile->id,
+                                 $content,
+                                 array_key_exists('source', $options) ?
+                                 $options['source'] : 'web',
+                                 $options);
+
+        return $saved;
+    }
+}
diff --git a/plugins/Poll/classes/User_poll_prefs.php b/plugins/Poll/classes/User_poll_prefs.php
new file mode 100644 (file)
index 0000000..450a6b9
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Data class to record user prefs for polls
+ *
+ * PHP version 5
+ *
+ * @category PollPlugin
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2012, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * For storing the poll prefs
+ *
+ * @category PollPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class User_poll_prefs extends Managed_DataObject
+{
+    public $__table = 'user_poll_prefs'; // table name
+    public $user_id;          // int id
+    public $hide_responses;   // boolean
+    public $created;          // datetime
+    public $modified;         // datetime
+
+    /**
+     * The One True Thingy that must be defined and declared.
+     */
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'Record of user preferences for polls',
+            'fields' => array(
+                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id'),
+                'hide_responses' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'Hide all poll responses'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('user_id')
+        );
+    }
+}
diff --git a/plugins/Poll/forms/newpoll.php b/plugins/Poll/forms/newpoll.php
new file mode 100644 (file)
index 0000000..309125a
--- /dev/null
@@ -0,0 +1,155 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Form for adding a new poll
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  PollPlugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form to add a new poll thingy
+ *
+ * @category  PollPlugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class NewpollForm extends Form
+{
+    protected $question = null;
+    protected $options = array();
+
+    /**
+     * Construct a new poll form
+     *
+     * @param HTMLOutputter $out         output channel
+     *
+     * @return void
+     */
+    function __construct($out=null, $question=null, $options=null)
+    {
+        parent::__construct($out);
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'newpoll-form';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings ajax-notice';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('newpoll');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('fieldset', array('id' => 'newpoll-data'));
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->out->input('question',
+                          // TRANS: Field label on the page to create a poll.
+                          _m('Question'),
+                          $this->question,
+                          // TRANS: Field title on the page to create a poll.
+                          _m('What question are people answering?'));
+        $this->unli();
+
+        $max = 5;
+        if (count($this->options) + 1 > $max) {
+            $max = count($this->options) + 2;
+        }
+        for ($i = 0; $i < $max; $i++) {
+            // @fixme make extensible
+            if (isset($this->options[$i])) {
+                $default = $this->options[$i];
+            } else {
+                $default = '';
+            }
+            $this->li();
+            $this->out->input('poll-option' . ($i + 1),
+                              // TRANS: Field label for an answer option on the page to create a poll.
+                              // TRANS: %d is the option number.
+                              sprintf(_m('Option %d'), $i + 1),
+                              $default,
+                              null,
+                              'option' . ($i + 1));
+            $this->unli();
+        }
+
+        $this->out->elementEnd('ul');
+
+        $toWidget = new ToSelector($this->out,
+                                   common_current_user(),
+                                   null);
+        $toWidget->show();
+
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text for saving a new poll.
+        $this->out->submit('poll-submit', _m('BUTTON', 'Save'), 'submit', 'submit');
+    }
+}
diff --git a/plugins/Poll/forms/pollresponse.php b/plugins/Poll/forms/pollresponse.php
new file mode 100644 (file)
index 0000000..31e8db9
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Form for adding a new poll
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  PollPlugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form to add a new poll thingy
+ *
+ * @category  PollPlugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class PollResponseForm extends Form
+{
+    protected $poll;
+
+    /**
+     * Construct a new poll form
+     *
+     * @param Poll $poll
+     * @param HTMLOutputter $out         output channel
+     *
+     * @return void
+     */
+    function __construct(Poll $poll, HTMLOutputter $out)
+    {
+        parent::__construct($out);
+        $this->poll = $poll;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'pollresponse-form';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings ajax';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('respondpoll', array('id' => $this->poll->id));
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $poll = $this->poll;
+        $out = $this->out;
+        $id = "poll-" . $poll->id;
+
+        $out->element('p', 'poll-question', $poll->question);
+        $out->elementStart('ul', 'poll-options');
+        foreach ($poll->getOptions() as $i => $opt) {
+            $out->elementStart('li');
+            $out->elementStart('label');
+            $out->element('input', array('type' => 'radio', 'name' => 'pollselection', 'value' => $i + 1), '');
+            $out->text(' ' . $opt);
+            $out->elementEnd('label');
+            $out->elementEnd('li');
+        }
+        $out->elementEnd('ul');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text for submitting a poll response.
+        $this->out->submit('poll-response-submit', _m('BUTTON', 'Submit'), 'submit', 'submit');
+    }
+}
diff --git a/plugins/Poll/forms/pollresult.php b/plugins/Poll/forms/pollresult.php
new file mode 100644 (file)
index 0000000..0701482
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Form for adding a new poll
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  PollPlugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form to add a new poll thingy
+ *
+ * @category  PollPlugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class PollResultForm extends Form
+{
+    protected $poll;
+
+    /**
+     * Construct a new poll form
+     *
+     * @param Poll $poll
+     * @param HTMLOutputter $out         output channel
+     *
+     * @return void
+     */
+    function __construct(Poll $poll, HTMLOutputter $out)
+    {
+        parent::__construct($out);
+        $this->poll = $poll;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'pollresult-form';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings ajax';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('respondpoll', array('id' => $this->poll->id));
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $poll = $this->poll;
+        $out = $this->out;
+        $counts = $poll->countResponses();
+
+        $width = 200;
+        $max = max($counts);
+        if ($max == 0) {
+            $max = 1; // quick hack :D
+        }
+
+        $out->element('p', 'poll-question', $poll->question);
+        $out->elementStart('table', 'poll-results');
+        foreach ($poll->getOptions() as $i => $opt) {
+            $w = intval($counts[$i] * $width / $max) + 1;
+
+            $out->elementStart('tr');
+
+            $out->elementStart('td');
+            $out->text($opt);
+            $out->elementEnd('td');
+
+            $out->elementStart('td');
+            $out->element('span', array('class' => 'poll-block',
+                                       'style' => "width: {$w}px"),
+                                  "\xc2\xa0"); // nbsp
+            $out->text($counts[$i]);
+            $out->elementEnd('td');
+
+            $out->elementEnd('tr');
+        }
+        $out->elementEnd('table');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+    }
+}
diff --git a/plugins/Poll/newpoll.php b/plugins/Poll/newpoll.php
deleted file mode 100644 (file)
index e7d5d3d..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Add a new Poll
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Poll
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Add a new Poll
- *
- * @category  Poll
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class NewPollAction extends Action
-{
-    protected $user        = null;
-    protected $error       = null;
-    protected $complete    = null;
-
-    protected $question    = null;
-    protected $options     = array();
-
-    /**
-     * Returns the title of the action
-     *
-     * @return string Action title
-     */
-    function title()
-    {
-        // TRANS: Title for poll page.
-        return _m('New poll');
-    }
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        $this->user = common_current_user();
-
-        if (empty($this->user)) {
-            // TRANS: Client exception thrown trying to create a poll while not logged in.
-            throw new ClientException(_m('You must be logged in to post a poll.'),
-                                      403);
-        }
-
-        if ($this->isPost()) {
-            $this->checkSessionToken();
-        }
-
-        $this->question = $this->trimmed('question');
-        for ($i = 1; $i < 20; $i++) {
-            $opt = $this->trimmed('option' . $i);
-            if ($opt != '') {
-                $this->options[] = $opt;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        parent::handle($argarray);
-
-        if ($this->isPost()) {
-            $this->newPoll();
-        } else {
-            $this->showPage();
-        }
-
-        return;
-    }
-
-    /**
-     * Add a new Poll
-     *
-     * @return void
-     */
-    function newPoll()
-    {
-        if ($this->boolean('ajax')) {
-            StatusNet::setApi(true);
-        }
-        try {
-            if (empty($this->question)) {
-            // TRANS: Client exception thrown trying to create a poll without a question.
-                throw new ClientException(_m('Poll must have a question.'));
-            }
-
-            if (count($this->options) < 2) {
-                // TRANS: Client exception thrown trying to create a poll with fewer than two options.
-                throw new ClientException(_m('Poll must have at least two options.'));
-            }
-
-            // Notice options; distinct from choices for the poll
-
-            $options = array();
-
-            // Does the heavy-lifting for getting "To:" information
-
-            ToSelector::fillOptions($this, $options);
-
-            $saved = Poll::saveNew($this->user->getProfile(),
-                                   $this->question,
-                                   $this->options,
-                                   $options);
-
-        } catch (ClientException $ce) {
-            $this->error = $ce->getMessage();
-            $this->showPage();
-            return;
-        }
-
-        if ($this->boolean('ajax')) {
-            header('Content-Type: text/xml;charset=utf-8');
-            $this->xw->startDocument('1.0', 'UTF-8');
-            $this->elementStart('html');
-            $this->elementStart('head');
-            // TRANS: Page title after sending a notice.
-            $this->element('title', null, _m('Notice posted'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $this->showNotice($saved);
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            common_redirect($saved->bestUrl(), 303);
-        }
-    }
-
-    /**
-     * Output a notice
-     *
-     * Used to generate the notice code for Ajax results.
-     *
-     * @param Notice $notice Notice that was saved
-     *
-     * @return void
-     */
-    function showNotice($notice)
-    {
-        class_exists('NoticeList'); // @fixme hack for autoloader
-        $nli = new NoticeListItem($notice, $this);
-        $nli->show();
-    }
-
-    /**
-     * Show the Poll form
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        if (!empty($this->error)) {
-            $this->element('p', 'error', $this->error);
-        }
-
-        $form = new NewPollForm($this,
-                                 $this->question,
-                                 $this->options);
-
-        $form->show();
-
-        return;
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
-            $_SERVER['REQUEST_METHOD'] == 'HEAD') {
-            return true;
-        } else {
-            return false;
-        }
-    }
-}
diff --git a/plugins/Poll/newpollform.php b/plugins/Poll/newpollform.php
deleted file mode 100644 (file)
index 309125a..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Form for adding a new poll
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  PollPlugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form to add a new poll thingy
- *
- * @category  PollPlugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class NewpollForm extends Form
-{
-    protected $question = null;
-    protected $options = array();
-
-    /**
-     * Construct a new poll form
-     *
-     * @param HTMLOutputter $out         output channel
-     *
-     * @return void
-     */
-    function __construct($out=null, $question=null, $options=null)
-    {
-        parent::__construct($out);
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'newpoll-form';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings ajax-notice';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('newpoll');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('fieldset', array('id' => 'newpoll-data'));
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-        $this->out->input('question',
-                          // TRANS: Field label on the page to create a poll.
-                          _m('Question'),
-                          $this->question,
-                          // TRANS: Field title on the page to create a poll.
-                          _m('What question are people answering?'));
-        $this->unli();
-
-        $max = 5;
-        if (count($this->options) + 1 > $max) {
-            $max = count($this->options) + 2;
-        }
-        for ($i = 0; $i < $max; $i++) {
-            // @fixme make extensible
-            if (isset($this->options[$i])) {
-                $default = $this->options[$i];
-            } else {
-                $default = '';
-            }
-            $this->li();
-            $this->out->input('poll-option' . ($i + 1),
-                              // TRANS: Field label for an answer option on the page to create a poll.
-                              // TRANS: %d is the option number.
-                              sprintf(_m('Option %d'), $i + 1),
-                              $default,
-                              null,
-                              'option' . ($i + 1));
-            $this->unli();
-        }
-
-        $this->out->elementEnd('ul');
-
-        $toWidget = new ToSelector($this->out,
-                                   common_current_user(),
-                                   null);
-        $toWidget->show();
-
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text for saving a new poll.
-        $this->out->submit('poll-submit', _m('BUTTON', 'Save'), 'submit', 'submit');
-    }
-}
diff --git a/plugins/Poll/pollresponseform.php b/plugins/Poll/pollresponseform.php
deleted file mode 100644 (file)
index 31e8db9..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Form for adding a new poll
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  PollPlugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form to add a new poll thingy
- *
- * @category  PollPlugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class PollResponseForm extends Form
-{
-    protected $poll;
-
-    /**
-     * Construct a new poll form
-     *
-     * @param Poll $poll
-     * @param HTMLOutputter $out         output channel
-     *
-     * @return void
-     */
-    function __construct(Poll $poll, HTMLOutputter $out)
-    {
-        parent::__construct($out);
-        $this->poll = $poll;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'pollresponse-form';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings ajax';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('respondpoll', array('id' => $this->poll->id));
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $poll = $this->poll;
-        $out = $this->out;
-        $id = "poll-" . $poll->id;
-
-        $out->element('p', 'poll-question', $poll->question);
-        $out->elementStart('ul', 'poll-options');
-        foreach ($poll->getOptions() as $i => $opt) {
-            $out->elementStart('li');
-            $out->elementStart('label');
-            $out->element('input', array('type' => 'radio', 'name' => 'pollselection', 'value' => $i + 1), '');
-            $out->text(' ' . $opt);
-            $out->elementEnd('label');
-            $out->elementEnd('li');
-        }
-        $out->elementEnd('ul');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text for submitting a poll response.
-        $this->out->submit('poll-response-submit', _m('BUTTON', 'Submit'), 'submit', 'submit');
-    }
-}
diff --git a/plugins/Poll/pollresultform.php b/plugins/Poll/pollresultform.php
deleted file mode 100644 (file)
index 0701482..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Form for adding a new poll
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  PollPlugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form to add a new poll thingy
- *
- * @category  PollPlugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class PollResultForm extends Form
-{
-    protected $poll;
-
-    /**
-     * Construct a new poll form
-     *
-     * @param Poll $poll
-     * @param HTMLOutputter $out         output channel
-     *
-     * @return void
-     */
-    function __construct(Poll $poll, HTMLOutputter $out)
-    {
-        parent::__construct($out);
-        $this->poll = $poll;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'pollresult-form';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings ajax';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('respondpoll', array('id' => $this->poll->id));
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $poll = $this->poll;
-        $out = $this->out;
-        $counts = $poll->countResponses();
-
-        $width = 200;
-        $max = max($counts);
-        if ($max == 0) {
-            $max = 1; // quick hack :D
-        }
-
-        $out->element('p', 'poll-question', $poll->question);
-        $out->elementStart('table', 'poll-results');
-        foreach ($poll->getOptions() as $i => $opt) {
-            $w = intval($counts[$i] * $width / $max) + 1;
-
-            $out->elementStart('tr');
-
-            $out->elementStart('td');
-            $out->text($opt);
-            $out->elementEnd('td');
-
-            $out->elementStart('td');
-            $out->element('span', array('class' => 'poll-block',
-                                       'style' => "width: {$w}px"),
-                                  "\xc2\xa0"); // nbsp
-            $out->text($counts[$i]);
-            $out->elementEnd('td');
-
-            $out->elementEnd('tr');
-        }
-        $out->elementEnd('table');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-    }
-}
diff --git a/plugins/Poll/pollsettings.php b/plugins/Poll/pollsettings.php
deleted file mode 100644 (file)
index b390812..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-<?php
-/**
- * Form to set your personal poll settings
- *
- * StatusNet, the distributed open-source microblogging tool
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Plugins
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2012 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-class PollSettingsAction extends SettingsAction
-{
-    /**
-     * Title of the page
-     *
-     * @return string Page title
-     */
-    function title()
-    {
-        // TRANS: Page title.
-        return _m('Poll settings');
-    }
-
-    /**
-     * Instructions for use
-     *
-     * @return string Instructions for use
-     */
-
-    function getInstructions()
-    {
-        // TRANS: Page instructions.
-        return _m('Set your poll preferences');
-    }
-
-    /**
-     * Show the form for Poll
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        $user = common_current_user();
-
-        $prefs = User_poll_prefs::getKV('user_id', $user->id);
-
-        $form = new PollPrefsForm($this, $prefs);
-
-        $form->show();
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-
-    function handlePost()
-    {
-        $user = common_current_user();
-
-        $upp = User_poll_prefs::getKV('user_id', $user->id);
-        $orig = null;
-
-        if (!empty($upp)) {
-            $orig = clone($upp);
-        } else {
-            $upp = new User_poll_prefs();
-            $upp->user_id = $user->id;
-            $upp->created = common_sql_now();
-        }
-
-        $upp->hide_responses = $this->boolean('hide_responses');
-        $upp->modified       = common_sql_now();
-
-        if (!empty($orig)) {
-            $upp->update($orig);
-        } else {
-            $upp->insert();
-        }
-
-        // TRANS: Confirmation shown when user profile settings are saved.
-        $this->showForm(_('Settings saved.'), true);
-
-        return;
-    }
-}
-
-class PollPrefsForm extends Form
-{
-    var $prefs;
-
-    function __construct($out, $prefs)
-    {
-        parent::__construct($out);
-        $this->prefs = $prefs;
-    }
-
-    /**
-     * Visible or invisible data elements
-     *
-     * Display the form fields that make up the data of the form.
-     * Sub-classes should overload this to show their data.
-     *
-     * @return void
-     */
-
-    function formData()
-    {
-        $this->elementStart('fieldset');
-        $this->elementStart('ul', 'form_data');
-        $this->elementStart('li');
-        $this->checkbox('hide_responses',
-                        _('Do not deliver poll responses to my home timeline'),
-                        (!empty($this->prefs) && $this->prefs->hide_responses));
-        $this->elementEnd('li');
-        $this->elementEnd('ul');
-        $this->elementEnd('fieldset');
-    }
-
-    /**
-     * Buttons for form actions
-     *
-     * Submit and cancel buttons (or whatever)
-     * Sub-classes should overload this to show their own buttons.
-     *
-     * @return void
-     */
-
-    function formActions()
-    {
-        $this->submit('submit', _('Save'));
-    }
-
-    /**
-     * ID of the form
-     *
-     * Should be unique on the page. Sub-classes should overload this
-     * to show their own IDs.
-     *
-     * @return int ID of the form
-     */
-
-    function id()
-    {
-        return 'form_poll_prefs';
-    }
-
-    /**
-     * Action of the form.
-     *
-     * URL to post to. Should be overloaded by subclasses to give
-     * somewhere to post to.
-     *
-     * @return string URL to post to
-     */
-
-    function action()
-    {
-        return common_local_url('pollsettings');
-    }
-
-    /**
-     * Class of the form. May include space-separated list of multiple classes.
-     *
-     * @return string the form's class
-     */
-
-    function formClass()
-    {
-        return 'form_settings';
-    }
-}
diff --git a/plugins/Poll/respondpoll.php b/plugins/Poll/respondpoll.php
deleted file mode 100644 (file)
index df3f68e..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Respond to a Poll
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Poll
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Respond to a Poll
- *
- * @category  Poll
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class RespondPollAction extends Action
-{
-    protected $user        = null;
-    protected $error       = null;
-    protected $complete    = null;
-
-    protected $poll        = null;
-    protected $selection   = null;
-
-    /**
-     * Returns the title of the action
-     *
-     * @return string Action title
-     */
-    function title()
-    {
-        // TRANS: Page title for poll response.
-        return _m('Poll response');
-    }
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-        if ($this->boolean('ajax')) {
-            StatusNet::setApi(true);
-        }
-
-        $this->user = common_current_user();
-
-        if (empty($this->user)) {
-            // TRANS: Client exception thrown trying to respond to a poll while not logged in.
-            throw new ClientException(_m('You must be logged in to respond to a poll.'),
-                                      403);
-        }
-
-        if ($this->isPost()) {
-            $this->checkSessionToken();
-        }
-
-        $id = $this->trimmed('id');
-        $this->poll = Poll::getKV('id', $id);
-        if (empty($this->poll)) {
-            // TRANS: Client exception thrown trying to respond to a non-existing poll.
-            throw new ClientException(_m('Invalid or missing poll.'), 404);
-        }
-
-        $selection = intval($this->trimmed('pollselection'));
-        if ($selection < 1 || $selection > count($this->poll->getOptions())) {
-            // TRANS: Client exception thrown responding to a poll with an invalid answer.
-            throw new ClientException(_m('Invalid poll selection.'));
-        }
-        $this->selection = $selection;
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        parent::handle($argarray);
-
-        if ($this->isPost()) {
-            $this->respondPoll();
-        } else {
-            $this->showPage();
-        }
-
-        return;
-    }
-
-    /**
-     * Add a new Poll
-     *
-     * @return void
-     */
-    function respondPoll()
-    {
-        try {
-            $notice = Poll_response::saveNew($this->user->getProfile(),
-                                             $this->poll,
-                                             $this->selection);
-        } catch (ClientException $ce) {
-            $this->error = $ce->getMessage();
-            $this->showPage();
-            return;
-        }
-
-        if ($this->boolean('ajax')) {
-            header('Content-Type: text/xml;charset=utf-8');
-            $this->xw->startDocument('1.0', 'UTF-8');
-            $this->elementStart('html');
-            $this->elementStart('head');
-            // TRANS: Page title after sending a poll response.
-            $this->element('title', null, _m('Poll results'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $form = new PollResultForm($this->poll, $this);
-            $form->show();
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            common_redirect($this->poll->bestUrl(), 303);
-        }
-    }
-
-    /**
-     * Show the Poll form
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        if (!empty($this->error)) {
-            $this->element('p', 'error', $this->error);
-        }
-
-        $form = new PollResponseForm($this->poll, $this);
-
-        $form->show();
-
-        return;
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
-            $_SERVER['REQUEST_METHOD'] == 'HEAD') {
-            return true;
-        } else {
-            return false;
-        }
-    }
-}
diff --git a/plugins/Poll/showpoll.php b/plugins/Poll/showpoll.php
deleted file mode 100644 (file)
index 91f661e..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Show a single Poll
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  PollPlugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Show a single Poll, with associated information
- *
- * @category  PollPlugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class ShowPollAction extends ShownoticeAction
-{
-    protected $poll = null;
-
-    function getNotice()
-    {
-        $this->id = $this->trimmed('id');
-
-        $this->poll = Poll::getKV('id', $this->id);
-
-        if (empty($this->poll)) {
-            // TRANS: Client exception thrown trying to view a non-existing poll.
-            throw new ClientException(_m('No such poll.'), 404);
-        }
-
-        $notice = $this->poll->getNotice();
-
-        if (empty($notice)) {
-            // Did we used to have it, and it got deleted?
-            // TRANS: Client exception thrown trying to view a non-existing poll notice.
-            throw new ClientException(_m('No such poll notice.'), 404);
-        }
-
-        return $notice;
-    }
-
-    /**
-     * Title of the page
-     *
-     * Used by Action class for layout.
-     *
-     * @return string page tile
-     */
-    function title()
-    {
-        // TRANS: Page title for a poll.
-        // TRANS: %1$s is the nickname of the user that created the poll, %2$s is the poll question.
-        return sprintf(_m('%1$s\'s poll: %2$s'),
-                       $this->user->nickname,
-                       $this->poll->question);
-    }
-
-    /**
-     * @fixme combine the notice time with poll update time
-     */
-    function lastModified()
-    {
-        return Action::lastModified();
-    }
-
-
-    /**
-     * @fixme combine the notice time with poll update time
-     */
-    function etag()
-    {
-        return Action::etag();
-    }
-}
index ce9353638849aeff74c7f735aa698bf6f494e5f8..37cfcb9be3044e0b2d16e557c93a7fdb3e542d83 100644 (file)
@@ -65,48 +65,6 @@ class QnAPlugin extends MicroAppPlugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'QnanewquestionAction':
-        case 'QnanewanswerAction':
-        case 'QnashowquestionAction':
-        case 'QnaclosequestionAction':
-        case 'QnashowanswerAction':
-        case 'QnareviseanswerAction':
-        case 'QnavoteAction':
-            include_once $dir . '/actions/'
-                . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'QnanewquestionForm':
-        case 'QnashowquestionForm':
-        case 'QnanewanswerForm':
-        case 'QnashowanswerForm':
-        case 'QnareviseanswerForm':
-        case 'QnavoteForm':
-            include_once $dir . '/lib/' . strtolower($cls).'.php';
-            break;
-        case 'QnA_Question':
-        case 'QnA_Answer':
-        case 'QnA_Vote':
-            include_once $dir . '/classes/' . $cls.'.php';
-            return false;
-            break;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Map URLs to actions
      *
diff --git a/plugins/QnA/forms/qnanewanswer.php b/plugins/QnA/forms/qnanewanswer.php
new file mode 100644 (file)
index 0000000..c0ae220
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Form for answering a question
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  QnA
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form to add a new answer to a question
+ *
+ * @category  QnA
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class QnanewanswerForm extends Form
+{
+    protected $question;
+    protected $showQuestion;
+
+    /**
+     * Construct a new answer form
+     *
+     * @param QnA_Question $question
+     * @param HTMLOutputter $out output channel
+     *
+     * @return void
+     */
+    function __construct(HTMLOutputter $out, QnA_Question $question, $showQuestion = false)
+    {
+        parent::__construct($out);
+        $this->question = $question;
+        $this->showQuestion = $showQuestion;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'answer-form';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings qna_answer_form ajax-notice';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('qnanewanswer');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $question = $this->question;
+        $out      = $this->out;
+        $id       = "question-" . $question->id;
+
+        if ($this->showQuestion) {
+            $out->raw($this->question->asHTML());
+        }
+
+        $out->hidden('qna-question-id', $id, 'id');
+        // TRANS: Field label.
+        $out->textarea('qna-answer', _m('Enter your answer'), null, null, 'answer');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text for submitting a poll response.
+        $this->out->submit('qna-answer-submit', _m('BUTTON', 'Answer'), 'submit', 'submit');
+    }
+}
diff --git a/plugins/QnA/forms/qnanewquestion.php b/plugins/QnA/forms/qnanewquestion.php
new file mode 100644 (file)
index 0000000..a1a2a94
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Form for adding a new question
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  QnA
+ * @package   StatusNet
+ * @author    Zach Copley <zach@copley.name>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form to add a new question
+ *
+ * @category  QnA
+ * @package   StatusNet
+ * @author    Zach Copley <zach@copley.name>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class QnanewquestionForm extends Form
+{
+    protected $title;
+    protected $description;
+
+    /**
+     * Construct a new question form
+     *
+     * @param HTMLOutputter $out output channel
+     *
+     * @return void
+     */
+    function __construct($out = null, $title = null, $description = null, $options = null)
+    {
+        parent::__construct($out);
+        $this->title       = $title;
+        $this->description = $description;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'newquestion-form';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings ajax-notice';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('qnanewquestion');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('fieldset', array('id' => 'newquestion-data'));
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->out->input(
+            'qna-question-title',
+            // TRANS: Field label for a new question.
+            _m('LABEL','Title'),
+            $this->title,
+            // TRANS: Field title for a new question.
+            _m('The title of your question.'),
+            'title'
+        );
+        $this->unli();
+        $this->li();
+        $this->out->textarea(
+            'qna-question-description',
+            // TRANS: Field label for question details.
+            _m('LABEL','Description'),
+            $this->description,
+            // TRANS: Field title for question details.
+            _m('Your question in detail.'),
+            'description'
+        );
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+        $toWidget = new ToSelector(
+            $this->out,
+            common_current_user(),
+            null
+        );
+        $toWidget->show();
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text for saving a new question.
+        $this->out->submit('qna-question-submit', _m('BUTTON', 'Save'), 'submit', 'submit');
+    }
+}
diff --git a/plugins/QnA/forms/qnareviseanswer.php b/plugins/QnA/forms/qnareviseanswer.php
new file mode 100644 (file)
index 0000000..bf20895
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Form for revising a question
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  QnA
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form to revise a question
+ *
+ * @category  QnA
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class QnareviseanswerForm extends Form
+{
+    protected $question;
+    protected $answer;
+
+    /**
+     * Construct a new answer form
+     *
+     * @param QnA_Answer $answer
+     * @param HTMLOutputter $out output channel
+     *
+     * @return void
+     */
+    function __construct(QnA_Answer $answer, HTMLOutputter $out)
+    {
+        parent::__construct($out);
+        $this->question = $answer->getQuestion();
+        $this->answer   = $answer;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'answered-form';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings ajax';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('qnareviseanswer');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $out = $this->out;
+        $out->element('p', 'revise-answer', 'Your answer');
+        $id = "answer-" . $this->answer->id;
+        $out->hidden('id', $id);
+        $out->textarea('answer', 'answer', $this->answer->content);
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text for submitting a revised answer.
+        $this->out->submit('submit', _m('BUTTON', 'Submit'));
+    }
+}
diff --git a/plugins/QnA/forms/qnashowanswer.php b/plugins/QnA/forms/qnashowanswer.php
new file mode 100644 (file)
index 0000000..8ca573c
--- /dev/null
@@ -0,0 +1,190 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for showing / revising an answer
+ *
+ * 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  Form
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/lib/form.php';
+
+/**
+ * Form for showing / revising an answer
+ *
+ * @category Form
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ */
+class QnashowanswerForm extends Form
+{
+    /**
+     * The answer to show
+     */
+    protected $answer   = null;
+
+    /**
+     * The question this is an answer to
+     */
+    protected $question = null;
+
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out    output channel
+     * @param QnA_Answer    $answer answer to revise
+     */
+    function __construct($out = null, $answer = null)
+    {
+        parent::__construct($out);
+
+        $this->answer   = $answer;
+        $this->question = $answer->getQuestion();
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'show-' . $this->answer->id;
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('qnareviseanswer');
+    }
+
+    /**
+     * Include a session token for CSRF protection
+     *
+     * @return void
+     */
+    function sessionToken()
+    {
+        $this->out->hidden(
+            'token',
+            common_session_token()
+        );
+    }
+
+    /**
+     * Legend of the Form
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+        // TRANS: Form legend for showing the answer.
+        $this->out->element('legend', null, _m('Answer'));
+    }
+
+    /**
+     * Data elements
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->hidden(
+            'qna-answer-id',
+            'answer-' . $this->answer->id,
+            'id'
+        );
+
+        $this->out->raw($this->answer->asHTML());
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        $user = common_current_user();
+        if (empty($user)) {
+            return;
+        }
+
+        if (empty($this->question->closed)) {
+            if ($user->id == $this->question->profile_id) {
+                if (empty($this->answer->best)) {
+                    $this->out->submit(
+                        'qna-best-answer',
+                        // TRANS: Button text for marking an answer as "best".
+                        _m('BUTTON', 'Best'),
+                        'submit',
+                        'best',
+                        // TRANS: Title for button text marking an answer as "best".
+                        _m('Mark this answer as the best answer.')
+                    );
+
+                }
+            }
+
+            /*
+             * @todo FIXME: Revise is disabled until we figure out the
+             *         Ostatus bits This comment is just a reminder
+             *         that the UI for this works.
+             */
+            /*
+            if ($user->id == $this->answer->profile_id) {
+                $this->out->submit(
+                    'revise',
+                    // TRANS: Button text for revising an answer.
+                    _m('BUTTON', 'Revise'),
+                    'submit',
+                    null,
+                    // TRANS: Title for button text for revising an answer.
+                    _m('Revise your answer.')
+                );
+            }
+             */
+        }
+    }
+
+    /**
+     * Class of the form.
+     *
+     * @return string the form's class
+     */
+    function formClass()
+    {
+        return 'form_answer_show ajax';
+    }
+}
diff --git a/plugins/QnA/forms/qnashowquestion.php b/plugins/QnA/forms/qnashowquestion.php
new file mode 100644 (file)
index 0000000..0ac1c82
--- /dev/null
@@ -0,0 +1,170 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for showing / revising an answer
+ *
+ * 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  Form
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/lib/form.php';
+
+/**
+ * Form for showing a question
+ *
+ * @category Form
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ */
+class QnashowquestionForm extends Form
+{
+    /**
+     * The question to show
+     */
+    var $question = null;
+
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out      output channel
+     * @param QnA_Question  $question the question to show
+     */
+    function __construct($out = null, $question = null)
+    {
+        parent::__construct($out);
+        $this->question = $question;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'question-' . $this->question->id;
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('qnaclosequestion');
+    }
+
+    /**
+     * Include a session token for CSRF protection
+     *
+     * @return void
+     */
+    function sessionToken()
+    {
+        $this->out->hidden(
+            'token',
+            common_session_token()
+        );
+    }
+
+    /**
+     * Legend of the Form
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+        // TRANS: Form legend for revising the answer.
+        $this->out->element('legend', null, _m('LEGEND','Question'));
+    }
+
+    /**
+     * Data elements
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->hidden(
+            'qna-question-id',
+            'question-' . $this->question->id,
+            'id'
+        );
+
+        $this->out->hidden(
+            'answer-action',
+            common_local_url(
+                'qnanewanswer',
+                null,
+                array('id' => 'question-' . $this->question->id)
+            )
+        );
+
+        $this->out->raw($this->question->asHTML());
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        $user = common_current_user();
+        if (empty($user)) {
+            return;
+        }
+
+        if (empty($this->question->closed)) {
+            if ($user->id == $this->question->profile_id) {
+             $this->out->submit(
+                'qna-question-close',
+                // TRANS: Button text for closing a question.
+                _m('BUTTON', 'Close'),
+                'submit',
+                'submit',
+                // TRANS: Title for button text for closing a question.
+                _m('Close the question to no one can answer it anymore.')
+             );
+            }
+        }
+    }
+
+    /**
+     * Class of the form.
+     *
+     * @return string the form's class
+     */
+    function formClass()
+    {
+        return 'form_question_show ajax';
+    }
+}
diff --git a/plugins/QnA/forms/qnavote.php b/plugins/QnA/forms/qnavote.php
new file mode 100644 (file)
index 0000000..ac13ea4
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Form for answering a question
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  QnA
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Form to add a new answer to a question
+ *
+ * @category  QnA
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class QnavoteForm extends Form
+{
+    protected $question;
+
+    /**
+     * Construct a new answer form
+     *
+     * @param QnA_Question $question
+     * @param HTMLOutputter $out output channel
+     *
+     * @return void
+     */
+    function __construct(QnA_Question $question, HTMLOutputter $out)
+    {
+        parent::__construct($out);
+        $this->question = $question;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'answer-form';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings ajax';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('qnavote', array('id' => $this->question->id));
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $question = $this->question;
+        $out      = $this->out;
+        $id       = "question-" . $question->id;
+
+        $out->element('p', 'answer', $question->question);
+        $out->element('input', array('type' => 'text', 'name' => 'vote'));
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text for submitting a poll response.
+        $this->out->submit('submit', _m('BUTTON', 'Submit'));
+    }
+}
diff --git a/plugins/QnA/lib/qnanewanswerform.php b/plugins/QnA/lib/qnanewanswerform.php
deleted file mode 100644 (file)
index c0ae220..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Form for answering a question
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  QnA
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form to add a new answer to a question
- *
- * @category  QnA
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class QnanewanswerForm extends Form
-{
-    protected $question;
-    protected $showQuestion;
-
-    /**
-     * Construct a new answer form
-     *
-     * @param QnA_Question $question
-     * @param HTMLOutputter $out output channel
-     *
-     * @return void
-     */
-    function __construct(HTMLOutputter $out, QnA_Question $question, $showQuestion = false)
-    {
-        parent::__construct($out);
-        $this->question = $question;
-        $this->showQuestion = $showQuestion;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'answer-form';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings qna_answer_form ajax-notice';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('qnanewanswer');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $question = $this->question;
-        $out      = $this->out;
-        $id       = "question-" . $question->id;
-
-        if ($this->showQuestion) {
-            $out->raw($this->question->asHTML());
-        }
-
-        $out->hidden('qna-question-id', $id, 'id');
-        // TRANS: Field label.
-        $out->textarea('qna-answer', _m('Enter your answer'), null, null, 'answer');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text for submitting a poll response.
-        $this->out->submit('qna-answer-submit', _m('BUTTON', 'Answer'), 'submit', 'submit');
-    }
-}
diff --git a/plugins/QnA/lib/qnanewquestionform.php b/plugins/QnA/lib/qnanewquestionform.php
deleted file mode 100644 (file)
index a1a2a94..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Form for adding a new question
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  QnA
- * @package   StatusNet
- * @author    Zach Copley <zach@copley.name>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form to add a new question
- *
- * @category  QnA
- * @package   StatusNet
- * @author    Zach Copley <zach@copley.name>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class QnanewquestionForm extends Form
-{
-    protected $title;
-    protected $description;
-
-    /**
-     * Construct a new question form
-     *
-     * @param HTMLOutputter $out output channel
-     *
-     * @return void
-     */
-    function __construct($out = null, $title = null, $description = null, $options = null)
-    {
-        parent::__construct($out);
-        $this->title       = $title;
-        $this->description = $description;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'newquestion-form';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings ajax-notice';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('qnanewquestion');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('fieldset', array('id' => 'newquestion-data'));
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-        $this->out->input(
-            'qna-question-title',
-            // TRANS: Field label for a new question.
-            _m('LABEL','Title'),
-            $this->title,
-            // TRANS: Field title for a new question.
-            _m('The title of your question.'),
-            'title'
-        );
-        $this->unli();
-        $this->li();
-        $this->out->textarea(
-            'qna-question-description',
-            // TRANS: Field label for question details.
-            _m('LABEL','Description'),
-            $this->description,
-            // TRANS: Field title for question details.
-            _m('Your question in detail.'),
-            'description'
-        );
-        $this->unli();
-
-        $this->out->elementEnd('ul');
-        $toWidget = new ToSelector(
-            $this->out,
-            common_current_user(),
-            null
-        );
-        $toWidget->show();
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text for saving a new question.
-        $this->out->submit('qna-question-submit', _m('BUTTON', 'Save'), 'submit', 'submit');
-    }
-}
diff --git a/plugins/QnA/lib/qnareviseanswerform.php b/plugins/QnA/lib/qnareviseanswerform.php
deleted file mode 100644 (file)
index bf20895..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Form for revising a question
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  QnA
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form to revise a question
- *
- * @category  QnA
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class QnareviseanswerForm extends Form
-{
-    protected $question;
-    protected $answer;
-
-    /**
-     * Construct a new answer form
-     *
-     * @param QnA_Answer $answer
-     * @param HTMLOutputter $out output channel
-     *
-     * @return void
-     */
-    function __construct(QnA_Answer $answer, HTMLOutputter $out)
-    {
-        parent::__construct($out);
-        $this->question = $answer->getQuestion();
-        $this->answer   = $answer;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'answered-form';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings ajax';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('qnareviseanswer');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $out = $this->out;
-        $out->element('p', 'revise-answer', 'Your answer');
-        $id = "answer-" . $this->answer->id;
-        $out->hidden('id', $id);
-        $out->textarea('answer', 'answer', $this->answer->content);
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text for submitting a revised answer.
-        $this->out->submit('submit', _m('BUTTON', 'Submit'));
-    }
-}
diff --git a/plugins/QnA/lib/qnashowanswerform.php b/plugins/QnA/lib/qnashowanswerform.php
deleted file mode 100644 (file)
index 8ca573c..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Form for showing / revising an answer
- *
- * 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  Form
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/lib/form.php';
-
-/**
- * Form for showing / revising an answer
- *
- * @category Form
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- */
-class QnashowanswerForm extends Form
-{
-    /**
-     * The answer to show
-     */
-    protected $answer   = null;
-
-    /**
-     * The question this is an answer to
-     */
-    protected $question = null;
-
-    /**
-     * Constructor
-     *
-     * @param HTMLOutputter $out    output channel
-     * @param QnA_Answer    $answer answer to revise
-     */
-    function __construct($out = null, $answer = null)
-    {
-        parent::__construct($out);
-
-        $this->answer   = $answer;
-        $this->question = $answer->getQuestion();
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'show-' . $this->answer->id;
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('qnareviseanswer');
-    }
-
-    /**
-     * Include a session token for CSRF protection
-     *
-     * @return void
-     */
-    function sessionToken()
-    {
-        $this->out->hidden(
-            'token',
-            common_session_token()
-        );
-    }
-
-    /**
-     * Legend of the Form
-     *
-     * @return void
-     */
-    function formLegend()
-    {
-        // TRANS: Form legend for showing the answer.
-        $this->out->element('legend', null, _m('Answer'));
-    }
-
-    /**
-     * Data elements
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->hidden(
-            'qna-answer-id',
-            'answer-' . $this->answer->id,
-            'id'
-        );
-
-        $this->out->raw($this->answer->asHTML());
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        $user = common_current_user();
-        if (empty($user)) {
-            return;
-        }
-
-        if (empty($this->question->closed)) {
-            if ($user->id == $this->question->profile_id) {
-                if (empty($this->answer->best)) {
-                    $this->out->submit(
-                        'qna-best-answer',
-                        // TRANS: Button text for marking an answer as "best".
-                        _m('BUTTON', 'Best'),
-                        'submit',
-                        'best',
-                        // TRANS: Title for button text marking an answer as "best".
-                        _m('Mark this answer as the best answer.')
-                    );
-
-                }
-            }
-
-            /*
-             * @todo FIXME: Revise is disabled until we figure out the
-             *         Ostatus bits This comment is just a reminder
-             *         that the UI for this works.
-             */
-            /*
-            if ($user->id == $this->answer->profile_id) {
-                $this->out->submit(
-                    'revise',
-                    // TRANS: Button text for revising an answer.
-                    _m('BUTTON', 'Revise'),
-                    'submit',
-                    null,
-                    // TRANS: Title for button text for revising an answer.
-                    _m('Revise your answer.')
-                );
-            }
-             */
-        }
-    }
-
-    /**
-     * Class of the form.
-     *
-     * @return string the form's class
-     */
-    function formClass()
-    {
-        return 'form_answer_show ajax';
-    }
-}
diff --git a/plugins/QnA/lib/qnashowquestionform.php b/plugins/QnA/lib/qnashowquestionform.php
deleted file mode 100644 (file)
index 0ac1c82..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Form for showing / revising an answer
- *
- * 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  Form
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/lib/form.php';
-
-/**
- * Form for showing a question
- *
- * @category Form
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- */
-class QnashowquestionForm extends Form
-{
-    /**
-     * The question to show
-     */
-    var $question = null;
-
-    /**
-     * Constructor
-     *
-     * @param HTMLOutputter $out      output channel
-     * @param QnA_Question  $question the question to show
-     */
-    function __construct($out = null, $question = null)
-    {
-        parent::__construct($out);
-        $this->question = $question;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'question-' . $this->question->id;
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('qnaclosequestion');
-    }
-
-    /**
-     * Include a session token for CSRF protection
-     *
-     * @return void
-     */
-    function sessionToken()
-    {
-        $this->out->hidden(
-            'token',
-            common_session_token()
-        );
-    }
-
-    /**
-     * Legend of the Form
-     *
-     * @return void
-     */
-    function formLegend()
-    {
-        // TRANS: Form legend for revising the answer.
-        $this->out->element('legend', null, _m('LEGEND','Question'));
-    }
-
-    /**
-     * Data elements
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->hidden(
-            'qna-question-id',
-            'question-' . $this->question->id,
-            'id'
-        );
-
-        $this->out->hidden(
-            'answer-action',
-            common_local_url(
-                'qnanewanswer',
-                null,
-                array('id' => 'question-' . $this->question->id)
-            )
-        );
-
-        $this->out->raw($this->question->asHTML());
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        $user = common_current_user();
-        if (empty($user)) {
-            return;
-        }
-
-        if (empty($this->question->closed)) {
-            if ($user->id == $this->question->profile_id) {
-             $this->out->submit(
-                'qna-question-close',
-                // TRANS: Button text for closing a question.
-                _m('BUTTON', 'Close'),
-                'submit',
-                'submit',
-                // TRANS: Title for button text for closing a question.
-                _m('Close the question to no one can answer it anymore.')
-             );
-            }
-        }
-    }
-
-    /**
-     * Class of the form.
-     *
-     * @return string the form's class
-     */
-    function formClass()
-    {
-        return 'form_question_show ajax';
-    }
-}
diff --git a/plugins/QnA/lib/qnavoteform.php b/plugins/QnA/lib/qnavoteform.php
deleted file mode 100644 (file)
index ac13ea4..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Form for answering a question
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  QnA
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Form to add a new answer to a question
- *
- * @category  QnA
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class QnavoteForm extends Form
-{
-    protected $question;
-
-    /**
-     * Construct a new answer form
-     *
-     * @param QnA_Question $question
-     * @param HTMLOutputter $out output channel
-     *
-     * @return void
-     */
-    function __construct(QnA_Question $question, HTMLOutputter $out)
-    {
-        parent::__construct($out);
-        $this->question = $question;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'answer-form';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings ajax';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('qnavote', array('id' => $this->question->id));
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $question = $this->question;
-        $out      = $this->out;
-        $id       = "question-" . $question->id;
-
-        $out->element('p', 'answer', $question->question);
-        $out->element('input', array('type' => 'text', 'name' => 'vote'));
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text for submitting a poll response.
-        $this->out->submit('submit', _m('BUTTON', 'Submit'));
-    }
-}
diff --git a/plugins/RSSCloud/LoggingAggregator.php b/plugins/RSSCloud/LoggingAggregator.php
deleted file mode 100644 (file)
index 824fa9e..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-/**
- * This test class pretends to be an RSS aggregator. It logs notifications
- * from the cloud.
- *
- * PHP version 5
- *
- * @category Plugin
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Dummy aggregator that acts as a proper notification handler. It
- * doesn't do anything but respond correctly when notified via
- * REST.  Mostly, this is just and action I used to develop the plugin
- * and easily test things end-to-end. I'm leaving it in here as it
- * may be useful for developing the plugin further.
- *
- * @category Plugin
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class LoggingAggregatorAction extends Action
-{
-    var $challenge = null;
-    var $url       = null;
-
-    /**
-     * Initialization.
-     *
-     * @param array $args Web and URL arguments
-     *
-     * @return boolean false if user doesn't exist
-     */
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $this->url       = $this->arg('url');
-        $this->challenge = $this->arg('challenge');
-
-        common_debug("args = " . var_export($this->args, true));
-        common_debug('url = ' . $this->url . ' challenge = ' . $this->challenge);
-
-        return true;
-    }
-
-    /**
-     * Handle the request
-     *
-     * @param array $args $_REQUEST data (unused)
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-
-        if (empty($this->url)) {
-            // TRANS: Form validation error displayed when a URL parameter is missing.
-            $this->showError(_m('A URL parameter is required.'));
-            return;
-        }
-
-        if (!empty($this->challenge)) {
-            // must be a GET
-            if ($_SERVER['REQUEST_METHOD'] != 'GET') {
-                // TRANS: Form validation error displayed when HTTP GET is not used.
-                $this->showError(_m('This resource requires an HTTP GET.'));
-                return;
-            }
-
-            header('Content-Type: text/xml');
-            echo $this->challenge;
-        } else {
-            // must be a POST
-            if ($_SERVER['REQUEST_METHOD'] != 'POST') {
-                // TRANS: Form validation error displayed when HTTP POST is not used.
-                $this->showError(_m('This resource requires an HTTP POST.'));
-                return;
-            }
-
-            header('Content-Type: text/xml');
-            Echo "<notifyResult success='true' msg='Thanks for the update.' />\n";
-        }
-
-        $this->ip = $_SERVER['REMOTE_ADDR'];
-
-        common_log(LOG_INFO, 'RSSCloud Logging Aggregator - ' .
-                   $this->ip . ' claims the feed at ' .
-                   $this->url . ' has been updated.');
-    }
-
-    /**
-     * Show an XML error when things go badly
-     *
-     * @param string $msg the error message
-     *
-     * @return void
-     */
-    function showError($msg)
-    {
-        header('HTTP/1.1 400 Bad Request');
-        header('Content-Type: text/xml');
-        echo "<?xml version='1.0'?>\n";
-        echo "<notifyResult success='false' msg='$msg' />\n";
-    }
-}
diff --git a/plugins/RSSCloud/RSSCloudNotifier.php b/plugins/RSSCloud/RSSCloudNotifier.php
deleted file mode 100644 (file)
index 6efe419..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Class to ping an rssCloud endpoint when a feed has been updated
- *
- * 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  Plugin
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Class for notifying cloud-enabled RSS aggregators that StatusNet
- * feeds have been updated.
- *
- * @category Plugin
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class RSSCloudNotifier
-{
-    const MAX_FAILURES = 3;
-
-    /**
-     * Send an HTTP GET to the notification handler with a
-     * challenge string to see if it repsonds correctly.
-     *
-     * @param string $endpoint URL of the notification handler
-     * @param string $feed     the feed being subscribed to
-     *
-     * @return boolean success
-     */
-    function challenge($endpoint, $feed)
-    {
-        $code   = common_confirmation_code(128);
-        $params = array('url' => $feed, 'challenge' => $code);
-        $url    = $endpoint . '?' . http_build_query($params);
-
-        try {
-            $client   = new HTTPClient();
-            $response = $client->get($url);
-        } catch (HTTP_Request2_Exception $e) {
-            common_log(LOG_INFO,
-                       'RSSCloud plugin - failure testing notify handler ' .
-                       $endpoint . ' - '  . $e->getMessage());
-            return false;
-        }
-
-        // Check response is betweet 200 and 299 and body contains challenge data
-
-        $status = $response->getStatus();
-        $body   = $response->getBody();
-
-        if ($status >= 200 && $status < 300) {
-            // NOTE: the spec says that the body must contain the string
-            // challenge.  It doesn't say that the body must contain the
-            // challenge string ONLY, although that seems to be the way
-            // the other implementors have interpreted it.
-
-            if (strpos($body, $code) !== false) {
-                common_log(LOG_INFO, 'RSSCloud plugin - ' .
-                           "success testing notify handler:  $endpoint");
-                return true;
-            } else {
-                common_log(LOG_INFO, 'RSSCloud plugin - ' .
-                          'challenge/repsonse failed for notify handler ' .
-                           $endpoint);
-                common_debug('body = ' . var_export($body, true));
-                return false;
-            }
-        } else {
-            common_log(LOG_INFO, 'RSSCloud plugin - ' .
-                       "failure testing notify handler:  $endpoint " .
-                       ' - got HTTP ' . $status);
-            common_debug('body = ' . var_export($body, true));
-            return false;
-        }
-    }
-
-    /**
-     * HTTP POST a notification that a feed has been updated
-     * ('ping the cloud').
-     *
-     * @param String $endpoint URL of the notification handler
-     * @param String $feed     the feed being subscribed to
-     *
-     * @return boolean success
-     */
-    function postUpdate($endpoint, $feed)
-    {
-        $headers  = array();
-        $postdata = array('url' => $feed);
-
-        try {
-            $client   = new HTTPClient();
-            $response = $client->post($endpoint, $headers, $postdata);
-        } catch (HTTP_Request2_Exception $e) {
-            common_log(LOG_INFO, 'RSSCloud plugin - failure notifying ' .
-                       $endpoint . ' that feed ' . $feed .
-                       ' has changed: ' . $e->getMessage());
-            return false;
-        }
-
-        $status = $response->getStatus();
-
-        if ($status >= 200 && $status < 300) {
-            common_log(LOG_INFO, 'RSSCloud plugin - success notifying ' .
-                       $endpoint . ' that feed ' . $feed . ' has changed.');
-            return true;
-        } else {
-            common_log(LOG_INFO, 'RSSCloud plugin - failure notifying ' .
-                       $endpoint . ' that feed ' . $feed .
-                       ' has changed: got HTTP ' . $status);
-            return false;
-        }
-    }
-
-    /**
-     * Notify all subscribers to a profile feed that it has changed.
-     *
-     * @param Profile $profile the profile whose feed has been
-     *        updated
-     *
-     * @return boolean success
-     */
-    function notify($profile)
-    {
-        $feed = common_path('api/statuses/user_timeline/') .
-          $profile->id . '.rss';
-
-        $cloudSub = new RSSCloudSubscription();
-
-        $cloudSub->subscribed = $profile->id;
-
-        if ($cloudSub->find()) {
-            while ($cloudSub->fetch()) {
-                $result = $this->postUpdate($cloudSub->url, $feed);
-                if ($result == false) {
-                    $this->handleFailure($cloudSub);
-                }
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Handle problems posting cloud notifications. Increment the failure
-     * count, or delete the subscription if the maximum number of failures
-     * is exceeded.
-     *
-     * XXX: Redo with proper DB_DataObject methods once I figure out what
-     * what the problem is with pluginized DB_DataObjects. -Z
-     *
-     * @param RSSCloudSubscription $cloudSub the subscription in question
-     *
-     * @return boolean success
-     */
-    function handleFailure($cloudSub)
-    {
-        $failCnt = $cloudSub->failures + 1;
-
-        if ($failCnt == self::MAX_FAILURES) {
-
-            common_log(LOG_INFO,
-                       'Deleting RSSCloud subcription ' .
-                       '(max failure count reached), profile: ' .
-                       $cloudSub->subscribed .
-                       ' handler: ' .
-                       $cloudSub->url);
-
-            // XXX: WTF! ->delete() doesn't work. Clearly, there are some issues with
-            // the DB_DataObject, or my understanding of it.  Have to drop into SQL.
-
-            // $result = $cloudSub->delete();
-
-            $qry = 'DELETE from rsscloud_subscription' .
-              ' WHERE subscribed = ' . $cloudSub->subscribed .
-              ' AND url = \'' . $cloudSub->url . '\'';
-
-            $result = $cloudSub->query($qry);
-
-            if (!$result) {
-                common_log_db_error($cloudSub, 'DELETE', __FILE__);
-                common_log(LOG_ERR, 'Could not delete RSSCloud subscription.');
-            }
-        } else {
-            common_debug('Updating failure count on RSSCloud subscription. ' .
-                         $failCnt);
-
-            $failCnt = $cloudSub->failures + 1;
-
-            // XXX: ->update() not working either, gar!
-
-            $qry = 'UPDATE rsscloud_subscription' .
-              ' SET failures = ' . $failCnt .
-              ' WHERE subscribed = ' . $cloudSub->subscribed .
-              ' AND url = \'' . $cloudSub->url . '\'';
-
-            $result = $cloudSub->query($qry);
-
-            if (!$result) {
-                common_log_db_error($cloudsub, 'UPDATE', __FILE__);
-                common_log(LOG_ERR,
-                           'Could not update failure ' .
-                           'count on RSSCloud subscription');
-            }
-        }
-    }
-}
index 144e0ca57d134d519e1608fba217d099553c3310..823094f269eace621d530cf66a4ca0e80f4ff46f 100644 (file)
@@ -115,38 +115,6 @@ class RSSCloudPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Automatically load the actions and libraries used by
-     * the RSSCloud plugin
-     *
-     * @param Class $cls the class
-     *
-     * @return boolean hook return
-     *
-     */
-    function onAutoload($cls)
-    {
-        switch ($cls)
-        {
-        case 'RSSCloudSubscription':
-            include_once INSTALLDIR . '/plugins/RSSCloud/RSSCloudSubscription.php';
-            return false;
-        case 'RSSCloudNotifier':
-            include_once INSTALLDIR . '/plugins/RSSCloud/RSSCloudNotifier.php';
-            return false;
-        case 'RSSCloudQueueHandler':
-            include_once INSTALLDIR . '/plugins/RSSCloud/RSSCloudQueueHandler.php';
-            return false;
-        case 'RSSCloudRequestNotifyAction':
-        case 'LoggingAggregatorAction':
-            include_once INSTALLDIR . '/plugins/RSSCloud/' .
-              mb_substr($cls, 0, -6) . '.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Add a <cloud> element to the RSS feed (after the rss <channel>
      * element is started).
diff --git a/plugins/RSSCloud/RSSCloudQueueHandler.php b/plugins/RSSCloud/RSSCloudQueueHandler.php
deleted file mode 100644 (file)
index 8a09977..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-class RSSCloudQueueHandler extends QueueHandler
-{
-    function transport()
-    {
-        return 'rsscloud';
-    }
-
-    function handle($notice)
-    {
-        try {
-            $profile = $notice->getProfile();
-        } catch (Exception $e) {
-            common_log(LOG_ERR, "Dropping RSSCloud item for notice with bogus profile: " . $e->getMessage());
-            return true;
-        }
-        $notifier = new RSSCloudNotifier();
-        return $notifier->notify($profile);
-    }
-}
diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php
deleted file mode 100644 (file)
index 7fd6da0..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-<?php
-/**
- * Action to let RSSCloud aggregators request update notification when
- * user profile feeds change.
- *
- * PHP version 5
- *
- * @category Plugin
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Action class to handle RSSCloud notification (subscription) requests
- *
- * @category Plugin
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class RSSCloudRequestNotifyAction extends Action
-{
-    /**
-     * Initialization.
-     *
-     * @param array $args Web and URL arguments
-     *
-     * @return boolean false if user doesn't exist
-     */
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $this->ip   = $_SERVER['REMOTE_ADDR'];
-        $this->port = $this->arg('port');
-        $this->path = $this->arg('path');
-
-        if ($this->path[0] != '/') {
-            $this->path = '/' . $this->path;
-        }
-
-        $this->protocol  = $this->arg('protocol');
-        $this->procedure = $this->arg('notifyProcedure');
-        $this->domain    = $this->arg('domain');
-
-        $this->feeds = $this->getFeeds();
-
-        return true;
-    }
-
-    /**
-     * Handle the request
-     *
-     * Checks for all the required parameters for a subscription,
-     * validates that the feed being subscribed to is real, and then
-     * saves the subsctiption.
-     *
-     * @param array $args $_REQUEST data (unused)
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-
-        if ($_SERVER['REQUEST_METHOD'] != 'POST') {
-            // TRANS: Form validation error displayed when POST is not used.
-            $this->showResult(false, _m('Request must be POST.'));
-            return;
-        }
-
-        $missing = array();
-
-        if (empty($this->port)) {
-            $missing[] = 'port';
-        }
-
-        if (empty($this->path)) {
-            $missing[] = 'path';
-        }
-
-        if (empty($this->protocol)) {
-            $missing[] = 'protocol';
-        } else if (strtolower($this->protocol) != 'http-post') {
-            // TRANS: Form validation error displayed when HTTP POST is not used.
-            $msg = _m('Only HTTP POST notifications are supported at this time.');
-            $this->showResult(false, $msg);
-            return;
-        }
-
-        if (!isset($this->procedure)) {
-            $missing[] = 'notifyProcedure';
-        }
-
-        if (!empty($missing)) {
-            // TRANS: List separator.
-            $separator = _m('SEPARATOR',', ');
-            // TRANS: Form validation error displayed when a request body is missing expected parameters.
-            // TRANS: %s is a list of parameters separated by a list separator (default: ", ").
-            $msg = sprintf(_m('The following parameters were missing from the request body: %s.'),implode($separator, $missing));
-            $this->showResult(false, $msg);
-            return;
-        }
-
-        if (empty($this->feeds)) {
-            // TRANS: Form validation error displayed when not providing any valid profile feed URLs.
-            $msg = _m('You must provide at least one valid profile feed URL ' .
-              '(url1, url2, url3 ... urlN).');
-            $this->showResult(false, $msg);
-            return;
-        }
-
-        // We have to validate everything before saving anything.
-        // We only return one success or failure no matter how
-        // many feeds the subscriber is trying to subscribe to
-        foreach ($this->feeds as $feed) {
-            if (!$this->validateFeed($feed)) {
-                $nh = $this->getNotifyUrl();
-                common_log(LOG_WARNING,
-                           "RSSCloud plugin - $nh tried to subscribe to invalid feed: $feed");
-
-                // TRANS: Form validation error displayed when not providing a valid feed URL.
-                $msg = _m('Feed subscription failed: Not a valid feed.');
-                $this->showResult(false, $msg);
-                return;
-            }
-
-            if (!$this->testNotificationHandler($feed)) {
-                // TRANS: Form validation error displayed when feed subscription failed.
-                $msg = _m('Feed subscription failed: ' .
-                'Notification handler does not respond correctly.');
-                $this->showResult(false, $msg);
-                return;
-            }
-        }
-
-        foreach ($this->feeds as $feed) {
-            $this->saveSubscription($feed);
-        }
-
-        // XXX: What to do about deleting stale subscriptions?
-        // 25 hours seems harsh. WordPress doesn't ever remove
-        // subscriptions.
-        // TRANS: Success message after subscribing to one or more feeds.
-        $msg = _m('Thanks for the subscription. ' .
-          'When the feed(s) update(s), you will be notified.');
-
-        $this->showResult(true, $msg);
-    }
-
-    /**
-     * Validate that the requested feed is one we serve
-     * up via RSSCloud.
-     *
-     * @param string $feed the feed in question
-     *
-     * @return void
-     */
-    function validateFeed($feed)
-    {
-        $user = $this->userFromFeed($feed);
-
-        if (empty($user)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Pull all of the urls (url1, url2, url3...urlN) that
-     * the subscriber wants to subscribe to.
-     *
-     * @return array $feeds the list of feeds
-     */
-    function getFeeds()
-    {
-        $feeds = array();
-
-        while (list($key, $feed) = each($this->args)) {
-            if (preg_match('/^url\d*$/', $key)) {
-                $feeds[] = $feed;
-            }
-        }
-
-        return $feeds;
-    }
-
-    /**
-     * Test that a notification handler is there and is reponding
-     * correctly.  This is called before adding a subscription.
-     *
-     * @param string $feed the feed to verify
-     *
-     * @return boolean success result
-     */
-    function testNotificationHandler($feed)
-    {
-        $notifyUrl = $this->getNotifyUrl();
-
-        $notifier = new RSSCloudNotifier();
-
-        if (isset($this->domain)) {
-            // 'domain' param set, so we have to use GET and send a challenge
-            common_log(LOG_INFO,
-                       'RSSCloud plugin - Testing notification handler with challenge: ' .
-                       $notifyUrl);
-            return $notifier->challenge($notifyUrl, $feed);
-        } else {
-            common_log(LOG_INFO, 'RSSCloud plugin - Testing notification handler: ' .
-                       $notifyUrl);
-
-            return $notifier->postUpdate($notifyUrl, $feed);
-        }
-    }
-
-    /**
-     * Build the URL for the notification handler based on the
-     * parameters passed in with the subscription request.
-     *
-     * @return string notification handler url
-     */
-    function getNotifyUrl()
-    {
-        if (isset($this->domain)) {
-            return 'http://' . $this->domain . ':' . $this->port . $this->path;
-        } else {
-            return 'http://' . $this->ip . ':' . $this->port . $this->path;
-        }
-    }
-
-    /**
-     * Uses the nickname part of the subscribed feed URL to figure out
-     * whethere there's really a user with such a feed.  Used to
-     * validate feeds before adding a subscription.
-     *
-     * @param string $feed the feed in question
-     *
-     * @return boolean success
-     */
-    function userFromFeed($feed)
-    {
-        // We only do canonical RSS2 profile feeds (specified by ID), e.g.:
-        // http://www.example.com/api/statuses/user_timeline/2.rss
-        $path  = common_path('api/statuses/user_timeline/');
-        $valid = '%^' . $path . '(?<id>.*)\.rss$%';
-
-        if (preg_match($valid, $feed, $matches)) {
-            $user = User::getKV('id', $matches['id']);
-            if (!empty($user)) {
-                return $user;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Save an RSSCloud subscription
-     *
-     * @param string $feed a valid profile feed
-     *
-     * @return boolean success result
-     */
-    function saveSubscription($feed)
-    {
-        $user = $this->userFromFeed($feed);
-
-        $notifyUrl = $this->getNotifyUrl();
-
-        $sub = RSSCloudSubscription::getSubscription($user->id, $notifyUrl);
-
-        if ($sub) {
-            common_log(LOG_INFO, "RSSCloud plugin - $notifyUrl refreshed subscription" .
-                         " to user $user->nickname (id: $user->id).");
-        } else {
-            $sub = new RSSCloudSubscription();
-
-            $sub->subscribed = $user->id;
-            $sub->url        = $notifyUrl;
-            $sub->created    = common_sql_now();
-
-            if (!$sub->insert()) {
-                common_log_db_error($sub, 'INSERT', __FILE__);
-                return false;
-            }
-
-            common_log(LOG_INFO, "RSSCloud plugin - $notifyUrl subscribed" .
-                       " to user $user->nickname (id: $user->id)");
-        }
-
-        return true;
-    }
-
-    /**
-     * Show an XML message indicating the subscription
-     * was successful or failed.
-     *
-     * @param boolean $success whether it was good or bad
-     * @param string  $msg     the message to output
-     *
-     * @return boolean success result
-     */
-    function showResult($success, $msg)
-    {
-        $this->startXML();
-        $this->elementStart('notifyResult',
-                            array('success' => ($success) ? 'true' : 'false',
-                                  'msg'     => $msg));
-        $this->endXML();
-    }
-}
diff --git a/plugins/RSSCloud/RSSCloudSubscription.php b/plugins/RSSCloud/RSSCloudSubscription.php
deleted file mode 100644 (file)
index e5acb9d..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * Table Definition for rsscloud_subscription
- */
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-class RSSCloudSubscription extends Memcached_DataObject {
-
-    var $__table='rsscloud_subscription'; // table name
-    var $subscribed;                      // int    primary key user id
-    var $url;                             // string primary key
-    var $failures;                        // int
-    var $created;                         // datestamp()
-    var $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    static function getKV($k,$v=NULL) { return DB_DataObject::staticGet('DataObjects_Grp',$k,$v); }
-
-    function table()
-    {
-
-        $db = $this->getDatabaseConnection();
-        $dbtype = $db->phptype;
-
-        $cols = array('subscribed' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
-                      'url'        => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
-                      'failures'   => DB_DATAOBJECT_INT,
-                      'created'    => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
-                      'modified'  => ($dbtype == 'mysql' || $dbtype == 'mysqli') ?
-                      DB_DATAOBJECT_MYSQLTIMESTAMP + DB_DATAOBJECT_NOTNULL :
-                      DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME
-                      );
-
-        return $cols;
-    }
-
-    function keys()
-    {
-        return array('subscribed' => 'N', 'url' => 'N');
-    }
-
-    static function getSubscription($subscribed, $url)
-    {
-        $sub = new RSSCloudSubscription();
-        $sub->whereAdd("subscribed = $subscribed");
-        $sub->whereAdd("url = '$url'");
-        $sub->limit(1);
-
-        if ($sub->find()) {
-            $sub->fetch();
-            return $sub;
-        }
-
-        return false;
-    }
-}
diff --git a/plugins/RSSCloud/actions/loggingaggregator.php b/plugins/RSSCloud/actions/loggingaggregator.php
new file mode 100644 (file)
index 0000000..824fa9e
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+/**
+ * This test class pretends to be an RSS aggregator. It logs notifications
+ * from the cloud.
+ *
+ * PHP version 5
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Dummy aggregator that acts as a proper notification handler. It
+ * doesn't do anything but respond correctly when notified via
+ * REST.  Mostly, this is just and action I used to develop the plugin
+ * and easily test things end-to-end. I'm leaving it in here as it
+ * may be useful for developing the plugin further.
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class LoggingAggregatorAction extends Action
+{
+    var $challenge = null;
+    var $url       = null;
+
+    /**
+     * Initialization.
+     *
+     * @param array $args Web and URL arguments
+     *
+     * @return boolean false if user doesn't exist
+     */
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $this->url       = $this->arg('url');
+        $this->challenge = $this->arg('challenge');
+
+        common_debug("args = " . var_export($this->args, true));
+        common_debug('url = ' . $this->url . ' challenge = ' . $this->challenge);
+
+        return true;
+    }
+
+    /**
+     * Handle the request
+     *
+     * @param array $args $_REQUEST data (unused)
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+
+        if (empty($this->url)) {
+            // TRANS: Form validation error displayed when a URL parameter is missing.
+            $this->showError(_m('A URL parameter is required.'));
+            return;
+        }
+
+        if (!empty($this->challenge)) {
+            // must be a GET
+            if ($_SERVER['REQUEST_METHOD'] != 'GET') {
+                // TRANS: Form validation error displayed when HTTP GET is not used.
+                $this->showError(_m('This resource requires an HTTP GET.'));
+                return;
+            }
+
+            header('Content-Type: text/xml');
+            echo $this->challenge;
+        } else {
+            // must be a POST
+            if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+                // TRANS: Form validation error displayed when HTTP POST is not used.
+                $this->showError(_m('This resource requires an HTTP POST.'));
+                return;
+            }
+
+            header('Content-Type: text/xml');
+            Echo "<notifyResult success='true' msg='Thanks for the update.' />\n";
+        }
+
+        $this->ip = $_SERVER['REMOTE_ADDR'];
+
+        common_log(LOG_INFO, 'RSSCloud Logging Aggregator - ' .
+                   $this->ip . ' claims the feed at ' .
+                   $this->url . ' has been updated.');
+    }
+
+    /**
+     * Show an XML error when things go badly
+     *
+     * @param string $msg the error message
+     *
+     * @return void
+     */
+    function showError($msg)
+    {
+        header('HTTP/1.1 400 Bad Request');
+        header('Content-Type: text/xml');
+        echo "<?xml version='1.0'?>\n";
+        echo "<notifyResult success='false' msg='$msg' />\n";
+    }
+}
diff --git a/plugins/RSSCloud/actions/rsscloudrequestnotify.php b/plugins/RSSCloud/actions/rsscloudrequestnotify.php
new file mode 100644 (file)
index 0000000..7fd6da0
--- /dev/null
@@ -0,0 +1,335 @@
+<?php
+/**
+ * Action to let RSSCloud aggregators request update notification when
+ * user profile feeds change.
+ *
+ * PHP version 5
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Action class to handle RSSCloud notification (subscription) requests
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class RSSCloudRequestNotifyAction extends Action
+{
+    /**
+     * Initialization.
+     *
+     * @param array $args Web and URL arguments
+     *
+     * @return boolean false if user doesn't exist
+     */
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $this->ip   = $_SERVER['REMOTE_ADDR'];
+        $this->port = $this->arg('port');
+        $this->path = $this->arg('path');
+
+        if ($this->path[0] != '/') {
+            $this->path = '/' . $this->path;
+        }
+
+        $this->protocol  = $this->arg('protocol');
+        $this->procedure = $this->arg('notifyProcedure');
+        $this->domain    = $this->arg('domain');
+
+        $this->feeds = $this->getFeeds();
+
+        return true;
+    }
+
+    /**
+     * Handle the request
+     *
+     * Checks for all the required parameters for a subscription,
+     * validates that the feed being subscribed to is real, and then
+     * saves the subsctiption.
+     *
+     * @param array $args $_REQUEST data (unused)
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+
+        if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+            // TRANS: Form validation error displayed when POST is not used.
+            $this->showResult(false, _m('Request must be POST.'));
+            return;
+        }
+
+        $missing = array();
+
+        if (empty($this->port)) {
+            $missing[] = 'port';
+        }
+
+        if (empty($this->path)) {
+            $missing[] = 'path';
+        }
+
+        if (empty($this->protocol)) {
+            $missing[] = 'protocol';
+        } else if (strtolower($this->protocol) != 'http-post') {
+            // TRANS: Form validation error displayed when HTTP POST is not used.
+            $msg = _m('Only HTTP POST notifications are supported at this time.');
+            $this->showResult(false, $msg);
+            return;
+        }
+
+        if (!isset($this->procedure)) {
+            $missing[] = 'notifyProcedure';
+        }
+
+        if (!empty($missing)) {
+            // TRANS: List separator.
+            $separator = _m('SEPARATOR',', ');
+            // TRANS: Form validation error displayed when a request body is missing expected parameters.
+            // TRANS: %s is a list of parameters separated by a list separator (default: ", ").
+            $msg = sprintf(_m('The following parameters were missing from the request body: %s.'),implode($separator, $missing));
+            $this->showResult(false, $msg);
+            return;
+        }
+
+        if (empty($this->feeds)) {
+            // TRANS: Form validation error displayed when not providing any valid profile feed URLs.
+            $msg = _m('You must provide at least one valid profile feed URL ' .
+              '(url1, url2, url3 ... urlN).');
+            $this->showResult(false, $msg);
+            return;
+        }
+
+        // We have to validate everything before saving anything.
+        // We only return one success or failure no matter how
+        // many feeds the subscriber is trying to subscribe to
+        foreach ($this->feeds as $feed) {
+            if (!$this->validateFeed($feed)) {
+                $nh = $this->getNotifyUrl();
+                common_log(LOG_WARNING,
+                           "RSSCloud plugin - $nh tried to subscribe to invalid feed: $feed");
+
+                // TRANS: Form validation error displayed when not providing a valid feed URL.
+                $msg = _m('Feed subscription failed: Not a valid feed.');
+                $this->showResult(false, $msg);
+                return;
+            }
+
+            if (!$this->testNotificationHandler($feed)) {
+                // TRANS: Form validation error displayed when feed subscription failed.
+                $msg = _m('Feed subscription failed: ' .
+                'Notification handler does not respond correctly.');
+                $this->showResult(false, $msg);
+                return;
+            }
+        }
+
+        foreach ($this->feeds as $feed) {
+            $this->saveSubscription($feed);
+        }
+
+        // XXX: What to do about deleting stale subscriptions?
+        // 25 hours seems harsh. WordPress doesn't ever remove
+        // subscriptions.
+        // TRANS: Success message after subscribing to one or more feeds.
+        $msg = _m('Thanks for the subscription. ' .
+          'When the feed(s) update(s), you will be notified.');
+
+        $this->showResult(true, $msg);
+    }
+
+    /**
+     * Validate that the requested feed is one we serve
+     * up via RSSCloud.
+     *
+     * @param string $feed the feed in question
+     *
+     * @return void
+     */
+    function validateFeed($feed)
+    {
+        $user = $this->userFromFeed($feed);
+
+        if (empty($user)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Pull all of the urls (url1, url2, url3...urlN) that
+     * the subscriber wants to subscribe to.
+     *
+     * @return array $feeds the list of feeds
+     */
+    function getFeeds()
+    {
+        $feeds = array();
+
+        while (list($key, $feed) = each($this->args)) {
+            if (preg_match('/^url\d*$/', $key)) {
+                $feeds[] = $feed;
+            }
+        }
+
+        return $feeds;
+    }
+
+    /**
+     * Test that a notification handler is there and is reponding
+     * correctly.  This is called before adding a subscription.
+     *
+     * @param string $feed the feed to verify
+     *
+     * @return boolean success result
+     */
+    function testNotificationHandler($feed)
+    {
+        $notifyUrl = $this->getNotifyUrl();
+
+        $notifier = new RSSCloudNotifier();
+
+        if (isset($this->domain)) {
+            // 'domain' param set, so we have to use GET and send a challenge
+            common_log(LOG_INFO,
+                       'RSSCloud plugin - Testing notification handler with challenge: ' .
+                       $notifyUrl);
+            return $notifier->challenge($notifyUrl, $feed);
+        } else {
+            common_log(LOG_INFO, 'RSSCloud plugin - Testing notification handler: ' .
+                       $notifyUrl);
+
+            return $notifier->postUpdate($notifyUrl, $feed);
+        }
+    }
+
+    /**
+     * Build the URL for the notification handler based on the
+     * parameters passed in with the subscription request.
+     *
+     * @return string notification handler url
+     */
+    function getNotifyUrl()
+    {
+        if (isset($this->domain)) {
+            return 'http://' . $this->domain . ':' . $this->port . $this->path;
+        } else {
+            return 'http://' . $this->ip . ':' . $this->port . $this->path;
+        }
+    }
+
+    /**
+     * Uses the nickname part of the subscribed feed URL to figure out
+     * whethere there's really a user with such a feed.  Used to
+     * validate feeds before adding a subscription.
+     *
+     * @param string $feed the feed in question
+     *
+     * @return boolean success
+     */
+    function userFromFeed($feed)
+    {
+        // We only do canonical RSS2 profile feeds (specified by ID), e.g.:
+        // http://www.example.com/api/statuses/user_timeline/2.rss
+        $path  = common_path('api/statuses/user_timeline/');
+        $valid = '%^' . $path . '(?<id>.*)\.rss$%';
+
+        if (preg_match($valid, $feed, $matches)) {
+            $user = User::getKV('id', $matches['id']);
+            if (!empty($user)) {
+                return $user;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Save an RSSCloud subscription
+     *
+     * @param string $feed a valid profile feed
+     *
+     * @return boolean success result
+     */
+    function saveSubscription($feed)
+    {
+        $user = $this->userFromFeed($feed);
+
+        $notifyUrl = $this->getNotifyUrl();
+
+        $sub = RSSCloudSubscription::getSubscription($user->id, $notifyUrl);
+
+        if ($sub) {
+            common_log(LOG_INFO, "RSSCloud plugin - $notifyUrl refreshed subscription" .
+                         " to user $user->nickname (id: $user->id).");
+        } else {
+            $sub = new RSSCloudSubscription();
+
+            $sub->subscribed = $user->id;
+            $sub->url        = $notifyUrl;
+            $sub->created    = common_sql_now();
+
+            if (!$sub->insert()) {
+                common_log_db_error($sub, 'INSERT', __FILE__);
+                return false;
+            }
+
+            common_log(LOG_INFO, "RSSCloud plugin - $notifyUrl subscribed" .
+                       " to user $user->nickname (id: $user->id)");
+        }
+
+        return true;
+    }
+
+    /**
+     * Show an XML message indicating the subscription
+     * was successful or failed.
+     *
+     * @param boolean $success whether it was good or bad
+     * @param string  $msg     the message to output
+     *
+     * @return boolean success result
+     */
+    function showResult($success, $msg)
+    {
+        $this->startXML();
+        $this->elementStart('notifyResult',
+                            array('success' => ($success) ? 'true' : 'false',
+                                  'msg'     => $msg));
+        $this->endXML();
+    }
+}
diff --git a/plugins/RSSCloud/classes/RSSCloudSubscription.php b/plugins/RSSCloud/classes/RSSCloudSubscription.php
new file mode 100644 (file)
index 0000000..e5acb9d
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Table Definition for rsscloud_subscription
+ */
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+class RSSCloudSubscription extends Memcached_DataObject {
+
+    var $__table='rsscloud_subscription'; // table name
+    var $subscribed;                      // int    primary key user id
+    var $url;                             // string primary key
+    var $failures;                        // int
+    var $created;                         // datestamp()
+    var $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    static function getKV($k,$v=NULL) { return DB_DataObject::staticGet('DataObjects_Grp',$k,$v); }
+
+    function table()
+    {
+
+        $db = $this->getDatabaseConnection();
+        $dbtype = $db->phptype;
+
+        $cols = array('subscribed' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
+                      'url'        => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
+                      'failures'   => DB_DATAOBJECT_INT,
+                      'created'    => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
+                      'modified'  => ($dbtype == 'mysql' || $dbtype == 'mysqli') ?
+                      DB_DATAOBJECT_MYSQLTIMESTAMP + DB_DATAOBJECT_NOTNULL :
+                      DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME
+                      );
+
+        return $cols;
+    }
+
+    function keys()
+    {
+        return array('subscribed' => 'N', 'url' => 'N');
+    }
+
+    static function getSubscription($subscribed, $url)
+    {
+        $sub = new RSSCloudSubscription();
+        $sub->whereAdd("subscribed = $subscribed");
+        $sub->whereAdd("url = '$url'");
+        $sub->limit(1);
+
+        if ($sub->find()) {
+            $sub->fetch();
+            return $sub;
+        }
+
+        return false;
+    }
+}
diff --git a/plugins/RSSCloud/lib/rsscloudnotifier.php b/plugins/RSSCloud/lib/rsscloudnotifier.php
new file mode 100644 (file)
index 0000000..6efe419
--- /dev/null
@@ -0,0 +1,234 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Class to ping an rssCloud endpoint when a feed has been updated
+ *
+ * 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  Plugin
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Class for notifying cloud-enabled RSS aggregators that StatusNet
+ * feeds have been updated.
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class RSSCloudNotifier
+{
+    const MAX_FAILURES = 3;
+
+    /**
+     * Send an HTTP GET to the notification handler with a
+     * challenge string to see if it repsonds correctly.
+     *
+     * @param string $endpoint URL of the notification handler
+     * @param string $feed     the feed being subscribed to
+     *
+     * @return boolean success
+     */
+    function challenge($endpoint, $feed)
+    {
+        $code   = common_confirmation_code(128);
+        $params = array('url' => $feed, 'challenge' => $code);
+        $url    = $endpoint . '?' . http_build_query($params);
+
+        try {
+            $client   = new HTTPClient();
+            $response = $client->get($url);
+        } catch (HTTP_Request2_Exception $e) {
+            common_log(LOG_INFO,
+                       'RSSCloud plugin - failure testing notify handler ' .
+                       $endpoint . ' - '  . $e->getMessage());
+            return false;
+        }
+
+        // Check response is betweet 200 and 299 and body contains challenge data
+
+        $status = $response->getStatus();
+        $body   = $response->getBody();
+
+        if ($status >= 200 && $status < 300) {
+            // NOTE: the spec says that the body must contain the string
+            // challenge.  It doesn't say that the body must contain the
+            // challenge string ONLY, although that seems to be the way
+            // the other implementors have interpreted it.
+
+            if (strpos($body, $code) !== false) {
+                common_log(LOG_INFO, 'RSSCloud plugin - ' .
+                           "success testing notify handler:  $endpoint");
+                return true;
+            } else {
+                common_log(LOG_INFO, 'RSSCloud plugin - ' .
+                          'challenge/repsonse failed for notify handler ' .
+                           $endpoint);
+                common_debug('body = ' . var_export($body, true));
+                return false;
+            }
+        } else {
+            common_log(LOG_INFO, 'RSSCloud plugin - ' .
+                       "failure testing notify handler:  $endpoint " .
+                       ' - got HTTP ' . $status);
+            common_debug('body = ' . var_export($body, true));
+            return false;
+        }
+    }
+
+    /**
+     * HTTP POST a notification that a feed has been updated
+     * ('ping the cloud').
+     *
+     * @param String $endpoint URL of the notification handler
+     * @param String $feed     the feed being subscribed to
+     *
+     * @return boolean success
+     */
+    function postUpdate($endpoint, $feed)
+    {
+        $headers  = array();
+        $postdata = array('url' => $feed);
+
+        try {
+            $client   = new HTTPClient();
+            $response = $client->post($endpoint, $headers, $postdata);
+        } catch (HTTP_Request2_Exception $e) {
+            common_log(LOG_INFO, 'RSSCloud plugin - failure notifying ' .
+                       $endpoint . ' that feed ' . $feed .
+                       ' has changed: ' . $e->getMessage());
+            return false;
+        }
+
+        $status = $response->getStatus();
+
+        if ($status >= 200 && $status < 300) {
+            common_log(LOG_INFO, 'RSSCloud plugin - success notifying ' .
+                       $endpoint . ' that feed ' . $feed . ' has changed.');
+            return true;
+        } else {
+            common_log(LOG_INFO, 'RSSCloud plugin - failure notifying ' .
+                       $endpoint . ' that feed ' . $feed .
+                       ' has changed: got HTTP ' . $status);
+            return false;
+        }
+    }
+
+    /**
+     * Notify all subscribers to a profile feed that it has changed.
+     *
+     * @param Profile $profile the profile whose feed has been
+     *        updated
+     *
+     * @return boolean success
+     */
+    function notify($profile)
+    {
+        $feed = common_path('api/statuses/user_timeline/') .
+          $profile->id . '.rss';
+
+        $cloudSub = new RSSCloudSubscription();
+
+        $cloudSub->subscribed = $profile->id;
+
+        if ($cloudSub->find()) {
+            while ($cloudSub->fetch()) {
+                $result = $this->postUpdate($cloudSub->url, $feed);
+                if ($result == false) {
+                    $this->handleFailure($cloudSub);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Handle problems posting cloud notifications. Increment the failure
+     * count, or delete the subscription if the maximum number of failures
+     * is exceeded.
+     *
+     * XXX: Redo with proper DB_DataObject methods once I figure out what
+     * what the problem is with pluginized DB_DataObjects. -Z
+     *
+     * @param RSSCloudSubscription $cloudSub the subscription in question
+     *
+     * @return boolean success
+     */
+    function handleFailure($cloudSub)
+    {
+        $failCnt = $cloudSub->failures + 1;
+
+        if ($failCnt == self::MAX_FAILURES) {
+
+            common_log(LOG_INFO,
+                       'Deleting RSSCloud subcription ' .
+                       '(max failure count reached), profile: ' .
+                       $cloudSub->subscribed .
+                       ' handler: ' .
+                       $cloudSub->url);
+
+            // XXX: WTF! ->delete() doesn't work. Clearly, there are some issues with
+            // the DB_DataObject, or my understanding of it.  Have to drop into SQL.
+
+            // $result = $cloudSub->delete();
+
+            $qry = 'DELETE from rsscloud_subscription' .
+              ' WHERE subscribed = ' . $cloudSub->subscribed .
+              ' AND url = \'' . $cloudSub->url . '\'';
+
+            $result = $cloudSub->query($qry);
+
+            if (!$result) {
+                common_log_db_error($cloudSub, 'DELETE', __FILE__);
+                common_log(LOG_ERR, 'Could not delete RSSCloud subscription.');
+            }
+        } else {
+            common_debug('Updating failure count on RSSCloud subscription. ' .
+                         $failCnt);
+
+            $failCnt = $cloudSub->failures + 1;
+
+            // XXX: ->update() not working either, gar!
+
+            $qry = 'UPDATE rsscloud_subscription' .
+              ' SET failures = ' . $failCnt .
+              ' WHERE subscribed = ' . $cloudSub->subscribed .
+              ' AND url = \'' . $cloudSub->url . '\'';
+
+            $result = $cloudSub->query($qry);
+
+            if (!$result) {
+                common_log_db_error($cloudsub, 'UPDATE', __FILE__);
+                common_log(LOG_ERR,
+                           'Could not update failure ' .
+                           'count on RSSCloud subscription');
+            }
+        }
+    }
+}
diff --git a/plugins/RSSCloud/lib/rsscloudqueuehandler.php b/plugins/RSSCloud/lib/rsscloudqueuehandler.php
new file mode 100644 (file)
index 0000000..8a09977
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+class RSSCloudQueueHandler extends QueueHandler
+{
+    function transport()
+    {
+        return 'rsscloud';
+    }
+
+    function handle($notice)
+    {
+        try {
+            $profile = $notice->getProfile();
+        } catch (Exception $e) {
+            common_log(LOG_ERR, "Dropping RSSCloud item for notice with bogus profile: " . $e->getMessage());
+            return true;
+        }
+        $notifier = new RSSCloudNotifier();
+        return $notifier->notify($profile);
+    }
+}
index aec53d48f33385aa7a60d0520dcc898664a04900..42a254ab0c844b51d15d0187e8a635bd002d8698 100644 (file)
@@ -66,24 +66,6 @@ class RealtimePlugin extends Plugin
         return true;
     }
 
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'KeepalivechannelAction':
-        case 'ClosechannelAction':
-            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'Realtime_channel':
-            include_once $dir.'/'.$cls.'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Hook for RouterInitialized event.
      *
diff --git a/plugins/Realtime/Realtime_channel.php b/plugins/Realtime/Realtime_channel.php
deleted file mode 100644 (file)
index ffd7d34..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * A channel for real-time browser data
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Realtime
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * A channel for real-time browser data
- *
- * For each user currently browsing the site, we want to know which page they're on
- * so we can send real-time updates to their browser.
- *
- * @category Realtime
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Realtime_channel extends Managed_DataObject
-{
-    const TIMEOUT = 1800; // 30 minutes
-
-    public $__table = 'realtime_channel'; // table name
-
-    public $user_id;       // int -> user.id, can be null
-    public $action;        // string
-    public $arg1;          // argument
-    public $arg2;          // argument, usually null
-    public $channel_key;   // 128-bit shared secret key
-    public $audience;      // listener count
-    public $created;       // created date
-    public $modified;      // modified date
-
-    /**
-     * The One True Thingy that must be defined and declared.
-     */
-    public static function schemaDef()
-    {
-        return array(
-            'description' => 'A channel of realtime notice data',
-            'fields' => array(
-                'user_id' => array('type' => 'int',
-                                   'not null' => false,
-                                   'description' => 'user viewing page; can be null'),
-                'action' => array('type' => 'varchar',
-                                  'length' => 255,
-                                  'not null' => true,
-                                  'description' => 'page being viewed'),
-                'arg1' => array('type' => 'varchar',
-                                'length' => 255,
-                                'not null' => false,
-                                'description' => 'page argument, like username or tag'),
-                'arg2' => array('type' => 'varchar',
-                                'length' => 255,
-                                'not null' => false,
-                                'description' => 'second page argument, like tag for showstream'),
-                'channel_key' => array('type' => 'varchar',
-                               'length' => 32,
-                               'not null' => true,
-                               'description' => 'shared secret key for this channel'),
-                'audience' => array('type' => 'integer',
-                                    'not null' => true,
-                                    'default' => 0,
-                                    'description' => 'reference count'),
-                'created' => array('type' => 'datetime',
-                                   'not null' => true,
-                                   'description' => 'date this record was created'),
-                'modified' => array('type' => 'datetime',
-                                    'not null' => true,
-                                    'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('channel_key'),
-            'unique keys' => array('realtime_channel_user_page_idx' => array('user_id', 'action', 'arg1', 'arg2')),
-            'foreign keys' => array(
-                'realtime_channel_user_id_fkey' => array('user', array('user_id' => 'id')),
-            ),
-            'indexes' => array(
-                'realtime_channel_modified_idx' => array('modified'),
-                'realtime_channel_page_idx' => array('action', 'arg1', 'arg2')
-            ),
-        );
-    }
-
-    static function saveNew($user_id, $action, $arg1, $arg2)
-    {
-        $channel = new Realtime_channel();
-
-        $channel->user_id = $user_id;
-        $channel->action  = $action;
-        $channel->arg1    = $arg1;
-        $channel->arg2    = $arg2;
-        $channel->audience  = 1;
-
-        $channel->channel_key = common_good_rand(16); // 128-bit key, 32 hex chars
-
-        $channel->created  = common_sql_now();
-        $channel->modified = $channel->created;
-
-        $channel->insert();
-
-        return $channel;
-    }
-
-    static function getChannel($user_id, $action, $arg1, $arg2)
-    {
-        $channel = self::fetchChannel($user_id, $action, $arg1, $arg2);
-
-        // Ignore (and delete!) old channels
-
-        if (!empty($channel)) {
-            $modTime = strtotime($channel->modified);
-            if ((time() - $modTime) > self::TIMEOUT) {
-                $channel->delete();
-                $channel = null;
-            }
-        }
-
-        if (empty($channel)) {
-            $channel = self::saveNew($user_id, $action, $arg1, $arg2);
-        }
-
-        return $channel;
-    }
-
-    static function getAllChannels($action, $arg1, $arg2)
-    {
-        $channel = new Realtime_channel();
-
-        $channel->action = $action;
-
-        if (is_null($arg1)) {
-            $channel->whereAdd('arg1 is null');
-        } else {
-            $channel->arg1 = $arg1;
-        }
-
-        if (is_null($arg2)) {
-            $channel->whereAdd('arg2 is null');
-        } else {
-            $channel->arg2 = $arg2;
-        }
-
-        $channel->whereAdd('modified > "' . common_sql_date(time() - self::TIMEOUT) . '"');
-
-        $channels = array();
-
-        if ($channel->find()) {
-            $channels = $channel->fetchAll();
-        }
-
-        return $channels;
-    }
-
-    static function fetchChannel($user_id, $action, $arg1, $arg2)
-    {
-        $channel = new Realtime_channel();
-
-        if (is_null($user_id)) {
-            $channel->whereAdd('user_id is null');
-        } else {
-            $channel->user_id = $user_id;
-        }
-
-        $channel->action = $action;
-
-        if (is_null($arg1)) {
-            $channel->whereAdd('arg1 is null');
-        } else {
-            $channel->arg1 = $arg1;
-        }
-
-        if (is_null($arg2)) {
-            $channel->whereAdd('arg2 is null');
-        } else {
-            $channel->arg2 = $arg2;
-        }
-
-        if ($channel->find(true)) {
-            $channel->increment();
-            return $channel;
-        } else {
-            return null;
-        }
-    }
-
-    function increment()
-    {
-        // XXX: race
-        $orig = clone($this);
-        $this->audience++;
-        $this->modified = common_sql_now();
-        $this->update($orig);
-    }
-
-    function touch()
-    {
-        // XXX: race
-        $orig = clone($this);
-        $this->modified = common_sql_now();
-        $this->update($orig);
-    }
-
-    function decrement()
-    {
-        // XXX: race
-        if ($this->audience == 1) {
-            $this->delete();
-        } else {
-            $orig = clone($this);
-            $this->audience--;
-            $this->modified = common_sql_now();
-            $this->update($orig);
-        }
-    }
-}
diff --git a/plugins/Realtime/actions/closechannel.php b/plugins/Realtime/actions/closechannel.php
new file mode 100644 (file)
index 0000000..ee092ce
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * action to close a channel
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Realtime
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Action to close a channel
+ *
+ * @category  Realtime
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class ClosechannelAction extends Action
+{
+    protected $channelKey = null;
+    protected $channel    = null;
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        if (!$this->isPost()) {
+            // TRANS: Client exception. Do not translate POST.
+            throw new ClientException(_m('You have to POST it.'));
+        }
+
+        $this->channelKey = $this->trimmed('channelkey');
+
+        if (empty($this->channelKey)) {
+            // TRANS: Client exception thrown when the channel key argument is missing.
+            throw new ClientException(_m('No channel key argument.'));
+        }
+
+        $this->channel = Realtime_channel::getKV('channel_key', $this->channelKey);
+
+        if (empty($this->channel)) {
+            // TRANS: Client exception thrown when referring to a non-existing channel.
+            throw new ClientException(_m('No such channel.'));
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        $this->channel->decrement();
+
+        header('HTTP/1.1 204 No Content');
+
+        return;
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        return false;
+    }
+}
diff --git a/plugins/Realtime/actions/keepalivechannel.php b/plugins/Realtime/actions/keepalivechannel.php
new file mode 100644 (file)
index 0000000..e9319ed
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * action periodically pinged by a page to keep a channel alive
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Realtime
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Action periodically pinged by a page to keep a channel alive
+ *
+ * @category  Realtime
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class KeepalivechannelAction extends Action
+{
+    protected $channelKey = null;
+    protected $channel    = null;
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+
+        if (!$this->isPost()) {
+            // TRANS: Client exception. Do not translate POST.
+            throw new ClientException(_m('You have to POST it.'));
+        }
+
+        $this->channelKey = $this->trimmed('channelkey');
+
+        if (empty($this->channelKey)) {
+            // TRANS: Client exception thrown when the channel key argument is missing.
+            throw new ClientException(_m('No channel key argument.'));
+        }
+
+        $this->channel = Realtime_channel::getKV('channel_key', $this->channelKey);
+
+        if (empty($this->channel)) {
+            // TRANS: Client exception thrown when referring to a non-existing channel.
+            throw new ClientException(_m('No such channel.'));
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        $this->channel->touch();
+
+        header('HTTP/1.1 204 No Content');
+
+        return;
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * MAY override
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        return false;
+    }
+}
diff --git a/plugins/Realtime/classes/Realtime_channel.php b/plugins/Realtime/classes/Realtime_channel.php
new file mode 100644 (file)
index 0000000..ffd7d34
--- /dev/null
@@ -0,0 +1,245 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * A channel for real-time browser data
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Realtime
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * A channel for real-time browser data
+ *
+ * For each user currently browsing the site, we want to know which page they're on
+ * so we can send real-time updates to their browser.
+ *
+ * @category Realtime
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Realtime_channel extends Managed_DataObject
+{
+    const TIMEOUT = 1800; // 30 minutes
+
+    public $__table = 'realtime_channel'; // table name
+
+    public $user_id;       // int -> user.id, can be null
+    public $action;        // string
+    public $arg1;          // argument
+    public $arg2;          // argument, usually null
+    public $channel_key;   // 128-bit shared secret key
+    public $audience;      // listener count
+    public $created;       // created date
+    public $modified;      // modified date
+
+    /**
+     * The One True Thingy that must be defined and declared.
+     */
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'A channel of realtime notice data',
+            'fields' => array(
+                'user_id' => array('type' => 'int',
+                                   'not null' => false,
+                                   'description' => 'user viewing page; can be null'),
+                'action' => array('type' => 'varchar',
+                                  'length' => 255,
+                                  'not null' => true,
+                                  'description' => 'page being viewed'),
+                'arg1' => array('type' => 'varchar',
+                                'length' => 255,
+                                'not null' => false,
+                                'description' => 'page argument, like username or tag'),
+                'arg2' => array('type' => 'varchar',
+                                'length' => 255,
+                                'not null' => false,
+                                'description' => 'second page argument, like tag for showstream'),
+                'channel_key' => array('type' => 'varchar',
+                               'length' => 32,
+                               'not null' => true,
+                               'description' => 'shared secret key for this channel'),
+                'audience' => array('type' => 'integer',
+                                    'not null' => true,
+                                    'default' => 0,
+                                    'description' => 'reference count'),
+                'created' => array('type' => 'datetime',
+                                   'not null' => true,
+                                   'description' => 'date this record was created'),
+                'modified' => array('type' => 'datetime',
+                                    'not null' => true,
+                                    'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('channel_key'),
+            'unique keys' => array('realtime_channel_user_page_idx' => array('user_id', 'action', 'arg1', 'arg2')),
+            'foreign keys' => array(
+                'realtime_channel_user_id_fkey' => array('user', array('user_id' => 'id')),
+            ),
+            'indexes' => array(
+                'realtime_channel_modified_idx' => array('modified'),
+                'realtime_channel_page_idx' => array('action', 'arg1', 'arg2')
+            ),
+        );
+    }
+
+    static function saveNew($user_id, $action, $arg1, $arg2)
+    {
+        $channel = new Realtime_channel();
+
+        $channel->user_id = $user_id;
+        $channel->action  = $action;
+        $channel->arg1    = $arg1;
+        $channel->arg2    = $arg2;
+        $channel->audience  = 1;
+
+        $channel->channel_key = common_good_rand(16); // 128-bit key, 32 hex chars
+
+        $channel->created  = common_sql_now();
+        $channel->modified = $channel->created;
+
+        $channel->insert();
+
+        return $channel;
+    }
+
+    static function getChannel($user_id, $action, $arg1, $arg2)
+    {
+        $channel = self::fetchChannel($user_id, $action, $arg1, $arg2);
+
+        // Ignore (and delete!) old channels
+
+        if (!empty($channel)) {
+            $modTime = strtotime($channel->modified);
+            if ((time() - $modTime) > self::TIMEOUT) {
+                $channel->delete();
+                $channel = null;
+            }
+        }
+
+        if (empty($channel)) {
+            $channel = self::saveNew($user_id, $action, $arg1, $arg2);
+        }
+
+        return $channel;
+    }
+
+    static function getAllChannels($action, $arg1, $arg2)
+    {
+        $channel = new Realtime_channel();
+
+        $channel->action = $action;
+
+        if (is_null($arg1)) {
+            $channel->whereAdd('arg1 is null');
+        } else {
+            $channel->arg1 = $arg1;
+        }
+
+        if (is_null($arg2)) {
+            $channel->whereAdd('arg2 is null');
+        } else {
+            $channel->arg2 = $arg2;
+        }
+
+        $channel->whereAdd('modified > "' . common_sql_date(time() - self::TIMEOUT) . '"');
+
+        $channels = array();
+
+        if ($channel->find()) {
+            $channels = $channel->fetchAll();
+        }
+
+        return $channels;
+    }
+
+    static function fetchChannel($user_id, $action, $arg1, $arg2)
+    {
+        $channel = new Realtime_channel();
+
+        if (is_null($user_id)) {
+            $channel->whereAdd('user_id is null');
+        } else {
+            $channel->user_id = $user_id;
+        }
+
+        $channel->action = $action;
+
+        if (is_null($arg1)) {
+            $channel->whereAdd('arg1 is null');
+        } else {
+            $channel->arg1 = $arg1;
+        }
+
+        if (is_null($arg2)) {
+            $channel->whereAdd('arg2 is null');
+        } else {
+            $channel->arg2 = $arg2;
+        }
+
+        if ($channel->find(true)) {
+            $channel->increment();
+            return $channel;
+        } else {
+            return null;
+        }
+    }
+
+    function increment()
+    {
+        // XXX: race
+        $orig = clone($this);
+        $this->audience++;
+        $this->modified = common_sql_now();
+        $this->update($orig);
+    }
+
+    function touch()
+    {
+        // XXX: race
+        $orig = clone($this);
+        $this->modified = common_sql_now();
+        $this->update($orig);
+    }
+
+    function decrement()
+    {
+        // XXX: race
+        if ($this->audience == 1) {
+            $this->delete();
+        } else {
+            $orig = clone($this);
+            $this->audience--;
+            $this->modified = common_sql_now();
+            $this->update($orig);
+        }
+    }
+}
diff --git a/plugins/Realtime/cleanupchannels.php b/plugins/Realtime/cleanupchannels.php
deleted file mode 100644 (file)
index ea195b0..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/env php
-<?php
-/*
- * StatusNet - a distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Script to print out current version of the software
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
-
-$shortoptions = 'u';
-$longoptions = array('universe');
-
-$helptext = <<<END_OF_CLEANUPCHANNELS_HELP
-cleanupchannels.php [options]
-Garbage-collects old realtime channels
-
--u --universe Do all sites
-
-END_OF_CLEANUPCHANNELS_HELP;
-
-require_once INSTALLDIR.'/scripts/commandline.inc';
-
-function cleanupChannels()
-{
-    $rc = new Realtime_channel();
-
-    $rc->selectAdd();
-    $rc->selectAdd('channel_key');
-
-    $rc->whereAdd('modified < "' . common_sql_date(time() - Realtime_channel::TIMEOUT) . '"');
-
-    if ($rc->find()) {
-        $keys = $rc->fetchAll();
-
-        foreach ($keys as $key) {
-            $rc = Realtime_channel::getKV('channel_key', $key);
-            if (!empty($rc)) {
-                printfv("Deleting realtime channel '$key'\n");
-                $rc->delete();
-            }
-        }
-    }
-}
-
-if (have_option('u', 'universe')) {
-    $sn = new Status_network();
-    if ($sn->find()) {
-        while ($sn->fetch()) {
-            $server = $sn->getServerName();
-            StatusNet::init($server);
-            cleanupChannels();
-        }
-    }
-} else {
-    cleanupChannels();
-}
diff --git a/plugins/Realtime/closechannel.php b/plugins/Realtime/closechannel.php
deleted file mode 100644 (file)
index ee092ce..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * action to close a channel
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Realtime
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Action to close a channel
- *
- * @category  Realtime
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class ClosechannelAction extends Action
-{
-    protected $channelKey = null;
-    protected $channel    = null;
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        if (!$this->isPost()) {
-            // TRANS: Client exception. Do not translate POST.
-            throw new ClientException(_m('You have to POST it.'));
-        }
-
-        $this->channelKey = $this->trimmed('channelkey');
-
-        if (empty($this->channelKey)) {
-            // TRANS: Client exception thrown when the channel key argument is missing.
-            throw new ClientException(_m('No channel key argument.'));
-        }
-
-        $this->channel = Realtime_channel::getKV('channel_key', $this->channelKey);
-
-        if (empty($this->channel)) {
-            // TRANS: Client exception thrown when referring to a non-existing channel.
-            throw new ClientException(_m('No such channel.'));
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        $this->channel->decrement();
-
-        header('HTTP/1.1 204 No Content');
-
-        return;
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        return false;
-    }
-}
diff --git a/plugins/Realtime/keepalivechannel.php b/plugins/Realtime/keepalivechannel.php
deleted file mode 100644 (file)
index e9319ed..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * action periodically pinged by a page to keep a channel alive
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Realtime
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Action periodically pinged by a page to keep a channel alive
- *
- * @category  Realtime
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class KeepalivechannelAction extends Action
-{
-    protected $channelKey = null;
-    protected $channel    = null;
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-
-        if (!$this->isPost()) {
-            // TRANS: Client exception. Do not translate POST.
-            throw new ClientException(_m('You have to POST it.'));
-        }
-
-        $this->channelKey = $this->trimmed('channelkey');
-
-        if (empty($this->channelKey)) {
-            // TRANS: Client exception thrown when the channel key argument is missing.
-            throw new ClientException(_m('No channel key argument.'));
-        }
-
-        $this->channel = Realtime_channel::getKV('channel_key', $this->channelKey);
-
-        if (empty($this->channel)) {
-            // TRANS: Client exception thrown when referring to a non-existing channel.
-            throw new ClientException(_m('No such channel.'));
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        $this->channel->touch();
-
-        header('HTTP/1.1 204 No Content');
-
-        return;
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * MAY override
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        return false;
-    }
-}
diff --git a/plugins/Realtime/scripts/cleanupchannels.php b/plugins/Realtime/scripts/cleanupchannels.php
new file mode 100644 (file)
index 0000000..ea195b0
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Script to print out current version of the software
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
+
+$shortoptions = 'u';
+$longoptions = array('universe');
+
+$helptext = <<<END_OF_CLEANUPCHANNELS_HELP
+cleanupchannels.php [options]
+Garbage-collects old realtime channels
+
+-u --universe Do all sites
+
+END_OF_CLEANUPCHANNELS_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+function cleanupChannels()
+{
+    $rc = new Realtime_channel();
+
+    $rc->selectAdd();
+    $rc->selectAdd('channel_key');
+
+    $rc->whereAdd('modified < "' . common_sql_date(time() - Realtime_channel::TIMEOUT) . '"');
+
+    if ($rc->find()) {
+        $keys = $rc->fetchAll();
+
+        foreach ($keys as $key) {
+            $rc = Realtime_channel::getKV('channel_key', $key);
+            if (!empty($rc)) {
+                printfv("Deleting realtime channel '$key'\n");
+                $rc->delete();
+            }
+        }
+    }
+}
+
+if (have_option('u', 'universe')) {
+    $sn = new Status_network();
+    if ($sn->find()) {
+        while ($sn->fetch()) {
+            $server = $sn->getServerName();
+            StatusNet::init($server);
+            cleanupChannels();
+        }
+    }
+} else {
+    cleanupChannels();
+}
index d6ab7fc0f4b8e8ce8d31264e144330a936d8465c..be986fa2b818ca03edc4cc832bb50c00757b8388 100644 (file)
@@ -83,27 +83,6 @@ class RegisterThrottlePlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'Registration_ip':
-            include_once $dir . '/'.$cls.'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Called when someone tries to register.
      *
diff --git a/plugins/RegisterThrottle/Registration_ip.php b/plugins/RegisterThrottle/Registration_ip.php
deleted file mode 100644 (file)
index 431b6c0..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-/**
- * Data class for storing IP addresses of new registrants.
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for storing IP addresses of new registrants.
- *
- * @category Spam
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- */
-class Registration_ip extends Managed_DataObject
-{
-    public $__table = 'registration_ip';     // table name
-    public $user_id;                         // int(4)  primary_key not_null
-    public $ipaddress;                       // varchar(15)
-    public $created;                         // datetime()   not_null
-    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id this registration relates to'),
-                'ipaddress' => array('type' => 'varchar', 'length' => 45, 'description' => 'IP address, max 45+null in IPv6'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('user_id'),
-            'foreign keys' => array(
-                'registration_ip_user_id_fkey' => array('user', array('user_id' => 'id')),
-            ),
-            'indexes' => array(
-                'registration_ip_ipaddress_idx' => array('ipaddress'),
-                'registration_ip_created_idx' => array('created'),
-            ),
-        );
-    }
-
-    /**
-     * Get the users who've registered with this ip address.
-     *
-     * @param Array $ipaddress IP address to check for
-     *
-     * @return Array IDs of users who registered with this address.
-     */
-    static function usersByIP($ipaddress)
-    {
-        $ids = array();
-
-        $ri            = new Registration_ip();
-        $ri->ipaddress = $ipaddress;
-
-        if ($ri->find()) {
-            while ($ri->fetch()) {
-                $ids[] = $ri->user_id;
-            }
-        }
-
-        return $ids;
-    }
-}
diff --git a/plugins/RegisterThrottle/classes/Registration_ip.php b/plugins/RegisterThrottle/classes/Registration_ip.php
new file mode 100644 (file)
index 0000000..431b6c0
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+/**
+ * Data class for storing IP addresses of new registrants.
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for storing IP addresses of new registrants.
+ *
+ * @category Spam
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class Registration_ip extends Managed_DataObject
+{
+    public $__table = 'registration_ip';     // table name
+    public $user_id;                         // int(4)  primary_key not_null
+    public $ipaddress;                       // varchar(15)
+    public $created;                         // datetime()   not_null
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id this registration relates to'),
+                'ipaddress' => array('type' => 'varchar', 'length' => 45, 'description' => 'IP address, max 45+null in IPv6'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('user_id'),
+            'foreign keys' => array(
+                'registration_ip_user_id_fkey' => array('user', array('user_id' => 'id')),
+            ),
+            'indexes' => array(
+                'registration_ip_ipaddress_idx' => array('ipaddress'),
+                'registration_ip_created_idx' => array('created'),
+            ),
+        );
+    }
+
+    /**
+     * Get the users who've registered with this ip address.
+     *
+     * @param Array $ipaddress IP address to check for
+     *
+     * @return Array IDs of users who registered with this address.
+     */
+    static function usersByIP($ipaddress)
+    {
+        $ids = array();
+
+        $ri            = new Registration_ip();
+        $ri->ipaddress = $ipaddress;
+
+        if ($ri->find()) {
+            while ($ri->fetch()) {
+                $ids[] = $ri->user_id;
+            }
+        }
+
+        return $ids;
+    }
+}
index 0a79796ebf1ae5b88ac7e37fdad55fc33391ced6..8ad41fe05d6620c981423684dbf17d30f4692777 100644 (file)
@@ -80,20 +80,6 @@ class RequireValidatedEmailPlugin extends Plugin
      */
     public $disallowLogin = false;
 
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'ConfirmfirstemailAction':
-            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     function onRouterInitialized($m)
     {
         $m->connect('main/confirmfirst/:code',
diff --git a/plugins/RequireValidatedEmail/actions/confirmfirstemail.php b/plugins/RequireValidatedEmail/actions/confirmfirstemail.php
new file mode 100644 (file)
index 0000000..0019d1c
--- /dev/null
@@ -0,0 +1,235 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Action for confirming first email registration
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Confirmation
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Class comment
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class ConfirmfirstemailAction extends Action
+{
+    public $confirm;
+    public $code;
+    public $password;
+    public $user;
+
+    /**
+     * For initializing members of the class.
+     *
+     * @param array $argarray misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($argarray)
+    {
+        parent::prepare($argarray);
+        $user = common_current_user();
+
+        if (!empty($user)) {
+            // TRANS: Client exception thrown when trying to register while already logged in.
+            throw new ClientException(_m('You are already logged in.'));
+        }
+
+        $this->code = $this->trimmed('code');
+
+        $this->confirm = Confirm_address::getKV('code', $this->code);
+
+        if (empty($this->confirm)) {
+            // TRANS: Client exception thrown when trying to register with a non-existing confirmation code.
+            throw new ClientException(_m('Confirmation code not found.'));
+            return;
+        }
+
+        $this->user = User::getKV('id', $this->confirm->user_id);
+
+        if (empty($this->user)) {
+            // TRANS: Client exception thrown when trying to register with a confirmation code that is not connected with a user.
+            throw new ServerException(_m('No user for that confirmation code.'));
+        }
+
+        $type = $this->confirm->address_type;
+
+        if ($type != 'email') {
+            // TRANS: Client exception thrown when trying to register with a invalid e-mail address.
+            // TRANS: %s is the invalid e-mail address.
+            throw new ServerException(sprintf(_m('Unrecognized address type %s.'), $type));
+        }
+
+        if (!empty($this->user->email) && $this->user->email == $confirm->address) {
+            // TRANS: Client error for an already confirmed email/jabber/sms address.
+            throw new ClientException(_m('That address has already been confirmed.'));
+        }
+
+        if ($this->isPost()) {
+
+            $this->checkSessionToken();
+
+            $password = $this->trimmed('password');
+            $confirm  = $this->trimmed('confirm');
+
+            if (strlen($password) < 6) {
+                // TRANS: Client exception thrown when trying to register with too short a password.
+                throw new ClientException(_m('Password too short.'));
+                return;
+            } else if (0 != strcmp($password, $confirm)) {
+                // TRANS: Client exception thrown when trying to register without providing the same password twice.
+                throw new ClientException(_m('Passwords do not match.'));
+                return;
+            }
+
+            $this->password = $password;
+        }
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $argarray is ignored since it's now passed in in prepare()
+     *
+     * @return void
+     */
+    function handle($argarray=null)
+    {
+        $homepage = common_local_url('all',
+                                     array('nickname' => $this->user->nickname));
+
+        if ($this->isPost()) {
+            $this->confirmUser();
+            common_set_user($this->user);
+            common_real_login(true);
+            common_redirect($homepage, 303);
+        } else {
+            $this->showPage();
+        }
+        return;
+    }
+
+    function confirmUser()
+    {
+        $orig = clone($this->user);
+
+        $this->user->email = $this->confirm->address;
+
+        $this->user->updateKeys($orig);
+
+        $this->user->emailChanged();
+
+        $orig = clone($this->user);
+
+        $this->user->password = common_munge_password($this->password, $this->user->id);
+
+        $this->user->update($orig);
+
+        $this->confirm->delete();
+    }
+
+    function showContent()
+    {
+        $this->element('p', 'instructions',
+                       // TRANS: Form instructions. %s is the nickname of the to be registered user.
+                       sprintf(_m('You have confirmed the email address for your new user account %s. '.
+                                 'Use the form below to set your new password.'),
+                               $this->user->nickname));
+
+        $form = new ConfirmFirstEmailForm($this, $this->code);
+        $form->show();
+    }
+
+    function title()
+    {
+        // TRANS: Page title.
+        return _m('Set a password');
+    }
+}
+
+class ConfirmFirstEmailForm extends Form
+{
+    public $code;
+
+    function __construct($out, $code)
+    {
+        parent::__construct($out);
+        $this->code = $code;
+    }
+
+    function formLegend()
+    {
+        // TRANS: Form legend.
+        return _m('Confirm email address');
+    }
+
+    function action()
+    {
+        return common_local_url('confirmfirstemail',
+                                array('code' => $this->code));
+    }
+
+    function formClass()
+    {
+        return 'form_settings';
+    }
+
+    function formData()
+    {
+        $this->out->elementStart('ul', 'form_data');
+        $this->out->elementStart('li');
+        // TRANS: Field label.
+        $this->out->password('password', _m('New password'),
+                             // TRANS: Field title for password field.
+                             _m('6 or more characters.'));
+        $this->out->elementEnd('li');
+        $this->out->elementStart('li');
+        // TRANS: Field label for repeat password field.
+        $this->out->password('confirm', _m('LABEL','Confirm'),
+                             // TRANS: Field title for repeat password field.
+                             _m('Same as password above.'));
+        $this->out->elementEnd('li');
+        $this->out->elementEnd('ul');
+    }
+
+    function formActions()
+    {
+        // TRANS: Button text for completing registration by e-mail.
+        $this->out->submit('save', _m('BUTTON','Save'));
+    }
+}
diff --git a/plugins/RequireValidatedEmail/confirmfirstemail.php b/plugins/RequireValidatedEmail/confirmfirstemail.php
deleted file mode 100644 (file)
index 0019d1c..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Action for confirming first email registration
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Confirmation
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Class comment
- *
- * @category  Action
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class ConfirmfirstemailAction extends Action
-{
-    public $confirm;
-    public $code;
-    public $password;
-    public $user;
-
-    /**
-     * For initializing members of the class.
-     *
-     * @param array $argarray misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($argarray)
-    {
-        parent::prepare($argarray);
-        $user = common_current_user();
-
-        if (!empty($user)) {
-            // TRANS: Client exception thrown when trying to register while already logged in.
-            throw new ClientException(_m('You are already logged in.'));
-        }
-
-        $this->code = $this->trimmed('code');
-
-        $this->confirm = Confirm_address::getKV('code', $this->code);
-
-        if (empty($this->confirm)) {
-            // TRANS: Client exception thrown when trying to register with a non-existing confirmation code.
-            throw new ClientException(_m('Confirmation code not found.'));
-            return;
-        }
-
-        $this->user = User::getKV('id', $this->confirm->user_id);
-
-        if (empty($this->user)) {
-            // TRANS: Client exception thrown when trying to register with a confirmation code that is not connected with a user.
-            throw new ServerException(_m('No user for that confirmation code.'));
-        }
-
-        $type = $this->confirm->address_type;
-
-        if ($type != 'email') {
-            // TRANS: Client exception thrown when trying to register with a invalid e-mail address.
-            // TRANS: %s is the invalid e-mail address.
-            throw new ServerException(sprintf(_m('Unrecognized address type %s.'), $type));
-        }
-
-        if (!empty($this->user->email) && $this->user->email == $confirm->address) {
-            // TRANS: Client error for an already confirmed email/jabber/sms address.
-            throw new ClientException(_m('That address has already been confirmed.'));
-        }
-
-        if ($this->isPost()) {
-
-            $this->checkSessionToken();
-
-            $password = $this->trimmed('password');
-            $confirm  = $this->trimmed('confirm');
-
-            if (strlen($password) < 6) {
-                // TRANS: Client exception thrown when trying to register with too short a password.
-                throw new ClientException(_m('Password too short.'));
-                return;
-            } else if (0 != strcmp($password, $confirm)) {
-                // TRANS: Client exception thrown when trying to register without providing the same password twice.
-                throw new ClientException(_m('Passwords do not match.'));
-                return;
-            }
-
-            $this->password = $password;
-        }
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $argarray is ignored since it's now passed in in prepare()
-     *
-     * @return void
-     */
-    function handle($argarray=null)
-    {
-        $homepage = common_local_url('all',
-                                     array('nickname' => $this->user->nickname));
-
-        if ($this->isPost()) {
-            $this->confirmUser();
-            common_set_user($this->user);
-            common_real_login(true);
-            common_redirect($homepage, 303);
-        } else {
-            $this->showPage();
-        }
-        return;
-    }
-
-    function confirmUser()
-    {
-        $orig = clone($this->user);
-
-        $this->user->email = $this->confirm->address;
-
-        $this->user->updateKeys($orig);
-
-        $this->user->emailChanged();
-
-        $orig = clone($this->user);
-
-        $this->user->password = common_munge_password($this->password, $this->user->id);
-
-        $this->user->update($orig);
-
-        $this->confirm->delete();
-    }
-
-    function showContent()
-    {
-        $this->element('p', 'instructions',
-                       // TRANS: Form instructions. %s is the nickname of the to be registered user.
-                       sprintf(_m('You have confirmed the email address for your new user account %s. '.
-                                 'Use the form below to set your new password.'),
-                               $this->user->nickname));
-
-        $form = new ConfirmFirstEmailForm($this, $this->code);
-        $form->show();
-    }
-
-    function title()
-    {
-        // TRANS: Page title.
-        return _m('Set a password');
-    }
-}
-
-class ConfirmFirstEmailForm extends Form
-{
-    public $code;
-
-    function __construct($out, $code)
-    {
-        parent::__construct($out);
-        $this->code = $code;
-    }
-
-    function formLegend()
-    {
-        // TRANS: Form legend.
-        return _m('Confirm email address');
-    }
-
-    function action()
-    {
-        return common_local_url('confirmfirstemail',
-                                array('code' => $this->code));
-    }
-
-    function formClass()
-    {
-        return 'form_settings';
-    }
-
-    function formData()
-    {
-        $this->out->elementStart('ul', 'form_data');
-        $this->out->elementStart('li');
-        // TRANS: Field label.
-        $this->out->password('password', _m('New password'),
-                             // TRANS: Field title for password field.
-                             _m('6 or more characters.'));
-        $this->out->elementEnd('li');
-        $this->out->elementStart('li');
-        // TRANS: Field label for repeat password field.
-        $this->out->password('confirm', _m('LABEL','Confirm'),
-                             // TRANS: Field title for repeat password field.
-                             _m('Same as password above.'));
-        $this->out->elementEnd('li');
-        $this->out->elementEnd('ul');
-    }
-
-    function formActions()
-    {
-        // TRANS: Button text for completing registration by e-mail.
-        $this->out->submit('save', _m('BUTTON','Save'));
-    }
-}
diff --git a/plugins/RequireValidatedEmail/registerbyemail.php b/plugins/RequireValidatedEmail/registerbyemail.php
deleted file mode 100644 (file)
index 4d2000a..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/usr/bin/env php
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
-
-$shortoptions = "e:";
-
-$helptext = <<<END_OF_REGISTERBYEMAIL_HELP
-USAGE: registerbyemail.php
-Registers a new user by email address and sends a confirmation email
-
-  -e email     Email to register
-
-END_OF_REGISTERBYEMAIL_HELP;
-
-require_once INSTALLDIR.'/scripts/commandline.inc';
-
-$email = get_option_value('e', 'email');
-
-$parts = explode('@', $email);
-$nickname = common_nicknamize($parts[0]);
-
-$user = User::getKV('nickname', $nickname);
-
-if (!empty($user)) {
-    $confirm = new Confirm_address();
-
-    $confirm->user_id      = $user->id;
-    $confirm->address_type = 'email';
-
-    if ($confirm->find(true)) {
-        $url = common_local_url('confirmfirstemail',
-                                array('code' => $confirm->code));
-
-        print "$url\n";
-    } else {
-        print "User not waiting for confirmation.\n";
-    }
-
-    exit;
-}
-
-$user = User::register(array('nickname' => $nickname,
-                             'password' => null));
-
-$confirm = new Confirm_address();
-$confirm->code = common_confirmation_code(128);
-$confirm->user_id = $user->id;
-$confirm->address = $email;
-$confirm->address_type = 'email';
-
-$confirm->insert();
-
-$url = common_local_url('confirmfirstemail',
-                        array('code' => $confirm->code));
-
-print "$url\n";
-
-mail_confirm_address($user,
-                     $confirm->code,
-                     $user->nickname,
-                     $email,
-                     $url);
diff --git a/plugins/RequireValidatedEmail/scripts/registerbyemail.php b/plugins/RequireValidatedEmail/scripts/registerbyemail.php
new file mode 100644 (file)
index 0000000..4d2000a
--- /dev/null
@@ -0,0 +1,80 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
+
+$shortoptions = "e:";
+
+$helptext = <<<END_OF_REGISTERBYEMAIL_HELP
+USAGE: registerbyemail.php
+Registers a new user by email address and sends a confirmation email
+
+  -e email     Email to register
+
+END_OF_REGISTERBYEMAIL_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+$email = get_option_value('e', 'email');
+
+$parts = explode('@', $email);
+$nickname = common_nicknamize($parts[0]);
+
+$user = User::getKV('nickname', $nickname);
+
+if (!empty($user)) {
+    $confirm = new Confirm_address();
+
+    $confirm->user_id      = $user->id;
+    $confirm->address_type = 'email';
+
+    if ($confirm->find(true)) {
+        $url = common_local_url('confirmfirstemail',
+                                array('code' => $confirm->code));
+
+        print "$url\n";
+    } else {
+        print "User not waiting for confirmation.\n";
+    }
+
+    exit;
+}
+
+$user = User::register(array('nickname' => $nickname,
+                             'password' => null));
+
+$confirm = new Confirm_address();
+$confirm->code = common_confirmation_code(128);
+$confirm->user_id = $user->id;
+$confirm->address = $email;
+$confirm->address_type = 'email';
+
+$confirm->insert();
+
+$url = common_local_url('confirmfirstemail',
+                        array('code' => $confirm->code));
+
+print "$url\n";
+
+mail_confirm_address($user,
+                     $confirm->code,
+                     $user->nickname,
+                     $email,
+                     $url);
index f2d333bbcb2c0f826d40b36bfda7d32b6c788b38..93654106137dc27d70e0036e288b6c61c33f0ce8 100644 (file)
@@ -174,41 +174,6 @@ class SamplePlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * Most non-trivial plugins will require extra modules to do their work. Typically
-     * these include data classes, action classes, widget classes, or external libraries.
-     *
-     * This method receives a class name and loads the PHP file related to that class. By
-     * tradition, action classes typically have files named for the action, all lower-case.
-     * Data classes are in files with the data class name, initial letter capitalized.
-     *
-     * Note that this method will be called for *all* overloaded classes, not just ones
-     * in this plugin! So, make sure to return true by default to let other plugins, and
-     * the core code, get a chance.
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'HelloAction':
-            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'User_greeting_count':
-            include_once $dir . '/'.$cls.'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Map URLs to actions
      *
diff --git a/plugins/Sample/User_greeting_count.php b/plugins/Sample/User_greeting_count.php
deleted file mode 100644 (file)
index 3da00b2..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-/**
- * Data class for counting greetings
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for counting greetings
- *
- * We use the DB_DataObject framework for data classes in StatusNet. Each
- * table maps to a particular data class, making it easier to manipulate
- * data.
- *
- * Data classes should extend Memcached_DataObject, the (slightly misnamed)
- * extension of DB_DataObject that provides caching, internationalization,
- * and other bits of good functionality to StatusNet-specific data classes.
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class User_greeting_count extends Managed_DataObject
-{
-    public $__table = 'user_greeting_count'; // table name
-    public $user_id;                         // int(4)  primary_key not_null
-    public $greeting_count;                  // int(4)
-    public $created;                         // datetime()   not_null
-    public $modified;                        // datetime   not_null default_0000-00-00%2000%3A00%3A00
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id'),
-                'greeting_count' => array('type' => 'int', 'not null' => true, 'description' => 'the greeting count'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('user_id'),
-            'foreign keys' => array(
-                'user_greeting_count_user_id_fkey' => array('user', array('user_id' => 'id')),
-            ),
-        );
-    }
-
-    /**
-     * Increment a user's greeting count and return instance
-     *
-     * This method handles the ins and outs of creating a new greeting_count for a
-     * user or fetching the existing greeting count and incrementing its value.
-     *
-     * @param integer $user_id ID of the user to get a count for
-     *
-     * @return User_greeting_count instance for this user, with count already incremented.
-     */
-    static function inc($user_id)
-    {
-        $gc = User_greeting_count::getKV('user_id', $user_id);
-
-        if (empty($gc)) {
-            $gc = new User_greeting_count();
-
-            $gc->user_id        = $user_id;
-            $gc->greeting_count = 1;
-
-            $result = $gc->insert();
-
-            if (!$result) {
-                // TRANS: Exception thrown when the user greeting count could not be saved in the database.
-                // TRANS: %d is a user ID (number).
-                throw Exception(sprintf(_m('Could not save new greeting count for %d.'),
-                                        $user_id));
-            }
-        } else {
-            $orig = clone($gc);
-
-            $gc->greeting_count++;
-
-            $result = $gc->update($orig);
-
-            if (!$result) {
-                // TRANS: Exception thrown when the user greeting count could not be saved in the database.
-                // TRANS: %d is a user ID (number).
-                throw Exception(sprintf(_m('Could not increment greeting count for %d.'),
-                                        $user_id));
-            }
-        }
-
-        return $gc;
-    }
-}
diff --git a/plugins/Sample/actions/hello.php b/plugins/Sample/actions/hello.php
new file mode 100644 (file)
index 0000000..da5682b
--- /dev/null
@@ -0,0 +1,170 @@
+<?php
+/**
+ * Give a warm greeting to our friendly user
+ *
+ * PHP version 5
+ *
+ * @category Sample
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Give a warm greeting to our friendly user
+ *
+ * This sample action shows some basic ways of doing output in an action
+ * class.
+ *
+ * Action classes have several output methods that they override from
+ * the parent class.
+ *
+ * @category Sample
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class HelloAction extends Action
+{
+    var $user = null;
+    var $gc   = null;
+
+    /**
+     * Take arguments for running
+     *
+     * This method is called first, and it lets the action class get
+     * all its arguments and validate them. It's also the time
+     * to fetch any relevant data from the database.
+     *
+     * Action classes should run parent::prepare($args) as the first
+     * line of this method to make sure the default argument-processing
+     * happens.
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     */
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $this->user = common_current_user();
+
+        if (!empty($this->user)) {
+            $this->gc = User_greeting_count::inc($this->user->id);
+        }
+
+        return true;
+    }
+
+    /**
+     * Handle request
+     *
+     * This is the main method for handling a request. Note that
+     * most preparation should be done in the prepare() method;
+     * by the time handle() is called the action should be
+     * more or less ready to go.
+     *
+     * @param array $args $_REQUEST args; handled in prepare()
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+
+        $this->showPage();
+    }
+
+    /**
+     * Title of this page
+     *
+     * Override this method to show a custom title.
+     *
+     * @return string Title of the page
+     */
+    function title()
+    {
+        if (empty($this->user)) {
+            // TRANS: Page title for sample plugin.
+            return _m('Hello');
+        } else {
+            // TRANS: Page title for sample plugin. %s is a user nickname.
+            return sprintf(_m('Hello, %s!'), $this->user->nickname);
+        }
+    }
+
+    /**
+     * Show content in the content area
+     *
+     * The default StatusNet page has a lot of decorations: menus,
+     * logos, tabs, all that jazz. This method is used to show
+     * content in the content area of the page; it's the main
+     * thing you want to overload.
+     *
+     * This method also demonstrates use of a plural localized string.
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        if (empty($this->user)) {
+            $this->element('p', array('class' => 'greeting'),
+                           // TRANS: Message in sample plugin.
+                           _m('Hello, stranger!'));
+        } else {
+            $this->element('p', array('class' => 'greeting'),
+                           // TRANS: Message in sample plugin. %s is a user nickname.
+                           sprintf(_m('Hello, %s'), $this->user->nickname));
+            $this->element('p', array('class' => 'greeting_count'),
+                           // TRANS: Message in sample plugin.
+                           // TRANS: %d is the number of times a user is greeted.
+                           sprintf(_m('I have greeted you %d time.',
+                                      'I have greeted you %d times.',
+                                      $this->gc->greeting_count),
+                                   $this->gc->greeting_count));
+        }
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * Some actions only read from the database; others read and write.
+     * The simple database load-balancer built into StatusNet will
+     * direct read-only actions to database mirrors (if they are configured),
+     * and read-write actions to the master database.
+     *
+     * This defaults to false to avoid data integrity issues, but you
+     * should make sure to overload it for performance gains.
+     *
+     * @param array $args other arguments, if RO/RW status depends on them.
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        return false;
+    }
+}
diff --git a/plugins/Sample/classes/User_greeting_count.php b/plugins/Sample/classes/User_greeting_count.php
new file mode 100644 (file)
index 0000000..3da00b2
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+/**
+ * Data class for counting greetings
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for counting greetings
+ *
+ * We use the DB_DataObject framework for data classes in StatusNet. Each
+ * table maps to a particular data class, making it easier to manipulate
+ * data.
+ *
+ * Data classes should extend Memcached_DataObject, the (slightly misnamed)
+ * extension of DB_DataObject that provides caching, internationalization,
+ * and other bits of good functionality to StatusNet-specific data classes.
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class User_greeting_count extends Managed_DataObject
+{
+    public $__table = 'user_greeting_count'; // table name
+    public $user_id;                         // int(4)  primary_key not_null
+    public $greeting_count;                  // int(4)
+    public $created;                         // datetime()   not_null
+    public $modified;                        // datetime   not_null default_0000-00-00%2000%3A00%3A00
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id'),
+                'greeting_count' => array('type' => 'int', 'not null' => true, 'description' => 'the greeting count'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('user_id'),
+            'foreign keys' => array(
+                'user_greeting_count_user_id_fkey' => array('user', array('user_id' => 'id')),
+            ),
+        );
+    }
+
+    /**
+     * Increment a user's greeting count and return instance
+     *
+     * This method handles the ins and outs of creating a new greeting_count for a
+     * user or fetching the existing greeting count and incrementing its value.
+     *
+     * @param integer $user_id ID of the user to get a count for
+     *
+     * @return User_greeting_count instance for this user, with count already incremented.
+     */
+    static function inc($user_id)
+    {
+        $gc = User_greeting_count::getKV('user_id', $user_id);
+
+        if (empty($gc)) {
+            $gc = new User_greeting_count();
+
+            $gc->user_id        = $user_id;
+            $gc->greeting_count = 1;
+
+            $result = $gc->insert();
+
+            if (!$result) {
+                // TRANS: Exception thrown when the user greeting count could not be saved in the database.
+                // TRANS: %d is a user ID (number).
+                throw Exception(sprintf(_m('Could not save new greeting count for %d.'),
+                                        $user_id));
+            }
+        } else {
+            $orig = clone($gc);
+
+            $gc->greeting_count++;
+
+            $result = $gc->update($orig);
+
+            if (!$result) {
+                // TRANS: Exception thrown when the user greeting count could not be saved in the database.
+                // TRANS: %d is a user ID (number).
+                throw Exception(sprintf(_m('Could not increment greeting count for %d.'),
+                                        $user_id));
+            }
+        }
+
+        return $gc;
+    }
+}
diff --git a/plugins/Sample/hello.php b/plugins/Sample/hello.php
deleted file mode 100644 (file)
index da5682b..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-<?php
-/**
- * Give a warm greeting to our friendly user
- *
- * PHP version 5
- *
- * @category Sample
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Give a warm greeting to our friendly user
- *
- * This sample action shows some basic ways of doing output in an action
- * class.
- *
- * Action classes have several output methods that they override from
- * the parent class.
- *
- * @category Sample
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- */
-class HelloAction extends Action
-{
-    var $user = null;
-    var $gc   = null;
-
-    /**
-     * Take arguments for running
-     *
-     * This method is called first, and it lets the action class get
-     * all its arguments and validate them. It's also the time
-     * to fetch any relevant data from the database.
-     *
-     * Action classes should run parent::prepare($args) as the first
-     * line of this method to make sure the default argument-processing
-     * happens.
-     *
-     * @param array $args $_REQUEST args
-     *
-     * @return boolean success flag
-     */
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $this->user = common_current_user();
-
-        if (!empty($this->user)) {
-            $this->gc = User_greeting_count::inc($this->user->id);
-        }
-
-        return true;
-    }
-
-    /**
-     * Handle request
-     *
-     * This is the main method for handling a request. Note that
-     * most preparation should be done in the prepare() method;
-     * by the time handle() is called the action should be
-     * more or less ready to go.
-     *
-     * @param array $args $_REQUEST args; handled in prepare()
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-
-        $this->showPage();
-    }
-
-    /**
-     * Title of this page
-     *
-     * Override this method to show a custom title.
-     *
-     * @return string Title of the page
-     */
-    function title()
-    {
-        if (empty($this->user)) {
-            // TRANS: Page title for sample plugin.
-            return _m('Hello');
-        } else {
-            // TRANS: Page title for sample plugin. %s is a user nickname.
-            return sprintf(_m('Hello, %s!'), $this->user->nickname);
-        }
-    }
-
-    /**
-     * Show content in the content area
-     *
-     * The default StatusNet page has a lot of decorations: menus,
-     * logos, tabs, all that jazz. This method is used to show
-     * content in the content area of the page; it's the main
-     * thing you want to overload.
-     *
-     * This method also demonstrates use of a plural localized string.
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        if (empty($this->user)) {
-            $this->element('p', array('class' => 'greeting'),
-                           // TRANS: Message in sample plugin.
-                           _m('Hello, stranger!'));
-        } else {
-            $this->element('p', array('class' => 'greeting'),
-                           // TRANS: Message in sample plugin. %s is a user nickname.
-                           sprintf(_m('Hello, %s'), $this->user->nickname));
-            $this->element('p', array('class' => 'greeting_count'),
-                           // TRANS: Message in sample plugin.
-                           // TRANS: %d is the number of times a user is greeted.
-                           sprintf(_m('I have greeted you %d time.',
-                                      'I have greeted you %d times.',
-                                      $this->gc->greeting_count),
-                                   $this->gc->greeting_count));
-        }
-    }
-
-    /**
-     * Return true if read only.
-     *
-     * Some actions only read from the database; others read and write.
-     * The simple database load-balancer built into StatusNet will
-     * direct read-only actions to database mirrors (if they are configured),
-     * and read-write actions to the master database.
-     *
-     * This defaults to false to avoid data integrity issues, but you
-     * should make sure to overload it for performance gains.
-     *
-     * @param array $args other arguments, if RO/RW status depends on them.
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        return false;
-    }
-}
diff --git a/plugins/SearchSub/SearchSub.php b/plugins/SearchSub/SearchSub.php
deleted file mode 100644 (file)
index f3a4485..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-/**
- * Data class to store local search subscriptions
- *
- * PHP version 5
- *
- * @category SearchSubPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * For storing the search subscriptions
- *
- * @category PollPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-
-class SearchSub extends Managed_DataObject
-{
-    public $__table = 'searchsub'; // table name
-    public $search;         // text
-    public $profile_id;  // int -> profile.id
-    public $created;     // datetime
-
-    /**
-     * The One True Thingy that must be defined and declared.
-     */
-    public static function schemaDef()
-    {
-        return array(
-            'description' => 'SearchSubPlugin search subscription records',
-            'fields' => array(
-                'search' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'hash search associated with this subscription'),
-                'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile ID of subscribing user'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-            ),
-            'primary key' => array('search', 'profile_id'),
-            'foreign keys' => array(
-                'searchsub_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
-            ),
-            'indexes' => array(
-                'searchsub_created_idx' => array('created'),
-                'searchsub_profile_id_tag_idx' => array('profile_id', 'search'),
-            ),
-        );
-    }
-
-    /**
-     * Start a search subscription!
-     *
-     * @param profile $profile subscriber
-     * @param string $search subscribee
-     * @return SearchSub
-     */
-    static function start(Profile $profile, $search)
-    {
-        $ts = new SearchSub();
-        $ts->search = $search;
-        $ts->profile_id = $profile->id;
-        $ts->created = common_sql_now();
-        $ts->insert();
-        self::blow('searchsub:by_profile:%d', $profile->id);
-        return $ts;
-    }
-
-    /**
-     * End a search subscription!
-     *
-     * @param profile $profile subscriber
-     * @param string $search subscribee
-     */
-    static function cancel(Profile $profile, $search)
-    {
-        $ts = SearchSub::pkeyGet(array('search' => $search,
-                                    'profile_id' => $profile->id));
-        if ($ts) {
-            $ts->delete();
-            self::blow('searchsub:by_profile:%d', $profile->id);
-        }
-    }
-
-    static function forProfile(Profile $profile)
-    {
-        $searches = array();
-
-        $keypart = sprintf('searchsub:by_profile:%d', $profile->id);
-        $searchstring = self::cacheGet($keypart);
-        
-        if ($searchstring !== false) {
-               if (!empty($searchstring)) {
-               $searches = explode(',', $searchstring);
-               }
-        } else {
-            $searchsub = new SearchSub();
-            $searchsub->profile_id = $profile->id;
-            $searchsub->selectAdd();
-            $searchsub->selectAdd('search');
-
-            if ($searchsub->find()) {
-                $searches = $searchsub->fetchAll('search');
-            }
-
-            self::cacheSet($keypart, implode(',', $searches));
-        }
-
-        return $searches;
-    }
-}
index 858474240e9c6c1eed3dff6c103db99331825fad..ba25bf1b2b52dab9cafdce1208b7b9fe46e391ed 100644 (file)
@@ -60,39 +60,6 @@ class SearchSubPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'SearchSub':
-            include_once $dir.'/'.$cls.'.php';
-            return false;
-        case 'SearchsubAction':
-        case 'SearchunsubAction':
-        case 'SearchsubsAction':
-        case 'SearchSubForm':
-        case 'SearchSubMenu':
-        case 'SearchUnsubForm':
-        case 'SearchSubTrackCommand':
-        case 'SearchSubTrackOffCommand':
-        case 'SearchSubTrackingCommand':
-        case 'SearchSubUntrackCommand':
-            include_once $dir.'/'.strtolower($cls).'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Map URLs to actions
      *
diff --git a/plugins/SearchSub/actions/searchsub.php b/plugins/SearchSub/actions/searchsub.php
new file mode 100644 (file)
index 0000000..0234fee
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008-2011, StatusNet, Inc.
+ *
+ * Search subscription action.
+ *
+ * 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/>.
+ *
+ * PHP version 5
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Search subscription action
+ *
+ * Takes parameters:
+ *
+ *    - token: session token to prevent CSRF attacks
+ *    - ajax: boolean; whether to return Ajax or full-browser results
+ *
+ * Only works if the current user is logged in.
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2008-2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link      http://status.net/
+ */
+class SearchsubAction extends Action
+{
+    var $user;
+    var $search;
+
+    /**
+     * Check pre-requisites and instantiate attributes
+     *
+     * @param Array $args array of arguments (URL, GET, POST)
+     *
+     * @return boolean success flag
+     */
+    function prepare($args)
+    {
+        parent::prepare($args);
+        if ($this->boolean('ajax')) {
+            StatusNet::setApi(true);
+        }
+
+        // Only allow POST requests
+
+        if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+            // TRANS: Client error displayed trying to perform any request method other than POST.
+            // TRANS: Do not translate POST.
+            $this->clientError(_m('This action only accepts POST requests.'));
+            return false;
+        }
+
+        // CSRF protection
+
+        $token = $this->trimmed('token');
+
+        if (!$token || $token != common_session_token()) {
+            // TRANS: Client error displayed when the session token is not okay.
+            $this->clientError(_m('There was a problem with your session token.'.
+                                 ' Try again, please.'));
+            return false;
+        }
+
+        // Only for logged-in users
+
+        $this->user = common_current_user();
+
+        if (empty($this->user)) {
+            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
+            $this->clientError(_m('Not logged in.'));
+            return false;
+        }
+
+        // Profile to subscribe to
+
+        $this->search = $this->arg('search');
+
+        if (empty($this->search)) {
+            // TRANS: Client error displayed trying to subscribe to a non-existing profile.
+            $this->clientError(_m('No such profile.'));
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Handle request
+     *
+     * Does the subscription and returns results.
+     *
+     * @param Array $args unused.
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        // Throws exception on error
+
+        SearchSub::start($this->user->getProfile(),
+                      $this->search);
+
+        if ($this->boolean('ajax')) {
+            $this->startHTML('text/xml;charset=utf-8');
+            $this->elementStart('head');
+            // TRANS: Page title when search subscription succeeded.
+            $this->element('title', null, _m('Subscribed'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $unsubscribe = new SearchUnsubForm($this, $this->search);
+            $unsubscribe->show();
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            $url = common_local_url('search',
+                                    array('search' => $this->search));
+            common_redirect($url, 303);
+        }
+    }
+}
diff --git a/plugins/SearchSub/actions/searchsubs.php b/plugins/SearchSub/actions/searchsubs.php
new file mode 100644 (file)
index 0000000..54563ed
--- /dev/null
@@ -0,0 +1,196 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * List of a user's subscriptions
+ *
+ * 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  Social
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @author    Sarven Capadisli <csarven@status.net>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * A list of the user's subscriptions
+ *
+ * @category Social
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class SearchSubsAction extends GalleryAction
+{
+    function title()
+    {
+        if ($this->page == 1) {
+            // TRANS: Header for subscriptions overview for a user (first page).
+            // TRANS: %s is a user nickname.
+            return sprintf(_m('%s\'s search subscriptions'), $this->user->nickname);
+        } else {
+            // TRANS: Header for subscriptions overview for a user (not first page).
+            // TRANS: %1$s is a user nickname, %2$d is the page number.
+            return sprintf(_m('%1$s\'s search subscriptions, page %2$d'),
+                           $this->user->nickname,
+                           $this->page);
+        }
+    }
+
+    function showPageNotice()
+    {
+        $user = common_current_user();
+        if ($user && ($user->id == $this->profile->id)) {
+            $this->element('p', null,
+                           // TRANS: Page notice for page with an overview of all search subscriptions
+                           // TRANS: of the logged in user's own profile.
+                           _m('You have subscribed to receive all notices on this site matching the following searches:'));
+        } else {
+            $this->element('p', null,
+                           // TRANS: Page notice for page with an overview of all subscriptions of a user other
+                           // TRANS: than the logged in user. %s is the user nickname.
+                           sprintf(_m('%s has subscribed to receive all notices on this site matching the following searches:'),
+                                   $this->profile->nickname));
+        }
+    }
+
+    function showContent()
+    {
+        if (Event::handle('StartShowTagSubscriptionsContent', array($this))) {
+            parent::showContent();
+
+            $offset = ($this->page-1) * PROFILES_PER_PAGE;
+            $limit =  PROFILES_PER_PAGE + 1;
+
+            $cnt = 0;
+
+            $searchsub = new SearchSub();
+            $searchsub->profile_id = $this->user->id;
+            $searchsub->limit($limit, $offset);
+            $searchsub->find();
+
+            if ($searchsub->N) {
+                $list = new SearchSubscriptionsList($searchsub, $this->user, $this);
+                $cnt = $list->show();
+                if (0 == $cnt) {
+                    $this->showEmptyListMessage();
+                }
+            } else {
+                $this->showEmptyListMessage();
+            }
+
+            $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
+                              $this->page, 'searchsubs',
+                              array('nickname' => $this->user->nickname));
+
+
+            Event::handle('EndShowTagSubscriptionsContent', array($this));
+        }
+    }
+
+    function showEmptyListMessage()
+    {
+        if (common_logged_in()) {
+            $current_user = common_current_user();
+            if ($this->user->id === $current_user->id) {
+                // TRANS: Search subscription list text when the logged in user has no search subscriptions.
+                $message = _m('You are not subscribed to any text searches right now. You can push the "Subscribe" button ' .
+                             'on any notice text search to automatically receive any public messages on this site that match that ' .
+                             'search, even if you are not subscribed to the poster.');
+            } else {
+                // TRANS: Search subscription list text when looking at the subscriptions for a of a user other
+                // TRANS: than the logged in user that has no search subscriptions. %s is the user nickname.
+                $message = sprintf(_m('%s is not subscribed to any searches.'), $this->user->nickname);
+            }
+        }
+        else {
+            // TRANS: Subscription list text when looking at the subscriptions for a of a user that has none
+            // TRANS: as an anonymous user. %s is the user nickname.
+            $message = sprintf(_m('%s is not subscribed to any searches.'), $this->user->nickname);
+        }
+
+        $this->elementStart('div', 'guide');
+        $this->raw(common_markup_to_html($message));
+        $this->elementEnd('div');
+    }
+}
+
+// XXX SubscriptionsList and SubscriptionList are dangerously close
+
+class SearchSubscriptionsList extends SubscriptionList
+{
+    function newListItem($searchsub)
+    {
+        return new SearchSubscriptionsListItem($searchsub, $this->owner, $this->action);
+    }
+}
+
+class SearchSubscriptionsListItem extends SubscriptionListItem
+{
+    function startItem()
+    {
+        $this->out->elementStart('li', array('class' => 'searchsub'));
+    }
+
+    function showProfile()
+    {
+        $searchsub = $this->profile;
+        $search = $searchsub->search;
+
+        // Relevant portion!
+        $cur = common_current_user();
+        if (!empty($cur) && $cur->id == $this->owner->id) {
+            $this->showOwnerControls();
+        }
+
+        $url = common_local_url('noticesearch', array('q' => $search));
+        // TRANS: Search subscription list item. %1$s is a URL to a notice search,
+        // TRANS: %2$s are the search criteria, %3$s is a datestring.
+        $linkline = sprintf(_m('"<a href="%1$s">%2$s</a>" since %3$s'),
+                            htmlspecialchars($url),
+                            htmlspecialchars($search),
+                            common_date_string($searchsub->created));
+
+        $this->out->elementStart('div', 'searchsub-item');
+        $this->out->raw($linkline);
+        $this->out->element('div', array('style' => 'clear: both'));
+        $this->out->elementEnd('div');
+    }
+
+    function showActions()
+    {
+    }
+
+    function showOwnerControls()
+    {
+        $this->out->elementStart('div', 'entity_actions');
+
+        $searchsub = $this->profile; // ?
+        $form = new SearchUnsubForm($this->out, $searchsub->search);
+        $form->show();
+
+        $this->out->elementEnd('div');
+        return;
+    }
+}
diff --git a/plugins/SearchSub/actions/searchunsub.php b/plugins/SearchSub/actions/searchunsub.php
new file mode 100644 (file)
index 0000000..f7f006e
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008-2011, StatusNet, Inc.
+ *
+ * Search subscription action.
+ *
+ * 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/>.
+ *
+ * PHP version 5
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Search unsubscription action
+ *
+ * Takes parameters:
+ *
+ *    - token: session token to prevent CSRF attacks
+ *    - ajax: boolean; whether to return Ajax or full-browser results
+ *
+ * Only works if the current user is logged in.
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2008-2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link      http://status.net/
+ */
+class SearchunsubAction extends SearchsubAction
+{
+    /**
+     * Handle request
+     *
+     * Does the subscription and returns results.
+     *
+     * @param Array $args unused.
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        // Throws exception on error
+
+        SearchSub::cancel($this->user->getProfile(),
+                       $this->search);
+
+        if ($this->boolean('ajax')) {
+            $this->startHTML('text/xml;charset=utf-8');
+            $this->elementStart('head');
+            // TRANS: Page title when search unsubscription succeeded.
+            $this->element('title', null, _m('Unsubscribed'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $subscribe = new SearchSubForm($this, $this->search);
+            $subscribe->show();
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            $url = common_local_url('search',
+                                    array('search' => $this->search));
+            common_redirect($url, 303);
+        }
+    }
+}
diff --git a/plugins/SearchSub/classes/SearchSub.php b/plugins/SearchSub/classes/SearchSub.php
new file mode 100644 (file)
index 0000000..f3a4485
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+/**
+ * Data class to store local search subscriptions
+ *
+ * PHP version 5
+ *
+ * @category SearchSubPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * For storing the search subscriptions
+ *
+ * @category PollPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+
+class SearchSub extends Managed_DataObject
+{
+    public $__table = 'searchsub'; // table name
+    public $search;         // text
+    public $profile_id;  // int -> profile.id
+    public $created;     // datetime
+
+    /**
+     * The One True Thingy that must be defined and declared.
+     */
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'SearchSubPlugin search subscription records',
+            'fields' => array(
+                'search' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'hash search associated with this subscription'),
+                'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile ID of subscribing user'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+            ),
+            'primary key' => array('search', 'profile_id'),
+            'foreign keys' => array(
+                'searchsub_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
+            ),
+            'indexes' => array(
+                'searchsub_created_idx' => array('created'),
+                'searchsub_profile_id_tag_idx' => array('profile_id', 'search'),
+            ),
+        );
+    }
+
+    /**
+     * Start a search subscription!
+     *
+     * @param profile $profile subscriber
+     * @param string $search subscribee
+     * @return SearchSub
+     */
+    static function start(Profile $profile, $search)
+    {
+        $ts = new SearchSub();
+        $ts->search = $search;
+        $ts->profile_id = $profile->id;
+        $ts->created = common_sql_now();
+        $ts->insert();
+        self::blow('searchsub:by_profile:%d', $profile->id);
+        return $ts;
+    }
+
+    /**
+     * End a search subscription!
+     *
+     * @param profile $profile subscriber
+     * @param string $search subscribee
+     */
+    static function cancel(Profile $profile, $search)
+    {
+        $ts = SearchSub::pkeyGet(array('search' => $search,
+                                    'profile_id' => $profile->id));
+        if ($ts) {
+            $ts->delete();
+            self::blow('searchsub:by_profile:%d', $profile->id);
+        }
+    }
+
+    static function forProfile(Profile $profile)
+    {
+        $searches = array();
+
+        $keypart = sprintf('searchsub:by_profile:%d', $profile->id);
+        $searchstring = self::cacheGet($keypart);
+        
+        if ($searchstring !== false) {
+               if (!empty($searchstring)) {
+               $searches = explode(',', $searchstring);
+               }
+        } else {
+            $searchsub = new SearchSub();
+            $searchsub->profile_id = $profile->id;
+            $searchsub->selectAdd();
+            $searchsub->selectAdd('search');
+
+            if ($searchsub->find()) {
+                $searches = $searchsub->fetchAll('search');
+            }
+
+            self::cacheSet($keypart, implode(',', $searches));
+        }
+
+        return $searches;
+    }
+}
diff --git a/plugins/SearchSub/forms/searchsub.php b/plugins/SearchSub/forms/searchsub.php
new file mode 100644 (file)
index 0000000..4d44910
--- /dev/null
@@ -0,0 +1,140 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for subscribing to a search
+ *
+ * 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  SearchSubPlugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @author    Evan Prodromou <evan@status.net>
+ * @author    Sarven Capadisli <csarven@status.net>
+ * @copyright 2009-2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Form for subscribing to a user
+ *
+ * @category SearchSubPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Sarven Capadisli <csarven@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ * @see      UnsubscribeForm
+ */
+class SearchSubForm extends Form
+{
+    /**
+     * Name of search to subscribe to
+     */
+    var $search = '';
+
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out     output channel
+     * @param string        $search     name of search to subscribe to
+     */
+    function __construct($out=null, $search=null)
+    {
+        parent::__construct($out);
+
+        $this->search = $search;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'search-subscribe-' . $this->search;
+    }
+
+
+    /**
+     * class of the form
+     *
+     * @return string of the form class
+     */
+    function formClass()
+    {
+        // class to match existing styles...
+        return 'form_user_subscribe ajax';
+    }
+
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('searchsub', array('search' => $this->search));
+    }
+
+    /**
+     * Legend of the Form
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+        // TRANS: Form legend.
+        $this->out->element('legend', null, _m('Subscribe to this search'));
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->hidden('subscribeto-' . $this->search,
+                           $this->search,
+                           'subscribeto');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        $this->out->submit('submit',
+                           // TRANS: Button text for subscribing to a search.
+                           _m('BUTTON','Subscribe'),
+                           'submit',
+                           null,
+                           // TRANS: Button title for subscribing to a search.
+                           _m('Subscribe to this search.'));
+    }
+}
diff --git a/plugins/SearchSub/forms/searchunsub.php b/plugins/SearchSub/forms/searchunsub.php
new file mode 100644 (file)
index 0000000..ae07f8d
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for subscribing to a search
+ *
+ * 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  SearchSubPlugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @author    Evan Prodromou <evan@status.net>
+ * @author    Sarven Capadisli <csarven@status.net>
+ * @copyright 2009-2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Form for subscribing to a user
+ *
+ * @category SearchSubPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Sarven Capadisli <csarven@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ * @see      UnsubscribeForm
+ */
+class SearchUnsubForm extends SearchSubForm
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'search-unsubscribe-' . $this->search;
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string of the form class
+     */
+    function formClass()
+    {
+        // class to match existing styles...
+        return 'form_user_unsubscribe ajax';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('searchunsub', array('search' => $this->search));
+    }
+
+    /**
+     * Legend of the Form
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+        // TRANS: Form legend.
+        $this->out->element('legend', null, _m('Unsubscribe from this search'));
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        $this->out->submit('submit',
+                           // TRANS: Button text for unsubscribing from a text search.
+                           _m('BUTTON','Unsubscribe'),
+                           'submit',
+                           null,
+                           // TRANS: Button title for unsubscribing from a text search.
+                           _m('Unsubscribe from this search.'));
+    }
+}
diff --git a/plugins/SearchSub/lib/searchsubmenu.php b/plugins/SearchSub/lib/searchsubmenu.php
new file mode 100644 (file)
index 0000000..fa5b349
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Menu to show searches you're subscribed to
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Menu
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Class comment
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class SearchSubMenu extends MoreMenu
+{
+    protected $user;
+    protected $searches;
+
+    function __construct($out, $user, $searches)
+    {
+        parent::__construct($out);
+        $this->user = $user;
+        $this->searches = $searches;
+    }
+
+    function tag()
+    {
+        return 'searchsubs';
+    }
+
+    function seeAllItem()
+    {
+        return array('searchsubs',
+                     array('nickname' => $this->user->nickname),
+                     _('See all'),
+                     _('See all searches you are following'));
+    }
+
+    function getItems()
+    {
+        $items = array();
+        
+        foreach ($this->searches as $search) {
+            if (!empty($search)) {
+                $items[] = array('noticesearch',
+                                 array('q' => $search),
+                                 sprintf('"%s"', $search),
+                                 sprintf(_('Notices including %s'), $search));;
+            }
+        }
+
+        return $items;
+    } 
+
+    function item($actionName, $args, $label, $description, $id=null, $cls=null)
+    {
+        if (empty($id)) {
+            $id = $this->menuItemID($actionName, $args);
+        }
+
+        if ($actionName == 'noticesearch') {
+            // Add 'q' as a search param, not part of the url path
+            $url = common_local_url($actionName, array(), $args);
+        } else {
+            $url = common_local_url($actionName, $args);
+        }
+
+        $this->out->menuItem($url,
+                             $label,
+                             $description,
+                             $this->isCurrent($actionName, $args),
+                             $id,
+                             $cls);
+    }
+}
+
diff --git a/plugins/SearchSub/lib/searchsubtrackcommand.php b/plugins/SearchSub/lib/searchsubtrackcommand.php
new file mode 100644 (file)
index 0000000..bba2cb3
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+class SearchSubTrackCommand extends Command
+{
+    var $keyword = null;
+
+    function __construct($user, $keyword)
+    {
+        parent::__construct($user);
+        $this->keyword = $keyword;
+    }
+
+    function handle($channel)
+    {
+        $cur = $this->user;
+        $searchsub = SearchSub::pkeyGet(array('search' => $this->keyword,
+                                              'profile_id' => $cur->id));
+
+        if ($searchsub) {
+            // TRANS: Error text shown a user tries to track a search query they're already subscribed to.
+            $channel->error($cur, sprintf(_m('You are already tracking the search "%s".'), $this->keyword));
+            return;
+        }
+
+        try {
+            SearchSub::start($cur->getProfile(), $this->keyword);
+        } catch (Exception $e) {
+            // TRANS: Message given having failed to set up a search subscription by track command.
+            $channel->error($cur, sprintf(_m('Could not start a search subscription for query "%s".'),
+                                          $this->keyword));
+            return;
+        }
+
+        // TRANS: Message given having added a search subscription by track command.
+        $channel->output($cur, sprintf(_m('You are subscribed to the search "%s".'),
+                                              $this->keyword));
+    }
+}
\ No newline at end of file
diff --git a/plugins/SearchSub/lib/searchsubtrackingcommand.php b/plugins/SearchSub/lib/searchsubtrackingcommand.php
new file mode 100644 (file)
index 0000000..a404fd4
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+class SearchSubTrackingCommand extends Command
+{
+    function handle($channel)
+    {
+        $cur = $this->user;
+        $all = new SearchSub();
+        $all->profile_id = $cur->id;
+        $all->find();
+
+        if ($all->N == 0) {
+            // TRANS: Error text shown a user tries to disable all a search subscriptions with track off command, but has none.
+            $channel->error($cur, _m('You are not tracking any searches.'));
+            return;
+        }
+
+        $list = array();
+        while ($all->fetch()) {
+            $list[] = $all->search;
+        }
+
+        // TRANS: Separator for list of tracked searches.
+        $separator = _m('SEPARATOR','", "');
+
+        // TRANS: Message given having disabled all search subscriptions with 'track off'.
+        // TRANS: %s is a list of searches. Separator default is '", "'.
+        $channel->output($cur, sprintf(_m('You are tracking searches for: "%s".'),
+                                       implode($separator, $list)));
+    }
+}
diff --git a/plugins/SearchSub/lib/searchsubtrackoffcommand.php b/plugins/SearchSub/lib/searchsubtrackoffcommand.php
new file mode 100644 (file)
index 0000000..4ab78a5
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+class SearchSubTrackoffCommand extends Command
+{
+    function handle($channel)
+    {
+        $cur = $this->user;
+        $all = new SearchSub();
+        $all->profile_id = $cur->id;
+        $all->find();
+
+        if ($all->N == 0) {
+            // TRANS: Error text shown a user tries to disable all a search subscriptions with track off command, but has none.
+            $channel->error($cur, _m('You are not tracking any searches.'));
+            return;
+        }
+
+        $profile = $cur->getProfile();
+        while ($all->fetch()) {
+            try {
+                SearchSub::cancel($profile, $all->search);
+            } catch (Exception $e) {
+                // TRANS: Message given having failed to cancel one of the search subs with 'track off' command.
+                // TRANS: %s is the search for which the subscription removal failed.
+                $channel->error($cur, sprintf(_m('Error disabling search subscription for query "%s".'),
+                                              $all->search));
+                return;
+            }
+        }
+
+        // TRANS: Message given having disabled all search subscriptions with 'track off'.
+        $channel->output($cur, _m('Disabled all your search subscriptions.'));
+    }
+}
diff --git a/plugins/SearchSub/lib/searchsubuntrackcommand.php b/plugins/SearchSub/lib/searchsubuntrackcommand.php
new file mode 100644 (file)
index 0000000..fb3d78a
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+class SearchSubUntrackCommand extends Command
+{
+    var $keyword = null;
+
+    function __construct($user, $keyword)
+    {
+        parent::__construct($user);
+        $this->keyword = $keyword;
+    }
+
+    function handle($channel)
+    {
+        $cur = $this->user;
+        $searchsub = SearchSub::pkeyGet(array('search' => $this->keyword,
+                                              'profile_id' => $cur->id));
+
+        if (!$searchsub) {
+            // TRANS: Error text shown a user tries to untrack a search query they're not subscribed to.
+            // TRANS: %s is the keyword for the search.
+            $channel->error($cur, sprintf(_m('You are not tracking the search "%s".'), $this->keyword));
+            return;
+        }
+
+        try {
+            SearchSub::cancel($cur->getProfile(), $this->keyword);
+        } catch (Exception $e) {
+            // TRANS: Message given having failed to cancel a search subscription by untrack command.
+            // TRANS: %s is the keyword for the query.
+            $channel->error($cur, sprintf(_m('Could not end a search subscription for query "%s".'),
+                                          $this->keyword));
+            return;
+        }
+
+        // TRANS: Message given having removed a search subscription by untrack command.
+        // TRANS: %s is the keyword for the search.
+        $channel->output($cur, sprintf(_m('You are no longer subscribed to the search "%s".'),
+                                              $this->keyword));
+    }
+}
diff --git a/plugins/SearchSub/searchsubaction.php b/plugins/SearchSub/searchsubaction.php
deleted file mode 100644 (file)
index 0234fee..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008-2011, StatusNet, Inc.
- *
- * Search subscription action.
- *
- * 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/>.
- *
- * PHP version 5
- *
- * @category  Action
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008-2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Search subscription action
- *
- * Takes parameters:
- *
- *    - token: session token to prevent CSRF attacks
- *    - ajax: boolean; whether to return Ajax or full-browser results
- *
- * Only works if the current user is logged in.
- *
- * @category  Action
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2008-2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
- * @link      http://status.net/
- */
-class SearchsubAction extends Action
-{
-    var $user;
-    var $search;
-
-    /**
-     * Check pre-requisites and instantiate attributes
-     *
-     * @param Array $args array of arguments (URL, GET, POST)
-     *
-     * @return boolean success flag
-     */
-    function prepare($args)
-    {
-        parent::prepare($args);
-        if ($this->boolean('ajax')) {
-            StatusNet::setApi(true);
-        }
-
-        // Only allow POST requests
-
-        if ($_SERVER['REQUEST_METHOD'] != 'POST') {
-            // TRANS: Client error displayed trying to perform any request method other than POST.
-            // TRANS: Do not translate POST.
-            $this->clientError(_m('This action only accepts POST requests.'));
-            return false;
-        }
-
-        // CSRF protection
-
-        $token = $this->trimmed('token');
-
-        if (!$token || $token != common_session_token()) {
-            // TRANS: Client error displayed when the session token is not okay.
-            $this->clientError(_m('There was a problem with your session token.'.
-                                 ' Try again, please.'));
-            return false;
-        }
-
-        // Only for logged-in users
-
-        $this->user = common_current_user();
-
-        if (empty($this->user)) {
-            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
-            $this->clientError(_m('Not logged in.'));
-            return false;
-        }
-
-        // Profile to subscribe to
-
-        $this->search = $this->arg('search');
-
-        if (empty($this->search)) {
-            // TRANS: Client error displayed trying to subscribe to a non-existing profile.
-            $this->clientError(_m('No such profile.'));
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Handle request
-     *
-     * Does the subscription and returns results.
-     *
-     * @param Array $args unused.
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        // Throws exception on error
-
-        SearchSub::start($this->user->getProfile(),
-                      $this->search);
-
-        if ($this->boolean('ajax')) {
-            $this->startHTML('text/xml;charset=utf-8');
-            $this->elementStart('head');
-            // TRANS: Page title when search subscription succeeded.
-            $this->element('title', null, _m('Subscribed'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $unsubscribe = new SearchUnsubForm($this, $this->search);
-            $unsubscribe->show();
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            $url = common_local_url('search',
-                                    array('search' => $this->search));
-            common_redirect($url, 303);
-        }
-    }
-}
diff --git a/plugins/SearchSub/searchsubform.php b/plugins/SearchSub/searchsubform.php
deleted file mode 100644 (file)
index 4d44910..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Form for subscribing to a search
- *
- * 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  SearchSubPlugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @author    Evan Prodromou <evan@status.net>
- * @author    Sarven Capadisli <csarven@status.net>
- * @copyright 2009-2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * Form for subscribing to a user
- *
- * @category SearchSubPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @author   Evan Prodromou <evan@status.net>
- * @author   Sarven Capadisli <csarven@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- * @see      UnsubscribeForm
- */
-class SearchSubForm extends Form
-{
-    /**
-     * Name of search to subscribe to
-     */
-    var $search = '';
-
-    /**
-     * Constructor
-     *
-     * @param HTMLOutputter $out     output channel
-     * @param string        $search     name of search to subscribe to
-     */
-    function __construct($out=null, $search=null)
-    {
-        parent::__construct($out);
-
-        $this->search = $search;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'search-subscribe-' . $this->search;
-    }
-
-
-    /**
-     * class of the form
-     *
-     * @return string of the form class
-     */
-    function formClass()
-    {
-        // class to match existing styles...
-        return 'form_user_subscribe ajax';
-    }
-
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('searchsub', array('search' => $this->search));
-    }
-
-    /**
-     * Legend of the Form
-     *
-     * @return void
-     */
-    function formLegend()
-    {
-        // TRANS: Form legend.
-        $this->out->element('legend', null, _m('Subscribe to this search'));
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->hidden('subscribeto-' . $this->search,
-                           $this->search,
-                           'subscribeto');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        $this->out->submit('submit',
-                           // TRANS: Button text for subscribing to a search.
-                           _m('BUTTON','Subscribe'),
-                           'submit',
-                           null,
-                           // TRANS: Button title for subscribing to a search.
-                           _m('Subscribe to this search.'));
-    }
-}
diff --git a/plugins/SearchSub/searchsubmenu.php b/plugins/SearchSub/searchsubmenu.php
deleted file mode 100644 (file)
index fa5b349..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Menu to show searches you're subscribed to
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Menu
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Class comment
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class SearchSubMenu extends MoreMenu
-{
-    protected $user;
-    protected $searches;
-
-    function __construct($out, $user, $searches)
-    {
-        parent::__construct($out);
-        $this->user = $user;
-        $this->searches = $searches;
-    }
-
-    function tag()
-    {
-        return 'searchsubs';
-    }
-
-    function seeAllItem()
-    {
-        return array('searchsubs',
-                     array('nickname' => $this->user->nickname),
-                     _('See all'),
-                     _('See all searches you are following'));
-    }
-
-    function getItems()
-    {
-        $items = array();
-        
-        foreach ($this->searches as $search) {
-            if (!empty($search)) {
-                $items[] = array('noticesearch',
-                                 array('q' => $search),
-                                 sprintf('"%s"', $search),
-                                 sprintf(_('Notices including %s'), $search));;
-            }
-        }
-
-        return $items;
-    } 
-
-    function item($actionName, $args, $label, $description, $id=null, $cls=null)
-    {
-        if (empty($id)) {
-            $id = $this->menuItemID($actionName, $args);
-        }
-
-        if ($actionName == 'noticesearch') {
-            // Add 'q' as a search param, not part of the url path
-            $url = common_local_url($actionName, array(), $args);
-        } else {
-            $url = common_local_url($actionName, $args);
-        }
-
-        $this->out->menuItem($url,
-                             $label,
-                             $description,
-                             $this->isCurrent($actionName, $args),
-                             $id,
-                             $cls);
-    }
-}
-
diff --git a/plugins/SearchSub/searchsubsaction.php b/plugins/SearchSub/searchsubsaction.php
deleted file mode 100644 (file)
index 54563ed..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * List of a user's subscriptions
- *
- * 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  Social
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @author    Sarven Capadisli <csarven@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * A list of the user's subscriptions
- *
- * @category Social
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class SearchSubsAction extends GalleryAction
-{
-    function title()
-    {
-        if ($this->page == 1) {
-            // TRANS: Header for subscriptions overview for a user (first page).
-            // TRANS: %s is a user nickname.
-            return sprintf(_m('%s\'s search subscriptions'), $this->user->nickname);
-        } else {
-            // TRANS: Header for subscriptions overview for a user (not first page).
-            // TRANS: %1$s is a user nickname, %2$d is the page number.
-            return sprintf(_m('%1$s\'s search subscriptions, page %2$d'),
-                           $this->user->nickname,
-                           $this->page);
-        }
-    }
-
-    function showPageNotice()
-    {
-        $user = common_current_user();
-        if ($user && ($user->id == $this->profile->id)) {
-            $this->element('p', null,
-                           // TRANS: Page notice for page with an overview of all search subscriptions
-                           // TRANS: of the logged in user's own profile.
-                           _m('You have subscribed to receive all notices on this site matching the following searches:'));
-        } else {
-            $this->element('p', null,
-                           // TRANS: Page notice for page with an overview of all subscriptions of a user other
-                           // TRANS: than the logged in user. %s is the user nickname.
-                           sprintf(_m('%s has subscribed to receive all notices on this site matching the following searches:'),
-                                   $this->profile->nickname));
-        }
-    }
-
-    function showContent()
-    {
-        if (Event::handle('StartShowTagSubscriptionsContent', array($this))) {
-            parent::showContent();
-
-            $offset = ($this->page-1) * PROFILES_PER_PAGE;
-            $limit =  PROFILES_PER_PAGE + 1;
-
-            $cnt = 0;
-
-            $searchsub = new SearchSub();
-            $searchsub->profile_id = $this->user->id;
-            $searchsub->limit($limit, $offset);
-            $searchsub->find();
-
-            if ($searchsub->N) {
-                $list = new SearchSubscriptionsList($searchsub, $this->user, $this);
-                $cnt = $list->show();
-                if (0 == $cnt) {
-                    $this->showEmptyListMessage();
-                }
-            } else {
-                $this->showEmptyListMessage();
-            }
-
-            $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
-                              $this->page, 'searchsubs',
-                              array('nickname' => $this->user->nickname));
-
-
-            Event::handle('EndShowTagSubscriptionsContent', array($this));
-        }
-    }
-
-    function showEmptyListMessage()
-    {
-        if (common_logged_in()) {
-            $current_user = common_current_user();
-            if ($this->user->id === $current_user->id) {
-                // TRANS: Search subscription list text when the logged in user has no search subscriptions.
-                $message = _m('You are not subscribed to any text searches right now. You can push the "Subscribe" button ' .
-                             'on any notice text search to automatically receive any public messages on this site that match that ' .
-                             'search, even if you are not subscribed to the poster.');
-            } else {
-                // TRANS: Search subscription list text when looking at the subscriptions for a of a user other
-                // TRANS: than the logged in user that has no search subscriptions. %s is the user nickname.
-                $message = sprintf(_m('%s is not subscribed to any searches.'), $this->user->nickname);
-            }
-        }
-        else {
-            // TRANS: Subscription list text when looking at the subscriptions for a of a user that has none
-            // TRANS: as an anonymous user. %s is the user nickname.
-            $message = sprintf(_m('%s is not subscribed to any searches.'), $this->user->nickname);
-        }
-
-        $this->elementStart('div', 'guide');
-        $this->raw(common_markup_to_html($message));
-        $this->elementEnd('div');
-    }
-}
-
-// XXX SubscriptionsList and SubscriptionList are dangerously close
-
-class SearchSubscriptionsList extends SubscriptionList
-{
-    function newListItem($searchsub)
-    {
-        return new SearchSubscriptionsListItem($searchsub, $this->owner, $this->action);
-    }
-}
-
-class SearchSubscriptionsListItem extends SubscriptionListItem
-{
-    function startItem()
-    {
-        $this->out->elementStart('li', array('class' => 'searchsub'));
-    }
-
-    function showProfile()
-    {
-        $searchsub = $this->profile;
-        $search = $searchsub->search;
-
-        // Relevant portion!
-        $cur = common_current_user();
-        if (!empty($cur) && $cur->id == $this->owner->id) {
-            $this->showOwnerControls();
-        }
-
-        $url = common_local_url('noticesearch', array('q' => $search));
-        // TRANS: Search subscription list item. %1$s is a URL to a notice search,
-        // TRANS: %2$s are the search criteria, %3$s is a datestring.
-        $linkline = sprintf(_m('"<a href="%1$s">%2$s</a>" since %3$s'),
-                            htmlspecialchars($url),
-                            htmlspecialchars($search),
-                            common_date_string($searchsub->created));
-
-        $this->out->elementStart('div', 'searchsub-item');
-        $this->out->raw($linkline);
-        $this->out->element('div', array('style' => 'clear: both'));
-        $this->out->elementEnd('div');
-    }
-
-    function showActions()
-    {
-    }
-
-    function showOwnerControls()
-    {
-        $this->out->elementStart('div', 'entity_actions');
-
-        $searchsub = $this->profile; // ?
-        $form = new SearchUnsubForm($this->out, $searchsub->search);
-        $form->show();
-
-        $this->out->elementEnd('div');
-        return;
-    }
-}
diff --git a/plugins/SearchSub/searchsubtrackcommand.php b/plugins/SearchSub/searchsubtrackcommand.php
deleted file mode 100644 (file)
index bba2cb3..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-class SearchSubTrackCommand extends Command
-{
-    var $keyword = null;
-
-    function __construct($user, $keyword)
-    {
-        parent::__construct($user);
-        $this->keyword = $keyword;
-    }
-
-    function handle($channel)
-    {
-        $cur = $this->user;
-        $searchsub = SearchSub::pkeyGet(array('search' => $this->keyword,
-                                              'profile_id' => $cur->id));
-
-        if ($searchsub) {
-            // TRANS: Error text shown a user tries to track a search query they're already subscribed to.
-            $channel->error($cur, sprintf(_m('You are already tracking the search "%s".'), $this->keyword));
-            return;
-        }
-
-        try {
-            SearchSub::start($cur->getProfile(), $this->keyword);
-        } catch (Exception $e) {
-            // TRANS: Message given having failed to set up a search subscription by track command.
-            $channel->error($cur, sprintf(_m('Could not start a search subscription for query "%s".'),
-                                          $this->keyword));
-            return;
-        }
-
-        // TRANS: Message given having added a search subscription by track command.
-        $channel->output($cur, sprintf(_m('You are subscribed to the search "%s".'),
-                                              $this->keyword));
-    }
-}
\ No newline at end of file
diff --git a/plugins/SearchSub/searchsubtrackingcommand.php b/plugins/SearchSub/searchsubtrackingcommand.php
deleted file mode 100644 (file)
index a404fd4..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-class SearchSubTrackingCommand extends Command
-{
-    function handle($channel)
-    {
-        $cur = $this->user;
-        $all = new SearchSub();
-        $all->profile_id = $cur->id;
-        $all->find();
-
-        if ($all->N == 0) {
-            // TRANS: Error text shown a user tries to disable all a search subscriptions with track off command, but has none.
-            $channel->error($cur, _m('You are not tracking any searches.'));
-            return;
-        }
-
-        $list = array();
-        while ($all->fetch()) {
-            $list[] = $all->search;
-        }
-
-        // TRANS: Separator for list of tracked searches.
-        $separator = _m('SEPARATOR','", "');
-
-        // TRANS: Message given having disabled all search subscriptions with 'track off'.
-        // TRANS: %s is a list of searches. Separator default is '", "'.
-        $channel->output($cur, sprintf(_m('You are tracking searches for: "%s".'),
-                                       implode($separator, $list)));
-    }
-}
diff --git a/plugins/SearchSub/searchsubtrackoffcommand.php b/plugins/SearchSub/searchsubtrackoffcommand.php
deleted file mode 100644 (file)
index 4ab78a5..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-class SearchSubTrackoffCommand extends Command
-{
-    function handle($channel)
-    {
-        $cur = $this->user;
-        $all = new SearchSub();
-        $all->profile_id = $cur->id;
-        $all->find();
-
-        if ($all->N == 0) {
-            // TRANS: Error text shown a user tries to disable all a search subscriptions with track off command, but has none.
-            $channel->error($cur, _m('You are not tracking any searches.'));
-            return;
-        }
-
-        $profile = $cur->getProfile();
-        while ($all->fetch()) {
-            try {
-                SearchSub::cancel($profile, $all->search);
-            } catch (Exception $e) {
-                // TRANS: Message given having failed to cancel one of the search subs with 'track off' command.
-                // TRANS: %s is the search for which the subscription removal failed.
-                $channel->error($cur, sprintf(_m('Error disabling search subscription for query "%s".'),
-                                              $all->search));
-                return;
-            }
-        }
-
-        // TRANS: Message given having disabled all search subscriptions with 'track off'.
-        $channel->output($cur, _m('Disabled all your search subscriptions.'));
-    }
-}
diff --git a/plugins/SearchSub/searchsubuntrackcommand.php b/plugins/SearchSub/searchsubuntrackcommand.php
deleted file mode 100644 (file)
index fb3d78a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-class SearchSubUntrackCommand extends Command
-{
-    var $keyword = null;
-
-    function __construct($user, $keyword)
-    {
-        parent::__construct($user);
-        $this->keyword = $keyword;
-    }
-
-    function handle($channel)
-    {
-        $cur = $this->user;
-        $searchsub = SearchSub::pkeyGet(array('search' => $this->keyword,
-                                              'profile_id' => $cur->id));
-
-        if (!$searchsub) {
-            // TRANS: Error text shown a user tries to untrack a search query they're not subscribed to.
-            // TRANS: %s is the keyword for the search.
-            $channel->error($cur, sprintf(_m('You are not tracking the search "%s".'), $this->keyword));
-            return;
-        }
-
-        try {
-            SearchSub::cancel($cur->getProfile(), $this->keyword);
-        } catch (Exception $e) {
-            // TRANS: Message given having failed to cancel a search subscription by untrack command.
-            // TRANS: %s is the keyword for the query.
-            $channel->error($cur, sprintf(_m('Could not end a search subscription for query "%s".'),
-                                          $this->keyword));
-            return;
-        }
-
-        // TRANS: Message given having removed a search subscription by untrack command.
-        // TRANS: %s is the keyword for the search.
-        $channel->output($cur, sprintf(_m('You are no longer subscribed to the search "%s".'),
-                                              $this->keyword));
-    }
-}
diff --git a/plugins/SearchSub/searchunsubaction.php b/plugins/SearchSub/searchunsubaction.php
deleted file mode 100644 (file)
index f7f006e..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008-2011, StatusNet, Inc.
- *
- * Search subscription action.
- *
- * 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/>.
- *
- * PHP version 5
- *
- * @category  Action
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008-2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Search unsubscription action
- *
- * Takes parameters:
- *
- *    - token: session token to prevent CSRF attacks
- *    - ajax: boolean; whether to return Ajax or full-browser results
- *
- * Only works if the current user is logged in.
- *
- * @category  Action
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2008-2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
- * @link      http://status.net/
- */
-class SearchunsubAction extends SearchsubAction
-{
-    /**
-     * Handle request
-     *
-     * Does the subscription and returns results.
-     *
-     * @param Array $args unused.
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        // Throws exception on error
-
-        SearchSub::cancel($this->user->getProfile(),
-                       $this->search);
-
-        if ($this->boolean('ajax')) {
-            $this->startHTML('text/xml;charset=utf-8');
-            $this->elementStart('head');
-            // TRANS: Page title when search unsubscription succeeded.
-            $this->element('title', null, _m('Unsubscribed'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $subscribe = new SearchSubForm($this, $this->search);
-            $subscribe->show();
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            $url = common_local_url('search',
-                                    array('search' => $this->search));
-            common_redirect($url, 303);
-        }
-    }
-}
diff --git a/plugins/SearchSub/searchunsubform.php b/plugins/SearchSub/searchunsubform.php
deleted file mode 100644 (file)
index ae07f8d..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Form for subscribing to a search
- *
- * 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  SearchSubPlugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @author    Evan Prodromou <evan@status.net>
- * @author    Sarven Capadisli <csarven@status.net>
- * @copyright 2009-2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * Form for subscribing to a user
- *
- * @category SearchSubPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @author   Evan Prodromou <evan@status.net>
- * @author   Sarven Capadisli <csarven@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- * @see      UnsubscribeForm
- */
-class SearchUnsubForm extends SearchSubForm
-{
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'search-unsubscribe-' . $this->search;
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string of the form class
-     */
-    function formClass()
-    {
-        // class to match existing styles...
-        return 'form_user_unsubscribe ajax';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('searchunsub', array('search' => $this->search));
-    }
-
-    /**
-     * Legend of the Form
-     *
-     * @return void
-     */
-    function formLegend()
-    {
-        // TRANS: Form legend.
-        $this->out->element('legend', null, _m('Unsubscribe from this search'));
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        $this->out->submit('submit',
-                           // TRANS: Button text for unsubscribing from a text search.
-                           _m('BUTTON','Unsubscribe'),
-                           'submit',
-                           null,
-                           // TRANS: Button title for unsubscribing from a text search.
-                           _m('Unsubscribe from this search.'));
-    }
-}
index 4e6aade2a519b33d54b5efdefc1c7addbf1f3632..b487d867204a51414fb8a8ce9f7fa25ef0d14140 100644 (file)
@@ -75,18 +75,4 @@ class SiteNoticeInSidebarPlugin extends Plugin
         $action->element('style', null, '#site_notice { width: 100% }');
         return true;
     }
-
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'SiteNoticeSection':
-            include_once $dir . '/'.strtolower($cls).'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
 }
diff --git a/plugins/SiteNoticeInSidebar/lib/sitenoticesection.php b/plugins/SiteNoticeInSidebar/lib/sitenoticesection.php
new file mode 100644 (file)
index 0000000..37c37ed
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Site notice section
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Site
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Site notice section
+ *
+ * @category  Site
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class SiteNoticeSection extends Section
+{
+    var $text;
+
+    function __construct($action, $text)
+    {
+        parent::__construct($action);
+        $this->text = $text;
+    }
+
+    function title()
+    {
+        return _('Site notice');
+    }
+
+    function showContent()
+    {
+        $this->out->raw($this->text);
+    }
+}
diff --git a/plugins/SiteNoticeInSidebar/sitenoticesection.php b/plugins/SiteNoticeInSidebar/sitenoticesection.php
deleted file mode 100644 (file)
index 37c37ed..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Site notice section
- * 
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Site
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Site notice section
- *
- * @category  Site
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-class SiteNoticeSection extends Section
-{
-    var $text;
-
-    function __construct($action, $text)
-    {
-        parent::__construct($action);
-        $this->text = $text;
-    }
-
-    function title()
-    {
-        return _('Site notice');
-    }
-
-    function showContent()
-    {
-        $this->out->raw($this->text);
-    }
-}
index ae90134db950dcaf1dcc0b84b17ccdcaa338dfc4..d65aba1222b32cdb4a6416d2f575bffcb6a546ac 100644 (file)
@@ -49,37 +49,6 @@ class SitemapPlugin extends Plugin
     const USERS_PER_MAP   = 50000;
     const NOTICES_PER_MAP = 50000;
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'Sitemap_user_count':
-        case 'Sitemap_notice_count':
-            require_once $dir . '/' . $cls . '.php';
-            return false;
-        case 'SitemapindexAction':
-        case 'NoticesitemapAction':
-        case 'UsersitemapAction':
-        case 'SitemapadminpanelAction':
-            require_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'SitemapAction':
-            require_once $dir . '/' . strtolower($cls) . '.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Add sitemap-related information at the end of robots.txt
      *
diff --git a/plugins/Sitemap/Sitemap_notice_count.php b/plugins/Sitemap/Sitemap_notice_count.php
deleted file mode 100644 (file)
index 9e523df..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-<?php
-/**
- * Data class for counting notice postings by date
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for counting notices by date
- *
- * We make a separate sitemap for each notice posted by date.
- * To save ourselves some (not inconsiderable) processing effort,
- * we cache this data in the sitemap_notice_count table. Each
- * row represents a day since the site has been started, with a count
- * of notices posted on that day. Since, after the end of the day,
- * this number doesn't change, it's a good candidate for persistent caching.
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Sitemap_notice_count extends Managed_DataObject
-{
-    public $__table = 'sitemap_notice_count'; // table name
-
-    public $notice_date;                       // date primary_key not_null
-    public $notice_count;                      // int(4)
-    public $created;                           // datetime()   not_null
-    public $modified;                          // datetime   not_null default_0000-00-00%2000%3A00%3A00
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'notice_date' => array('type' => 'date', 'not null' => true, 'description' => 'record date'),
-                'notice_count' => array('type' => 'int', 'not null' => true, 'description' => 'the notice count'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('notice_date'),
-        );
-    }
-
-    static function getAll()
-    {
-        $noticeCounts = self::cacheGet('sitemap:notice:counts');
-
-        if ($noticeCounts === false) {
-            $snc = new Sitemap_notice_count();
-            $snc->orderBy('notice_date DESC');
-
-            // Fetch the first one to check up-to-date-itude
-
-            $n = $snc->find(true);
-
-            $today = self::today();
-            $noticeCounts = array();
-
-            if (!$n) { // No counts saved yet
-                $noticeCounts = self::initializeCounts();
-            } else if ($snc->notice_date < $today) { // There are counts but not up to today
-                $noticeCounts = self::fillInCounts($snc->notice_date);
-            } else if ($snc->notice_date == $today) { // Refresh today's
-                $noticeCounts[$today] = self::updateToday();
-            }
-
-            // starts with second-to-last date
-
-            while ($snc->fetch()) {
-                $noticeCounts[$snc->notice_date] = $snc->notice_count;
-            }
-
-            // Cache notice counts for 4 hours.
-
-            self::cacheSet('sitemap:notice:counts', $noticeCounts, null, time() + 4 * 60 * 60);
-        }
-
-        return $noticeCounts;
-    }
-
-    static function initializeCounts()
-    {
-        $firstDate = self::getFirstDate(); // awww
-        $today     = self::today();
-
-        $counts = array();
-
-        for ($d = $firstDate; $d <= $today; $d = self::incrementDay($d)) {
-            $n = self::getCount($d);
-            self::insertCount($d, $n);
-            $counts[$d] = $n;
-        }
-
-        return $counts;
-    }
-
-    static function fillInCounts($lastDate)
-    {
-        $today = self::today();
-
-        $counts = array();
-
-        $n = self::getCount($lastDate);
-        self::updateCount($lastDate, $n);
-
-        $counts[$lastDate] = $n;
-
-        for ($d = self::incrementDay($lastDate); $d <= $today; $d = self::incrementDay($d)) {
-            $n = self::getCount($d);
-            self::insertCount($d, $n);
-        }
-
-        return $counts;
-    }
-
-    static function updateToday()
-    {
-        $today = self::today();
-
-        $n = self::getCount($today);
-        self::updateCount($today, $n);
-
-        return $n;
-    }
-
-    static function getCount($d)
-    {
-        $notice = new Notice();
-        $notice->whereAdd('created BETWEEN "'.$d.' 00:00:00" AND "'.self::incrementDay($d).' 00:00:00"');
-        $notice->whereAdd('is_local = ' . Notice::LOCAL_PUBLIC);
-        $n = $notice->count();
-
-        return $n;
-    }
-
-    static function insertCount($d, $n)
-    {
-        $snc = new Sitemap_notice_count();
-
-        $snc->notice_date = DB_DataObject_Cast::date($d);
-
-        $snc->notice_count      = $n;
-        $snc->created           = common_sql_now();
-        $snc->modified          = $snc->created;
-
-        if (!$snc->insert()) {
-            common_log(LOG_WARNING, "Could not save user counts for '$d'");
-        }
-    }
-
-    static function updateCount($d, $n)
-    {
-        $snc = Sitemap_notice_count::getKV('notice_date', DB_DataObject_Cast::date($d));
-
-        if (empty($snc)) {
-            // TRANS: Exception
-            throw new Exception(_m("No such registration date: $d."));
-        }
-
-        $orig = clone($snc);
-
-        $snc->notice_date = DB_DataObject_Cast::date($d);
-
-        $snc->notice_count      = $n;
-        $snc->created           = common_sql_now();
-        $snc->modified          = $snc->created;
-
-        if (!$snc->update($orig)) {
-            common_log(LOG_WARNING, "Could not save user counts for '$d'");
-        }
-    }
-
-    static function incrementDay($d)
-    {
-        $dt = self::dateStrToInt($d);
-        return self::dateIntToStr($dt + 24 * 60 * 60);
-    }
-
-    static function dateStrToInt($d)
-    {
-        return strtotime($d.' 00:00:00');
-    }
-
-    static function dateIntToStr($dt)
-    {
-        return date('Y-m-d', $dt);
-    }
-
-    static function getFirstDate()
-    {
-        $n = new Notice();
-
-        $n->selectAdd();
-        $n->selectAdd('date(min(created)) as first_date');
-
-        if ($n->find(true)) {
-            return $n->first_date;
-        } else {
-            // Is this right?
-            return self::dateIntToStr(time());
-        }
-    }
-
-    static function today()
-    {
-        return self::dateIntToStr(time());
-    }
-}
diff --git a/plugins/Sitemap/Sitemap_user_count.php b/plugins/Sitemap/Sitemap_user_count.php
deleted file mode 100644 (file)
index 865cf24..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-<?php
-/**
- * Data class for counting user registrations by date
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for counting users by date
- *
- * We make a separate sitemap for each user registered by date.
- * To save ourselves some processing effort, we cache this data
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Sitemap_user_count extends Managed_DataObject
-{
-    public $__table = 'sitemap_user_count'; // table name
-
-    public $registration_date;               // date primary_key not_null
-    public $user_count;                      // int(4)
-    public $created;                         // datetime()   not_null
-    public $modified;                        // datetime   not_null default_0000-00-00%2000%3A00%3A00
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'registration_date' => array('type' => 'date', 'not null' => true, 'description' => 'record date'),
-                'user_count' => array('type' => 'int', 'not null' => true, 'description' => 'the user count of the recorded date'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('registration_date'),
-        );
-    }
-
-    static function getAll()
-    {
-        $userCounts = self::cacheGet('sitemap:user:counts');
-
-        if ($userCounts === false) {
-
-            $suc = new Sitemap_user_count();
-            $suc->orderBy('registration_date DESC');
-
-            // Fetch the first one to check up-to-date-itude
-
-            $n = $suc->find(true);
-
-            $today = self::today();
-            $userCounts = array();
-
-            if (!$n) { // No counts saved yet
-                $userCounts = self::initializeCounts();
-            } else if ($suc->registration_date < $today) { // There are counts but not up to today
-                $userCounts = self::fillInCounts($suc->registration_date);
-            } else if ($suc->registration_date == $today) { // Refresh today's
-                $userCounts[$today] = self::updateToday();
-            }
-
-            // starts with second-to-last date
-
-            while ($suc->fetch()) {
-                $userCounts[$suc->registration_date] = $suc->user_count;
-            }
-
-            // Cache user counts for 4 hours.
-
-            self::cacheSet('sitemap:user:counts', $userCounts, null, time() + 4 * 60 * 60);
-        }
-
-        return $userCounts;
-    }
-
-    static function initializeCounts()
-    {
-        $firstDate = self::getFirstDate(); // awww
-        $today     = self::today();
-
-        $counts = array();
-
-        for ($d = $firstDate; $d <= $today; $d = self::incrementDay($d)) {
-            $n = self::getCount($d);
-            self::insertCount($d, $n);
-            $counts[$d] = $n;
-        }
-
-        return $counts;
-    }
-
-    static function fillInCounts($lastDate)
-    {
-        $today = self::today();
-
-        $counts = array();
-
-        $n = self::getCount($lastDate);
-        self::updateCount($lastDate, $n);
-
-        $counts[$lastDate] = $n;
-
-        for ($d = self::incrementDay($lastDate); $d <= $today; $d = self::incrementDay($d)) {
-            $n = self::getCount($d);
-            self::insertCount($d, $n);
-        }
-
-        return $counts;
-    }
-
-    static function updateToday()
-    {
-        $today = self::today();
-
-        $n = self::getCount($today);
-        self::updateCount($today, $n);
-
-        return $n;
-    }
-
-    static function getCount($d)
-    {
-        $user = new User();
-        $user->whereAdd('created BETWEEN "'.$d.' 00:00:00" AND "'.self::incrementDay($d).' 00:00:00"');
-        $n = $user->count();
-
-        return $n;
-    }
-
-    static function insertCount($d, $n)
-    {
-        $suc = new Sitemap_user_count();
-
-        $suc->registration_date = DB_DataObject_Cast::date($d);
-        $suc->user_count        = $n;
-        $suc->created           = common_sql_now();
-        $suc->modified          = $suc->created;
-
-        if (!$suc->insert()) {
-            common_log(LOG_WARNING, "Could not save user counts for '$d'");
-        }
-    }
-
-    static function updateCount($d, $n)
-    {
-        $suc = Sitemap_user_count::getKV('registration_date', DB_DataObject_Cast::date($d));
-
-        if (empty($suc)) {
-            // TRANS: Exception thrown when a registration date cannot be found.
-            throw new Exception(_m("No such registration date: $d."));
-        }
-
-        $orig = clone($suc);
-
-        $suc->registration_date = DB_DataObject_Cast::date($d);
-        $suc->user_count        = $n;
-        $suc->created           = common_sql_now();
-        $suc->modified          = $suc->created;
-
-        if (!$suc->update($orig)) {
-            common_log(LOG_WARNING, "Could not save user counts for '$d'");
-        }
-    }
-
-    static function incrementDay($d)
-    {
-        $dt = self::dateStrToInt($d);
-        return self::dateIntToStr($dt + 24 * 60 * 60);
-    }
-
-    static function dateStrToInt($d)
-    {
-        return strtotime($d.' 00:00:00');
-    }
-
-    static function dateIntToStr($dt)
-    {
-        return date('Y-m-d', $dt);
-    }
-
-    static function getFirstDate()
-    {
-        $u = new User();
-        $u->selectAdd();
-        $u->selectAdd('date(min(created)) as first_date');
-        if ($u->find(true)) {
-            return $u->first_date;
-        } else {
-            // Is this right?
-            return self::dateIntToStr(time());
-        }
-    }
-
-    static function today()
-    {
-        return self::dateIntToStr(time());
-    }
-}
diff --git a/plugins/Sitemap/actions/noticesitemap.php b/plugins/Sitemap/actions/noticesitemap.php
new file mode 100644 (file)
index 0000000..efa23b9
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show list of user pages
+ *
+ * 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  Sitemap
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * sitemap for users
+ *
+ * @category Sitemap
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class NoticesitemapAction extends SitemapAction
+{
+    var $notices = null;
+    var $j = 0;
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $y = $this->trimmed('year');
+
+        $m = $this->trimmed('month');
+        $d = $this->trimmed('day');
+
+        $i = $this->trimmed('index');
+
+        $y += 0;
+        $m += 0;
+        $d += 0;
+        $i += 0;
+
+        $this->notices = $this->getNotices($y, $m, $d, $i);
+        $this->j       = 0;
+
+        return true;
+    }
+
+    function nextUrl()
+    {
+        if ($this->j < count($this->notices)) {
+            $n = $this->notices[$this->j];
+            $this->j++;
+            return array(common_local_url('shownotice', array('notice' => $n[0])),
+                         common_date_w3dtf($n[1]),
+                         'never',
+                         null);
+        } else {
+            return null;
+        }
+    }
+
+    function getNotices($y, $m, $d, $i)
+    {
+        $n = Notice::cacheGet("sitemap:notice:$y:$m:$d:$i");
+
+        if ($n === false) {
+
+            $notice = new Notice();
+
+            $begindt = sprintf('%04d-%02d-%02d 00:00:00', $y, $m, $d);
+
+            // XXX: estimates 1d == 24h, which screws up days
+            // with leap seconds (1d == 24h + 1s). Thankfully they're
+            // few and far between.
+
+            $theend = strtotime($begindt) + (24 * 60 * 60);
+            $enddt  = common_sql_date($theend);
+
+            $notice->selectAdd();
+            $notice->selectAdd('id, created');
+
+            $notice->whereAdd("created >= '$begindt'");
+            $notice->whereAdd("created <  '$enddt'");
+
+            $notice->whereAdd('is_local = ' . Notice::LOCAL_PUBLIC);
+
+            $notice->orderBy('created');
+
+            $offset = ($i-1) * SitemapPlugin::NOTICES_PER_MAP;
+            $limit  = SitemapPlugin::NOTICES_PER_MAP;
+
+            $notice->limit($offset, $limit);
+
+            $notice->find();
+
+            $n = array();
+
+            while ($notice->fetch()) {
+                $n[] = array($notice->id, $notice->created);
+            }
+
+            $c = Cache::instance();
+
+            if (!empty($c)) {
+                $c->set(Cache::key("sitemap:notice:$y:$m:$d:$i"),
+                        $n,
+                        Cache::COMPRESSED,
+                        ((time() > $theend) ? (time() + 90 * 24 * 60 * 60) : (time() + 5 * 60)));
+            }
+        }
+
+        return $n;
+    }
+}
diff --git a/plugins/Sitemap/actions/sitemap.php b/plugins/Sitemap/actions/sitemap.php
new file mode 100644 (file)
index 0000000..ef77645
--- /dev/null
@@ -0,0 +1,116 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Superclass for sitemap-generating actions
+ *
+ * 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  Sitemap
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * superclass for sitemap actions
+ *
+ * @category Sitemap
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class SitemapAction extends Action
+{
+    /**
+     * handle the action
+     *
+     * @param array $args unused.
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+
+        header('Content-Type: text/xml; charset=UTF-8');
+        $this->startXML();
+
+        $this->elementStart('urlset', array('xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9'));
+
+        while (list($url, $lm, $cf, $p) = $this->nextUrl()) {
+            $this->showUrl($url, $lm, $cf, $p);
+        }
+
+        $this->elementEnd('urlset');
+
+        $this->endXML();
+    }
+
+    function lastModified()
+    {
+        $y = $this->trimmed('year');
+
+        $m = $this->trimmed('month');
+        $d = $this->trimmed('day');
+
+        $y += 0;
+        $m += 0;
+        $d += 0;
+
+        $begdate = strtotime("$y-$m-$d 00:00:00");
+        $enddate = $begdate + (24 * 60 * 60);
+
+        if ($enddate < time()) {
+            return $enddate;
+        } else {
+            return null;
+        }
+    }
+
+    function showUrl($url, $lastMod=null, $changeFreq=null, $priority=null)
+    {
+        $this->elementStart('url');
+        $this->element('loc', null, $url);
+        if (!is_null($lastMod)) {
+            $this->element('lastmod', null, $lastMod);
+        }
+        if (!is_null($changeFreq)) {
+            $this->element('changefreq', null, $changeFreq);
+        }
+        if (!is_null($priority)) {
+            $this->element('priority', null, $priority);
+        }
+        $this->elementEnd('url');
+    }
+
+    function nextUrl()
+    {
+        return null;
+    }
+
+    function isReadOnly()
+    {
+        return true;
+    }
+}
diff --git a/plugins/Sitemap/actions/sitemapadminpanel.php b/plugins/Sitemap/actions/sitemapadminpanel.php
new file mode 100644 (file)
index 0000000..3304cfd
--- /dev/null
@@ -0,0 +1,206 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Sitemap administration panel
+ *
+ * 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  Sitemap
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Administer sitemap settings
+ *
+ * @category Sitemap
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class SitemapadminpanelAction extends AdminPanelAction
+{
+    /**
+     * Returns the page title
+     *
+     * @return string page title
+     */
+    function title()
+    {
+        // TRANS: Title for sitemap.
+        return _m('Sitemap');
+    }
+
+    /**
+     * Instructions for using this form.
+     *
+     * @return string instructions
+     */
+    function getInstructions()
+    {
+        // TRANS: Instructions for sitemap.
+        return _m('Sitemap settings for this StatusNet site');
+    }
+
+    /**
+     * Show the site admin panel form
+     *
+     * @return void
+     */
+    function showForm()
+    {
+        $form = new SitemapAdminPanelForm($this);
+        $form->show();
+        return;
+    }
+
+    /**
+     * Save settings from the form
+     *
+     * @return void
+     */
+    function saveSettings()
+    {
+        static $settings = array('sitemap' => array('googlekey', 'yahookey', 'bingkey'));
+
+        $values = array();
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                $values[$section][$setting] = $this->trimmed($setting);
+            }
+        }
+
+        // This throws an exception on validation errors
+        $this->validate($values);
+
+        // assert(all values are valid);
+
+        $config = new Config();
+
+        $config->query('BEGIN');
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                Config::save($section, $setting, $values[$section][$setting]);
+            }
+        }
+
+        $config->query('COMMIT');
+
+        return;
+    }
+
+    function validate(&$values)
+    {
+    }
+}
+
+/**
+ * Form for the sitemap admin panel
+ */
+class SitemapAdminPanelForm extends AdminForm
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'form_sitemap_admin_panel';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_sitemap';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('sitemapadminpanel');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('ul', 'form_data');
+        $this->li();
+        $this->input('googlekey',
+                     // TRANS: Field label.
+                     _m('Google key'),
+                     // TRANS: Title for field label.
+                     _m('Google Webmaster Tools verification key.'),
+                     'sitemap');
+        $this->unli();
+        $this->li();
+        $this->input('yahookey',
+                     // TRANS: Field label.
+                     _m('Yahoo key'),
+                     // TRANS: Title for field label.
+                     _m('Yahoo! Site Explorer verification key.'),
+                     'sitemap');
+        $this->unli();
+        $this->li();
+        $this->input('bingkey',
+                     // TRANS: Field label.
+                     _m('Bing key'),
+                     // TRANS: Title for field label.
+                     _m('Bing Webmaster Tools verification key.'),
+                     'sitemap');
+        $this->unli();
+        $this->out->elementEnd('ul');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        $this->out->submit('submit',
+                           // TRANS: Submit button text to save sitemap settings.
+                           _m('BUTTON','Save'),
+                           'submit',
+                           null,
+                           // TRANS: Submit button title to save sitemap settings.
+                           _m('Save sitemap settings.'));
+    }
+}
diff --git a/plugins/Sitemap/actions/sitemapindex.php b/plugins/Sitemap/actions/sitemapindex.php
new file mode 100644 (file)
index 0000000..ab89c21
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Generate sitemap index
+ *
+ * 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  Sitemap
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Show the sitemap index
+ *
+ * @category Sitemap
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class SitemapindexAction extends Action
+{
+    /**
+     * handle the action
+     *
+     * @param array $args unused.
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        header('Content-Type: text/xml; charset=UTF-8');
+        $this->startXML();
+
+        $this->elementStart('sitemapindex', array('xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9'));
+
+        $this->showNoticeSitemaps();
+        $this->showUserSitemaps();
+
+        $this->elementEnd('sitemapindex');
+
+        $this->endXML();
+    }
+
+    function showUserSitemaps()
+    {
+        $userCounts = Sitemap_user_count::getAll();
+
+        foreach ($userCounts as $dt => $cnt) {
+            $cnt = $cnt+0;
+
+            if ($cnt == 0) {
+                continue;
+            }
+
+            $n = (int)$cnt / (int)SitemapPlugin::USERS_PER_MAP;
+            if (($cnt % SitemapPlugin::USERS_PER_MAP) != 0) {
+                $n++;
+            }
+            for ($i = 1; $i <= $n; $i++) {
+                $this->showSitemap('user', $dt, $i);
+            }
+        }
+    }
+
+    function showNoticeSitemaps()
+    {
+        $noticeCounts = Sitemap_notice_count::getAll();
+
+        foreach ($noticeCounts as $dt => $cnt) {
+            if ($cnt == 0) {
+                continue;
+            }
+            $n = $cnt / SitemapPlugin::NOTICES_PER_MAP;
+            if ($cnt % SitemapPlugin::NOTICES_PER_MAP) {
+                $n++;
+            }
+            for ($i = 1; $i <= $n; $i++) {
+                $this->showSitemap('notice', $dt, $i);
+            }
+        }
+    }
+
+    function showSitemap($prefix, $dt, $i)
+    {
+        list($y, $m, $d) = explode('-', $dt);
+
+        $this->elementStart('sitemap');
+        $this->element('loc', null, common_local_url($prefix.'sitemap',
+                                                     array('year' => $y,
+                                                           'month' => $m,
+                                                           'day' => $d,
+                                                           'index' => $i)));
+
+        $begdate = strtotime("$y-$m-$d 00:00:00");
+        $enddate = $begdate + (24 * 60 * 60);
+
+        if ($enddate < time()) {
+            $this->element('lastmod', null, date(DATE_W3C, $enddate));
+        }
+
+        $this->elementEnd('sitemap');
+    }
+}
diff --git a/plugins/Sitemap/actions/usersitemap.php b/plugins/Sitemap/actions/usersitemap.php
new file mode 100644 (file)
index 0000000..c39165d
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show list of user pages
+ *
+ * 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  Sitemap
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * sitemap for users
+ *
+ * @category Sitemap
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class UsersitemapAction extends SitemapAction
+{
+    var $users = null;
+    var $j     = 0;
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $y = $this->trimmed('year');
+
+        $m = $this->trimmed('month');
+        $d = $this->trimmed('day');
+
+        $i = $this->trimmed('index');
+
+        $y += 0;
+        $m += 0;
+        $d += 0;
+        $i += 0;
+
+        $this->users = $this->getUsers($y, $m, $d, $i);
+        $this->j     = 0;
+        return true;
+    }
+
+    function nextUrl()
+    {
+        if ($this->j < count($this->users)) {
+            $nickname = $this->users[$this->j];
+            $this->j++;
+            return array(common_profile_url($nickname), null, null, '1.0');
+        } else {
+            return null;
+        }
+    }
+
+    function getUsers($y, $m, $d, $i)
+    {
+        $u = User::cacheGet("sitemap:user:$y:$m:$d:$i");
+
+        if ($u === false) {
+
+            $user = new User();
+
+            $begindt = sprintf('%04d-%02d-%02d 00:00:00', $y, $m, $d);
+
+            // XXX: estimates 1d == 24h, which screws up days
+            // with leap seconds (1d == 24h + 1s). Thankfully they're
+            // few and far between.
+
+            $theend = strtotime($begindt) + (24 * 60 * 60);
+            $enddt  = common_sql_date($theend);
+
+            $user->selectAdd();
+            $user->selectAdd('nickname');
+            $user->whereAdd("created >= '$begindt'");
+            $user->whereAdd("created <  '$enddt'");
+
+            $user->orderBy('created');
+
+            $offset = ($i-1) * SitemapPlugin::USERS_PER_MAP;
+            $limit  = SitemapPlugin::USERS_PER_MAP;
+
+            $user->limit($offset, $limit);
+
+            $user->find();
+
+            while ($user->fetch()) {
+                $u[] = $user->nickname;
+            }
+
+            $c = Cache::instance();
+
+            if (!empty($c)) {
+                $c->set(Cache::key("sitemap:user:$y:$m:$d:$i"),
+                        $u,
+                        Cache::COMPRESSED,
+                        ((time() > $theend) ? (time() + 90 * 24 * 60 * 60) : (time() + 5 * 60)));
+            }
+        }
+
+        return $u;
+    }
+}
diff --git a/plugins/Sitemap/classes/Sitemap_notice_count.php b/plugins/Sitemap/classes/Sitemap_notice_count.php
new file mode 100644 (file)
index 0000000..9e523df
--- /dev/null
@@ -0,0 +1,240 @@
+<?php
+/**
+ * Data class for counting notice postings by date
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for counting notices by date
+ *
+ * We make a separate sitemap for each notice posted by date.
+ * To save ourselves some (not inconsiderable) processing effort,
+ * we cache this data in the sitemap_notice_count table. Each
+ * row represents a day since the site has been started, with a count
+ * of notices posted on that day. Since, after the end of the day,
+ * this number doesn't change, it's a good candidate for persistent caching.
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Sitemap_notice_count extends Managed_DataObject
+{
+    public $__table = 'sitemap_notice_count'; // table name
+
+    public $notice_date;                       // date primary_key not_null
+    public $notice_count;                      // int(4)
+    public $created;                           // datetime()   not_null
+    public $modified;                          // datetime   not_null default_0000-00-00%2000%3A00%3A00
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'notice_date' => array('type' => 'date', 'not null' => true, 'description' => 'record date'),
+                'notice_count' => array('type' => 'int', 'not null' => true, 'description' => 'the notice count'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('notice_date'),
+        );
+    }
+
+    static function getAll()
+    {
+        $noticeCounts = self::cacheGet('sitemap:notice:counts');
+
+        if ($noticeCounts === false) {
+            $snc = new Sitemap_notice_count();
+            $snc->orderBy('notice_date DESC');
+
+            // Fetch the first one to check up-to-date-itude
+
+            $n = $snc->find(true);
+
+            $today = self::today();
+            $noticeCounts = array();
+
+            if (!$n) { // No counts saved yet
+                $noticeCounts = self::initializeCounts();
+            } else if ($snc->notice_date < $today) { // There are counts but not up to today
+                $noticeCounts = self::fillInCounts($snc->notice_date);
+            } else if ($snc->notice_date == $today) { // Refresh today's
+                $noticeCounts[$today] = self::updateToday();
+            }
+
+            // starts with second-to-last date
+
+            while ($snc->fetch()) {
+                $noticeCounts[$snc->notice_date] = $snc->notice_count;
+            }
+
+            // Cache notice counts for 4 hours.
+
+            self::cacheSet('sitemap:notice:counts', $noticeCounts, null, time() + 4 * 60 * 60);
+        }
+
+        return $noticeCounts;
+    }
+
+    static function initializeCounts()
+    {
+        $firstDate = self::getFirstDate(); // awww
+        $today     = self::today();
+
+        $counts = array();
+
+        for ($d = $firstDate; $d <= $today; $d = self::incrementDay($d)) {
+            $n = self::getCount($d);
+            self::insertCount($d, $n);
+            $counts[$d] = $n;
+        }
+
+        return $counts;
+    }
+
+    static function fillInCounts($lastDate)
+    {
+        $today = self::today();
+
+        $counts = array();
+
+        $n = self::getCount($lastDate);
+        self::updateCount($lastDate, $n);
+
+        $counts[$lastDate] = $n;
+
+        for ($d = self::incrementDay($lastDate); $d <= $today; $d = self::incrementDay($d)) {
+            $n = self::getCount($d);
+            self::insertCount($d, $n);
+        }
+
+        return $counts;
+    }
+
+    static function updateToday()
+    {
+        $today = self::today();
+
+        $n = self::getCount($today);
+        self::updateCount($today, $n);
+
+        return $n;
+    }
+
+    static function getCount($d)
+    {
+        $notice = new Notice();
+        $notice->whereAdd('created BETWEEN "'.$d.' 00:00:00" AND "'.self::incrementDay($d).' 00:00:00"');
+        $notice->whereAdd('is_local = ' . Notice::LOCAL_PUBLIC);
+        $n = $notice->count();
+
+        return $n;
+    }
+
+    static function insertCount($d, $n)
+    {
+        $snc = new Sitemap_notice_count();
+
+        $snc->notice_date = DB_DataObject_Cast::date($d);
+
+        $snc->notice_count      = $n;
+        $snc->created           = common_sql_now();
+        $snc->modified          = $snc->created;
+
+        if (!$snc->insert()) {
+            common_log(LOG_WARNING, "Could not save user counts for '$d'");
+        }
+    }
+
+    static function updateCount($d, $n)
+    {
+        $snc = Sitemap_notice_count::getKV('notice_date', DB_DataObject_Cast::date($d));
+
+        if (empty($snc)) {
+            // TRANS: Exception
+            throw new Exception(_m("No such registration date: $d."));
+        }
+
+        $orig = clone($snc);
+
+        $snc->notice_date = DB_DataObject_Cast::date($d);
+
+        $snc->notice_count      = $n;
+        $snc->created           = common_sql_now();
+        $snc->modified          = $snc->created;
+
+        if (!$snc->update($orig)) {
+            common_log(LOG_WARNING, "Could not save user counts for '$d'");
+        }
+    }
+
+    static function incrementDay($d)
+    {
+        $dt = self::dateStrToInt($d);
+        return self::dateIntToStr($dt + 24 * 60 * 60);
+    }
+
+    static function dateStrToInt($d)
+    {
+        return strtotime($d.' 00:00:00');
+    }
+
+    static function dateIntToStr($dt)
+    {
+        return date('Y-m-d', $dt);
+    }
+
+    static function getFirstDate()
+    {
+        $n = new Notice();
+
+        $n->selectAdd();
+        $n->selectAdd('date(min(created)) as first_date');
+
+        if ($n->find(true)) {
+            return $n->first_date;
+        } else {
+            // Is this right?
+            return self::dateIntToStr(time());
+        }
+    }
+
+    static function today()
+    {
+        return self::dateIntToStr(time());
+    }
+}
diff --git a/plugins/Sitemap/classes/Sitemap_user_count.php b/plugins/Sitemap/classes/Sitemap_user_count.php
new file mode 100644 (file)
index 0000000..865cf24
--- /dev/null
@@ -0,0 +1,232 @@
+<?php
+/**
+ * Data class for counting user registrations by date
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for counting users by date
+ *
+ * We make a separate sitemap for each user registered by date.
+ * To save ourselves some processing effort, we cache this data
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Sitemap_user_count extends Managed_DataObject
+{
+    public $__table = 'sitemap_user_count'; // table name
+
+    public $registration_date;               // date primary_key not_null
+    public $user_count;                      // int(4)
+    public $created;                         // datetime()   not_null
+    public $modified;                        // datetime   not_null default_0000-00-00%2000%3A00%3A00
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'registration_date' => array('type' => 'date', 'not null' => true, 'description' => 'record date'),
+                'user_count' => array('type' => 'int', 'not null' => true, 'description' => 'the user count of the recorded date'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('registration_date'),
+        );
+    }
+
+    static function getAll()
+    {
+        $userCounts = self::cacheGet('sitemap:user:counts');
+
+        if ($userCounts === false) {
+
+            $suc = new Sitemap_user_count();
+            $suc->orderBy('registration_date DESC');
+
+            // Fetch the first one to check up-to-date-itude
+
+            $n = $suc->find(true);
+
+            $today = self::today();
+            $userCounts = array();
+
+            if (!$n) { // No counts saved yet
+                $userCounts = self::initializeCounts();
+            } else if ($suc->registration_date < $today) { // There are counts but not up to today
+                $userCounts = self::fillInCounts($suc->registration_date);
+            } else if ($suc->registration_date == $today) { // Refresh today's
+                $userCounts[$today] = self::updateToday();
+            }
+
+            // starts with second-to-last date
+
+            while ($suc->fetch()) {
+                $userCounts[$suc->registration_date] = $suc->user_count;
+            }
+
+            // Cache user counts for 4 hours.
+
+            self::cacheSet('sitemap:user:counts', $userCounts, null, time() + 4 * 60 * 60);
+        }
+
+        return $userCounts;
+    }
+
+    static function initializeCounts()
+    {
+        $firstDate = self::getFirstDate(); // awww
+        $today     = self::today();
+
+        $counts = array();
+
+        for ($d = $firstDate; $d <= $today; $d = self::incrementDay($d)) {
+            $n = self::getCount($d);
+            self::insertCount($d, $n);
+            $counts[$d] = $n;
+        }
+
+        return $counts;
+    }
+
+    static function fillInCounts($lastDate)
+    {
+        $today = self::today();
+
+        $counts = array();
+
+        $n = self::getCount($lastDate);
+        self::updateCount($lastDate, $n);
+
+        $counts[$lastDate] = $n;
+
+        for ($d = self::incrementDay($lastDate); $d <= $today; $d = self::incrementDay($d)) {
+            $n = self::getCount($d);
+            self::insertCount($d, $n);
+        }
+
+        return $counts;
+    }
+
+    static function updateToday()
+    {
+        $today = self::today();
+
+        $n = self::getCount($today);
+        self::updateCount($today, $n);
+
+        return $n;
+    }
+
+    static function getCount($d)
+    {
+        $user = new User();
+        $user->whereAdd('created BETWEEN "'.$d.' 00:00:00" AND "'.self::incrementDay($d).' 00:00:00"');
+        $n = $user->count();
+
+        return $n;
+    }
+
+    static function insertCount($d, $n)
+    {
+        $suc = new Sitemap_user_count();
+
+        $suc->registration_date = DB_DataObject_Cast::date($d);
+        $suc->user_count        = $n;
+        $suc->created           = common_sql_now();
+        $suc->modified          = $suc->created;
+
+        if (!$suc->insert()) {
+            common_log(LOG_WARNING, "Could not save user counts for '$d'");
+        }
+    }
+
+    static function updateCount($d, $n)
+    {
+        $suc = Sitemap_user_count::getKV('registration_date', DB_DataObject_Cast::date($d));
+
+        if (empty($suc)) {
+            // TRANS: Exception thrown when a registration date cannot be found.
+            throw new Exception(_m("No such registration date: $d."));
+        }
+
+        $orig = clone($suc);
+
+        $suc->registration_date = DB_DataObject_Cast::date($d);
+        $suc->user_count        = $n;
+        $suc->created           = common_sql_now();
+        $suc->modified          = $suc->created;
+
+        if (!$suc->update($orig)) {
+            common_log(LOG_WARNING, "Could not save user counts for '$d'");
+        }
+    }
+
+    static function incrementDay($d)
+    {
+        $dt = self::dateStrToInt($d);
+        return self::dateIntToStr($dt + 24 * 60 * 60);
+    }
+
+    static function dateStrToInt($d)
+    {
+        return strtotime($d.' 00:00:00');
+    }
+
+    static function dateIntToStr($dt)
+    {
+        return date('Y-m-d', $dt);
+    }
+
+    static function getFirstDate()
+    {
+        $u = new User();
+        $u->selectAdd();
+        $u->selectAdd('date(min(created)) as first_date');
+        if ($u->find(true)) {
+            return $u->first_date;
+        } else {
+            // Is this right?
+            return self::dateIntToStr(time());
+        }
+    }
+
+    static function today()
+    {
+        return self::dateIntToStr(time());
+    }
+}
diff --git a/plugins/Sitemap/noticesitemap.php b/plugins/Sitemap/noticesitemap.php
deleted file mode 100644 (file)
index efa23b9..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Show list of user pages
- *
- * 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  Sitemap
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * sitemap for users
- *
- * @category Sitemap
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class NoticesitemapAction extends SitemapAction
-{
-    var $notices = null;
-    var $j = 0;
-
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $y = $this->trimmed('year');
-
-        $m = $this->trimmed('month');
-        $d = $this->trimmed('day');
-
-        $i = $this->trimmed('index');
-
-        $y += 0;
-        $m += 0;
-        $d += 0;
-        $i += 0;
-
-        $this->notices = $this->getNotices($y, $m, $d, $i);
-        $this->j       = 0;
-
-        return true;
-    }
-
-    function nextUrl()
-    {
-        if ($this->j < count($this->notices)) {
-            $n = $this->notices[$this->j];
-            $this->j++;
-            return array(common_local_url('shownotice', array('notice' => $n[0])),
-                         common_date_w3dtf($n[1]),
-                         'never',
-                         null);
-        } else {
-            return null;
-        }
-    }
-
-    function getNotices($y, $m, $d, $i)
-    {
-        $n = Notice::cacheGet("sitemap:notice:$y:$m:$d:$i");
-
-        if ($n === false) {
-
-            $notice = new Notice();
-
-            $begindt = sprintf('%04d-%02d-%02d 00:00:00', $y, $m, $d);
-
-            // XXX: estimates 1d == 24h, which screws up days
-            // with leap seconds (1d == 24h + 1s). Thankfully they're
-            // few and far between.
-
-            $theend = strtotime($begindt) + (24 * 60 * 60);
-            $enddt  = common_sql_date($theend);
-
-            $notice->selectAdd();
-            $notice->selectAdd('id, created');
-
-            $notice->whereAdd("created >= '$begindt'");
-            $notice->whereAdd("created <  '$enddt'");
-
-            $notice->whereAdd('is_local = ' . Notice::LOCAL_PUBLIC);
-
-            $notice->orderBy('created');
-
-            $offset = ($i-1) * SitemapPlugin::NOTICES_PER_MAP;
-            $limit  = SitemapPlugin::NOTICES_PER_MAP;
-
-            $notice->limit($offset, $limit);
-
-            $notice->find();
-
-            $n = array();
-
-            while ($notice->fetch()) {
-                $n[] = array($notice->id, $notice->created);
-            }
-
-            $c = Cache::instance();
-
-            if (!empty($c)) {
-                $c->set(Cache::key("sitemap:notice:$y:$m:$d:$i"),
-                        $n,
-                        Cache::COMPRESSED,
-                        ((time() > $theend) ? (time() + 90 * 24 * 60 * 60) : (time() + 5 * 60)));
-            }
-        }
-
-        return $n;
-    }
-}
diff --git a/plugins/Sitemap/sitemapaction.php b/plugins/Sitemap/sitemapaction.php
deleted file mode 100644 (file)
index ef77645..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Superclass for sitemap-generating actions
- *
- * 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  Sitemap
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * superclass for sitemap actions
- *
- * @category Sitemap
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class SitemapAction extends Action
-{
-    /**
-     * handle the action
-     *
-     * @param array $args unused.
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-
-        header('Content-Type: text/xml; charset=UTF-8');
-        $this->startXML();
-
-        $this->elementStart('urlset', array('xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9'));
-
-        while (list($url, $lm, $cf, $p) = $this->nextUrl()) {
-            $this->showUrl($url, $lm, $cf, $p);
-        }
-
-        $this->elementEnd('urlset');
-
-        $this->endXML();
-    }
-
-    function lastModified()
-    {
-        $y = $this->trimmed('year');
-
-        $m = $this->trimmed('month');
-        $d = $this->trimmed('day');
-
-        $y += 0;
-        $m += 0;
-        $d += 0;
-
-        $begdate = strtotime("$y-$m-$d 00:00:00");
-        $enddate = $begdate + (24 * 60 * 60);
-
-        if ($enddate < time()) {
-            return $enddate;
-        } else {
-            return null;
-        }
-    }
-
-    function showUrl($url, $lastMod=null, $changeFreq=null, $priority=null)
-    {
-        $this->elementStart('url');
-        $this->element('loc', null, $url);
-        if (!is_null($lastMod)) {
-            $this->element('lastmod', null, $lastMod);
-        }
-        if (!is_null($changeFreq)) {
-            $this->element('changefreq', null, $changeFreq);
-        }
-        if (!is_null($priority)) {
-            $this->element('priority', null, $priority);
-        }
-        $this->elementEnd('url');
-    }
-
-    function nextUrl()
-    {
-        return null;
-    }
-
-    function isReadOnly()
-    {
-        return true;
-    }
-}
diff --git a/plugins/Sitemap/sitemapadminpanel.php b/plugins/Sitemap/sitemapadminpanel.php
deleted file mode 100644 (file)
index 3304cfd..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Sitemap administration panel
- *
- * 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  Sitemap
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Administer sitemap settings
- *
- * @category Sitemap
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class SitemapadminpanelAction extends AdminPanelAction
-{
-    /**
-     * Returns the page title
-     *
-     * @return string page title
-     */
-    function title()
-    {
-        // TRANS: Title for sitemap.
-        return _m('Sitemap');
-    }
-
-    /**
-     * Instructions for using this form.
-     *
-     * @return string instructions
-     */
-    function getInstructions()
-    {
-        // TRANS: Instructions for sitemap.
-        return _m('Sitemap settings for this StatusNet site');
-    }
-
-    /**
-     * Show the site admin panel form
-     *
-     * @return void
-     */
-    function showForm()
-    {
-        $form = new SitemapAdminPanelForm($this);
-        $form->show();
-        return;
-    }
-
-    /**
-     * Save settings from the form
-     *
-     * @return void
-     */
-    function saveSettings()
-    {
-        static $settings = array('sitemap' => array('googlekey', 'yahookey', 'bingkey'));
-
-        $values = array();
-
-        foreach ($settings as $section => $parts) {
-            foreach ($parts as $setting) {
-                $values[$section][$setting] = $this->trimmed($setting);
-            }
-        }
-
-        // This throws an exception on validation errors
-        $this->validate($values);
-
-        // assert(all values are valid);
-
-        $config = new Config();
-
-        $config->query('BEGIN');
-
-        foreach ($settings as $section => $parts) {
-            foreach ($parts as $setting) {
-                Config::save($section, $setting, $values[$section][$setting]);
-            }
-        }
-
-        $config->query('COMMIT');
-
-        return;
-    }
-
-    function validate(&$values)
-    {
-    }
-}
-
-/**
- * Form for the sitemap admin panel
- */
-class SitemapAdminPanelForm extends AdminForm
-{
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'form_sitemap_admin_panel';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_sitemap';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('sitemapadminpanel');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('ul', 'form_data');
-        $this->li();
-        $this->input('googlekey',
-                     // TRANS: Field label.
-                     _m('Google key'),
-                     // TRANS: Title for field label.
-                     _m('Google Webmaster Tools verification key.'),
-                     'sitemap');
-        $this->unli();
-        $this->li();
-        $this->input('yahookey',
-                     // TRANS: Field label.
-                     _m('Yahoo key'),
-                     // TRANS: Title for field label.
-                     _m('Yahoo! Site Explorer verification key.'),
-                     'sitemap');
-        $this->unli();
-        $this->li();
-        $this->input('bingkey',
-                     // TRANS: Field label.
-                     _m('Bing key'),
-                     // TRANS: Title for field label.
-                     _m('Bing Webmaster Tools verification key.'),
-                     'sitemap');
-        $this->unli();
-        $this->out->elementEnd('ul');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        $this->out->submit('submit',
-                           // TRANS: Submit button text to save sitemap settings.
-                           _m('BUTTON','Save'),
-                           'submit',
-                           null,
-                           // TRANS: Submit button title to save sitemap settings.
-                           _m('Save sitemap settings.'));
-    }
-}
diff --git a/plugins/Sitemap/sitemapindex.php b/plugins/Sitemap/sitemapindex.php
deleted file mode 100644 (file)
index ab89c21..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Generate sitemap index
- *
- * 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  Sitemap
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Show the sitemap index
- *
- * @category Sitemap
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class SitemapindexAction extends Action
-{
-    /**
-     * handle the action
-     *
-     * @param array $args unused.
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        header('Content-Type: text/xml; charset=UTF-8');
-        $this->startXML();
-
-        $this->elementStart('sitemapindex', array('xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9'));
-
-        $this->showNoticeSitemaps();
-        $this->showUserSitemaps();
-
-        $this->elementEnd('sitemapindex');
-
-        $this->endXML();
-    }
-
-    function showUserSitemaps()
-    {
-        $userCounts = Sitemap_user_count::getAll();
-
-        foreach ($userCounts as $dt => $cnt) {
-            $cnt = $cnt+0;
-
-            if ($cnt == 0) {
-                continue;
-            }
-
-            $n = (int)$cnt / (int)SitemapPlugin::USERS_PER_MAP;
-            if (($cnt % SitemapPlugin::USERS_PER_MAP) != 0) {
-                $n++;
-            }
-            for ($i = 1; $i <= $n; $i++) {
-                $this->showSitemap('user', $dt, $i);
-            }
-        }
-    }
-
-    function showNoticeSitemaps()
-    {
-        $noticeCounts = Sitemap_notice_count::getAll();
-
-        foreach ($noticeCounts as $dt => $cnt) {
-            if ($cnt == 0) {
-                continue;
-            }
-            $n = $cnt / SitemapPlugin::NOTICES_PER_MAP;
-            if ($cnt % SitemapPlugin::NOTICES_PER_MAP) {
-                $n++;
-            }
-            for ($i = 1; $i <= $n; $i++) {
-                $this->showSitemap('notice', $dt, $i);
-            }
-        }
-    }
-
-    function showSitemap($prefix, $dt, $i)
-    {
-        list($y, $m, $d) = explode('-', $dt);
-
-        $this->elementStart('sitemap');
-        $this->element('loc', null, common_local_url($prefix.'sitemap',
-                                                     array('year' => $y,
-                                                           'month' => $m,
-                                                           'day' => $d,
-                                                           'index' => $i)));
-
-        $begdate = strtotime("$y-$m-$d 00:00:00");
-        $enddate = $begdate + (24 * 60 * 60);
-
-        if ($enddate < time()) {
-            $this->element('lastmod', null, date(DATE_W3C, $enddate));
-        }
-
-        $this->elementEnd('sitemap');
-    }
-}
diff --git a/plugins/Sitemap/usersitemap.php b/plugins/Sitemap/usersitemap.php
deleted file mode 100644 (file)
index c39165d..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Show list of user pages
- *
- * 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  Sitemap
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * sitemap for users
- *
- * @category Sitemap
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class UsersitemapAction extends SitemapAction
-{
-    var $users = null;
-    var $j     = 0;
-
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $y = $this->trimmed('year');
-
-        $m = $this->trimmed('month');
-        $d = $this->trimmed('day');
-
-        $i = $this->trimmed('index');
-
-        $y += 0;
-        $m += 0;
-        $d += 0;
-        $i += 0;
-
-        $this->users = $this->getUsers($y, $m, $d, $i);
-        $this->j     = 0;
-        return true;
-    }
-
-    function nextUrl()
-    {
-        if ($this->j < count($this->users)) {
-            $nickname = $this->users[$this->j];
-            $this->j++;
-            return array(common_profile_url($nickname), null, null, '1.0');
-        } else {
-            return null;
-        }
-    }
-
-    function getUsers($y, $m, $d, $i)
-    {
-        $u = User::cacheGet("sitemap:user:$y:$m:$d:$i");
-
-        if ($u === false) {
-
-            $user = new User();
-
-            $begindt = sprintf('%04d-%02d-%02d 00:00:00', $y, $m, $d);
-
-            // XXX: estimates 1d == 24h, which screws up days
-            // with leap seconds (1d == 24h + 1s). Thankfully they're
-            // few and far between.
-
-            $theend = strtotime($begindt) + (24 * 60 * 60);
-            $enddt  = common_sql_date($theend);
-
-            $user->selectAdd();
-            $user->selectAdd('nickname');
-            $user->whereAdd("created >= '$begindt'");
-            $user->whereAdd("created <  '$enddt'");
-
-            $user->orderBy('created');
-
-            $offset = ($i-1) * SitemapPlugin::USERS_PER_MAP;
-            $limit  = SitemapPlugin::USERS_PER_MAP;
-
-            $user->limit($offset, $limit);
-
-            $user->find();
-
-            while ($user->fetch()) {
-                $u[] = $user->nickname;
-            }
-
-            $c = Cache::instance();
-
-            if (!empty($c)) {
-                $c->set(Cache::key("sitemap:user:$y:$m:$d:$i"),
-                        $u,
-                        Cache::COMPRESSED,
-                        ((time() > $theend) ? (time() + 90 * 24 * 60 * 60) : (time() + 5 * 60)));
-            }
-        }
-
-        return $u;
-    }
-}
index d2407484da57839c8743cf4735d997ba7dcff474..2a87a647456abe1e5aa8305206f13d676decb2f7 100644 (file)
@@ -77,27 +77,6 @@ class SlicedFavoritesPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Automatically load the actions and libraries used by the plugin
-     *
-     * @param Class $cls the class
-     *
-     * @return boolean hook return
-     *
-     */
-    function onAutoload($cls)
-    {
-        $base = dirname(__FILE__);
-        $lower = strtolower($cls);
-        switch ($lower) {
-        case 'favoritedsliceaction':
-            require_once "$base/$lower.php";
-            return false;
-        default:
-            return true;
-        }
-    }
-
     function onSlicedFavoritesGetSettings($slice, &$data)
     {
         if (isset($this->slices[$slice])) {
diff --git a/plugins/SlicedFavorites/actions/favoritedslice.php b/plugins/SlicedFavorites/actions/favoritedslice.php
new file mode 100644 (file)
index 0000000..5c9fb31
--- /dev/null
@@ -0,0 +1,153 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * List of popular notices
+ *
+ * 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  Public
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+class FavoritedSliceAction extends FavoritedAction
+{
+    private $includeUsers = array(), $excludeUsers = array();
+
+    /**
+     * Take arguments for running
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     *
+     * @todo move queries from showContent() to here
+     */
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $this->slice = $this->arg('slice', 'default');
+        $data = array();
+        if (Event::handle('SlicedFavoritesGetSettings', array($this->slice, &$data))) {
+            // TRANS: Client exception.
+            throw new ClientException(_m('Unknown favorites slice.'));
+        }
+        if (isset($data['include'])) {
+            $this->includeUsers = $data['include'];
+        }
+        if (isset($data['exclude'])) {
+            $this->excludeUsers = $data['exclude'];
+        }
+
+        return true;
+    }
+
+    /**
+     * Content area
+     *
+     * Shows the list of popular notices
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        $slice = $this->sliceWhereClause();
+        if (!$slice) {
+            return parent::showContent();
+        }
+
+        $weightexpr = common_sql_weight('fave.modified', common_config('popular', 'dropoff'));
+        $cutoff = sprintf("fave.modified > '%s'",
+                          common_sql_date(time() - common_config('popular', 'cutoff')));
+
+        $qry = 'SELECT notice.*, '.
+          $weightexpr . ' as weight ' .
+          'FROM notice JOIN fave ON notice.id = fave.notice_id ' .
+          "WHERE $cutoff AND $slice " .
+          'GROUP BY id,profile_id,uri,content,rendered,url,created,notice.modified,reply_to,is_local,source,notice.conversation ' .
+          'ORDER BY weight DESC';
+
+        $offset = ($this->page - 1) * NOTICES_PER_PAGE;
+        $limit  = NOTICES_PER_PAGE + 1;
+
+        if (common_config('db', 'type') == 'pgsql') {
+            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
+        } else {
+            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
+        }
+
+        $notice = Memcached_DataObject::cachedQuery('Notice',
+                                                    $qry,
+                                                    600);
+
+        $nl = new NoticeList($notice, $this);
+
+        $cnt = $nl->show();
+
+        if ($cnt == 0) {
+            $this->showEmptyList();
+        }
+
+        $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
+                          $this->page, 'favorited');
+    }
+
+    private function sliceWhereClause()
+    {
+        $include = $this->nicknamesToIds($this->includeUsers);
+        $exclude = $this->nicknamesToIds($this->excludeUsers);
+
+        if (count($include) == 1) {
+            return "profile_id = " . intval($include[0]);
+        } else if (count($include) > 1) {
+            return "profile_id IN (" . implode(',', $include) . ")";
+        } else if (count($exclude) == 1) {
+            return "profile_id != " . intval($exclude[0]);
+        } else if (count($exclude) > 1) {
+            return "profile_id NOT IN (" . implode(',', $exclude) . ")";
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     *
+     * @param array $nicks array of user nicknames
+     * @return array of profile/user IDs
+     */
+    private function nicknamesToIds($nicks)
+    {
+        $ids = array();
+        foreach ($nicks as $nick) {
+            // not the most efficient way for a big list!
+            $user = User::getKV('nickname', $nick);
+            if ($user) {
+                $ids[] = intval($user->id);
+            }
+        }
+        return $ids;
+    }
+}
diff --git a/plugins/SlicedFavorites/favoritedsliceaction.php b/plugins/SlicedFavorites/favoritedsliceaction.php
deleted file mode 100644 (file)
index 5c9fb31..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * List of popular notices
- *
- * 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  Public
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-class FavoritedSliceAction extends FavoritedAction
-{
-    private $includeUsers = array(), $excludeUsers = array();
-
-    /**
-     * Take arguments for running
-     *
-     * @param array $args $_REQUEST args
-     *
-     * @return boolean success flag
-     *
-     * @todo move queries from showContent() to here
-     */
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $this->slice = $this->arg('slice', 'default');
-        $data = array();
-        if (Event::handle('SlicedFavoritesGetSettings', array($this->slice, &$data))) {
-            // TRANS: Client exception.
-            throw new ClientException(_m('Unknown favorites slice.'));
-        }
-        if (isset($data['include'])) {
-            $this->includeUsers = $data['include'];
-        }
-        if (isset($data['exclude'])) {
-            $this->excludeUsers = $data['exclude'];
-        }
-
-        return true;
-    }
-
-    /**
-     * Content area
-     *
-     * Shows the list of popular notices
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        $slice = $this->sliceWhereClause();
-        if (!$slice) {
-            return parent::showContent();
-        }
-
-        $weightexpr = common_sql_weight('fave.modified', common_config('popular', 'dropoff'));
-        $cutoff = sprintf("fave.modified > '%s'",
-                          common_sql_date(time() - common_config('popular', 'cutoff')));
-
-        $qry = 'SELECT notice.*, '.
-          $weightexpr . ' as weight ' .
-          'FROM notice JOIN fave ON notice.id = fave.notice_id ' .
-          "WHERE $cutoff AND $slice " .
-          'GROUP BY id,profile_id,uri,content,rendered,url,created,notice.modified,reply_to,is_local,source,notice.conversation ' .
-          'ORDER BY weight DESC';
-
-        $offset = ($this->page - 1) * NOTICES_PER_PAGE;
-        $limit  = NOTICES_PER_PAGE + 1;
-
-        if (common_config('db', 'type') == 'pgsql') {
-            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
-        } else {
-            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
-        }
-
-        $notice = Memcached_DataObject::cachedQuery('Notice',
-                                                    $qry,
-                                                    600);
-
-        $nl = new NoticeList($notice, $this);
-
-        $cnt = $nl->show();
-
-        if ($cnt == 0) {
-            $this->showEmptyList();
-        }
-
-        $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
-                          $this->page, 'favorited');
-    }
-
-    private function sliceWhereClause()
-    {
-        $include = $this->nicknamesToIds($this->includeUsers);
-        $exclude = $this->nicknamesToIds($this->excludeUsers);
-
-        if (count($include) == 1) {
-            return "profile_id = " . intval($include[0]);
-        } else if (count($include) > 1) {
-            return "profile_id IN (" . implode(',', $include) . ")";
-        } else if (count($exclude) == 1) {
-            return "profile_id != " . intval($exclude[0]);
-        } else if (count($exclude) > 1) {
-            return "profile_id NOT IN (" . implode(',', $exclude) . ")";
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     *
-     * @param array $nicks array of user nicknames
-     * @return array of profile/user IDs
-     */
-    private function nicknamesToIds($nicks)
-    {
-        $ids = array();
-        foreach ($nicks as $nick) {
-            // not the most efficient way for a big list!
-            $user = User::getKV('nickname', $nick);
-            if ($user) {
-                $ids[] = intval($user->id);
-            }
-        }
-        return $ids;
-    }
-}
index ad34a7cc151ebd509986b00104a88fcc13a97164..7abfb04e9d2e18ac6990e00fd55111e9f11553aa 100644 (file)
@@ -66,9 +66,9 @@ class SphinxSearchPlugin extends Plugin
             include_once INSTALLDIR . '/plugins/SphinxSearch/' .
               strtolower($cls) . '.php';
             return false;
-        default:
-            return true;
         }
+
+        return parent::onAutoload($cls);
     }
 
     /**
index 427153ab22ff7c2b35f5d5fb865cf807ddd30350..7eb30d4d9d4578a09bbccea27688449226e04a4c 100644 (file)
@@ -45,32 +45,6 @@ class SubMirrorPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Automatically load the actions and libraries used by the plugin
-     *
-     * @param Class $cls the class
-     *
-     * @return boolean hook return
-     *
-     */
-    function onAutoload($cls)
-    {
-        $base = dirname(__FILE__);
-        $lower = strtolower($cls);
-        $files = array("$base/lib/$lower.php",
-                       "$base/classes/$cls.php");
-        if (substr($lower, -6) == 'action') {
-            $files[] = "$base/actions/" . substr($lower, 0, -6) . ".php";
-        }
-        foreach ($files as $file) {
-            if (file_exists($file)) {
-                include_once $file;
-                return false;
-            }
-        }
-        return true;
-    }
-
     function handle($notice)
     {
         // Is anybody mirroring?
diff --git a/plugins/SubMirror/forms/addmirror.php b/plugins/SubMirror/forms/addmirror.php
new file mode 100644 (file)
index 0000000..949b716
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   StatusNet
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+class AddMirrorForm extends Form
+{
+    /**
+     * Name of the form
+     *
+     * Sub-classes should overload this with the name of their form.
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+    }
+
+    /**
+     * Visible or invisible data elements
+     *
+     * Display the form fields that make up the data of the form.
+     * Sub-classes should overload this to show their data.
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->hidden('provider', 'feed');
+        $this->out->elementStart('fieldset');
+
+        $this->out->elementStart('ul');
+
+        $this->li();
+        $this->doInput('addmirror-feedurl',
+                       'feedurl',
+                       // TRANS: Field label.
+                       _m('Web page or feed URL:'),
+                       $this->out->trimmed('feedurl'));
+        $this->unli();
+
+        $this->li();
+        // TRANS: Button text for adding a feed.
+        $this->out->submit('addmirror-save', _m('BUTTON','Add feed'));
+        $this->unli();
+        $this->out->elementEnd('ul');
+        $this->out->elementEnd('fieldset');
+    }
+
+    protected function doInput($id, $name, $label, $value=null, $instructions=null)
+    {
+        $this->out->element('label', array('for' => $id), $label);
+        $attrs = array('name' => $name,
+                       'type' => 'text',
+                       'id' => $id,
+                       'style' => 'width: 80%');
+        if ($value) {
+            $attrs['value'] = $value;
+        }
+        $this->out->element('input', $attrs);
+        if ($instructions) {
+            $this->out->element('p', 'form_guide', $instructions);
+        }
+    }
+
+    /**
+     * Buttons for form actions
+     *
+     * Submit and cancel buttons (or whatever)
+     * Sub-classes should overload this to show their own buttons.
+     *
+     * @return void
+     */
+    function formActions()
+    {
+    }
+
+    /**
+     * ID of the form
+     *
+     * Should be unique on the page. Sub-classes should overload this
+     * to show their own IDs.
+     *
+     * @return string ID of the form
+     */
+    function id()
+    {
+        return 'add-mirror-form';
+    }
+
+    /**
+     * Action of the form.
+     *
+     * URL to post to. Should be overloaded by subclasses to give
+     * somewhere to post to.
+     *
+     * @return string URL to post to
+     */
+    function action()
+    {
+        return common_local_url('addmirror');
+    }
+
+    /**
+     * Class of the form.
+     *
+     * @return string the form's class
+     */
+    function formClass()
+    {
+        return 'form_settings';
+    }
+}
diff --git a/plugins/SubMirror/forms/addtwitter.php b/plugins/SubMirror/forms/addtwitter.php
new file mode 100644 (file)
index 0000000..172d321
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   StatusNet
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+class AddTwitterMirrorForm extends AddMirrorForm
+{
+
+    /**
+     * Visible or invisible data elements
+     *
+     * Display the form fields that make up the data of the form.
+     * Sub-classes should overload this to show their data.
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->hidden('provider', 'twitter');
+        $this->out->elementStart('fieldset');
+
+        $this->out->elementStart('ul');
+
+        $this->li();
+        $this->doInput('addmirror-feedurl',
+                       'screen_name',
+                       // TRANS: Field label.
+                       _m('Twitter username:'),
+                       $this->out->trimmed('screen_name'));
+        $this->unli();
+
+        $this->li();
+        // TRANS: Button text for adding a Twitter feed mirror.
+        $this->out->submit('addmirror-save', _m('BUTTON','Add feed'));
+        $this->unli();
+        $this->out->elementEnd('ul');
+        $this->out->elementEnd('fieldset');
+    }
+}
diff --git a/plugins/SubMirror/forms/editmirror.php b/plugins/SubMirror/forms/editmirror.php
new file mode 100644 (file)
index 0000000..1fc13e8
--- /dev/null
@@ -0,0 +1,191 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   StatusNet
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+class EditMirrorForm extends Form
+{
+    function __construct($action, $profile)
+    {
+        parent::__construct($action);
+
+        $this->profile = clone($profile);
+        $this->user = common_current_user();
+        $this->mirror = SubMirror::pkeyGet(array('subscriber' => $this->user->id,
+                                                 'subscribed' => $this->profile->id));
+    }
+
+    /**
+     * Name of the form
+     *
+     * Sub-classes should overload this with the name of their form.
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+    }
+
+    /**
+     * Visible or invisible data elements
+     *
+     * Display the form fields that make up the data of the form.
+     * Sub-classes should overload this to show their data.
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart('fieldset');
+
+        $this->out->hidden('profile', $this->profile->id);
+
+        $this->out->elementStart('div', array('style' => 'float: left; width: 80px;'));
+        $img = $this->getAvatar($this->profile);
+        $feed = $this->getFeed($this->profile);
+        $this->out->elementStart('a', array('href' => $this->profile->profileurl));
+        $this->out->element('img', array('src' => $img, 'style' => 'float: left'));
+        $this->out->elementEnd('a');
+        $this->out->elementEnd('div');
+
+
+        $this->out->elementStart('div', array('style' => 'margin-left: 80px; margin-right: 20px'));
+        $this->out->elementStart('p');
+        $this->out->elementStart('div');
+        $this->out->element('a', array('href' => $this->profile->profileurl), $this->profile->getBestName());
+        $this->out->elementEnd('div');
+        $this->out->elementStart('div');
+        if ($feed) {
+            // XXX: Why the hard coded space?
+            // TRANS: Field label (URL expectected).
+            $this->out->text(_m('LABEL', 'Remote feed:') . ' ');
+            //$this->out->element('a', array('href' => $feed), $feed);
+            $this->out->element('input', array('value' => $feed, 'readonly' => 'readonly', 'style' => 'width: 100%'));
+        } else {
+            // TRANS: Field label.
+            $this->out->text(_m('LABEL', 'Local user'));
+        }
+        $this->out->elementEnd('div');
+        $this->out->elementEnd('p');
+
+        $this->out->elementStart('fieldset', array('style' => 'margin-top: 20px'));
+        // TRANS: Fieldset legend for feed mirror setting.
+        $this->out->element('legend', false, _m('Mirroring style'));
+
+        // TRANS: Feed mirror style (radio button option).
+        $styles = array('repeat' => _m('Repeat: reference the original user\'s post (sometimes shows as "RT @blah")'),
+                        // TRANS: Feed mirror style (radio button option).
+                        'copy' => _m('Repost the content under my account'));
+        foreach ($styles as $key => $label) {
+            $this->out->elementStart('div');
+            $attribs = array('type' => 'radio',
+                             'value' => $key,
+                             'name' => 'style',
+                             'id' => $this->id() . '-style');
+            if ($key == $this->mirror->style || ($key == 'repeat' && empty($this->mirror->style))) {
+                $attribs['checked'] = 'checked';
+            }
+            $this->out->element('input', $attribs);
+            $this->out->element('span', false, $label); // @todo FIXME: should be label, but the styles muck it up for now
+            $this->out->elementEnd('div');
+
+        }
+        $this->out->elementEnd('fieldset');
+
+
+        $this->out->elementStart('div');
+        // TRANS: Button text to save feed mirror settings.
+        $this->out->submit($this->id() . '-save', _m('BUTTON','Save'));
+        $this->out->element('input', array('type' => 'submit',
+                                           // TRANS: Button text to stop mirroring a feed.
+                                           'value' => _m('BUTTON','Stop mirroring'),
+                                           'name' => 'delete',
+                                           'class' => 'submit'));
+        $this->out->elementEnd('div');
+
+        $this->out->elementEnd('div');
+        $this->out->elementEnd('fieldset');
+    }
+
+    private function getAvatar($profile)
+    {
+        $avatar = $this->profile->getAvatar(48);
+        if ($avatar) {
+            return $avatar->displayUrl();
+        } else {
+            return Avatar::defaultImage(48);
+        }
+    }
+
+    private function getFeed($profile)
+    {
+        // Ok this is a bit of a hack. ;)
+        if (class_exists('Ostatus_profile')) {
+            $oprofile = Ostatus_profile::getKV('profile_id', $profile->id);
+            if ($oprofile) {
+                return $oprofile->feeduri;
+            }
+        }
+        var_dump('wtf');
+        return false;
+    }
+
+    /**
+     * ID of the form
+     *
+     * Should be unique on the page. Sub-classes should overload this
+     * to show their own IDs.
+     *
+     * @return string ID of the form
+     */
+    function id()
+    {
+        return 'edit-mirror-form-' . $this->profile->id;
+    }
+
+    /**
+     * Action of the form.
+     *
+     * URL to post to. Should be overloaded by subclasses to give
+     * somewhere to post to.
+     *
+     * @return string URL to post to
+     */
+    function action()
+    {
+        return common_local_url('editmirror');
+    }
+
+    /**
+     * Class of the form.
+     *
+     * @return string the form's class
+     */
+    function formClass()
+    {
+        return 'form_settings';
+    }
+}
diff --git a/plugins/SubMirror/lib/addmirrorform.php b/plugins/SubMirror/lib/addmirrorform.php
deleted file mode 100644 (file)
index 949b716..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   StatusNet
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-class AddMirrorForm extends Form
-{
-    /**
-     * Name of the form
-     *
-     * Sub-classes should overload this with the name of their form.
-     *
-     * @return void
-     */
-    function formLegend()
-    {
-    }
-
-    /**
-     * Visible or invisible data elements
-     *
-     * Display the form fields that make up the data of the form.
-     * Sub-classes should overload this to show their data.
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->hidden('provider', 'feed');
-        $this->out->elementStart('fieldset');
-
-        $this->out->elementStart('ul');
-
-        $this->li();
-        $this->doInput('addmirror-feedurl',
-                       'feedurl',
-                       // TRANS: Field label.
-                       _m('Web page or feed URL:'),
-                       $this->out->trimmed('feedurl'));
-        $this->unli();
-
-        $this->li();
-        // TRANS: Button text for adding a feed.
-        $this->out->submit('addmirror-save', _m('BUTTON','Add feed'));
-        $this->unli();
-        $this->out->elementEnd('ul');
-        $this->out->elementEnd('fieldset');
-    }
-
-    protected function doInput($id, $name, $label, $value=null, $instructions=null)
-    {
-        $this->out->element('label', array('for' => $id), $label);
-        $attrs = array('name' => $name,
-                       'type' => 'text',
-                       'id' => $id,
-                       'style' => 'width: 80%');
-        if ($value) {
-            $attrs['value'] = $value;
-        }
-        $this->out->element('input', $attrs);
-        if ($instructions) {
-            $this->out->element('p', 'form_guide', $instructions);
-        }
-    }
-
-    /**
-     * Buttons for form actions
-     *
-     * Submit and cancel buttons (or whatever)
-     * Sub-classes should overload this to show their own buttons.
-     *
-     * @return void
-     */
-    function formActions()
-    {
-    }
-
-    /**
-     * ID of the form
-     *
-     * Should be unique on the page. Sub-classes should overload this
-     * to show their own IDs.
-     *
-     * @return string ID of the form
-     */
-    function id()
-    {
-        return 'add-mirror-form';
-    }
-
-    /**
-     * Action of the form.
-     *
-     * URL to post to. Should be overloaded by subclasses to give
-     * somewhere to post to.
-     *
-     * @return string URL to post to
-     */
-    function action()
-    {
-        return common_local_url('addmirror');
-    }
-
-    /**
-     * Class of the form.
-     *
-     * @return string the form's class
-     */
-    function formClass()
-    {
-        return 'form_settings';
-    }
-}
diff --git a/plugins/SubMirror/lib/addtwittermirrorform.php b/plugins/SubMirror/lib/addtwittermirrorform.php
deleted file mode 100644 (file)
index 172d321..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   StatusNet
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-class AddTwitterMirrorForm extends AddMirrorForm
-{
-
-    /**
-     * Visible or invisible data elements
-     *
-     * Display the form fields that make up the data of the form.
-     * Sub-classes should overload this to show their data.
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->hidden('provider', 'twitter');
-        $this->out->elementStart('fieldset');
-
-        $this->out->elementStart('ul');
-
-        $this->li();
-        $this->doInput('addmirror-feedurl',
-                       'screen_name',
-                       // TRANS: Field label.
-                       _m('Twitter username:'),
-                       $this->out->trimmed('screen_name'));
-        $this->unli();
-
-        $this->li();
-        // TRANS: Button text for adding a Twitter feed mirror.
-        $this->out->submit('addmirror-save', _m('BUTTON','Add feed'));
-        $this->unli();
-        $this->out->elementEnd('ul');
-        $this->out->elementEnd('fieldset');
-    }
-}
diff --git a/plugins/SubMirror/lib/editmirrorform.php b/plugins/SubMirror/lib/editmirrorform.php
deleted file mode 100644 (file)
index 1fc13e8..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @package   StatusNet
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-class EditMirrorForm extends Form
-{
-    function __construct($action, $profile)
-    {
-        parent::__construct($action);
-
-        $this->profile = clone($profile);
-        $this->user = common_current_user();
-        $this->mirror = SubMirror::pkeyGet(array('subscriber' => $this->user->id,
-                                                 'subscribed' => $this->profile->id));
-    }
-
-    /**
-     * Name of the form
-     *
-     * Sub-classes should overload this with the name of their form.
-     *
-     * @return void
-     */
-    function formLegend()
-    {
-    }
-
-    /**
-     * Visible or invisible data elements
-     *
-     * Display the form fields that make up the data of the form.
-     * Sub-classes should overload this to show their data.
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart('fieldset');
-
-        $this->out->hidden('profile', $this->profile->id);
-
-        $this->out->elementStart('div', array('style' => 'float: left; width: 80px;'));
-        $img = $this->getAvatar($this->profile);
-        $feed = $this->getFeed($this->profile);
-        $this->out->elementStart('a', array('href' => $this->profile->profileurl));
-        $this->out->element('img', array('src' => $img, 'style' => 'float: left'));
-        $this->out->elementEnd('a');
-        $this->out->elementEnd('div');
-
-
-        $this->out->elementStart('div', array('style' => 'margin-left: 80px; margin-right: 20px'));
-        $this->out->elementStart('p');
-        $this->out->elementStart('div');
-        $this->out->element('a', array('href' => $this->profile->profileurl), $this->profile->getBestName());
-        $this->out->elementEnd('div');
-        $this->out->elementStart('div');
-        if ($feed) {
-            // XXX: Why the hard coded space?
-            // TRANS: Field label (URL expectected).
-            $this->out->text(_m('LABEL', 'Remote feed:') . ' ');
-            //$this->out->element('a', array('href' => $feed), $feed);
-            $this->out->element('input', array('value' => $feed, 'readonly' => 'readonly', 'style' => 'width: 100%'));
-        } else {
-            // TRANS: Field label.
-            $this->out->text(_m('LABEL', 'Local user'));
-        }
-        $this->out->elementEnd('div');
-        $this->out->elementEnd('p');
-
-        $this->out->elementStart('fieldset', array('style' => 'margin-top: 20px'));
-        // TRANS: Fieldset legend for feed mirror setting.
-        $this->out->element('legend', false, _m('Mirroring style'));
-
-        // TRANS: Feed mirror style (radio button option).
-        $styles = array('repeat' => _m('Repeat: reference the original user\'s post (sometimes shows as "RT @blah")'),
-                        // TRANS: Feed mirror style (radio button option).
-                        'copy' => _m('Repost the content under my account'));
-        foreach ($styles as $key => $label) {
-            $this->out->elementStart('div');
-            $attribs = array('type' => 'radio',
-                             'value' => $key,
-                             'name' => 'style',
-                             'id' => $this->id() . '-style');
-            if ($key == $this->mirror->style || ($key == 'repeat' && empty($this->mirror->style))) {
-                $attribs['checked'] = 'checked';
-            }
-            $this->out->element('input', $attribs);
-            $this->out->element('span', false, $label); // @todo FIXME: should be label, but the styles muck it up for now
-            $this->out->elementEnd('div');
-
-        }
-        $this->out->elementEnd('fieldset');
-
-
-        $this->out->elementStart('div');
-        // TRANS: Button text to save feed mirror settings.
-        $this->out->submit($this->id() . '-save', _m('BUTTON','Save'));
-        $this->out->element('input', array('type' => 'submit',
-                                           // TRANS: Button text to stop mirroring a feed.
-                                           'value' => _m('BUTTON','Stop mirroring'),
-                                           'name' => 'delete',
-                                           'class' => 'submit'));
-        $this->out->elementEnd('div');
-
-        $this->out->elementEnd('div');
-        $this->out->elementEnd('fieldset');
-    }
-
-    private function getAvatar($profile)
-    {
-        $avatar = $this->profile->getAvatar(48);
-        if ($avatar) {
-            return $avatar->displayUrl();
-        } else {
-            return Avatar::defaultImage(48);
-        }
-    }
-
-    private function getFeed($profile)
-    {
-        // Ok this is a bit of a hack. ;)
-        if (class_exists('Ostatus_profile')) {
-            $oprofile = Ostatus_profile::getKV('profile_id', $profile->id);
-            if ($oprofile) {
-                return $oprofile->feeduri;
-            }
-        }
-        var_dump('wtf');
-        return false;
-    }
-
-    /**
-     * ID of the form
-     *
-     * Should be unique on the page. Sub-classes should overload this
-     * to show their own IDs.
-     *
-     * @return string ID of the form
-     */
-    function id()
-    {
-        return 'edit-mirror-form-' . $this->profile->id;
-    }
-
-    /**
-     * Action of the form.
-     *
-     * URL to post to. Should be overloaded by subclasses to give
-     * somewhere to post to.
-     *
-     * @return string URL to post to
-     */
-    function action()
-    {
-        return common_local_url('editmirror');
-    }
-
-    /**
-     * Class of the form.
-     *
-     * @return string the form's class
-     */
-    function formClass()
-    {
-        return 'form_settings';
-    }
-}
diff --git a/plugins/TagSub/TagSub.php b/plugins/TagSub/TagSub.php
deleted file mode 100644 (file)
index 5bc0c43..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-/**
- * Data class to store local tag subscriptions
- *
- * PHP version 5
- *
- * @category TagSubPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * For storing the tag subscriptions
- *
- * @category PollPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class TagSub extends Managed_DataObject
-{
-    public $__table = 'tagsub'; // table name
-    public $tag;         // text
-    public $profile_id;  // int -> profile.id
-    public $created;     // datetime
-
-    /**
-     * The One True Thingy that must be defined and declared.
-     */
-    public static function schemaDef()
-    {
-        return array(
-            'description' => 'TagSubPlugin tag subscription records',
-            'fields' => array(
-                'tag' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'hash tag associated with this subscription'),
-                'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile ID of subscribing user'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-            ),
-            'primary key' => array('tag', 'profile_id'),
-            'foreign keys' => array(
-                'tagsub_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
-            ),
-            'indexes' => array(
-                'tagsub_created_idx' => array('created'),
-                'tagsub_profile_id_tag_idx' => array('profile_id', 'tag'),
-            ),
-        );
-    }
-
-    /**
-     * Start a tag subscription!
-     *
-     * @param profile $profile subscriber
-     * @param string $tag subscribee
-     * @return TagSub
-     */
-    static function start(Profile $profile, $tag)
-    {
-        $ts = new TagSub();
-        $ts->tag = $tag;
-        $ts->profile_id = $profile->id;
-        $ts->created = common_sql_now();
-        $ts->insert();
-        self::blow('tagsub:by_profile:%d', $profile->id);
-        return $ts;
-    }
-
-    /**
-     * End a tag subscription!
-     *
-     * @param profile $profile subscriber
-     * @param string $tag subscribee
-     */
-    static function cancel(Profile $profile, $tag)
-    {
-        $ts = TagSub::pkeyGet(array('tag' => $tag,
-                                    'profile_id' => $profile->id));
-        if ($ts) {
-            $ts->delete();
-            self::blow('tagsub:by_profile:%d', $profile->id);
-        }
-    }
-
-    static function forProfile(Profile $profile)
-    {
-        $tags = array();
-
-        $keypart = sprintf('tagsub:by_profile:%d', $profile->id);
-        $tagstring = self::cacheGet($keypart);
-        
-        if ($tagstring !== false) { // cache hit
-               if (!empty($tagstring)) {
-               $tags = explode(',', $tagstring);
-               }
-        } else {
-            $tagsub             = new TagSub();
-            $tagsub->profile_id = $profile->id;
-            $tagsub->selectAdd();
-            $tagsub->selectAdd('tag');
-
-            if ($tagsub->find()) {
-                               $tags = $tagsub->fetchAll('tag');
-            }
-
-            self::cacheSet($keypart, implode(',', $tags));
-        }
-
-        return $tags;
-    }
-}
index b84b2080066c5d891c41281fe7a874321cbd802a..1c493de0cd5e04e9540c381a1246b0869911258c 100644 (file)
@@ -60,35 +60,6 @@ class TagSubPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Load related modules when needed
-     *
-     * @param string $cls Name of the class to be loaded
-     *
-     * @return boolean hook value; true means continue processing, false means stop.
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls)
-        {
-        case 'TagSub':
-            include_once $dir.'/'.$cls.'.php';
-            return false;
-        case 'TagsubAction':
-        case 'TagunsubAction':
-        case 'TagsubsAction':
-        case 'TagSubForm':
-        case 'TagSubMenu':
-        case 'TagUnsubForm':
-            include_once $dir.'/'.strtolower($cls).'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Map URLs to actions
      *
diff --git a/plugins/TagSub/actions/tagsub.php b/plugins/TagSub/actions/tagsub.php
new file mode 100644 (file)
index 0000000..18335f4
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008-2011, StatusNet, Inc.
+ *
+ * Tag subscription action.
+ *
+ * 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/>.
+ *
+ * PHP version 5
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Tag subscription action
+ *
+ * Takes parameters:
+ *
+ *    - token: session token to prevent CSRF attacks
+ *    - ajax: boolean; whether to return Ajax or full-browser results
+ *
+ * Only works if the current user is logged in.
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2008-2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link      http://status.net/
+ */
+class TagsubAction extends Action
+{
+    var $user;
+    var $tag;
+
+    /**
+     * Check pre-requisites and instantiate attributes
+     *
+     * @param Array $args array of arguments (URL, GET, POST)
+     *
+     * @return boolean success flag
+     */
+    function prepare($args)
+    {
+        parent::prepare($args);
+        if ($this->boolean('ajax')) {
+            StatusNet::setApi(true);
+        }
+
+        // Only allow POST requests
+
+        if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+            // TRANS: Client error displayed trying to perform any request method other than POST.
+            // TRANS: Do not translate POST.
+            $this->clientError(_m('This action only accepts POST requests.'));
+            return false;
+        }
+
+        // CSRF protection
+
+        $token = $this->trimmed('token');
+
+        if (!$token || $token != common_session_token()) {
+            // TRANS: Client error displayed when the session token is not okay.
+            $this->clientError(_m('There was a problem with your session token.'.
+                                 ' Try again, please.'));
+            return false;
+        }
+
+        // Only for logged-in users
+
+        $this->user = common_current_user();
+
+        if (empty($this->user)) {
+            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
+            $this->clientError(_m('Not logged in.'));
+            return false;
+        }
+
+        // Profile to subscribe to
+
+        $this->tag = $this->arg('tag');
+
+        if (empty($this->tag)) {
+            // TRANS: Client error displayed trying to subscribe to a non-existing profile.
+            $this->clientError(_m('No such profile.'));
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Handle request
+     *
+     * Does the subscription and returns results.
+     *
+     * @param Array $args unused.
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        // Throws exception on error
+
+        TagSub::start($this->user->getProfile(),
+                      $this->tag);
+
+        if ($this->boolean('ajax')) {
+            $this->startHTML('text/xml;charset=utf-8');
+            $this->elementStart('head');
+            // TRANS: Page title when tag subscription succeeded.
+            $this->element('title', null, _m('Subscribed'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $unsubscribe = new TagUnsubForm($this, $this->tag);
+            $unsubscribe->show();
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            $url = common_local_url('tag',
+                                    array('tag' => $this->tag));
+            common_redirect($url, 303);
+        }
+    }
+}
diff --git a/plugins/TagSub/actions/tagsubs.php b/plugins/TagSub/actions/tagsubs.php
new file mode 100644 (file)
index 0000000..be19525
--- /dev/null
@@ -0,0 +1,196 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * List of a user's subscriptions
+ *
+ * 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  Social
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @author    Sarven Capadisli <csarven@status.net>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * A list of the user's subscriptions
+ *
+ * @category Social
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class TagSubsAction extends GalleryAction
+{
+    function title()
+    {
+        if ($this->page == 1) {
+            // TRANS: Header for subscriptions overview for a user (first page).
+            // TRANS: %s is a user nickname.
+            return sprintf(_m('%s\'s tag subscriptions'), $this->user->nickname);
+        } else {
+            // TRANS: Header for subscriptions overview for a user (not first page).
+            // TRANS: %1$s is a user nickname, %2$d is the page number.
+            return sprintf(_m('%1$s\'s tag subscriptions, page %2$d'),
+                           $this->user->nickname,
+                           $this->page);
+        }
+    }
+
+    function showPageNotice()
+    {
+        $user = common_current_user();
+        if ($user && ($user->id == $this->profile->id)) {
+            $this->element('p', null,
+                           // TRANS: Page notice for page with an overview of all tag subscriptions
+                           // TRANS: of the logged in user's own profile.
+                           _m('You have subscribed to receive all notices on this site containing the following tags:'));
+        } else {
+            $this->element('p', null,
+                           // TRANS: Page notice for page with an overview of all subscriptions of a user other
+                           // TRANS: than the logged in user. %s is the user nickname.
+                           sprintf(_m('%s has subscribed to receive all notices on this site containing the following tags:'),
+                                   $this->profile->nickname));
+        }
+    }
+
+    function showContent()
+    {
+        if (Event::handle('StartShowTagSubscriptionsContent', array($this))) {
+            parent::showContent();
+
+            $offset = ($this->page-1) * PROFILES_PER_PAGE;
+            $limit =  PROFILES_PER_PAGE + 1;
+
+            $cnt = 0;
+
+            $tagsub = new TagSub();
+            $tagsub->profile_id = $this->user->id;
+            $tagsub->limit($limit, $offset);
+            $tagsub->find();
+
+            if ($tagsub->N) {
+                $list = new TagSubscriptionsList($tagsub, $this->user, $this);
+                $cnt = $list->show();
+                if (0 == $cnt) {
+                    $this->showEmptyListMessage();
+                }
+            } else {
+                $this->showEmptyListMessage();
+            }
+
+            $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
+                              $this->page, 'tagsubs',
+                              array('nickname' => $this->user->nickname));
+
+
+            Event::handle('EndShowTagSubscriptionsContent', array($this));
+        }
+    }
+
+    function showEmptyListMessage()
+    {
+        if (common_logged_in()) {
+            $current_user = common_current_user();
+            if ($this->user->id === $current_user->id) {
+                // TRANS: Tag subscription list text when the logged in user has no tag subscriptions.
+                $message = _m('You are not listening to any hash tags right now. You can push the "Subscribe" button ' .
+                              'on any hashtag page to automatically receive any public messages on this site that use that ' .
+                              'tag, even if you are not subscribed to the poster.');
+            } else {
+                // TRANS: Tag subscription list text when looking at the subscriptions for a of a user other
+                // TRANS: than the logged in user that has no tag subscriptions. %s is the user nickname.
+                $message = sprintf(_m('%s is not following any tags.'), $this->user->nickname);
+            }
+        }
+        else {
+            // TRANS: Subscription list text when looking at the subscriptions for a of a user that has none
+            // TRANS: as an anonymous user. %s is the user nickname.
+            $message = sprintf(_m('%s is not following any tags.'), $this->user->nickname);
+        }
+
+        $this->elementStart('div', 'guide');
+        $this->raw(common_markup_to_html($message));
+        $this->elementEnd('div');
+    }
+}
+
+// XXX SubscriptionsList and SubscriptionList are dangerously close
+
+class TagSubscriptionsList extends SubscriptionList
+{
+    function newListItem($tagsub)
+    {
+        return new TagSubscriptionsListItem($tagsub, $this->owner, $this->action);
+    }
+}
+
+class TagSubscriptionsListItem extends SubscriptionListItem
+{
+    function startItem()
+    {
+        $this->out->elementStart('li', array('class' => 'tagsub'));
+    }
+
+    function showProfile()
+    {
+        $tagsub = $this->profile;
+        $tag = $tagsub->tag;
+
+        // Relevant portion!
+        $cur = common_current_user();
+        if (!empty($cur) && $cur->id == $this->owner->id) {
+            $this->showOwnerControls();
+        }
+
+        $url = common_local_url('tag', array('tag' => $tag));
+        // TRANS: %1$s is a URL to a tag, %2$s is a tag,
+        // TRANS: %3$s a date string.
+        $linkline = sprintf(_m('#<a href="%1$s">%2$s</a> since %3$s'),
+                            htmlspecialchars($url),
+                            htmlspecialchars($tag),
+                            common_date_string($tagsub->created));
+
+        $this->out->elementStart('div', 'tagsub-item');
+        $this->out->raw($linkline);
+        $this->out->element('div', array('style' => 'clear: both'));
+        $this->out->elementEnd('div');
+    }
+
+    function showActions()
+    {
+    }
+
+    function showOwnerControls()
+    {
+        $this->out->elementStart('div', 'entity_actions');
+
+        $tagsub = $this->profile; // ?
+        $form = new TagUnsubForm($this->out, $tagsub->tag);
+        $form->show();
+
+        $this->out->elementEnd('div');
+        return;
+    }
+}
diff --git a/plugins/TagSub/actions/tagunsub.php b/plugins/TagSub/actions/tagunsub.php
new file mode 100644 (file)
index 0000000..26fb9ff
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008-2011, StatusNet, Inc.
+ *
+ * Tag subscription action.
+ *
+ * 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/>.
+ *
+ * PHP version 5
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Tag unsubscription action
+ *
+ * Takes parameters:
+ *
+ *    - token: session token to prevent CSRF attacks
+ *    - ajax: boolean; whether to return Ajax or full-browser results
+ *
+ * Only works if the current user is logged in.
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2008-2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link      http://status.net/
+ */
+class TagunsubAction extends TagsubAction
+{
+    /**
+     * Handle request
+     *
+     * Does the subscription and returns results.
+     *
+     * @param Array $args unused.
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        // Throws exception on error
+
+        TagSub::cancel($this->user->getProfile(),
+                       $this->tag);
+
+        if ($this->boolean('ajax')) {
+            $this->startHTML('text/xml;charset=utf-8');
+            $this->elementStart('head');
+            // TRANS: Page title when tag unsubscription succeeded.
+            $this->element('title', null, _m('Unsubscribed'));
+            $this->elementEnd('head');
+            $this->elementStart('body');
+            $subscribe = new TagSubForm($this, $this->tag);
+            $subscribe->show();
+            $this->elementEnd('body');
+            $this->elementEnd('html');
+        } else {
+            $url = common_local_url('tag',
+                                    array('tag' => $this->tag));
+            common_redirect($url, 303);
+        }
+    }
+}
diff --git a/plugins/TagSub/classes/TagSub.php b/plugins/TagSub/classes/TagSub.php
new file mode 100644 (file)
index 0000000..5bc0c43
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/**
+ * Data class to store local tag subscriptions
+ *
+ * PHP version 5
+ *
+ * @category TagSubPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * For storing the tag subscriptions
+ *
+ * @category PollPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class TagSub extends Managed_DataObject
+{
+    public $__table = 'tagsub'; // table name
+    public $tag;         // text
+    public $profile_id;  // int -> profile.id
+    public $created;     // datetime
+
+    /**
+     * The One True Thingy that must be defined and declared.
+     */
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'TagSubPlugin tag subscription records',
+            'fields' => array(
+                'tag' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'hash tag associated with this subscription'),
+                'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile ID of subscribing user'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+            ),
+            'primary key' => array('tag', 'profile_id'),
+            'foreign keys' => array(
+                'tagsub_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
+            ),
+            'indexes' => array(
+                'tagsub_created_idx' => array('created'),
+                'tagsub_profile_id_tag_idx' => array('profile_id', 'tag'),
+            ),
+        );
+    }
+
+    /**
+     * Start a tag subscription!
+     *
+     * @param profile $profile subscriber
+     * @param string $tag subscribee
+     * @return TagSub
+     */
+    static function start(Profile $profile, $tag)
+    {
+        $ts = new TagSub();
+        $ts->tag = $tag;
+        $ts->profile_id = $profile->id;
+        $ts->created = common_sql_now();
+        $ts->insert();
+        self::blow('tagsub:by_profile:%d', $profile->id);
+        return $ts;
+    }
+
+    /**
+     * End a tag subscription!
+     *
+     * @param profile $profile subscriber
+     * @param string $tag subscribee
+     */
+    static function cancel(Profile $profile, $tag)
+    {
+        $ts = TagSub::pkeyGet(array('tag' => $tag,
+                                    'profile_id' => $profile->id));
+        if ($ts) {
+            $ts->delete();
+            self::blow('tagsub:by_profile:%d', $profile->id);
+        }
+    }
+
+    static function forProfile(Profile $profile)
+    {
+        $tags = array();
+
+        $keypart = sprintf('tagsub:by_profile:%d', $profile->id);
+        $tagstring = self::cacheGet($keypart);
+        
+        if ($tagstring !== false) { // cache hit
+               if (!empty($tagstring)) {
+               $tags = explode(',', $tagstring);
+               }
+        } else {
+            $tagsub             = new TagSub();
+            $tagsub->profile_id = $profile->id;
+            $tagsub->selectAdd();
+            $tagsub->selectAdd('tag');
+
+            if ($tagsub->find()) {
+                               $tags = $tagsub->fetchAll('tag');
+            }
+
+            self::cacheSet($keypart, implode(',', $tags));
+        }
+
+        return $tags;
+    }
+}
diff --git a/plugins/TagSub/forms/tagsub.php b/plugins/TagSub/forms/tagsub.php
new file mode 100644 (file)
index 0000000..22eed25
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for subscribing to a tag
+ *
+ * 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  TagSubPlugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @author    Evan Prodromou <evan@status.net>
+ * @author    Sarven Capadisli <csarven@status.net>
+ * @copyright 2009-2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Form for subscribing to a user
+ *
+ * @category TagSubPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Sarven Capadisli <csarven@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ * @see      UnsubscribeForm
+ */
+class TagSubForm extends Form
+{
+    /**
+     * Name of tag to subscribe to
+     */
+    var $tag = '';
+
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out     output channel
+     * @param string        $tag     name of tag to subscribe to
+     */
+    function __construct($out=null, $tag=null)
+    {
+        parent::__construct($out);
+
+        $this->tag = $tag;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'tag-subscribe-' . $this->tag;
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string of the form class
+     */
+    function formClass()
+    {
+        // class to match existing styles...
+        return 'form_user_subscribe ajax';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('tagsub', array('tag' => $this->tag));
+    }
+
+    /**
+     * Legend of the Form
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+        // TRANS: Form legend.
+        $this->out->element('legend', null, _m('Subscribe to this tag'));
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->hidden('subscribeto-' . $this->tag,
+                           $this->tag,
+                           'subscribeto');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Submit button text to subscribe to a tag.
+        $this->out->submit('submit', _m('BUTTON','Subscribe'),
+                           // TRANS: Submit button title to subscribe to a tag.
+                           'submit', null, _m('Subscribe to this tag.'));
+    }
+}
diff --git a/plugins/TagSub/forms/tagunsub.php b/plugins/TagSub/forms/tagunsub.php
new file mode 100644 (file)
index 0000000..6bec4b0
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for subscribing to a tag
+ *
+ * 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  TagSubPlugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @author    Evan Prodromou <evan@status.net>
+ * @author    Sarven Capadisli <csarven@status.net>
+ * @copyright 2009-2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Form for subscribing to a user
+ *
+ * @category TagSubPlugin
+ * @package  StatusNet
+ * @author   Brion Vibber <brion@status.net>
+ * @author   Evan Prodromou <evan@status.net>
+ * @author   Sarven Capadisli <csarven@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ * @see      UnsubscribeForm
+ */
+class TagUnsubForm extends TagSubForm
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'tag-unsubscribe-' . $this->tag;
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string of the form class
+     */
+    function formClass()
+    {
+        // class to match existing styles...
+        return 'form_user_unsubscribe ajax';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('tagunsub', array('tag' => $this->tag));
+    }
+
+    /**
+     * Legend of the Form
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+        // TRANS: Form legend.
+        $this->out->element('legend', null, _m('Unsubscribe from this tag'));
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Submit button text to unsubscribe from a tag.
+        $this->out->submit('submit', _m('BUTTON','Unsubscribe'),
+                           // TRANS: Submit button title to unsubscribe from a tag.
+                           'submit', null, _m('Unsubscribe from this tag.'));
+    }
+}
diff --git a/plugins/TagSub/lib/tagsubmenu.php b/plugins/TagSub/lib/tagsubmenu.php
new file mode 100644 (file)
index 0000000..01fa372
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Menu to show tags you're subscribed to
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Menu
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Class comment
+ *
+ * @category  General
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+class TagSubMenu extends MoreMenu
+{
+    protected $user;
+    protected $tags;
+
+    function __construct($out, $user, $tags)
+    {
+        parent::__construct($out);
+        $this->user = $user;
+        $this->tags = $tags;
+    }
+
+    function getItems()
+    {
+        $items = array();
+        
+        foreach ($this->tags as $tag) {
+            if (!empty($tag)) {
+                $items[] = array('tag',
+                                 array('tag' => $tag),
+                                 sprintf('#%s', $tag),
+                                 // TRANS: Menu item title. %s is a tag.
+                                 sprintf(_('Notices tagged with "%s".'), $tag));
+            }
+        }
+
+        return $items;
+    }
+
+    function tag()
+    {
+        return 'tagsubs';
+    }
+    
+    function seeAllItem()
+    {
+        return array('tagsubs',
+                     array('nickname' => $this->user->nickname),
+                     _('See all'),
+                     _('See all tags you are following'));
+    }
+}
diff --git a/plugins/TagSub/tagsubaction.php b/plugins/TagSub/tagsubaction.php
deleted file mode 100644 (file)
index 18335f4..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008-2011, StatusNet, Inc.
- *
- * Tag subscription action.
- *
- * 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/>.
- *
- * PHP version 5
- *
- * @category  Action
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008-2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Tag subscription action
- *
- * Takes parameters:
- *
- *    - token: session token to prevent CSRF attacks
- *    - ajax: boolean; whether to return Ajax or full-browser results
- *
- * Only works if the current user is logged in.
- *
- * @category  Action
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2008-2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
- * @link      http://status.net/
- */
-class TagsubAction extends Action
-{
-    var $user;
-    var $tag;
-
-    /**
-     * Check pre-requisites and instantiate attributes
-     *
-     * @param Array $args array of arguments (URL, GET, POST)
-     *
-     * @return boolean success flag
-     */
-    function prepare($args)
-    {
-        parent::prepare($args);
-        if ($this->boolean('ajax')) {
-            StatusNet::setApi(true);
-        }
-
-        // Only allow POST requests
-
-        if ($_SERVER['REQUEST_METHOD'] != 'POST') {
-            // TRANS: Client error displayed trying to perform any request method other than POST.
-            // TRANS: Do not translate POST.
-            $this->clientError(_m('This action only accepts POST requests.'));
-            return false;
-        }
-
-        // CSRF protection
-
-        $token = $this->trimmed('token');
-
-        if (!$token || $token != common_session_token()) {
-            // TRANS: Client error displayed when the session token is not okay.
-            $this->clientError(_m('There was a problem with your session token.'.
-                                 ' Try again, please.'));
-            return false;
-        }
-
-        // Only for logged-in users
-
-        $this->user = common_current_user();
-
-        if (empty($this->user)) {
-            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
-            $this->clientError(_m('Not logged in.'));
-            return false;
-        }
-
-        // Profile to subscribe to
-
-        $this->tag = $this->arg('tag');
-
-        if (empty($this->tag)) {
-            // TRANS: Client error displayed trying to subscribe to a non-existing profile.
-            $this->clientError(_m('No such profile.'));
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Handle request
-     *
-     * Does the subscription and returns results.
-     *
-     * @param Array $args unused.
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        // Throws exception on error
-
-        TagSub::start($this->user->getProfile(),
-                      $this->tag);
-
-        if ($this->boolean('ajax')) {
-            $this->startHTML('text/xml;charset=utf-8');
-            $this->elementStart('head');
-            // TRANS: Page title when tag subscription succeeded.
-            $this->element('title', null, _m('Subscribed'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $unsubscribe = new TagUnsubForm($this, $this->tag);
-            $unsubscribe->show();
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            $url = common_local_url('tag',
-                                    array('tag' => $this->tag));
-            common_redirect($url, 303);
-        }
-    }
-}
diff --git a/plugins/TagSub/tagsubform.php b/plugins/TagSub/tagsubform.php
deleted file mode 100644 (file)
index 22eed25..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Form for subscribing to a tag
- *
- * 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  TagSubPlugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @author    Evan Prodromou <evan@status.net>
- * @author    Sarven Capadisli <csarven@status.net>
- * @copyright 2009-2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * Form for subscribing to a user
- *
- * @category TagSubPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @author   Evan Prodromou <evan@status.net>
- * @author   Sarven Capadisli <csarven@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- * @see      UnsubscribeForm
- */
-class TagSubForm extends Form
-{
-    /**
-     * Name of tag to subscribe to
-     */
-    var $tag = '';
-
-    /**
-     * Constructor
-     *
-     * @param HTMLOutputter $out     output channel
-     * @param string        $tag     name of tag to subscribe to
-     */
-    function __construct($out=null, $tag=null)
-    {
-        parent::__construct($out);
-
-        $this->tag = $tag;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'tag-subscribe-' . $this->tag;
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string of the form class
-     */
-    function formClass()
-    {
-        // class to match existing styles...
-        return 'form_user_subscribe ajax';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('tagsub', array('tag' => $this->tag));
-    }
-
-    /**
-     * Legend of the Form
-     *
-     * @return void
-     */
-    function formLegend()
-    {
-        // TRANS: Form legend.
-        $this->out->element('legend', null, _m('Subscribe to this tag'));
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->hidden('subscribeto-' . $this->tag,
-                           $this->tag,
-                           'subscribeto');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Submit button text to subscribe to a tag.
-        $this->out->submit('submit', _m('BUTTON','Subscribe'),
-                           // TRANS: Submit button title to subscribe to a tag.
-                           'submit', null, _m('Subscribe to this tag.'));
-    }
-}
diff --git a/plugins/TagSub/tagsubmenu.php b/plugins/TagSub/tagsubmenu.php
deleted file mode 100644 (file)
index 01fa372..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2011, StatusNet, Inc.
- *
- * Menu to show tags you're subscribed to
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Menu
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-/**
- * Class comment
- *
- * @category  General
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-class TagSubMenu extends MoreMenu
-{
-    protected $user;
-    protected $tags;
-
-    function __construct($out, $user, $tags)
-    {
-        parent::__construct($out);
-        $this->user = $user;
-        $this->tags = $tags;
-    }
-
-    function getItems()
-    {
-        $items = array();
-        
-        foreach ($this->tags as $tag) {
-            if (!empty($tag)) {
-                $items[] = array('tag',
-                                 array('tag' => $tag),
-                                 sprintf('#%s', $tag),
-                                 // TRANS: Menu item title. %s is a tag.
-                                 sprintf(_('Notices tagged with "%s".'), $tag));
-            }
-        }
-
-        return $items;
-    }
-
-    function tag()
-    {
-        return 'tagsubs';
-    }
-    
-    function seeAllItem()
-    {
-        return array('tagsubs',
-                     array('nickname' => $this->user->nickname),
-                     _('See all'),
-                     _('See all tags you are following'));
-    }
-}
diff --git a/plugins/TagSub/tagsubsaction.php b/plugins/TagSub/tagsubsaction.php
deleted file mode 100644 (file)
index be19525..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * List of a user's subscriptions
- *
- * 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  Social
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @author    Sarven Capadisli <csarven@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * A list of the user's subscriptions
- *
- * @category Social
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class TagSubsAction extends GalleryAction
-{
-    function title()
-    {
-        if ($this->page == 1) {
-            // TRANS: Header for subscriptions overview for a user (first page).
-            // TRANS: %s is a user nickname.
-            return sprintf(_m('%s\'s tag subscriptions'), $this->user->nickname);
-        } else {
-            // TRANS: Header for subscriptions overview for a user (not first page).
-            // TRANS: %1$s is a user nickname, %2$d is the page number.
-            return sprintf(_m('%1$s\'s tag subscriptions, page %2$d'),
-                           $this->user->nickname,
-                           $this->page);
-        }
-    }
-
-    function showPageNotice()
-    {
-        $user = common_current_user();
-        if ($user && ($user->id == $this->profile->id)) {
-            $this->element('p', null,
-                           // TRANS: Page notice for page with an overview of all tag subscriptions
-                           // TRANS: of the logged in user's own profile.
-                           _m('You have subscribed to receive all notices on this site containing the following tags:'));
-        } else {
-            $this->element('p', null,
-                           // TRANS: Page notice for page with an overview of all subscriptions of a user other
-                           // TRANS: than the logged in user. %s is the user nickname.
-                           sprintf(_m('%s has subscribed to receive all notices on this site containing the following tags:'),
-                                   $this->profile->nickname));
-        }
-    }
-
-    function showContent()
-    {
-        if (Event::handle('StartShowTagSubscriptionsContent', array($this))) {
-            parent::showContent();
-
-            $offset = ($this->page-1) * PROFILES_PER_PAGE;
-            $limit =  PROFILES_PER_PAGE + 1;
-
-            $cnt = 0;
-
-            $tagsub = new TagSub();
-            $tagsub->profile_id = $this->user->id;
-            $tagsub->limit($limit, $offset);
-            $tagsub->find();
-
-            if ($tagsub->N) {
-                $list = new TagSubscriptionsList($tagsub, $this->user, $this);
-                $cnt = $list->show();
-                if (0 == $cnt) {
-                    $this->showEmptyListMessage();
-                }
-            } else {
-                $this->showEmptyListMessage();
-            }
-
-            $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
-                              $this->page, 'tagsubs',
-                              array('nickname' => $this->user->nickname));
-
-
-            Event::handle('EndShowTagSubscriptionsContent', array($this));
-        }
-    }
-
-    function showEmptyListMessage()
-    {
-        if (common_logged_in()) {
-            $current_user = common_current_user();
-            if ($this->user->id === $current_user->id) {
-                // TRANS: Tag subscription list text when the logged in user has no tag subscriptions.
-                $message = _m('You are not listening to any hash tags right now. You can push the "Subscribe" button ' .
-                              'on any hashtag page to automatically receive any public messages on this site that use that ' .
-                              'tag, even if you are not subscribed to the poster.');
-            } else {
-                // TRANS: Tag subscription list text when looking at the subscriptions for a of a user other
-                // TRANS: than the logged in user that has no tag subscriptions. %s is the user nickname.
-                $message = sprintf(_m('%s is not following any tags.'), $this->user->nickname);
-            }
-        }
-        else {
-            // TRANS: Subscription list text when looking at the subscriptions for a of a user that has none
-            // TRANS: as an anonymous user. %s is the user nickname.
-            $message = sprintf(_m('%s is not following any tags.'), $this->user->nickname);
-        }
-
-        $this->elementStart('div', 'guide');
-        $this->raw(common_markup_to_html($message));
-        $this->elementEnd('div');
-    }
-}
-
-// XXX SubscriptionsList and SubscriptionList are dangerously close
-
-class TagSubscriptionsList extends SubscriptionList
-{
-    function newListItem($tagsub)
-    {
-        return new TagSubscriptionsListItem($tagsub, $this->owner, $this->action);
-    }
-}
-
-class TagSubscriptionsListItem extends SubscriptionListItem
-{
-    function startItem()
-    {
-        $this->out->elementStart('li', array('class' => 'tagsub'));
-    }
-
-    function showProfile()
-    {
-        $tagsub = $this->profile;
-        $tag = $tagsub->tag;
-
-        // Relevant portion!
-        $cur = common_current_user();
-        if (!empty($cur) && $cur->id == $this->owner->id) {
-            $this->showOwnerControls();
-        }
-
-        $url = common_local_url('tag', array('tag' => $tag));
-        // TRANS: %1$s is a URL to a tag, %2$s is a tag,
-        // TRANS: %3$s a date string.
-        $linkline = sprintf(_m('#<a href="%1$s">%2$s</a> since %3$s'),
-                            htmlspecialchars($url),
-                            htmlspecialchars($tag),
-                            common_date_string($tagsub->created));
-
-        $this->out->elementStart('div', 'tagsub-item');
-        $this->out->raw($linkline);
-        $this->out->element('div', array('style' => 'clear: both'));
-        $this->out->elementEnd('div');
-    }
-
-    function showActions()
-    {
-    }
-
-    function showOwnerControls()
-    {
-        $this->out->elementStart('div', 'entity_actions');
-
-        $tagsub = $this->profile; // ?
-        $form = new TagUnsubForm($this->out, $tagsub->tag);
-        $form->show();
-
-        $this->out->elementEnd('div');
-        return;
-    }
-}
diff --git a/plugins/TagSub/tagunsubaction.php b/plugins/TagSub/tagunsubaction.php
deleted file mode 100644 (file)
index 26fb9ff..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008-2011, StatusNet, Inc.
- *
- * Tag subscription action.
- *
- * 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/>.
- *
- * PHP version 5
- *
- * @category  Action
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008-2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Tag unsubscription action
- *
- * Takes parameters:
- *
- *    - token: session token to prevent CSRF attacks
- *    - ajax: boolean; whether to return Ajax or full-browser results
- *
- * Only works if the current user is logged in.
- *
- * @category  Action
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2008-2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
- * @link      http://status.net/
- */
-class TagunsubAction extends TagsubAction
-{
-    /**
-     * Handle request
-     *
-     * Does the subscription and returns results.
-     *
-     * @param Array $args unused.
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        // Throws exception on error
-
-        TagSub::cancel($this->user->getProfile(),
-                       $this->tag);
-
-        if ($this->boolean('ajax')) {
-            $this->startHTML('text/xml;charset=utf-8');
-            $this->elementStart('head');
-            // TRANS: Page title when tag unsubscription succeeded.
-            $this->element('title', null, _m('Unsubscribed'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $subscribe = new TagSubForm($this, $this->tag);
-            $subscribe->show();
-            $this->elementEnd('body');
-            $this->elementEnd('html');
-        } else {
-            $url = common_local_url('tag',
-                                    array('tag' => $this->tag));
-            common_redirect($url, 303);
-        }
-    }
-}
diff --git a/plugins/TagSub/tagunsubform.php b/plugins/TagSub/tagunsubform.php
deleted file mode 100644 (file)
index 6bec4b0..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Form for subscribing to a tag
- *
- * 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  TagSubPlugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @author    Evan Prodromou <evan@status.net>
- * @author    Sarven Capadisli <csarven@status.net>
- * @copyright 2009-2011 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * Form for subscribing to a user
- *
- * @category TagSubPlugin
- * @package  StatusNet
- * @author   Brion Vibber <brion@status.net>
- * @author   Evan Prodromou <evan@status.net>
- * @author   Sarven Capadisli <csarven@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- * @see      UnsubscribeForm
- */
-class TagUnsubForm extends TagSubForm
-{
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'tag-unsubscribe-' . $this->tag;
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string of the form class
-     */
-    function formClass()
-    {
-        // class to match existing styles...
-        return 'form_user_unsubscribe ajax';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('tagunsub', array('tag' => $this->tag));
-    }
-
-    /**
-     * Legend of the Form
-     *
-     * @return void
-     */
-    function formLegend()
-    {
-        // TRANS: Form legend.
-        $this->out->element('legend', null, _m('Unsubscribe from this tag'));
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Submit button text to unsubscribe from a tag.
-        $this->out->submit('submit', _m('BUTTON','Unsubscribe'),
-                           // TRANS: Submit button title to unsubscribe from a tag.
-                           'submit', null, _m('Unsubscribe from this tag.'));
-    }
-}
diff --git a/plugins/TwitterBridge/Notice_to_status.php b/plugins/TwitterBridge/Notice_to_status.php
deleted file mode 100644 (file)
index 6f75e3c..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-/**
- * Data class for remembering notice-to-status mappings
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for mapping notices to statuses
- *
- * Notices flow back and forth between Twitter and StatusNet. We use this
- * table to remember which StatusNet notice corresponds to which Twitter
- * status.
- *
- * Note that notice_id is unique only within a single database; if you
- * want to share this data for some reason, get the notice's URI and use
- * that instead, since it's universally unique.
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-
-class Notice_to_status extends Managed_DataObject
-{
-    public $__table = 'notice_to_status'; // table name
-    public $notice_id;                    // int(4)  primary_key not_null
-    public $status_id;                    // bigint not_null
-    public $created;                      // datetime()   not_null
-    public $modified;                     // datetime   not_null default_0000-00-00%2000%3A00%3A00
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'local notice id'),
-                'status_id' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'twitter status id'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('notice_id'),
-            'unique keys' => array(
-                'status_id_key' => array('status_id'),
-            ),
-            'foreign keys' => array(
-                'notice_to_status_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
-            ),
-        );
-    }
-
-    /**
-     * Save a mapping between a notice and a status
-     * Warning: status_id values may not fit in 32-bit integers.
-     *
-     * @param integer $notice_id ID of the notice in StatusNet
-     * @param integer $status_id ID of the status in Twitter
-     *
-     * @return Notice_to_status new object for this value
-     */
-    static function saveNew($notice_id, $status_id)
-    {
-        if (empty($notice_id)) {
-            throw new Exception("Invalid notice_id $notice_id");
-        }
-        $n2s = Notice_to_status::getKV('notice_id', $notice_id);
-
-        if (!empty($n2s)) {
-            return $n2s;
-        }
-
-        if (empty($status_id)) {
-            throw new Exception("Invalid status_id $status_id");
-        }
-        $n2s = Notice_to_status::getKV('status_id', $status_id);
-
-        if (!empty($n2s)) {
-            return $n2s;
-        }
-
-        common_debug("Mapping notice {$notice_id} to Twitter status {$status_id}");
-
-        $n2s = new Notice_to_status();
-
-        $n2s->notice_id = $notice_id;
-        $n2s->status_id = $status_id;
-        $n2s->created   = common_sql_now();
-
-        $n2s->insert();
-
-        return $n2s;
-    }
-}
index 1e0f328ad95796d0b83b870fcc6d529a1905ce4a..f11234b5e17f772a5cb72b19ed206d25ca6c0496 100644 (file)
@@ -183,46 +183,6 @@ class TwitterBridgePlugin extends Plugin
         return true;
     }
 
-    /**
-     * Automatically load the actions and libraries used by the Twitter bridge
-     *
-     * @param Class $cls the class
-     *
-     * @return boolean hook return
-     *
-     */
-    function onAutoload($cls)
-    {
-        $dir = dirname(__FILE__);
-
-        switch ($cls) {
-        case 'TwittersettingsAction':
-        case 'TwitterauthorizationAction':
-        case 'TwitterloginAction':
-        case 'TwitteradminpanelAction':
-            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'TwitterOAuthClient':
-        case 'TwitterQueueHandler':
-        case 'TweetInQueueHandler':
-        case 'TwitterImport':
-        case 'JsonStreamReader':
-        case 'TwitterStreamReader':
-            include_once $dir . '/' . strtolower($cls) . '.php';
-            return false;
-        case 'TwitterSiteStream':
-        case 'TwitterUserStream':
-            include_once $dir . '/twitterstreamreader.php';
-            return false;
-        case 'Notice_to_status':
-        case 'Twitter_synch_status':
-            include_once $dir . '/' . $cls . '.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Add a Twitter queue item for each notice
      *
diff --git a/plugins/TwitterBridge/Twitter_synch_status.php b/plugins/TwitterBridge/Twitter_synch_status.php
deleted file mode 100644 (file)
index 9a20536..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-/**
- * Store last-touched ID for various timelines
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Store various timeline data
- *
- * We don't want to keep re-fetching the same statuses and direct messages from Twitter.
- * So, we store the last ID we see from a timeline, and store it. Next time
- * around, we use that ID in the since_id parameter.
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * @see      DB_DataObject
- */
-class Twitter_synch_status extends Managed_DataObject
-{
-    public $__table = 'twitter_synch_status'; // table name
-    public $foreign_id;                      // bigint primary_key not_null
-    public $timeline;                        // varchar(255)  primary_key not_null
-    public $last_id;                         // bigint not_null
-    public $created;                         // datetime()   not_null
-    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'foreign_id' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'Foreign message ID'),
-                'timeline' => array('type' => 'varchar', 'length' => 255, 'description' => 'timeline name'),
-                'last_id' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'last id fetched'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('foreign_id', 'timeline'),
-        );
-    }
-
-    static function getLastId($foreign_id, $timeline)
-    {
-        $tss = self::pkeyGet(array('foreign_id' => $foreign_id,
-                                   'timeline' => $timeline));
-
-        if (empty($tss)) {
-            return null;
-        } else {
-            return $tss->last_id;
-        }
-    }
-
-    static function setLastId($foreign_id, $timeline, $last_id)
-    {
-        $tss = self::pkeyGet(array('foreign_id' => $foreign_id,
-                                   'timeline' => $timeline));
-
-        if (empty($tss)) {
-            $tss = new Twitter_synch_status();
-
-            $tss->foreign_id = $foreign_id;
-            $tss->timeline   = $timeline;
-            $tss->last_id    = $last_id;
-            $tss->created    = common_sql_now();
-            $tss->modified   = $tss->created;
-
-            $tss->insert();
-
-            return true;
-        } else {
-            $orig = clone($tss);
-
-            $tss->last_id  = $last_id;
-            $tss->modified = common_sql_now();
-
-            $tss->update();
-
-            return true;
-        }
-    }
-}
diff --git a/plugins/TwitterBridge/actions/twitteradminpanel.php b/plugins/TwitterBridge/actions/twitteradminpanel.php
new file mode 100644 (file)
index 0000000..9ace4e4
--- /dev/null
@@ -0,0 +1,315 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Twitter bridge administration panel
+ *
+ * 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  Settings
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Administer global Twitter bridge settings
+ *
+ * @category Admin
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class TwitteradminpanelAction extends AdminPanelAction
+{
+    /**
+     * Returns the page title
+     *
+     * @return string page title
+     */
+    function title()
+    {
+        // TRANS: Page title for Twitter administration panel.
+        return _m('TITLE','Twitter');
+    }
+
+    /**
+     * Instructions for using this form.
+     *
+     * @return string instructions
+     */
+    function getInstructions()
+    {
+        // TRANS: Instructions for Twitter bridge administration page.
+        return _m('Twitter bridge settings');
+    }
+
+    /**
+     * Show the Twitter admin panel form
+     *
+     * @return void
+     */
+    function showForm()
+    {
+        $form = new TwitterAdminPanelForm($this);
+        $form->show();
+        return;
+    }
+
+    /**
+     * Save settings from the form
+     *
+     * @return void
+     */
+    function saveSettings()
+    {
+        static $settings = array(
+            'twitter'     => array('consumer_key', 'consumer_secret'),
+            'integration' => array('source')
+        );
+
+        static $booleans = array(
+            'twitter'       => array('signin')
+        );
+        if (Event::handle('TwitterBridgeAdminImportControl')) {
+            $booleans['twitterimport'] = array('enabled');
+        }
+
+        $values = array();
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                $values[$section][$setting]
+                    = $this->trimmed($setting);
+            }
+        }
+
+        foreach ($booleans as $section => $parts) {
+            foreach ($parts as $setting) {
+                $values[$section][$setting]
+                    = ($this->boolean($setting)) ? 1 : 0;
+            }
+        }
+
+        // This throws an exception on validation errors
+
+        $this->validate($values);
+
+        // assert(all values are valid);
+
+        $config = new Config();
+
+        $config->query('BEGIN');
+
+        foreach ($settings as $section => $parts) {
+            foreach ($parts as $setting) {
+                Config::save($section, $setting, $values[$section][$setting]);
+            }
+        }
+
+        foreach ($booleans as $section => $parts) {
+            foreach ($parts as $setting) {
+                Config::save($section, $setting, $values[$section][$setting]);
+            }
+        }
+
+        $config->query('COMMIT');
+
+        // Flush the router cache: we may have enabled/disabled bridging,
+        // which will add or remove some actions.
+        $cache = Cache::instance();
+        $cache->delete(Router::cacheKey());
+
+        return;
+    }
+
+    function validate(&$values)
+    {
+        // Validate consumer key and secret (can't be too long)
+
+        if (mb_strlen($values['twitter']['consumer_key']) > 255) {
+            $this->clientError(
+                // TRANS: Client error displayed when a consumer key is invalid because it is too long.
+                _m('Invalid consumer key. Maximum length is 255 characters.')
+            );
+        }
+
+        if (mb_strlen($values['twitter']['consumer_secret']) > 255) {
+            $this->clientError(
+                // TRANS: Client error displayed when a consumer secret is invalid because it is too long.
+                _m('Invalid consumer secret. Maximum length is 255 characters.')
+            );
+        }
+    }
+
+    function isImportEnabled()
+    {
+        // Since daemon setup isn't automated yet...
+        // @todo: if merged into main queues, detect presence of daemon config
+        return true;
+    }
+}
+
+class TwitterAdminPanelForm extends AdminForm
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+    function id()
+    {
+        return 'twitteradminpanel';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_settings';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('twitteradminpanel');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->elementStart(
+            'fieldset',
+            array('id' => 'settings_twitter-application')
+        );
+        // TRANS: Fieldset legend for Twitter application settings.
+        $this->out->element('legend', null, _m('Twitter application settings'));
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+        $this->input(
+            'consumer_key',
+            // TRANS: Field label for Twitter assigned consumer key.
+            _m('Consumer key'),
+            // TRANS: Field title for Twitter assigned consumer key.
+            _m('The consumer key assigned by Twitter.'),
+            'twitter'
+        );
+        $this->unli();
+
+        $this->li();
+        $this->input(
+            'consumer_secret',
+            // TRANS: Field label for Twitter assigned consumer secret.
+            _m('Consumer secret'),
+            // TRANS: Field title for Twitter assigned consumer secret.
+            _m('The consumer secret assigned by Twitter.'),
+            'twitter'
+        );
+        $this->unli();
+
+        $globalConsumerKey = common_config('twitter', 'global_consumer_key');
+        $globalConsumerSec = common_config('twitter', 'global_consumer_secret');
+
+        if (!empty($globalConsumerKey) && !empty($globalConsumerSec)) {
+            $this->li();
+            // TRANS: Form guide displayed when two required fields have already been provided.
+            $this->out->element('p', 'form_guide', _m('Note: A global consumer key and secret are set.'));
+            $this->unli();
+        }
+
+        $this->li();
+        $this->input(
+            'source',
+            // TRANS: Field label for Twitter application name.
+            _m('Integration source'),
+            // TRANS: Field title for Twitter application name.
+            _m('The name of your Twitter application.'),
+            'integration'
+        );
+        $this->unli();
+
+        $this->out->elementEnd('ul');
+        $this->out->elementEnd('fieldset');
+
+        $this->out->elementStart(
+            'fieldset',
+            array('id' => 'settings_twitter-options')
+        );
+        // TRANS: Fieldset legend for Twitter integration options.
+        $this->out->element('legend', null, _m('Options'));
+
+        $this->out->elementStart('ul', 'form_data');
+
+        $this->li();
+
+        $this->out->checkbox(
+            // TRANS: Checkbox label for global setting.
+            'signin', _m('Enable "Sign-in with Twitter"'),
+            (bool) $this->value('signin', 'twitter'),
+            // TRANS: Checkbox title.
+            _m('This allow users to login with their Twitter credentials.')
+        );
+        $this->unli();
+
+        if (Event::handle('TwitterBridgeAdminImportControl')) {
+            $this->li();
+            $this->out->checkbox(
+                // TRANS: Checkbox label for global setting.
+                'enabled', _m('Enable Twitter import'),
+                (bool) $this->value('enabled', 'twitterimport'),
+                // TRANS: Checkbox title for global setting.
+                _m('Allow users to import their Twitter friends\' timelines. Requires daemons to be manually configured.')
+            );
+            $this->unli();
+        }
+
+        $this->out->elementEnd('ul');
+
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+    function formActions()
+    {
+        // TRANS: Button text for saving the administrative Twitter bridge settings.
+        $this->out->submit('submit', _m('BUTTON','Save'), 'submit', null,
+        // TRANS: Button title for saving the administrative Twitter bridge settings.
+        _m('Save the Twitter bridge settings.'));
+    }
+}
diff --git a/plugins/TwitterBridge/actions/twitterauthorization.php b/plugins/TwitterBridge/actions/twitterauthorization.php
new file mode 100644 (file)
index 0000000..6a86c81
--- /dev/null
@@ -0,0 +1,723 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Class for doing OAuth authentication against Twitter
+ *
+ * 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  Plugin
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @author    Julien C <chaumond@gmail.com>
+ * @copyright 2009-2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
+
+/**
+ * Class for doing OAuth authentication against Twitter
+ *
+ * Peforms the OAuth "dance" between StatusNet and Twitter -- requests a token,
+ * authorizes it, and exchanges it for an access token.  It also creates a link
+ * (Foreign_link) between the StatusNet user and Twitter user and stores the
+ * access token and secret in the link.
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @author   Julien C <chaumond@gmail.com>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ */
+class TwitterauthorizationAction extends Action
+{
+    var $twuid        = null;
+    var $tw_fields    = null;
+    var $access_token = null;
+    var $signin       = null;
+    var $verifier     = null;
+
+    /**
+     * Initialize class members. Looks for 'oauth_token' parameter.
+     *
+     * @param array $args misc. arguments
+     *
+     * @return boolean true
+     */
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $this->signin      = $this->boolean('signin');
+        $this->oauth_token = $this->arg('oauth_token');
+        $this->verifier    = $this->arg('oauth_verifier');
+
+        return true;
+    }
+
+    /**
+     * Handler method
+     *
+     * @param array $args is ignored since it's now passed in in prepare()
+     *
+     * @return nothing
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+
+        if (common_logged_in()) {
+            $user  = common_current_user();
+            $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
+
+            // If there's already a foreign link record and a foreign user
+            // it means the accounts are already linked, and this is unecessary.
+            // So go back.
+
+            if (isset($flink)) {
+                $fuser = $flink->getForeignUser();
+                if (!empty($fuser)) {
+                    common_redirect(common_local_url('twittersettings'));
+                }
+            }
+        }
+
+        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+
+            // User was not logged in to StatusNet before
+
+            $this->twuid = $this->trimmed('twuid');
+
+            $this->tw_fields = array('screen_name' => $this->trimmed('tw_fields_screen_name'),
+                                     'fullname' => $this->trimmed('tw_fields_fullname'));
+
+            $this->access_token = new OAuthToken($this->trimmed('access_token_key'), $this->trimmed('access_token_secret'));
+
+            $token = $this->trimmed('token');
+
+            if (!$token || $token != common_session_token()) {
+                // TRANS: Client error displayed when the session token does not match or is not given.
+                $this->showForm(_m('There was a problem with your session token. Try again, please.'));
+                return;
+            }
+
+            if ($this->arg('create')) {
+                if (!$this->boolean('license')) {
+                    // TRANS: Form validation error displayed when the checkbox to agree to the license has not been checked.
+                    $this->showForm(_m('You cannot register if you do not agree to the license.'),
+                                    $this->trimmed('newname'));
+                    return;
+                }
+                $this->createNewUser();
+            } else if ($this->arg('connect')) {
+                $this->connectNewUser();
+            } else {
+                common_debug('Twitter bridge - ' . print_r($this->args, true));
+                // TRANS: Form validation error displayed when an unhandled error occurs.
+                $this->showForm(_m('Something weird happened.'),
+                                $this->trimmed('newname'));
+            }
+        } else {
+            // $this->oauth_token is only populated once Twitter authorizes our
+            // request token. If it's empty we're at the beginning of the auth
+            // process
+
+            if (empty($this->oauth_token)) {
+                $this->authorizeRequestToken();
+            } else {
+                $this->saveAccessToken();
+            }
+        }
+    }
+
+    /**
+     * Asks Twitter for a request token, and then redirects to Twitter
+     * to authorize it.
+     *
+     * @return nothing
+     */
+    function authorizeRequestToken()
+    {
+        try {
+            // Get a new request token and authorize it
+
+            $client  = new TwitterOAuthClient();
+            $req_tok = $client->getRequestToken();
+
+            // Sock the request token away in the session temporarily
+
+            $_SESSION['twitter_request_token']        = $req_tok->key;
+            $_SESSION['twitter_request_token_secret'] = $req_tok->secret;
+
+            $auth_link = $client->getAuthorizeLink($req_tok, $this->signin);
+        } catch (OAuthClientException $e) {
+            $msg = sprintf(
+                'OAuth client error - code: %1s, msg: %2s',
+                $e->getCode(),
+                $e->getMessage()
+            );
+            common_log(LOG_INFO, 'Twitter bridge - ' . $msg);
+            $this->serverError(
+                // TRANS: Server error displayed when linking to a Twitter account fails.
+                _m('Could not link your Twitter account.')
+            );
+        }
+
+        common_redirect($auth_link);
+    }
+
+    /**
+     * Called when Twitter returns an authorized request token. Exchanges
+     * it for an access token and stores it.
+     *
+     * @return nothing
+     */
+    function saveAccessToken()
+    {
+        // Check to make sure Twitter returned the same request
+        // token we sent them
+
+        if ($_SESSION['twitter_request_token'] != $this->oauth_token) {
+            $this->serverError(
+                // TRANS: Server error displayed when linking to a Twitter account fails because of an incorrect oauth_token.
+                _m('Could not link your Twitter account: oauth_token mismatch.')
+            );
+        }
+
+        $twitter_user = null;
+
+        try {
+
+            $client = new TwitterOAuthClient($_SESSION['twitter_request_token'],
+                $_SESSION['twitter_request_token_secret']);
+
+            // Exchange the request token for an access token
+
+            $atok = $client->getAccessToken($this->verifier);
+
+            // Test the access token and get the user's Twitter info
+
+            $client       = new TwitterOAuthClient($atok->key, $atok->secret);
+            $twitter_user = $client->verifyCredentials();
+
+        } catch (OAuthClientException $e) {
+            $msg = sprintf(
+                'OAuth client error - code: %1$s, msg: %2$s',
+                $e->getCode(),
+                $e->getMessage()
+            );
+            common_log(LOG_INFO, 'Twitter bridge - ' . $msg);
+            $this->serverError(
+                // TRANS: Server error displayed when linking to a Twitter account fails.
+                _m('Could not link your Twitter account.')
+            );
+        }
+
+        if (common_logged_in()) {
+            // Save the access token and Twitter user info
+
+            $user = common_current_user();
+            $this->saveForeignLink($user->id, $twitter_user->id, $atok);
+            save_twitter_user($twitter_user->id, $twitter_user->screen_name);
+
+        } else {
+
+            $this->twuid = $twitter_user->id;
+            $this->tw_fields = array("screen_name" => $twitter_user->screen_name,
+                                     "fullname" => $twitter_user->name);
+            $this->access_token = $atok;
+            $this->tryLogin();
+        }
+
+        // Clean up the the mess we made in the session
+
+        unset($_SESSION['twitter_request_token']);
+        unset($_SESSION['twitter_request_token_secret']);
+
+        if (common_logged_in()) {
+            common_redirect(common_local_url('twittersettings'));
+        }
+    }
+
+    /**
+     * Saves a Foreign_link between Twitter user and local user,
+     * which includes the access token and secret.
+     *
+     * @param int        $user_id StatusNet user ID
+     * @param int        $twuid   Twitter user ID
+     * @param OAuthToken $token   the access token to save
+     *
+     * @return nothing
+     */
+    function saveForeignLink($user_id, $twuid, $access_token)
+    {
+        $flink = new Foreign_link();
+
+        $flink->user_id = $user_id;
+        $flink->service = TWITTER_SERVICE;
+
+        // delete stale flink, if any
+        $result = $flink->find(true);
+
+        if (!empty($result)) {
+            $flink->safeDelete();
+        }
+
+        $flink->user_id     = $user_id;
+        $flink->foreign_id  = $twuid;
+        $flink->service     = TWITTER_SERVICE;
+
+        $creds = TwitterOAuthClient::packToken($access_token);
+
+        $flink->credentials = $creds;
+        $flink->created     = common_sql_now();
+
+        // Defaults: noticesync on, everything else off
+
+        $flink->set_flags(true, false, false, false);
+
+        $flink_id = $flink->insert();
+
+        if (empty($flink_id)) {
+            common_log_db_error($flink, 'INSERT', __FILE__);
+            // TRANS: Server error displayed when linking to a Twitter account fails.
+            $this->serverError(_m('Could not link your Twitter account.'));
+        }
+
+        return $flink_id;
+    }
+
+    function showPageNotice()
+    {
+        if ($this->error) {
+            $this->element('div', array('class' => 'error'), $this->error);
+        } else {
+            $this->element('div', 'instructions',
+                           // TRANS: Page instruction. %s is the StatusNet sitename.
+                           sprintf(_m('This is the first time you have logged into %s so we must connect your Twitter account to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
+        }
+    }
+
+    function title()
+    {
+        // TRANS: Page title.
+        return _m('Twitter Account Setup');
+    }
+
+    function showForm($error=null, $username=null)
+    {
+        $this->error = $error;
+        $this->username = $username;
+
+        $this->showPage();
+    }
+
+    function showPage()
+    {
+        parent::showPage();
+    }
+
+    /**
+     * @fixme much of this duplicates core code, which is very fragile.
+     * Should probably be replaced with an extensible mini version of
+     * the core registration form.
+     */
+    function showContent()
+    {
+        if (!empty($this->message_text)) {
+            $this->element('p', null, $this->message);
+            return;
+        }
+
+        $this->elementStart('form', array('method' => 'post',
+                                          'id' => 'form_settings_twitter_connect',
+                                          'class' => 'form_settings',
+                                          'action' => common_local_url('twitterauthorization')));
+        $this->elementStart('fieldset', array('id' => 'settings_twitter_connect_options'));
+        // TRANS: Fieldset legend.
+        $this->element('legend', null, _m('Connection options'));
+
+        $this->hidden('access_token_key', $this->access_token->key);
+        $this->hidden('access_token_secret', $this->access_token->secret);
+        $this->hidden('twuid', $this->twuid);
+        $this->hidden('tw_fields_screen_name', $this->tw_fields['screen_name']);
+        $this->hidden('tw_fields_name', $this->tw_fields['fullname']);
+        $this->hidden('token', common_session_token());
+
+        // Don't allow new account creation if site is flagged as invite only
+       if (common_config('site', 'inviteonly') == false) {
+            $this->elementStart('fieldset');
+            $this->element('legend', null,
+                           // TRANS: Fieldset legend.
+                           _m('Create new account'));
+            $this->element('p', null,
+                           // TRANS: Sub form introduction text.
+                          _m('Create a new user with this nickname.'));
+            $this->elementStart('ul', 'form_data');
+
+            // Hook point for captcha etc
+            Event::handle('StartRegistrationFormData', array($this));
+
+            $this->elementStart('li');
+            // TRANS: Field label.
+            $this->input('newname', _m('New nickname'),
+                         ($this->username) ? $this->username : '',
+                         // TRANS: Field title for nickname field.
+                         _m('1-64 lowercase letters or numbers, no punctuation or spaces.'));
+            $this->elementEnd('li');
+            $this->elementStart('li');
+            // TRANS: Field label.
+            $this->input('email', _m('LABEL','Email'), $this->getEmail(),
+                         // TRANS: Field title for e-mail address field.
+                         _m('Used only for updates, announcements, '.
+                           'and password recovery'));
+            $this->elementEnd('li');
+
+            // Hook point for captcha etc
+            Event::handle('EndRegistrationFormData', array($this));
+
+            $this->elementEnd('ul');
+            // TRANS: Button text for creating a new StatusNet account in the Twitter connect page.
+            $this->submit('create', _m('BUTTON','Create'));
+            $this->elementEnd('fieldset');
+        }
+
+        $this->elementStart('fieldset');
+        $this->element('legend', null,
+                       // TRANS: Fieldset legend.
+                       _m('Connect existing account'));
+        $this->element('p', null,
+                       // TRANS: Sub form introduction text.
+                       _m('If you already have an account, login with your username and password to connect it to your Twitter account.'));
+        $this->elementStart('ul', 'form_data');
+        $this->elementStart('li');
+        // TRANS: Field label.
+        $this->input('nickname', _m('Existing nickname'));
+        $this->elementEnd('li');
+        $this->elementStart('li');
+        // TRANS: Field label.
+        $this->password('password', _m('Password'));
+        $this->elementEnd('li');
+        $this->elementEnd('ul');
+        $this->elementEnd('fieldset');
+
+        $this->elementStart('fieldset');
+        $this->element('legend', null,
+                       // TRANS: Fieldset legend.
+                       _m('License'));
+        $this->elementStart('ul', 'form_data');
+        $this->elementStart('li');
+        $this->element('input', array('type' => 'checkbox',
+                                      'id' => 'license',
+                                      'class' => 'checkbox',
+                                      'name' => 'license',
+                                      'value' => 'true'));
+        $this->elementStart('label', array('class' => 'checkbox', 'for' => 'license'));
+        // TRANS: Text for license agreement checkbox.
+        // TRANS: %s is the license as configured for the StatusNet site.
+        $message = _m('My text and files are available under %s ' .
+                     'except this private data: password, ' .
+                     'email address, IM address, and phone number.');
+        $link = '<a href="' .
+                htmlspecialchars(common_config('license', 'url')) .
+                '">' .
+                htmlspecialchars(common_config('license', 'title')) .
+                '</a>';
+        $this->raw(sprintf(htmlspecialchars($message), $link));
+        $this->elementEnd('label');
+        $this->elementEnd('li');
+        $this->elementEnd('ul');
+        $this->elementEnd('fieldset');
+        // TRANS: Button text for connecting an existing StatusNet account in the Twitter connect page..
+        $this->submit('connect', _m('BUTTON','Connect'));
+        $this->elementEnd('fieldset');
+        $this->elementEnd('form');
+    }
+
+    /**
+     * Get specified e-mail from the form, or the invite code.
+     *
+     * @return string
+     */
+    function getEmail()
+    {
+        $email = $this->trimmed('email');
+        if (!empty($email)) {
+            return $email;
+        }
+
+        // Terrible hack for invites...
+        if (common_config('site', 'inviteonly')) {
+            $code = $_SESSION['invitecode'];
+            if ($code) {
+                $invite = Invitation::getKV($code);
+
+                if ($invite && $invite->address_type == 'email') {
+                    return $invite->address;
+                }
+            }
+        }
+        return '';
+    }
+
+    function message($msg)
+    {
+        $this->message_text = $msg;
+        $this->showPage();
+    }
+
+    function createNewUser()
+    {
+        if (!Event::handle('StartRegistrationTry', array($this))) {
+            return;
+        }
+
+        if (common_config('site', 'closed')) {
+            // TRANS: Client error displayed when trying to create a new user while creating new users is not allowed.
+            $this->clientError(_m('Registration not allowed.'));
+            return;
+        }
+
+        $invite = null;
+
+        if (common_config('site', 'inviteonly')) {
+            $code = $_SESSION['invitecode'];
+            if (empty($code)) {
+                // TRANS: Client error displayed when trying to create a new user while creating new users is not allowed.
+                $this->clientError(_m('Registration not allowed.'));
+                return;
+            }
+
+            $invite = Invitation::getKV($code);
+
+            if (empty($invite)) {
+                // TRANS: Client error displayed when trying to create a new user with an invalid invitation code.
+                $this->clientError(_m('Not a valid invitation code.'));
+                return;
+            }
+        }
+
+        try {
+            $nickname = Nickname::normalize($this->trimmed('newname'));
+        } catch (NicknameException $e) {
+            $this->showForm($e->getMessage());
+            return;
+        }
+
+        if (!User::allowed_nickname($nickname)) {
+            // TRANS: Client error displayed when trying to create a new user with an invalid username.
+            $this->showForm(_m('Nickname not allowed.'));
+            return;
+        }
+
+        if (User::getKV('nickname', $nickname)) {
+            // TRANS: Client error displayed when trying to create a new user with a username that is already in use.
+            $this->showForm(_m('Nickname already in use. Try another one.'));
+            return;
+        }
+
+        $fullname = trim($this->tw_fields['fullname']);
+
+        $args = array('nickname' => $nickname, 'fullname' => $fullname);
+
+        if (!empty($invite)) {
+            $args['code'] = $invite->code;
+        }
+
+        $email = $this->getEmail();
+        if (!empty($email)) {
+            $args['email'] = $email;
+        }
+
+        $user = User::register($args);
+
+        if (empty($user)) {
+            // TRANS: Server error displayed when creating a new user has failed.
+            $this->serverError(_m('Error registering user.'));
+            return;
+        }
+
+        $result = $this->saveForeignLink($user->id,
+                                         $this->twuid,
+                                         $this->access_token);
+
+        save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
+
+        if (!$result) {
+            // TRANS: Server error displayed when connecting a user to a Twitter user has failed.
+            $this->serverError(_m('Error connecting user to Twitter.'));
+            return;
+        }
+
+        common_set_user($user);
+        common_real_login(true);
+
+        common_debug('TwitterBridge Plugin - ' .
+                     "Registered new user $user->id from Twitter user $this->twuid");
+
+        Event::handle('EndRegistrationTry', array($this));
+
+        common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)),
+                        303);
+    }
+
+    function connectNewUser()
+    {
+        $nickname = $this->trimmed('nickname');
+        $password = $this->trimmed('password');
+
+        if (!common_check_user($nickname, $password)) {
+            // TRANS: Form validation error displayed when connecting an existing user to a Twitter user fails because
+            // TRANS: the provided username and/or password are incorrect.
+            $this->showForm(_m('Invalid username or password.'));
+            return;
+        }
+
+        $user = User::getKV('nickname', $nickname);
+
+        if (!empty($user)) {
+            common_debug('TwitterBridge Plugin - ' .
+                         "Legit user to connect to Twitter: $nickname");
+        }
+
+        $result = $this->saveForeignLink($user->id,
+                                         $this->twuid,
+                                         $this->access_token);
+
+        save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
+
+        if (!$result) {
+            // TRANS: Server error displayed connecting a user to a Twitter user has failed.
+            $this->serverError(_m('Error connecting user to Twitter.'));
+            return;
+        }
+
+        common_debug('TwitterBridge Plugin - ' .
+                     "Connected Twitter user $this->twuid to local user $user->id");
+
+        common_set_user($user);
+        common_real_login(true);
+
+        $this->goHome($user->nickname);
+    }
+
+    function connectUser()
+    {
+        $user = common_current_user();
+
+        $result = $this->flinkUser($user->id, $this->twuid);
+
+        if (empty($result)) {
+            // TRANS: Server error displayed connecting a user to a Twitter user has failed.
+            $this->serverError(_m('Error connecting user to Twitter.'));
+            return;
+        }
+
+        common_debug('TwitterBridge Plugin - ' .
+                     "Connected Twitter user $this->twuid to local user $user->id");
+
+        // Return to Twitter connection settings tab
+        common_redirect(common_local_url('twittersettings'), 303);
+    }
+
+    function tryLogin()
+    {
+        common_debug('TwitterBridge Plugin - ' .
+                     "Trying login for Twitter user $this->twuid.");
+
+        $flink = Foreign_link::getByForeignID($this->twuid,
+                                              TWITTER_SERVICE);
+
+        if (!empty($flink)) {
+            $user = $flink->getUser();
+
+            if (!empty($user)) {
+
+                common_debug('TwitterBridge Plugin - ' .
+                             "Logged in Twitter user $flink->foreign_id as user $user->id ($user->nickname)");
+
+                common_set_user($user);
+                common_real_login(true);
+                $this->goHome($user->nickname);
+            }
+
+        } else {
+
+            common_debug('TwitterBridge Plugin - ' .
+                         "No flink found for twuid: $this->twuid - new user");
+
+            $this->showForm(null, $this->bestNewNickname());
+        }
+    }
+
+    function goHome($nickname)
+    {
+        $url = common_get_returnto();
+        if ($url) {
+            // We don't have to return to it again
+            common_set_returnto(null);
+        } else {
+            $url = common_local_url('all',
+                                    array('nickname' =>
+                                          $nickname));
+        }
+
+        common_redirect($url, 303);
+    }
+
+    function bestNewNickname()
+    {
+        if (!empty($this->tw_fields['fullname'])) {
+            $nickname = $this->nicknamize($this->tw_fields['fullname']);
+            if ($this->isNewNickname($nickname)) {
+                return $nickname;
+            }
+        }
+
+        return null;
+    }
+
+     // Given a string, try to make it work as a nickname
+
+     function nicknamize($str)
+     {
+         $str = preg_replace('/\W/', '', $str);
+         $str = str_replace(array('-', '_'), '', $str);
+         return strtolower($str);
+     }
+
+    function isNewNickname($str)
+    {
+        if (!Nickname::isValid($str)) {
+            return false;
+        }
+        if (!User::allowed_nickname($str)) {
+            return false;
+        }
+        if (User::getKV('nickname', $str)) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/plugins/TwitterBridge/actions/twitterlogin.php b/plugins/TwitterBridge/actions/twitterlogin.php
new file mode 100644 (file)
index 0000000..379e136
--- /dev/null
@@ -0,0 +1,100 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * 'Sign in with Twitter' login page
+ *
+ * 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  Login
+ * @package   StatusNet
+ * @author    Julien Chaumond <chaumond@gmail.com>
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
+
+/**
+ * Page for logging in with Twitter
+ *
+ * @category Login
+ * @package  StatusNet
+ * @author   Julien Chaumond <chaumond@gmail.com>
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ * @see      SettingsAction
+ */
+class TwitterloginAction extends Action
+{
+    function handle($args)
+    {
+        parent::handle($args);
+
+        if (common_is_real_login()) {
+            // TRANS: Client error displayed when trying to log in using Twitter while already logged in to StatusNet.
+            $this->clientError(_m('Already logged in.'));
+        }
+
+        $this->showPage();
+    }
+
+    function title()
+    {
+        // TRANS: Title for login using Twitter page.
+        return _m('TITLE','Twitter Login');
+    }
+
+    function getInstructions()
+    {
+        // TRANS: Instructions for login using Twitter page.
+        return _m('Login with your Twitter account');
+    }
+
+    function showPageNotice()
+    {
+        $instr = $this->getInstructions();
+        $output = common_markup_to_html($instr);
+        $this->elementStart('div', 'instructions');
+        $this->raw($output);
+        $this->elementEnd('div');
+    }
+
+    function showContent()
+    {
+        $this->elementStart('a', array('href' => common_local_url('twitterauthorization',
+                                                                  null,
+                                                                  array('signin' => true))));
+        $this->element('img', array('src' => Plugin::staticPath('TwitterBridge', 'Sign-in-with-Twitter-lighter.png'),
+                                    // TRANS: Alternative text for "sign in with Twitter" image.
+                                    'alt' => _m('Sign in with Twitter')));
+        $this->elementEnd('a');
+    }
+
+    function showLocalNav()
+    {
+        $nav = new LoginGroupNav($this);
+        $nav->show();
+    }
+}
diff --git a/plugins/TwitterBridge/actions/twittersettings.php b/plugins/TwitterBridge/actions/twittersettings.php
new file mode 100644 (file)
index 0000000..88799e7
--- /dev/null
@@ -0,0 +1,333 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Settings for Twitter integration
+ *
+ * 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  Settings
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
+
+/**
+ * Settings for Twitter integration
+ *
+ * @category Settings
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ * @see      SettingsAction
+ */
+class TwittersettingsAction extends ProfileSettingsAction
+{
+    /**
+     * Title of the page
+     *
+     * @return string Title of the page
+     */
+
+    function title()
+    {
+        // TRANS: Title for page with Twitter integration settings.
+        return _m('Twitter settings');
+    }
+
+    /**
+     * Instructions for use
+     *
+     * @return instructions for use
+     */
+
+    function getInstructions()
+    {
+        // TRANS: Instructions for page with Twitter integration settings.
+        return _m('Connect your Twitter account to share your updates ' .
+                  'with your Twitter friends and vice-versa.');
+    }
+
+    /**
+     * Content area of the page
+     *
+     * Shows a form for associating a Twitter account with this
+     * StatusNet account. Also lets the user set preferences.
+     *
+     * @return void
+     */
+    function showContent()
+    {
+
+        $user = common_current_user();
+
+        $profile = $user->getProfile();
+
+        $fuser = null;
+
+        $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
+
+        if (!empty($flink)) {
+            $fuser = $flink->getForeignUser();
+        }
+
+        $this->elementStart('form', array('method' => 'post',
+                                          'id' => 'form_settings_twitter',
+                                          'class' => 'form_settings',
+                                          'action' =>
+                                          common_local_url('twittersettings')));
+
+        $this->hidden('token', common_session_token());
+
+        $this->elementStart('fieldset', array('id' => 'settings_twitter_account'));
+
+        if (empty($fuser)) {
+            $this->elementStart('ul', 'form_data');
+            $this->elementStart('li', array('id' => 'settings_twitter_login_button'));
+            $this->element('a', array('href' => common_local_url('twitterauthorization')),
+                           // TRANS: Link description to connect to a Twitter account.
+                           'Connect my Twitter account');
+            $this->elementEnd('li');
+            $this->elementEnd('ul');
+
+            $this->elementEnd('fieldset');
+        } else {
+            // TRANS: Fieldset legend.
+            $this->element('legend', null, _m('Twitter account'));
+            $this->elementStart('p', array('id' => 'form_confirmed'));
+            $this->element('a', array('href' => $fuser->uri), $fuser->nickname);
+            $this->elementEnd('p');
+            $this->element('p', 'form_note',
+                           // TRANS: Form note when a Twitter account has been connected.
+                           _m('Connected Twitter account'));
+            $this->elementEnd('fieldset');
+
+            $this->elementStart('fieldset');
+
+            // TRANS: Fieldset legend.
+            $this->element('legend', null, _m('Disconnect my account from Twitter'));
+
+            if (!$user->password) {
+                $this->elementStart('p', array('class' => 'form_guide'));
+                // TRANS: Form guide. %s is a URL to the password settings.
+                // TRANS: This message contains a Markdown link in the form [description](link).
+                $message = sprintf(_m('Disconnecting your Twitter account ' .
+                                      'could make it impossible to log in! Please ' .
+                                      '[set a password](%s) first.'),
+                                   common_local_url('passwordsettings'));
+                $message = common_markup_to_html($message);
+                $this->text($message);
+                $this->elementEnd('p');
+            } else {
+                // TRANS: Form instructions. %1$s is the StatusNet sitename.
+                $note = _m('Keep your %1$s account but disconnect from Twitter. ' .
+                    'You can use your %1$s password to log in.');
+                $site = common_config('site', 'name');
+
+                $this->element('p', 'instructions',
+                    sprintf($note, $site));
+
+                // TRANS: Button text for disconnecting a Twitter account.
+                $this->submit('disconnect', _m('BUTTON','Disconnect'));
+            }
+
+            $this->elementEnd('fieldset');
+
+            $this->elementStart('fieldset', array('id' => 'settings_twitter_preferences'));
+
+            // TRANS: Fieldset legend.
+            $this->element('legend', null, _m('Preferences'));
+            $this->elementStart('ul', 'form_data');
+            $this->elementStart('li');
+            $this->checkbox('noticesend',
+                            // TRANS: Checkbox label.
+                            _m('Automatically send my notices to Twitter.'),
+                            ($flink) ?
+                            ($flink->noticesync & FOREIGN_NOTICE_SEND) :
+                            true);
+            $this->elementEnd('li');
+            $this->elementStart('li');
+            $this->checkbox('replysync',
+                            // TRANS: Checkbox label.
+                            _m('Send local "@" replies to Twitter.'),
+                            ($flink) ?
+                            ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) :
+                            true);
+            $this->elementEnd('li');
+            $this->elementStart('li');
+            $this->checkbox('friendsync',
+                            // TRANS: Checkbox label.
+                            _m('Subscribe to my Twitter friends here.'),
+                            ($flink) ?
+                            ($flink->friendsync & FOREIGN_FRIEND_RECV) :
+                            false);
+            $this->elementEnd('li');
+
+            if (common_config('twitterimport','enabled')) {
+                $this->elementStart('li');
+                $this->checkbox('noticerecv',
+                                // TRANS: Checkbox label.
+                                _m('Import my friends timeline.'),
+                                ($flink) ?
+                                ($flink->noticesync & FOREIGN_NOTICE_RECV) :
+                                false);
+                $this->elementEnd('li');
+            } else {
+                // preserve setting even if bidrection bridge toggled off
+
+                if ($flink && ($flink->noticesync & FOREIGN_NOTICE_RECV)) {
+                    $this->hidden('noticerecv', true, 'noticerecv');
+                }
+            }
+
+            $this->elementEnd('ul');
+
+            if ($flink) {
+                // TRANS: Button text for saving Twitter integration settings.
+                $this->submit('save', _m('BUTTON','Save'));
+            } else {
+                // TRANS: Button text for adding Twitter integration.
+                $this->submit('add', _m('BUTTON','Add'));
+            }
+
+            $this->elementEnd('fieldset');
+        }
+
+        $this->elementEnd('form');
+    }
+
+    /**
+     * Handle posts to this form
+     *
+     * Based on the button that was pressed, muxes out to other functions
+     * to do the actual task requested.
+     *
+     * All sub-functions reload the form with a message -- success or failure.
+     *
+     * @return void
+     */
+    function handlePost()
+    {
+        // CSRF protection
+        $token = $this->trimmed('token');
+        if (!$token || $token != common_session_token()) {
+            // TRANS: Client error displayed when the session token does not match or is not given.
+            $this->showForm(_m('There was a problem with your session token. '.
+                               'Try again, please.'));
+            return;
+        }
+
+        if ($this->arg('save')) {
+            $this->savePreferences();
+        } else if ($this->arg('disconnect')) {
+            $this->removeTwitterAccount();
+        } else {
+            // TRANS: Client error displayed when the submitted form contains unexpected data.
+            $this->showForm(_m('Unexpected form submission.'));
+        }
+    }
+
+    /**
+     * Disassociate an existing Twitter account from this account
+     *
+     * @return void
+     */
+    function removeTwitterAccount()
+    {
+        $user = common_current_user();
+        $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
+
+        if (empty($flink)) {
+            // TRANS: Client error displayed when trying to remove a connected Twitter account when there isn't one connected.
+            $this->clientError(_m('No Twitter connection to remove.'));
+            return;
+        }
+
+        $result = $flink->safeDelete();
+
+        if (empty($result)) {
+            common_log_db_error($flink, 'DELETE', __FILE__);
+            // TRANS: Server error displayed when trying to remove a connected Twitter account fails.
+            $this->serverError(_m('Could not remove Twitter user.'));
+            return;
+        }
+
+        // TRANS: Success message displayed after disconnecting a Twitter account.
+        $this->showForm(_m('Twitter account disconnected.'), true);
+    }
+
+    /**
+     * Save user's Twitter-bridging preferences
+     *
+     * @return void
+     */
+    function savePreferences()
+    {
+        $noticesend = $this->boolean('noticesend');
+        $noticerecv = $this->boolean('noticerecv');
+        $friendsync = $this->boolean('friendsync');
+        $replysync  = $this->boolean('replysync');
+
+        $user = common_current_user();
+        $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
+
+        if (empty($flink)) {
+            common_log_db_error($flink, 'SELECT', __FILE__);
+            // @todo FIXME: Shouldn't this be a serverError()?
+            // TRANS: Server error displayed when saving Twitter integration preferences fails.
+            $this->showForm(_m('Could not save Twitter preferences.'));
+            return;
+        }
+
+        $original = clone($flink);
+        $wasReceiving = (bool)($original->noticesync & FOREIGN_NOTICE_RECV);
+        $flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync);
+        $result = $flink->update($original);
+
+        if ($result === false) {
+            common_log_db_error($flink, 'UPDATE', __FILE__);
+            // @todo FIXME: Shouldn't this be a serverError()?
+            // TRANS: Server error displayed when saving Twitter integration preferences fails.
+            $this->showForm(_m('Could not save Twitter preferences.'));
+            return;
+        }
+
+        if ($wasReceiving xor $noticerecv) {
+            $this->notifyDaemon($flink->foreign_id, $noticerecv);
+        }
+
+        // TRANS: Success message after saving Twitter integration preferences.
+        $this->showForm(_m('Twitter preferences saved.'), true);
+    }
+
+    /**
+     * Tell the import daemon that we've updated a user's receive status.
+     */
+    function notifyDaemon($twitterUserId, $receiving)
+    {
+        // @todo... should use control signals rather than queues
+    }
+}
diff --git a/plugins/TwitterBridge/classes/Notice_to_status.php b/plugins/TwitterBridge/classes/Notice_to_status.php
new file mode 100644 (file)
index 0000000..6f75e3c
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+/**
+ * Data class for remembering notice-to-status mappings
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for mapping notices to statuses
+ *
+ * Notices flow back and forth between Twitter and StatusNet. We use this
+ * table to remember which StatusNet notice corresponds to which Twitter
+ * status.
+ *
+ * Note that notice_id is unique only within a single database; if you
+ * want to share this data for some reason, get the notice's URI and use
+ * that instead, since it's universally unique.
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+
+class Notice_to_status extends Managed_DataObject
+{
+    public $__table = 'notice_to_status'; // table name
+    public $notice_id;                    // int(4)  primary_key not_null
+    public $status_id;                    // bigint not_null
+    public $created;                      // datetime()   not_null
+    public $modified;                     // datetime   not_null default_0000-00-00%2000%3A00%3A00
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'local notice id'),
+                'status_id' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'twitter status id'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('notice_id'),
+            'unique keys' => array(
+                'status_id_key' => array('status_id'),
+            ),
+            'foreign keys' => array(
+                'notice_to_status_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
+            ),
+        );
+    }
+
+    /**
+     * Save a mapping between a notice and a status
+     * Warning: status_id values may not fit in 32-bit integers.
+     *
+     * @param integer $notice_id ID of the notice in StatusNet
+     * @param integer $status_id ID of the status in Twitter
+     *
+     * @return Notice_to_status new object for this value
+     */
+    static function saveNew($notice_id, $status_id)
+    {
+        if (empty($notice_id)) {
+            throw new Exception("Invalid notice_id $notice_id");
+        }
+        $n2s = Notice_to_status::getKV('notice_id', $notice_id);
+
+        if (!empty($n2s)) {
+            return $n2s;
+        }
+
+        if (empty($status_id)) {
+            throw new Exception("Invalid status_id $status_id");
+        }
+        $n2s = Notice_to_status::getKV('status_id', $status_id);
+
+        if (!empty($n2s)) {
+            return $n2s;
+        }
+
+        common_debug("Mapping notice {$notice_id} to Twitter status {$status_id}");
+
+        $n2s = new Notice_to_status();
+
+        $n2s->notice_id = $notice_id;
+        $n2s->status_id = $status_id;
+        $n2s->created   = common_sql_now();
+
+        $n2s->insert();
+
+        return $n2s;
+    }
+}
diff --git a/plugins/TwitterBridge/classes/Twitter_synch_status.php b/plugins/TwitterBridge/classes/Twitter_synch_status.php
new file mode 100644 (file)
index 0000000..9a20536
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Store last-touched ID for various timelines
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Store various timeline data
+ *
+ * We don't want to keep re-fetching the same statuses and direct messages from Twitter.
+ * So, we store the last ID we see from a timeline, and store it. Next time
+ * around, we use that ID in the since_id parameter.
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+class Twitter_synch_status extends Managed_DataObject
+{
+    public $__table = 'twitter_synch_status'; // table name
+    public $foreign_id;                      // bigint primary_key not_null
+    public $timeline;                        // varchar(255)  primary_key not_null
+    public $last_id;                         // bigint not_null
+    public $created;                         // datetime()   not_null
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'foreign_id' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'Foreign message ID'),
+                'timeline' => array('type' => 'varchar', 'length' => 255, 'description' => 'timeline name'),
+                'last_id' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'last id fetched'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('foreign_id', 'timeline'),
+        );
+    }
+
+    static function getLastId($foreign_id, $timeline)
+    {
+        $tss = self::pkeyGet(array('foreign_id' => $foreign_id,
+                                   'timeline' => $timeline));
+
+        if (empty($tss)) {
+            return null;
+        } else {
+            return $tss->last_id;
+        }
+    }
+
+    static function setLastId($foreign_id, $timeline, $last_id)
+    {
+        $tss = self::pkeyGet(array('foreign_id' => $foreign_id,
+                                   'timeline' => $timeline));
+
+        if (empty($tss)) {
+            $tss = new Twitter_synch_status();
+
+            $tss->foreign_id = $foreign_id;
+            $tss->timeline   = $timeline;
+            $tss->last_id    = $last_id;
+            $tss->created    = common_sql_now();
+            $tss->modified   = $tss->created;
+
+            $tss->insert();
+
+            return true;
+        } else {
+            $orig = clone($tss);
+
+            $tss->last_id  = $last_id;
+            $tss->modified = common_sql_now();
+
+            $tss->update();
+
+            return true;
+        }
+    }
+}
index 47a44597be532add9a8d5418c15a794fb0fa721f..6599058fd48239ab2da5f49d4edc4a322be82ff5 100755 (executable)
@@ -40,7 +40,7 @@ require_once INSTALLDIR . '/scripts/commandline.inc';
 require_once INSTALLDIR . '/lib/common.php';
 require_once INSTALLDIR . '/lib/daemon.php';
 require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
-require_once INSTALLDIR . '/plugins/TwitterBridge/twitteroauthclient.php';
+require_once INSTALLDIR . '/plugins/TwitterBridge/lib/twitteroauthclient.php';
 
 /**
  * Fetch statuses from Twitter
diff --git a/plugins/TwitterBridge/jsonstreamreader.php b/plugins/TwitterBridge/jsonstreamreader.php
deleted file mode 100644 (file)
index d5852c9..0000000
+++ /dev/null
@@ -1,275 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Plugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-class OAuthData
-{
-    public $consumer_key, $consumer_secret, $token, $token_secret;
-}
-
-/**
- *
- */
-abstract class JsonStreamReader
-{
-    const CRLF = "\r\n";
-
-    public $id;
-    protected $socket = null;
-    protected $state = 'init'; // 'init', 'connecting', 'waiting', 'headers', 'active'
-
-    public function __construct()
-    {
-        $this->id = get_class($this) . '.' . substr(md5(mt_rand()), 0, 8);
-    }
-
-    /**
-     * Starts asynchronous connect operation...
-     *
-     * @fixme Can we do the open-socket fully async to? (need write select infrastructure)
-     *
-     * @param string $url
-     */
-    public function connect($url)
-    {
-        common_log(LOG_DEBUG, "$this->id opening connection to $url");
-
-        $scheme = parse_url($url, PHP_URL_SCHEME);
-        if ($scheme == 'http') {
-            $rawScheme = 'tcp';
-        } else if ($scheme == 'https') {
-            $rawScheme = 'ssl';
-        } else {
-            // TRANS: Server exception thrown when an invalid URL scheme is detected.
-            throw new ServerException(_m('Invalid URL scheme for HTTP stream reader.'));
-        }
-
-        $host = parse_url($url, PHP_URL_HOST);
-        $port = parse_url($url, PHP_URL_PORT);
-        if (!$port) {
-            if ($scheme == 'https') {
-                $port = 443;
-            } else {
-                $port = 80;
-            }
-        }
-
-        $path = parse_url($url, PHP_URL_PATH);
-        $query = parse_url($url, PHP_URL_QUERY);
-        if ($query) {
-            $path .= '?' . $query;
-        }
-
-        $errno = $errstr = null;
-        $timeout = 5;
-        //$flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT;
-        $flags = STREAM_CLIENT_CONNECT;
-        // @fixme add SSL params
-        $this->socket = stream_socket_client("$rawScheme://$host:$port", $errno, $errstr, $timeout, $flags);
-
-        $this->send($this->httpOpen($host, $path));
-
-        stream_set_blocking($this->socket, false);
-        $this->state = 'waiting';
-    }
-
-    /**
-     * Send some fun data off to the server.
-     *
-     * @param string $buffer
-     */
-    function send($buffer)
-    {
-        fwrite($this->socket, $buffer);
-    }
-
-    /**
-     * Read next packet of data from the socket.
-     *
-     * @return string
-     */
-    function read()
-    {
-        $buffer = fread($this->socket, 65536);
-        return $buffer;
-    }
-
-    /**
-     * Build HTTP request headers.
-     *
-     * @param string $host
-     * @param string $path
-     * @return string
-     */
-    protected function httpOpen($host, $path)
-    {
-        $lines = array(
-            "GET $path HTTP/1.1",
-            "Host: $host",
-            "User-Agent: StatusNet/" . STATUSNET_VERSION . " (TwitterBridgePlugin)",
-            "Connection: close",
-            "",
-            ""
-        );
-        return implode(self::CRLF, $lines);
-    }
-
-    /**
-     * Close the current connection, if open.
-     */
-    public function close()
-    {
-        if ($this->isConnected()) {
-            common_log(LOG_DEBUG, "$this->id closing connection.");
-            fclose($this->socket);
-            $this->socket = null;
-        }
-    }
-
-    /**
-     * Are we currently connected?
-     *
-     * @return boolean
-     */
-    public function isConnected()
-    {
-        return $this->socket !== null;
-    }
-
-    /**
-     * Send any sockets we're listening on to the IO manager
-     * to wait for input.
-     *
-     * @return array of resources
-     */
-    public function getSockets()
-    {
-        if ($this->isConnected()) {
-            return array($this->socket);
-        }
-        return array();
-    }
-
-    /**
-     * Take a chunk of input over the horn and go go go! :D
-     *
-     * @param string $buffer
-     */
-    public function handleInput($socket)
-    {
-        if ($this->socket !== $socket) {
-            // TRANS: Exception thrown when input from an inexpected socket is encountered.
-            throw new Exception(_m('Got input from unexpected socket!'));
-        }
-
-        try {
-            $buffer = $this->read();
-            $lines = explode(self::CRLF, $buffer);
-            foreach ($lines as $line) {
-                $this->handleLine($line);
-            }
-        } catch (Exception $e) {
-            common_log(LOG_ERR, "$this->id aborting connection due to error: " . $e->getMessage());
-            fclose($this->socket);
-            throw $e;
-        }
-    }
-
-    protected function handleLine($line)
-    {
-        switch ($this->state)
-        {
-            case 'waiting':
-                $this->handleLineWaiting($line);
-                break;
-            case 'headers':
-                $this->handleLineHeaders($line);
-                break;
-            case 'active':
-                $this->handleLineActive($line);
-                break;
-            default:
-                // TRANS: Exception thrown when an invalid state is encountered in handleLine.
-                // TRANS: %s is the invalid state.
-                throw new Exception(sprintf(_m('Invalid state in handleLine: %s.'),$this->state));
-        }
-    }
-
-    /**
-     *
-     * @param <type> $line
-     */
-    protected function handleLineWaiting($line)
-    {
-        $bits = explode(' ', $line, 3);
-        if (count($bits) != 3) {
-            // TRANS: Exception thrown when an invalid response line is encountered.
-            // TRANS: %s is the invalid line.
-            throw new Exception(sprintf(_m('Invalid HTTP response line: %s.'),$line));
-        }
-
-        list($http, $status, $text) = $bits;
-        if (substr($http, 0, 5) != 'HTTP/') {
-            // TRANS: Exception thrown when an invalid response line part is encountered.
-            // TRANS: %1$s is the chunk, %2$s is the line.
-            throw new Exception(sprintf(_m('Invalid HTTP response line chunk "%1$s": %2$s.'),$http, $line));
-        }
-        if ($status != '200') {
-            // TRANS: Exception thrown when an invalid response code is encountered.
-            // TRANS: %1$s is the response code, %2$s is the line.
-            throw new Exception(sprintf(_m('Bad HTTP response code %1$s: %2$s.'),$status,$line));
-        }
-        common_log(LOG_DEBUG, "$this->id $line");
-        $this->state = 'headers';
-    }
-
-    protected function handleLineHeaders($line)
-    {
-        if ($line == '') {
-            $this->state = 'active';
-            common_log(LOG_DEBUG, "$this->id connection is active!");
-        } else {
-            common_log(LOG_DEBUG, "$this->id read HTTP header: $line");
-            $this->responseHeaders[] = $line;
-        }
-    }
-
-    protected function handleLineActive($line)
-    {
-        if ($line == "") {
-            // Server sends empty lines as keepalive.
-            return;
-        }
-        $data = json_decode($line);
-        if ($data) {
-            $this->handleJson($data);
-        } else {
-            common_log(LOG_ERR, "$this->id received bogus JSON data: " . var_export($line, true));
-        }
-    }
-
-    abstract protected function handleJson(stdClass $data);
-}
diff --git a/plugins/TwitterBridge/lib/jsonstreamreader.php b/plugins/TwitterBridge/lib/jsonstreamreader.php
new file mode 100644 (file)
index 0000000..d5852c9
--- /dev/null
@@ -0,0 +1,275 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+class OAuthData
+{
+    public $consumer_key, $consumer_secret, $token, $token_secret;
+}
+
+/**
+ *
+ */
+abstract class JsonStreamReader
+{
+    const CRLF = "\r\n";
+
+    public $id;
+    protected $socket = null;
+    protected $state = 'init'; // 'init', 'connecting', 'waiting', 'headers', 'active'
+
+    public function __construct()
+    {
+        $this->id = get_class($this) . '.' . substr(md5(mt_rand()), 0, 8);
+    }
+
+    /**
+     * Starts asynchronous connect operation...
+     *
+     * @fixme Can we do the open-socket fully async to? (need write select infrastructure)
+     *
+     * @param string $url
+     */
+    public function connect($url)
+    {
+        common_log(LOG_DEBUG, "$this->id opening connection to $url");
+
+        $scheme = parse_url($url, PHP_URL_SCHEME);
+        if ($scheme == 'http') {
+            $rawScheme = 'tcp';
+        } else if ($scheme == 'https') {
+            $rawScheme = 'ssl';
+        } else {
+            // TRANS: Server exception thrown when an invalid URL scheme is detected.
+            throw new ServerException(_m('Invalid URL scheme for HTTP stream reader.'));
+        }
+
+        $host = parse_url($url, PHP_URL_HOST);
+        $port = parse_url($url, PHP_URL_PORT);
+        if (!$port) {
+            if ($scheme == 'https') {
+                $port = 443;
+            } else {
+                $port = 80;
+            }
+        }
+
+        $path = parse_url($url, PHP_URL_PATH);
+        $query = parse_url($url, PHP_URL_QUERY);
+        if ($query) {
+            $path .= '?' . $query;
+        }
+
+        $errno = $errstr = null;
+        $timeout = 5;
+        //$flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT;
+        $flags = STREAM_CLIENT_CONNECT;
+        // @fixme add SSL params
+        $this->socket = stream_socket_client("$rawScheme://$host:$port", $errno, $errstr, $timeout, $flags);
+
+        $this->send($this->httpOpen($host, $path));
+
+        stream_set_blocking($this->socket, false);
+        $this->state = 'waiting';
+    }
+
+    /**
+     * Send some fun data off to the server.
+     *
+     * @param string $buffer
+     */
+    function send($buffer)
+    {
+        fwrite($this->socket, $buffer);
+    }
+
+    /**
+     * Read next packet of data from the socket.
+     *
+     * @return string
+     */
+    function read()
+    {
+        $buffer = fread($this->socket, 65536);
+        return $buffer;
+    }
+
+    /**
+     * Build HTTP request headers.
+     *
+     * @param string $host
+     * @param string $path
+     * @return string
+     */
+    protected function httpOpen($host, $path)
+    {
+        $lines = array(
+            "GET $path HTTP/1.1",
+            "Host: $host",
+            "User-Agent: StatusNet/" . STATUSNET_VERSION . " (TwitterBridgePlugin)",
+            "Connection: close",
+            "",
+            ""
+        );
+        return implode(self::CRLF, $lines);
+    }
+
+    /**
+     * Close the current connection, if open.
+     */
+    public function close()
+    {
+        if ($this->isConnected()) {
+            common_log(LOG_DEBUG, "$this->id closing connection.");
+            fclose($this->socket);
+            $this->socket = null;
+        }
+    }
+
+    /**
+     * Are we currently connected?
+     *
+     * @return boolean
+     */
+    public function isConnected()
+    {
+        return $this->socket !== null;
+    }
+
+    /**
+     * Send any sockets we're listening on to the IO manager
+     * to wait for input.
+     *
+     * @return array of resources
+     */
+    public function getSockets()
+    {
+        if ($this->isConnected()) {
+            return array($this->socket);
+        }
+        return array();
+    }
+
+    /**
+     * Take a chunk of input over the horn and go go go! :D
+     *
+     * @param string $buffer
+     */
+    public function handleInput($socket)
+    {
+        if ($this->socket !== $socket) {
+            // TRANS: Exception thrown when input from an inexpected socket is encountered.
+            throw new Exception(_m('Got input from unexpected socket!'));
+        }
+
+        try {
+            $buffer = $this->read();
+            $lines = explode(self::CRLF, $buffer);
+            foreach ($lines as $line) {
+                $this->handleLine($line);
+            }
+        } catch (Exception $e) {
+            common_log(LOG_ERR, "$this->id aborting connection due to error: " . $e->getMessage());
+            fclose($this->socket);
+            throw $e;
+        }
+    }
+
+    protected function handleLine($line)
+    {
+        switch ($this->state)
+        {
+            case 'waiting':
+                $this->handleLineWaiting($line);
+                break;
+            case 'headers':
+                $this->handleLineHeaders($line);
+                break;
+            case 'active':
+                $this->handleLineActive($line);
+                break;
+            default:
+                // TRANS: Exception thrown when an invalid state is encountered in handleLine.
+                // TRANS: %s is the invalid state.
+                throw new Exception(sprintf(_m('Invalid state in handleLine: %s.'),$this->state));
+        }
+    }
+
+    /**
+     *
+     * @param <type> $line
+     */
+    protected function handleLineWaiting($line)
+    {
+        $bits = explode(' ', $line, 3);
+        if (count($bits) != 3) {
+            // TRANS: Exception thrown when an invalid response line is encountered.
+            // TRANS: %s is the invalid line.
+            throw new Exception(sprintf(_m('Invalid HTTP response line: %s.'),$line));
+        }
+
+        list($http, $status, $text) = $bits;
+        if (substr($http, 0, 5) != 'HTTP/') {
+            // TRANS: Exception thrown when an invalid response line part is encountered.
+            // TRANS: %1$s is the chunk, %2$s is the line.
+            throw new Exception(sprintf(_m('Invalid HTTP response line chunk "%1$s": %2$s.'),$http, $line));
+        }
+        if ($status != '200') {
+            // TRANS: Exception thrown when an invalid response code is encountered.
+            // TRANS: %1$s is the response code, %2$s is the line.
+            throw new Exception(sprintf(_m('Bad HTTP response code %1$s: %2$s.'),$status,$line));
+        }
+        common_log(LOG_DEBUG, "$this->id $line");
+        $this->state = 'headers';
+    }
+
+    protected function handleLineHeaders($line)
+    {
+        if ($line == '') {
+            $this->state = 'active';
+            common_log(LOG_DEBUG, "$this->id connection is active!");
+        } else {
+            common_log(LOG_DEBUG, "$this->id read HTTP header: $line");
+            $this->responseHeaders[] = $line;
+        }
+    }
+
+    protected function handleLineActive($line)
+    {
+        if ($line == "") {
+            // Server sends empty lines as keepalive.
+            return;
+        }
+        $data = json_decode($line);
+        if ($data) {
+            $this->handleJson($data);
+        } else {
+            common_log(LOG_ERR, "$this->id received bogus JSON data: " . var_export($line, true));
+        }
+    }
+
+    abstract protected function handleJson(stdClass $data);
+}
diff --git a/plugins/TwitterBridge/lib/tweetinqueuehandler.php b/plugins/TwitterBridge/lib/tweetinqueuehandler.php
new file mode 100644 (file)
index 0000000..7f34ade
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
+
+/**
+ * Queue handler to deal with incoming Twitter status updates, as retrieved by
+ * TwitterDaemon (twitterdaemon.php).
+ *
+ * The queue handler passes the status through TwitterImporter for import into the
+ * local database (if necessary), then adds the imported notice to the local inbox
+ * of the attached Twitter user.
+ *
+ * Warning: the way we do inbox distribution manually means that realtime, XMPP, etc
+ * don't work on Twitter-borne messages. When TwitterImporter is changed to handle
+ * that correctly, we'll only need to do this once...?
+ */
+class TweetInQueueHandler extends QueueHandler
+{
+    function transport()
+    {
+        return 'tweetin';
+    }
+
+    function handle($data)
+    {
+        // JSON object with Twitter data
+        $status = $data['status'];
+
+        // Twitter user ID this incoming data belongs to.
+        $receiver = $data['for_user'];
+
+        $importer = new TwitterImport();
+        $notice = $importer->importStatus($status);
+        if ($notice) {
+            $flink = Foreign_link::getByForeignID($receiver, TWITTER_SERVICE);
+            if ($flink) {
+                common_log(LOG_DEBUG, "TweetInQueueHandler - Got flink so add notice ".
+                           $notice->id." to inbox ".$flink->user_id);
+                // @fixme this should go through more regular channels?
+                Inbox::insertNotice($flink->user_id, $notice->id);
+            }else {
+               common_log(LOG_DEBUG, "TweetInQueueHandler - No flink found for foreign user ".$receiver);
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/plugins/TwitterBridge/lib/twitterimport.php b/plugins/TwitterBridge/lib/twitterimport.php
new file mode 100644 (file)
index 0000000..cdfb8f8
--- /dev/null
@@ -0,0 +1,712 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @author    Julien C <chaumond@gmail.com>
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2009-2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
+
+/**
+ * Encapsulation of the Twitter status -> notice incoming bridge import.
+ * Is used by both the polling twitterstatusfetcher.php daemon, and the
+ * in-progress streaming import.
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @author   Julien C <chaumond@gmail.com>
+ * @author   Brion Vibber <brion@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ * @link     http://twitter.com/
+ */
+class TwitterImport
+{
+    public function importStatus($status)
+    {
+        // Hacktastic: filter out stuff coming from this StatusNet
+        $source = mb_strtolower(common_config('integration', 'source'));
+
+        if (preg_match("/$source/", mb_strtolower($status->source))) {
+            common_debug($this->name() . ' - Skipping import of status ' .
+                         twitter_id($status) . ' with source ' . $source);
+            return null;
+        }
+
+        // Don't save it if the user is protected
+        // FIXME: save it but treat it as private
+        if ($status->user->protected) {
+            return null;
+        }
+
+        $notice = $this->saveStatus($status);
+
+        return $notice;
+    }
+
+    function name()
+    {
+        return get_class($this);
+    }
+
+    function saveStatus($status)
+    {
+        $profile = $this->ensureProfile($status->user);
+
+        if (empty($profile)) {
+            common_log(LOG_ERR, $this->name() .
+                ' - Problem saving notice. No associated Profile.');
+            return null;
+        }
+
+        $statusId = twitter_id($status);
+        $statusUri = $this->makeStatusURI($status->user->screen_name, $statusId);
+
+        // check to see if we've already imported the status
+        $n2s = Notice_to_status::getKV('status_id', $statusId);
+
+        if (!empty($n2s)) {
+            common_log(
+                LOG_INFO,
+                $this->name() .
+                " - Ignoring duplicate import: {$statusId}"
+            );
+            return Notice::getKV('id', $n2s->notice_id);
+        }
+
+        // If it's a retweet, save it as a repeat!
+        if (!empty($status->retweeted_status)) {
+            common_log(LOG_INFO, "Status {$statusId} is a retweet of " . twitter_id($status->retweeted_status) . ".");
+            $original = $this->saveStatus($status->retweeted_status);
+            if (empty($original)) {
+                return null;
+            } else {
+                $author = $original->getProfile();
+                // TRANS: Message used to repeat a notice. RT is the abbreviation of 'retweet'.
+                // TRANS: %1$s is the repeated user's name, %2$s is the repeated notice.
+                $content = sprintf(_m('RT @%1$s %2$s'),
+                                   $author->nickname,
+                                   $original->content);
+
+                if (Notice::contentTooLong($content)) {
+                    $contentlimit = Notice::maxContent();
+                    $content = mb_substr($content, 0, $contentlimit - 4) . ' ...';
+                }
+
+                $repeat = Notice::saveNew($profile->id,
+                                          $content,
+                                          'twitter',
+                                          array('repeat_of' => $original->id,
+                                                'uri' => $statusUri,
+                                                'is_local' => Notice::GATEWAY));
+                common_log(LOG_INFO, "Saved {$repeat->id} as a repeat of {$original->id}");
+                Notice_to_status::saveNew($repeat->id, $statusId);
+                return $repeat;
+            }
+        }
+
+        $notice = new Notice();
+
+        $notice->profile_id = $profile->id;
+        $notice->uri        = $statusUri;
+        $notice->url        = $statusUri;
+        $notice->created    = strftime(
+            '%Y-%m-%d %H:%M:%S',
+            strtotime($status->created_at)
+        );
+
+        $notice->source     = 'twitter';
+
+        $notice->reply_to   = null;
+
+        $replyTo = twitter_id($status, 'in_reply_to_status_id');
+        if (!empty($replyTo)) {
+            common_log(LOG_INFO, "Status {$statusId} is a reply to status {$replyTo}");
+            $n2s = Notice_to_status::getKV('status_id', $replyTo);
+            if (empty($n2s)) {
+                common_log(LOG_INFO, "Couldn't find local notice for status {$replyTo}");
+            } else {
+                $reply = Notice::getKV('id', $n2s->notice_id);
+                if (empty($reply)) {
+                    common_log(LOG_INFO, "Couldn't find local notice for status {$replyTo}");
+                } else {
+                    common_log(LOG_INFO, "Found local notice {$reply->id} for status {$replyTo}");
+                    $notice->reply_to     = $reply->id;
+                    $notice->conversation = $reply->conversation;
+                }
+            }
+        }
+
+        if (empty($notice->conversation)) {
+            $conv = Conversation::create();
+            $notice->conversation = $conv->id;
+            common_log(LOG_INFO, "No known conversation for status {$statusId} so making a new one {$conv->id}.");
+        }
+
+        $notice->is_local   = Notice::GATEWAY;
+
+        $notice->content  = html_entity_decode($this->linkify($status, FALSE), ENT_QUOTES, 'UTF-8');
+        $notice->rendered = $this->linkify($status, TRUE);
+
+        if (Event::handle('StartNoticeSave', array(&$notice))) {
+
+            $id = $notice->insert();
+
+            if (!$id) {
+                common_log_db_error($notice, 'INSERT', __FILE__);
+                common_log(LOG_ERR, $this->name() .
+                    ' - Problem saving notice.');
+            }
+
+            Event::handle('EndNoticeSave', array($notice));
+        }
+
+        Notice_to_status::saveNew($notice->id, $statusId);
+
+        $this->saveStatusMentions($notice, $status);
+        $this->saveStatusAttachments($notice, $status);
+
+        $notice->blowOnInsert();
+
+        return $notice;
+    }
+
+    /**
+     * Make an URI for a status.
+     *
+     * @param object $status status object
+     *
+     * @return string URI
+     */
+    function makeStatusURI($username, $id)
+    {
+        return 'http://twitter.com/#!/'
+          . $username
+          . '/status/'
+          . $id;
+    }
+
+
+    /**
+     * Look up a Profile by profileurl field.  Profile::getKV() was
+     * not working consistently.
+     *
+     * @param string $nickname   local nickname of the Twitter user
+     * @param string $profileurl the profile url
+     *
+     * @return mixed value the first Profile with that url, or null
+     */
+    function getProfileByUrl($nickname, $profileurl)
+    {
+        $profile = new Profile();
+        $profile->nickname = $nickname;
+        $profile->profileurl = $profileurl;
+        $profile->limit(1);
+
+        if ($profile->find()) {
+            $profile->fetch();
+            return $profile;
+        }
+
+        return null;
+    }
+
+    /**
+     * Check to see if this Twitter status has already been imported
+     *
+     * @param Profile $profile   Twitter user's local profile
+     * @param string  $statusUri URI of the status on Twitter
+     *
+     * @return mixed value a matching Notice or null
+     */
+    function checkDupe($profile, $statusUri)
+    {
+        $notice = new Notice();
+        $notice->uri = $statusUri;
+        $notice->profile_id = $profile->id;
+        $notice->limit(1);
+
+        if ($notice->find()) {
+            $notice->fetch();
+            return $notice;
+        }
+
+        return null;
+    }
+
+    function ensureProfile($user)
+    {
+        // check to see if there's already a profile for this user
+        $profileurl = 'http://twitter.com/' . $user->screen_name;
+        $profile = $this->getProfileByUrl($user->screen_name, $profileurl);
+
+        if (!empty($profile)) {
+            common_debug($this->name() .
+                         " - Profile for $profile->nickname found.");
+
+            // Check to see if the user's Avatar has changed
+
+            $this->checkAvatar($user, $profile);
+            return $profile;
+
+        } else {
+            common_debug($this->name() . ' - Adding profile and remote profile ' .
+                         "for Twitter user: $profileurl.");
+
+            $profile = new Profile();
+            $profile->query("BEGIN");
+
+            $profile->nickname = $user->screen_name;
+            $profile->fullname = $user->name;
+            $profile->homepage = $user->url;
+            $profile->bio = $user->description;
+            $profile->location = $user->location;
+            $profile->profileurl = $profileurl;
+            $profile->created = common_sql_now();
+
+            try {
+                $id = $profile->insert();
+            } catch(Exception $e) {
+                common_log(LOG_WARNING, $this->name() . ' Couldn\'t insert profile - ' . $e->getMessage());
+            }
+
+            if (empty($id)) {
+                common_log_db_error($profile, 'INSERT', __FILE__);
+                $profile->query("ROLLBACK");
+                return false;
+            }
+
+            // check for remote profile
+
+            $remote_pro = Remote_profile::getKV('uri', $profileurl);
+
+            if (empty($remote_pro)) {
+                $remote_pro = new Remote_profile();
+
+                $remote_pro->id = $id;
+                $remote_pro->uri = $profileurl;
+                $remote_pro->created = common_sql_now();
+
+                try {
+                    $rid = $remote_pro->insert();
+                } catch (Exception $e) {
+                    common_log(LOG_WARNING, $this->name() . ' Couldn\'t save remote profile - ' . $e->getMessage());
+                }
+
+                if (empty($rid)) {
+                    common_log_db_error($profile, 'INSERT', __FILE__);
+                    $profile->query("ROLLBACK");
+                    return false;
+                }
+            }
+
+            $profile->query("COMMIT");
+
+            $this->saveAvatars($user, $id);
+
+            return $profile;
+        }
+    }
+
+    function checkAvatar($twitter_user, $profile)
+    {
+        global $config;
+
+        $newname = 'Twitter_' . $twitter_user->id . '_' . basename($twitter_user->profile_image_url);
+
+        $oldname = $profile->getAvatar(48)->filename;
+
+        if ($newname != $oldname) {
+            common_debug($this->name() . ' - Avatar for Twitter user ' .
+                         "$profile->nickname has changed.");
+            common_debug($this->name() . " - old: $oldname new: $newname");
+
+            $this->updateAvatars($twitter_user, $profile);
+        }
+
+        if ($this->missingAvatarFile($profile)) {
+            common_debug($this->name() . ' - Twitter user ' .
+                         $profile->nickname .
+                         ' is missing one or more local avatars.');
+            common_debug($this->name() ." - old: $oldname new: $newname");
+
+            $this->updateAvatars($twitter_user, $profile);
+        }
+    }
+
+    function updateAvatars($twitter_user, $profile) {
+
+        global $config;
+
+        $path_parts = pathinfo($twitter_user->profile_image_url);
+
+        $ext = (isset($path_parts['extension']) ? '.'.$path_parts['extension'] : '');  // some lack extension
+        $img_root = basename($path_parts['basename'], '_normal'.$ext); // cut off extension
+        $mediatype = $this->getMediatype(substr($ext, 1));
+
+        foreach (array('mini', 'normal', 'bigger') as $size) {
+            $url = $path_parts['dirname'] . '/' .
+                $img_root . '_' . $size . $ext;
+            $filename = 'Twitter_' . $twitter_user->id . '_' .
+                $img_root . '_' . $size . $ext;
+
+            $this->updateAvatar($profile->id, $size, $mediatype, $filename);
+            $this->fetchAvatar($url, $filename);
+        }
+    }
+
+    function missingAvatarFile($profile) {
+        foreach (array(24, 48, 73) as $size) {
+            $filename = $profile->getAvatar($size)->filename;
+            $avatarpath = Avatar::path($filename);
+            if (file_exists($avatarpath) == FALSE) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    function getMediatype($ext)
+    {
+        $mediatype = null;
+
+        switch (strtolower($ext)) {
+        case 'jpeg':
+        case 'jpg':
+            $mediatype = 'image/jpeg';
+            break;
+        case 'gif':
+            $mediatype = 'image/gif';
+            break;
+        default:
+            $mediatype = 'image/png';
+        }
+
+        return $mediatype;
+    }
+
+    function saveAvatars($user, $id)
+    {
+        global $config;
+
+        $path_parts = pathinfo($user->profile_image_url);
+        $ext = (isset($path_parts['extension']) ? '.'.$path_parts['extension'] : '');
+        $img_root = basename($path_parts['basename'], '_normal'.$ext);
+        $mediatype = $this->getMediatype(substr($ext, 1));
+
+        foreach (array('mini', 'normal', 'bigger') as $size) {
+            $url = $path_parts['dirname'] . '/' .
+                $img_root . '_' . $size . $ext;
+            $filename = 'Twitter_' . $user->id . '_' .
+                $img_root . '_' . $size . $ext;
+
+            if ($this->fetchAvatar($url, $filename)) {
+                $this->newAvatar($id, $size, $mediatype, $filename);
+            } else {
+                common_log(LOG_WARNING, $id() .
+                           " - Problem fetching Avatar: $url");
+            }
+        }
+    }
+
+    function updateAvatar($profile_id, $size, $mediatype, $filename) {
+
+        common_debug($this->name() . " - Updating avatar: $size");
+
+        $profile = Profile::getKV($profile_id);
+
+        if (empty($profile)) {
+            common_debug($this->name() . " - Couldn't get profile: $profile_id!");
+            return;
+        }
+
+        $sizes = array('mini' => 24, 'normal' => 48, 'bigger' => 73);
+        $avatar = $profile->getAvatar($sizes[$size]);
+
+        // Delete the avatar, if present
+        if ($avatar) {
+            $avatar->delete();
+        }
+
+        $this->newAvatar($profile->id, $size, $mediatype, $filename);
+    }
+
+    function newAvatar($profile_id, $size, $mediatype, $filename)
+    {
+        global $config;
+
+        $avatar = new Avatar();
+        $avatar->profile_id = $profile_id;
+
+        switch($size) {
+        case 'mini':
+            $avatar->width  = 24;
+            $avatar->height = 24;
+            break;
+        case 'normal':
+            $avatar->width  = 48;
+            $avatar->height = 48;
+            break;
+        default:
+            // Note: Twitter's big avatars are a different size than
+            // StatusNet's (StatusNet's = 96)
+            $avatar->width  = 73;
+            $avatar->height = 73;
+        }
+
+        $avatar->original = 0; // we don't have the original
+        $avatar->mediatype = $mediatype;
+        $avatar->filename = $filename;
+        $avatar->url = Avatar::url($filename);
+
+        $avatar->created = common_sql_now();
+
+        try {
+            $id = $avatar->insert();
+        } catch (Exception $e) {
+            common_log(LOG_WARNING, $this->name() . ' Couldn\'t insert avatar - ' . $e->getMessage());
+        }
+
+        if (empty($id)) {
+            common_log_db_error($avatar, 'INSERT', __FILE__);
+            return null;
+        }
+
+        common_debug($this->name() .
+                     " - Saved new $size avatar for $profile_id.");
+
+        return $id;
+    }
+
+    /**
+     * Fetch a remote avatar image and save to local storage.
+     *
+     * @param string $url avatar source URL
+     * @param string $filename bare local filename for download
+     * @return bool true on success, false on failure
+     */
+    function fetchAvatar($url, $filename)
+    {
+        common_debug($this->name() . " - Fetching Twitter avatar: $url");
+
+        $request = HTTPClient::start();
+        $response = $request->get($url);
+        if ($response->isOk()) {
+            $avatarfile = Avatar::path($filename);
+            $ok = file_put_contents($avatarfile, $response->getBody());
+            if (!$ok) {
+                common_log(LOG_WARNING, $this->name() .
+                           " - Couldn't open file $filename");
+                return false;
+            }
+        } else {
+            return false;
+        }
+
+        return true;
+    }
+
+    const URL = 1;
+    const HASHTAG = 2;
+    const MENTION = 3;
+
+    function linkify($status, $html = FALSE)
+    {
+        $text = $status->text;
+
+        if (empty($status->entities)) {
+            $statusId = twitter_id($status);
+            common_log(LOG_WARNING, "No entities data for {$statusId}; trying to fake up links ourselves.");
+            $text = common_replace_urls_callback($text, 'common_linkify');
+            $text = preg_replace('/(^|\&quot\;|\'|\(|\[|\{|\s+)#([\pL\pN_\-\.]{1,64})/e', "'\\1#'.TwitterStatusFetcher::tagLink('\\2')", $text);
+            $text = preg_replace('/(^|\s+)@([a-z0-9A-Z_]{1,64})/e', "'\\1@'.TwitterStatusFetcher::atLink('\\2')", $text);
+            return $text;
+        }
+
+        // Move all the entities into order so we can
+        // replace them and escape surrounding plaintext
+        // in order
+
+        $toReplace = array();
+
+        if (!empty($status->entities->urls)) {
+            foreach ($status->entities->urls as $url) {
+                $toReplace[$url->indices[0]] = array(self::URL, $url);
+            }
+        }
+
+        if (!empty($status->entities->hashtags)) {
+            foreach ($status->entities->hashtags as $hashtag) {
+                $toReplace[$hashtag->indices[0]] = array(self::HASHTAG, $hashtag);
+            }
+        }
+
+        if (!empty($status->entities->user_mentions)) {
+            foreach ($status->entities->user_mentions as $mention) {
+                $toReplace[$mention->indices[0]] = array(self::MENTION, $mention);
+            }
+        }
+
+        // sort in forward order by key
+
+        ksort($toReplace);
+
+        $result = '';
+        $cursor = 0;
+
+        foreach ($toReplace as $part) {
+            list($type, $object) = $part;
+            $start = $object->indices[0];
+            $end = $object->indices[1];
+            if ($cursor < $start) {
+                // Copy in the preceding plaintext
+                $result .= $this->twitEscape(mb_substr($text, $cursor, $start - $cursor));
+                $cursor = $start;
+            }
+            $orig = $this->twitEscape(mb_substr($text, $start, $end - $start));
+            switch($type) {
+            case self::URL:
+                $linkText = $this->makeUrlLink($object, $orig, $html);
+                break;
+            case self::HASHTAG:
+                if ($html) {
+                    $linkText = $this->makeHashtagLink($object, $orig);
+                }else{
+                    $linkText = $orig;
+                }
+                break;
+            case self::MENTION:
+                if ($html) {
+                    $linkText = $this->makeMentionLink($object, $orig);
+                }else{
+                    $linkText = $orig;
+                }
+                break;
+            default:
+                $linkText = $orig;
+                continue;
+            }
+            $result .= $linkText;
+            $cursor = $end;
+        }
+        $last = $this->twitEscape(mb_substr($text, $cursor));
+        $result .= $last;
+
+        return $result;
+    }
+
+    function twitEscape($str)
+    {
+        // Twitter seems to preemptive turn < and > into &lt; and &gt;
+        // but doesn't for &, so while you may have some magic protection
+        // against XSS by not bothing to escape manually, you still get
+        // invalid XHTML. Thanks!
+        //
+        // Looks like their web interface pretty much sends anything
+        // through intact, so.... to do equivalent, decode all entities
+        // and then re-encode the special ones.
+        return htmlspecialchars(html_entity_decode($str, ENT_COMPAT, 'UTF-8'));
+    }
+
+    function makeUrlLink($object, $orig, $html)
+    {
+        if ($html) {
+            return '<a href="'.htmlspecialchars($object->expanded_url).'" class="extlink">'.htmlspecialchars($object->display_url).'</a>';
+        }else{
+            return htmlspecialchars($object->expanded_url);
+        }
+    }
+
+    function makeHashtagLink($object, $orig)
+    {
+        return "#" . self::tagLink($object->text, substr($orig, 1));
+    }
+
+    function makeMentionLink($object, $orig)
+    {
+        return "@".self::atLink($object->screen_name, $object->name, substr($orig, 1));
+    }
+
+    static function tagLink($tag, $orig)
+    {
+        return "<a href='https://search.twitter.com/search?q=%23{$tag}' class='hashtag'>{$orig}</a>";
+    }
+
+    static function atLink($screenName, $fullName, $orig)
+    {
+        if (!empty($fullName)) {
+            return "<a href='http://twitter.com/#!/{$screenName}' title='{$fullName}'>{$orig}</a>";
+        } else {
+            return "<a href='http://twitter.com/#!/{$screenName}'>{$orig}</a>";
+        }
+    }
+
+    function saveStatusMentions($notice, $status)
+    {
+        $mentions = array();
+
+        if (empty($status->entities) || empty($status->entities->user_mentions)) {
+            return;
+        }
+
+        foreach ($status->entities->user_mentions as $mention) {
+            $flink = Foreign_link::getByForeignID($mention->id, TWITTER_SERVICE);
+            if (!empty($flink)) {
+                $user = User::getKV('id', $flink->user_id);
+                if (!empty($user)) {
+                    $reply = new Reply();
+                    $reply->notice_id  = $notice->id;
+                    $reply->profile_id = $user->id;
+                    $reply->modified   = $notice->created;
+                    common_log(LOG_INFO, __METHOD__ . ": saving reply: notice {$notice->id} to profile {$user->id}");
+                    $id = $reply->insert();
+                }
+            }
+        }
+    }
+
+    /**
+     * Record URL links from the notice. Needed to get thumbnail records
+     * for referenced photo and video posts, etc.
+     *
+     * @param Notice $notice
+     * @param object $status
+     */
+    function saveStatusAttachments($notice, $status)
+    {
+        if (common_config('attachments', 'process_links')) {
+            if (!empty($status->entities) && !empty($status->entities->urls)) {
+                foreach ($status->entities->urls as $url) {
+                    File::processNew($url->url, $notice->id);
+                }
+            }
+        }
+    }
+}
diff --git a/plugins/TwitterBridge/lib/twitteroauthclient.php b/plugins/TwitterBridge/lib/twitteroauthclient.php
new file mode 100644 (file)
index 0000000..7208442
--- /dev/null
@@ -0,0 +1,370 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Class for doing OAuth calls against Twitter
+ *
+ * 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  Integration
+ * @package   StatusNet
+ * @author    Zach Copley <zach@status.net>
+ * @copyright 2009-2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Class for talking to the Twitter API with OAuth.
+ *
+ * @category Integration
+ * @package  StatusNet
+ * @author   Zach Copley <zach@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ *
+ */
+class TwitterOAuthClient extends OAuthClient
+{
+    public static $requestTokenURL = 'https://api.twitter.com/oauth/request_token';
+    public static $authorizeURL    = 'https://api.twitter.com/oauth/authorize';
+    public static $signinUrl       = 'https://api.twitter.com/oauth/authenticate';
+    public static $accessTokenURL  = 'https://api.twitter.com/oauth/access_token';
+
+    /**
+     * Constructor
+     *
+     * @param string $oauth_token        the user's token
+     * @param string $oauth_token_secret the user's token secret
+     *
+     * @return nothing
+     */
+    function __construct($oauth_token = null, $oauth_token_secret = null)
+    {
+        $consumer_key    = common_config('twitter', 'consumer_key');
+        $consumer_secret = common_config('twitter', 'consumer_secret');
+
+        if (empty($consumer_key) && empty($consumer_secret)) {
+            $consumer_key = common_config(
+                'twitter',
+                'global_consumer_key'
+            );
+            $consumer_secret = common_config(
+                'twitter',
+                'global_consumer_secret'
+            );
+        }
+
+        parent::__construct(
+            $consumer_key,
+            $consumer_secret,
+            $oauth_token,
+            $oauth_token_secret
+        );
+    }
+
+    // XXX: the following two functions are to support the horrible hack
+    // of using the credentils field in Foreign_link to store both
+    // the access token and token secret.  This hack should go away with
+    // 0.9, in which we can make DB changes and add a new column for the
+    // token itself.
+
+    static function packToken($token)
+    {
+        return implode(chr(0), array($token->key, $token->secret));
+    }
+
+    static function unpackToken($str)
+    {
+        $vals = explode(chr(0), $str);
+        return new OAuthToken($vals[0], $vals[1]);
+    }
+
+    static function isPackedToken($str)
+    {
+        if (strpos($str, chr(0)) === false) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Gets a request token from Twitter
+     *
+     * @return OAuthToken $token the request token
+     */
+    function getRequestToken()
+    {
+        return parent::getRequestToken(
+            self::$requestTokenURL,
+            common_local_url('twitterauthorization')
+        );
+    }
+
+    /**
+     * Builds a link to Twitter's endpoint for authorizing a request token
+     *
+     * @param OAuthToken $request_token token to authorize
+     *
+     * @return the link
+     */
+    function getAuthorizeLink($request_token, $signin = false)
+    {
+        $url = ($signin) ? self::$signinUrl : self::$authorizeURL;
+
+        return parent::getAuthorizeLink($url,
+                                        $request_token,
+                                        common_local_url('twitterauthorization'));
+    }
+
+    /**
+     * Fetches an access token from Twitter
+     *
+     * @param string $verifier 1.0a verifier
+     *
+     * @return OAuthToken $token the access token
+     */
+    function getAccessToken($verifier = null)
+    {
+        return parent::getAccessToken(
+            self::$accessTokenURL,
+            $verifier
+        );
+    }
+
+    /**
+     * Calls Twitter's /account/verify_credentials API method
+     *
+     * @return mixed the Twitter user
+     */
+    function verifyCredentials()
+    {
+        $url          = 'https://api.twitter.com/1.1/account/verify_credentials.json';
+        $response     = $this->oAuthGet($url);
+        $twitter_user = json_decode($response);
+        return $twitter_user;
+    }
+
+    /**
+     * Calls Twitter's /statuses/update API method
+     *
+     * @param string $status  text of the status
+     * @param mixed  $params  optional other parameters to pass to Twitter,
+     *                        as defined. For back-compatibility, if an int
+     *                        is passed we'll consider it a reply-to ID.
+     *
+     * @return mixed the status
+     */
+    function statusesUpdate($status, $params=array())
+    {
+        $url      = 'https://api.twitter.com/1.1/statuses/update.json';
+        if (is_numeric($params)) {
+            $params = array('in_reply_to_status_id' => intval($params));
+        }
+        $params['status'] = $status;
+        // We don't have to pass 'source' as the oauth key is tied to an app.
+
+        $response = $this->oAuthPost($url, $params);
+        $status   = json_decode($response);
+        return $status;
+    }
+
+    /**
+     * Calls Twitter's /statuses/home_timeline API method
+     *
+     * @param int    $since_id    show statuses after this id
+     * @param string $timelineUri timeline to poll statuses from
+     * @param int    $max_id      show statuses before this id
+     * @param int    $cnt         number of statuses to show
+     * @param int    $page        page number
+     *
+     * @return mixed an array of statuses
+     */
+    function statusesTimeline($since_id = null, $timelineUri = 'home_timeline',
+                              $max_id = null, $cnt = 200, $page = null)
+    {
+        $url    = 'https://api.twitter.com/1.1/statuses/'.$timelineUri.'.json';
+
+        $params = array('include_entities' => 'true',
+                        'include_rts'      => 'true');
+
+        if (!empty($since_id)) {
+            $params['since_id'] = $since_id;
+        }
+        if (!empty($max_id)) {
+            $params['max_id'] = $max_id;
+        }
+        if (!empty($cnt)) {
+            $params['count'] = $cnt;
+        }
+        if (!empty($page)) {
+            $params['page'] = $page;
+        }
+
+        $response = $this->oAuthGet($url, $params);
+        $statuses = json_decode($response);
+        return $statuses;
+    }
+
+    /**
+     * Calls Twitter's /statuses/friends API method
+     *
+     * @param int $id          id of the user whom you wish to see friends of
+     * @param int $user_id     numerical user id
+     * @param int $screen_name screen name
+     * @param int $page        page number
+     *
+     * @return mixed an array of twitter users and their latest status
+     */
+    function statusesFriends($id = null, $user_id = null, $screen_name = null,
+                             $page = null)
+    {
+        $url = "https://api.twitter.com/1.1/friends/list.json";
+
+        $params = array();
+
+        if (!empty($id)) {
+            $params['id'] = $id;
+        }
+
+        if (!empty($user_id)) {
+            $params['user_id'] = $user_id;
+        }
+
+        if (!empty($screen_name)) {
+            $params['screen_name'] = $screen_name;
+        }
+
+        if (!empty($page)) {
+            $params['page'] = $page;
+        }
+
+        $response = $this->oAuthGet($url, $params);
+        $friends  = json_decode($response);
+        return $friends;
+    }
+
+    /**
+     * Calls Twitter's /statuses/friends/ids API method
+     *
+     * @param int $id          id of the user whom you wish to see friends of
+     * @param int $user_id     numerical user id
+     * @param int $screen_name screen name
+     * @param int $page        page number
+     *
+     * @return mixed a list of ids, 100 per page
+     */
+    function friendsIds($id = null, $user_id = null, $screen_name = null,
+                         $page = null)
+    {
+        $url = "https://api.twitter.com/1.1/friends/ids.json";
+
+        $params = array();
+
+        if (!empty($id)) {
+            $params['id'] = $id;
+        }
+
+        if (!empty($user_id)) {
+            $params['user_id'] = $user_id;
+        }
+
+        if (!empty($screen_name)) {
+            $params['screen_name'] = $screen_name;
+        }
+
+        if (!empty($page)) {
+            $params['page'] = $page;
+        }
+
+        $response = $this->oAuthGet($url, $params);
+        $ids      = json_decode($response);
+        return $ids;
+    }
+
+    /**
+     * Calls Twitter's /statuses/retweet/id.json API method
+     *
+     * @param int $id id of the notice to retweet
+     *
+     * @return retweeted status
+     */
+
+    function statusesRetweet($id)
+    {
+        $url = "http://api.twitter.com/1.1/statuses/retweet/$id.json";
+        $response = $this->oAuthPost($url);
+        $status = json_decode($response);
+        return $status;
+    }
+
+    /**
+     * Calls Twitter's /favorites/create API method
+     *
+     * @param int $id ID of the status to favorite
+     *
+     * @return object faved status
+     */
+
+    function favoritesCreate($id)
+    {
+        $url = "http://api.twitter.com/1.1/favorites/create.json";
+        $params=array();
+        $params['id'] = $id;
+        $response = $this->oAuthPost($url, $params);
+        $status = json_decode($response);
+        return $status;
+    }
+
+    /**
+     * Calls Twitter's /favorites/destroy API method
+     *
+     * @param int $id ID of the status to unfavorite
+     *
+     * @return object unfaved status
+     */
+
+    function favoritesDestroy($id)
+    {
+        $url = "http://api.twitter.com/1.1/favorites/destroy.json";
+        $params=array();
+        $params['id'] = $id;
+        $response = $this->oAuthPost($url,$params);
+        $status = json_decode($response);
+        return $status;
+    }
+
+    /**
+     * Calls Twitter's /statuses/destroy API method
+     *
+     * @param int $id ID of the status to destroy
+     *
+     * @return object destroyed
+     */
+
+    function statusesDestroy($id)
+    {
+        $url = "http://api.twitter.com/1.1/statuses/destroy/$id.json";
+        $response = $this->oAuthPost($url);
+        $status = json_decode($response);
+        return $status;
+    }
+}
diff --git a/plugins/TwitterBridge/lib/twitterqueuehandler.php b/plugins/TwitterBridge/lib/twitterqueuehandler.php
new file mode 100644 (file)
index 0000000..644ce17
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
+
+class TwitterQueueHandler extends QueueHandler
+{
+    function transport()
+    {
+        return 'twitter';
+    }
+
+    function handle($notice)
+    {
+        $ok = broadcast_twitter($notice);
+        return $ok || common_config('twitter', 'ignore_errors');
+    }
+}
diff --git a/plugins/TwitterBridge/lib/twittersitestream.php b/plugins/TwitterBridge/lib/twittersitestream.php
new file mode 100644 (file)
index 0000000..2f11f0a
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+/**
+ * Multiuser stream listener for Twitter Site Streams API
+ * http://dev.twitter.com/pages/site_streams
+ *
+ * The site streams API allows listening to updates for multiple users.
+ * Pass in the user IDs to listen to in via followUser() -- note they
+ * must each have a valid OAuth token for the application ID we're
+ * connecting as.
+ *
+ * You'll need to be connecting with the auth keys for the user who
+ * owns the application registration.
+ *
+ * The user each message is destined for will be passed to event handlers
+ * in $context['for_user_id'].
+ */
+class TwitterSiteStream extends TwitterStreamReader
+{
+    protected $userIds;
+
+    public function __construct(TwitterOAuthClient $auth, $baseUrl='https://sitestream.twitter.com')
+    {
+        parent::__construct($auth, $baseUrl);
+    }
+
+    public function connect($method='2b/site.json')
+    {
+        $params = array();
+        if ($this->userIds) {
+            $params['follow'] = implode(',', $this->userIds);
+        }
+        return parent::connect($method, $params);
+    }
+
+    /**
+     * Set the users whose home streams should be pulled.
+     * They all must have valid oauth tokens for this application.
+     *
+     * Must be called before connect().
+     *
+     * @param array $userIds
+     */
+    function followUsers($userIds)
+    {
+        $this->userIds = $userIds;
+    }
+
+    /**
+     * Each message in the site stream tells us which user ID it should be
+     * routed to; we'll need that to let the caller know what to do.
+     *
+     * @param array $data
+     */
+    function routeMessage(stdClass $data)
+    {
+        $context = array(
+            'source' => 'sitestream',
+            'for_user' => $data->for_user
+        );
+        parent::handleMessage($data->message, $context);
+    }
+}
diff --git a/plugins/TwitterBridge/lib/twitterstreamreader.php b/plugins/TwitterBridge/lib/twitterstreamreader.php
new file mode 100644 (file)
index 0000000..2441b13
--- /dev/null
@@ -0,0 +1,285 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+/**
+ * Base class for reading Twitter's User Streams and Site Streams
+ * real-time streaming APIs.
+ *
+ * Caller can hook event callbacks for various types of messages;
+ * the data from the stream and some context info will be passed
+ * on to the callbacks.
+ */
+abstract class TwitterStreamReader extends JsonStreamReader
+{
+    protected $callbacks = array();
+
+    function __construct(TwitterOAuthClient $auth, $baseUrl)
+    {
+        $this->baseUrl = $baseUrl;
+        $this->oauth = $auth;
+    }
+
+    public function connect($method, $params=array())
+    {
+        $url = $this->oAuthUrl($this->baseUrl . '/' . $method, $params);
+        return parent::connect($url);
+    }
+
+    /**
+     * Sign our target URL with OAuth auth stuff.
+     *
+     * @param string $url
+     * @param array $params
+     * @return string
+     */
+    protected function oAuthUrl($url, $params=array())
+    {
+        // In an ideal world this would be better encapsulated. :)
+        $request = OAuthRequest::from_consumer_and_token($this->oauth->consumer,
+            $this->oauth->token, 'GET', $url, $params);
+        $request->sign_request($this->oauth->sha1_method,
+            $this->oauth->consumer, $this->oauth->token);
+
+        return $request->to_url();
+    }
+
+    /**
+     * Add an event callback to receive notifications when things come in
+     * over the wire.
+     *
+     * Callbacks should be in the form: function(object $data, array $context)
+     * where $context may list additional data on some streams, such as the
+     * user to whom the message should be routed.
+     *
+     * Available events:
+     *
+     * Messaging:
+     *
+     * 'status': $data contains a status update in standard Twitter JSON format.
+     *      $data->user: sending user in standard Twitter JSON format.
+     *      $data->text... etc
+     *
+     * 'direct_message': $data contains a direct message in standard Twitter JSON format.
+     *      $data->sender: sending user in standard Twitter JSON format.
+     *      $data->recipient: receiving user in standard Twitter JSON format.
+     *      $data->text... etc
+     *
+     *
+     * Out of band events:
+     *
+     * 'follow': User has either started following someone, or is being followed.
+     *      $data->source: following user in standard Twitter JSON format.
+     *      $data->target: followed user in standard Twitter JSON format.
+     *
+     * 'favorite': Someone has favorited a status update.
+     *      $data->source: user doing the favoriting, in standard Twitter JSON format.
+     *      $data->target: user whose status was favorited, in standard Twitter JSON format.
+     *      $data->target_object: the favorited status update in standard Twitter JSON format.
+     *
+     * 'unfavorite': Someone has unfavorited a status update.
+     *      $data->source: user doing the unfavoriting, in standard Twitter JSON format.
+     *      $data->target: user whose status was unfavorited, in standard Twitter JSON format.
+     *      $data->target_object: the unfavorited status update in standard Twitter JSON format.
+     *
+     *
+     * Meta information:
+     *
+     * 'friends':
+     *      $data->friends: array of user IDs of the current user's friends.
+     *
+     * 'delete': Advisory that a Twitter status has been deleted; nice clients
+     *           should follow suit.
+     *      $data->id: ID of status being deleted
+     *      $data->user_id: ID of its owning user
+     *
+     * 'scrub_geo': Advisory that a user is clearing geo data from their status
+     *              stream; nice clients should follow suit.
+     *      $data->user_id: ID of user
+     *      $data->up_to_status_id: any notice older than this should be scrubbed.
+     *
+     * 'limit': Advisory that tracking has hit a resource limit.
+     *      $data->track
+     *
+     * 'raw': receives the full JSON data for all message types.
+     *
+     * @param string $event
+     * @param callable $callback
+     */
+    public function hookEvent($event, $callback)
+    {
+        $this->callbacks[$event][] = $callback;
+    }
+
+    /**
+     * Call event handler callbacks for the given event.
+     *
+     * @param string $event
+     * @param mixed $arg1 ... one or more params to pass on
+     */
+    protected function fireEvent($event, $arg1)
+    {
+        if (array_key_exists($event, $this->callbacks)) {
+            $args = array_slice(func_get_args(), 1);
+            foreach ($this->callbacks[$event] as $callback) {
+                call_user_func_array($callback, $args);
+            }
+        }
+    }
+
+    protected function handleJson(stdClass $data)
+    {
+        $this->routeMessage($data);
+    }
+
+    abstract protected function routeMessage(stdClass $data);
+
+    /**
+     * Send the decoded JSON object out to any event listeners.
+     *
+     * @param array $data
+     * @param array $context optional additional context data to pass on
+     */
+    protected function handleMessage(stdClass $data, array $context=array())
+    {
+        $this->fireEvent('raw', $data, $context);
+
+        if (isset($data->text)) {
+            $this->fireEvent('status', $data, $context);
+            return;
+        }
+        if (isset($data->event)) {
+            $this->fireEvent($data->event, $data, $context);
+            return;
+        }
+        if (isset($data->friends)) {
+            $this->fireEvent('friends', $data, $context);
+        }
+
+        $knownMeta = array('delete', 'scrub_geo', 'limit', 'direct_message');
+        foreach ($knownMeta as $key) {
+            if (isset($data->$key)) {
+                $this->fireEvent($key, $data->$key, $context);
+                return;
+            }
+        }
+    }
+}
+
+/**
+ * Multiuser stream listener for Twitter Site Streams API
+ * http://dev.twitter.com/pages/site_streams
+ *
+ * The site streams API allows listening to updates for multiple users.
+ * Pass in the user IDs to listen to in via followUser() -- note they
+ * must each have a valid OAuth token for the application ID we're
+ * connecting as.
+ *
+ * You'll need to be connecting with the auth keys for the user who
+ * owns the application registration.
+ *
+ * The user each message is destined for will be passed to event handlers
+ * in $context['for_user_id'].
+ */
+class TwitterSiteStream extends TwitterStreamReader
+{
+    protected $userIds;
+
+    public function __construct(TwitterOAuthClient $auth, $baseUrl='https://sitestream.twitter.com')
+    {
+        parent::__construct($auth, $baseUrl);
+    }
+
+    public function connect($method='2b/site.json')
+    {
+        $params = array();
+        if ($this->userIds) {
+            $params['follow'] = implode(',', $this->userIds);
+        }
+        return parent::connect($method, $params);
+    }
+
+    /**
+     * Set the users whose home streams should be pulled.
+     * They all must have valid oauth tokens for this application.
+     *
+     * Must be called before connect().
+     *
+     * @param array $userIds
+     */
+    function followUsers($userIds)
+    {
+        $this->userIds = $userIds;
+    }
+
+    /**
+     * Each message in the site stream tells us which user ID it should be
+     * routed to; we'll need that to let the caller know what to do.
+     *
+     * @param array $data
+     */
+    function routeMessage(stdClass $data)
+    {
+        $context = array(
+            'source' => 'sitestream',
+            'for_user' => $data->for_user
+        );
+        parent::handleMessage($data->message, $context);
+    }
+}
+
+/**
+ * Stream listener for Twitter User Streams API
+ * http://dev.twitter.com/pages/user_streams
+ *
+ * This will pull the home stream and additional events just for the user
+ * we've authenticated as.
+ */
+class TwitterUserStream extends TwitterStreamReader
+{
+    public function __construct(TwitterOAuthClient $auth, $baseUrl='https://userstream.twitter.com')
+    {
+        parent::__construct($auth, $baseUrl);
+    }
+
+    public function connect($method='2/user.json')
+    {
+        return parent::connect($method);
+    }
+
+    /**
+     * Each message in the user stream is just ready to go.
+     *
+     * @param array $data
+     */
+    function routeMessage(stdClass $data)
+    {
+        $context = array(
+            'source' => 'userstream'
+        );
+        parent::handleMessage($data, $context);
+    }
+}
diff --git a/plugins/TwitterBridge/lib/twitteruserstream.php b/plugins/TwitterBridge/lib/twitteruserstream.php
new file mode 100644 (file)
index 0000000..ad4094d
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Plugin
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+/**
+ * Stream listener for Twitter User Streams API
+ * http://dev.twitter.com/pages/user_streams
+ *
+ * This will pull the home stream and additional events just for the user
+ * we've authenticated as.
+ */
+class TwitterUserStream extends TwitterStreamReader
+{
+    public function __construct(TwitterOAuthClient $auth, $baseUrl='https://userstream.twitter.com')
+    {
+        parent::__construct($auth, $baseUrl);
+    }
+
+    public function connect($method='2/user.json')
+    {
+        return parent::connect($method);
+    }
+
+    /**
+     * Each message in the user stream is just ready to go.
+     *
+     * @param array $data
+     */
+    function routeMessage(stdClass $data)
+    {
+        $context = array(
+            'source' => 'userstream'
+        );
+        parent::handleMessage($data, $context);
+    }
+}
index e0c48efe57c1c4a292f69a37432a4638c5c304f6..800d8c1e1cef9043fe5cca1de83924ac9c153281 100644 (file)
@@ -44,8 +44,8 @@ data as it comes.
 ENDOFHELP;
 
 require_once INSTALLDIR.'/scripts/commandline.inc';
-require_once dirname(dirname(__FILE__)) . '/jsonstreamreader.php';
-require_once dirname(dirname(__FILE__)) . '/twitterstreamreader.php';
+require_once dirname(dirname(__FILE__)) . '/lib/jsonstreamreader.php';
+require_once dirname(dirname(__FILE__)) . '/lib/twitterstreamreader.php';
 
 if (have_option('n')) {
     $nickname = get_option_value('n');
diff --git a/plugins/TwitterBridge/tweetinqueuehandler.php b/plugins/TwitterBridge/tweetinqueuehandler.php
deleted file mode 100644 (file)
index 7f34ade..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
-
-/**
- * Queue handler to deal with incoming Twitter status updates, as retrieved by
- * TwitterDaemon (twitterdaemon.php).
- *
- * The queue handler passes the status through TwitterImporter for import into the
- * local database (if necessary), then adds the imported notice to the local inbox
- * of the attached Twitter user.
- *
- * Warning: the way we do inbox distribution manually means that realtime, XMPP, etc
- * don't work on Twitter-borne messages. When TwitterImporter is changed to handle
- * that correctly, we'll only need to do this once...?
- */
-class TweetInQueueHandler extends QueueHandler
-{
-    function transport()
-    {
-        return 'tweetin';
-    }
-
-    function handle($data)
-    {
-        // JSON object with Twitter data
-        $status = $data['status'];
-
-        // Twitter user ID this incoming data belongs to.
-        $receiver = $data['for_user'];
-
-        $importer = new TwitterImport();
-        $notice = $importer->importStatus($status);
-        if ($notice) {
-            $flink = Foreign_link::getByForeignID($receiver, TWITTER_SERVICE);
-            if ($flink) {
-                common_log(LOG_DEBUG, "TweetInQueueHandler - Got flink so add notice ".
-                           $notice->id." to inbox ".$flink->user_id);
-                // @fixme this should go through more regular channels?
-                Inbox::insertNotice($flink->user_id, $notice->id);
-            }else {
-               common_log(LOG_DEBUG, "TweetInQueueHandler - No flink found for foreign user ".$receiver);
-            }
-        }
-
-        return true;
-    }
-}
diff --git a/plugins/TwitterBridge/twitteradminpanel.php b/plugins/TwitterBridge/twitteradminpanel.php
deleted file mode 100644 (file)
index 9ace4e4..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Twitter bridge administration panel
- *
- * 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  Settings
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Administer global Twitter bridge settings
- *
- * @category Admin
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class TwitteradminpanelAction extends AdminPanelAction
-{
-    /**
-     * Returns the page title
-     *
-     * @return string page title
-     */
-    function title()
-    {
-        // TRANS: Page title for Twitter administration panel.
-        return _m('TITLE','Twitter');
-    }
-
-    /**
-     * Instructions for using this form.
-     *
-     * @return string instructions
-     */
-    function getInstructions()
-    {
-        // TRANS: Instructions for Twitter bridge administration page.
-        return _m('Twitter bridge settings');
-    }
-
-    /**
-     * Show the Twitter admin panel form
-     *
-     * @return void
-     */
-    function showForm()
-    {
-        $form = new TwitterAdminPanelForm($this);
-        $form->show();
-        return;
-    }
-
-    /**
-     * Save settings from the form
-     *
-     * @return void
-     */
-    function saveSettings()
-    {
-        static $settings = array(
-            'twitter'     => array('consumer_key', 'consumer_secret'),
-            'integration' => array('source')
-        );
-
-        static $booleans = array(
-            'twitter'       => array('signin')
-        );
-        if (Event::handle('TwitterBridgeAdminImportControl')) {
-            $booleans['twitterimport'] = array('enabled');
-        }
-
-        $values = array();
-
-        foreach ($settings as $section => $parts) {
-            foreach ($parts as $setting) {
-                $values[$section][$setting]
-                    = $this->trimmed($setting);
-            }
-        }
-
-        foreach ($booleans as $section => $parts) {
-            foreach ($parts as $setting) {
-                $values[$section][$setting]
-                    = ($this->boolean($setting)) ? 1 : 0;
-            }
-        }
-
-        // This throws an exception on validation errors
-
-        $this->validate($values);
-
-        // assert(all values are valid);
-
-        $config = new Config();
-
-        $config->query('BEGIN');
-
-        foreach ($settings as $section => $parts) {
-            foreach ($parts as $setting) {
-                Config::save($section, $setting, $values[$section][$setting]);
-            }
-        }
-
-        foreach ($booleans as $section => $parts) {
-            foreach ($parts as $setting) {
-                Config::save($section, $setting, $values[$section][$setting]);
-            }
-        }
-
-        $config->query('COMMIT');
-
-        // Flush the router cache: we may have enabled/disabled bridging,
-        // which will add or remove some actions.
-        $cache = Cache::instance();
-        $cache->delete(Router::cacheKey());
-
-        return;
-    }
-
-    function validate(&$values)
-    {
-        // Validate consumer key and secret (can't be too long)
-
-        if (mb_strlen($values['twitter']['consumer_key']) > 255) {
-            $this->clientError(
-                // TRANS: Client error displayed when a consumer key is invalid because it is too long.
-                _m('Invalid consumer key. Maximum length is 255 characters.')
-            );
-        }
-
-        if (mb_strlen($values['twitter']['consumer_secret']) > 255) {
-            $this->clientError(
-                // TRANS: Client error displayed when a consumer secret is invalid because it is too long.
-                _m('Invalid consumer secret. Maximum length is 255 characters.')
-            );
-        }
-    }
-
-    function isImportEnabled()
-    {
-        // Since daemon setup isn't automated yet...
-        // @todo: if merged into main queues, detect presence of daemon config
-        return true;
-    }
-}
-
-class TwitterAdminPanelForm extends AdminForm
-{
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-    function id()
-    {
-        return 'twitteradminpanel';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_settings';
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('twitteradminpanel');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->elementStart(
-            'fieldset',
-            array('id' => 'settings_twitter-application')
-        );
-        // TRANS: Fieldset legend for Twitter application settings.
-        $this->out->element('legend', null, _m('Twitter application settings'));
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-        $this->input(
-            'consumer_key',
-            // TRANS: Field label for Twitter assigned consumer key.
-            _m('Consumer key'),
-            // TRANS: Field title for Twitter assigned consumer key.
-            _m('The consumer key assigned by Twitter.'),
-            'twitter'
-        );
-        $this->unli();
-
-        $this->li();
-        $this->input(
-            'consumer_secret',
-            // TRANS: Field label for Twitter assigned consumer secret.
-            _m('Consumer secret'),
-            // TRANS: Field title for Twitter assigned consumer secret.
-            _m('The consumer secret assigned by Twitter.'),
-            'twitter'
-        );
-        $this->unli();
-
-        $globalConsumerKey = common_config('twitter', 'global_consumer_key');
-        $globalConsumerSec = common_config('twitter', 'global_consumer_secret');
-
-        if (!empty($globalConsumerKey) && !empty($globalConsumerSec)) {
-            $this->li();
-            // TRANS: Form guide displayed when two required fields have already been provided.
-            $this->out->element('p', 'form_guide', _m('Note: A global consumer key and secret are set.'));
-            $this->unli();
-        }
-
-        $this->li();
-        $this->input(
-            'source',
-            // TRANS: Field label for Twitter application name.
-            _m('Integration source'),
-            // TRANS: Field title for Twitter application name.
-            _m('The name of your Twitter application.'),
-            'integration'
-        );
-        $this->unli();
-
-        $this->out->elementEnd('ul');
-        $this->out->elementEnd('fieldset');
-
-        $this->out->elementStart(
-            'fieldset',
-            array('id' => 'settings_twitter-options')
-        );
-        // TRANS: Fieldset legend for Twitter integration options.
-        $this->out->element('legend', null, _m('Options'));
-
-        $this->out->elementStart('ul', 'form_data');
-
-        $this->li();
-
-        $this->out->checkbox(
-            // TRANS: Checkbox label for global setting.
-            'signin', _m('Enable "Sign-in with Twitter"'),
-            (bool) $this->value('signin', 'twitter'),
-            // TRANS: Checkbox title.
-            _m('This allow users to login with their Twitter credentials.')
-        );
-        $this->unli();
-
-        if (Event::handle('TwitterBridgeAdminImportControl')) {
-            $this->li();
-            $this->out->checkbox(
-                // TRANS: Checkbox label for global setting.
-                'enabled', _m('Enable Twitter import'),
-                (bool) $this->value('enabled', 'twitterimport'),
-                // TRANS: Checkbox title for global setting.
-                _m('Allow users to import their Twitter friends\' timelines. Requires daemons to be manually configured.')
-            );
-            $this->unli();
-        }
-
-        $this->out->elementEnd('ul');
-
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-    function formActions()
-    {
-        // TRANS: Button text for saving the administrative Twitter bridge settings.
-        $this->out->submit('submit', _m('BUTTON','Save'), 'submit', null,
-        // TRANS: Button title for saving the administrative Twitter bridge settings.
-        _m('Save the Twitter bridge settings.'));
-    }
-}
diff --git a/plugins/TwitterBridge/twitterauthorization.php b/plugins/TwitterBridge/twitterauthorization.php
deleted file mode 100644 (file)
index 6a86c81..0000000
+++ /dev/null
@@ -1,723 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Class for doing OAuth authentication against Twitter
- *
- * 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  Plugin
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @author    Julien C <chaumond@gmail.com>
- * @copyright 2009-2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
-
-/**
- * Class for doing OAuth authentication against Twitter
- *
- * Peforms the OAuth "dance" between StatusNet and Twitter -- requests a token,
- * authorizes it, and exchanges it for an access token.  It also creates a link
- * (Foreign_link) between the StatusNet user and Twitter user and stores the
- * access token and secret in the link.
- *
- * @category Plugin
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @author   Julien C <chaumond@gmail.com>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- */
-class TwitterauthorizationAction extends Action
-{
-    var $twuid        = null;
-    var $tw_fields    = null;
-    var $access_token = null;
-    var $signin       = null;
-    var $verifier     = null;
-
-    /**
-     * Initialize class members. Looks for 'oauth_token' parameter.
-     *
-     * @param array $args misc. arguments
-     *
-     * @return boolean true
-     */
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $this->signin      = $this->boolean('signin');
-        $this->oauth_token = $this->arg('oauth_token');
-        $this->verifier    = $this->arg('oauth_verifier');
-
-        return true;
-    }
-
-    /**
-     * Handler method
-     *
-     * @param array $args is ignored since it's now passed in in prepare()
-     *
-     * @return nothing
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-
-        if (common_logged_in()) {
-            $user  = common_current_user();
-            $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
-
-            // If there's already a foreign link record and a foreign user
-            // it means the accounts are already linked, and this is unecessary.
-            // So go back.
-
-            if (isset($flink)) {
-                $fuser = $flink->getForeignUser();
-                if (!empty($fuser)) {
-                    common_redirect(common_local_url('twittersettings'));
-                }
-            }
-        }
-
-        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-
-            // User was not logged in to StatusNet before
-
-            $this->twuid = $this->trimmed('twuid');
-
-            $this->tw_fields = array('screen_name' => $this->trimmed('tw_fields_screen_name'),
-                                     'fullname' => $this->trimmed('tw_fields_fullname'));
-
-            $this->access_token = new OAuthToken($this->trimmed('access_token_key'), $this->trimmed('access_token_secret'));
-
-            $token = $this->trimmed('token');
-
-            if (!$token || $token != common_session_token()) {
-                // TRANS: Client error displayed when the session token does not match or is not given.
-                $this->showForm(_m('There was a problem with your session token. Try again, please.'));
-                return;
-            }
-
-            if ($this->arg('create')) {
-                if (!$this->boolean('license')) {
-                    // TRANS: Form validation error displayed when the checkbox to agree to the license has not been checked.
-                    $this->showForm(_m('You cannot register if you do not agree to the license.'),
-                                    $this->trimmed('newname'));
-                    return;
-                }
-                $this->createNewUser();
-            } else if ($this->arg('connect')) {
-                $this->connectNewUser();
-            } else {
-                common_debug('Twitter bridge - ' . print_r($this->args, true));
-                // TRANS: Form validation error displayed when an unhandled error occurs.
-                $this->showForm(_m('Something weird happened.'),
-                                $this->trimmed('newname'));
-            }
-        } else {
-            // $this->oauth_token is only populated once Twitter authorizes our
-            // request token. If it's empty we're at the beginning of the auth
-            // process
-
-            if (empty($this->oauth_token)) {
-                $this->authorizeRequestToken();
-            } else {
-                $this->saveAccessToken();
-            }
-        }
-    }
-
-    /**
-     * Asks Twitter for a request token, and then redirects to Twitter
-     * to authorize it.
-     *
-     * @return nothing
-     */
-    function authorizeRequestToken()
-    {
-        try {
-            // Get a new request token and authorize it
-
-            $client  = new TwitterOAuthClient();
-            $req_tok = $client->getRequestToken();
-
-            // Sock the request token away in the session temporarily
-
-            $_SESSION['twitter_request_token']        = $req_tok->key;
-            $_SESSION['twitter_request_token_secret'] = $req_tok->secret;
-
-            $auth_link = $client->getAuthorizeLink($req_tok, $this->signin);
-        } catch (OAuthClientException $e) {
-            $msg = sprintf(
-                'OAuth client error - code: %1s, msg: %2s',
-                $e->getCode(),
-                $e->getMessage()
-            );
-            common_log(LOG_INFO, 'Twitter bridge - ' . $msg);
-            $this->serverError(
-                // TRANS: Server error displayed when linking to a Twitter account fails.
-                _m('Could not link your Twitter account.')
-            );
-        }
-
-        common_redirect($auth_link);
-    }
-
-    /**
-     * Called when Twitter returns an authorized request token. Exchanges
-     * it for an access token and stores it.
-     *
-     * @return nothing
-     */
-    function saveAccessToken()
-    {
-        // Check to make sure Twitter returned the same request
-        // token we sent them
-
-        if ($_SESSION['twitter_request_token'] != $this->oauth_token) {
-            $this->serverError(
-                // TRANS: Server error displayed when linking to a Twitter account fails because of an incorrect oauth_token.
-                _m('Could not link your Twitter account: oauth_token mismatch.')
-            );
-        }
-
-        $twitter_user = null;
-
-        try {
-
-            $client = new TwitterOAuthClient($_SESSION['twitter_request_token'],
-                $_SESSION['twitter_request_token_secret']);
-
-            // Exchange the request token for an access token
-
-            $atok = $client->getAccessToken($this->verifier);
-
-            // Test the access token and get the user's Twitter info
-
-            $client       = new TwitterOAuthClient($atok->key, $atok->secret);
-            $twitter_user = $client->verifyCredentials();
-
-        } catch (OAuthClientException $e) {
-            $msg = sprintf(
-                'OAuth client error - code: %1$s, msg: %2$s',
-                $e->getCode(),
-                $e->getMessage()
-            );
-            common_log(LOG_INFO, 'Twitter bridge - ' . $msg);
-            $this->serverError(
-                // TRANS: Server error displayed when linking to a Twitter account fails.
-                _m('Could not link your Twitter account.')
-            );
-        }
-
-        if (common_logged_in()) {
-            // Save the access token and Twitter user info
-
-            $user = common_current_user();
-            $this->saveForeignLink($user->id, $twitter_user->id, $atok);
-            save_twitter_user($twitter_user->id, $twitter_user->screen_name);
-
-        } else {
-
-            $this->twuid = $twitter_user->id;
-            $this->tw_fields = array("screen_name" => $twitter_user->screen_name,
-                                     "fullname" => $twitter_user->name);
-            $this->access_token = $atok;
-            $this->tryLogin();
-        }
-
-        // Clean up the the mess we made in the session
-
-        unset($_SESSION['twitter_request_token']);
-        unset($_SESSION['twitter_request_token_secret']);
-
-        if (common_logged_in()) {
-            common_redirect(common_local_url('twittersettings'));
-        }
-    }
-
-    /**
-     * Saves a Foreign_link between Twitter user and local user,
-     * which includes the access token and secret.
-     *
-     * @param int        $user_id StatusNet user ID
-     * @param int        $twuid   Twitter user ID
-     * @param OAuthToken $token   the access token to save
-     *
-     * @return nothing
-     */
-    function saveForeignLink($user_id, $twuid, $access_token)
-    {
-        $flink = new Foreign_link();
-
-        $flink->user_id = $user_id;
-        $flink->service = TWITTER_SERVICE;
-
-        // delete stale flink, if any
-        $result = $flink->find(true);
-
-        if (!empty($result)) {
-            $flink->safeDelete();
-        }
-
-        $flink->user_id     = $user_id;
-        $flink->foreign_id  = $twuid;
-        $flink->service     = TWITTER_SERVICE;
-
-        $creds = TwitterOAuthClient::packToken($access_token);
-
-        $flink->credentials = $creds;
-        $flink->created     = common_sql_now();
-
-        // Defaults: noticesync on, everything else off
-
-        $flink->set_flags(true, false, false, false);
-
-        $flink_id = $flink->insert();
-
-        if (empty($flink_id)) {
-            common_log_db_error($flink, 'INSERT', __FILE__);
-            // TRANS: Server error displayed when linking to a Twitter account fails.
-            $this->serverError(_m('Could not link your Twitter account.'));
-        }
-
-        return $flink_id;
-    }
-
-    function showPageNotice()
-    {
-        if ($this->error) {
-            $this->element('div', array('class' => 'error'), $this->error);
-        } else {
-            $this->element('div', 'instructions',
-                           // TRANS: Page instruction. %s is the StatusNet sitename.
-                           sprintf(_m('This is the first time you have logged into %s so we must connect your Twitter account to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
-        }
-    }
-
-    function title()
-    {
-        // TRANS: Page title.
-        return _m('Twitter Account Setup');
-    }
-
-    function showForm($error=null, $username=null)
-    {
-        $this->error = $error;
-        $this->username = $username;
-
-        $this->showPage();
-    }
-
-    function showPage()
-    {
-        parent::showPage();
-    }
-
-    /**
-     * @fixme much of this duplicates core code, which is very fragile.
-     * Should probably be replaced with an extensible mini version of
-     * the core registration form.
-     */
-    function showContent()
-    {
-        if (!empty($this->message_text)) {
-            $this->element('p', null, $this->message);
-            return;
-        }
-
-        $this->elementStart('form', array('method' => 'post',
-                                          'id' => 'form_settings_twitter_connect',
-                                          'class' => 'form_settings',
-                                          'action' => common_local_url('twitterauthorization')));
-        $this->elementStart('fieldset', array('id' => 'settings_twitter_connect_options'));
-        // TRANS: Fieldset legend.
-        $this->element('legend', null, _m('Connection options'));
-
-        $this->hidden('access_token_key', $this->access_token->key);
-        $this->hidden('access_token_secret', $this->access_token->secret);
-        $this->hidden('twuid', $this->twuid);
-        $this->hidden('tw_fields_screen_name', $this->tw_fields['screen_name']);
-        $this->hidden('tw_fields_name', $this->tw_fields['fullname']);
-        $this->hidden('token', common_session_token());
-
-        // Don't allow new account creation if site is flagged as invite only
-       if (common_config('site', 'inviteonly') == false) {
-            $this->elementStart('fieldset');
-            $this->element('legend', null,
-                           // TRANS: Fieldset legend.
-                           _m('Create new account'));
-            $this->element('p', null,
-                           // TRANS: Sub form introduction text.
-                          _m('Create a new user with this nickname.'));
-            $this->elementStart('ul', 'form_data');
-
-            // Hook point for captcha etc
-            Event::handle('StartRegistrationFormData', array($this));
-
-            $this->elementStart('li');
-            // TRANS: Field label.
-            $this->input('newname', _m('New nickname'),
-                         ($this->username) ? $this->username : '',
-                         // TRANS: Field title for nickname field.
-                         _m('1-64 lowercase letters or numbers, no punctuation or spaces.'));
-            $this->elementEnd('li');
-            $this->elementStart('li');
-            // TRANS: Field label.
-            $this->input('email', _m('LABEL','Email'), $this->getEmail(),
-                         // TRANS: Field title for e-mail address field.
-                         _m('Used only for updates, announcements, '.
-                           'and password recovery'));
-            $this->elementEnd('li');
-
-            // Hook point for captcha etc
-            Event::handle('EndRegistrationFormData', array($this));
-
-            $this->elementEnd('ul');
-            // TRANS: Button text for creating a new StatusNet account in the Twitter connect page.
-            $this->submit('create', _m('BUTTON','Create'));
-            $this->elementEnd('fieldset');
-        }
-
-        $this->elementStart('fieldset');
-        $this->element('legend', null,
-                       // TRANS: Fieldset legend.
-                       _m('Connect existing account'));
-        $this->element('p', null,
-                       // TRANS: Sub form introduction text.
-                       _m('If you already have an account, login with your username and password to connect it to your Twitter account.'));
-        $this->elementStart('ul', 'form_data');
-        $this->elementStart('li');
-        // TRANS: Field label.
-        $this->input('nickname', _m('Existing nickname'));
-        $this->elementEnd('li');
-        $this->elementStart('li');
-        // TRANS: Field label.
-        $this->password('password', _m('Password'));
-        $this->elementEnd('li');
-        $this->elementEnd('ul');
-        $this->elementEnd('fieldset');
-
-        $this->elementStart('fieldset');
-        $this->element('legend', null,
-                       // TRANS: Fieldset legend.
-                       _m('License'));
-        $this->elementStart('ul', 'form_data');
-        $this->elementStart('li');
-        $this->element('input', array('type' => 'checkbox',
-                                      'id' => 'license',
-                                      'class' => 'checkbox',
-                                      'name' => 'license',
-                                      'value' => 'true'));
-        $this->elementStart('label', array('class' => 'checkbox', 'for' => 'license'));
-        // TRANS: Text for license agreement checkbox.
-        // TRANS: %s is the license as configured for the StatusNet site.
-        $message = _m('My text and files are available under %s ' .
-                     'except this private data: password, ' .
-                     'email address, IM address, and phone number.');
-        $link = '<a href="' .
-                htmlspecialchars(common_config('license', 'url')) .
-                '">' .
-                htmlspecialchars(common_config('license', 'title')) .
-                '</a>';
-        $this->raw(sprintf(htmlspecialchars($message), $link));
-        $this->elementEnd('label');
-        $this->elementEnd('li');
-        $this->elementEnd('ul');
-        $this->elementEnd('fieldset');
-        // TRANS: Button text for connecting an existing StatusNet account in the Twitter connect page..
-        $this->submit('connect', _m('BUTTON','Connect'));
-        $this->elementEnd('fieldset');
-        $this->elementEnd('form');
-    }
-
-    /**
-     * Get specified e-mail from the form, or the invite code.
-     *
-     * @return string
-     */
-    function getEmail()
-    {
-        $email = $this->trimmed('email');
-        if (!empty($email)) {
-            return $email;
-        }
-
-        // Terrible hack for invites...
-        if (common_config('site', 'inviteonly')) {
-            $code = $_SESSION['invitecode'];
-            if ($code) {
-                $invite = Invitation::getKV($code);
-
-                if ($invite && $invite->address_type == 'email') {
-                    return $invite->address;
-                }
-            }
-        }
-        return '';
-    }
-
-    function message($msg)
-    {
-        $this->message_text = $msg;
-        $this->showPage();
-    }
-
-    function createNewUser()
-    {
-        if (!Event::handle('StartRegistrationTry', array($this))) {
-            return;
-        }
-
-        if (common_config('site', 'closed')) {
-            // TRANS: Client error displayed when trying to create a new user while creating new users is not allowed.
-            $this->clientError(_m('Registration not allowed.'));
-            return;
-        }
-
-        $invite = null;
-
-        if (common_config('site', 'inviteonly')) {
-            $code = $_SESSION['invitecode'];
-            if (empty($code)) {
-                // TRANS: Client error displayed when trying to create a new user while creating new users is not allowed.
-                $this->clientError(_m('Registration not allowed.'));
-                return;
-            }
-
-            $invite = Invitation::getKV($code);
-
-            if (empty($invite)) {
-                // TRANS: Client error displayed when trying to create a new user with an invalid invitation code.
-                $this->clientError(_m('Not a valid invitation code.'));
-                return;
-            }
-        }
-
-        try {
-            $nickname = Nickname::normalize($this->trimmed('newname'));
-        } catch (NicknameException $e) {
-            $this->showForm($e->getMessage());
-            return;
-        }
-
-        if (!User::allowed_nickname($nickname)) {
-            // TRANS: Client error displayed when trying to create a new user with an invalid username.
-            $this->showForm(_m('Nickname not allowed.'));
-            return;
-        }
-
-        if (User::getKV('nickname', $nickname)) {
-            // TRANS: Client error displayed when trying to create a new user with a username that is already in use.
-            $this->showForm(_m('Nickname already in use. Try another one.'));
-            return;
-        }
-
-        $fullname = trim($this->tw_fields['fullname']);
-
-        $args = array('nickname' => $nickname, 'fullname' => $fullname);
-
-        if (!empty($invite)) {
-            $args['code'] = $invite->code;
-        }
-
-        $email = $this->getEmail();
-        if (!empty($email)) {
-            $args['email'] = $email;
-        }
-
-        $user = User::register($args);
-
-        if (empty($user)) {
-            // TRANS: Server error displayed when creating a new user has failed.
-            $this->serverError(_m('Error registering user.'));
-            return;
-        }
-
-        $result = $this->saveForeignLink($user->id,
-                                         $this->twuid,
-                                         $this->access_token);
-
-        save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
-
-        if (!$result) {
-            // TRANS: Server error displayed when connecting a user to a Twitter user has failed.
-            $this->serverError(_m('Error connecting user to Twitter.'));
-            return;
-        }
-
-        common_set_user($user);
-        common_real_login(true);
-
-        common_debug('TwitterBridge Plugin - ' .
-                     "Registered new user $user->id from Twitter user $this->twuid");
-
-        Event::handle('EndRegistrationTry', array($this));
-
-        common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)),
-                        303);
-    }
-
-    function connectNewUser()
-    {
-        $nickname = $this->trimmed('nickname');
-        $password = $this->trimmed('password');
-
-        if (!common_check_user($nickname, $password)) {
-            // TRANS: Form validation error displayed when connecting an existing user to a Twitter user fails because
-            // TRANS: the provided username and/or password are incorrect.
-            $this->showForm(_m('Invalid username or password.'));
-            return;
-        }
-
-        $user = User::getKV('nickname', $nickname);
-
-        if (!empty($user)) {
-            common_debug('TwitterBridge Plugin - ' .
-                         "Legit user to connect to Twitter: $nickname");
-        }
-
-        $result = $this->saveForeignLink($user->id,
-                                         $this->twuid,
-                                         $this->access_token);
-
-        save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
-
-        if (!$result) {
-            // TRANS: Server error displayed connecting a user to a Twitter user has failed.
-            $this->serverError(_m('Error connecting user to Twitter.'));
-            return;
-        }
-
-        common_debug('TwitterBridge Plugin - ' .
-                     "Connected Twitter user $this->twuid to local user $user->id");
-
-        common_set_user($user);
-        common_real_login(true);
-
-        $this->goHome($user->nickname);
-    }
-
-    function connectUser()
-    {
-        $user = common_current_user();
-
-        $result = $this->flinkUser($user->id, $this->twuid);
-
-        if (empty($result)) {
-            // TRANS: Server error displayed connecting a user to a Twitter user has failed.
-            $this->serverError(_m('Error connecting user to Twitter.'));
-            return;
-        }
-
-        common_debug('TwitterBridge Plugin - ' .
-                     "Connected Twitter user $this->twuid to local user $user->id");
-
-        // Return to Twitter connection settings tab
-        common_redirect(common_local_url('twittersettings'), 303);
-    }
-
-    function tryLogin()
-    {
-        common_debug('TwitterBridge Plugin - ' .
-                     "Trying login for Twitter user $this->twuid.");
-
-        $flink = Foreign_link::getByForeignID($this->twuid,
-                                              TWITTER_SERVICE);
-
-        if (!empty($flink)) {
-            $user = $flink->getUser();
-
-            if (!empty($user)) {
-
-                common_debug('TwitterBridge Plugin - ' .
-                             "Logged in Twitter user $flink->foreign_id as user $user->id ($user->nickname)");
-
-                common_set_user($user);
-                common_real_login(true);
-                $this->goHome($user->nickname);
-            }
-
-        } else {
-
-            common_debug('TwitterBridge Plugin - ' .
-                         "No flink found for twuid: $this->twuid - new user");
-
-            $this->showForm(null, $this->bestNewNickname());
-        }
-    }
-
-    function goHome($nickname)
-    {
-        $url = common_get_returnto();
-        if ($url) {
-            // We don't have to return to it again
-            common_set_returnto(null);
-        } else {
-            $url = common_local_url('all',
-                                    array('nickname' =>
-                                          $nickname));
-        }
-
-        common_redirect($url, 303);
-    }
-
-    function bestNewNickname()
-    {
-        if (!empty($this->tw_fields['fullname'])) {
-            $nickname = $this->nicknamize($this->tw_fields['fullname']);
-            if ($this->isNewNickname($nickname)) {
-                return $nickname;
-            }
-        }
-
-        return null;
-    }
-
-     // Given a string, try to make it work as a nickname
-
-     function nicknamize($str)
-     {
-         $str = preg_replace('/\W/', '', $str);
-         $str = str_replace(array('-', '_'), '', $str);
-         return strtolower($str);
-     }
-
-    function isNewNickname($str)
-    {
-        if (!Nickname::isValid($str)) {
-            return false;
-        }
-        if (!User::allowed_nickname($str)) {
-            return false;
-        }
-        if (User::getKV('nickname', $str)) {
-            return false;
-        }
-        return true;
-    }
-}
diff --git a/plugins/TwitterBridge/twitterimport.php b/plugins/TwitterBridge/twitterimport.php
deleted file mode 100644 (file)
index cdfb8f8..0000000
+++ /dev/null
@@ -1,712 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Plugin
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @author    Julien C <chaumond@gmail.com>
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2009-2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
-
-/**
- * Encapsulation of the Twitter status -> notice incoming bridge import.
- * Is used by both the polling twitterstatusfetcher.php daemon, and the
- * in-progress streaming import.
- *
- * @category Plugin
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @author   Julien C <chaumond@gmail.com>
- * @author   Brion Vibber <brion@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- * @link     http://twitter.com/
- */
-class TwitterImport
-{
-    public function importStatus($status)
-    {
-        // Hacktastic: filter out stuff coming from this StatusNet
-        $source = mb_strtolower(common_config('integration', 'source'));
-
-        if (preg_match("/$source/", mb_strtolower($status->source))) {
-            common_debug($this->name() . ' - Skipping import of status ' .
-                         twitter_id($status) . ' with source ' . $source);
-            return null;
-        }
-
-        // Don't save it if the user is protected
-        // FIXME: save it but treat it as private
-        if ($status->user->protected) {
-            return null;
-        }
-
-        $notice = $this->saveStatus($status);
-
-        return $notice;
-    }
-
-    function name()
-    {
-        return get_class($this);
-    }
-
-    function saveStatus($status)
-    {
-        $profile = $this->ensureProfile($status->user);
-
-        if (empty($profile)) {
-            common_log(LOG_ERR, $this->name() .
-                ' - Problem saving notice. No associated Profile.');
-            return null;
-        }
-
-        $statusId = twitter_id($status);
-        $statusUri = $this->makeStatusURI($status->user->screen_name, $statusId);
-
-        // check to see if we've already imported the status
-        $n2s = Notice_to_status::getKV('status_id', $statusId);
-
-        if (!empty($n2s)) {
-            common_log(
-                LOG_INFO,
-                $this->name() .
-                " - Ignoring duplicate import: {$statusId}"
-            );
-            return Notice::getKV('id', $n2s->notice_id);
-        }
-
-        // If it's a retweet, save it as a repeat!
-        if (!empty($status->retweeted_status)) {
-            common_log(LOG_INFO, "Status {$statusId} is a retweet of " . twitter_id($status->retweeted_status) . ".");
-            $original = $this->saveStatus($status->retweeted_status);
-            if (empty($original)) {
-                return null;
-            } else {
-                $author = $original->getProfile();
-                // TRANS: Message used to repeat a notice. RT is the abbreviation of 'retweet'.
-                // TRANS: %1$s is the repeated user's name, %2$s is the repeated notice.
-                $content = sprintf(_m('RT @%1$s %2$s'),
-                                   $author->nickname,
-                                   $original->content);
-
-                if (Notice::contentTooLong($content)) {
-                    $contentlimit = Notice::maxContent();
-                    $content = mb_substr($content, 0, $contentlimit - 4) . ' ...';
-                }
-
-                $repeat = Notice::saveNew($profile->id,
-                                          $content,
-                                          'twitter',
-                                          array('repeat_of' => $original->id,
-                                                'uri' => $statusUri,
-                                                'is_local' => Notice::GATEWAY));
-                common_log(LOG_INFO, "Saved {$repeat->id} as a repeat of {$original->id}");
-                Notice_to_status::saveNew($repeat->id, $statusId);
-                return $repeat;
-            }
-        }
-
-        $notice = new Notice();
-
-        $notice->profile_id = $profile->id;
-        $notice->uri        = $statusUri;
-        $notice->url        = $statusUri;
-        $notice->created    = strftime(
-            '%Y-%m-%d %H:%M:%S',
-            strtotime($status->created_at)
-        );
-
-        $notice->source     = 'twitter';
-
-        $notice->reply_to   = null;
-
-        $replyTo = twitter_id($status, 'in_reply_to_status_id');
-        if (!empty($replyTo)) {
-            common_log(LOG_INFO, "Status {$statusId} is a reply to status {$replyTo}");
-            $n2s = Notice_to_status::getKV('status_id', $replyTo);
-            if (empty($n2s)) {
-                common_log(LOG_INFO, "Couldn't find local notice for status {$replyTo}");
-            } else {
-                $reply = Notice::getKV('id', $n2s->notice_id);
-                if (empty($reply)) {
-                    common_log(LOG_INFO, "Couldn't find local notice for status {$replyTo}");
-                } else {
-                    common_log(LOG_INFO, "Found local notice {$reply->id} for status {$replyTo}");
-                    $notice->reply_to     = $reply->id;
-                    $notice->conversation = $reply->conversation;
-                }
-            }
-        }
-
-        if (empty($notice->conversation)) {
-            $conv = Conversation::create();
-            $notice->conversation = $conv->id;
-            common_log(LOG_INFO, "No known conversation for status {$statusId} so making a new one {$conv->id}.");
-        }
-
-        $notice->is_local   = Notice::GATEWAY;
-
-        $notice->content  = html_entity_decode($this->linkify($status, FALSE), ENT_QUOTES, 'UTF-8');
-        $notice->rendered = $this->linkify($status, TRUE);
-
-        if (Event::handle('StartNoticeSave', array(&$notice))) {
-
-            $id = $notice->insert();
-
-            if (!$id) {
-                common_log_db_error($notice, 'INSERT', __FILE__);
-                common_log(LOG_ERR, $this->name() .
-                    ' - Problem saving notice.');
-            }
-
-            Event::handle('EndNoticeSave', array($notice));
-        }
-
-        Notice_to_status::saveNew($notice->id, $statusId);
-
-        $this->saveStatusMentions($notice, $status);
-        $this->saveStatusAttachments($notice, $status);
-
-        $notice->blowOnInsert();
-
-        return $notice;
-    }
-
-    /**
-     * Make an URI for a status.
-     *
-     * @param object $status status object
-     *
-     * @return string URI
-     */
-    function makeStatusURI($username, $id)
-    {
-        return 'http://twitter.com/#!/'
-          . $username
-          . '/status/'
-          . $id;
-    }
-
-
-    /**
-     * Look up a Profile by profileurl field.  Profile::getKV() was
-     * not working consistently.
-     *
-     * @param string $nickname   local nickname of the Twitter user
-     * @param string $profileurl the profile url
-     *
-     * @return mixed value the first Profile with that url, or null
-     */
-    function getProfileByUrl($nickname, $profileurl)
-    {
-        $profile = new Profile();
-        $profile->nickname = $nickname;
-        $profile->profileurl = $profileurl;
-        $profile->limit(1);
-
-        if ($profile->find()) {
-            $profile->fetch();
-            return $profile;
-        }
-
-        return null;
-    }
-
-    /**
-     * Check to see if this Twitter status has already been imported
-     *
-     * @param Profile $profile   Twitter user's local profile
-     * @param string  $statusUri URI of the status on Twitter
-     *
-     * @return mixed value a matching Notice or null
-     */
-    function checkDupe($profile, $statusUri)
-    {
-        $notice = new Notice();
-        $notice->uri = $statusUri;
-        $notice->profile_id = $profile->id;
-        $notice->limit(1);
-
-        if ($notice->find()) {
-            $notice->fetch();
-            return $notice;
-        }
-
-        return null;
-    }
-
-    function ensureProfile($user)
-    {
-        // check to see if there's already a profile for this user
-        $profileurl = 'http://twitter.com/' . $user->screen_name;
-        $profile = $this->getProfileByUrl($user->screen_name, $profileurl);
-
-        if (!empty($profile)) {
-            common_debug($this->name() .
-                         " - Profile for $profile->nickname found.");
-
-            // Check to see if the user's Avatar has changed
-
-            $this->checkAvatar($user, $profile);
-            return $profile;
-
-        } else {
-            common_debug($this->name() . ' - Adding profile and remote profile ' .
-                         "for Twitter user: $profileurl.");
-
-            $profile = new Profile();
-            $profile->query("BEGIN");
-
-            $profile->nickname = $user->screen_name;
-            $profile->fullname = $user->name;
-            $profile->homepage = $user->url;
-            $profile->bio = $user->description;
-            $profile->location = $user->location;
-            $profile->profileurl = $profileurl;
-            $profile->created = common_sql_now();
-
-            try {
-                $id = $profile->insert();
-            } catch(Exception $e) {
-                common_log(LOG_WARNING, $this->name() . ' Couldn\'t insert profile - ' . $e->getMessage());
-            }
-
-            if (empty($id)) {
-                common_log_db_error($profile, 'INSERT', __FILE__);
-                $profile->query("ROLLBACK");
-                return false;
-            }
-
-            // check for remote profile
-
-            $remote_pro = Remote_profile::getKV('uri', $profileurl);
-
-            if (empty($remote_pro)) {
-                $remote_pro = new Remote_profile();
-
-                $remote_pro->id = $id;
-                $remote_pro->uri = $profileurl;
-                $remote_pro->created = common_sql_now();
-
-                try {
-                    $rid = $remote_pro->insert();
-                } catch (Exception $e) {
-                    common_log(LOG_WARNING, $this->name() . ' Couldn\'t save remote profile - ' . $e->getMessage());
-                }
-
-                if (empty($rid)) {
-                    common_log_db_error($profile, 'INSERT', __FILE__);
-                    $profile->query("ROLLBACK");
-                    return false;
-                }
-            }
-
-            $profile->query("COMMIT");
-
-            $this->saveAvatars($user, $id);
-
-            return $profile;
-        }
-    }
-
-    function checkAvatar($twitter_user, $profile)
-    {
-        global $config;
-
-        $newname = 'Twitter_' . $twitter_user->id . '_' . basename($twitter_user->profile_image_url);
-
-        $oldname = $profile->getAvatar(48)->filename;
-
-        if ($newname != $oldname) {
-            common_debug($this->name() . ' - Avatar for Twitter user ' .
-                         "$profile->nickname has changed.");
-            common_debug($this->name() . " - old: $oldname new: $newname");
-
-            $this->updateAvatars($twitter_user, $profile);
-        }
-
-        if ($this->missingAvatarFile($profile)) {
-            common_debug($this->name() . ' - Twitter user ' .
-                         $profile->nickname .
-                         ' is missing one or more local avatars.');
-            common_debug($this->name() ." - old: $oldname new: $newname");
-
-            $this->updateAvatars($twitter_user, $profile);
-        }
-    }
-
-    function updateAvatars($twitter_user, $profile) {
-
-        global $config;
-
-        $path_parts = pathinfo($twitter_user->profile_image_url);
-
-        $ext = (isset($path_parts['extension']) ? '.'.$path_parts['extension'] : '');  // some lack extension
-        $img_root = basename($path_parts['basename'], '_normal'.$ext); // cut off extension
-        $mediatype = $this->getMediatype(substr($ext, 1));
-
-        foreach (array('mini', 'normal', 'bigger') as $size) {
-            $url = $path_parts['dirname'] . '/' .
-                $img_root . '_' . $size . $ext;
-            $filename = 'Twitter_' . $twitter_user->id . '_' .
-                $img_root . '_' . $size . $ext;
-
-            $this->updateAvatar($profile->id, $size, $mediatype, $filename);
-            $this->fetchAvatar($url, $filename);
-        }
-    }
-
-    function missingAvatarFile($profile) {
-        foreach (array(24, 48, 73) as $size) {
-            $filename = $profile->getAvatar($size)->filename;
-            $avatarpath = Avatar::path($filename);
-            if (file_exists($avatarpath) == FALSE) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    function getMediatype($ext)
-    {
-        $mediatype = null;
-
-        switch (strtolower($ext)) {
-        case 'jpeg':
-        case 'jpg':
-            $mediatype = 'image/jpeg';
-            break;
-        case 'gif':
-            $mediatype = 'image/gif';
-            break;
-        default:
-            $mediatype = 'image/png';
-        }
-
-        return $mediatype;
-    }
-
-    function saveAvatars($user, $id)
-    {
-        global $config;
-
-        $path_parts = pathinfo($user->profile_image_url);
-        $ext = (isset($path_parts['extension']) ? '.'.$path_parts['extension'] : '');
-        $img_root = basename($path_parts['basename'], '_normal'.$ext);
-        $mediatype = $this->getMediatype(substr($ext, 1));
-
-        foreach (array('mini', 'normal', 'bigger') as $size) {
-            $url = $path_parts['dirname'] . '/' .
-                $img_root . '_' . $size . $ext;
-            $filename = 'Twitter_' . $user->id . '_' .
-                $img_root . '_' . $size . $ext;
-
-            if ($this->fetchAvatar($url, $filename)) {
-                $this->newAvatar($id, $size, $mediatype, $filename);
-            } else {
-                common_log(LOG_WARNING, $id() .
-                           " - Problem fetching Avatar: $url");
-            }
-        }
-    }
-
-    function updateAvatar($profile_id, $size, $mediatype, $filename) {
-
-        common_debug($this->name() . " - Updating avatar: $size");
-
-        $profile = Profile::getKV($profile_id);
-
-        if (empty($profile)) {
-            common_debug($this->name() . " - Couldn't get profile: $profile_id!");
-            return;
-        }
-
-        $sizes = array('mini' => 24, 'normal' => 48, 'bigger' => 73);
-        $avatar = $profile->getAvatar($sizes[$size]);
-
-        // Delete the avatar, if present
-        if ($avatar) {
-            $avatar->delete();
-        }
-
-        $this->newAvatar($profile->id, $size, $mediatype, $filename);
-    }
-
-    function newAvatar($profile_id, $size, $mediatype, $filename)
-    {
-        global $config;
-
-        $avatar = new Avatar();
-        $avatar->profile_id = $profile_id;
-
-        switch($size) {
-        case 'mini':
-            $avatar->width  = 24;
-            $avatar->height = 24;
-            break;
-        case 'normal':
-            $avatar->width  = 48;
-            $avatar->height = 48;
-            break;
-        default:
-            // Note: Twitter's big avatars are a different size than
-            // StatusNet's (StatusNet's = 96)
-            $avatar->width  = 73;
-            $avatar->height = 73;
-        }
-
-        $avatar->original = 0; // we don't have the original
-        $avatar->mediatype = $mediatype;
-        $avatar->filename = $filename;
-        $avatar->url = Avatar::url($filename);
-
-        $avatar->created = common_sql_now();
-
-        try {
-            $id = $avatar->insert();
-        } catch (Exception $e) {
-            common_log(LOG_WARNING, $this->name() . ' Couldn\'t insert avatar - ' . $e->getMessage());
-        }
-
-        if (empty($id)) {
-            common_log_db_error($avatar, 'INSERT', __FILE__);
-            return null;
-        }
-
-        common_debug($this->name() .
-                     " - Saved new $size avatar for $profile_id.");
-
-        return $id;
-    }
-
-    /**
-     * Fetch a remote avatar image and save to local storage.
-     *
-     * @param string $url avatar source URL
-     * @param string $filename bare local filename for download
-     * @return bool true on success, false on failure
-     */
-    function fetchAvatar($url, $filename)
-    {
-        common_debug($this->name() . " - Fetching Twitter avatar: $url");
-
-        $request = HTTPClient::start();
-        $response = $request->get($url);
-        if ($response->isOk()) {
-            $avatarfile = Avatar::path($filename);
-            $ok = file_put_contents($avatarfile, $response->getBody());
-            if (!$ok) {
-                common_log(LOG_WARNING, $this->name() .
-                           " - Couldn't open file $filename");
-                return false;
-            }
-        } else {
-            return false;
-        }
-
-        return true;
-    }
-
-    const URL = 1;
-    const HASHTAG = 2;
-    const MENTION = 3;
-
-    function linkify($status, $html = FALSE)
-    {
-        $text = $status->text;
-
-        if (empty($status->entities)) {
-            $statusId = twitter_id($status);
-            common_log(LOG_WARNING, "No entities data for {$statusId}; trying to fake up links ourselves.");
-            $text = common_replace_urls_callback($text, 'common_linkify');
-            $text = preg_replace('/(^|\&quot\;|\'|\(|\[|\{|\s+)#([\pL\pN_\-\.]{1,64})/e', "'\\1#'.TwitterStatusFetcher::tagLink('\\2')", $text);
-            $text = preg_replace('/(^|\s+)@([a-z0-9A-Z_]{1,64})/e', "'\\1@'.TwitterStatusFetcher::atLink('\\2')", $text);
-            return $text;
-        }
-
-        // Move all the entities into order so we can
-        // replace them and escape surrounding plaintext
-        // in order
-
-        $toReplace = array();
-
-        if (!empty($status->entities->urls)) {
-            foreach ($status->entities->urls as $url) {
-                $toReplace[$url->indices[0]] = array(self::URL, $url);
-            }
-        }
-
-        if (!empty($status->entities->hashtags)) {
-            foreach ($status->entities->hashtags as $hashtag) {
-                $toReplace[$hashtag->indices[0]] = array(self::HASHTAG, $hashtag);
-            }
-        }
-
-        if (!empty($status->entities->user_mentions)) {
-            foreach ($status->entities->user_mentions as $mention) {
-                $toReplace[$mention->indices[0]] = array(self::MENTION, $mention);
-            }
-        }
-
-        // sort in forward order by key
-
-        ksort($toReplace);
-
-        $result = '';
-        $cursor = 0;
-
-        foreach ($toReplace as $part) {
-            list($type, $object) = $part;
-            $start = $object->indices[0];
-            $end = $object->indices[1];
-            if ($cursor < $start) {
-                // Copy in the preceding plaintext
-                $result .= $this->twitEscape(mb_substr($text, $cursor, $start - $cursor));
-                $cursor = $start;
-            }
-            $orig = $this->twitEscape(mb_substr($text, $start, $end - $start));
-            switch($type) {
-            case self::URL:
-                $linkText = $this->makeUrlLink($object, $orig, $html);
-                break;
-            case self::HASHTAG:
-                if ($html) {
-                    $linkText = $this->makeHashtagLink($object, $orig);
-                }else{
-                    $linkText = $orig;
-                }
-                break;
-            case self::MENTION:
-                if ($html) {
-                    $linkText = $this->makeMentionLink($object, $orig);
-                }else{
-                    $linkText = $orig;
-                }
-                break;
-            default:
-                $linkText = $orig;
-                continue;
-            }
-            $result .= $linkText;
-            $cursor = $end;
-        }
-        $last = $this->twitEscape(mb_substr($text, $cursor));
-        $result .= $last;
-
-        return $result;
-    }
-
-    function twitEscape($str)
-    {
-        // Twitter seems to preemptive turn < and > into &lt; and &gt;
-        // but doesn't for &, so while you may have some magic protection
-        // against XSS by not bothing to escape manually, you still get
-        // invalid XHTML. Thanks!
-        //
-        // Looks like their web interface pretty much sends anything
-        // through intact, so.... to do equivalent, decode all entities
-        // and then re-encode the special ones.
-        return htmlspecialchars(html_entity_decode($str, ENT_COMPAT, 'UTF-8'));
-    }
-
-    function makeUrlLink($object, $orig, $html)
-    {
-        if ($html) {
-            return '<a href="'.htmlspecialchars($object->expanded_url).'" class="extlink">'.htmlspecialchars($object->display_url).'</a>';
-        }else{
-            return htmlspecialchars($object->expanded_url);
-        }
-    }
-
-    function makeHashtagLink($object, $orig)
-    {
-        return "#" . self::tagLink($object->text, substr($orig, 1));
-    }
-
-    function makeMentionLink($object, $orig)
-    {
-        return "@".self::atLink($object->screen_name, $object->name, substr($orig, 1));
-    }
-
-    static function tagLink($tag, $orig)
-    {
-        return "<a href='https://search.twitter.com/search?q=%23{$tag}' class='hashtag'>{$orig}</a>";
-    }
-
-    static function atLink($screenName, $fullName, $orig)
-    {
-        if (!empty($fullName)) {
-            return "<a href='http://twitter.com/#!/{$screenName}' title='{$fullName}'>{$orig}</a>";
-        } else {
-            return "<a href='http://twitter.com/#!/{$screenName}'>{$orig}</a>";
-        }
-    }
-
-    function saveStatusMentions($notice, $status)
-    {
-        $mentions = array();
-
-        if (empty($status->entities) || empty($status->entities->user_mentions)) {
-            return;
-        }
-
-        foreach ($status->entities->user_mentions as $mention) {
-            $flink = Foreign_link::getByForeignID($mention->id, TWITTER_SERVICE);
-            if (!empty($flink)) {
-                $user = User::getKV('id', $flink->user_id);
-                if (!empty($user)) {
-                    $reply = new Reply();
-                    $reply->notice_id  = $notice->id;
-                    $reply->profile_id = $user->id;
-                    $reply->modified   = $notice->created;
-                    common_log(LOG_INFO, __METHOD__ . ": saving reply: notice {$notice->id} to profile {$user->id}");
-                    $id = $reply->insert();
-                }
-            }
-        }
-    }
-
-    /**
-     * Record URL links from the notice. Needed to get thumbnail records
-     * for referenced photo and video posts, etc.
-     *
-     * @param Notice $notice
-     * @param object $status
-     */
-    function saveStatusAttachments($notice, $status)
-    {
-        if (common_config('attachments', 'process_links')) {
-            if (!empty($status->entities) && !empty($status->entities->urls)) {
-                foreach ($status->entities->urls as $url) {
-                    File::processNew($url->url, $notice->id);
-                }
-            }
-        }
-    }
-}
diff --git a/plugins/TwitterBridge/twitterlogin.php b/plugins/TwitterBridge/twitterlogin.php
deleted file mode 100644 (file)
index 379e136..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * 'Sign in with Twitter' login page
- *
- * 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  Login
- * @package   StatusNet
- * @author    Julien Chaumond <chaumond@gmail.com>
- * @author    Zach Copley <zach@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
-
-/**
- * Page for logging in with Twitter
- *
- * @category Login
- * @package  StatusNet
- * @author   Julien Chaumond <chaumond@gmail.com>
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- * @see      SettingsAction
- */
-class TwitterloginAction extends Action
-{
-    function handle($args)
-    {
-        parent::handle($args);
-
-        if (common_is_real_login()) {
-            // TRANS: Client error displayed when trying to log in using Twitter while already logged in to StatusNet.
-            $this->clientError(_m('Already logged in.'));
-        }
-
-        $this->showPage();
-    }
-
-    function title()
-    {
-        // TRANS: Title for login using Twitter page.
-        return _m('TITLE','Twitter Login');
-    }
-
-    function getInstructions()
-    {
-        // TRANS: Instructions for login using Twitter page.
-        return _m('Login with your Twitter account');
-    }
-
-    function showPageNotice()
-    {
-        $instr = $this->getInstructions();
-        $output = common_markup_to_html($instr);
-        $this->elementStart('div', 'instructions');
-        $this->raw($output);
-        $this->elementEnd('div');
-    }
-
-    function showContent()
-    {
-        $this->elementStart('a', array('href' => common_local_url('twitterauthorization',
-                                                                  null,
-                                                                  array('signin' => true))));
-        $this->element('img', array('src' => Plugin::staticPath('TwitterBridge', 'Sign-in-with-Twitter-lighter.png'),
-                                    // TRANS: Alternative text for "sign in with Twitter" image.
-                                    'alt' => _m('Sign in with Twitter')));
-        $this->elementEnd('a');
-    }
-
-    function showLocalNav()
-    {
-        $nav = new LoginGroupNav($this);
-        $nav->show();
-    }
-}
diff --git a/plugins/TwitterBridge/twitteroauthclient.php b/plugins/TwitterBridge/twitteroauthclient.php
deleted file mode 100644 (file)
index 7208442..0000000
+++ /dev/null
@@ -1,370 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Class for doing OAuth calls against Twitter
- *
- * 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  Integration
- * @package   StatusNet
- * @author    Zach Copley <zach@status.net>
- * @copyright 2009-2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * Class for talking to the Twitter API with OAuth.
- *
- * @category Integration
- * @package  StatusNet
- * @author   Zach Copley <zach@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- */
-class TwitterOAuthClient extends OAuthClient
-{
-    public static $requestTokenURL = 'https://api.twitter.com/oauth/request_token';
-    public static $authorizeURL    = 'https://api.twitter.com/oauth/authorize';
-    public static $signinUrl       = 'https://api.twitter.com/oauth/authenticate';
-    public static $accessTokenURL  = 'https://api.twitter.com/oauth/access_token';
-
-    /**
-     * Constructor
-     *
-     * @param string $oauth_token        the user's token
-     * @param string $oauth_token_secret the user's token secret
-     *
-     * @return nothing
-     */
-    function __construct($oauth_token = null, $oauth_token_secret = null)
-    {
-        $consumer_key    = common_config('twitter', 'consumer_key');
-        $consumer_secret = common_config('twitter', 'consumer_secret');
-
-        if (empty($consumer_key) && empty($consumer_secret)) {
-            $consumer_key = common_config(
-                'twitter',
-                'global_consumer_key'
-            );
-            $consumer_secret = common_config(
-                'twitter',
-                'global_consumer_secret'
-            );
-        }
-
-        parent::__construct(
-            $consumer_key,
-            $consumer_secret,
-            $oauth_token,
-            $oauth_token_secret
-        );
-    }
-
-    // XXX: the following two functions are to support the horrible hack
-    // of using the credentils field in Foreign_link to store both
-    // the access token and token secret.  This hack should go away with
-    // 0.9, in which we can make DB changes and add a new column for the
-    // token itself.
-
-    static function packToken($token)
-    {
-        return implode(chr(0), array($token->key, $token->secret));
-    }
-
-    static function unpackToken($str)
-    {
-        $vals = explode(chr(0), $str);
-        return new OAuthToken($vals[0], $vals[1]);
-    }
-
-    static function isPackedToken($str)
-    {
-        if (strpos($str, chr(0)) === false) {
-            return false;
-        } else {
-            return true;
-        }
-    }
-
-    /**
-     * Gets a request token from Twitter
-     *
-     * @return OAuthToken $token the request token
-     */
-    function getRequestToken()
-    {
-        return parent::getRequestToken(
-            self::$requestTokenURL,
-            common_local_url('twitterauthorization')
-        );
-    }
-
-    /**
-     * Builds a link to Twitter's endpoint for authorizing a request token
-     *
-     * @param OAuthToken $request_token token to authorize
-     *
-     * @return the link
-     */
-    function getAuthorizeLink($request_token, $signin = false)
-    {
-        $url = ($signin) ? self::$signinUrl : self::$authorizeURL;
-
-        return parent::getAuthorizeLink($url,
-                                        $request_token,
-                                        common_local_url('twitterauthorization'));
-    }
-
-    /**
-     * Fetches an access token from Twitter
-     *
-     * @param string $verifier 1.0a verifier
-     *
-     * @return OAuthToken $token the access token
-     */
-    function getAccessToken($verifier = null)
-    {
-        return parent::getAccessToken(
-            self::$accessTokenURL,
-            $verifier
-        );
-    }
-
-    /**
-     * Calls Twitter's /account/verify_credentials API method
-     *
-     * @return mixed the Twitter user
-     */
-    function verifyCredentials()
-    {
-        $url          = 'https://api.twitter.com/1.1/account/verify_credentials.json';
-        $response     = $this->oAuthGet($url);
-        $twitter_user = json_decode($response);
-        return $twitter_user;
-    }
-
-    /**
-     * Calls Twitter's /statuses/update API method
-     *
-     * @param string $status  text of the status
-     * @param mixed  $params  optional other parameters to pass to Twitter,
-     *                        as defined. For back-compatibility, if an int
-     *                        is passed we'll consider it a reply-to ID.
-     *
-     * @return mixed the status
-     */
-    function statusesUpdate($status, $params=array())
-    {
-        $url      = 'https://api.twitter.com/1.1/statuses/update.json';
-        if (is_numeric($params)) {
-            $params = array('in_reply_to_status_id' => intval($params));
-        }
-        $params['status'] = $status;
-        // We don't have to pass 'source' as the oauth key is tied to an app.
-
-        $response = $this->oAuthPost($url, $params);
-        $status   = json_decode($response);
-        return $status;
-    }
-
-    /**
-     * Calls Twitter's /statuses/home_timeline API method
-     *
-     * @param int    $since_id    show statuses after this id
-     * @param string $timelineUri timeline to poll statuses from
-     * @param int    $max_id      show statuses before this id
-     * @param int    $cnt         number of statuses to show
-     * @param int    $page        page number
-     *
-     * @return mixed an array of statuses
-     */
-    function statusesTimeline($since_id = null, $timelineUri = 'home_timeline',
-                              $max_id = null, $cnt = 200, $page = null)
-    {
-        $url    = 'https://api.twitter.com/1.1/statuses/'.$timelineUri.'.json';
-
-        $params = array('include_entities' => 'true',
-                        'include_rts'      => 'true');
-
-        if (!empty($since_id)) {
-            $params['since_id'] = $since_id;
-        }
-        if (!empty($max_id)) {
-            $params['max_id'] = $max_id;
-        }
-        if (!empty($cnt)) {
-            $params['count'] = $cnt;
-        }
-        if (!empty($page)) {
-            $params['page'] = $page;
-        }
-
-        $response = $this->oAuthGet($url, $params);
-        $statuses = json_decode($response);
-        return $statuses;
-    }
-
-    /**
-     * Calls Twitter's /statuses/friends API method
-     *
-     * @param int $id          id of the user whom you wish to see friends of
-     * @param int $user_id     numerical user id
-     * @param int $screen_name screen name
-     * @param int $page        page number
-     *
-     * @return mixed an array of twitter users and their latest status
-     */
-    function statusesFriends($id = null, $user_id = null, $screen_name = null,
-                             $page = null)
-    {
-        $url = "https://api.twitter.com/1.1/friends/list.json";
-
-        $params = array();
-
-        if (!empty($id)) {
-            $params['id'] = $id;
-        }
-
-        if (!empty($user_id)) {
-            $params['user_id'] = $user_id;
-        }
-
-        if (!empty($screen_name)) {
-            $params['screen_name'] = $screen_name;
-        }
-
-        if (!empty($page)) {
-            $params['page'] = $page;
-        }
-
-        $response = $this->oAuthGet($url, $params);
-        $friends  = json_decode($response);
-        return $friends;
-    }
-
-    /**
-     * Calls Twitter's /statuses/friends/ids API method
-     *
-     * @param int $id          id of the user whom you wish to see friends of
-     * @param int $user_id     numerical user id
-     * @param int $screen_name screen name
-     * @param int $page        page number
-     *
-     * @return mixed a list of ids, 100 per page
-     */
-    function friendsIds($id = null, $user_id = null, $screen_name = null,
-                         $page = null)
-    {
-        $url = "https://api.twitter.com/1.1/friends/ids.json";
-
-        $params = array();
-
-        if (!empty($id)) {
-            $params['id'] = $id;
-        }
-
-        if (!empty($user_id)) {
-            $params['user_id'] = $user_id;
-        }
-
-        if (!empty($screen_name)) {
-            $params['screen_name'] = $screen_name;
-        }
-
-        if (!empty($page)) {
-            $params['page'] = $page;
-        }
-
-        $response = $this->oAuthGet($url, $params);
-        $ids      = json_decode($response);
-        return $ids;
-    }
-
-    /**
-     * Calls Twitter's /statuses/retweet/id.json API method
-     *
-     * @param int $id id of the notice to retweet
-     *
-     * @return retweeted status
-     */
-
-    function statusesRetweet($id)
-    {
-        $url = "http://api.twitter.com/1.1/statuses/retweet/$id.json";
-        $response = $this->oAuthPost($url);
-        $status = json_decode($response);
-        return $status;
-    }
-
-    /**
-     * Calls Twitter's /favorites/create API method
-     *
-     * @param int $id ID of the status to favorite
-     *
-     * @return object faved status
-     */
-
-    function favoritesCreate($id)
-    {
-        $url = "http://api.twitter.com/1.1/favorites/create.json";
-        $params=array();
-        $params['id'] = $id;
-        $response = $this->oAuthPost($url, $params);
-        $status = json_decode($response);
-        return $status;
-    }
-
-    /**
-     * Calls Twitter's /favorites/destroy API method
-     *
-     * @param int $id ID of the status to unfavorite
-     *
-     * @return object unfaved status
-     */
-
-    function favoritesDestroy($id)
-    {
-        $url = "http://api.twitter.com/1.1/favorites/destroy.json";
-        $params=array();
-        $params['id'] = $id;
-        $response = $this->oAuthPost($url,$params);
-        $status = json_decode($response);
-        return $status;
-    }
-
-    /**
-     * Calls Twitter's /statuses/destroy API method
-     *
-     * @param int $id ID of the status to destroy
-     *
-     * @return object destroyed
-     */
-
-    function statusesDestroy($id)
-    {
-        $url = "http://api.twitter.com/1.1/statuses/destroy/$id.json";
-        $response = $this->oAuthPost($url);
-        $status = json_decode($response);
-        return $status;
-    }
-}
diff --git a/plugins/TwitterBridge/twitterqueuehandler.php b/plugins/TwitterBridge/twitterqueuehandler.php
deleted file mode 100644 (file)
index 644ce17..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
-
-class TwitterQueueHandler extends QueueHandler
-{
-    function transport()
-    {
-        return 'twitter';
-    }
-
-    function handle($notice)
-    {
-        $ok = broadcast_twitter($notice);
-        return $ok || common_config('twitter', 'ignore_errors');
-    }
-}
diff --git a/plugins/TwitterBridge/twittersettings.php b/plugins/TwitterBridge/twittersettings.php
deleted file mode 100644 (file)
index 88799e7..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Settings for Twitter integration
- *
- * 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  Settings
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
-
-/**
- * Settings for Twitter integration
- *
- * @category Settings
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- *
- * @see      SettingsAction
- */
-class TwittersettingsAction extends ProfileSettingsAction
-{
-    /**
-     * Title of the page
-     *
-     * @return string Title of the page
-     */
-
-    function title()
-    {
-        // TRANS: Title for page with Twitter integration settings.
-        return _m('Twitter settings');
-    }
-
-    /**
-     * Instructions for use
-     *
-     * @return instructions for use
-     */
-
-    function getInstructions()
-    {
-        // TRANS: Instructions for page with Twitter integration settings.
-        return _m('Connect your Twitter account to share your updates ' .
-                  'with your Twitter friends and vice-versa.');
-    }
-
-    /**
-     * Content area of the page
-     *
-     * Shows a form for associating a Twitter account with this
-     * StatusNet account. Also lets the user set preferences.
-     *
-     * @return void
-     */
-    function showContent()
-    {
-
-        $user = common_current_user();
-
-        $profile = $user->getProfile();
-
-        $fuser = null;
-
-        $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
-
-        if (!empty($flink)) {
-            $fuser = $flink->getForeignUser();
-        }
-
-        $this->elementStart('form', array('method' => 'post',
-                                          'id' => 'form_settings_twitter',
-                                          'class' => 'form_settings',
-                                          'action' =>
-                                          common_local_url('twittersettings')));
-
-        $this->hidden('token', common_session_token());
-
-        $this->elementStart('fieldset', array('id' => 'settings_twitter_account'));
-
-        if (empty($fuser)) {
-            $this->elementStart('ul', 'form_data');
-            $this->elementStart('li', array('id' => 'settings_twitter_login_button'));
-            $this->element('a', array('href' => common_local_url('twitterauthorization')),
-                           // TRANS: Link description to connect to a Twitter account.
-                           'Connect my Twitter account');
-            $this->elementEnd('li');
-            $this->elementEnd('ul');
-
-            $this->elementEnd('fieldset');
-        } else {
-            // TRANS: Fieldset legend.
-            $this->element('legend', null, _m('Twitter account'));
-            $this->elementStart('p', array('id' => 'form_confirmed'));
-            $this->element('a', array('href' => $fuser->uri), $fuser->nickname);
-            $this->elementEnd('p');
-            $this->element('p', 'form_note',
-                           // TRANS: Form note when a Twitter account has been connected.
-                           _m('Connected Twitter account'));
-            $this->elementEnd('fieldset');
-
-            $this->elementStart('fieldset');
-
-            // TRANS: Fieldset legend.
-            $this->element('legend', null, _m('Disconnect my account from Twitter'));
-
-            if (!$user->password) {
-                $this->elementStart('p', array('class' => 'form_guide'));
-                // TRANS: Form guide. %s is a URL to the password settings.
-                // TRANS: This message contains a Markdown link in the form [description](link).
-                $message = sprintf(_m('Disconnecting your Twitter account ' .
-                                      'could make it impossible to log in! Please ' .
-                                      '[set a password](%s) first.'),
-                                   common_local_url('passwordsettings'));
-                $message = common_markup_to_html($message);
-                $this->text($message);
-                $this->elementEnd('p');
-            } else {
-                // TRANS: Form instructions. %1$s is the StatusNet sitename.
-                $note = _m('Keep your %1$s account but disconnect from Twitter. ' .
-                    'You can use your %1$s password to log in.');
-                $site = common_config('site', 'name');
-
-                $this->element('p', 'instructions',
-                    sprintf($note, $site));
-
-                // TRANS: Button text for disconnecting a Twitter account.
-                $this->submit('disconnect', _m('BUTTON','Disconnect'));
-            }
-
-            $this->elementEnd('fieldset');
-
-            $this->elementStart('fieldset', array('id' => 'settings_twitter_preferences'));
-
-            // TRANS: Fieldset legend.
-            $this->element('legend', null, _m('Preferences'));
-            $this->elementStart('ul', 'form_data');
-            $this->elementStart('li');
-            $this->checkbox('noticesend',
-                            // TRANS: Checkbox label.
-                            _m('Automatically send my notices to Twitter.'),
-                            ($flink) ?
-                            ($flink->noticesync & FOREIGN_NOTICE_SEND) :
-                            true);
-            $this->elementEnd('li');
-            $this->elementStart('li');
-            $this->checkbox('replysync',
-                            // TRANS: Checkbox label.
-                            _m('Send local "@" replies to Twitter.'),
-                            ($flink) ?
-                            ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) :
-                            true);
-            $this->elementEnd('li');
-            $this->elementStart('li');
-            $this->checkbox('friendsync',
-                            // TRANS: Checkbox label.
-                            _m('Subscribe to my Twitter friends here.'),
-                            ($flink) ?
-                            ($flink->friendsync & FOREIGN_FRIEND_RECV) :
-                            false);
-            $this->elementEnd('li');
-
-            if (common_config('twitterimport','enabled')) {
-                $this->elementStart('li');
-                $this->checkbox('noticerecv',
-                                // TRANS: Checkbox label.
-                                _m('Import my friends timeline.'),
-                                ($flink) ?
-                                ($flink->noticesync & FOREIGN_NOTICE_RECV) :
-                                false);
-                $this->elementEnd('li');
-            } else {
-                // preserve setting even if bidrection bridge toggled off
-
-                if ($flink && ($flink->noticesync & FOREIGN_NOTICE_RECV)) {
-                    $this->hidden('noticerecv', true, 'noticerecv');
-                }
-            }
-
-            $this->elementEnd('ul');
-
-            if ($flink) {
-                // TRANS: Button text for saving Twitter integration settings.
-                $this->submit('save', _m('BUTTON','Save'));
-            } else {
-                // TRANS: Button text for adding Twitter integration.
-                $this->submit('add', _m('BUTTON','Add'));
-            }
-
-            $this->elementEnd('fieldset');
-        }
-
-        $this->elementEnd('form');
-    }
-
-    /**
-     * Handle posts to this form
-     *
-     * Based on the button that was pressed, muxes out to other functions
-     * to do the actual task requested.
-     *
-     * All sub-functions reload the form with a message -- success or failure.
-     *
-     * @return void
-     */
-    function handlePost()
-    {
-        // CSRF protection
-        $token = $this->trimmed('token');
-        if (!$token || $token != common_session_token()) {
-            // TRANS: Client error displayed when the session token does not match or is not given.
-            $this->showForm(_m('There was a problem with your session token. '.
-                               'Try again, please.'));
-            return;
-        }
-
-        if ($this->arg('save')) {
-            $this->savePreferences();
-        } else if ($this->arg('disconnect')) {
-            $this->removeTwitterAccount();
-        } else {
-            // TRANS: Client error displayed when the submitted form contains unexpected data.
-            $this->showForm(_m('Unexpected form submission.'));
-        }
-    }
-
-    /**
-     * Disassociate an existing Twitter account from this account
-     *
-     * @return void
-     */
-    function removeTwitterAccount()
-    {
-        $user = common_current_user();
-        $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
-
-        if (empty($flink)) {
-            // TRANS: Client error displayed when trying to remove a connected Twitter account when there isn't one connected.
-            $this->clientError(_m('No Twitter connection to remove.'));
-            return;
-        }
-
-        $result = $flink->safeDelete();
-
-        if (empty($result)) {
-            common_log_db_error($flink, 'DELETE', __FILE__);
-            // TRANS: Server error displayed when trying to remove a connected Twitter account fails.
-            $this->serverError(_m('Could not remove Twitter user.'));
-            return;
-        }
-
-        // TRANS: Success message displayed after disconnecting a Twitter account.
-        $this->showForm(_m('Twitter account disconnected.'), true);
-    }
-
-    /**
-     * Save user's Twitter-bridging preferences
-     *
-     * @return void
-     */
-    function savePreferences()
-    {
-        $noticesend = $this->boolean('noticesend');
-        $noticerecv = $this->boolean('noticerecv');
-        $friendsync = $this->boolean('friendsync');
-        $replysync  = $this->boolean('replysync');
-
-        $user = common_current_user();
-        $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
-
-        if (empty($flink)) {
-            common_log_db_error($flink, 'SELECT', __FILE__);
-            // @todo FIXME: Shouldn't this be a serverError()?
-            // TRANS: Server error displayed when saving Twitter integration preferences fails.
-            $this->showForm(_m('Could not save Twitter preferences.'));
-            return;
-        }
-
-        $original = clone($flink);
-        $wasReceiving = (bool)($original->noticesync & FOREIGN_NOTICE_RECV);
-        $flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync);
-        $result = $flink->update($original);
-
-        if ($result === false) {
-            common_log_db_error($flink, 'UPDATE', __FILE__);
-            // @todo FIXME: Shouldn't this be a serverError()?
-            // TRANS: Server error displayed when saving Twitter integration preferences fails.
-            $this->showForm(_m('Could not save Twitter preferences.'));
-            return;
-        }
-
-        if ($wasReceiving xor $noticerecv) {
-            $this->notifyDaemon($flink->foreign_id, $noticerecv);
-        }
-
-        // TRANS: Success message after saving Twitter integration preferences.
-        $this->showForm(_m('Twitter preferences saved.'), true);
-    }
-
-    /**
-     * Tell the import daemon that we've updated a user's receive status.
-     */
-    function notifyDaemon($twitterUserId, $receiving)
-    {
-        // @todo... should use control signals rather than queues
-    }
-}
diff --git a/plugins/TwitterBridge/twitterstreamreader.php b/plugins/TwitterBridge/twitterstreamreader.php
deleted file mode 100644 (file)
index 2441b13..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Plugin
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-/**
- * Base class for reading Twitter's User Streams and Site Streams
- * real-time streaming APIs.
- *
- * Caller can hook event callbacks for various types of messages;
- * the data from the stream and some context info will be passed
- * on to the callbacks.
- */
-abstract class TwitterStreamReader extends JsonStreamReader
-{
-    protected $callbacks = array();
-
-    function __construct(TwitterOAuthClient $auth, $baseUrl)
-    {
-        $this->baseUrl = $baseUrl;
-        $this->oauth = $auth;
-    }
-
-    public function connect($method, $params=array())
-    {
-        $url = $this->oAuthUrl($this->baseUrl . '/' . $method, $params);
-        return parent::connect($url);
-    }
-
-    /**
-     * Sign our target URL with OAuth auth stuff.
-     *
-     * @param string $url
-     * @param array $params
-     * @return string
-     */
-    protected function oAuthUrl($url, $params=array())
-    {
-        // In an ideal world this would be better encapsulated. :)
-        $request = OAuthRequest::from_consumer_and_token($this->oauth->consumer,
-            $this->oauth->token, 'GET', $url, $params);
-        $request->sign_request($this->oauth->sha1_method,
-            $this->oauth->consumer, $this->oauth->token);
-
-        return $request->to_url();
-    }
-
-    /**
-     * Add an event callback to receive notifications when things come in
-     * over the wire.
-     *
-     * Callbacks should be in the form: function(object $data, array $context)
-     * where $context may list additional data on some streams, such as the
-     * user to whom the message should be routed.
-     *
-     * Available events:
-     *
-     * Messaging:
-     *
-     * 'status': $data contains a status update in standard Twitter JSON format.
-     *      $data->user: sending user in standard Twitter JSON format.
-     *      $data->text... etc
-     *
-     * 'direct_message': $data contains a direct message in standard Twitter JSON format.
-     *      $data->sender: sending user in standard Twitter JSON format.
-     *      $data->recipient: receiving user in standard Twitter JSON format.
-     *      $data->text... etc
-     *
-     *
-     * Out of band events:
-     *
-     * 'follow': User has either started following someone, or is being followed.
-     *      $data->source: following user in standard Twitter JSON format.
-     *      $data->target: followed user in standard Twitter JSON format.
-     *
-     * 'favorite': Someone has favorited a status update.
-     *      $data->source: user doing the favoriting, in standard Twitter JSON format.
-     *      $data->target: user whose status was favorited, in standard Twitter JSON format.
-     *      $data->target_object: the favorited status update in standard Twitter JSON format.
-     *
-     * 'unfavorite': Someone has unfavorited a status update.
-     *      $data->source: user doing the unfavoriting, in standard Twitter JSON format.
-     *      $data->target: user whose status was unfavorited, in standard Twitter JSON format.
-     *      $data->target_object: the unfavorited status update in standard Twitter JSON format.
-     *
-     *
-     * Meta information:
-     *
-     * 'friends':
-     *      $data->friends: array of user IDs of the current user's friends.
-     *
-     * 'delete': Advisory that a Twitter status has been deleted; nice clients
-     *           should follow suit.
-     *      $data->id: ID of status being deleted
-     *      $data->user_id: ID of its owning user
-     *
-     * 'scrub_geo': Advisory that a user is clearing geo data from their status
-     *              stream; nice clients should follow suit.
-     *      $data->user_id: ID of user
-     *      $data->up_to_status_id: any notice older than this should be scrubbed.
-     *
-     * 'limit': Advisory that tracking has hit a resource limit.
-     *      $data->track
-     *
-     * 'raw': receives the full JSON data for all message types.
-     *
-     * @param string $event
-     * @param callable $callback
-     */
-    public function hookEvent($event, $callback)
-    {
-        $this->callbacks[$event][] = $callback;
-    }
-
-    /**
-     * Call event handler callbacks for the given event.
-     *
-     * @param string $event
-     * @param mixed $arg1 ... one or more params to pass on
-     */
-    protected function fireEvent($event, $arg1)
-    {
-        if (array_key_exists($event, $this->callbacks)) {
-            $args = array_slice(func_get_args(), 1);
-            foreach ($this->callbacks[$event] as $callback) {
-                call_user_func_array($callback, $args);
-            }
-        }
-    }
-
-    protected function handleJson(stdClass $data)
-    {
-        $this->routeMessage($data);
-    }
-
-    abstract protected function routeMessage(stdClass $data);
-
-    /**
-     * Send the decoded JSON object out to any event listeners.
-     *
-     * @param array $data
-     * @param array $context optional additional context data to pass on
-     */
-    protected function handleMessage(stdClass $data, array $context=array())
-    {
-        $this->fireEvent('raw', $data, $context);
-
-        if (isset($data->text)) {
-            $this->fireEvent('status', $data, $context);
-            return;
-        }
-        if (isset($data->event)) {
-            $this->fireEvent($data->event, $data, $context);
-            return;
-        }
-        if (isset($data->friends)) {
-            $this->fireEvent('friends', $data, $context);
-        }
-
-        $knownMeta = array('delete', 'scrub_geo', 'limit', 'direct_message');
-        foreach ($knownMeta as $key) {
-            if (isset($data->$key)) {
-                $this->fireEvent($key, $data->$key, $context);
-                return;
-            }
-        }
-    }
-}
-
-/**
- * Multiuser stream listener for Twitter Site Streams API
- * http://dev.twitter.com/pages/site_streams
- *
- * The site streams API allows listening to updates for multiple users.
- * Pass in the user IDs to listen to in via followUser() -- note they
- * must each have a valid OAuth token for the application ID we're
- * connecting as.
- *
- * You'll need to be connecting with the auth keys for the user who
- * owns the application registration.
- *
- * The user each message is destined for will be passed to event handlers
- * in $context['for_user_id'].
- */
-class TwitterSiteStream extends TwitterStreamReader
-{
-    protected $userIds;
-
-    public function __construct(TwitterOAuthClient $auth, $baseUrl='https://sitestream.twitter.com')
-    {
-        parent::__construct($auth, $baseUrl);
-    }
-
-    public function connect($method='2b/site.json')
-    {
-        $params = array();
-        if ($this->userIds) {
-            $params['follow'] = implode(',', $this->userIds);
-        }
-        return parent::connect($method, $params);
-    }
-
-    /**
-     * Set the users whose home streams should be pulled.
-     * They all must have valid oauth tokens for this application.
-     *
-     * Must be called before connect().
-     *
-     * @param array $userIds
-     */
-    function followUsers($userIds)
-    {
-        $this->userIds = $userIds;
-    }
-
-    /**
-     * Each message in the site stream tells us which user ID it should be
-     * routed to; we'll need that to let the caller know what to do.
-     *
-     * @param array $data
-     */
-    function routeMessage(stdClass $data)
-    {
-        $context = array(
-            'source' => 'sitestream',
-            'for_user' => $data->for_user
-        );
-        parent::handleMessage($data->message, $context);
-    }
-}
-
-/**
- * Stream listener for Twitter User Streams API
- * http://dev.twitter.com/pages/user_streams
- *
- * This will pull the home stream and additional events just for the user
- * we've authenticated as.
- */
-class TwitterUserStream extends TwitterStreamReader
-{
-    public function __construct(TwitterOAuthClient $auth, $baseUrl='https://userstream.twitter.com')
-    {
-        parent::__construct($auth, $baseUrl);
-    }
-
-    public function connect($method='2/user.json')
-    {
-        return parent::connect($method);
-    }
-
-    /**
-     * Each message in the user stream is just ready to go.
-     *
-     * @param array $data
-     */
-    function routeMessage(stdClass $data)
-    {
-        $context = array(
-            'source' => 'userstream'
-        );
-        parent::handleMessage($data, $context);
-    }
-}
index 1d8cccd54d5e8707e260015d4ab12efcfb54397b..22955a2ebc0ee19ee1929f99fb0fed5093e003de 100644 (file)
@@ -79,35 +79,6 @@ class UserFlagPlugin extends Plugin
         return true;
     }
 
-    /**
-     * Auto-load our classes if called
-     *
-     * @param string $cls Class to load
-     *
-     * @return boolean hook return
-     */
-    function onAutoload($cls)
-    {
-        switch (strtolower($cls))
-        {
-        case 'flagprofileaction':
-        case 'adminprofileflagaction':
-        case 'clearflagaction':
-            include_once INSTALLDIR.'/plugins/UserFlag/' .
-              strtolower(mb_substr($cls, 0, -6)) . '.php';
-            return false;
-        case 'flagprofileform':
-        case 'clearflagform':
-            include_once INSTALLDIR.'/plugins/UserFlag/' . strtolower($cls . '.php');
-            return false;
-        case 'user_flag_profile':
-            include_once INSTALLDIR.'/plugins/UserFlag/'.ucfirst(strtolower($cls)).'.php';
-            return false;
-        default:
-            return true;
-        }
-    }
-
     /**
      * Add a 'flag' button to profile page
      *
diff --git a/plugins/UserFlag/User_flag_profile.php b/plugins/UserFlag/User_flag_profile.php
deleted file mode 100644 (file)
index d0cdedc..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-/**
- * Data class for profile flags
- *
- * PHP version 5
- *
- * @category Data
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
-
-/**
- * Data class for profile flags
- *
- * A class representing a user flagging another profile for review.
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- */
-class User_flag_profile extends Managed_DataObject
-{
-    ###START_AUTOCODE
-    /* the code below is auto generated do not remove the above tag */
-
-    public $__table = 'user_flag_profile';               // table name
-    public $profile_id;                      // int(11)  primary_key not_null
-    public $user_id;                         // int(11)  primary_key not_null
-    public $cleared;                         // datetime   default_0000-00-00%2000%3A00%3A00
-    public $created;                         // datetime()   not_null
-    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    /* the code above is auto generated do not remove the tag below */
-    ###END_AUTOCODE
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile id flagged'),
-                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id of the actor'),
-                'cleared' => array('type' => 'datetime', 'description' => 'when flag was removed'),
-                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('profile_id', 'user_id'),
-            'indexes' => array(
-                'user_flag_profile_cleared_idx' => array('cleared'),
-                'user_flag_profile_created_idx' => array('created'),
-            ),
-        );
-    }
-
-    /**
-     * Check if a flag exists for given profile and user
-     *
-     * @param integer $profile_id Profile to check for
-     * @param integer $user_id    User to check for
-     *
-     * @return boolean true if exists, else false
-     */
-    static function exists($profile_id, $user_id)
-    {
-        $ufp = User_flag_profile::pkeyGet(array('profile_id' => $profile_id,
-                                                'user_id' => $user_id));
-
-        return !empty($ufp);
-    }
-
-    /**
-     * Create a new flag
-     *
-     * @param integer $user_id    ID of user who's flagging
-     * @param integer $profile_id ID of profile being flagged
-     *
-     * @return boolean success flag
-     */
-    static function create($user_id, $profile_id)
-    {
-        $ufp = new User_flag_profile();
-
-        $ufp->profile_id = $profile_id;
-        $ufp->user_id    = $user_id;
-        $ufp->created    = common_sql_now();
-
-        if (!$ufp->insert()) {
-            // TRANS: Server exception.
-            // TRANS: %d is a profile ID (number).
-            $msg = sprintf(_m('Could not flag profile "%d" for review.'),
-                           $profile_id);
-            throw new ServerException($msg);
-        }
-
-        $ufp->free();
-
-        return true;
-    }
-}
diff --git a/plugins/UserFlag/actions/adminprofileflag.php b/plugins/UserFlag/actions/adminprofileflag.php
new file mode 100644 (file)
index 0000000..b7538c6
--- /dev/null
@@ -0,0 +1,402 @@
+<?php
+/**
+ * Show latest and greatest profile flags
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Show the latest and greatest profile flags
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class AdminprofileflagAction extends Action
+{
+    var $page     = null;
+    var $profiles = null;
+
+    /**
+     * Take arguments for running
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     */
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $user = common_current_user();
+
+        // User must be logged in.
+
+        if (!common_logged_in()) {
+            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
+            $this->clientError(_m('Not logged in.'));
+            return;
+        }
+
+        $user = common_current_user();
+
+        // ...because they're logged in
+
+        assert(!empty($user));
+
+        // It must be a "real" login, not saved cookie login
+
+        if (!common_is_real_login()) {
+            // Cookie theft is too easy; we require automatic
+            // logins to re-authenticate before admining the site
+            common_set_returnto($this->selfUrl());
+            if (Event::handle('RedirectToLogin', array($this, $user))) {
+                common_redirect(common_local_url('login'), 303);
+            }
+        }
+
+        // User must have the right to review flags
+
+        if (!$user->hasRight(UserFlagPlugin::REVIEWFLAGS)) {
+            // TRANS: Error message displayed when trying to review profile flags while not authorised.
+            $this->clientError(_m('You cannot review profile flags.'));
+            return false;
+        }
+
+        $this->page = $this->trimmed('page');
+
+        if (empty($this->page)) {
+            $this->page = 1;
+        }
+
+        $this->profiles = $this->getProfiles();
+
+        return true;
+    }
+
+    /**
+     * Handle request
+     *
+     * @param array $args $_REQUEST args; handled in prepare()
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+
+        $this->showPage();
+    }
+
+    /**
+     * Title of this page
+     *
+     * @return string Title of the page
+     */
+    function title()
+    {
+        // TRANS: Title for page with a list of profiles that were flagged for review.
+        return _m('Flagged profiles');
+    }
+
+    /**
+     * save the profile flag
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        $pl = new FlaggedProfileList($this->profiles, $this);
+
+        $cnt = $pl->show();
+
+        $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
+                          $this->page, 'adminprofileflag');
+    }
+
+    /**
+     * Retrieve this action's profiles
+     *
+     * @return Profile $profile Profile query results
+     */
+    function getProfiles()
+    {
+        $ufp = new User_flag_profile();
+
+        $ufp->selectAdd();
+        $ufp->selectAdd('profile_id');
+        $ufp->selectAdd('count(*) as flag_count');
+
+        $ufp->whereAdd('cleared is NULL');
+
+        $ufp->groupBy('profile_id');
+        $ufp->orderBy('flag_count DESC, profile_id DESC');
+
+        $offset = ($this->page-1) * PROFILES_PER_PAGE;
+        $limit  = PROFILES_PER_PAGE + 1;
+
+        $ufp->limit($offset, $limit);
+
+        $profiles = array();
+
+        if ($ufp->find()) {
+            while ($ufp->fetch()) {
+                $profile = Profile::getKV('id', $ufp->profile_id);
+                if (!empty($profile)) {
+                    $profiles[] = $profile;
+                }
+            }
+        }
+
+        $ufp->free();
+
+        return new ArrayWrapper($profiles);
+    }
+}
+
+/**
+ * Specialization of ProfileList to show flagging information
+ *
+ * Most of the hard part is done in FlaggedProfileListItem.
+ *
+ * @category Widget
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class FlaggedProfileList extends ProfileList
+{
+    /**
+     * Factory method for creating new list items
+     *
+     * @param Profile $profile Profile to create an item for
+     *
+     * @return ProfileListItem newly-created item
+     */
+    function newListItem($profile)
+    {
+        return new FlaggedProfileListItem($this->profile, $this->action);
+    }
+}
+
+/**
+ * Specialization of ProfileListItem to show flagging information
+ *
+ * @category Widget
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class FlaggedProfileListItem extends ProfileListItem
+{
+    const MAX_FLAGGERS = 5;
+
+    var $user   = null;
+    var $r2args = null;
+
+    /**
+     * Overload parent's action list with our own moderation-oriented buttons
+     *
+     * @return void
+     */
+    function showActions()
+    {
+        $this->user = common_current_user();
+
+        list($action, $this->r2args) = $this->out->returnToArgs();
+
+        $this->r2args['action'] = $action;
+
+        $this->startActions();
+        if (Event::handle('StartProfileListItemActionElements', array($this))) {
+            $this->out->elementStart('li', 'entity_moderation');
+            // TRANS: Header for moderation menu with action buttons for flagged profiles (like 'sandbox', 'silence', ...).
+            $this->out->element('p', null, _m('Moderate'));
+            $this->out->elementStart('ul');
+            $this->showSandboxButton();
+            $this->showSilenceButton();
+            $this->showDeleteButton();
+            $this->showClearButton();
+            $this->out->elementEnd('ul');
+            $this->out->elementEnd('li');
+            Event::handle('EndProfileListItemActionElements', array($this));
+        }
+        $this->endActions();
+    }
+
+    /**
+     * Show a button to sandbox the profile
+     *
+     * @return void
+     */
+    function showSandboxButton()
+    {
+        if ($this->user->hasRight(Right::SANDBOXUSER)) {
+            $this->out->elementStart('li', 'entity_sandbox');
+            if ($this->profile->isSandboxed()) {
+                $usf = new UnSandboxForm($this->out, $this->profile, $this->r2args);
+                $usf->show();
+            } else {
+                $sf = new SandboxForm($this->out, $this->profile, $this->r2args);
+                $sf->show();
+            }
+            $this->out->elementEnd('li');
+        }
+    }
+
+    /**
+     * Show a button to silence the profile
+     *
+     * @return void
+     */
+    function showSilenceButton()
+    {
+        if ($this->user->hasRight(Right::SILENCEUSER)) {
+            $this->out->elementStart('li', 'entity_silence');
+            if ($this->profile->isSilenced()) {
+                $usf = new UnSilenceForm($this->out, $this->profile, $this->r2args);
+                $usf->show();
+            } else {
+                $sf = new SilenceForm($this->out, $this->profile, $this->r2args);
+                $sf->show();
+            }
+            $this->out->elementEnd('li');
+        }
+    }
+
+    /**
+     * Show a button to delete user and profile
+     *
+     * @return void
+     */
+    function showDeleteButton()
+    {
+
+        if ($this->user->hasRight(Right::DELETEUSER)) {
+            $this->out->elementStart('li', 'entity_delete');
+            $df = new DeleteUserForm($this->out, $this->profile, $this->r2args);
+            $df->show();
+            $this->out->elementEnd('li');
+        }
+    }
+
+    /**
+     * Show a button to clear flags
+     *
+     * @return void
+     */
+    function showClearButton()
+    {
+        if ($this->user->hasRight(UserFlagPlugin::CLEARFLAGS)) {
+            $this->out->elementStart('li', 'entity_clear');
+            $cf = new ClearFlagForm($this->out, $this->profile, $this->r2args);
+            $cf->show();
+            $this->out->elementEnd('li');
+        }
+    }
+
+    /**
+     * Overload parent function to add flaggers list
+     *
+     * @return void
+     */
+    function endProfile()
+    {
+        $this->showFlaggersList();
+        parent::endProfile();
+    }
+
+    /**
+     * Show a list of people who've flagged this profile
+     *
+     * @return void
+     */
+    function showFlaggersList()
+    {
+        $flaggers = array();
+
+        $ufp = new User_flag_profile();
+
+        $ufp->selectAdd();
+        $ufp->selectAdd('user_id');
+        $ufp->profile_id = $this->profile->id;
+        $ufp->orderBy('created');
+
+        if ($ufp->find()) { // XXX: this should always happen
+            while ($ufp->fetch()) {
+                $user = User::getKV('id', $ufp->user_id);
+                if (!empty($user)) { // XXX: this would also be unusual
+                    $flaggers[] = clone($user);
+                }
+            }
+        }
+
+        $cnt    = count($flaggers);
+        $others = 0;
+
+        if ($cnt > self::MAX_FLAGGERS) {
+            $flaggers = array_slice($flaggers, 0, self::MAX_FLAGGERS);
+            $others   = $cnt - self::MAX_FLAGGERS;
+        }
+
+        $lnks = array();
+
+        foreach ($flaggers as $flagger) {
+
+            $url = common_local_url('showstream',
+                                    array('nickname' => $flagger->nickname));
+
+            $lnks[] = XMLStringer::estring('a', array('href' => $url,
+                                                      'class' => 'flagger'),
+                                           $flagger->nickname);
+        }
+
+        if ($cnt > 0) {
+            if ($others > 0) {
+                $flagging_users = implode(', ', $lnks);
+                // TRANS: Message displayed on a profile if it has been flagged.
+                // TRANS: %1$s is a comma separated list of at most 5 user nicknames that flagged.
+                // TRANS: %2$d is a positive integer of additional flagging users. Also used for plural.
+                $text .= sprintf(_m('Flagged by %1$s and %2$d other', 'Flagged by %1$s and %2$d others', $others), $flagging_users, $others);
+            } else {
+                // TRANS: Message displayed on a profile if it has been flagged.
+                // TRANS: %s is a comma separated list of at most 5 user nicknames that flagged.
+                $text .= sprintf(_m('Flagged by %s'), $flagging_users);
+            }
+
+            $this->out->elementStart('p', array('class' => 'flaggers'));
+            $this->out->raw($text);
+            $this->out->elementEnd('p');
+        }
+    }
+}
diff --git a/plugins/UserFlag/actions/clearflag.php b/plugins/UserFlag/actions/clearflag.php
new file mode 100644 (file)
index 0000000..e5704d6
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+/**
+ * Clear all flags for a profile
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Action to clear flags for a profile
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class ClearflagAction extends ProfileFormAction
+{
+    /**
+     * Take arguments for running
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     */
+    function prepare($args)
+    {
+        if (!parent::prepare($args)) {
+            return false;
+        }
+
+        $user = common_current_user();
+
+        assert(!empty($user)); // checked above
+        assert(!empty($this->profile)); // checked above
+
+        return true;
+    }
+
+    /**
+     * Handle request
+     *
+     * Overriding the base Action's handle() here to deal check
+     * for Ajax and return an HXR response if necessary
+     *
+     * @param array $args $_REQUEST args; handled in prepare()
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $this->handlePost();
+            if (!$this->boolean('ajax')) {
+                $this->returnToPrevious();
+            }
+        }
+    }
+
+    /**
+     * Handle POST
+     *
+     * Executes the actions; deletes all flags
+     *
+     * @return void
+     */
+    function handlePost()
+    {
+        $ufp = new User_flag_profile();
+
+        $result = $ufp->query('UPDATE user_flag_profile ' .
+                              'SET cleared = now() ' .
+                              'WHERE cleared is null ' .
+                              'AND profile_id = ' . $this->profile->id);
+
+        if ($result == false) {
+            // TRANS: Server exception given when flags could not be cleared.
+            // TRANS: %s is a profile nickname.
+            $msg = sprintf(_m('Could not clear flags for profile "%s".'),
+                           $this->profile->nickname);
+            throw new ServerException($msg);
+        }
+
+        $ufp->free();
+
+        if ($this->boolean('ajax')) {
+            $this->ajaxResults();
+        }
+    }
+
+    /**
+     * Return results in ajax form
+     *
+     * @return void
+     */
+    function ajaxResults()
+    {
+        header('Content-Type: text/xml;charset=utf-8');
+        $this->xw->startDocument('1.0', 'UTF-8');
+        $this->elementStart('html');
+        $this->elementStart('head');
+        // TRANS: Title for AJAX form to indicated that flags were removed.
+        $this->element('title', null, _m('Flags cleared'));
+        $this->elementEnd('head');
+        $this->elementStart('body');
+        // TRANS: Body element for "flags cleared" form.
+        $this->element('p', 'cleared', _m('Cleared'));
+        $this->elementEnd('body');
+        $this->elementEnd('html');
+    }
+}
diff --git a/plugins/UserFlag/actions/flagprofile.php b/plugins/UserFlag/actions/flagprofile.php
new file mode 100644 (file)
index 0000000..7096d37
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+/**
+ * Add a flag to a profile
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Action to flag a profile.
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class FlagprofileAction extends ProfileFormAction
+{
+    /**
+     * Take arguments for running
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     */
+    function prepare($args)
+    {
+        if (!parent::prepare($args)) {
+            return false;
+        }
+
+        $user = common_current_user();
+
+        assert(!empty($user)); // checked above
+        assert(!empty($this->profile)); // checked above
+
+        return true;
+    }
+
+    /**
+     * Handle request
+     *
+     * Overriding the base Action's handle() here to deal check
+     * for Ajax and return an HXR response if necessary
+     *
+     * @param array $args $_REQUEST args; handled in prepare()
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $this->handlePost();
+            if (!$this->boolean('ajax')) {
+                $this->returnToPrevious();
+            }
+        }
+    }
+
+    /**
+     * Handle POST
+     *
+     * @return void
+     */
+    function handlePost()
+    {
+        $user = common_current_user();
+
+        assert(!empty($user));
+        assert(!empty($this->profile));
+
+        // throws an exception on error
+
+        if (User_flag_profile::exists($this->profile->id,
+                                      $user->id)) {
+            // We'll return to the profile page (or return the updated AJAX form)
+            // showing the current state, so no harm done.
+        } else {
+            User_flag_profile::create($user->id, $this->profile->id);
+        }
+
+        if ($this->boolean('ajax')) {
+            $this->ajaxResults();
+        }
+    }
+
+    /**
+     * Return results as AJAX message
+     *
+     * @return void
+     */
+    function ajaxResults()
+    {
+        header('Content-Type: text/xml;charset=utf-8');
+        $this->xw->startDocument('1.0', 'UTF-8');
+        $this->elementStart('html');
+        $this->elementStart('head');
+        // TRANS: AJAX form title for a flagged profile.
+        $this->element('title', null, _m('Flagged for review'));
+        $this->elementEnd('head');
+        $this->elementStart('body');
+        // TRANS: Body text for AJAX form when a profile has been flagged for review.
+        $this->element('p', 'flagged', _m('Flagged'));
+        $this->elementEnd('body');
+        $this->elementEnd('html');
+    }
+}
diff --git a/plugins/UserFlag/adminprofileflag.php b/plugins/UserFlag/adminprofileflag.php
deleted file mode 100644 (file)
index b7538c6..0000000
+++ /dev/null
@@ -1,402 +0,0 @@
-<?php
-/**
- * Show latest and greatest profile flags
- *
- * PHP version 5
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Show the latest and greatest profile flags
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- */
-class AdminprofileflagAction extends Action
-{
-    var $page     = null;
-    var $profiles = null;
-
-    /**
-     * Take arguments for running
-     *
-     * @param array $args $_REQUEST args
-     *
-     * @return boolean success flag
-     */
-    function prepare($args)
-    {
-        parent::prepare($args);
-
-        $user = common_current_user();
-
-        // User must be logged in.
-
-        if (!common_logged_in()) {
-            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
-            $this->clientError(_m('Not logged in.'));
-            return;
-        }
-
-        $user = common_current_user();
-
-        // ...because they're logged in
-
-        assert(!empty($user));
-
-        // It must be a "real" login, not saved cookie login
-
-        if (!common_is_real_login()) {
-            // Cookie theft is too easy; we require automatic
-            // logins to re-authenticate before admining the site
-            common_set_returnto($this->selfUrl());
-            if (Event::handle('RedirectToLogin', array($this, $user))) {
-                common_redirect(common_local_url('login'), 303);
-            }
-        }
-
-        // User must have the right to review flags
-
-        if (!$user->hasRight(UserFlagPlugin::REVIEWFLAGS)) {
-            // TRANS: Error message displayed when trying to review profile flags while not authorised.
-            $this->clientError(_m('You cannot review profile flags.'));
-            return false;
-        }
-
-        $this->page = $this->trimmed('page');
-
-        if (empty($this->page)) {
-            $this->page = 1;
-        }
-
-        $this->profiles = $this->getProfiles();
-
-        return true;
-    }
-
-    /**
-     * Handle request
-     *
-     * @param array $args $_REQUEST args; handled in prepare()
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        parent::handle($args);
-
-        $this->showPage();
-    }
-
-    /**
-     * Title of this page
-     *
-     * @return string Title of the page
-     */
-    function title()
-    {
-        // TRANS: Title for page with a list of profiles that were flagged for review.
-        return _m('Flagged profiles');
-    }
-
-    /**
-     * save the profile flag
-     *
-     * @return void
-     */
-    function showContent()
-    {
-        $pl = new FlaggedProfileList($this->profiles, $this);
-
-        $cnt = $pl->show();
-
-        $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
-                          $this->page, 'adminprofileflag');
-    }
-
-    /**
-     * Retrieve this action's profiles
-     *
-     * @return Profile $profile Profile query results
-     */
-    function getProfiles()
-    {
-        $ufp = new User_flag_profile();
-
-        $ufp->selectAdd();
-        $ufp->selectAdd('profile_id');
-        $ufp->selectAdd('count(*) as flag_count');
-
-        $ufp->whereAdd('cleared is NULL');
-
-        $ufp->groupBy('profile_id');
-        $ufp->orderBy('flag_count DESC, profile_id DESC');
-
-        $offset = ($this->page-1) * PROFILES_PER_PAGE;
-        $limit  = PROFILES_PER_PAGE + 1;
-
-        $ufp->limit($offset, $limit);
-
-        $profiles = array();
-
-        if ($ufp->find()) {
-            while ($ufp->fetch()) {
-                $profile = Profile::getKV('id', $ufp->profile_id);
-                if (!empty($profile)) {
-                    $profiles[] = $profile;
-                }
-            }
-        }
-
-        $ufp->free();
-
-        return new ArrayWrapper($profiles);
-    }
-}
-
-/**
- * Specialization of ProfileList to show flagging information
- *
- * Most of the hard part is done in FlaggedProfileListItem.
- *
- * @category Widget
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- */
-class FlaggedProfileList extends ProfileList
-{
-    /**
-     * Factory method for creating new list items
-     *
-     * @param Profile $profile Profile to create an item for
-     *
-     * @return ProfileListItem newly-created item
-     */
-    function newListItem($profile)
-    {
-        return new FlaggedProfileListItem($this->profile, $this->action);
-    }
-}
-
-/**
- * Specialization of ProfileListItem to show flagging information
- *
- * @category Widget
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- */
-class FlaggedProfileListItem extends ProfileListItem
-{
-    const MAX_FLAGGERS = 5;
-
-    var $user   = null;
-    var $r2args = null;
-
-    /**
-     * Overload parent's action list with our own moderation-oriented buttons
-     *
-     * @return void
-     */
-    function showActions()
-    {
-        $this->user = common_current_user();
-
-        list($action, $this->r2args) = $this->out->returnToArgs();
-
-        $this->r2args['action'] = $action;
-
-        $this->startActions();
-        if (Event::handle('StartProfileListItemActionElements', array($this))) {
-            $this->out->elementStart('li', 'entity_moderation');
-            // TRANS: Header for moderation menu with action buttons for flagged profiles (like 'sandbox', 'silence', ...).
-            $this->out->element('p', null, _m('Moderate'));
-            $this->out->elementStart('ul');
-            $this->showSandboxButton();
-            $this->showSilenceButton();
-            $this->showDeleteButton();
-            $this->showClearButton();
-            $this->out->elementEnd('ul');
-            $this->out->elementEnd('li');
-            Event::handle('EndProfileListItemActionElements', array($this));
-        }
-        $this->endActions();
-    }
-
-    /**
-     * Show a button to sandbox the profile
-     *
-     * @return void
-     */
-    function showSandboxButton()
-    {
-        if ($this->user->hasRight(Right::SANDBOXUSER)) {
-            $this->out->elementStart('li', 'entity_sandbox');
-            if ($this->profile->isSandboxed()) {
-                $usf = new UnSandboxForm($this->out, $this->profile, $this->r2args);
-                $usf->show();
-            } else {
-                $sf = new SandboxForm($this->out, $this->profile, $this->r2args);
-                $sf->show();
-            }
-            $this->out->elementEnd('li');
-        }
-    }
-
-    /**
-     * Show a button to silence the profile
-     *
-     * @return void
-     */
-    function showSilenceButton()
-    {
-        if ($this->user->hasRight(Right::SILENCEUSER)) {
-            $this->out->elementStart('li', 'entity_silence');
-            if ($this->profile->isSilenced()) {
-                $usf = new UnSilenceForm($this->out, $this->profile, $this->r2args);
-                $usf->show();
-            } else {
-                $sf = new SilenceForm($this->out, $this->profile, $this->r2args);
-                $sf->show();
-            }
-            $this->out->elementEnd('li');
-        }
-    }
-
-    /**
-     * Show a button to delete user and profile
-     *
-     * @return void
-     */
-    function showDeleteButton()
-    {
-
-        if ($this->user->hasRight(Right::DELETEUSER)) {
-            $this->out->elementStart('li', 'entity_delete');
-            $df = new DeleteUserForm($this->out, $this->profile, $this->r2args);
-            $df->show();
-            $this->out->elementEnd('li');
-        }
-    }
-
-    /**
-     * Show a button to clear flags
-     *
-     * @return void
-     */
-    function showClearButton()
-    {
-        if ($this->user->hasRight(UserFlagPlugin::CLEARFLAGS)) {
-            $this->out->elementStart('li', 'entity_clear');
-            $cf = new ClearFlagForm($this->out, $this->profile, $this->r2args);
-            $cf->show();
-            $this->out->elementEnd('li');
-        }
-    }
-
-    /**
-     * Overload parent function to add flaggers list
-     *
-     * @return void
-     */
-    function endProfile()
-    {
-        $this->showFlaggersList();
-        parent::endProfile();
-    }
-
-    /**
-     * Show a list of people who've flagged this profile
-     *
-     * @return void
-     */
-    function showFlaggersList()
-    {
-        $flaggers = array();
-
-        $ufp = new User_flag_profile();
-
-        $ufp->selectAdd();
-        $ufp->selectAdd('user_id');
-        $ufp->profile_id = $this->profile->id;
-        $ufp->orderBy('created');
-
-        if ($ufp->find()) { // XXX: this should always happen
-            while ($ufp->fetch()) {
-                $user = User::getKV('id', $ufp->user_id);
-                if (!empty($user)) { // XXX: this would also be unusual
-                    $flaggers[] = clone($user);
-                }
-            }
-        }
-
-        $cnt    = count($flaggers);
-        $others = 0;
-
-        if ($cnt > self::MAX_FLAGGERS) {
-            $flaggers = array_slice($flaggers, 0, self::MAX_FLAGGERS);
-            $others   = $cnt - self::MAX_FLAGGERS;
-        }
-
-        $lnks = array();
-
-        foreach ($flaggers as $flagger) {
-
-            $url = common_local_url('showstream',
-                                    array('nickname' => $flagger->nickname));
-
-            $lnks[] = XMLStringer::estring('a', array('href' => $url,
-                                                      'class' => 'flagger'),
-                                           $flagger->nickname);
-        }
-
-        if ($cnt > 0) {
-            if ($others > 0) {
-                $flagging_users = implode(', ', $lnks);
-                // TRANS: Message displayed on a profile if it has been flagged.
-                // TRANS: %1$s is a comma separated list of at most 5 user nicknames that flagged.
-                // TRANS: %2$d is a positive integer of additional flagging users. Also used for plural.
-                $text .= sprintf(_m('Flagged by %1$s and %2$d other', 'Flagged by %1$s and %2$d others', $others), $flagging_users, $others);
-            } else {
-                // TRANS: Message displayed on a profile if it has been flagged.
-                // TRANS: %s is a comma separated list of at most 5 user nicknames that flagged.
-                $text .= sprintf(_m('Flagged by %s'), $flagging_users);
-            }
-
-            $this->out->elementStart('p', array('class' => 'flaggers'));
-            $this->out->raw($text);
-            $this->out->elementEnd('p');
-        }
-    }
-}
diff --git a/plugins/UserFlag/classes/User_flag_profile.php b/plugins/UserFlag/classes/User_flag_profile.php
new file mode 100644 (file)
index 0000000..d0cdedc
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+/**
+ * Data class for profile flags
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for profile flags
+ *
+ * A class representing a user flagging another profile for review.
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class User_flag_profile extends Managed_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'user_flag_profile';               // table name
+    public $profile_id;                      // int(11)  primary_key not_null
+    public $user_id;                         // int(11)  primary_key not_null
+    public $cleared;                         // datetime   default_0000-00-00%2000%3A00%3A00
+    public $created;                         // datetime()   not_null
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile id flagged'),
+                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id of the actor'),
+                'cleared' => array('type' => 'datetime', 'description' => 'when flag was removed'),
+                'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('profile_id', 'user_id'),
+            'indexes' => array(
+                'user_flag_profile_cleared_idx' => array('cleared'),
+                'user_flag_profile_created_idx' => array('created'),
+            ),
+        );
+    }
+
+    /**
+     * Check if a flag exists for given profile and user
+     *
+     * @param integer $profile_id Profile to check for
+     * @param integer $user_id    User to check for
+     *
+     * @return boolean true if exists, else false
+     */
+    static function exists($profile_id, $user_id)
+    {
+        $ufp = User_flag_profile::pkeyGet(array('profile_id' => $profile_id,
+                                                'user_id' => $user_id));
+
+        return !empty($ufp);
+    }
+
+    /**
+     * Create a new flag
+     *
+     * @param integer $user_id    ID of user who's flagging
+     * @param integer $profile_id ID of profile being flagged
+     *
+     * @return boolean success flag
+     */
+    static function create($user_id, $profile_id)
+    {
+        $ufp = new User_flag_profile();
+
+        $ufp->profile_id = $profile_id;
+        $ufp->user_id    = $user_id;
+        $ufp->created    = common_sql_now();
+
+        if (!$ufp->insert()) {
+            // TRANS: Server exception.
+            // TRANS: %d is a profile ID (number).
+            $msg = sprintf(_m('Could not flag profile "%d" for review.'),
+                           $profile_id);
+            throw new ServerException($msg);
+        }
+
+        $ufp->free();
+
+        return true;
+    }
+}
diff --git a/plugins/UserFlag/clearflag.php b/plugins/UserFlag/clearflag.php
deleted file mode 100644 (file)
index e5704d6..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-<?php
-/**
- * Clear all flags for a profile
- *
- * PHP version 5
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Action to clear flags for a profile
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- */
-class ClearflagAction extends ProfileFormAction
-{
-    /**
-     * Take arguments for running
-     *
-     * @param array $args $_REQUEST args
-     *
-     * @return boolean success flag
-     */
-    function prepare($args)
-    {
-        if (!parent::prepare($args)) {
-            return false;
-        }
-
-        $user = common_current_user();
-
-        assert(!empty($user)); // checked above
-        assert(!empty($this->profile)); // checked above
-
-        return true;
-    }
-
-    /**
-     * Handle request
-     *
-     * Overriding the base Action's handle() here to deal check
-     * for Ajax and return an HXR response if necessary
-     *
-     * @param array $args $_REQUEST args; handled in prepare()
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-            $this->handlePost();
-            if (!$this->boolean('ajax')) {
-                $this->returnToPrevious();
-            }
-        }
-    }
-
-    /**
-     * Handle POST
-     *
-     * Executes the actions; deletes all flags
-     *
-     * @return void
-     */
-    function handlePost()
-    {
-        $ufp = new User_flag_profile();
-
-        $result = $ufp->query('UPDATE user_flag_profile ' .
-                              'SET cleared = now() ' .
-                              'WHERE cleared is null ' .
-                              'AND profile_id = ' . $this->profile->id);
-
-        if ($result == false) {
-            // TRANS: Server exception given when flags could not be cleared.
-            // TRANS: %s is a profile nickname.
-            $msg = sprintf(_m('Could not clear flags for profile "%s".'),
-                           $this->profile->nickname);
-            throw new ServerException($msg);
-        }
-
-        $ufp->free();
-
-        if ($this->boolean('ajax')) {
-            $this->ajaxResults();
-        }
-    }
-
-    /**
-     * Return results in ajax form
-     *
-     * @return void
-     */
-    function ajaxResults()
-    {
-        header('Content-Type: text/xml;charset=utf-8');
-        $this->xw->startDocument('1.0', 'UTF-8');
-        $this->elementStart('html');
-        $this->elementStart('head');
-        // TRANS: Title for AJAX form to indicated that flags were removed.
-        $this->element('title', null, _m('Flags cleared'));
-        $this->elementEnd('head');
-        $this->elementStart('body');
-        // TRANS: Body element for "flags cleared" form.
-        $this->element('p', 'cleared', _m('Cleared'));
-        $this->elementEnd('body');
-        $this->elementEnd('html');
-    }
-}
diff --git a/plugins/UserFlag/clearflagform.php b/plugins/UserFlag/clearflagform.php
deleted file mode 100644 (file)
index 47dd392..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Form for clearing profile flags
- *
- * 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  Form
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/form.php';
-
-/**
- * Form for clearing profile flags
- *
- * @category Form
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class ClearFlagForm extends ProfileActionForm
-{
-    /**
-     * class of the form
-     * Action this form provides
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_user_clearflag';
-    }
-
-    /**
-     * Action this form provides
-     *
-     * @return string Name of the action, lowercased.
-     */
-    function target()
-    {
-        return 'clearflag';
-    }
-
-    /**
-     * Title of the form
-     *
-     * @return string Title of the form, internationalized
-     */
-    function title()
-    {
-        // TRANS: Form title for action on a profile.
-        return _m('Clear');
-    }
-
-    /**
-     * Description of the form
-     *
-     * @return string description of the form, internationalized
-     */
-
-    function description()
-    {
-        // TRANS: Form description for clearing flags from a profile.
-        return _m('Clear all flags');
-    }
-}
diff --git a/plugins/UserFlag/flagprofile.php b/plugins/UserFlag/flagprofile.php
deleted file mode 100644 (file)
index 7096d37..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-/**
- * Add a flag to a profile
- *
- * PHP version 5
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-/**
- * Action to flag a profile.
- *
- * @category Action
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://status.net/
- */
-class FlagprofileAction extends ProfileFormAction
-{
-    /**
-     * Take arguments for running
-     *
-     * @param array $args $_REQUEST args
-     *
-     * @return boolean success flag
-     */
-    function prepare($args)
-    {
-        if (!parent::prepare($args)) {
-            return false;
-        }
-
-        $user = common_current_user();
-
-        assert(!empty($user)); // checked above
-        assert(!empty($this->profile)); // checked above
-
-        return true;
-    }
-
-    /**
-     * Handle request
-     *
-     * Overriding the base Action's handle() here to deal check
-     * for Ajax and return an HXR response if necessary
-     *
-     * @param array $args $_REQUEST args; handled in prepare()
-     *
-     * @return void
-     */
-    function handle($args)
-    {
-        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-            $this->handlePost();
-            if (!$this->boolean('ajax')) {
-                $this->returnToPrevious();
-            }
-        }
-    }
-
-    /**
-     * Handle POST
-     *
-     * @return void
-     */
-    function handlePost()
-    {
-        $user = common_current_user();
-
-        assert(!empty($user));
-        assert(!empty($this->profile));
-
-        // throws an exception on error
-
-        if (User_flag_profile::exists($this->profile->id,
-                                      $user->id)) {
-            // We'll return to the profile page (or return the updated AJAX form)
-            // showing the current state, so no harm done.
-        } else {
-            User_flag_profile::create($user->id, $this->profile->id);
-        }
-
-        if ($this->boolean('ajax')) {
-            $this->ajaxResults();
-        }
-    }
-
-    /**
-     * Return results as AJAX message
-     *
-     * @return void
-     */
-    function ajaxResults()
-    {
-        header('Content-Type: text/xml;charset=utf-8');
-        $this->xw->startDocument('1.0', 'UTF-8');
-        $this->elementStart('html');
-        $this->elementStart('head');
-        // TRANS: AJAX form title for a flagged profile.
-        $this->element('title', null, _m('Flagged for review'));
-        $this->elementEnd('head');
-        $this->elementStart('body');
-        // TRANS: Body text for AJAX form when a profile has been flagged for review.
-        $this->element('p', 'flagged', _m('Flagged'));
-        $this->elementEnd('body');
-        $this->elementEnd('html');
-    }
-}
diff --git a/plugins/UserFlag/flagprofileform.php b/plugins/UserFlag/flagprofileform.php
deleted file mode 100644 (file)
index acb4762..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Form for flagging a profile
- *
- * 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  Form
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/form.php';
-
-/**
- * Form for flagging a profile
- *
- * A form for flagging a profile
- *
- * @category Form
- * @package  StatusNet
- * @author   Evan Prodromou <evan@status.net>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://status.net/
- */
-class FlagProfileForm extends ProfileActionForm
-{
-    /**
-     * class of the form
-     * Action this form provides
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        return 'form_entity_flag ajax';
-    }
-
-    /**
-     * Action this form provides
-     *
-     * @return string Name of the action, lowercased.
-     */
-    function target()
-    {
-        return 'flagprofile';
-    }
-
-    /**
-     * Title of the form
-     *
-     * @return string Title of the form, internationalized
-     */
-    function title()
-    {
-        // TRANS: Form title for flagging a profile for review.
-        return _m('Flag');
-    }
-
-    /**
-     * Description of the form
-     *
-     * @return string description of the form, internationalized
-     */
-    function description()
-    {
-        // TRANS: Form description.
-        return _m('Flag profile for review.');
-    }
-}
diff --git a/plugins/UserFlag/forms/clearflag.php b/plugins/UserFlag/forms/clearflag.php
new file mode 100644 (file)
index 0000000..47dd392
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for clearing profile flags
+ *
+ * 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  Form
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/lib/form.php';
+
+/**
+ * Form for clearing profile flags
+ *
+ * @category Form
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class ClearFlagForm extends ProfileActionForm
+{
+    /**
+     * class of the form
+     * Action this form provides
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_user_clearflag';
+    }
+
+    /**
+     * Action this form provides
+     *
+     * @return string Name of the action, lowercased.
+     */
+    function target()
+    {
+        return 'clearflag';
+    }
+
+    /**
+     * Title of the form
+     *
+     * @return string Title of the form, internationalized
+     */
+    function title()
+    {
+        // TRANS: Form title for action on a profile.
+        return _m('Clear');
+    }
+
+    /**
+     * Description of the form
+     *
+     * @return string description of the form, internationalized
+     */
+
+    function description()
+    {
+        // TRANS: Form description for clearing flags from a profile.
+        return _m('Clear all flags');
+    }
+}
diff --git a/plugins/UserFlag/forms/flagprofile.php b/plugins/UserFlag/forms/flagprofile.php
new file mode 100644 (file)
index 0000000..acb4762
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for flagging a profile
+ *
+ * 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  Form
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/lib/form.php';
+
+/**
+ * Form for flagging a profile
+ *
+ * A form for flagging a profile
+ *
+ * @category Form
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+class FlagProfileForm extends ProfileActionForm
+{
+    /**
+     * class of the form
+     * Action this form provides
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        return 'form_entity_flag ajax';
+    }
+
+    /**
+     * Action this form provides
+     *
+     * @return string Name of the action, lowercased.
+     */
+    function target()
+    {
+        return 'flagprofile';
+    }
+
+    /**
+     * Title of the form
+     *
+     * @return string Title of the form, internationalized
+     */
+    function title()
+    {
+        // TRANS: Form title for flagging a profile for review.
+        return _m('Flag');
+    }
+
+    /**
+     * Description of the form
+     *
+     * @return string description of the form, internationalized
+     */
+    function description()
+    {
+        // TRANS: Form description.
+        return _m('Flag profile for review.');
+    }
+}
diff --git a/plugins/Xmpp/Queued_XMPP.php b/plugins/Xmpp/Queued_XMPP.php
deleted file mode 100644 (file)
index aeb7584..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Queue-mediated proxy class for outgoing XMPP messages.
- *
- * 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  Network
- * @package   StatusNet
- * @author    Brion Vibber <brion@status.net>
- * @copyright 2010 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-class Queued_XMPP extends XMPPHP_XMPP
-{
-    /**
-     * Reference to the XmppPlugin object we're hooked up to.
-     */
-    public $plugin;
-
-       /**
-        * Constructor
-        *
-     * @param XmppPlugin $plugin
-        * @param string  $host
-        * @param integer $port
-        * @param string  $user
-        * @param string  $password
-        * @param string  $resource
-        * @param string  $server
-        * @param boolean $printlog
-        * @param string  $loglevel
-        */
-       public function __construct($plugin, $host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null)
-       {
-        $this->plugin = $plugin;
-
-        parent::__construct($host, $port, $user, $password, $resource, $server, $printlog, $loglevel);
-
-        // We use $host to connect, but $server to build JIDs if specified.
-        // This seems to fix an upstream bug where $host was used to build
-        // $this->basejid, never seen since it isn't actually used in the base
-        // classes.
-        if (!$server) {
-            $server = $this->host;
-        }
-        $this->basejid = $this->user . '@' . $server;
-
-        // Normally the fulljid is filled out by the server at resource binding
-        // time, but we need to do it since we're not talking to a real server.
-        $this->fulljid = "{$this->basejid}/{$this->resource}";
-    }
-
-    /**
-     * Send a formatted message to the outgoing queue for later forwarding
-     * to a real XMPP connection.
-     *
-     * @param string $msg
-     */
-    public function send($msg, $timeout=NULL)
-    {
-        $this->plugin->enqueueOutgoingRaw($msg);
-    }
-
-    //@{
-    /**
-     * Stream i/o functions disabled; only do output
-     */
-    public function connect($timeout = 30, $persistent = false, $sendinit = true)
-    {
-        // No i18n needed. Test message.
-        throw new Exception('Cannot connect to server from fake XMPP.');
-    }
-
-    public function disconnect()
-    {
-        // No i18n needed. Test message.
-        throw new Exception('Cannot connect to server from fake XMPP.');
-    }
-
-    public function process()
-    {
-        // No i18n needed. Test message.
-        throw new Exception('Cannot read stream from fake XMPP.');
-    }
-
-    public function processUntil($event, $timeout=-1)
-    {
-        // No i18n needed. Test message.
-        throw new Exception('Cannot read stream from fake XMPP.');
-    }
-
-    public function read()
-    {
-        // No i18n needed. Test message.
-        throw new Exception('Cannot read stream from fake XMPP.');
-    }
-
-    public function readyToProcess()
-    {
-        // No i18n needed. Test message.
-        throw new Exception('Cannot read stream from fake XMPP.');
-    }
-    //@}
-}
diff --git a/plugins/Xmpp/Sharing_XMPP.php b/plugins/Xmpp/Sharing_XMPP.php
deleted file mode 100644 (file)
index 4b69125..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-/**
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * Send and receive notices using the Jabber network
- *
- * PHP version 5
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Jabber
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2009 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET')) {
-    // This check helps protect against security problems;
-    // your code file can't be executed directly from the web.
-    exit(1);
-}
-
-class Sharing_XMPP extends XMPPHP_XMPP
-{
-    function getSocket()
-    {
-        return $this->socket;
-    }
-}
index f56d33f0a3c7ba180b7100d29d8a508be61bf05b..61b299865691bfa46d1a272cbcfb8f5d80f7b9f3 100644 (file)
@@ -296,16 +296,9 @@ class XmppPlugin extends ImPlugin
         case 'XMPPHP_XMPP':
             require_once $dir . '/extlib/XMPPHP/XMPP.php';
             return false;
-        case 'Sharing_XMPP':
-        case 'Queued_XMPP':
-            require_once $dir . '/'.$cls.'.php';
-            return false;
-        case 'XmppManager':
-            require_once $dir . '/'.strtolower($cls).'.php';
-            return false;
-        default:
-            return true;
         }
+
+        return parent::onAutoload($cls);
     }
 
     function onStartImDaemonIoManagers(&$classes)
diff --git a/plugins/Xmpp/lib/queued_xmpp.php b/plugins/Xmpp/lib/queued_xmpp.php
new file mode 100644 (file)
index 0000000..aeb7584
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Queue-mediated proxy class for outgoing XMPP messages.
+ *
+ * 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  Network
+ * @package   StatusNet
+ * @author    Brion Vibber <brion@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+class Queued_XMPP extends XMPPHP_XMPP
+{
+    /**
+     * Reference to the XmppPlugin object we're hooked up to.
+     */
+    public $plugin;
+
+       /**
+        * Constructor
+        *
+     * @param XmppPlugin $plugin
+        * @param string  $host
+        * @param integer $port
+        * @param string  $user
+        * @param string  $password
+        * @param string  $resource
+        * @param string  $server
+        * @param boolean $printlog
+        * @param string  $loglevel
+        */
+       public function __construct($plugin, $host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null)
+       {
+        $this->plugin = $plugin;
+
+        parent::__construct($host, $port, $user, $password, $resource, $server, $printlog, $loglevel);
+
+        // We use $host to connect, but $server to build JIDs if specified.
+        // This seems to fix an upstream bug where $host was used to build
+        // $this->basejid, never seen since it isn't actually used in the base
+        // classes.
+        if (!$server) {
+            $server = $this->host;
+        }
+        $this->basejid = $this->user . '@' . $server;
+
+        // Normally the fulljid is filled out by the server at resource binding
+        // time, but we need to do it since we're not talking to a real server.
+        $this->fulljid = "{$this->basejid}/{$this->resource}";
+    }
+
+    /**
+     * Send a formatted message to the outgoing queue for later forwarding
+     * to a real XMPP connection.
+     *
+     * @param string $msg
+     */
+    public function send($msg, $timeout=NULL)
+    {
+        $this->plugin->enqueueOutgoingRaw($msg);
+    }
+
+    //@{
+    /**
+     * Stream i/o functions disabled; only do output
+     */
+    public function connect($timeout = 30, $persistent = false, $sendinit = true)
+    {
+        // No i18n needed. Test message.
+        throw new Exception('Cannot connect to server from fake XMPP.');
+    }
+
+    public function disconnect()
+    {
+        // No i18n needed. Test message.
+        throw new Exception('Cannot connect to server from fake XMPP.');
+    }
+
+    public function process()
+    {
+        // No i18n needed. Test message.
+        throw new Exception('Cannot read stream from fake XMPP.');
+    }
+
+    public function processUntil($event, $timeout=-1)
+    {
+        // No i18n needed. Test message.
+        throw new Exception('Cannot read stream from fake XMPP.');
+    }
+
+    public function read()
+    {
+        // No i18n needed. Test message.
+        throw new Exception('Cannot read stream from fake XMPP.');
+    }
+
+    public function readyToProcess()
+    {
+        // No i18n needed. Test message.
+        throw new Exception('Cannot read stream from fake XMPP.');
+    }
+    //@}
+}
diff --git a/plugins/Xmpp/lib/sharing_xmpp.php b/plugins/Xmpp/lib/sharing_xmpp.php
new file mode 100644 (file)
index 0000000..4b69125
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * Send and receive notices using the Jabber network
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Jabber
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+class Sharing_XMPP extends XMPPHP_XMPP
+{
+    function getSocket()
+    {
+        return $this->socket;
+    }
+}
diff --git a/plugins/Xmpp/lib/xmppmanager.php b/plugins/Xmpp/lib/xmppmanager.php
new file mode 100644 (file)
index 0000000..c7d4c15
--- /dev/null
@@ -0,0 +1,279 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+/**
+ * XMPP background connection manager for XMPP-using queue handlers,
+ * allowing them to send outgoing messages on the right connection.
+ *
+ * Input is handled during socket select loop, keepalive pings during idle.
+ * Any incoming messages will be handled.
+ *
+ * In a multi-site queuedaemon.php run, one connection will be instantiated
+ * for each site being handled by the current process that has XMPP enabled.
+ */
+class XmppManager extends ImManager
+{
+    protected $lastping = null;
+    protected $pingid = null;
+
+    public $conn = null;
+
+    const PING_INTERVAL = 120;
+
+    /**
+     * Initialize connection to server.
+     * @return boolean true on success
+     */
+    public function start($master)
+    {
+        if(parent::start($master))
+        {
+            $this->connect();
+            return true;
+        }else{
+            return false;
+        }
+    }
+
+    function send_raw_message($data)
+    {
+        $this->connect();
+        if (!$this->conn || $this->conn->isDisconnected()) {
+            return false;
+        }
+        $this->conn->send($data);
+        return true;
+    }
+
+    /**
+     * Message pump is triggered on socket input, so we only need an idle()
+     * call often enough to trigger our outgoing pings.
+     */
+    function timeout()
+    {
+        return self::PING_INTERVAL;
+    }
+
+    /**
+     * Process XMPP events that have come in over the wire.
+     * @fixme may kill process on XMPP error
+     * @param resource $socket
+     */
+    public function handleInput($socket)
+    {
+        // Process the queue for as long as needed
+        try {
+            common_log(LOG_DEBUG, "Servicing the XMPP queue.");
+            $this->stats('xmpp_process');
+            $this->conn->processTime(0);
+        } catch (XMPPHP_Exception $e) {
+            common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
+            die($e->getMessage());
+        }
+    }
+
+    /**
+     * Lists the IM connection socket to allow i/o master to wake
+     * when input comes in here as well as from the queue source.
+     *
+     * @return array of resources
+     */
+    public function getSockets()
+    {
+        $this->connect();
+        if($this->conn){
+            return array($this->conn->getSocket());
+        }else{
+            return array();
+        }
+    }
+
+    /**
+     * Idle processing for io manager's execution loop.
+     * Send keepalive pings to server.
+     *
+     * Side effect: kills process on exception from XMPP library.
+     *
+     * @todo FIXME: non-dying error handling
+     */
+    public function idle($timeout=0)
+    {
+        $now = time();
+        if (empty($this->lastping) || $now - $this->lastping > self::PING_INTERVAL) {
+            try {
+                $this->send_ping();
+            } catch (XMPPHP_Exception $e) {
+                common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
+                die($e->getMessage());
+            }
+        }
+    }
+
+    function connect()
+    {
+        if (!$this->conn || $this->conn->isDisconnected()) {
+            $resource = 'queue' . posix_getpid();
+            $this->conn = new Sharing_XMPP($this->plugin->host ?
+                                    $this->plugin->host :
+                                    $this->plugin->server,
+                                    $this->plugin->port,
+                                    $this->plugin->user,
+                                    $this->plugin->password,
+                                    $this->plugin->resource,
+                                    $this->plugin->server,
+                                    $this->plugin->debug ?
+                                    true : false,
+                                    $this->plugin->debug ?
+                                    XMPPHP_Log::LEVEL_VERBOSE :  null
+                                    );
+
+            if (!$this->conn) {
+                return false;
+            }
+            $this->conn->addEventHandler('message', 'handle_xmpp_message', $this);
+            $this->conn->addEventHandler('reconnect', 'handle_xmpp_reconnect', $this);
+            $this->conn->setReconnectTimeout(600);
+
+            $this->conn->autoSubscribe();
+            $this->conn->useEncryption($this->plugin->encryption);
+
+            try {
+                $this->conn->connect(true); // true = persistent connection
+            } catch (XMPPHP_Exception $e) {
+                common_log(LOG_ERR, $e->getMessage());
+                return false;
+            }
+
+            $this->conn->processUntil('session_start');
+            // TRANS: Presence announcement for XMPP.
+            $this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100);
+        }
+        return $this->conn;
+    }
+
+    function send_ping()
+    {
+        $this->connect();
+        if (!$this->conn || $this->conn->isDisconnected()) {
+            return false;
+        }
+        $now = time();
+        if (!isset($this->pingid)) {
+            $this->pingid = 0;
+        } else {
+            $this->pingid++;
+        }
+
+        common_log(LOG_DEBUG, "Sending ping #{$this->pingid}");
+               $this->conn->send("<iq from='{" . $this->plugin->daemonScreenname() . "}' to='{$this->plugin->server}' id='ping_{$this->pingid}' type='get'><ping xmlns='urn:xmpp:ping'/></iq>");
+        $this->lastping = $now;
+        return true;
+    }
+
+    function handle_xmpp_message(&$pl)
+    {
+        $this->plugin->enqueueIncomingRaw($pl);
+        return true;
+    }
+
+    /**
+     * Callback for Jabber reconnect event
+     * @param $pl
+     */
+    function handle_xmpp_reconnect(&$pl)
+    {
+        common_log(LOG_NOTICE, 'XMPP reconnected');
+
+        $this->conn->processUntil('session_start');
+        // TRANS: Message for XMPP reconnect.
+        $this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100);
+    }
+
+    /**
+     * sends a presence stanza on the XMPP network
+     *
+     * @param string $status   current status, free-form string
+     * @param string $show     structured status value
+     * @param string $to       recipient of presence, null for general
+     * @param string $type     type of status message, related to $show
+     * @param int    $priority priority of the presence
+     *
+     * @return boolean success value
+     */
+
+    function send_presence($status, $show='available', $to=null,
+                                  $type = 'available', $priority=null)
+    {
+        $this->connect();
+        if (!$this->conn || $this->conn->isDisconnected()) {
+            return false;
+        }
+        $this->conn->presence($status, $show, $to, $type, $priority);
+        return true;
+    }
+
+    /**
+     * sends a "special" presence stanza on the XMPP network
+     *
+     * @param string $type   Type of presence
+     * @param string $to     JID to send presence to
+     * @param string $show   show value for presence
+     * @param string $status status value for presence
+     *
+     * @return boolean success flag
+     *
+     * @see send_presence()
+     */
+
+    function special_presence($type, $to=null, $show=null, $status=null)
+    {
+        // @todo FIXME: why use this instead of send_presence()?
+        $this->connect();
+        if (!$this->conn || $this->conn->isDisconnected()) {
+            return false;
+        }
+
+        $to     = htmlspecialchars($to);
+        $status = htmlspecialchars($status);
+
+        $out = "<presence";
+        if ($to) {
+            $out .= " to='$to'";
+        }
+        if ($type) {
+            $out .= " type='$type'";
+        }
+        if ($show == 'available' and !$status) {
+            $out .= "/>";
+        } else {
+            $out .= ">";
+            if ($show && ($show != 'available')) {
+                $out .= "<show>$show</show>";
+            }
+            if ($status) {
+                $out .= "<status>$status</status>";
+            }
+            $out .= "</presence>";
+        }
+        $this->conn->send($out);
+        return true;
+    }
+}
diff --git a/plugins/Xmpp/xmppmanager.php b/plugins/Xmpp/xmppmanager.php
deleted file mode 100644 (file)
index c7d4c15..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-/**
- * XMPP background connection manager for XMPP-using queue handlers,
- * allowing them to send outgoing messages on the right connection.
- *
- * Input is handled during socket select loop, keepalive pings during idle.
- * Any incoming messages will be handled.
- *
- * In a multi-site queuedaemon.php run, one connection will be instantiated
- * for each site being handled by the current process that has XMPP enabled.
- */
-class XmppManager extends ImManager
-{
-    protected $lastping = null;
-    protected $pingid = null;
-
-    public $conn = null;
-
-    const PING_INTERVAL = 120;
-
-    /**
-     * Initialize connection to server.
-     * @return boolean true on success
-     */
-    public function start($master)
-    {
-        if(parent::start($master))
-        {
-            $this->connect();
-            return true;
-        }else{
-            return false;
-        }
-    }
-
-    function send_raw_message($data)
-    {
-        $this->connect();
-        if (!$this->conn || $this->conn->isDisconnected()) {
-            return false;
-        }
-        $this->conn->send($data);
-        return true;
-    }
-
-    /**
-     * Message pump is triggered on socket input, so we only need an idle()
-     * call often enough to trigger our outgoing pings.
-     */
-    function timeout()
-    {
-        return self::PING_INTERVAL;
-    }
-
-    /**
-     * Process XMPP events that have come in over the wire.
-     * @fixme may kill process on XMPP error
-     * @param resource $socket
-     */
-    public function handleInput($socket)
-    {
-        // Process the queue for as long as needed
-        try {
-            common_log(LOG_DEBUG, "Servicing the XMPP queue.");
-            $this->stats('xmpp_process');
-            $this->conn->processTime(0);
-        } catch (XMPPHP_Exception $e) {
-            common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
-            die($e->getMessage());
-        }
-    }
-
-    /**
-     * Lists the IM connection socket to allow i/o master to wake
-     * when input comes in here as well as from the queue source.
-     *
-     * @return array of resources
-     */
-    public function getSockets()
-    {
-        $this->connect();
-        if($this->conn){
-            return array($this->conn->getSocket());
-        }else{
-            return array();
-        }
-    }
-
-    /**
-     * Idle processing for io manager's execution loop.
-     * Send keepalive pings to server.
-     *
-     * Side effect: kills process on exception from XMPP library.
-     *
-     * @todo FIXME: non-dying error handling
-     */
-    public function idle($timeout=0)
-    {
-        $now = time();
-        if (empty($this->lastping) || $now - $this->lastping > self::PING_INTERVAL) {
-            try {
-                $this->send_ping();
-            } catch (XMPPHP_Exception $e) {
-                common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
-                die($e->getMessage());
-            }
-        }
-    }
-
-    function connect()
-    {
-        if (!$this->conn || $this->conn->isDisconnected()) {
-            $resource = 'queue' . posix_getpid();
-            $this->conn = new Sharing_XMPP($this->plugin->host ?
-                                    $this->plugin->host :
-                                    $this->plugin->server,
-                                    $this->plugin->port,
-                                    $this->plugin->user,
-                                    $this->plugin->password,
-                                    $this->plugin->resource,
-                                    $this->plugin->server,
-                                    $this->plugin->debug ?
-                                    true : false,
-                                    $this->plugin->debug ?
-                                    XMPPHP_Log::LEVEL_VERBOSE :  null
-                                    );
-
-            if (!$this->conn) {
-                return false;
-            }
-            $this->conn->addEventHandler('message', 'handle_xmpp_message', $this);
-            $this->conn->addEventHandler('reconnect', 'handle_xmpp_reconnect', $this);
-            $this->conn->setReconnectTimeout(600);
-
-            $this->conn->autoSubscribe();
-            $this->conn->useEncryption($this->plugin->encryption);
-
-            try {
-                $this->conn->connect(true); // true = persistent connection
-            } catch (XMPPHP_Exception $e) {
-                common_log(LOG_ERR, $e->getMessage());
-                return false;
-            }
-
-            $this->conn->processUntil('session_start');
-            // TRANS: Presence announcement for XMPP.
-            $this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100);
-        }
-        return $this->conn;
-    }
-
-    function send_ping()
-    {
-        $this->connect();
-        if (!$this->conn || $this->conn->isDisconnected()) {
-            return false;
-        }
-        $now = time();
-        if (!isset($this->pingid)) {
-            $this->pingid = 0;
-        } else {
-            $this->pingid++;
-        }
-
-        common_log(LOG_DEBUG, "Sending ping #{$this->pingid}");
-               $this->conn->send("<iq from='{" . $this->plugin->daemonScreenname() . "}' to='{$this->plugin->server}' id='ping_{$this->pingid}' type='get'><ping xmlns='urn:xmpp:ping'/></iq>");
-        $this->lastping = $now;
-        return true;
-    }
-
-    function handle_xmpp_message(&$pl)
-    {
-        $this->plugin->enqueueIncomingRaw($pl);
-        return true;
-    }
-
-    /**
-     * Callback for Jabber reconnect event
-     * @param $pl
-     */
-    function handle_xmpp_reconnect(&$pl)
-    {
-        common_log(LOG_NOTICE, 'XMPP reconnected');
-
-        $this->conn->processUntil('session_start');
-        // TRANS: Message for XMPP reconnect.
-        $this->send_presence(_m('Send me a message to post a notice'), 'available', null, 'available', 100);
-    }
-
-    /**
-     * sends a presence stanza on the XMPP network
-     *
-     * @param string $status   current status, free-form string
-     * @param string $show     structured status value
-     * @param string $to       recipient of presence, null for general
-     * @param string $type     type of status message, related to $show
-     * @param int    $priority priority of the presence
-     *
-     * @return boolean success value
-     */
-
-    function send_presence($status, $show='available', $to=null,
-                                  $type = 'available', $priority=null)
-    {
-        $this->connect();
-        if (!$this->conn || $this->conn->isDisconnected()) {
-            return false;
-        }
-        $this->conn->presence($status, $show, $to, $type, $priority);
-        return true;
-    }
-
-    /**
-     * sends a "special" presence stanza on the XMPP network
-     *
-     * @param string $type   Type of presence
-     * @param string $to     JID to send presence to
-     * @param string $show   show value for presence
-     * @param string $status status value for presence
-     *
-     * @return boolean success flag
-     *
-     * @see send_presence()
-     */
-
-    function special_presence($type, $to=null, $show=null, $status=null)
-    {
-        // @todo FIXME: why use this instead of send_presence()?
-        $this->connect();
-        if (!$this->conn || $this->conn->isDisconnected()) {
-            return false;
-        }
-
-        $to     = htmlspecialchars($to);
-        $status = htmlspecialchars($status);
-
-        $out = "<presence";
-        if ($to) {
-            $out .= " to='$to'";
-        }
-        if ($type) {
-            $out .= " type='$type'";
-        }
-        if ($show == 'available' and !$status) {
-            $out .= "/>";
-        } else {
-            $out .= ">";
-            if ($show && ($show != 'available')) {
-                $out .= "<show>$show</show>";
-            }
-            if ($status) {
-                $out .= "<status>$status</status>";
-            }
-            $out .= "</presence>";
-        }
-        $this->conn->send($out);
-        return true;
-    }
-}
index b93eb3ba4aebecdeb20216bf4d2ca484a80c846e..9d6c5ad41e850847e2a9154e3f0121c578bc4176 100644 (file)
@@ -105,44 +105,4 @@ class YammerImportPlugin extends Plugin
 
         return true;
     }
-
-    /**
-     * Automatically load the actions and libraries used by the plugin
-     *
-     * @param Class $cls the class
-     *
-     * @return boolean hook return
-     *
-     */
-    function onAutoload($cls)
-    {
-        $base = dirname(__FILE__);
-        $lower = strtolower($cls);
-        switch ($lower) {
-        case 'sn_yammerclient':
-        case 'yammerimporter':
-        case 'yammerrunner':
-        case 'yammerapikeyform':
-        case 'yammerauthinitform':
-        case 'yammerauthverifyform':
-        case 'yammerprogressform':
-        case 'yammerqueuehandler':
-            require_once "$base/lib/$lower.php";
-            return false;
-        case 'yammeradminpanelaction':
-            $crop = substr($lower, 0, strlen($lower) - strlen('action'));
-            require_once "$base/actions/$crop.php";
-            return false;
-        case 'yammer_state':
-        case 'yammer_notice_stub':
-        case 'yammer_common':
-        case 'yammer_user':
-        case 'yammer_group':
-        case 'yammer_notice':
-            require_once "$base/classes/$cls.php";
-            return false;
-        default:
-            return true;
-        }
-    }
 }
diff --git a/plugins/YammerImport/forms/yammerapikey.php b/plugins/YammerImport/forms/yammerapikey.php
new file mode 100644 (file)
index 0000000..72c14c3
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+
+class YammerApikeyForm extends Form
+{
+    private $runner;
+
+    function __construct($out)
+    {
+        parent::__construct($out);
+        $this->runner = $runner;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+
+    function id()
+    {
+        return 'yammer-apikey-form';
+    }
+
+
+    /**
+     * class of the form
+     *
+     * @return string of the form class
+     */
+
+    function formClass()
+    {
+        return 'form_yammer_apikey form_settings';
+    }
+
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+
+    function action()
+    {
+        return common_local_url('yammeradminpanel');
+    }
+
+
+    /**
+     * Legend of the Form
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+        // TRANS: Form legend for adding details to connect to a remote Yammer API.
+        $this->out->element('legend', null, _m('Yammer API registration'));
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+
+    function formData()
+    {
+        $this->out->hidden('subaction', 'apikey');
+
+        $this->out->elementStart('fieldset');
+
+        $this->out->elementStart('p');
+        // TRANS: Explanation of what needs to be done to connect to a Yammer network.
+        $this->out->text(_m('Before we can connect to your Yammer network, ' .
+                            'you will need to register the importer as an ' .
+                            'application authorized to pull data on your behalf. ' .
+                            'This registration will work only for your own network. ' .
+                            'Follow this link to register the app at Yammer; ' .
+                            'you will be prompted to log in if necessary:'));
+        $this->out->elementEnd('p');
+
+        $this->out->elementStart('p', array('class' => 'magiclink'));
+        $this->out->element('a',
+            array('href' => 'https://www.yammer.com/client_applications/new',
+                  'target' => '_blank'),
+            // TRANS: Link description to a Yammer application registration form.
+            _m('Open Yammer application registration form'));
+        $this->out->elementEnd('p');
+
+        // TRANS: Instructions.
+        $this->out->element('p', array(), _m('Copy the consumer key and secret you are given into the form below:'));
+
+        $this->out->elementStart('ul', array('class' => 'form_data'));
+        $this->out->elementStart('li');
+        // TRANS: Field label for a Yammer consumer key.
+        $this->out->input('consumer_key', _m('Consumer key:'), common_config('yammer', 'consumer_key'));
+        $this->out->elementEnd('li');
+        $this->out->elementStart('li');
+        // TRANS: Field label for a Yammer consumer secret.
+        $this->out->input('consumer_secret', _m('Consumer secret:'), common_config('yammer', 'consumer_secret'));
+        $this->out->elementEnd('li');
+        $this->out->elementEnd('ul');
+
+        // TRANS: Button text for saving a Yammer API registration.
+        $this->out->submit('submit', _m('BUTTON','Save'),
+                           // TRANS: Button title for saving a Yammer API registration.
+                           'submit', null, _m('Save the entered consumer key and consumer secret.'));
+
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+    }
+}
diff --git a/plugins/YammerImport/forms/yammerauthinit.php b/plugins/YammerImport/forms/yammerauthinit.php
new file mode 100644 (file)
index 0000000..c123bbb
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+
+class YammerAuthInitForm extends Form
+{
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+
+    function id()
+    {
+        return 'yammer-auth-init-form';
+    }
+
+
+    /**
+     * class of the form
+     *
+     * @return string of the form class
+     */
+
+    function formClass()
+    {
+        return 'form_yammer_auth_init form_settings';
+    }
+
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+
+    function action()
+    {
+        return common_local_url('yammeradminpanel');
+    }
+
+
+    /**
+     * Legend of the Form
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+        // TRANS: Form legend.
+        $this->out->element('legend', null, _m('Connect to Yammer'));
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+
+    function formData()
+    {
+        $this->out->hidden('subaction', 'authinit');
+
+        $this->out->elementStart('fieldset');
+        // TRANS: Button text for starting Yammer authentication.
+        $this->out->submit('submit', _m('BUTTON','Start authentication'),
+                           // TRANS: Button title for starting Yammer authentication.
+                           'submit', null, _m('Request authorization to connect to a Yammer account.'));
+        // TRANS: Button text for starting changing a Yammer API key.
+        $this->out->submit('change-apikey', _m('BUTTON','Change API key'));
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+    }
+}
diff --git a/plugins/YammerImport/forms/yammerauthverify.php b/plugins/YammerImport/forms/yammerauthverify.php
new file mode 100644 (file)
index 0000000..bb82eba
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+
+class YammerAuthVerifyForm extends Form
+{
+    private $runner;
+
+    function __construct($out, YammerRunner $runner)
+    {
+        parent::__construct($out);
+        $this->runner = $runner;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+
+    function id()
+    {
+        return 'yammer-auth-verify-form';
+    }
+
+
+    /**
+     * class of the form
+     *
+     * @return string of the form class
+     */
+
+    function formClass()
+    {
+        return 'form_yammer_auth_verify form_settings';
+    }
+
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+
+    function action()
+    {
+        return common_local_url('yammeradminpanel');
+    }
+
+
+    /**
+     * Legend of the Form
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+        // TRANS: Form legend.
+        $this->out->element('legend', null, _m('Connect to Yammer'));
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+
+    function formData()
+    {
+        $this->out->hidden('subaction', 'authverify');
+
+        $this->out->elementStart('fieldset');
+
+        $this->out->elementStart('p');
+        // TRANS: Form instructions.
+        $this->out->text(_m('Follow this link to confirm authorization at Yammer; you will be prompted to log in if necessary:'));
+        $this->out->elementEnd('p');
+
+        // iframe would be nice to avoid leaving -- since they don't seem to have callback url O_O
+        /*
+        $this->out->element('iframe', array('id' => 'yammer-oauth',
+                                            'src' => $this->runner->getAuthUrl()));
+        */
+        // yeah, it ignores the callback_url
+        // soo... crappy link. :(
+
+        $this->out->elementStart('p', array('class' => 'magiclink'));
+        $this->out->element('a',
+            array('href' => $this->runner->getAuthUrl(),
+                  'target' => '_blank'),
+            // TRANS: Link description for a link in an external Yammer system.
+            _m('Open Yammer authentication window'));
+        $this->out->elementEnd('p');
+
+        // TRANS: Form instructions.
+        $this->out->element('p', array(), _m('Copy the verification code you are given below:'));
+
+        $this->out->elementStart('ul', array('class' => 'form_data'));
+        $this->out->elementStart('li');
+        // TRANS: Field label.
+        $this->out->input('verify_token', _m('Verification code:'));
+        $this->out->elementEnd('li');
+        $this->out->elementEnd('ul');
+
+        // TRANS: Button text for saving  Yammer authorisation data and starting Yammer import.
+        $this->out->submit('submit', _m('BUTTON','Continue'),
+                           // TRANS: Button title for saving  Yammer authorisation data and starting Yammer import.
+                           'submit', null, _m('Save the verification code and begin import.'));
+        $this->out->elementEnd('fieldset');
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+    }
+}
diff --git a/plugins/YammerImport/forms/yammerprogress.php b/plugins/YammerImport/forms/yammerprogress.php
new file mode 100644 (file)
index 0000000..0b73472
--- /dev/null
@@ -0,0 +1,229 @@
+<?php
+
+class YammerProgressForm extends Form
+{
+    /**
+     * ID of the form
+     *
+     * @return string ID of the form
+     */
+    function id()
+    {
+        return 'yammer-progress-form';
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+    function formClass()
+    {
+        $classes = array('form_settings');
+        $runner = YammerRunner::init();
+        if ($runner->lastError()) {
+            $classes[] = 'import-error';
+        } else if ($runner->state() == 'done') {
+            $classes[] = 'import-done';
+        } else {
+            $classes[] = 'import-progress';
+        }
+        return implode(' ', $classes);
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+    function action()
+    {
+        return common_local_url('yammeradminpanel');
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+    function formData()
+    {
+        $this->out->hidden('subaction', 'progress');
+
+        $runner = YammerRunner::init();
+
+        $error = $runner->lastError();
+        $userCount = $runner->countUsers();
+        $groupCount = $runner->countGroups();
+        $fetchedCount = $runner->countFetchedNotices();
+        $savedCount = $runner->countSavedNotices();
+
+        $labels = array(
+            'init' => array(
+                // TRANS: Field label for a Yammer import initialise step.
+                'label' => _m('Initialize'),
+                // TRANS: "In progress" description.
+                'progress' => _m('No import running'),
+                // TRANS: "Complete" description for initialize state.
+                'complete' => _m('Initiated Yammer server connection...'),
+            ),
+            'requesting-auth' => array(
+                // TRANS: Field label for a Yammer import connect step.
+                'label' => _m('Connect to Yammer'),
+                // TRANS: "In progress" description.
+                'progress' => _m('Awaiting authorization...'),
+                // TRANS: "Complete" description for connect state.
+                'complete' => _m('Connected.'),
+            ),
+            'import-users' => array(
+                // TRANS: Field label for a Yammer user import users step.
+                'label' => _m('Import user accounts'),
+                // TRANS: "In progress" description.
+                // TRANS: %d is the number of users to be imported.
+                'progress' => sprintf(_m('Importing %d user...',
+                                         'Importing %d users...',
+                                         $userCount),
+                                      $userCount),
+                // TRANS: "Complete" description for step.
+                // TRANS: %d is the number of users imported.
+                'complete' => sprintf(_m('Imported %d user.',
+                                         'Imported %d users.',
+                                         $userCount),
+                                      $userCount),
+            ),
+            'import-groups' => array(
+                // TRANS: Field label for a Yammer group import step.
+                'label' => _m('Import user groups'),
+                // TRANS: "In progress" description.
+                // TRANS: %d is the number of groups to be imported.
+                'progress' => sprintf(_m('Importing %d group...',
+                                         'Importing %d groups...',
+                                         $groupCount),
+                                      $groupCount),
+                // TRANS: "Complete" description for step.
+                // TRANS: %d is the number of groups imported.
+                'complete' => sprintf(_m('Imported %d group.',
+                                         'Imported %d groups.',
+                                         $groupCount),
+                                      $groupCount),
+            ),
+            'fetch-messages' => array(
+                // TRANS: Field label for a Yammer import prepare notices step.
+                'label' => _m('Prepare public notices for import'),
+                // TRANS: "In progress" description.
+                // TRANS: %d is the number of notices to be prepared for import.
+                'progress' => sprintf(_m('Preparing %d notice...',
+                                         'Preparing %d notices...',
+                                         $fetchedCount),
+                                      $fetchedCount),
+                // TRANS: "Complete" description for step.
+                // TRANS: %d is the number of notices prepared for import.
+                'complete' => sprintf(_m('Prepared %d notice.',
+                                         'Prepared %d notices.',
+                                         $fetchedCount),
+                                      $fetchedCount),
+            ),
+            'save-messages' => array(
+                // TRANS: Field label for a Yammer import notices step.
+                'label' => _m('Import public notices'),
+                // TRANS: "In progress" description.
+                // TRANS: %d is the number of notices to be imported.
+                'progress' => sprintf(_m('Importing %d notice...',
+                                         'Importing %d notices...',
+                                         $savedCount),
+                                      $savedCount),
+                // TRANS: "Complete" description for step.
+                // TRANS: %d is the number of notices imported.
+                'complete' => sprintf(_m('Imported %d notice.',
+                                         'Imported %d notices.',
+                                         $savedCount),
+                                      $savedCount),
+            ),
+            'done' => array(
+                // TRANS: Field label for a Yammer import done step.
+                'label' => _m('Done'),
+                // TRANS: "In progress" description for done step.
+                'progress' => sprintf(_m('Import is complete!')),
+                // TRANS: "Complete" description for done step.
+                'complete' => sprintf(_m('Import is complete!')),
+            )
+        );
+        $steps = array_keys($labels);
+        $currentStep = array_search($runner->state(), $steps);
+
+        $classes = array('yammer-import');
+        if ($error) {
+            $classes[] = 'yammer-error';
+        } else {
+            $classes[] = 'yammer-running';
+        }
+        $this->out->elementStart('fieldset', array('class' => implode(' ', $classes)));
+        // TRANS: Fieldset legend.
+        $this->out->element('legend', array(), _m('Import status'));
+        foreach ($steps as $step => $state) {
+            if ($state == 'init') {
+                // Don't show 'init', it's boring.
+                continue;
+            }
+            if ($step < $currentStep) {
+                // This step is done
+                $this->progressBar($state,
+                                   'complete',
+                                   $labels[$state]['label'],
+                                   $labels[$state]['complete']);
+            } else if ($step == $currentStep) {
+                // This step is in progress
+                $this->progressBar($state,
+                                   'progress',
+                                   $labels[$state]['label'],
+                                   $labels[$state]['progress'],
+                                   $error);
+            } else {
+                // This step has not yet been done.
+                $this->progressBar($state,
+                                   'waiting',
+                                   $labels[$state]['label'],
+                                   // TRANS: Progress bar status.
+                                   _m('Waiting...'));
+            }
+        }
+        $this->out->elementEnd('fieldset');
+    }
+
+    private function progressBar($state, $class, $label, $status, $error=null)
+    {
+        // @fixme prettify ;)
+        $this->out->elementStart('div', array('class' => "import-step import-step-$state $class"));
+        $this->out->element('div', array('class' => 'import-label'), $label);
+        $this->out->element('div', array('class' => 'import-status'), $status);
+        if ($class == 'progress') {
+            if ($state == 'done') {
+                // TRANS: Button text for resetting the import state.
+                $this->out->submit('abort-import', _m('Reset import state'));
+            } else {
+                if ($error) {
+                    $this->errorBox($error);
+                } else {
+                    // TRANS: Button text for pausing an import.
+                    $this->out->submit('pause-import', _m('Pause import'));
+                }
+            }
+        }
+        $this->out->elementEnd('div');
+    }
+
+    private function errorBox($msg)
+    {
+        // TRANS: Error message. %s are the error details.
+        $errline = sprintf(_m('Encountered error "%s".'), $msg);
+        $this->out->elementStart('fieldset', array('class' => 'import-error'));
+        // TRANS: Fieldset legend for a paused import.
+        $this->out->element('legend', array(), _m('Paused'));
+        $this->out->element('p', array(), $errline);
+        // TRANS: Button text for continuing a paused import.
+        $this->out->submit('continue-import', _m('Continue'));
+        // TRANS: Button text for aborting a paused import.
+        $this->out->submit('abort-import', _m('Abort import'));
+        $this->out->elementEnd('fieldset');
+    }
+}
diff --git a/plugins/YammerImport/lib/yammerapikeyform.php b/plugins/YammerImport/lib/yammerapikeyform.php
deleted file mode 100644 (file)
index 72c14c3..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-<?php
-
-class YammerApikeyForm extends Form
-{
-    private $runner;
-
-    function __construct($out)
-    {
-        parent::__construct($out);
-        $this->runner = $runner;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-
-    function id()
-    {
-        return 'yammer-apikey-form';
-    }
-
-
-    /**
-     * class of the form
-     *
-     * @return string of the form class
-     */
-
-    function formClass()
-    {
-        return 'form_yammer_apikey form_settings';
-    }
-
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-
-    function action()
-    {
-        return common_local_url('yammeradminpanel');
-    }
-
-
-    /**
-     * Legend of the Form
-     *
-     * @return void
-     */
-    function formLegend()
-    {
-        // TRANS: Form legend for adding details to connect to a remote Yammer API.
-        $this->out->element('legend', null, _m('Yammer API registration'));
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-
-    function formData()
-    {
-        $this->out->hidden('subaction', 'apikey');
-
-        $this->out->elementStart('fieldset');
-
-        $this->out->elementStart('p');
-        // TRANS: Explanation of what needs to be done to connect to a Yammer network.
-        $this->out->text(_m('Before we can connect to your Yammer network, ' .
-                            'you will need to register the importer as an ' .
-                            'application authorized to pull data on your behalf. ' .
-                            'This registration will work only for your own network. ' .
-                            'Follow this link to register the app at Yammer; ' .
-                            'you will be prompted to log in if necessary:'));
-        $this->out->elementEnd('p');
-
-        $this->out->elementStart('p', array('class' => 'magiclink'));
-        $this->out->element('a',
-            array('href' => 'https://www.yammer.com/client_applications/new',
-                  'target' => '_blank'),
-            // TRANS: Link description to a Yammer application registration form.
-            _m('Open Yammer application registration form'));
-        $this->out->elementEnd('p');
-
-        // TRANS: Instructions.
-        $this->out->element('p', array(), _m('Copy the consumer key and secret you are given into the form below:'));
-
-        $this->out->elementStart('ul', array('class' => 'form_data'));
-        $this->out->elementStart('li');
-        // TRANS: Field label for a Yammer consumer key.
-        $this->out->input('consumer_key', _m('Consumer key:'), common_config('yammer', 'consumer_key'));
-        $this->out->elementEnd('li');
-        $this->out->elementStart('li');
-        // TRANS: Field label for a Yammer consumer secret.
-        $this->out->input('consumer_secret', _m('Consumer secret:'), common_config('yammer', 'consumer_secret'));
-        $this->out->elementEnd('li');
-        $this->out->elementEnd('ul');
-
-        // TRANS: Button text for saving a Yammer API registration.
-        $this->out->submit('submit', _m('BUTTON','Save'),
-                           // TRANS: Button title for saving a Yammer API registration.
-                           'submit', null, _m('Save the entered consumer key and consumer secret.'));
-
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-
-    function formActions()
-    {
-    }
-}
diff --git a/plugins/YammerImport/lib/yammerauthinitform.php b/plugins/YammerImport/lib/yammerauthinitform.php
deleted file mode 100644 (file)
index c123bbb..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-
-class YammerAuthInitForm extends Form
-{
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-
-    function id()
-    {
-        return 'yammer-auth-init-form';
-    }
-
-
-    /**
-     * class of the form
-     *
-     * @return string of the form class
-     */
-
-    function formClass()
-    {
-        return 'form_yammer_auth_init form_settings';
-    }
-
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-
-    function action()
-    {
-        return common_local_url('yammeradminpanel');
-    }
-
-
-    /**
-     * Legend of the Form
-     *
-     * @return void
-     */
-    function formLegend()
-    {
-        // TRANS: Form legend.
-        $this->out->element('legend', null, _m('Connect to Yammer'));
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-
-    function formData()
-    {
-        $this->out->hidden('subaction', 'authinit');
-
-        $this->out->elementStart('fieldset');
-        // TRANS: Button text for starting Yammer authentication.
-        $this->out->submit('submit', _m('BUTTON','Start authentication'),
-                           // TRANS: Button title for starting Yammer authentication.
-                           'submit', null, _m('Request authorization to connect to a Yammer account.'));
-        // TRANS: Button text for starting changing a Yammer API key.
-        $this->out->submit('change-apikey', _m('BUTTON','Change API key'));
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-
-    function formActions()
-    {
-    }
-}
diff --git a/plugins/YammerImport/lib/yammerauthverifyform.php b/plugins/YammerImport/lib/yammerauthverifyform.php
deleted file mode 100644 (file)
index bb82eba..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-<?php
-
-class YammerAuthVerifyForm extends Form
-{
-    private $runner;
-
-    function __construct($out, YammerRunner $runner)
-    {
-        parent::__construct($out);
-        $this->runner = $runner;
-    }
-
-    /**
-     * ID of the form
-     *
-     * @return int ID of the form
-     */
-
-    function id()
-    {
-        return 'yammer-auth-verify-form';
-    }
-
-
-    /**
-     * class of the form
-     *
-     * @return string of the form class
-     */
-
-    function formClass()
-    {
-        return 'form_yammer_auth_verify form_settings';
-    }
-
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-
-    function action()
-    {
-        return common_local_url('yammeradminpanel');
-    }
-
-
-    /**
-     * Legend of the Form
-     *
-     * @return void
-     */
-    function formLegend()
-    {
-        // TRANS: Form legend.
-        $this->out->element('legend', null, _m('Connect to Yammer'));
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-
-    function formData()
-    {
-        $this->out->hidden('subaction', 'authverify');
-
-        $this->out->elementStart('fieldset');
-
-        $this->out->elementStart('p');
-        // TRANS: Form instructions.
-        $this->out->text(_m('Follow this link to confirm authorization at Yammer; you will be prompted to log in if necessary:'));
-        $this->out->elementEnd('p');
-
-        // iframe would be nice to avoid leaving -- since they don't seem to have callback url O_O
-        /*
-        $this->out->element('iframe', array('id' => 'yammer-oauth',
-                                            'src' => $this->runner->getAuthUrl()));
-        */
-        // yeah, it ignores the callback_url
-        // soo... crappy link. :(
-
-        $this->out->elementStart('p', array('class' => 'magiclink'));
-        $this->out->element('a',
-            array('href' => $this->runner->getAuthUrl(),
-                  'target' => '_blank'),
-            // TRANS: Link description for a link in an external Yammer system.
-            _m('Open Yammer authentication window'));
-        $this->out->elementEnd('p');
-
-        // TRANS: Form instructions.
-        $this->out->element('p', array(), _m('Copy the verification code you are given below:'));
-
-        $this->out->elementStart('ul', array('class' => 'form_data'));
-        $this->out->elementStart('li');
-        // TRANS: Field label.
-        $this->out->input('verify_token', _m('Verification code:'));
-        $this->out->elementEnd('li');
-        $this->out->elementEnd('ul');
-
-        // TRANS: Button text for saving  Yammer authorisation data and starting Yammer import.
-        $this->out->submit('submit', _m('BUTTON','Continue'),
-                           // TRANS: Button title for saving  Yammer authorisation data and starting Yammer import.
-                           'submit', null, _m('Save the verification code and begin import.'));
-        $this->out->elementEnd('fieldset');
-    }
-
-    /**
-     * Action elements
-     *
-     * @return void
-     */
-
-    function formActions()
-    {
-    }
-}
diff --git a/plugins/YammerImport/lib/yammerprogressform.php b/plugins/YammerImport/lib/yammerprogressform.php
deleted file mode 100644 (file)
index 0b73472..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-<?php
-
-class YammerProgressForm extends Form
-{
-    /**
-     * ID of the form
-     *
-     * @return string ID of the form
-     */
-    function id()
-    {
-        return 'yammer-progress-form';
-    }
-
-    /**
-     * class of the form
-     *
-     * @return string class of the form
-     */
-    function formClass()
-    {
-        $classes = array('form_settings');
-        $runner = YammerRunner::init();
-        if ($runner->lastError()) {
-            $classes[] = 'import-error';
-        } else if ($runner->state() == 'done') {
-            $classes[] = 'import-done';
-        } else {
-            $classes[] = 'import-progress';
-        }
-        return implode(' ', $classes);
-    }
-
-    /**
-     * Action of the form
-     *
-     * @return string URL of the action
-     */
-    function action()
-    {
-        return common_local_url('yammeradminpanel');
-    }
-
-    /**
-     * Data elements of the form
-     *
-     * @return void
-     */
-    function formData()
-    {
-        $this->out->hidden('subaction', 'progress');
-
-        $runner = YammerRunner::init();
-
-        $error = $runner->lastError();
-        $userCount = $runner->countUsers();
-        $groupCount = $runner->countGroups();
-        $fetchedCount = $runner->countFetchedNotices();
-        $savedCount = $runner->countSavedNotices();
-
-        $labels = array(
-            'init' => array(
-                // TRANS: Field label for a Yammer import initialise step.
-                'label' => _m('Initialize'),
-                // TRANS: "In progress" description.
-                'progress' => _m('No import running'),
-                // TRANS: "Complete" description for initialize state.
-                'complete' => _m('Initiated Yammer server connection...'),
-            ),
-            'requesting-auth' => array(
-                // TRANS: Field label for a Yammer import connect step.
-                'label' => _m('Connect to Yammer'),
-                // TRANS: "In progress" description.
-                'progress' => _m('Awaiting authorization...'),
-                // TRANS: "Complete" description for connect state.
-                'complete' => _m('Connected.'),
-            ),
-            'import-users' => array(
-                // TRANS: Field label for a Yammer user import users step.
-                'label' => _m('Import user accounts'),
-                // TRANS: "In progress" description.
-                // TRANS: %d is the number of users to be imported.
-                'progress' => sprintf(_m('Importing %d user...',
-                                         'Importing %d users...',
-                                         $userCount),
-                                      $userCount),
-                // TRANS: "Complete" description for step.
-                // TRANS: %d is the number of users imported.
-                'complete' => sprintf(_m('Imported %d user.',
-                                         'Imported %d users.',
-                                         $userCount),
-                                      $userCount),
-            ),
-            'import-groups' => array(
-                // TRANS: Field label for a Yammer group import step.
-                'label' => _m('Import user groups'),
-                // TRANS: "In progress" description.
-                // TRANS: %d is the number of groups to be imported.
-                'progress' => sprintf(_m('Importing %d group...',
-                                         'Importing %d groups...',
-                                         $groupCount),
-                                      $groupCount),
-                // TRANS: "Complete" description for step.
-                // TRANS: %d is the number of groups imported.
-                'complete' => sprintf(_m('Imported %d group.',
-                                         'Imported %d groups.',
-                                         $groupCount),
-                                      $groupCount),
-            ),
-            'fetch-messages' => array(
-                // TRANS: Field label for a Yammer import prepare notices step.
-                'label' => _m('Prepare public notices for import'),
-                // TRANS: "In progress" description.
-                // TRANS: %d is the number of notices to be prepared for import.
-                'progress' => sprintf(_m('Preparing %d notice...',
-                                         'Preparing %d notices...',
-                                         $fetchedCount),
-                                      $fetchedCount),
-                // TRANS: "Complete" description for step.
-                // TRANS: %d is the number of notices prepared for import.
-                'complete' => sprintf(_m('Prepared %d notice.',
-                                         'Prepared %d notices.',
-                                         $fetchedCount),
-                                      $fetchedCount),
-            ),
-            'save-messages' => array(
-                // TRANS: Field label for a Yammer import notices step.
-                'label' => _m('Import public notices'),
-                // TRANS: "In progress" description.
-                // TRANS: %d is the number of notices to be imported.
-                'progress' => sprintf(_m('Importing %d notice...',
-                                         'Importing %d notices...',
-                                         $savedCount),
-                                      $savedCount),
-                // TRANS: "Complete" description for step.
-                // TRANS: %d is the number of notices imported.
-                'complete' => sprintf(_m('Imported %d notice.',
-                                         'Imported %d notices.',
-                                         $savedCount),
-                                      $savedCount),
-            ),
-            'done' => array(
-                // TRANS: Field label for a Yammer import done step.
-                'label' => _m('Done'),
-                // TRANS: "In progress" description for done step.
-                'progress' => sprintf(_m('Import is complete!')),
-                // TRANS: "Complete" description for done step.
-                'complete' => sprintf(_m('Import is complete!')),
-            )
-        );
-        $steps = array_keys($labels);
-        $currentStep = array_search($runner->state(), $steps);
-
-        $classes = array('yammer-import');
-        if ($error) {
-            $classes[] = 'yammer-error';
-        } else {
-            $classes[] = 'yammer-running';
-        }
-        $this->out->elementStart('fieldset', array('class' => implode(' ', $classes)));
-        // TRANS: Fieldset legend.
-        $this->out->element('legend', array(), _m('Import status'));
-        foreach ($steps as $step => $state) {
-            if ($state == 'init') {
-                // Don't show 'init', it's boring.
-                continue;
-            }
-            if ($step < $currentStep) {
-                // This step is done
-                $this->progressBar($state,
-                                   'complete',
-                                   $labels[$state]['label'],
-                                   $labels[$state]['complete']);
-            } else if ($step == $currentStep) {
-                // This step is in progress
-                $this->progressBar($state,
-                                   'progress',
-                                   $labels[$state]['label'],
-                                   $labels[$state]['progress'],
-                                   $error);
-            } else {
-                // This step has not yet been done.
-                $this->progressBar($state,
-                                   'waiting',
-                                   $labels[$state]['label'],
-                                   // TRANS: Progress bar status.
-                                   _m('Waiting...'));
-            }
-        }
-        $this->out->elementEnd('fieldset');
-    }
-
-    private function progressBar($state, $class, $label, $status, $error=null)
-    {
-        // @fixme prettify ;)
-        $this->out->elementStart('div', array('class' => "import-step import-step-$state $class"));
-        $this->out->element('div', array('class' => 'import-label'), $label);
-        $this->out->element('div', array('class' => 'import-status'), $status);
-        if ($class == 'progress') {
-            if ($state == 'done') {
-                // TRANS: Button text for resetting the import state.
-                $this->out->submit('abort-import', _m('Reset import state'));
-            } else {
-                if ($error) {
-                    $this->errorBox($error);
-                } else {
-                    // TRANS: Button text for pausing an import.
-                    $this->out->submit('pause-import', _m('Pause import'));
-                }
-            }
-        }
-        $this->out->elementEnd('div');
-    }
-
-    private function errorBox($msg)
-    {
-        // TRANS: Error message. %s are the error details.
-        $errline = sprintf(_m('Encountered error "%s".'), $msg);
-        $this->out->elementStart('fieldset', array('class' => 'import-error'));
-        // TRANS: Fieldset legend for a paused import.
-        $this->out->element('legend', array(), _m('Paused'));
-        $this->out->element('p', array(), $errline);
-        // TRANS: Button text for continuing a paused import.
-        $this->out->submit('continue-import', _m('Continue'));
-        // TRANS: Button text for aborting a paused import.
-        $this->out->submit('abort-import', _m('Abort import'));
-        $this->out->elementEnd('fieldset');
-    }
-}