1212 Commits

Author SHA1 Message Date
Mikael Nordfeldth
b2cfbded2e Upgrading from 1.1.x would make uri fields have length=255 2016-03-15 16:54:10 +01:00
Mikael Nordfeldth
346a73c36f Fix a regression in 1f76c1e4 that stopped sending email confirmation on registration 2016-03-02 14:38:34 +01:00
Mikael Nordfeldth
6336248d71 Notice getRendered() can now be called on uninserted notices 2016-02-25 20:13:00 +01:00
Mikael Nordfeldth
67aff528f5 socialfy-your-domain made people think you needed manual interaction
I don't know why, but people started following those instructions for no
apparent reason and it ended up causing a bunch of federation issues or
homegrown cron script messes.

Maybe changing the name to "another" instead of "your" domain will make
people stop doing stuff randomly.
2016-02-25 20:12:56 +01:00
Mikael Nordfeldth
80f7a5f025 $metadata->thumbnail_url is not guaranteed to be set
We should probably have a separate class for this, so we can more
easily combine different technologies similar to oEmbed/OpenGraph.
2016-02-25 19:47:16 +01:00
Mikael Nordfeldth
e6f07d8554 Use in_array instead. Now we get third party responses to contextually interesting threads
I think this solves much of the "third party conversation" issues, assuming involved parties
are using modern GNU social instances.
2016-02-24 00:19:27 +01:00
Mikael Nordfeldth
31c9b2c1d8 Check the notice context for users in UsersalmonAction 2016-02-23 23:56:43 +01:00
Mikael Nordfeldth
9319033ff0 Properly attach activityobjects
For some reason they were written to ->object, which is incorrect as
we use the objects[] array (which usually just holds one entry though)
2016-02-23 23:50:57 +01:00
Mikael Nordfeldth
0eb5122817 Check that the user is in the context of a salmon slap 2016-02-23 23:42:41 +01:00
Mikael Nordfeldth
d672547112 getAliases should be only a list (numeric array) 2016-02-23 14:33:09 +01:00
Mikael Nordfeldth
e16f7d04a8 Let OpenID match against aliases (fix fancyurl stuff etc.) 2016-02-23 14:15:08 +01:00
Mikael Nordfeldth
b59dacb806 getAliases for Profile and Notice
Also move fancyurlfix into site-wide $config['fix']['fancyurls']

TODO: getByUri should make use of this directly I guess?
2016-02-23 14:00:59 +01:00
Mikael Nordfeldth
5f7032dfee Verify that authenticated API calls are made from our domain name.
Evil forms on other websites could otherwise potentially be configured
to have action="https://gnusocial.example/api/statuses/update.json" or
whatever. XHR is already blocked with CORS stuff.

Really, why do browsers allow cross domain POSTs at all? Sigh. The web.
2016-02-22 15:19:10 +01:00
Mikael Nordfeldth
c67b89e56b Make WebFinger fancyurlfix configurable 2016-02-21 20:05:32 +01:00
Mikael Nordfeldth
ce803f6d06 WebFinger aliases with 'index.php/' 2016-02-21 20:00:07 +01:00
Mikael Nordfeldth
1edb1bbc17 Claim that we are the URL without index.php/ in webfinger response 2016-02-21 19:09:39 +01:00
Mikael Nordfeldth
893d117309 throw new, not just throw 2016-02-21 19:01:37 +01:00
Mikael Nordfeldth
0c17c32267 Let the WebFingerPlugin lookup profile resources with index.php/ too 2016-02-21 18:48:48 +01:00
Mikael Nordfeldth
23e66bef64 common_fake_local_fancy_url to remove index.php/ from a local URL 2016-02-21 18:48:18 +01:00
Mikael Nordfeldth
d16a883e17 Allow lookup of User->getByUri (throws NoResultException) 2016-02-21 18:47:47 +01:00
Mikael Nordfeldth
b23cc7465f Keep a unique set of WebFingerResource aliases 2016-02-21 18:47:32 +01:00
Mikael Nordfeldth
afbdcf8938 Don't publish mbox_sha1sum in FOAF by default.
We say the email is private data, so reasonably we shouldn't reveal it
indirectly through a hash sum: http://xmlns.com/foaf/spec/#term_mbox_sha1sum
2016-02-19 00:10:05 +01:00
Mikael Nordfeldth
a838c90951 Only show "public:site" in ToSelector if notice/allowprivate is true 2016-02-18 00:33:16 +01:00
Mikael Nordfeldth
f68d1ade3f Put "Everyone" and "Everyone at [local instance]" at the top of ToSelector 2016-02-18 00:32:09 +01:00
Mikael Nordfeldth
543d968b81 NoAcctUriException->profile not $e directly 2016-02-18 00:13:59 +01:00
Mikael Nordfeldth
a361fdbd77 Sort ToSelector by AcctUri 2016-02-18 00:05:09 +01:00
Mikael Nordfeldth
73dbc5ca1b Use ToSelector choice again. 2016-02-17 23:44:15 +01:00
Mikael Nordfeldth
d9b649642d Show notice feed URLs (and author) 2016-02-17 23:32:56 +01:00
Mikael Nordfeldth
d2c11925bf To-selector padlock only shown if site config notice/allowprivate is true 2016-02-17 23:06:11 +01:00
Mikael Nordfeldth
5fbb01130a By default, disallow users to set private_stream 2016-02-17 22:58:31 +01:00
Mikael Nordfeldth
47dc15c9f6 Describe that we don't allow empty fullnames. 2016-02-17 22:48:32 +01:00
Mikael Nordfeldth
d6bf90cfb7 If profile fullname is 0 chars use nickname 2016-02-17 22:43:45 +01:00
Mikael Nordfeldth
ade4518ae4 Make the Link header give URI for WebFinger lookup 2016-02-17 22:36:33 +01:00
Mikael Nordfeldth
422d475e44 Differentiate two similar log warning messages 2016-02-17 21:57:52 +01:00
Mikael Nordfeldth
d2507a6266 Gotta declare FullNoticeStream as abstract class 2016-02-16 02:24:38 +01:00
Mikael Nordfeldth
46829c6d3c FullNoticeStream selects all verbs. 2016-02-16 02:21:39 +01:00
Mikael Nordfeldth
2d1b70c94d created column was ambigououuuouuus 2016-02-15 09:59:34 +01:00
Mikael Nordfeldth
2301862ae6 We only want POST and SHARE in the inbox/home timeline right? 2016-02-15 09:59:18 +01:00
Mikael Nordfeldth
dcb7ce36d8 Show shares in public timeline
Also, the unselect rule for DELETE was useless anyway since it would
already have been filtered out by not having true.

(the => false stuff are for when you want ALL _except_ that)
2016-02-14 20:53:26 +01:00
Mikael Nordfeldth
e2a090c9cc Use NoticeStream::filterVerbs for filtering in noticestreams 2016-02-14 20:46:13 +01:00
Mikael Nordfeldth
c23c3a4f53 Might as well put a FILTER_SANITIZE_EMAIL there
Not that I think we could break out of the directory since
we use basename, but you never know... maybe there's a unicode
bug in PHP or something.
2016-02-13 14:06:05 +01:00
Mikael Nordfeldth
4bf26eff4c socialfy-your-domain updated for webfinger (not tested) 2016-02-13 13:57:15 +01:00
Mikael Nordfeldth
be14e15dac Hide attachments in notices by silenced profiles 2016-02-13 13:17:39 +01:00
Mikael Nordfeldth
fbcca62ae1 listGet was not meant for that really 2016-02-13 01:19:47 +01:00
Mikael Nordfeldth
8ef2abf30b Render RegiserThrottle extra profile data properly 2016-02-13 01:16:34 +01:00
Mikael Nordfeldth
799c2e47fe Don't depend on ModLog 2016-02-13 01:10:01 +01:00
Mikael Nordfeldth
be35975b12 RegisterThrottle list-profiles-by-ip 2016-02-13 01:02:18 +01:00
Mikael Nordfeldth
557ad2d1fd Show user registration IP to users who can see ModLog 2016-02-13 00:51:43 +01:00
Mikael Nordfeldth
c7c34ec05a Only administrators can delete other privileged users. 2016-02-12 15:00:18 +01:00
Mikael Nordfeldth
83f679fb57 Profile->isPrivileged() to check if users have more rights than to post etc. 2016-02-12 14:47:49 +01:00
Mikael Nordfeldth
3cef75bcac Update the comment on silencing privileged users in ModHelper 2016-02-12 14:47:44 +01:00
Mikael Nordfeldth
e5ad98e601 Silence action can only be used on non-priviliged users 2016-02-12 14:22:25 +01:00
Mikael Nordfeldth
5dce08d068 Add Profile::ensureCurrent() to verify we _certainly_ got a Profile. 2016-02-12 13:52:48 +01:00
Mikael Nordfeldth
f10625f8bc file and avatar dirs on instances with no such dirs in filesystem 2016-02-12 02:29:33 +01:00
Mikael Nordfeldth
338df7e35b Fix Nickname::isSystemPath() work properly for routes 2016-02-12 02:21:11 +01:00
Mikael Nordfeldth
c8753353ed Do not delete_orphan_files on an instance with Qvitter 2016-02-12 01:45:47 +01:00
Mikael Nordfeldth
913595780f And LEFT JOIN to actually get all results 2016-02-12 00:05:36 +01:00
Mikael Nordfeldth
1471defff3 ...and avoid duplicate results... 2016-02-11 23:38:12 +01:00
Mikael Nordfeldth
05fea4cdc6 Aurhg, and get all the properties, not just id 2016-02-11 22:54:29 +01:00
Mikael Nordfeldth
2198f39597 Haha, it essentially became a NOOP with the last commit 2016-02-11 22:49:45 +01:00
Mikael Nordfeldth
6f2fbd448d Fixed the delete orphan script to include deleted notices
The file_to_post table sometimes had post_id with values that did not
exist in the notice table.
2016-02-11 22:43:26 +01:00
Mikael Nordfeldth
38a187b93e Delete orphan files maintenance script
When deleting a profile it'll delete its notices and the coupling to
file entries, but not the file entries themselves (and thus not the
files). So if one to delete a person uploading offending images, then
the images are left behind and can be hotlinked. This will remove it.
2016-02-11 22:19:56 +01:00
Mikael Nordfeldth
7fdcbd56d5 XMPP URI scheme for HTMLPurifier 2016-02-11 21:31:50 +01:00
Mikael Nordfeldth
67dfc0a046 application/xml allowed in uploads 2016-02-11 00:04:14 +01:00
Mikael Nordfeldth
b9d35659c8 Stricter exception check 2016-02-10 04:43:30 +01:00
Mikael Nordfeldth
733debd9b3 Use thumbnail upscaling config value 2016-02-10 04:40:54 +01:00
Mikael Nordfeldth
8806cce735 Default to avoid upscaling of thumbnails. 45x45=>450x450 is ugly 2016-02-10 04:40:10 +01:00
Mikael Nordfeldth
dd229e855a Allow finding the "original remote thumbnail"
This will probably cause older oEmbed images not to show, since they
probably were updated to use empty url entries because they were thought
of as local ones. During a migration period maybe you want to change
the default value of notNullUrl to 'false' in File_thumbnail::byFile(...)
2016-02-10 04:37:43 +01:00
Mikael Nordfeldth
893d888152 Add urlhash field to File_thumbnail for indexing 2016-02-10 04:15:41 +01:00
Mikael Nordfeldth
49b7648fea Managed_DataObject gets onInsert and onUpdate 2016-02-10 03:37:27 +01:00
Mikael Nordfeldth
d0b2d86ca1 background is not used, so removed from documentation 2016-02-10 02:32:17 +01:00
Mikael Nordfeldth
9f961597c6 Document site/sslproxy config value
Should be put into installation configuration too. Maybe a sample
and commented out entry in config.php as well.
2016-02-10 01:11:43 +01:00
Mikael Nordfeldth
a61235086b Use config site/sslproxy to force HTTPS (i.e. using reverse proxy to enable it)
Usage in config.php: $config['site']['sslproxy'] = true;

Add this to documentation...
2016-02-10 01:05:02 +01:00
Mikael Nordfeldth
ec257d940a Either use or don't use HTTPS
The risk of injection attacks using HTTP is too great to allow a
site that allows both HTTP and HTTPS...
2016-02-10 00:57:39 +01:00
Mikael Nordfeldth
dcf29c2a07 s/isHTTPS/useHTTPS/ for HTTPS URL generation 2016-02-10 00:38:14 +01:00
Mikael Nordfeldth
eaa394ed7d bitcoin schema for HTMLPurifier 2016-02-08 20:20:31 +01:00
Mikael Nordfeldth
7c90d7022b Require the license with HTML5 2016-02-08 17:56:35 +01:00
Mikael Nordfeldth
ef5ed10eb9 Log failed captcha entries 2016-02-08 17:51:21 +01:00
Mikael Nordfeldth
cd71188d3a SimpleCaptcha plugin to stop basic bots 2016-02-08 17:47:09 +01:00
Mikael Nordfeldth
a30d34be7f Returnto the previous page when deleting a notice. 2016-02-08 15:30:28 +01:00
Mikael Nordfeldth
d98784e059 Use functions instead of accessing properties in twitterUserArray 2016-02-08 12:21:58 +01:00
Mikael Nordfeldth
3dea259f52 Return intval from getID() 2016-02-08 12:21:46 +01:00
Mikael Nordfeldth
2938b3e960 Don't return true on requiresAuth if screen_name==='0' 2016-02-08 12:14:35 +01:00
Mikael Nordfeldth
13cf744fb3 Allow screennames that are === '0' 2016-02-08 11:40:46 +01:00
Mikael Nordfeldth
2686635f60 Keep the rel="tag" in HTML when purifying 2016-02-07 12:50:26 +01:00
Mikael Nordfeldth
1126f70786 Merge branch 'master' into mmn_fixes 2016-02-07 02:35:44 +01:00
Mikael Nordfeldth
7c7f2f890f Hidespam by default
...why would something described as "Whether to hide silenced users from timelines"
be set to false by default? :)
2016-02-07 02:35:21 +01:00
Mikael Nordfeldth
d6664f5735 Hidespam by default
...why would something described as "Whether to hide silenced users from timelines"
be set to false by default? :)
2016-02-07 02:33:53 +01:00
Mikael Nordfeldth
58e852f7f7 Use the -y parameter for ffmpeg/avconv to be non-interactive 2016-02-07 01:59:21 +01:00
Mikael Nordfeldth
6bec22ea4e Write to the tmp file in VideoThumbnails 2016-02-07 01:57:56 +01:00
Mikael Nordfeldth
25f623565a Catch http exception in StoreRemoteMedia 2016-02-07 01:54:37 +01:00
Mikael Nordfeldth
098c8b1df4 NoHttpResponseException extends HTTP_Request2_ConnectionException 2016-02-07 01:52:20 +01:00
Mikael Nordfeldth
55546a5aab Support ffmpeg and avconv depending on which you have 2016-02-07 01:02:59 +01:00
Mikael Nordfeldth
41e36e1f28 Unknown functionality of a script
Something added 6-8 years ago which we don't use anymore...
2016-02-04 12:16:36 +01:00
Mikael Nordfeldth
1f01356076 Fix issue #127 by catching exceptions
update-profile-data.php threw exceptions on http connection issues
2016-02-04 12:06:35 +01:00
Mikael Nordfeldth
60804d1902 ES3 compatibility layer not necessary (noone uses IE8 etc.)
All browsers with javascript support also support ES5 nowadays. Anyone
using older software should upgrade for other reasons, such as security.
2016-02-04 11:37:24 +01:00
Mikael Nordfeldth
d5ecbd05a1 Forgot a break in a switch when rendering attachments. 2016-02-03 19:32:51 +01:00
Mikael Nordfeldth
9960714896 Disallow zero-length magnet URIs
magnet: would match, but now we have a zero-length lookahead which
requires the following character to be a question mark: magnet:?
2016-02-03 15:26:19 +01:00
Mikael Nordfeldth
90045d66ea HTMLPurifierSchemes plugin to allow geo and magnet URIs 2016-02-03 14:36:51 +01:00
Mikael Nordfeldth
2c83614170 HTMLPurifier caches were included accidentally 2016-02-03 14:35:26 +01:00
Mikael Nordfeldth
349dba8be0 Only allow our specified URI schemes 2016-02-03 14:31:16 +01:00
Mikael Nordfeldth
e903bd0bc3 Hacky support for geo URI detection
Won't work with common_purify yet because there is no geo uri scheme for it
2016-02-03 14:19:08 +01:00
Mikael Nordfeldth
b1ed1f48ea Configurable linkify for bare IPv4/IPv6 2016-02-03 12:55:00 +01:00
Mikael Nordfeldth
25c15119bc Backupaccount is always readonly 2016-02-03 01:15:35 +01:00
Mikael Nordfeldth
84930f89f9 Don't allow account backups by default. 2016-02-03 01:08:36 +01:00
Mikael Nordfeldth
19b743a9f5 Set time limit to increase time backupaccount can take
Wills till run out of memory probably, we should fix that.
2016-02-03 01:04:14 +01:00
Mikael Nordfeldth
9fcfb7cb1d Proper error message on too much POST data 2016-02-03 01:03:58 +01:00
Mikael Nordfeldth
6a4aa34b0c Don't process further in redirection if HEAD gives 400 Bad request 2016-02-03 00:34:49 +01:00
Mikael Nordfeldth
40cffb9463 File::isProtected is static 2016-02-03 00:22:18 +01:00
Mikael Nordfeldth
a2b914ce60 Get URL schemes by URL type 2016-02-03 00:18:37 +01:00
Mikael Nordfeldth
43abfe659b Bump beta number to 4
We have better webfinger @mention@capability.example at least and
OpportunisticQM is somewhat refined.
2016-01-30 00:04:18 +01:00
Mikael Nordfeldth
367fc054dc Merge branch 'master' into mmn_fixes 2016-01-30 00:03:25 +01:00
Mikael Nordfeldth
771f08b3c7 Merge branch 'master' of git.gnu.io:gnu/gnu-social 2016-01-30 00:02:56 +01:00
Mikael Nordfeldth
5167b1fa40 Don't trust local HTML either
If we reallyreally want to include <img> or <script> or whatever then we
have to do that after Notice::saveActivity sets ->rendered.
2016-01-30 00:02:03 +01:00
Mikael Nordfeldth
c6ae883ad2 Don't trust local HTML either
If we reallyreally want to include <img> or <script> or whatever then we
have to do that after Notice::saveActivity sets ->rendered.
2016-01-30 00:00:37 +01:00
Mikael Nordfeldth
a5c1b063fd isPerson did not exist for Ostatus_profile 2016-01-29 16:15:06 +01:00
Mikael Nordfeldth
689e277c62 Allow @localuser@mysite.example to be looked up as a mention 2016-01-29 16:06:16 +01:00
Mikael Nordfeldth
36f099958c Don't match @nickname on @nickname@server.com 2016-01-29 15:53:58 +01:00
Mikael Nordfeldth
cb40f72c7e Use the profile URI when linking instead of URL
since we'll then get to /user/$id instead of /$nickname which is
good for future archives if someone changes their nickname...
2016-01-29 15:21:01 +01:00
Mikael Nordfeldth
5b11238010 Don't use system include path
Sometimes systems have _old_ DB_DataObject classes lying around that
get included by default, so we just try to avoid anything that we don't
ship ourselves.

<MMN-o> BeS: I'll commit a patch that will make this issue go away
<BeS> MMN-o: that would be awesome!
<MMN-o> but it might upset bashrc who's working on a Debian package (where you're _supposed_ to include from /usr/php etc. :P)
<MMN-o> but I'll leave a comment along with it
2016-01-28 20:31:46 +01:00
Mikael Nordfeldth
6b31feb70f Strict Standards: Declaration of MysqlSchema::get()
should be compatible with Schema::get($conn = NULL)
2016-01-28 20:18:06 +01:00
Mikael Nordfeldth
a6898b033d Fullname and location are now text, not varchar, and can be >191|255 2016-01-28 19:12:30 +01:00
Mikael Nordfeldth
c0851d59f5 migrateProfilePrefs added to scripts/upgrade.php
Makes it easier for plugin developers to change the topics set in Profile_prefs
2016-01-28 19:03:24 +01:00
Mikael Nordfeldth
fb7f572eed Purify oembed html (again)
For a commit or two we didn't do this, because htmLawed failed to filter
out CDATA javascript properly, but now we use HTML Purifier which works.
2016-01-28 19:02:16 +01:00
Mikael Nordfeldth
7e6783bb8f Replace htmLawed with HTMLPurifier 2016-01-28 19:01:13 +01:00
Mikael Nordfeldth
daea5647b6 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into mmn_fixes 2016-01-28 17:27:08 +01:00
mmn
9b3cbb373e Merge branch 'oembed_fb_wp_branch' into 'nightly'
Oembed: Fix UTF-8 bug and better wp&fb data (updated!)



See merge request !97
2016-01-28 16:26:33 +00:00
Mikael Nordfeldth
efe23ed404 updateWithKeys now understands multi-column keys
and automatically identifies _which_ columns are the right ones,
so for example 'uri' primary keys don't need to be explicitly set
2016-01-28 16:42:59 +01:00
hannes
05439831e7 add comment that DOMDocument('1.0', 'UTF-8') does not work 2016-01-28 15:32:11 +00:00
hannes
06e325d61b fixes two issues when the oembed thumbnail is blank 2016-01-28 15:19:29 +00:00
Mikael Nordfeldth
d94f9031ff Changelog deleted in php-gettext 2016-01-28 14:43:01 +01:00
Mikael Nordfeldth
10f16e4f32 php-gettext extlib updated as req !88
Thanks postblue
2016-01-28 14:41:25 +01:00
Mikael Nordfeldth
c459bded53 Markdown library updated to 1.6.0, merge req !90
Thanks postblue
2016-01-28 14:37:14 +01:00
Mikael Nordfeldth
fb15dc5080 htmLawed updated (manually merging !89)
Thanks postblue
2016-01-28 14:24:03 +01:00
mmn
bf38fb7bef Merge branch 'upstream-changes/google-analytics-removal' into 'master'
Removed plugin Google-Analytics

... as this is free/libre and decentralized software and should not promote centralized proprietary software. Please see the included Piwik plugin for a more decentralized alternative!

See merge request !94
2016-01-28 12:55:10 +00:00
Mikael Nordfeldth
7c80c9a1f9 Meh, let's just remove FirePHP, I don't think anyone uses it 2016-01-28 13:48:44 +01:00
mmn
dfc11f99ad Merge branch 'update-extlib-firephp' into 'nightly'
Update FirefPHP Core to v0.4.0 released on 23 Apr 2013



See merge request !92
2016-01-28 12:41:34 +00:00
Mikael Nordfeldth
bb0cb9b3f6 Manual merge of !87 Update LDAP2 extlib to 2.2.0 (stable) released on 2015-10-30
Thanks postblue
2016-01-28 13:35:23 +01:00
Mikael Nordfeldth
d73c264e25 Haha, forgot that the $content content must also be there! 2016-01-27 23:04:19 +01:00
Mikael Nordfeldth
34093388a7 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into mmn_fixes 2016-01-27 22:43:37 +01:00
Mikael Nordfeldth
a47563cf3d Don't add space if there's no string in new notice content 2016-01-27 22:41:53 +01:00
Mikael Nordfeldth
03d2b680f8 Allow empty new-notice content via web if there's an upload 2016-01-27 22:39:43 +01:00
mmn
52778e1882 Merge branch 'json_encode_fail_branch' into 'nightly'
output error if json_encode fails



See merge request !86
2016-01-26 21:16:24 +00:00
mmn
42545c6625 Merge branch 'mention_branch' into 'nightly'
correct mentions if parent mentions multiple users with same nickname (don't use first one for all)



See merge request !82
2016-01-26 21:15:25 +00:00
Mikael Nordfeldth
e69715005d Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into mmn_fixes 2016-01-26 22:10:23 +01:00
mmn
c01982c917 Merge branch 'oembed_thumbnail_branch' into 'nightly'
add a thumbnail to oembed response

just something i added to quitim

See merge request !85
2016-01-26 21:09:07 +00:00
hannes
aa76e5863f don't mess upp charsets in oembed/og! check for utf-8 in http header and meta tags, and add prolog when loading html with DOMDocument() 2016-01-26 13:37:52 +00:00
hannes
b8d1e1f4a6 silence errors on these xpath queries 2016-01-26 11:28:24 +00:00
hannes
884aeb4d2e common_purify() doesn't remove wordpress' and facebook's javascript properly, maybe better to keep the data intact, and do strip_tags or something similar when using the data 2016-01-26 01:10:15 +00:00
hannes
473f893d04 detab 2016-01-26 01:07:44 +00:00
hannes
76c8139054 not pretty, but gives us better oembed data for wordpress and facebook 2016-01-26 01:05:53 +00:00
Mikael Nordfeldth
a48055a3cc Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into mmn_fixes 2016-01-25 20:22:16 +01:00
mmn
c0b0f8e2f9 Merge branch 'file_redirection_cleanup_branch' into 'nightly'
Proper URL redirection fix

This greatly reduces the number of times GNU social looks up the same URL with File_redirection::where().

Someone must have mixed up the exlib's internal setting "follow_redirects" with our own. 

This also makes away with the weird backwards recursive handling of protected URLs in File_redirection::lookupWhere(), by saving the chain of redirects the first time HTTPClient:send() looks them up.

See merge request !95
2016-01-25 19:21:52 +00:00
hannes
e447964639 remove my ugly debug info 2016-01-25 19:10:35 +00:00
hannes
b1b6a0a69c config['follow_redirects'] is the extlib's config! if max_redirs is set we want to do our own redirection following in this function 2016-01-25 19:03:26 +00:00
hannes
a888294135 add our own protected urls 2016-01-25 19:00:46 +00:00
hannes
ca0c792ed3 File and File_redirection records are saved in File_redirection::where() now 2016-01-25 19:00:05 +00:00
hannes
48e1a2431b save File and File_redirection records in File_redirection::where(), because then we will have to run where() over and over again 2016-01-25 18:55:48 +00:00
Mikael Nordfeldth
b15434375c Show plain text files on attachment page. 2016-01-25 16:54:40 +01:00
Roland Haeder
9614aba0e1 Removed plugin Google-Analytics as this is free/libre and decentralized
software and should not promote centralized proprietary software. Please see
the included Piwik plugin for a more decentralized alternative!

Signed-off-by: Roland Haeder <roland@mxchange.org>
2016-01-25 13:19:43 +01:00
postblue
fef52d7b51 Update FirefPHP Core to v0.4.0 released on 23 Apr 2013 2016-01-24 21:11:38 +01:00
Mikael Nordfeldth
a9d18a077e Harmonize, clarify, categorize URL schemes
Regular expression + avoid-redirection list now match each other.
2016-01-24 12:47:31 +01:00
Mikael Nordfeldth
1cec627d72 Allow bitcoin scheme to URLs 2016-01-24 12:44:28 +01:00
hannes
4e50717e12 output error if json_encode fails 2016-01-23 15:01:27 +00:00
Mikael Nordfeldth
ef005987a1 Did the OpportunisticQM fixes in the wrong order 2016-01-22 12:26:53 +01:00
Mikael Nordfeldth
1121b38eb1 use connect_timeout value for execution margin 2016-01-22 12:21:06 +01:00
Mikael Nordfeldth
89dd44bf3e default connect_timeout to 5 instead of extlib 10 2016-01-22 12:20:03 +01:00
Mikael Nordfeldth
81f9a59f25 use connect_timeout value for execution margin 2016-01-22 12:19:17 +01:00
hannes
d0e2f8745d add a thumbnail to oembed response 2016-01-21 18:48:30 +00:00
Mikael Nordfeldth
3f9c1c142a Removing unnecessary debug messages etc. 2016-01-21 02:49:34 +01:00
Mikael Nordfeldth
45446f17ad Only set selectVerbs if it's not set in class var 2016-01-21 02:37:38 +01:00
Mikael Nordfeldth
be1759f112 i18n 2016-01-21 02:37:07 +01:00
Mikael Nordfeldth
d3a4a2225f We want the profile stream to be as raw as possible! 2016-01-21 02:33:43 +01:00
Mikael Nordfeldth
81bf0fd261 Various last fixes to RSVP I think 2016-01-21 02:20:57 +01:00
Mikael Nordfeldth
f74d2d555c Working on some RSVP code stuff 2016-01-21 02:10:34 +01:00
Mikael Nordfeldth
5999171c11 Throw NoObjectTypeException on Notice->getObjectType if no string 2016-01-20 21:37:14 +01:00
Mikael Nordfeldth
45b523bada Add xcal namespaces to location and url in event
Also, for fun, add stuff for RSS event module, see:
   http://web.resource.org/rss/1.0/modules/event/
2016-01-20 21:07:55 +01:00
Mikael Nordfeldth
21cc737f5c Cancelling RSVPs now seems to work. 2016-01-20 16:10:10 +01:00
Mikael Nordfeldth
9accd953e4 Merge branch 'master' into mmn_fixes 2016-01-20 15:33:18 +01:00
Mikael Nordfeldth
64e74d527f Handle exceptions when salmon slapping
Make it so notifyDeferred actually _always_ throws exceptions and handle
them in the places it is called.
2016-01-20 15:32:39 +01:00
Mikael Nordfeldth
c393bc9563 In very specific circumstances we can bulkDistribute 0 notices
Seems to be what caused an infinite loop on quitter.es, or I guess so anyway.
2016-01-20 15:32:29 +01:00
Mikael Nordfeldth
912d65c767 bulkDistribute won't add empty lists to database 2016-01-20 15:32:24 +01:00
Mikael Nordfeldth
fa8e02b832 Handle exceptions when salmon slapping
Make it so notifyDeferred actually _always_ throws exceptions and handle
them in the places it is called.
2016-01-20 14:56:24 +01:00
hannes
de047f9727 correct mentions if parent mention multiple users with same nickname (don't use first one for all) 2016-01-19 13:41:25 +00:00
Mikael Nordfeldth
80dc2788dd Started fiddling with CancelRSVP but more must be done
Remember to make event_uri be the selector for CancelRSVPForm and
preferrably even merge it into RSVPForm!
2016-01-19 01:41:06 +01:00
Mikael Nordfeldth
477d71c0bf RSVP stuff, mostly forms.
Now fix CancelRSVP stuff so it gets by event_uri and can cancel existing RSVP.
2016-01-19 01:33:09 +01:00
Mikael Nordfeldth
84dda697d6 RSVPs seem to be created now, just gotta fix CancelrsvpAction 2016-01-19 01:10:06 +01:00
Mikael Nordfeldth
73992a1ed8 Use "newer" terminology and throw exceptions 2016-01-19 00:21:16 +01:00
Mikael Nordfeldth
9eea255c79 Save with options so we get source=web 2016-01-19 00:08:31 +01:00
Mikael Nordfeldth
358684a5ed end_str, not start_str 2016-01-19 00:01:30 +01:00
Mikael Nordfeldth
385705c65b Events get rendered. 2016-01-18 23:58:32 +01:00
Mikael Nordfeldth
15d12b209d Don't include delete verbs in profile notice stream. 2016-01-18 22:04:42 +01:00
Mikael Nordfeldth
7715ea993b getVerb function in Notice class 2016-01-18 22:02:05 +01:00
Mikael Nordfeldth
f768de4b46 default connect_timeout to 5 instead of extlib 10 2016-01-18 22:01:45 +01:00
Mikael Nordfeldth
cae344b67b Events are now saved but not displayed properly again 2016-01-18 20:57:44 +01:00
Mikael Nordfeldth
6c46a93a81 Minor improvements on Notice::saveActivity 2016-01-18 19:29:45 +01:00
Mikael Nordfeldth
486a02d60d First steps on making NeweventAction a FormAction
Also saving new Happening objects via Notice::saveActivity
2016-01-18 18:42:42 +01:00
Mikael Nordfeldth
11b925c4ca Use a function to getCreated() 2016-01-18 17:03:37 +01:00
Mikael Nordfeldth
c1655c2c0f Merge branch 'master' into mmn_fixes 2016-01-17 00:41:43 +01:00
Mikael Nordfeldth
b0416ae592 Initial user doesn't need as strict checking on email 2016-01-17 00:41:03 +01:00
Mikael Nordfeldth
78346af572 Merge branch 'master' of git.gnu.io:gnu/gnu-social 2016-01-17 00:40:14 +01:00
Mikael Nordfeldth
bdc38a7204 Initial user doesn't need as strict checking on email 2016-01-17 00:39:49 +01:00
Mikael Nordfeldth
6dc0477c00 Let the remote side know the Salmon was accepted (sorta) 2016-01-16 22:39:59 +01:00
Mikael Nordfeldth
deda83fdef Distinguish notice saving errors from others for Salmon 2016-01-16 22:39:04 +01:00
Mikael Nordfeldth
4678546d33 We want exceptions to be noticable in activityhandlerplugin 2016-01-16 21:19:34 +01:00
Mikael Nordfeldth
a85a08ff35 Add a check in Notice->asActivityObject that we're actually returning such a thing! 2016-01-16 21:13:26 +01:00
Mikael Nordfeldth
0797ee0871 EmptyIdException doesn't carry ->obj 2016-01-16 21:12:53 +01:00
Mikael Nordfeldth
3019f8f23f dbqueuemanager logic 2016-01-16 21:05:34 +01:00
Mikael Nordfeldth
fdfa71a033 Extremely verbose debugging is annoying 2016-01-16 18:04:04 +01:00
Mikael Nordfeldth
c559b8ce2a bulkDistribute won't add empty lists to database 2016-01-16 17:34:27 +01:00
Mikael Nordfeldth
f53ebdeadb Start handling salmon entries directly with Notice::saveActivity
More to come...
2016-01-16 17:25:29 +01:00
Mikael Nordfeldth
1f76c1e4a9 Initial user doesn't need as strict checking on email 2016-01-16 17:23:50 +01:00
Mikael Nordfeldth
2b67b53112 In very specific circumstances we can bulkDistribute 0 notices
Seems to be what caused an infinite loop on quitter.es, or I guess so anyway.
2016-01-16 17:18:14 +01:00
mmn
44c10bb2aa Merge branch 'oembed_branch' into 'nightly'
purify oembed html and don't allow cdata

hopefully we never need stuff in cdata

reason for this is that this link serves javascript in its oembed data: https://www.maketecheasier.com/switch-windows-10-to-linux/

see:
https://www.maketecheasier.com/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fwww.maketecheasier.com%2Fswitch-windows-10-to-linux%2F

i don't feel we want that in our database.  

See merge request !79
2016-01-15 13:11:35 +00:00
Mikael Nordfeldth
a1530b0e68 Removing superflous debug stuff 2016-01-14 22:12:36 +01:00
Mikael Nordfeldth
8df38df5cb Get uploaded media_ids via File::getByID()
Filters out failed ones...
2016-01-14 22:05:03 +01:00
Mikael Nordfeldth
16088d9439 ErrorAction and InfoAction fixes, are now ManagedAction 2016-01-14 21:28:47 +01:00
Mikael Nordfeldth
bc0a903bd4 ErrorAction to autodiscoverable file. 2016-01-14 21:21:34 +01:00
Mikael Nordfeldth
818fb05cfe role examples in userrole.php 2016-01-14 20:34:12 +01:00
Mikael Nordfeldth
e5019c3858 Accept media_ids parameter from Twitter API v1.1 clients
They upload the media first and then provide media IDs in a comma
separated parameter (but we just split it to all whole integers).
2016-01-14 20:24:01 +01:00
Mikael Nordfeldth
b570f0d3cb Media files are accepted and uploaded via Twitter API now 2016-01-14 19:15:02 +01:00
Mikael Nordfeldth
2af9de4f23 Minor fixes in Linkback plugin 2016-01-14 19:14:24 +01:00
Mikael Nordfeldth
b530d385bc Exception object was not supplied there 2016-01-14 18:51:46 +01:00
Mikael Nordfeldth
0caf0612d0 Make Twitter Media upload API v1.1 reach us
Now we just have to accept the 'media' or 'media_data' (base64 encoded)
POST arguments instead of $_FILES uploads.
2016-01-14 18:29:21 +01:00
Mikael Nordfeldth
6e49281adb Use the Action class itself as error handler 2016-01-14 18:21:11 +01:00
Mikael Nordfeldth
2f1bfe126b Debug message formatting gone wrong 2016-01-14 13:52:11 +01:00
Mikael Nordfeldth
c173c4faa5 Actually throw NoQueueHandlerException too. 2016-01-14 13:48:33 +01:00
Mikael Nordfeldth
59e75ef966 Incorrect use of getByHashKey in HubSub 2016-01-14 13:15:31 +01:00
Mikael Nordfeldth
f092026541 Documentation fix from master 2016-01-14 13:11:27 +01:00
Mikael Nordfeldth
df00a88cb4 Forgot to add NoRouteMapException to 6834f355f2 2016-01-14 13:07:00 +01:00
Mikael Nordfeldth
bacd49a6a8 Don't try to replace http with https if https already exists 2016-01-14 13:06:37 +01:00
mmn
0781c2a7b9 Merge branch 'fix_readme' into 'master'
update readme for OpportunisticQM: array need to contain key/value pairs

according to the doc-block of addPlugin() the array should contain key/value pairs, see https://git.gnu.io/gnu/gnu-social/blob/master/lib/gnusocial.php#L46

I updated the readme for the OpportunisticQM plugin to reflect this.

See merge request !81
2016-01-14 12:03:11 +00:00
Björn Schießle
b7c849b5b0 array need to contain key/value pairs 2016-01-14 12:39:39 +01:00
Mikael Nordfeldth
83cb1dfa68 Salmon debugging 2016-01-14 03:48:41 +01:00
Mikael Nordfeldth
6834f355f2 Making ClientExceptions turn into ClientErrorAction
Got some 404s which were presented as 500
2016-01-14 02:47:28 +01:00
Mikael Nordfeldth
99261e0781 Don't LOG_ERR missing paths (misspelling clients aren't errors) 2016-01-14 02:22:47 +01:00
Mikael Nordfeldth
53339ff463 Fake oEmbed version in OpenGraph return object 2016-01-14 02:09:12 +01:00
Mikael Nordfeldth
331abf173b Forgot semicolon 2016-01-14 02:05:33 +01:00
Mikael Nordfeldth
f699ffeb8a Exception handling in queue handler logic 2016-01-14 02:04:15 +01:00
Mikael Nordfeldth
cf7d2f4d0f Salmon queue handler getByID for exception throwing 2016-01-14 01:51:00 +01:00
Mikael Nordfeldth
0ddaa6ff75 Handle exceptions in QueueHandler classes 2016-01-14 01:47:13 +01:00
Mikael Nordfeldth
0482b7de8e Debugging by indexing callback column in HubSub 2016-01-13 22:57:42 +01:00
Mikael Nordfeldth
24d9d76644 OpenGraph image/thumbnail width and height 2016-01-13 22:07:39 +01:00
Mikael Nordfeldth
1e5770bbcf $this->notice is never unset after prepare 2016-01-13 21:34:15 +01:00
Mikael Nordfeldth
adba38ce20 Deleted_notice is pluginified, don't call directly from core 2016-01-13 21:29:23 +01:00
Mikael Nordfeldth
45dd343126 Eventify Notice getAsTimestamp (for Deleted_notice) 2016-01-13 21:01:47 +01:00
Mikael Nordfeldth
29b45bb87a Unnecessary call to User::getKV 2016-01-13 20:08:17 +01:00
Mikael Nordfeldth
8ab98b72ac getLeaseRemaining for FeedSub too 2016-01-13 20:01:00 +01:00
Mikael Nordfeldth
377947c57f s/getLease/getLeaseTime/ just to be a bit more clear 2016-01-13 19:55:17 +01:00
Mikael Nordfeldth
1d26fedf12 Don't store lease seconds, just sub start and end
The seconds can always be calculated from the dates!
2016-01-13 19:45:20 +01:00
Mikael Nordfeldth
494746e665 Minor PuSH comment and debug stuff 2016-01-13 19:25:39 +01:00
Mikael Nordfeldth
a5fd4fde25 Request a month long sub lease by default 2016-01-13 19:24:07 +01:00
Mikael Nordfeldth
b38a789005 HubSub didn't save sub start and end datetimes 2016-01-13 19:23:34 +01:00
Mikael Nordfeldth
818aaa0578 We didn't get profiles from the new-style attention system 2016-01-13 18:35:25 +01:00
Mikael Nordfeldth
961725205d Try if Profile is a Person (in effect ActivityObject::PERSON) 2016-01-13 18:34:48 +01:00
hannes
ee305891c4 purify oembed html 2016-01-13 16:03:38 +00:00
hannes
3e7e3de554 don't allow cdata elements in purified html 2016-01-13 16:01:27 +00:00
Mikael Nordfeldth
5c262a788d Unused, unnecessary and intrusive cookie/storage removed! 2016-01-13 15:22:28 +01:00
Mikael Nordfeldth
3720e37f06 property attribute could be null in meta tags of course 2016-01-13 14:24:00 +01:00
Mikael Nordfeldth
99da1ebe41 Catch NoHttpResponseException when using HTTPClient 2016-01-13 14:17:49 +01:00
Mikael Nordfeldth
3ed632decf NoHttpResponseException needed instead of HTTP_Request2_Exception
HTTP_Request2_Exception assumed an HTTP response status code/line
2016-01-13 14:08:48 +01:00
Mikael Nordfeldth
e75472f460 Use the upstream function to get effectiveUrl 2016-01-13 14:00:05 +01:00
Akio Nishimura
eceafb84de lib/language.php: rewrited jp as ja. 2016-01-13 13:23:06 +01:00
Mikael Nordfeldth
14bd6daab1 Aspect ratio on elements in inline-attachment list 2016-01-12 23:12:41 +01:00
Mikael Nordfeldth
e49e113140 Ugly hack to show thumbnails of otherwise unrepresentable attachments
such as text/html, where the thumbnail has been retrieved via oEmbed/OpenGraph
2016-01-12 15:38:59 +01:00
Mikael Nordfeldth
3658774429 Super-basic OpenGraph image preview support, "works for me" 2016-01-12 15:29:03 +01:00
Mikael Nordfeldth
f4feef477b Don't follow redirects on PuSH POST. 2016-01-12 14:32:28 +01:00
Mikael Nordfeldth
b696fb4eb1 Mimetypes like image/svg+xml were misinterpreted when guessing
We don't really need the mb_ in mb_strtolower but what the heck, why not. I love Unicode.
2016-01-12 13:46:25 +01:00
Mikael Nordfeldth
8c28e54ccc same as previous, but for mime_to_ext 2016-01-12 13:14:17 +01:00
Mikael Nordfeldth
dbe5d72e4c If all file extensions are supported we have no list of comparisons 2016-01-12 13:08:54 +01:00
mmn
d07640caba Merge branch 'common_purify_branch' into 'nightly'
EndCommonPurify event

Because we have problems with htmLawed on quitter.se, we want to check its output and maybe use another HTML purifier... 

See merge request !78
2016-01-11 21:14:41 +00:00
mmn
e4a1dff98d Merge branch 'apiaccountregister_event_branch' into 'nightly'
APIStartRegistrationTry event



See merge request !77
2016-01-11 21:08:09 +00:00
mmn
e94017a552 Merge branch 'hidden-h2s' into 'nightly'
Show H2s in notice content

With notices coming from other sources (e.g. Diaspora) and rich-text plugins
such as Textile/Markdown/TinyMCE, it's possible notices include H2 we want
to display.

See merge request !76
2016-01-11 21:01:26 +00:00
hannes
a1b509bb0b forgot we need access to $html too 2016-01-11 20:58:34 +00:00
hannes
8d331b0f35 EndCommonPurify event 2016-01-11 20:54:19 +00:00
hannes
543a7e421e APIStartRegistrationTry event 2016-01-11 20:04:31 +00:00
Mikael Nordfeldth
c826fe0af4 $target was klantigtly copied from another debug message 2016-01-11 20:10:38 +01:00
Mikael Nordfeldth
bd6efa0e45 Update PuSH callback URL if remote side switched to HTTPS
See the comment in the source on why we're not following Location headers...
2016-01-11 19:55:02 +01:00
Mikael Nordfeldth
f24cdf4a80 Much more logging in PushHubAction (OStatus) 2016-01-11 19:54:05 +01:00
Mikael Nordfeldth
1a46d86ca6 lib/util.php quick function to do var_export($var,true)
Immensely useful when debugging and we want to put quotes around strings,
potentially stopping any "evil logging attacks" (where input data masks
as logging data).
2016-01-11 19:52:54 +01:00
Mikael Nordfeldth
93c614c184 Do updateWithKeys properly if someone would like to update PRI 2016-01-11 19:52:33 +01:00
Mikael Nordfeldth
c1f22f106b Might as well put a $limit on preg_replace here
Since there will (should) never be more than one ^http in that string anyway.
2016-01-11 18:27:26 +01:00
Mikael Nordfeldth
5b2b969a77 Tag notice streams should only show post verbs 2016-01-11 15:15:23 +01:00
Mikael Nordfeldth
8acf930c45 OpportunisticQM matches against _system_ max_execution_time
Probably never runs if max_execution_time is 0. I'll handle that later.
2016-01-11 13:35:29 +01:00
Mikael Nordfeldth
b13f8df79b HTTPClient would return null instead of exception
This caused $response->isOK() tests to call a function on a non-existing object, causing all hell to break loose.
2016-01-11 02:36:59 +01:00
Chimo
63fff7ef47 Show H2s in notice content
With notices coming from other sources (e.g. Diaspora) and rich-text plugins
such as Textile/Markdown/TinyMCE, it's possible notices include H2 we want
to display.
2016-01-10 15:37:30 +00:00
Mikael Nordfeldth
87ae5292b8 NSTALL/UPGRADE documentation edits
No more background folder for example. Also comments on chmod a+w
2016-01-10 10:44:16 +01:00
Mikael Nordfeldth
e498bc6b7b Control OpportunisticQM verbosity 2016-01-10 00:51:25 +01:00
Mikael Nordfeldth
7fbf72f9c1 Minor changes to OpportunisticQM for debugging 2016-01-10 00:48:04 +01:00
Mikael Nordfeldth
42dff2742a Put salmon slaps in queues before pushing user Atom feed 2016-01-10 00:29:32 +01:00
Mikael Nordfeldth
67801a5566 Not only local groups should be added to group inbox notificiation table 2016-01-09 15:14:38 +01:00
Mikael Nordfeldth
5ef10a14ef Get group attentions too for outbound notices 2016-01-09 15:06:44 +01:00
Mikael Nordfeldth
c3c5a9974d Do proper fromUri lookup on groups too 2016-01-09 14:36:47 +01:00
Mikael Nordfeldth
55aa68b941 CancelGroupForm gets same typing as Join and Leave 2016-01-09 14:13:19 +01:00
Mikael Nordfeldth
fbec7c4e75 Issue #121 - use correct Group ID and strict User_group typing 2016-01-09 14:06:50 +01:00
Mikael Nordfeldth
d1e609a886 Local user profile getUrl gives dynamically generated URL 2016-01-09 13:47:38 +01:00
Mikael Nordfeldth
f143925931 Actually return the group home URL 2016-01-09 13:33:30 +01:00
Mikael Nordfeldth
d13483ca20 Wups, $poster could be undefined 2016-01-09 13:15:09 +01:00
Mikael Nordfeldth
33194b3cff Attention goes to the parent notice author too 2016-01-08 02:58:31 +01:00
Mikael Nordfeldth
28cd5640c1 Save attentions to Reply table for backwards compatibility
local users only of course
2016-01-08 01:59:16 +01:00
Mikael Nordfeldth
0463d96392 Add more info in logging call
There shouldn't be *HandleSalmonTarget, only verification of actor and
then Notice::saveActivity()!
2016-01-08 01:52:10 +01:00
Mikael Nordfeldth
4e0ed61f7c OStatus queue handler uses Notice->getAttentionProfiles()
and lots more debugging for LOG_DEBUG
2016-01-08 01:31:47 +01:00
Mikael Nordfeldth
e6f2676c5c Default to not include delete verbs in notice streams 2016-01-07 23:33:47 +01:00
Mikael Nordfeldth
c48871cf1b Notice from web now saves context->attention too! ;) 2016-01-07 23:24:15 +01:00
Mikael Nordfeldth
6255e1dca3 saveAttentions, trying to move away from saveReply 2016-01-07 23:23:51 +01:00
Mikael Nordfeldth
801ca3531b common_find_attentions to populate activities from content text 2016-01-07 23:23:37 +01:00
Mikael Nordfeldth
3eebf2a353 Don't store the notice object for distrib queue handler, just id 2016-01-07 20:24:59 +01:00
Mikael Nordfeldth
cd2695190d BeforePluginCheckSchema event for checkschema.php 2016-01-07 18:45:36 +01:00
Mikael Nordfeldth
1f02dc639e shortenLinks _after_ media upload to be consistent with api 2016-01-07 18:14:45 +01:00
Mikael Nordfeldth
be58fd64f5 Use index for File url (urlhash) 2016-01-07 18:13:10 +01:00
Mikael Nordfeldth
6505504411 shortenLinks with a capital L 2016-01-07 18:12:29 +01:00
Mikael Nordfeldth
d4be5349b3 think I have managed to show oEmbed images better now 2016-01-07 17:35:37 +01:00
Mikael Nordfeldth
9e5c71e701 Fixed group representation in Directory plugin, also some ->raw calls 2016-01-07 12:58:14 +01:00
Mikael Nordfeldth
c02f23e63f Return Profile objects from getGroups in GroupdirectoryAction 2016-01-07 12:35:52 +01:00
Mikael Nordfeldth
5d4b1d0b88 Appropriate exception message in GroupNoProfileException 2016-01-07 12:14:48 +01:00
Mikael Nordfeldth
839ae571d8 Define User_group::$profile_id among class variables 2016-01-07 12:12:27 +01:00
Mikael Nordfeldth
e7308b0ecb Max execution time margin for OpportunisticQM
I guess it could continue a bit too long in case it got a really long
(failing, timeouting) HTTP request just before max_execution_time hit.
2016-01-07 11:58:09 +01:00
Mikael Nordfeldth
94392ab00c Index the verb column since we do COUNT(*) based on it 2016-01-07 11:39:46 +01:00
Mikael Nordfeldth
05d3622939 Conversation entries where id==0 would screw up the "re-auto-increment" sequencing 2016-01-06 22:26:17 +01:00
Mikael Nordfeldth
6924bb1d29 Gah and a 302 in the non-exception-handling redirect too 2016-01-06 20:09:59 +01:00
Mikael Nordfeldth
a9b5519293 Attachment thumbnail redirect is a 302 redirect
It SHOULD be cached but it might no longer be valid if we empty cache or something.
2016-01-06 20:07:40 +01:00
Mikael Nordfeldth
4d179c6b0c Our URLs are permanent redirects, mind you! 2016-01-06 20:06:14 +01:00
Mikael Nordfeldth
47c7e1b875 Breaking class definitions out into separate files and fixing typing 2016-01-06 19:46:56 +01:00
Mikael Nordfeldth
f1c4c64cd9 Don't update stored URLs just because we have a filename
This would overwrite remote URLs with local verisons which removes source href...
The reason one might have filenames for remote URLs is that StoreRemoteMedia plugin
fetches them and uses the filename field.
2016-01-06 19:24:03 +01:00
Mikael Nordfeldth
346e34e543 Return dynamically generated URLs for thumbnails for all locally stored entries 2016-01-06 19:06:29 +01:00
Mikael Nordfeldth
4352d1739d Always generate notice URLs on the fly for local notices
Related to issue #118
2016-01-06 16:24:33 +01:00
Mikael Nordfeldth
1a1e44cdfd Issue #118 wanted better TOR support, now Avatar URLs are not stored
There was no reason to store the generated Avatar URLs because it's so
cheap to generate them on the fly.
2016-01-06 16:14:26 +01:00
Mikael Nordfeldth
7b2036a4b5 Note in the debug log if we render the HTML of a message 2016-01-06 15:37:30 +01:00
Mikael Nordfeldth
b596391fcd Avoid having to check for notices without rendered copies in upgrade.php
Always call the Notice->getRendered() function to get a rendered copy.
We could perhaps put some sanitation there too in the future
2016-01-06 15:32:27 +01:00
Mikael Nordfeldth
d6ac002639 Get conversation ID from child too in scripts/upgrade.php 2016-01-06 14:57:30 +01:00
Mikael Nordfeldth
4a8e936e19 Somewhat more meaningful error message 2016-01-06 14:10:37 +01:00
Mikael Nordfeldth
0fd2ad649e Conversation IDs (again) no longer based on Notice ID 2016-01-06 13:58:46 +01:00
Mikael Nordfeldth
4b22b0c42a More listitems and lists into separate files + stronger typing 2016-01-06 01:36:46 +01:00
Mikael Nordfeldth
6d9f390ba8 Separating classes into files and stronger typing 2016-01-06 01:30:12 +01:00
Mikael Nordfeldth
da2f179ae9 Typing to Profile 2016-01-06 01:25:00 +01:00
Mikael Nordfeldth
e577e883f4 Subscriber lists to separate files and also Profile typing 2016-01-06 00:57:31 +01:00
Mikael Nordfeldth
59d3bbe037 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into mmn_fixes 2016-01-06 00:53:20 +01:00
mmn
d45457e4a9 Merge branch 'nightly' into 'nightly'
only count post-notices (i.e. don't include activity-notices in statuses_count in the API)



See merge request !73
2016-01-05 23:53:08 +00:00
Mikael Nordfeldth
1946197a1c Merge request #10 by aroque but in a slightly different version 2016-01-06 00:48:03 +01:00
hannes
32549cfd8c $notices->verb = ActivityVerb::POST; 2016-01-05 23:29:48 +00:00
mmn
2c5460eb0e Merge branch 'openid-plugin' into 'nightly'
OpenID plugin: 'openid_only' should be 'openidonly'

To match everywhere else.

See merge request !72
2016-01-05 23:28:28 +00:00
Mikael Nordfeldth
31c8416a8f Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into mmn_fixes 2016-01-06 00:24:41 +01:00
mmn
956e053da6 Merge branch 'lookup_url_fix' into 'nightly'
makes url lookup work and adds full url to href for shortened links



See merge request !74
2016-01-05 23:24:16 +00:00
Mikael Nordfeldth
792b62874e Require Profile for Profile->getLists 2016-01-06 00:18:10 +01:00
hannes
0b4b0de412 longurl in href 2016-01-05 23:14:51 +00:00
hannes
1c25cffbd8 add $redir->redir_url to use in common_linkify() 2016-01-05 23:07:24 +00:00
Mikael Nordfeldth
3d66d960a1 Require Profile for Profile->getOtherTags 2016-01-06 00:07:15 +01:00
hannes
8b78e01d4c $longurl->url is just the same $canon we fed to File_redirection::where() 2016-01-05 23:06:02 +00:00
hannes
64f2f3d976 effectiveUrl seems to contain the url we want 2016-01-05 22:44:06 +00:00
Mikael Nordfeldth
8ad6b8809a Don't abort on too long notices in Notice::saveActivity 2016-01-05 17:31:09 +01:00
hannes
e10691abea only count post-notices (i.e. don't include activity-notices in statuses_count in the API) 2016-01-05 14:19:33 +00:00
Mikael Nordfeldth
9a75778b29 If there's no Happening, we can't use the RSVP. 2016-01-05 15:00:34 +01:00
Mikael Nordfeldth
3471213d1c processFeed would abort on certain errors where findLocalObject failed 2016-01-05 15:00:07 +01:00
Mikael Nordfeldth
ab93bb009c XSS vulnerability when remote-subscribing
->raw was used on non-filtered strings for some reasons, changed
to ->text.
2016-01-05 12:15:50 +01:00
Mikael Nordfeldth
df8c14d66a ProfileMiniList->newListItem changed to match ProfileList->newListItem 2016-01-05 00:09:47 +01:00
Mikael Nordfeldth
701f6ff608 Allow Profile->getCurrentNotice to send scoped profile info
not that we're using it anywhere (yet anyway).
2016-01-04 21:42:24 +01:00
Mikael Nordfeldth
6a16939830 Profile->getCurrentNotice handles PrivateStreamException 2016-01-04 21:38:41 +01:00
Chimo
bf0df016e5 OpenID plugin: 'openid_only' should be 'openidonly'
To match everywhere else.
2016-01-04 10:36:05 -05:00
Mikael Nordfeldth
de7e8c59e8 Version bump since we've fixed a lot of bugs 2016-01-04 02:19:37 +01:00
Mikael Nordfeldth
fb537fb7f4 We would end up with a Managed_DataObject if now match was found
meaning we'd return for example a Notice with empty id (translated into 0)
and thus Faves coming in from remote instances where the fave'd notice was
not found would result in faving the first Notice in a table-wide search,
i.e. often the first post on the instance.

Whoopie!
2016-01-04 02:04:18 +01:00
Mikael Nordfeldth
065e23b1c4 Code cleanup in lib/activityutils.php 2016-01-04 01:58:45 +01:00
Mikael Nordfeldth
00ace6c2df More explicit catch 2016-01-04 01:53:33 +01:00
Mikael Nordfeldth
f7a1c8a94c The $options array isn't used here 2016-01-04 01:48:54 +01:00
Mikael Nordfeldth
34b25e6afc Use EmptyIdException in Fave deletion try-catch 2016-01-03 22:57:28 +01:00
Mikael Nordfeldth
b4b57bba54 EmptyIdException to make sure we get the right in catch 2016-01-03 22:56:48 +01:00
Mikael Nordfeldth
bda30a92bc Fave deletion would fail in some cases with missing profiles or notices 2016-01-03 22:35:49 +01:00
Mikael Nordfeldth
7266a968f9 Err, forgot semicolon. 2016-01-03 22:22:58 +01:00
Mikael Nordfeldth
f467b89f40 Try-catch on profile deletion unsubscribe actions 2016-01-03 22:21:03 +01:00
Mikael Nordfeldth
3bddf01350 Somewhat better layout for approving/rejecting subrequests 2016-01-03 20:42:21 +01:00
Mikael Nordfeldth
c19964094b Pending subscription requests now work as they should
A slight layout issue with the buttons still persists
2016-01-03 20:27:53 +01:00
Mikael Nordfeldth
b374e5f08b If a user has a private stream, assume a moderated subscription policy 2016-01-03 19:33:26 +01:00
Mikael Nordfeldth
df0f9547b5 Handle private streams better (failed to show profile before) 2016-01-03 19:16:29 +01:00
Mikael Nordfeldth
7df8a6b731 This version of the EventPlugin won't work with StatusNet any longer 2016-01-03 16:23:44 +01:00
Mikael Nordfeldth
aa3028e5d9 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into mmn_fixes 2016-01-03 16:23:07 +01:00
mmn
3251ef3b51 Merge branch 'beautify_profile_list' into 'nightly'
Beautify profiles and groups lists

Some css fixes to make groups and profiles list more consistent:

In base:

* Remove bullets that were floating around
* Reduce font size to fit and look better
* Group nickname is bold
* Same location and full name layout in both groups and profile lists

In neo-gnu:

* Align vertically location  

In neo-quitter:

* Make group list look like profile one
* Small adjustments due to changes to base

How it looks with neo-gnu

Profile:

![profile](https://git.gnu.io/gnu/gnu-social/uploads/c403c85c323bdcaa2fbb7ab10539ad39/profile.png)

Groups:

![group](https://git.gnu.io/gnu/gnu-social/uploads/51276daa838c404bd63cc04b208beaa6/group.png)

How it looks with neo-quitter

Profile:

![profile](https://git.gnu.io/gnu/gnu-social/uploads/d16bc2587025769dac9e2b46a0589999/profile.png)

Group:

![group](https://git.gnu.io/gnu/gnu-social/uploads/125670c8bcf08bcabf2cac0e0bb0661b/group.png)


See merge request !26
2016-01-03 15:22:32 +00:00
Mikael Nordfeldth
95d415257a Merge branch 'nightly' into singpolyma/gnu-social-events-saveObjectFromActivity
Conflicts:
	plugins/Event/EventPlugin.php
	plugins/Event/classes/RSVP.php

I just fixed 'em with magic!
2016-01-03 13:08:34 +01:00
Mikael Nordfeldth
0dc7fcce5b Fixes issue #94 with undefined 'fr'
I just copied the English example...
2016-01-03 00:45:47 +01:00
Mikael Nordfeldth
336f099241 Don't store object type for verbs (as they don't have it) 2016-01-02 16:05:20 +01:00
Mikael Nordfeldth
fe328ae5e4 Fix because qvitter supplies a twitter array value which is an array
works for json API, not for XML, so we make an exception here...
2016-01-02 02:01:54 +01:00
Mikael Nordfeldth
cf6e06a5dd Avoid exception for invalid URL aborting rendering for parent notice if it fails 2016-01-02 00:09:10 +01:00
Mikael Nordfeldth
0dfafe2567 NewnoticeAction now uses Notice::saveActivity(...) 2016-01-01 20:18:54 +01:00
Mikael Nordfeldth
26a6eca94e Location::fromOptions(Notice::locationOptions(...)) 2016-01-01 19:29:31 +01:00
Mikael Nordfeldth
f4309ed715 whitespace switch for code layout 2016-01-01 18:43:37 +01:00
Mikael Nordfeldth
e02c10a589 common_render_content doesn't require a Profile now 2016-01-01 18:40:58 +01:00
Mikael Nordfeldth
54325e266f upgrade script makes sure all notices have rendered copies 2016-01-01 18:30:38 +01:00
Mikael Nordfeldth
ad4beb9091 ->getID() instead of ->id 2016-01-01 18:21:54 +01:00
Mikael Nordfeldth
10973dcf69 Don't require a notice object to common_linkify_mentions 2016-01-01 18:20:42 +01:00
Mikael Nordfeldth
86106b890a Output fixed sizes of thumbnails so we don't get jumpy scrolling 2016-01-01 14:13:16 +01:00
Mikael Nordfeldth
4fc2b2584b RSVPs refer to Happening (event) by URI instead of ID now 2015-12-31 19:23:05 +01:00
Mikael Nordfeldth
34ce2f6cfa minor happening changes 2015-12-31 18:08:12 +01:00
Mikael Nordfeldth
f6df44ea85 Handle feed imports with exceptions better 2015-12-31 15:05:35 +01:00
Mikael Nordfeldth
7adf1c99fc s/LOG_ERROR/LOG_ERR/ 2015-12-31 13:04:50 +01:00
Mikael Nordfeldth
7f1ce07e9f Logging destinations and unnecessary debug 2015-12-31 13:00:20 +01:00
Mikael Nordfeldth
fab745c6d6 Exception throwing and proper db retrieval
ActivityModeration plugin and its Deleted_notice class.
2015-12-31 12:42:33 +01:00
Mikael Nordfeldth
6606781916 Proper error message in exception 2015-12-31 12:41:02 +01:00
Mikael Nordfeldth
5ba6be1a87 Deleted_notice had a superfluous field 2015-12-31 12:33:59 +01:00
Mikael Nordfeldth
df19791a8f Wups, $notice not $stored in the old function... 2015-12-31 02:37:42 +01:00
Mikael Nordfeldth
8b0fdfb4a0 Notice conversation is perhaps not set yet 2015-12-31 02:35:13 +01:00
Mikael Nordfeldth
7cf6aaecd0 Don't do saveKnownReplies unless it's a verb=post 2015-12-31 02:31:35 +01:00
Mikael Nordfeldth
66289d3e76 Use recently implemented functions in saveKnownReplies 2015-12-31 02:22:49 +01:00
Mikael Nordfeldth
6772d991ae Only provide Notice oEmbed data for local notices 2015-12-31 01:55:18 +01:00
Mikael Nordfeldth
f61187a210 local/* is already in root .gitignore 2015-12-31 01:54:34 +01:00
Mikael Nordfeldth
bceece3bb9 issue #93 2015-12-30 18:03:45 +01:00
Mikael Nordfeldth
b0bf620c61 RecoverpasswordAction uses User->setPassword() 2015-12-30 17:53:43 +01:00
Mikael Nordfeldth
29847f172f setPassword now runs validate too 2015-12-30 17:51:57 +01:00
Mikael Nordfeldth
d7a8ee99af Added User->setPassword($password) 2015-12-30 17:44:24 +01:00
Mikael Nordfeldth
998db39b1a Notification mails are sent again fixes ssue #99
The problem was that the ActivityVerbPostPlugin handled 'post' verbs
but didn't handle the notifications for them, so now we're returning
true in the event so the default behaviour (sending to 'getReplies'
recipients) is done by default.
2015-12-30 17:35:47 +01:00
mmn
ad5d5f8054 Merge branch 'nightly' into 'nightly'
fixes this issue: https://quitter.se/notice/4344600



See merge request !71
2015-12-30 15:38:19 +00:00
hannes
08be0e5c67 fixes this issue: https://quitter.se/notice/4344600 2015-12-30 01:54:48 +00:00
Mikael Nordfeldth
c0ef167514 create storage directories automatically 2015-12-28 00:35:02 +01:00
Mikael Nordfeldth
2f836e2a59 GNU social config paths, removed StatusNet/Laconica 2015-12-28 00:09:06 +01:00
Mikael Nordfeldth
17a65ff873 background folder has never been used for GNU social 2015-12-28 00:00:51 +01:00
Mikael Nordfeldth
7f4b51e246 minor tuning to nginx example config
for example we really do wish to force people to use HTTPS ;)
2015-12-27 23:58:10 +01:00
Mikael Nordfeldth
4bc0b374bc File_redirection should get URLs properly 2015-12-27 21:36:23 +01:00
Mikael Nordfeldth
bd00ef839d split() is deprecated and should be explode()
We don't need the regexpness anyway.
2015-12-27 15:22:38 +01:00
Mikael Nordfeldth
556d6e198f Confirm_address field address_extra _can_ be NULL 2015-12-27 15:01:28 +01:00
Mikael Nordfeldth
cf916141d0 Group membership URI needs dataobjects not IDs 2015-12-27 13:01:24 +01:00
Mikael Nordfeldth
feb6b636f4 File_oembed varchar to text changes
No need to have text length limitations in the database for fields which
very well may be longer than what was previously set.
2015-12-27 12:11:29 +01:00
Mikael Nordfeldth
12d77ac3e4 varchar to text since there's no reason to limit the fields
They caused problems when quitter.se was upgrading because someone was
named ryanjjjjjjjjjjjjjjjjjjj[and more than 191 'j's] etc.
2015-12-27 11:50:11 +01:00
Mikael Nordfeldth
4a31e4992d Clarify that notices are also deleted with nukefile.php 2015-12-27 11:43:25 +01:00
Mikael Nordfeldth
6026dcaed1 Report if the email was not sent! 2015-12-27 02:16:12 +01:00
Mikael Nordfeldth
62c4ffe889 Server exceptions should reasonably be 5xx 2015-12-27 02:15:37 +01:00
Mikael Nordfeldth
b76461fc78 syntax error 2015-12-27 01:42:03 +01:00
Mikael Nordfeldth
0ac71c2b7b Duplicate URI means we have it already, I assume 2015-12-27 01:40:00 +01:00
Mikael Nordfeldth
83c112e24b Handle lack of parent nicely 2015-12-26 16:36:03 +01:00
Mikael Nordfeldth
306df3dc3b Logging fixes 2015-12-26 16:27:06 +01:00
mmn
ace47a4512 Merge branch 'rm-design-config-doc' into 'nightly'
Remove $config['design'] documentation

I believe those options are gone since
36d619480a

Ref. #109 

See merge request !70
2015-12-23 21:04:25 +00:00
mmn
48f31928e9 Merge branch 'autocomplete-group' into 'nightly'
Autocomplete group

Group autocompletion wasn't working for a couple of reasons:

1. $profile wasn't set (d8092207c0)
2. Profile::getUri() wasn't working for Groups, causing Profile::getAcctUri() to throw a ProfileNoAcctUriException (3e830dad37)

See merge request !68
2015-12-23 21:04:18 +00:00
mmn
4904c2bc10 Merge branch 'profile-prefs-getall' into 'nightly'
Fixup Profile_prefs::getAll() 

Ref. #98 

See merge request !69
2015-12-23 21:03:55 +00:00
Chimo
cc26c4cb1a Remove $config['design'] documentation
I believe those options are gone since
36d619480a
2015-12-21 17:53:30 +00:00
Chimo
90945e548b Profile_prefs::getAll fix prefs loop
DataObject::fetch doesn't return an object.
2015-12-17 14:58:06 +00:00
Chimo
71119e4980 Profile_prefs::getAll fix call to listFind
2nd argument needs to be an array
2015-12-17 14:55:39 +00:00
Chimo
3e830dad37 Make Profile::getUri work with Groups
and by extension Profile::getAcctUri
2015-12-15 21:50:10 -05:00
Chimo
d8092207c0 Autocomplete: Fix $profile being null for groups 2015-12-15 21:48:18 -05:00
Mikael Nordfeldth
bb0faaea56 Merge branch 'mmn_fixes' into nightly 2015-12-15 12:34:12 +01:00
Mikael Nordfeldth
f5ed66280b $url should've been $file->getUrl() 2015-12-15 12:31:10 +01:00
Mikael Nordfeldth
2b4a6c7dd7 Don't check Link header if not set 2015-12-15 11:51:16 +01:00
Mikael Nordfeldth
ef4c11d262 Better checks for defined array indexes 2015-12-15 11:50:57 +01:00
Mikael Nordfeldth
0d39337683 Merge branch 'master' into nightly 2015-12-14 22:11:26 +01:00
mmn
67c0c1b6c5 Merge branch 'send-twitter-replies-to-twitter' into 'master'
Always send Twitter replies to Twitter

If is_twitter_notice($notice->reply_to) then send it to Twitter, even
if twitter import is off.  It's a reply to a Twitter notice, it should
go there!

(Also retweets.)

See merge request !42
2015-12-14 21:10:57 +00:00
mmn
0baa9debbc Merge branch 'direct-feed-sub' into 'master'
If we are given a direct URL to a feed, use that



See merge request !54
2015-12-14 21:09:56 +00:00
mmn
1644608376 Merge branch 'twitter-show-rel-syndication' into 'master'
Include rel-syndication link for tweets

As per: <http://microformats.org/wiki/rel-syndication>
This allows some services to find a post in Twitter.

See merge request !44
2015-12-14 21:09:08 +00:00
mmn
0e91a38c9c Merge branch 'firefox-sub-link' into 'master'
Link to add to Firefox as feedreader

This allows easily subscribing to any feed firefox detects in your GNU Social instance.

See merge request !55
2015-12-14 21:08:22 +00:00
mmn
0f938ff234 Merge branch 'fix-twitter-uri' into 'master'
Twitter URIs have changed

The #! was deprecated ages ago, and Twitter forces HTTPS these days.

See merge request !43
2015-12-14 21:07:48 +00:00
Mikael Nordfeldth
c2ea85a5e2 Merge branch 'master' into nightly 2015-12-14 22:07:06 +01:00
mmn
1be376cf87 Merge branch 'inbound-linkback' into 'master'
Accept mentions/replies/rsvps/likes via pingback and webmention

This makes the Linkback plugin bidirectional, allowing reply/mention federation between GNU Social and any pingback-enabled service (including any Wordpress site).

See merge request !41
2015-12-14 21:05:25 +00:00
mmn
1eff108561 Merge branch 'twitter-check-dupe-by-uri' into 'master'
TwitterBridge also check for dupe by uri

In case a twitter item came in from some other source (such as linkback).

See merge request !45
2015-12-14 21:04:22 +00:00
mmn
2addf8e456 Merge branch 'repost-of-uf2' into 'master'
Mark up link to original as a repost for repeats

http://indiewebcamp.com/repost

See merge request !46
2015-12-14 21:03:38 +00:00
Mikael Nordfeldth
ef4e61c91b Merge branch 'master' into nightly 2015-12-14 22:03:04 +01:00
Mikael Nordfeldth
4ea396f871 Merge branch 'master' into mmn_fixes 2015-12-14 22:01:50 +01:00
mmn
edd62e58fd Merge branch 'at-mention-url' into 'master'
MentionURL Plugin

This plugin enables users to use the syntax `@twitter.com/singpolyma` to mention users the system does not know about, or to be more specific when a nickname is ambiguous.

See merge request !53
2015-12-14 21:01:42 +00:00
mmn
8d26d83d7a Merge branch 'group-join-approve-reject-button-icons' into 'nightly'
Add icons to group-join approval/rejection buttons

Relates to #106 

See merge request !66
2015-12-14 20:48:36 +00:00
mmn
2dd968f94e Merge branch 'group-join-rejections' into 'nightly'
Fix group-join request rejections

Relates to #105 

See merge request !65
2015-12-14 20:48:18 +00:00
mmn
d5d8068632 Merge branch 'group-members-pending' into 'nightly'
Fix "pending members" group list

Related to #104 

See merge request !64
2015-12-14 20:47:47 +00:00
mmn
5f0412b9ee Merge branch 'api-docs' into 'nightly'
Add AtomPub, Twitter-compat. API documentation to doc-src/



See merge request !62
2015-12-14 20:47:17 +00:00
Mikael Nordfeldth
289116cb64 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-12-14 21:45:46 +01:00
Mikael Nordfeldth
20592f184d getID() will complain loudly if something is wrong
(just ->id would not)
2015-12-14 21:44:59 +01:00
Mikael Nordfeldth
42c653a9d0 Merge commit '5c4a7a10' into nightly
hannes making sure notice is a repeat and not a delete activity notice
2015-12-14 21:42:08 +01:00
mmn
c114c6bbad Merge branch 'php-minversion-detec' into 'nightly'
installer::checkPrereqs bump PHP version to 5.5.0

Related to #102 

See merge request !63
2015-12-14 20:37:53 +00:00
mmn
129bd1bf03 Merge branch 'fix-setpassword' into 'nightly'
Fix /scripts/setpassword.php

common_munge_password is expecting a Profile, we were passing a user_id.

Thanks @aroque for the fix ( ref. #100 )

See merge request !67
2015-12-14 20:37:26 +00:00
Mikael Nordfeldth
d659e0aaf9 We have to print/echo when outputting json (thanks hannes) 2015-12-14 21:31:10 +01:00
Mikael Nordfeldth
8d1105fe73 We have to print/echo when outputting json (thanks hannes) 2015-12-14 21:29:39 +01:00
Mikael Nordfeldth
441e2c8d34 redir_info only has data if we ran discovery 2015-12-08 11:42:09 +01:00
Mikael Nordfeldth
4fecda58ff Full identifiers for Autocomplete, less confusing 2015-12-05 16:18:59 +01:00
Mikael Nordfeldth
376d545082 Remember to purify HTML... 2015-12-05 15:56:50 +01:00
Mikael Nordfeldth
c498db147a ircs URLs work fine in Firefox at least 2015-12-05 13:02:49 +01:00
Chimo
10bb4cb659 Fix /scripts/setpassword.php
common_munge_password is expecting a Profile, we were passing a user_id.

Thanks @aroque for the fix ( ref. #100 )
2015-12-04 17:09:06 +00:00
Chimo
b537696370 Add icons to group-join approval/rejection buttons 2015-12-04 17:00:35 +00:00
Chimo
6d9ea620a3 Fix group-join request rejections 2015-12-04 16:11:15 +00:00
Chimo
a06dc57621 Fix "pending members" group list 2015-12-04 15:47:40 +00:00
Chimo
3e16e31080 installer::checkPrereqs bump PHP version to 5.5.0 2015-12-04 15:22:58 +00:00
Mikael Nordfeldth
831e9db7ec Various minor theme enhancements in neo-gnu 2015-11-30 02:31:51 +01:00
Mikael Nordfeldth
4479d780e5 List groups with nickname instead of full name in sidebar 2015-11-30 02:21:17 +01:00
Mikael Nordfeldth
334a0d56e7 Oembed slimmed to only do discovery (soon we get og: discovery too) 2015-11-30 02:06:04 +01:00
Mikael Nordfeldth
8bab642cc7 quickGetJson for HTTP requests to return json objects 2015-11-30 02:05:06 +01:00
Mikael Nordfeldth
b7edac2610 HTTPClient get $params array and oEmbedHelper uses it 2015-11-30 01:28:18 +01:00
Chimo
c95f74018d Add AtomPub, Twitter-compat. API documentation to doc-src/ 2015-11-24 13:00:32 -05:00
Mikael Nordfeldth
fbe0e68617 Events listing, but the stream will change soon 2015-11-23 14:40:59 +01:00
Mikael Nordfeldth
5b847eff12 bump beta version number 2015-11-23 00:42:13 +01:00
Mikael Nordfeldth
bca4bb8373 allow form ->li() to set $class 2015-11-23 00:40:28 +01:00
Mikael Nordfeldth
b01484a0a3 Managed_DataObject::newUri was called improperly 2015-11-23 00:39:49 +01:00
hannes
5c4a7a1052 make sure returned notice is a repeat, i.e. not a delete activity notice 2015-11-22 20:28:07 +00:00
hannes
1eba33590b ActivityVerb::SHARE instead of http://activitystrea.ms/schema/1.0/share 2015-11-22 20:26:08 +00:00
hannes
70695a1463 make sure the notice is a repeat, i.e. not a delete activity notice that also can have a repeat_of set 2015-11-22 18:59:56 +00:00
Mikael Nordfeldth
1630424797 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-11-20 18:50:26 +01:00
mmn
b33b40db45 Merge branch 'refactor-file-processNew' into 'nightly'
Refactor on File::processNew

The code was so involved there was even a comment asking for a refactor.

Now, File_redirection::where always returns a nice File_redirection
object instead of an array or string or nothing.  The object is
either one which already existed or else a new, unsaved object.

Instead of duplicating "does it exist" checks everywhere, do it in
File_redirection::where.  You either get what exists or something to save.

An unsaved File_redirection may be paired with an unsaved File.
You will want to save the File first (using ->saveFile()) and put the
id in File_redirection#file_id before saving.

See merge request !57
2015-11-20 17:50:01 +00:00
Mikael Nordfeldth
38c84a92c7 Merge request #59 which was directed at master 2015-11-20 18:47:21 +01:00
mmn
5d548d157f Merge branch 'master' into 'master'
block check can't be done inside the same try-expression as Subscription::getSub…

…scription(), since if that throws a NoResultException, the block check will not be done. and if you're blocking someone you are not subscribing to them.

See merge request !59
2015-11-20 17:46:53 +00:00
hannes
abb8d1273e block check can't be done inside the same try-expression as Subscription::getSubscription(), since if that throws a NoResultException, the block check will not be done. and if you're blocking someone you are not subscribing to them. 2015-11-19 15:55:11 +00:00
Stephen Paul Weber
4f0fae9e90 Show better source for linback items 2015-11-12 19:07:26 +00:00
Stephen Paul Weber
11810bbf54 Update conversation when we update reply_to
Or repeat_of
2015-11-12 19:06:17 +00:00
Stephen Paul Weber
47db1ab063 Autodiscovery header for webmention 2015-11-12 19:06:17 +00:00
Stephen Paul Weber
f09a82268f Ignore self pings 2015-11-12 19:06:17 +00:00
Stephen Paul Weber
b95b318052 That never was meant to be in that if 2015-11-12 19:06:16 +00:00
Stephen Paul Weber
6aba21fcd8 Use author-declared URL as URI
Will make brid.gy work better, for example.
2015-11-12 19:06:16 +00:00
Stephen Paul Weber
fa1e4620cf Add pingback frontend 2015-11-12 19:06:16 +00:00
Stephen Paul Weber
aa4d880148 Search in sub-items for links to the target as well 2015-11-12 19:06:16 +00:00
Stephen Paul Weber
96e0819f00 Catch exception that this generates when there is no result 2015-11-12 19:06:16 +00:00
Stephen Paul Weber
c5bdf6924d Fix duplicate detection 2015-11-12 19:06:16 +00:00
Stephen Paul Weber
64ac344efa Update on duplicate linkback
This especially allows mentioning mulitple users, etc.
2015-11-12 19:06:16 +00:00
Stephen Paul Weber
b9971e8a80 Linkback to user should work 2015-11-12 19:06:15 +00:00
Stephen Paul Weber
1e3cf08d82 Ignore non-local notices as targets 2015-11-12 19:06:15 +00:00
Stephen Paul Weber
dbfb698859 Webmention frontend 2015-11-12 19:06:15 +00:00
Stephen Paul Weber
96fb18da3f Initial helpers for verification and microformats
This will form the backend of all incoming linkbacks.
We verify the linkback is real and then try to form a rich notice
out of it.
2015-11-12 19:06:15 +00:00
Mikael Nordfeldth
d0e7203d88 Make it clearer that we're handling group IDs 2015-11-08 23:31:45 +01:00
Mikael Nordfeldth
acd4980ab4 Fix federation issue with groups (bad list order) 2015-11-08 23:31:23 +01:00
Mikael Nordfeldth
844670f88d Ooops, copypasted something into the wrong place. 2015-11-08 23:24:20 +01:00
Mikael Nordfeldth
65184782aa OStatus usage of static Validate::* calls fixed 2015-11-08 10:33:41 +01:00
Mikael Nordfeldth
f29daa22b6 Merge branch 'master' of git.gnu.io:gnu/gnu-social into nightly 2015-11-06 13:39:17 +01:00
mmn
dbdac9cfbe Merge branch 'in-reply-to-uf2' into 'master'
Add direct link to parent notice

This is partly for usability, and partly to make Linkbacks for replies work (when the plugin is enabled).

See merge request !37
2015-11-06 12:39:02 +00:00
Mikael Nordfeldth
e40014b4de separate classes into files in DirectMessage plugin 2015-11-06 13:35:45 +01:00
Mikael Nordfeldth
c83deecba2 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-11-06 13:33:24 +01:00
mmn
5f999e84d8 Merge branch 'nightly' into 'nightly'
getParent() method will fail if the parent notice does not exist in the notice table

getParent() method will fail if the parent notice does not exist in the notice table. See https://quitter.no/notice/709400

There are situations where a user delete his notice which could be a parent notice to a reply.
The user which replied to this deleted notice will then have broken timeline. 
This is caused by the Notice.getParent() method which tries to return the selection of non existing parent notice.

I've tried this code on quitter.no and it seems to work.

See merge request !35
2015-11-06 12:28:00 +00:00
abjectio
507f9a28c0 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-11-05 17:34:34 +01:00
Mikael Nordfeldth
7ccd36849e Merge branch 'master' of git.gnu.io:gnu/gnu-social into nightly
Conflicts:
	plugins/OStatus/OStatusPlugin.php

master vs. nightly thing
2015-11-05 16:16:02 +01:00
mmn
f345f1d605 Merge branch 'renew-pshb' into 'master'
Actually use the renew code

We have the code to check once a day and renew, but currently it's
just in a script directory.  This change adds an event listener
hook to check and renew subscriptions daily.

Closes #83

See merge request !38
2015-11-05 15:13:16 +00:00
mmn
deb5275c7a Merge branch 'object-types-are-absolute' into 'master'
Stop incorrecly changing object_type to relative URI

All other data is using absolute URI for object_type.

See merge request !40
2015-11-05 15:12:41 +00:00
mmn
959f971a65 Merge branch 'fix-webfinger-link-header' into 'master'
Add all link headers, not just the last one

Given the way Link headers work, it does not make any sense to
just replace all other ones.  Especially when we ourselves are adding
in a loop.

See merge request !47
2015-11-05 15:12:10 +00:00
mmn
9f83e60f25 Merge branch 'pings-interop' into 'master'
Clean up Linkback plugin and add Webmention support

Closes #82

Fixes Pingback, adds Webmention, makes linkbacks opt-out-able for users (at request of quitter.se admin)

See merge request !36
2015-11-05 15:11:29 +00:00
Mikael Nordfeldth
0e32729e59 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-11-05 16:07:42 +01:00
Mikael Nordfeldth
c950f18546 Merge branch 'master' into nightly
Some merge requests that were aimed at master
2015-11-05 16:07:05 +01:00
mmn
96e0867d4c Merge branch 'master' into 'master'
update URL

bugz.foocorp.net and gitorious.net are outdated
git.gnu.io is current

See merge request !58
2015-11-05 15:05:55 +00:00
mmn
45b24286e5 Merge branch 'status-204' into 'master'
Accept 204 for PuSH subscription

Some hubs (notably, pushpress, used by wordpress.com) return 204 to mean success.

This used to be allowed by the spec, so no harm in accepting it.

See merge request !48
2015-11-05 15:03:42 +00:00
mmn
f513ceac7d Merge branch 'handle-rss-guid' into 'master'
Respect isPermalLink=false on RSS guid



See merge request !49
2015-11-05 15:02:20 +00:00
mmn
5c768d7ef7 Merge branch 'twitter-verb' into 'nightly'
TwitterBridge: Add $notice->verb on import

Since /plugins/ActivityModeration::onStartNotieSave expects it to be there when calling ActivityUtils::compareVerbs. Otherwise, the following exception is unhandled:

2015-10-26 15:33:03 LOG_ERR: [sn.chromic.org:queuedaemon.php:25931] Unhandled exception: No URI to resolve in ActivityUtils::resolveUri  
 0: /lib/activityutils.php(353): ActivityUtils::resolveUri(NULL)  
 1: /lib/activityutils.php(364): ActivityUtils::compareTypes(NULL, Array)  
 2: /plugins/ActivityModeration/ActivityModerationPlugin.php(113): ActivityUtils::compareVerbs(NULL, Array)  
 3: [internal function]: ActivityModerationPlugin->onStartNoticeSave(Object(Notice))  
 4: /lib/event.php(105): call_user_func_array(Array, Array)  
 5: /plugins/TwitterBridge/lib/twitterimport.php(173): Event::handle('StartNoticeSave', Array)  
 6: /plugins/TwitterBridge/lib/twitterimport.php(72): TwitterImport->saveStatus(Object(stdClass))  
 7: /plugins/TwitterBridge/lib/tweetinqueuehandler.php(52): TwitterImport->importStatus(Object(stdClass))  
 8: /lib/dbqueuemanager.php(94): TweetInQueueHandler->handle(Array)  
 9: /lib/iomaster.php(287): DBQueueManager->poll()  
 10: /lib/iomaster.php(161): IoMaster->poll()  
 11: /scripts/queuedaemon.php(111): IoMaster->service()  
 12: /lib/spawningdaemon.php(189): QueueDaemon->runThread()  
 13: /lib/spawningdaemon.php(118): SpawningDaemon->initAndRunChild(2)  
 14: /lib/daemon.php(168): SpawningDaemon->run()  
 15: /scripts/queuedaemon.php(181): Daemon->runOnce()  
 16: {main}

See merge request !50
2015-11-05 15:01:07 +00:00
mmn
4b7c904fe0 Merge branch 'correct-author-for-repeat' into 'master'
Markup correct author for a repeat

The original author is not the author of this entry, but of the original entry.

See merge request !51
2015-11-05 15:00:35 +00:00
mmn
a3ddc4ec0c Merge branch 'support-atom-namespaces-in-rss-author' into 'master'
Support more author types on RSS

Specifically, any ActivityStreams or ATOM namespaces being used on
an rss channel.

See merge request !52
2015-11-05 15:00:07 +00:00
mmn
3ca4af6868 Merge branch 'group-join' into 'nightly'
Add User_group::getObjectType

Since 174586bd51, I'm unable to join groups with the logs complaining
about getObjectType not existing on the User_group object.

Note: I'm not sure if this is the correct value to return here, but I
was able to joing groups successfully using this. Let me know if it needs
to be changed and I'll update the Merge Request!

See merge request !56
2015-11-05 14:57:29 +00:00
Matthias Fritzsche
ba623d2b4e update URL
bugz.foocorp.net and gitorious.net are outdated
git.gnu.io is current
2015-11-04 12:31:23 +01:00
Stephen Paul Weber
a9b1b60a97 Refactor on File::processNew
The code was so involved there was even a comment asking for a refactor.

Now, File_redirection::where always returns a nice File_redirection
object instead of an array or string or nothing.  The object is
either one which already existed or else a new, unsaved object.

Instead of duplicating "does it exist" checks everywhere, do it in
File_redirection::where.  You either get what exists or something to save.

An unsaved File_redirection may be paired with an unsaved File.
You will want to save the File first (using ->saveFile()) and put the
id in File_redirection#file_id before saving.
2015-11-02 05:15:08 +00:00
Chimo
d7b58491ce Add User_group::getObjectType
Since 174586bd51, I'm unable to join groups with the logs complaining
about getObjectType not existing on the User_group object.
2015-10-29 20:11:57 -04:00
Stephen Paul Weber
a74572b469 Use mb_strlen 2015-10-28 01:46:08 +00:00
Stephen Paul Weber
6ac8b845bf Improvements inspired by the OStatus code 2015-10-28 01:45:51 +00:00
Stephen Paul Weber
fe4c8a771b Replace text with nickname if shorter 2015-10-28 01:24:58 +00:00
Stephen Paul Weber
0aa759fab7 Allow mentioning bare domains with OStatus 2015-10-28 01:24:42 +00:00
Stephen Paul Weber
2edf535ecd Add length argument to plugin 2015-10-28 01:24:29 +00:00
Stephen Paul Weber
85d5cfede5 Link to add to Firefox as feedreader 2015-10-28 01:10:28 +00:00
Stephen Paul Weber
e58c529c53 If we are given a direct URL to a feed, use that 2015-10-28 00:54:20 +00:00
Stephen Paul Weber
2207eacc92 New length format for other kinds of mentions 2015-10-28 00:15:08 +00:00
Stephen Paul Weber
fed0895d98 Move the functionality to a plugin
Use an associated model to prevent race conditions on creating the
profile object.
2015-10-28 00:11:54 +00:00
Mikael Nordfeldth
1b81131c51 Shared notices didn't save the URL 2015-10-28 00:13:17 +01:00
Stephen Paul Weber
e1de6e0aa9 Support more author types on RSS
Specifically, any ActivityStreams or ATOM namespaces being used on
an rss channel.
2015-10-27 18:43:57 +00:00
Mikael Nordfeldth
ebbb9a8990 Bad HTML in translation. 2015-10-27 19:05:07 +01:00
Stephen Paul Weber
54e87dd2c3 Morkup correct author for a repeat
The original author is not the author of this entry, but of the original entry.
2015-10-27 17:19:03 +00:00
Chimo
3ae81f095c TwitterBridge: Add $notice->verb on import
Since /plugins/ActivityModeration::onStartNotieSave expects it to be there when calling ActivityUtils::compareVerbs. Otherwise, the following exception is unhandled:

2015-10-26 15:33:03 LOG_ERR: [sn.chromic.org:queuedaemon.php:25931] Unhandled exception: No URI to resolve in ActivityUtils::resolveUri
 #0 /lib/activityutils.php(353): ActivityUtils::resolveUri(NULL)
 #1 /lib/activityutils.php(364): ActivityUtils::compareTypes(NULL, Array)
 #2 /plugins/ActivityModeration/ActivityModerationPlugin.php(113): ActivityUtils::compareVerbs(NULL, Array)
 #3 [internal function]: ActivityModerationPlugin->onStartNoticeSave(Object(Notice))
 #4 /lib/event.php(105): call_user_func_array(Array, Array)
 #5 /plugins/TwitterBridge/lib/twitterimport.php(173): Event::handle('StartNoticeSave', Array)
 #6 /plugins/TwitterBridge/lib/twitterimport.php(72): TwitterImport->saveStatus(Object(stdClass))
 #7 /plugins/TwitterBridge/lib/tweetinqueuehandler.php(52): TwitterImport->importStatus(Object(stdClass))
 #8 /lib/dbqueuemanager.php(94): TweetInQueueHandler->handle(Array)
 #9 /lib/iomaster.php(287): DBQueueManager->poll()
 #10 /lib/iomaster.php(161): IoMaster->poll()
 #11 /scripts/queuedaemon.php(111): IoMaster->service()
 #12 /lib/spawningdaemon.php(189): QueueDaemon->runThread()
 #13 /lib/spawningdaemon.php(118): SpawningDaemon->initAndRunChild(2)
 #14 /lib/daemon.php(168): SpawningDaemon->run()
 #15 /scripts/queuedaemon.php(181): Daemon->runOnce()
 #16 {main}
2015-10-27 10:12:33 -04:00
Stephen Paul Weber
03c68d283d Respect isPermalLink=false on RSS guid 2015-10-27 03:18:04 +00:00
Stephen Paul Weber
4c2bc465a0 Accept 204 for PuSH subscription
Some hubs (notably, pushpress, used by wordpress.com) return 204 to mean success.

This used to be allowed by the spec, so no harm in accepting it.
2015-10-27 03:16:39 +00:00
Stephen Paul Weber
fae7bc0bc0 Add all link headers, not just the last one
Given the way Link headers work, it does not make any sense to
just replace all other ones.  Especially when we ourselves are adding
in a loop.
2015-10-25 18:42:37 +00:00
Stephen Paul Weber
dc36621dc2 Linkback to mentioned profiles 2015-10-25 17:22:15 +00:00
Stephen Paul Weber
21979bb7d7 Allow users to @mention URLs
Because inferring who you mean (especially in the presence of remotes) can suck
2015-10-23 21:15:40 +00:00
Stephen Paul Weber
033ed7e4aa Mark up link to original as a repost for repeats
http://indiewebcamp.com/repost
2015-10-23 19:12:25 +00:00
Stephen Paul Weber
cd22be3374 Only show parent wrapper if there are some 2015-10-23 18:51:33 +00:00
Stephen Paul Weber
ddfb56d920 TwitterBridge also check for dupe by uri
In case a twitter item came in from some other source (such as linkback).
2015-10-23 18:03:44 +00:00
Stephen Paul Weber
983fd75ec9 Include rel-syndication link for tweets
As per: <http://microformats.org/wiki/rel-syndication>
This allows some services to find a post in Twitter.
2015-10-23 17:56:02 +00:00
Stephen Paul Weber
2030ddfc79 Reduce duplication by handling both cases in find 2015-10-23 16:26:35 +00:00
Stephen Paul Weber
439b466979 Use HTTPS for links to Twitter profiles as well 2015-10-23 16:24:04 +00:00
Stephen Paul Weber
d1f1063900 Twitter URIs have changed
The #! was deprecated ages ago, and Twitter forces HTTPS these days.
2015-10-23 16:19:54 +00:00
Stephen Paul Weber
123bb445db Always send Twitter replies to Twitter
If is_twitter_notice($notice->reply_to) then send it to Twitter, even
if twitter import is off.  It's a reply to a Twitter notice, it should
go there!
2015-10-23 15:37:11 +00:00
Stephen Paul Weber
8649591626 Stop incorrecly changing object_type to relative URI
All other data is using absolute URI for object_type.
2015-10-22 17:14:59 +00:00
Stephen Paul Weber
3e1a6a65e6 Event plugin uses saveObjectFromActivity for RSVP
The Event plugin is still using a deprecated hook for saving extra data,
which makes it harder to hook into from other plugins.  This commit
fixes it to use the new hook for at least RSVPs.

NOTE: this plugin incorrectly sets the object_type of RSVP notices to
their verb.  This was existing behaviour which I have preserved for
backwards-compatability.
2015-10-22 17:10:50 +00:00
Stephen Paul Weber
4b31bc3fd2 Enqueue renewals
Better for request times, etc
2015-10-21 01:50:03 +00:00
Stephen Paul Weber
df21c3c95d Renew 1 day *before* the end, not 1 day *after* 2015-10-21 01:49:26 +00:00
Stephen Paul Weber
df46f123dd Actually use the renew code
We have the code to check once a day and renew, but currently it's
just in a script directory.  This change adds an event listener
hook to check and renew subscriptions daily.
2015-10-21 01:10:48 +00:00
Stephen Paul Weber
fc3f1f6942 Add direct link to parent notice
This is partly for usability, and partly to make Linkbacks for replies
work (when the plugin is enabled).
2015-10-19 18:40:40 +00:00
Stephen Paul Weber
677f0ac479 Allow users to opt out of sending linkbacks 2015-10-18 21:28:55 +00:00
abjectio
303fa91720 Removed inboxmessagelist.php - seems to be not needed. Ref. Issue #77 2015-10-18 17:34:36 +02:00
Stephen Paul Weber
c7e08195e4 Fix Pingback
This code was using the HTTPRequest helpers wrong.  This commit sets the body directly instead of jamming the XML in as a POST param.
2015-10-17 13:39:46 +00:00
Stephen Paul Weber
d9d74ca96c Send URL we publish
Webmention and Pingback both need the exact URL we are going to claim to link to to be present in our HTML source, so send them our actual original link.

Webmention clients are supposed to resolve this link.  Pingback clients may still fail on shortened links.
2015-10-17 13:38:13 +00:00
abjectio
aa5bbdf6e5 Bug in catch(Exception) - parameter wrongly written 2015-10-16 23:48:05 +02:00
Stephen Paul Weber
3b1792c8b5 Add webmention support 2015-10-15 00:29:53 +00:00
Stephen Paul Weber
e4892d21b1 Note that we should handle relative URIs 2015-10-14 16:13:25 -05:00
Stephen Paul Weber
8edc5148d9 Normalize detection helpers
Will make it easier to see what's happening when we add a third one.
2015-10-14 15:54:47 -05:00
Stephen Paul Weber
63fd35dffa Notify replies and repeats
This is especially useful for partial federation with remote accounts
that are not fully OStatus-enabled but support a pingback protocol.
Such accounts will still be notified of replies and repeats of their
content even without OStatus support, thus adding to the federated
universe.
2015-10-14 15:47:00 -05:00
Stephen Paul Weber
b43294ec6f Use the getter, not a direct access 2015-10-14 15:33:48 -05:00
Stephen Paul Weber
a4a37d6fc9 More robust pingback link detection 2015-10-14 15:28:38 -05:00
abjectio
626333a59e geParent() method will fail if the parent notice does not exist in the notice table. See https://quitter.no/notice/709400 2015-10-14 18:50:39 +02:00
Mikael Nordfeldth
2c8536dbf0 Link source==share notices 2015-10-14 01:30:29 +02:00
Mikael Nordfeldth
401b774c2f Only set ->actor in a case where it has not yet been set
Otherwise it could overwrite it with the wrong actor (the "second round"
for share activities)
2015-10-14 01:20:50 +02:00
Mikael Nordfeldth
d260808cd3 intval for type assurance 2015-10-14 01:16:22 +02:00
Mikael Nordfeldth
79c40bc73b Type-aware comparison is necessary for Notice is_local/scope 2015-10-14 00:42:15 +02:00
Mikael Nordfeldth
b6aeff89c4 Call Notice::saveActivity instead of Ostatus_profile->processActivity 2015-10-14 00:10:54 +02:00
Mikael Nordfeldth
236bf0b0f5 Remote notice creators should not have notice is_local default to LOCAL_PUBLIC 2015-10-14 00:10:01 +02:00
Mikael Nordfeldth
005c108118 Shared activities were missing the actor 2015-10-13 23:40:35 +02:00
Mikael Nordfeldth
5b307c0417 Add a url test in Notice::saveActivity 2015-10-13 23:39:40 +02:00
Mikael Nordfeldth
65adefe170 Wrong variable names in SharePlugin 2015-10-13 23:18:13 +02:00
Mikael Nordfeldth
4b69ef41e2 Bad variable name in File_redirection 2015-10-13 22:57:45 +02:00
Mikael Nordfeldth
e379c49d49 Removing excess debug logging 2015-10-13 22:55:27 +02:00
Mikael Nordfeldth
c8a907c828 Bookmark:addNew did not add a proper rel=related 2015-10-13 12:31:35 +02:00
Mikael Nordfeldth
3f4d4cb7a0 Better error handling in Bookmark 2015-10-12 18:28:32 +02:00
Mikael Nordfeldth
67e7bc55ea Bookmarks are almost migrated
Now they are just not being saved in the bookmark table.
2015-10-12 17:48:23 +02:00
Mikael Nordfeldth
7834e67583 Just a functionifying of Notice->rendered 2015-10-12 17:16:13 +02:00
Mikael Nordfeldth
297d68f36c Change Bookmark plugin version details 2015-10-12 09:47:30 +02:00
Mikael Nordfeldth
0e588bdeca Woops, put the bookmark get* functions in the wrong file. 2015-10-11 22:08:28 +02:00
Mikael Nordfeldth
1dac0e709a First step of making Bookmark saveActivity-compatible 2015-10-10 23:47:43 +02:00
Mikael Nordfeldth
b209276e72 Stricter typing for Bookmark plugin 2015-10-10 23:15:51 +02:00
Mikael Nordfeldth
5726459629 unmaintained plugins that accidentally got translations 2015-10-10 22:40:05 +02:00
mmn
0926316a76 Merge branch 'master' into 'nightly'
convert activity stream RFC3339 dates to mysql datetime format when saving remote event dates to db

See merge request !34
2015-10-10 20:38:01 +00:00
mmn
3255e2e1b8 Merge branch 'page_title_showstream' into 'nightly'
Show page title in user's profile page

Enable page title on user's profile page to make it consistent with all other pages and better orient users.

![Screenshot_-_250715_-_14_50_00](https://git.gnu.io/aroque/gnu-social/uploads/06e92b233e88fdb66047fbc0571f9831/Screenshot_-_250715_-_14_50_00.png)

See merge request !22
2015-10-10 20:36:59 +00:00
mmn
32f77dbf05 Merge branch 'translation-snapshot-october-2015' into 'nightly'
Snapshot of the Transifex translation project - October 2015

It's been 7 months since the last localization update, and the files in the repository are out of sync with the current state of the project. Our Transifex team has since grown to 63 translators, many of them are active members of the GNU social community. I don't know how we will resolve this situation, but it is clear that we have to act. If you plan to redesign the plugin system in the future to support external repository for localizations, that would work as well. But now, please let us do an update. It would be a serious disgrace to the community to throw all of their hard work away.

See merge request !32
2015-10-10 20:36:04 +00:00
mmn
89d3d9b2ce Merge branch 'usergroups_page' into 'nightly'
Improve display of user's groups page.

* Show page notice as for subscriptions
* Show instructions when user has no groups
* Add explanation of remote groups and link to skilledtest wiki

Here is how it looks when a logged in user is not following groups yet

![login_nogroups](https://git.gnu.io/aroque/gnu-social/uploads/34b1bdcdb9e7277ec3deff4594061c59/login_nogroups.png)

After following a group, you still see the instruction notice. This way you have links to searching groups and creating a new one.

![login_groups](https://git.gnu.io/aroque/gnu-social/uploads/cb50689bcdfca7adc5cdcbe620487aa4/login_groups.png)

Finally when logged out:

![nologin_groups](https://git.gnu.io/aroque/gnu-social/uploads/40d81ab909321f8729c2696dd3938d46/nologin_groups.png)

The current version on nightly has basically no text at all.

See merge request !25
2015-10-10 20:35:36 +00:00
mmn
77951647ce Merge branch 'oauth-default-icon' into 'nightly'
Add default icon to OAuth apps

When MySQL runs in strict mode, it doesn't seem possible to add a new OAuth app.

This is because:
 * The 'icon' field has no defaults and also has NOT NULL constraint.
 * GS uploads/sets the icon[1] (if provided) after the SQL insert[2]

The easiest fix seemed to be to provide a default icon value.
This change sets the default to the  "blue peg" default avatar from the base theme.

[1]
7f30e61402/actions/newapplication.php (L176)

[2]
7f30e61402/actions/newapplication.php (L186)

See merge request !29
2015-10-10 20:34:22 +00:00
mmn
238d2a3876 Merge branch 'link-atom-fix' into 'nightly'
atom: <link> urls should be attr, not content

Some activities (ex: repeats and follows) have <link>s like this:
<link rel="alternate" type="text/html">http://example.org</link>

This commit changes them to:
<link rel="alternate" type="text/html" href="http://example.org"/>

See merge request !30
2015-10-10 20:33:58 +00:00
Mikael Nordfeldth
fe9457daea Syntax and minor fixes
Deleted_notice and Bookmark getByPK assocative array for example.
2015-10-10 22:23:24 +02:00
Mikael Nordfeldth
174586bd51 Some code aligning for consistency 2015-10-10 22:09:51 +02:00
Mikael Nordfeldth
cae43344ec Start reworking Bookmark plugin for more modern code 2015-10-10 21:56:02 +02:00
Mikael Nordfeldth
18f07a48e8 Using some Managed_DataObject::getByPK calls for consistency 2015-10-10 21:40:02 +02:00
Mikael Nordfeldth
37f2da03e1 newUri might as well be put in Managed_DataObject 2015-10-10 21:31:26 +02:00
Mikael Nordfeldth
45bc192625 If that $options key wasn't set, this won't change anything anyway 2015-10-10 21:22:07 +02:00
Mikael Nordfeldth
a793d211d2 Match parent class definition of clientError 2015-10-10 20:51:53 +02:00
Mikael Nordfeldth
b4c51b2d39 Use an atom:link to ApiMediaUpload response instead of mediahref
'cause why not?
2015-10-10 20:41:14 +02:00
Mikael Nordfeldth
9728270acd Add mediahref in our incredibly arbitrary response 2015-10-10 17:15:29 +02:00
Mikael Nordfeldth
5d91c9a820 Don't send multiple error responses in ApiMediaUpload :) 2015-10-10 17:12:44 +02:00
Mikael Nordfeldth
2ad7c4313d Search engine fix because Directory did a 'join'
Colliding columns with a very lacking error message from DB ;)
2015-10-10 12:16:12 +02:00
Mikael Nordfeldth
6376b78a80 I think I have made the delete verb generate proper AS
ActivityStreams had a different/better definition than me
in how to define the Delete verb data:
http://wiki.activitystrea.ms/w/page/23541872/Delete
2015-10-10 11:08:17 +02:00
Hannes Mannerheim
a9f879c2ba better explanation 2015-10-10 00:20:32 +02:00
Hannes Mannerheim
b24191f9f9 convert activity stream RFC3339 dates to mysql datetime format when saving remote event dates to db 2015-10-10 00:11:54 +02:00
mmn
3154302b53 Merge branch 'master' into 'nightly'
make events federate

See merge request !33
2015-10-09 14:43:45 +00:00
Hannes Mannerheim
61ed36e446 make event urls federate 2015-10-09 16:22:47 +02:00
Mikael Nordfeldth
dd149a76f3 AtomPub should work now, at least for post/note 2015-10-09 16:19:26 +02:00
Mikael Nordfeldth
2dfd34b12d Post verb default type is 'note' 2015-10-09 16:18:12 +02:00
Hannes Mannerheim
55655ba319 make event location federate 2015-10-09 16:16:13 +02:00
Hannes Mannerheim
827dffb94c make event dates federate 2015-10-09 16:00:33 +02:00
Mikael Nordfeldth
2d3f1ef520 You don't have to push objects as references 2015-10-09 15:45:23 +02:00
Mikael Nordfeldth
0bdbb32f3b Consistent camelcasing 2015-10-09 15:45:05 +02:00
Mikael Nordfeldth
5dc718c54d Make Bsaic Auth work properly with RW actions 2015-10-09 15:43:17 +02:00
Hannes Mannerheim
5a2d760177 update from upstream 2015-10-09 14:40:43 +02:00
Hannes Mannerheim
499d4aadbc make events federate 2015-10-09 13:10:48 +02:00
Mikael Nordfeldth
216a04df86 Switching variable access in ApiTimelineUser to GNUsocial improvements 2015-10-09 11:23:20 +02:00
Mikael Nordfeldth
3013b84e98 Don't enable StrictTransportSecurity by default
(but you should enable it if you use TLS, but there are many who do this
through their webserver and it's odd to have double headers etc...)
2015-10-09 10:55:12 +02:00
Mikael Nordfeldth
2d81df4760 Deleted_notice variable specification fixed 2015-10-08 13:20:17 +02:00
Mikael Nordfeldth
180958185f Found newer Diaspora protocol specifications
http://www.rubydoc.info/github/Raven24/diaspora-federation/master/DiasporaFederation/Salmon/EncryptedSlap
2015-10-05 12:49:54 +02:00
Mikael Nordfeldth
ee6096cca8 FIXME: Diaspora has its own salmon slap semantics 2015-10-05 11:32:10 +02:00
Mikael Nordfeldth
3902dc963a Diaspora encloses magic envelope in <atom:entry>??!!?! 2015-10-05 11:22:23 +02:00
Mikael Nordfeldth
0ba53edd96 Can't use return-value in empty() 2015-10-04 22:32:14 +02:00
Mikael Nordfeldth
ec19661312 Can't use return-value in empty() 2015-10-04 22:31:07 +02:00
digital dreamer
d7fd6bac72 Snapshot of the Transifex translation project - October 2015 2015-10-04 18:23:01 +02:00
Mikael Nordfeldth
af1b0915f4 Magic signature discovery and envelope changes 2015-10-04 17:26:35 +02:00
Mikael Nordfeldth
6afa091dca Change some Salmon events and similar
Use Profile instead of User and (if we know it) send along the target
profile, so a Diaspora plugin can encrypt to the receiver.
2015-10-04 17:26:23 +02:00
Mikael Nordfeldth
f4ed171397 Make Magicsig capable of loading public PKCS1 keys 2015-10-04 17:22:19 +02:00
Mikael Nordfeldth
2970333adb Set otherwise undiscovered salmonuri on OStatus script update-profile.php 2015-10-04 17:21:56 +02:00
Mikael Nordfeldth
57f26a97fb var_export without true 2015-10-04 16:40:21 +02:00
Mikael Nordfeldth
4238875ebe autoloading of Crypt_AES and Crypt_RSA is easier 2015-10-04 15:57:11 +02:00
Mikael Nordfeldth
684b9419a0 Add an event to get plugin discovery hints from XRD 2015-10-04 14:46:45 +02:00
Mikael Nordfeldth
2aed59a02a Diaspora plugin is almost there (for remote salmon slaps at least) 2015-10-04 12:06:48 +02:00
Mikael Nordfeldth
9b461db4da Send the entire XMLStringer object in MagicEnvelope events. 2015-10-04 09:59:01 +02:00
Mikael Nordfeldth
184293c634 Break out MagicEnvelope->toXML() functionality to allow for plugin flexibility 2015-10-04 00:17:07 +02:00
Mikael Nordfeldth
bc9e3b1843 Remove XSS attack vector in JoinListItem 2015-10-03 23:51:06 +02:00
Mikael Nordfeldth
30a4393afa Move around some code related to Magic_envelope and signing 2015-10-03 23:35:46 +02:00
Mikael Nordfeldth
aab7667cd5 Sigh, copy-pasting when tired is a bad idea. 2015-10-03 16:19:38 +02:00
Mikael Nordfeldth
833abbb191 Fixes to Deleted_notice 2015-10-03 16:07:19 +02:00
Mikael Nordfeldth
e6d7534a88 We haven't created the Deleted_notice yet, just use target URI directly 2015-10-03 15:39:57 +02:00
Mikael Nordfeldth
db726ca294 Return the correct URI in Deleted_notice 2015-10-03 15:31:56 +02:00
Mikael Nordfeldth
7c68537b06 Deletes now federated. But might not be handled properly 2015-10-03 14:39:49 +02:00
Mikael Nordfeldth
db9f68e651 Sigh, use the correct variable name... 2015-10-03 13:45:06 +02:00
Mikael Nordfeldth
66a1d63dd0 if is_local was not provided to Notice:saveActivity it wouldn't be set 2015-10-03 13:24:31 +02:00
Mikael Nordfeldth
aba38d5586 bump to 1.2.0-beta1 as we have remote delete functionality now 2015-10-03 12:39:23 +02:00
Mikael Nordfeldth
1244e8d929 Somewhat better logging while still in trial phase 2015-10-03 12:33:33 +02:00
Mikael Nordfeldth
2e77a83816 Store delete verb as object_type=activity 2015-10-03 12:29:22 +02:00
Mikael Nordfeldth
dac617d95a I think all the notice deletion calls are event-compatible now
This means we can handle DeleteNoticeAsProfile in plugins, such as
the ActivityModeration plugin.
2015-10-03 12:26:09 +02:00
Mikael Nordfeldth
88f7bb1ed5 Some work on ActivityModeration with notice deletion
Let's now create an event called DeleteNotice and also make sure we
handle the onNoticeDeleteRelated properly in ActivityModeration to
avoid possible endless loops etc.
2015-10-03 02:02:37 +02:00
Mikael Nordfeldth
ae73baf4ee Undefined variable use 2015-10-03 01:47:40 +02:00
Mikael Nordfeldth
0c6fe78a73 Shorthand function for getActor 2015-10-03 00:43:34 +02:00
Mikael Nordfeldth
b8f52965a9 StoreRemoteMedia plugin to enable locally served thumbs of remote images
Add addPlugin('StoreRemoteMedia'); in your config.php to enable.
2015-10-01 22:18:47 +02:00
Mikael Nordfeldth
d52b7e3124 Oembed fiddling, nothing major 2015-10-01 22:18:33 +02:00
Mikael Nordfeldth
80bc7f0e25 File handling changes for better logic
Also prepares for StoreRemoteMediaPlugin, coming up...
2015-10-01 22:14:49 +02:00
Mikael Nordfeldth
5b7deee0cc InlineAttachmentListItem put into its own file 2015-10-01 21:40:05 +02:00
Mikael Nordfeldth
3a6c98ff16 NoResultException is common if reply_to was not cleared on parent deletion 2015-09-30 22:55:06 +02:00
Mikael Nordfeldth
a09cf51b99 Move Ostatus_profile->processPost function into plugin 2015-09-29 15:19:13 +02:00
Mikael Nordfeldth
21a12a2f60 Some conversationRoot lookups failed with deleted notices 2015-09-28 22:36:04 +02:00
Mikael Nordfeldth
118a4f56ab Script to test remote oEmbed endpoints 2015-09-28 12:25:40 +02:00
Mikael Nordfeldth
34b6d37266 Revert "Accessibility fix, make yes button more explicit"
This reverts commit 1864a9de38.

The problem was another label that was missing (fixed in previous commit, the close button)
2015-09-28 00:49:26 +02:00
Mikael Nordfeldth
d6e56924fe Accessibility improvement, title for popup close button
Apparently it read as "times" in screen readers.
2015-09-28 00:47:16 +02:00
Mikael Nordfeldth
ad3b62cf2f ShowprofiletagAction now extends ShowstreamAction 2015-09-27 23:46:30 +02:00
Mikael Nordfeldth
0e24709989 Profile/Peopletag file splitting for autoload 2015-09-27 22:51:50 +02:00
Mikael Nordfeldth
edb73ec480 Use exception instead of if-statement in File::saveNew 2015-09-27 12:29:38 +02:00
Mikael Nordfeldth
02418cffd8 Be harsher on database saving failure for File entry 2015-09-27 11:54:52 +02:00
Mikael Nordfeldth
0bd0c65b87 Bring back title to DocAction 2015-09-15 14:26:33 +02:00
Mikael Nordfeldth
b209dcf8a7 DocAction now extends ManagedAction 2015-09-15 12:07:35 +02:00
Chimo
96a7bf2b90 atom: <link> urls should be attr, not content
Some activities (ex: repeats and follows) have <link>s like this:
<link rel="alternate" type="text/html">http://example.org</link>

This commit changes them to:
<link rel="alternate" type="text/html" href="http://example.org"/>
2015-09-14 21:34:56 -04:00
Mikael Nordfeldth
604ed1fd9a Prepare for ActivityModerationPlugin 2015-09-10 15:55:37 +02:00
Mikael Nordfeldth
9ad9b91efb Call it compareVerbs when comparing verbs 2015-09-10 15:27:40 +02:00
Mikael Nordfeldth
404d5781fc Annoying debug messages that were just meant to remind 2015-09-09 17:48:06 +02:00
Mikael Nordfeldth
52de57e2f6 Let's handle notice dataobjects instead, despite fetching twice from db
Actually the original object should be fixed here, but we'll handle the FIXME's later.
2015-09-09 17:03:52 +02:00
Mikael Nordfeldth
4b83d62750 Split classes into their own files 2015-09-09 16:30:14 +02:00
Mikael Nordfeldth
a541533e04 Use Profile objects in Subscription::saveNew 2015-09-06 02:07:52 +02:00
Mikael Nordfeldth
917a547f91 Subscription::saveNew is a static function 2015-09-06 02:01:39 +02:00
Mikael Nordfeldth
d448275713 Merge branch 'nightly' 2015-09-06 01:54:00 +02:00
Mikael Nordfeldth
4354ce21d1 introducing html_sprintf for easier sprintf'ing with htmlspecialchars 2015-09-06 01:53:11 +02:00
Chimo
dc5dd69f2e Add default icon to OAuth apps
When MySQL runs in strict mode, it doesn't seem possible to add a new OAuth app.

This is because:
 * The 'icon' field has no defaults and also has NOT NULL constraint.
 * GS uploads/sets the icon[1] (if provided) after the SQL insert[2]

The easiest fix seemed to be to provide a default icon value.
This change sets the default to the  "blue peg" default avatar from the base theme.

[1]
7f30e61402/actions/newapplication.php (L176)

[2]
7f30e61402/actions/newapplication.php (L186)
2015-09-05 17:02:17 -04:00
Mikael Nordfeldth
ea75e1c29c Notice_location class in db/core.php 2015-09-04 23:12:23 +02:00
Mikael Nordfeldth
7f30e61402 Oops, forgot Notice_location PHP file 2015-09-04 22:30:33 +02:00
Mikael Nordfeldth
3c86542a40 Move notice location data to Notice_location
Will probably take a _long_ time to do scripts/upgrade.php but don't
worry, it can be aborted and resumed.
2015-09-04 22:25:11 +02:00
Mikael Nordfeldth
476197569f sys_get_temp_dir was added in PHP 5 >= 5.2.1
Our requirements are higher than that, so let's just remove the workaround
2015-09-04 22:24:01 +02:00
Mikael Nordfeldth
1e07f8c045 Incorrect use of DataObject type matching after ->find() 2015-09-04 20:35:11 +02:00
mmn
57c87088d4 Merge branch 'configure-docs' into 'nightly'
Add a few missing 'attachments' config options

show_thumbs
show_html
filename_base

See merge request !27
2015-09-03 16:11:28 +00:00
mmn
f4b0756e70 Merge branch 'nightly' into 'nightly'
Adding ChooseTheme plugin into the plugin repository

This merge request propose the plugin ChooseTheme as a part of the plugin repository for GNU social.

See merge request !9
2015-09-03 16:11:04 +00:00
mmn
a597b28001 Merge branch 'nginx-sample' into 'nightly'
Add nginx sample configuration

See merge request !23
2015-09-03 16:10:23 +00:00
mmn
5f74ec6782 Merge branch 'webinstaller-faq-link' into 'nightly'
Fix broken link to FAQ in web installer

See merge request !24
2015-09-03 16:09:38 +00:00
Mikael Nordfeldth
e06553b15b Suggested edits by hannes2peer 2015-09-03 18:04:13 +02:00
Mikael Nordfeldth
84a65c7189 Include PHP libraries from system if not packaged and they are installed.
Thanks to:
    "Bhuvan Krishna" <bhuvan@swecha.net>
    "Sunil Mohan" <sunil@medhas.org>
2015-09-03 17:56:11 +02:00
abjectio
91c1ab6017 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-08-18 22:55:28 +02:00
Mikael Nordfeldth
c77bce12e5 Mf2 extlib update from https://github.com/indieweb/php-mf2/ 2015-08-15 11:48:39 +02:00
Hannes Mannerheim
76004660e9 make it possible to erase fields in the profile via the api 2015-08-11 22:18:57 +02:00
Mikael Nordfeldth
c1e6e4e65b OpenID extlib updated: Fixes CVE-2014-8150 2015-08-02 13:41:57 +02:00
Mikael Nordfeldth
b434243416 OpenID extlib updated: Fixes CVE-2014-8150 2015-08-02 13:39:38 +02:00
chimo
60e7dc1e39 Add a few missing 'attachments' config options
show_thumbs
show_html
filename_base
2015-08-01 09:24:05 -04:00
aroquen
de1d49865c Add some bottom margin to space profile items 2015-07-26 19:10:01 +02:00
aroquen
cd6ad8571f Make group lists look like profile ones in neo-quitter theme 2015-07-26 16:10:34 +02:00
aroquen
86ed4ac603 This margin is not needed anymore 2015-07-26 15:22:11 +02:00
aroquen
8056aec93e Remove bullets from user's groups list 2015-07-26 12:39:49 +02:00
aroquen
dc9342f184 Group location on a new line as in profile lists 2015-07-26 12:39:48 +02:00
aroquen
31bcf8cfa2 Show full name close to nickname in subscribers list. For consistency with groups list 2015-07-26 12:39:48 +02:00
aroquen
a416abb940 Beautify (a bit) groups list
* Reduce font size to fit and look better
* Group nickname is bold
* Add @ prefix to location
2015-07-26 12:39:47 +02:00
aroquen
7b04ee6a9b Align location label (fixes profile and group directory display) 2015-07-26 12:39:31 +02:00
aroquen
a04b1cda45 Drop dead variable 2015-07-26 11:10:57 +02:00
aroquen
b0abce9bc8 Page notice message as instructions class 2015-07-26 11:10:30 +02:00
aroquen
c6f58d432c Improve display of user's groups page.
* Show page notice as for subscriptions
* Show instructions when user has no groups
* Add explanation of remote groups and link to skilledtest wiki
2015-07-25 21:38:38 +02:00
Chimo
abde7a2682 Fix broken link to FAQ in web installer 2015-07-25 10:36:31 -04:00
Chimo
05b814ce68 Add nginx sample configuration 2015-07-25 09:39:55 -04:00
aroquen
ad49158fb5 Show page title in user's profile page 2015-07-25 14:45:45 +02:00
Mikael Nordfeldth
266b032b17 UsergroupbyidAction now extends ManagedAction 2015-07-21 02:32:17 +02:00
Mikael Nordfeldth
7ce32619cc Missing getTarget function in targetedrss10action.php 2015-07-21 02:17:34 +02:00
Mikael Nordfeldth
5b09a150bc Increased debugging and fixing conversation stitching for saveActivity 2015-07-18 19:19:16 +02:00
Mikael Nordfeldth
e0084a6fdf Exception handling regarding Foreign_link 2015-07-18 11:39:34 +02:00
Mikael Nordfeldth
b609a3610f Some missed exception throwing since fixing Foreign_link and Foreign_user 2015-07-18 11:04:35 +02:00
Mikael Nordfeldth
6f62adedfc Infinite loop on CLI initiated profile deletion for local users
profile deleting user deleting profile deleting user...
2015-07-18 02:16:52 +02:00
Mikael Nordfeldth
9fdf6474f8 TwitterBridge working again (for signin and posting to Twitter at least)
Now we just need to make it include the newname suggestion if the form fails
(for example forgetting to check the license checkbox or so).
2015-07-18 02:16:15 +02:00
Mikael Nordfeldth
beba2a25d0 Don't retry unhandled transports in OpportunisticQM
It'd continue trying xmpp transports forever, for example...
2015-07-18 01:09:50 +02:00
Mikael Nordfeldth
e10d081a56 TwitterBridge is closer to working again 2015-07-17 21:03:37 +02:00
Mikael Nordfeldth
6cd7a4a400 TwitterBridge messing about, Twitter OAuth requires Authorization header now?
https://dev.twitter.com/oauth/reference/post/oauth/request_token says that
the request should be a GET with a specific HTTP header instead of query
string parameters for OAuth?
2015-07-17 18:54:39 +02:00
Mikael Nordfeldth
5933056a5b Twittersettings now works better as Profilesettings extension 2015-07-17 17:50:43 +02:00
Mikael Nordfeldth
d6d06c8cbb SubMirror now works properly as extended from FormAction 2015-07-17 17:09:36 +02:00
Mikael Nordfeldth
712a6d49d0 Poll settings adapted to FormAction 2015-07-17 16:19:02 +02:00
Mikael Nordfeldth
da168674f9 OpenID settings aligned with FormAction 2015-07-17 16:08:22 +02:00
Mikael Nordfeldth
8d2504a809 Early return in FacebookBridge settings action 2015-07-17 15:27:47 +02:00
Mikael Nordfeldth
be0c10e8f6 Facebooksettings adapted to FormAction 2015-07-17 15:24:37 +02:00
Mikael Nordfeldth
992fe6896f Urlsettings now adapted to FormAction 2015-07-17 15:16:45 +02:00
Mikael Nordfeldth
8d516d7f08 Don't allow imports by default until it works well on large instances. 2015-07-17 14:40:09 +02:00
Mikael Nordfeldth
2dd979d3f8 SMS Settings now better adapted to FormAction 2015-07-17 14:29:07 +02:00
Mikael Nordfeldth
a093dea38c ExtendedProfile is not something we want by default. 2015-07-17 12:46:24 +02:00
Mikael Nordfeldth
9045575e62 ExtendedProfile now works better as extended ProfilesettingsAction 2015-07-17 12:46:09 +02:00
Mikael Nordfeldth
9f82da07f1 ProfilesettingsAction and related stuff modernised 2015-07-17 12:20:11 +02:00
Mikael Nordfeldth
53e820b466 Maximum character limit with utf8mb4 is 191 in varchar 2015-07-17 11:22:25 +02:00
Mikael Nordfeldth
cfaaf3c13c PasswordsettingsAction aligned with FormAction
Also made some changes in the password "munging" function call
common_munge_password to accept a profile instead of user ID (which
was only there because stoneage StatusNet used the ID to generate a
not-very-random salt, but nowadays we primarily use AuthCrypt plugin).
2015-07-17 01:47:43 +02:00
Mikael Nordfeldth
47ef917f62 oldschool settings adapted to FormAction
TODO: Rename this "Layout", "Display" or something, since it might
actually be interesting to have non-threaded views for some users.
2015-07-17 01:18:28 +02:00
Mikael Nordfeldth
a6e299a2fc OAuth stuff adapted for FormAction
TODO: Break OAuth out into a plugin.
2015-07-17 01:07:19 +02:00
Mikael Nordfeldth
ba5a43f2f9 If XMLOutputter $output arg is null, use php://output
Since pushing a null value to the argument actually sets it to null
and not the default fallback (previously $output='php://output');
2015-07-16 23:58:04 +02:00
Mikael Nordfeldth
f1d9d8a6ed ImSettings adapted to FormAction inheritance
TODO: Get separate Form classes and move User_im_prefs to Profile_prefs
2015-07-16 21:18:50 +02:00
Mikael Nordfeldth
647171e089 EmailSettingsAction adapted to FormAction
TODO: EmailSettingsForm as a separate class would be good!
2015-07-16 21:17:22 +02:00
Mikael Nordfeldth
fd2efbc6f8 AvatarSettings more aligned to FormAction
TODO: Make classes called AvatarCropForm and AvatarUploadForm
2015-07-16 19:21:12 +02:00
Mikael Nordfeldth
2d44400cfc SettingsAction now extends FormAction (and thus ManagedAction) 2015-07-16 19:03:53 +02:00
Mikael Nordfeldth
673bef2fda OAuth widgets separated into their own files 2015-07-16 18:52:43 +02:00
Mikael Nordfeldth
94d54ebc29 Function declarations to match parent class 2015-07-16 18:45:59 +02:00
Mikael Nordfeldth
44dc00a58c Non-replies cannot harvest parent notice nicknames
A feature we use of parent notices is that if you use the same @user
as the parent notice, the same @user will be notified, regardless if
there might be @user@site.com as well as @user@example.com and you're
subscribed to just one of them (or both, or none of them!).

But this threw an exception since we tested this on new notice threads.
2015-07-16 12:53:10 +02:00
Mikael Nordfeldth
cd23c78800 Less redundant code. 2015-07-15 19:21:21 +02:00
Mikael Nordfeldth
01a4ab30dc Removing MicroID as well as simplifying profileaction
sorry, forgot to commit in between
2015-07-14 16:52:20 +02:00
abjectio
c10a4cf6d2 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-07-12 10:40:24 +02:00
Mikael Nordfeldth
e868ac41cd userrss action didn't call parent preparation method 2015-07-11 19:48:18 +02:00
Mikael Nordfeldth
c5a5eaf288 Do we update feeduri and salmonuri for Ostatus_profile now?
When changing from HTTP to HTTPS, following up on commit
59763ceecb
where http to https Ostatus_profile URI changing was first introduced.
2015-07-11 19:46:01 +02:00
Mikael Nordfeldth
ac98600640 More RESTish URL (/notice/:notice/delete) for notice delete
Also returns to 'top' now after notice deletion.
2015-07-11 11:26:48 +02:00
Mikael Nordfeldth
7d524307d2 DeletenoticeForm is its own class now 2015-07-11 11:26:44 +02:00
Mikael Nordfeldth
9101a1db3d No limit argument to Rss10Action->getNotices() (use $this->limit) 2015-07-11 11:09:16 +02:00
Mikael Nordfeldth
2a1be57470 Merge branch 'nightly' into deletenotice_form_fix 2015-07-11 10:49:41 +02:00
Mikael Nordfeldth
e439ace944 bump alpha number to ease remote debugging help 2015-07-11 01:00:04 +02:00
Mikael Nordfeldth
fae79b5812 Forgot to push TargetedRss10Action 2015-07-11 00:32:05 +02:00
Mikael Nordfeldth
bb29d18017 ShowfavoritesAction now extends ShowstreamAction 2015-07-11 00:28:32 +02:00
Mikael Nordfeldth
5929b629e5 define Subscription_queue::exists as static 2015-07-11 00:08:23 +02:00
Mikael Nordfeldth
76396041e2 $notices and $notice is not the same, silly! 2015-07-10 23:30:59 +02:00
Mikael Nordfeldth
64fbc93217 ApiAction::dateTwitter was called statically from a plugin 2015-07-10 23:30:17 +02:00
Mikael Nordfeldth
24b1e26406 MagicEnvelope called DOMDocument::loadXML statically
but apparently we shouldn't do this, despite recommended on https://secure.php.net/manual/en/domdocument.loadxml.php
2015-07-10 23:24:50 +02:00
Mikael Nordfeldth
961031bc28 Cleaning up Directory plugin lists 2015-07-10 23:19:54 +02:00
Mikael Nordfeldth
ad453785f0 FoafAction now a ManagedAction 2015-07-10 23:10:24 +02:00
Mikael Nordfeldth
08bd4fa6a8 ShowstreamAction no longer has public ->profile 2015-07-10 23:09:44 +02:00
Mikael Nordfeldth
3900a739b9 Forgotten migrations of ->profile and ->user to ->getTarget()
GalleryAction extends ProfileAction which no longer uses ->profile and ->user
2015-07-10 23:05:02 +02:00
Mikael Nordfeldth
55b2d124bc The 'target' is an argument to common_local_url not the target profile 2015-07-10 22:49:38 +02:00
Mikael Nordfeldth
a964ea61cd Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-07-10 16:03:41 +02:00
mmn
c037f04d17 Merge branch 'jquery-cookie-undefined' into 'nightly'
Updates jquery.cookie usage to match changes from v1.4.0

`$.cookie('name')` returns `undefined` instead of `null` when cookie is not present.

This fixes a problem where the browser was never asking for location,
and the JSON.parse call was throwing an exception when the geolocation
cookie wasn't present.

Deleting a cookie should use `$.removeCookie('name')` instead of `$.cookie('name', null)`.

[Reference](7f88a4e631/CHANGELOG.md (140)).

See merge request !20
2015-07-10 14:03:30 +00:00
Mikael Nordfeldth
7c4e550e31 Merge branch 'master' into nightly 2015-07-10 16:02:55 +02:00
mmn
204f6bb503 Merge branch 'permissions-fix' into 'master'
Permissions fix

During Debian packaging I noticed that executable permissions on several files are incorrect.  In the fist case files such as .gitignore, .png, .ico were wrongly set as executable.  In the second case, files that are meant to be running on the command line such as maintenance scripts starting with "#!/usr/bin/env php" do not have executable permissions.  These files were identified by lintian automatic checking program.  This merge request aims to fix the problem.

See merge request !21
2015-07-10 14:01:49 +00:00
Mikael Nordfeldth
6c4985476e Merge branch 'nightly' into deletenotice_form_fix 2015-07-10 13:54:06 +02:00
Mikael Nordfeldth
9a92b58057 ShowstreamAction tidying up
Lots of these changes mean that we're requiring certain values to
either by typed properly or return the expected value. If it doesn't
there should be a fatal exception thrown which we can followup in the
logs and won't go silently suppressed.
2015-07-10 13:44:47 +02:00
Mikael Nordfeldth
f8877e015b static definition of User::getByNickname 2015-07-10 12:59:19 +02:00
Mikael Nordfeldth
50c297bcbe ShowstreamAction fixes so it's not as horrible 2015-07-10 12:34:06 +02:00
Mikael Nordfeldth
6a36121a56 Spiff up the PersonalTagCloudSection class
missing a fix in actions/showstream.php for the switched position
of arguments in the constructor
2015-07-10 12:23:55 +02:00
Mikael Nordfeldth
f9d82a6ac5 Easy comparison of two Profile objects 2015-07-10 12:19:55 +02:00
Mikael Nordfeldth
e567406c9f RedirecturlAction now extends ManagedAction 2015-07-10 12:08:33 +02:00
Bhuvan Krishna
1ab4c9998a Add executable permission to script missing it 2015-07-10 14:46:08 +05:30
Bhuvan Krishna
d6924f7680 Remove executable permissions where unnecessary 2015-07-10 14:36:34 +05:30
Mikael Nordfeldth
76432b958b delete notice form fix 2015-07-10 00:52:22 +02:00
Mikael Nordfeldth
e46b2803a7 Introducing TargetedRss10Action for simplifying RSS 1.0 2015-07-10 00:28:36 +02:00
Mikael Nordfeldth
1cbf2510e7 /:nickname/all/rss had to be before /:tagger/all/:tag
...though this makes it impossible to get a list called "rss"

so FIXME, the RSS 1.0 actions should be in the API or something
2015-07-10 00:27:26 +02:00
Mikael Nordfeldth
8cc85f684b Need to supply data to NoSuchUserException 2015-07-10 00:08:09 +02:00
Mikael Nordfeldth
b5b7a27f9b Rss10Action migrated to ManagedAction 2015-07-09 23:29:30 +02:00
Mikael Nordfeldth
06f60b57c1 StartpageAction essentially duplicated TopAction 2015-07-09 23:01:50 +02:00
Mikael Nordfeldth
37e8b027f9 PEAR's Mail_mimeDecode updated 2015-07-09 22:29:23 +02:00
Mikael Nordfeldth
ed248f7f5a No static calls (PEAR sucks ass) 2015-07-09 22:26:03 +02:00
Mikael Nordfeldth
90565cc44d searchsubmenu item() function definition 2015-07-09 22:19:19 +02:00
Mikael Nordfeldth
9fb3ea3ce6 static function definitions and minor simplifying 2015-07-09 22:16:14 +02:00
Mikael Nordfeldth
d63bca9d3c Rss10Action now in an autodetected file. 2015-07-09 14:22:22 +02:00
Mikael Nordfeldth
9b8ac2d404 CSS can handle alternating row colouring now 2015-07-09 14:10:38 +02:00
Mikael Nordfeldth
65f1f74f2b Type controlling in lib/atomnoticefeed.php 2015-07-09 13:56:02 +02:00
Mikael Nordfeldth
43cec8eaac API actions are not ManagedAction yet 2015-07-09 13:46:03 +02:00
Mikael Nordfeldth
2103075ffa Delete notice script added. Give an ID or URI. 2015-07-08 12:30:21 +02:00
Mikael Nordfeldth
f939485b52 Gravatar relies on closed source 3rd party 2015-07-08 07:57:27 +02:00
Mikael Nordfeldth
77957372ab File_redirection static fixes and simplifying 2015-07-07 20:16:05 +02:00
Mikael Nordfeldth
33dc06ae21 handle function declaration to match parent 2015-07-07 20:02:41 +02:00
Mikael Nordfeldth
d0458b824a File_redirection minor coding layout fix 2015-07-07 19:59:43 +02:00
Mikael Nordfeldth
45ee2060fa File_redirection is called statically 2015-07-07 19:48:18 +02:00
Mikael Nordfeldth
6919dda958 RobotstxtAction migrated to ManagedAction 2015-07-07 19:45:01 +02:00
Mikael Nordfeldth
acdcb2ad8d prepare and handle function to match parents 2015-07-07 19:34:42 +02:00
Mikael Nordfeldth
edef6f929a Don't statically call Validate functions 2015-07-07 19:30:14 +02:00
Mikael Nordfeldth
62c6ed58ba DirectMessagePlugin actions modernified 2015-07-07 19:18:45 +02:00
Mikael Nordfeldth
ed3022adc1 Janrain OpenID extlib updated
From their tree on a4090d0b30f850044413630333341cd327cbb55a
Source: https://github.com/openid/php-openid
2015-07-05 23:07:41 +02:00
Mikael Nordfeldth
3b6a424c9f Mismatching function declarations. 2015-07-04 19:51:36 +02:00
Mikael Nordfeldth
9054bb69e9 I had some inheritance the wrong way around. 2015-07-04 19:48:35 +02:00
Mikael Nordfeldth
5424c82423 Forgot NoticestreamAction, thanks roland. 2015-07-04 00:02:05 +02:00
Mikael Nordfeldth
3f3482f017 Stronger typing and fixes in RequireValidatedEmail 2015-07-03 10:17:15 +02:00
Mikael Nordfeldth
d5a24da167 only variables can be sent by reference (strict standards) 2015-06-25 20:13:27 +02:00
Mikael Nordfeldth
030b570051 non-static method was called statically, now defined static 2015-06-23 13:24:19 +02:00
Mikael Nordfeldth
3a74f49182 stricter typing in GroupsNav menu widget 2015-06-23 12:56:19 +02:00
Mikael Nordfeldth
748bd825e7 SelftagWidget had mismatching url() definition 2015-06-23 12:37:00 +02:00
Mikael Nordfeldth
e6507a0f18 FeedList widget had mismatching show() definition 2015-06-23 12:26:44 +02:00
Mikael Nordfeldth
1864a9de38 Accessibility fix, make yes button more explicit 2015-06-22 23:40:51 +02:00
Chimo
a25d952ef9 $.cookie('n', null) -> $.removeCookie as of 1.4.0 2015-06-20 19:39:12 -04:00
Chimo
9e1d4bfbf0 jquery-cookie returns undefined as of 1.4.0
when a cookie isn't set.

This fixes a problem where the browser was never asking for location,
and the JSON.parse call was throwing an exception when the geolocation cookie wasn't
present.
2015-06-20 19:38:58 -04:00
Mikael Nordfeldth
fde3b99e8e Less redundant code, please 2015-06-21 01:37:29 +02:00
mmn
29620619b5 Merge branch 'plugins-documentation' into 'nightly'
Plugins documentation

* Bring content from https://web.archive.org/web/20140821102047/http://status.net/wiki/HOWTO_Make_a_Plugin
* Point link on Plugins Admin Panel page to plugins/README.md ( Ref #53 )
* Add link to "Community Plugins"

See merge request !19
2015-06-20 22:49:31 +00:00
mmn
46cf5aa2a4 Merge branch 'remove_broken_plugins_from_default' into 'nightly'
Drop QnA from default plugins

Since it doesn't work (at least at the moment) it shouldn't be proposed as default plugin

See merge request !15
2015-06-20 22:48:49 +00:00
mmn
ee9176f20d Merge branch 'drop_corporate_text' into 'nightly'
Drop corporate text plus minor rewording

Following [this discussion](https://gnusocial.no/conversation/190441#notice-190441) I only fixed corporate-only occurrences. I suggest People instead of User directory. Moreover I fixed a minor inconsistency (Trends should be preferred to Trending topics).

The full locale folder should be fixed once we have digitaldreamer's feedback. But we can at least start from this.

See merge request !16
2015-06-20 22:48:31 +00:00
mmn
1d11153396 Merge branch 'character_count' into 'nightly'
Fix #31 character count floats in replies

See merge request !17
2015-06-20 22:47:22 +00:00
Mikael Nordfeldth
7c373edf4d implement javascript callbacks (no remove yet) 2015-06-14 23:14:00 +02:00
Mikael Nordfeldth
41fe435553 oops forgot an alert() 2015-06-14 22:04:57 +02:00
Mikael Nordfeldth
01cbe744b6 Move some javascript out to an E (Event) object 2015-06-14 21:57:39 +02:00
chimo
e32cd2dd15 Add link to 'community plugins' 2015-06-14 12:05:54 -04:00
chimo
39462c3a2e Documentation about plugins
* Content in plugins/doc/Plugin_development.md from:
https://web.archive.org/web/20140821102047/http://status.net/wiki/HOWTO_Make_a_Plugin

* Point link on Plugins Admin Panel page to plugins/README.md
2015-06-14 11:59:46 -04:00
abjectio
e9a6d5be51 Removed the full AGPL license file 2015-06-13 11:11:50 +02:00
abjectio
fca53a56b4 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-06-13 11:10:10 +02:00
Mikael Nordfeldth
a03249dd99 No reason to have makeRegex protected 2015-06-12 17:15:39 +02:00
abjectio
437dc3d713 Added .pot file and Norwegian translation 2015-06-10 22:11:34 +02:00
abjectio
ea076a8783 Bug in use of gettext/locale string 2015-06-10 22:10:14 +02:00
abjectio
965cd92a72 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-06-10 22:09:30 +02:00
aroquen
204ebe607d Fix #31 character count floats in replies 2015-06-07 22:44:33 +02:00
aroquen
267a49103e Rewording: trends instead of trending topics for consistency with other occurrences 2015-06-07 22:28:19 +02:00
aroquen
a42d74a238 Rewording: people instead of user directory 2015-06-07 22:27:31 +02:00
aroquen
f9c51c2478 No corporate-only strings in code base anymore 2015-06-07 22:24:14 +02:00
aroquen
776b82e6e8 Drop QnA from default plugins 2015-06-07 14:05:51 +02:00
Mikael Nordfeldth
8a002e98bc File-related functions not declared static 2015-06-07 10:01:28 +02:00
Mikael Nordfeldth
4fc62a8bf5 Another function not matching parent declaration 2015-06-07 09:26:12 +02:00
abjectio
3bdcdfd114 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-06-06 23:32:10 +02:00
mmn
f7769c17b5 Merge branch 'subscribers_as_addressees' into 'nightly'
Add subscribers as addressees to toselector

Populate the dropdown recipient menu first with groups, then with followed users. There is no alphabetical sorting (perhaps to be added later?). See this discussion https://gnusocial.no/conversation/190705#notice-190705. Thanks to @chimo.

Hope it is good now.

See merge request !14
2015-06-06 21:14:50 +00:00
Chimo
76c9971c31 Show nickname if user has oldschool option enabled 2015-06-06 23:07:34 +02:00
aroquen
e46dcc0cc4 Add subscribed users to dropdown menu. 2015-06-06 23:07:13 +02:00
abjectio
9dc8250956 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-06-06 22:27:51 +02:00
mmn
3a910d8c8c Merge branch 'doc-src' into 'nightly'
Small updates to doc-src files

* Remove mentions of GTalk since it's been replaced by Hangouts which doesn't do XMPP
* Update URLS, use HTTPS where possible

See merge request !11
2015-06-06 20:27:00 +00:00
mmn
e203271d95 Merge branch 'nightly' into 'nightly'
fixed realtime action buttons and marker in base, adjusted settings in neo-quitter

fixed realtime action buttons and marker in base so that all neo themes benefit from it, adjusted settings in neo-quitter

See merge request !12
2015-06-06 20:20:52 +00:00
Mikael Nordfeldth
6267e7a930 Merge branch 'hackfest' into nightly 2015-06-06 22:15:39 +02:00
Mikael Nordfeldth
d76d37340a Function declarations not matching their parents 2015-06-06 22:14:56 +02:00
Mikael Nordfeldth
871912a00a Plugins didn't match lib/plugin.php onPluginVersion function definition
I ran:
for i in `grep -R onPluginVersion...version plugins/|cut -d: -f1`; do sed -i '{ s/\(onPluginVersion(\)\(\&\$versions\)/\1array \2/ }' $i; done
2015-06-06 22:04:01 +02:00
Mikael Nordfeldth
628e7fa583 ApiOAuthAction functions didn't match parents 2015-06-06 21:57:42 +02:00
Mikael Nordfeldth
15643c802b EndActionExecute event should only run if the Action is not overridden 2015-06-06 20:13:11 +02:00
Mikael Nordfeldth
814696c202 DB_DataObject delete function calls PEAR::isError directly, ignore E_STRICT 2015-06-06 20:01:03 +02:00
Mikael Nordfeldth
50070c9b0a GS_DataObject overrides DB_DataObject insert() too 2015-06-06 19:59:01 +02:00
Mikael Nordfeldth
06503cc11b joinAdd calls DB_DataObject::factory directly so no GS_DataObject override 2015-06-06 19:41:23 +02:00
Mikael Nordfeldth
daaafd86e2 DB_DataObject recommends using ->tableName() 2015-06-06 19:35:10 +02:00
Mikael Nordfeldth
9e52b6f2d1 DB_DataObject v1.11.4 made some functions static (finally!) 2015-06-06 19:30:53 +02:00
Marcus Moeller
fae8e38616 fixed realtime action buttons and marker in base, adjusted settings in neo-quitter 2015-06-06 19:18:20 +02:00
Mikael Nordfeldth
4cba7a4002 DB_DataObject v1.11.4, finally! 2015-06-06 19:13:57 +02:00
Mikael Nordfeldth
9712a92853 MITM the DB_DataObject to avoid PEAR::DB strict standards warnings 2015-06-06 18:29:15 +02:00
abjectio
28eb441812 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-06-06 18:15:17 +02:00
Mikael Nordfeldth
3c2a720b21 Found a remnant staticGet call 2015-06-06 17:39:25 +02:00
Mikael Nordfeldth
6478034e92 Diaspora-compatible Salmon slap receival
We're not all the way there yet, there is something which seems to bugger
up profile discovery from their end.
2015-06-06 17:14:38 +02:00
Mikael Nordfeldth
faf14197cd Diaspora doesn't understand our Salmon POST, so send again 2015-06-06 16:57:29 +02:00
Mikael Nordfeldth
b63054cb1d OStatus update-profile.php script now finds Diaspora salmon URLs 2015-06-06 16:18:22 +02:00
Mikael Nordfeldth
4de125dd84 Moved FeedSubException parent class to own file 2015-06-06 16:02:25 +02:00
Mikael Nordfeldth
268b901048 Maintainer change for Ostatus_profile 2015-06-06 15:58:08 +02:00
Chimo
136c3140c2 doc-src: Update URLs 2015-06-06 09:12:30 -04:00
Chimo
7e90103f21 "im" doc-src: GTalk has been deprecated 2015-06-06 09:10:42 -04:00
Mikael Nordfeldth
d4fc064e44 Include the Diaspora GUID string in our XRD metadata 2015-06-06 14:49:39 +02:00
Mikael Nordfeldth
623a7eee57 Diaspora seeds tend to give the key type in 'type' attribute 2015-06-06 14:46:17 +02:00
Mikael Nordfeldth
57943cad99 Magicsig gets toFingerprint output
We give this as a lowercase, sha256 hexadecimal digest of the string:
TYPE + "." + BASE64(modulus as bytes) + "." + BASE64(exponent as bytes)

Where TYPE in all our cases up until now at least are "RSA"
2015-06-06 14:35:48 +02:00
Mikael Nordfeldth
c5f79fd2f3 Magicsig gets toFingerprint function. 2015-06-06 14:33:43 +02:00
Mikael Nordfeldth
e212f2ae77 Moved Diaspora specific metadata to own plugin 2015-06-06 13:49:27 +02:00
mmn
a39f51c044 Merge branch 'tagprofile-ajax-fix' into 'nightly'
Only serve tagprofile HTML if we aren't POSTing via ajax

This fixes an issue where POSTing the selftag form in the
profile_block sidebar via AJAX would generate an XML response
containing both the content from doPost() and showPage(),
resulting in invalid XML.

These changes make it so that if we're POSTing via AJAX,
we serve content from doPost(), otherwise we serve showPage()
but never both.

See merge request !8
2015-06-06 10:26:43 +00:00
abjectio
8abf96cfd8 Edited the readme file to be more inline with the core repo 2015-06-06 12:14:50 +02:00
abjectio
b0a598ba7b Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-06-06 11:31:18 +02:00
abjectio
e269a3fad9 Added ChooseTheme plugin to the repository 2015-06-06 11:28:34 +02:00
Chimo
47c83f4c49 Only serve tagprofile HTML if we aren't POSTing via ajax
This fixes an issue where POSTing the selftag form in the
profile_block sidebar via AJAX would generate an XML response
containing both the content from doPost() and showPage(),
resulting in invalid XML.

These changes make it so that if we're POSTing via AJAX,
we serve content from doPost(), otherwise we serve showPage()
but never both.
2015-06-06 00:22:14 -04:00
mmn
1635d459ec Merge branch 'threaded_replies_nightly' into 'nightly'
Fix threaded replies floating

Following Marcus fixes for neo-quitter, I think the floating of replies should be fixed directly in the base theme. What do you think? This issue affects all derived themes (although sometimes only marginally) see https://gnusocial.no/conversation/126588.

These commits implement some of Marcus floating fixes directly in base plus the fav, reply icons are better aligned with the permalink and timestamp (notice-options change).

See merge request !7
2015-06-05 22:38:13 +00:00
aroquen
77db161aca Threaded replies and conversations do not float left 2015-06-06 00:23:54 +02:00
aroquen
b3667c72c7 Align notice-options with timestamp and permalink 2015-06-06 00:23:23 +02:00
mmn
e3b1d401fd Merge branch 'nightly' into 'nightly'
Updated neo-quitter

Updated neo-quitter to match the colors of the default theme
Fixed alignment issues
Changed threaded view to inline in order to make it more readable

See merge request !6
2015-06-05 21:44:49 +00:00
Mikael Nordfeldth
ad7cd155fb floating elements CSS fix by aroque 2015-06-05 23:44:59 +02:00
Mikael Nordfeldth
882ce56cf4 buttle's local/plugins translation file fix 2015-06-05 23:43:28 +02:00
Mikael Nordfeldth
29e1203609 Merge request for documentation and URLs 2015-06-05 23:16:22 +02:00
mmn
a3011febdf Merge branch 'nightly' into 'nightly'
Fixing broken links during end of installation #26 - https://git.gnu.io/gnu/gnu-social/issues/26

See merge request !2
2015-06-05 21:10:53 +00:00
Mikael Nordfeldth
6e28524647 Extensions of GalleryActions enabled again (nickname check) 2015-06-05 21:50:00 +02:00
Mikael Nordfeldth
c26e08d6a6 some more extensions of ShowstreamAction broke in last commit 2015-06-05 21:49:34 +02:00
Mikael Nordfeldth
9376a6d75a ModPlus plugin now links to remote profiles with UserbyidAction 2015-06-05 21:30:50 +02:00
Mikael Nordfeldth
8ad81a6aa3 Allow showing profiles with UserbyidAction (/user/:id) 2015-06-05 21:24:41 +02:00
abjectio
c00b2ecc4b Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-06-05 20:57:28 +02:00
root
dd2fb27316 improved favocin 2015-06-05 19:12:17 +02:00
Marcus Moeller
bc7eb792f4 updated favicon color 2015-06-05 17:08:35 +02:00
Marcus Moeller
abfa052861 use none instead of unset 2015-06-05 16:54:55 +02:00
Marcus Moeller
dbfc8092d7 even display threads inline if no user is logged in 2015-06-05 16:47:41 +02:00
Marcus Moeller
6137d301be set threaded view to inline to make it more readable 2015-06-05 16:37:34 +02:00
Marcus Moeller
af87552e04 updated neo-quitter colours to match the default theme, fixed some alignment and margin issues 2015-06-05 16:21:04 +02:00
Mikael Nordfeldth
94f5247f91 $this->client_ip was not always set in AntiBrutePlugin onEndCheckPassword 2015-06-05 15:34:14 +02:00
Mikael Nordfeldth
dee4ca37ab GROUPS_PER_MINILIST was undefined in profileaction.php 2015-06-05 14:07:03 +02:00
Mikael Nordfeldth
b24d711f55 Network wide feed link would NEVER show! 2015-06-05 14:01:03 +02:00
Mikael Nordfeldth
50f0235654 Oops, don't forget to remove var_dump(...) 2015-06-04 22:34:28 +02:00
Mikael Nordfeldth
cafab14f2b Modernize File_redirection to use Managed_DataObject functions 2015-06-04 22:33:36 +02:00
Mikael Nordfeldth
63251fb9d0 Modernize File_thumbnail to use Managed_DataObject functions 2015-06-04 22:29:40 +02:00
Mikael Nordfeldth
fc9de94cbd Modernize Queue_item to use Managed_DataObject functions 2015-06-04 22:27:53 +02:00
Mikael Nordfeldth
f9698598c6 Modernize Profile_prefs to use Managed_DataObject functions 2015-06-04 22:26:31 +02:00
Mikael Nordfeldth
d1afc78124 Modernize File_to_post to use Managed_DataObject functions 2015-06-04 22:22:49 +02:00
Mikael Nordfeldth
ebdd792b6f getByPK (primary key) lookup for Managed_DataObject instances 2015-06-04 22:17:40 +02:00
Mikael Nordfeldth
ca19a5cd6d Easier pkeyCols call to get primary key columns 2015-06-04 21:51:56 +02:00
Mikael Nordfeldth
20145092ce Publish OAuth data in host-meta 2015-06-04 18:54:09 +02:00
Mikael Nordfeldth
5718f812d9 Network public publicgroupnav link would always show 2015-06-04 17:39:23 +02:00
Mikael Nordfeldth
fe6498e7c8 Send objects instead of integers to File_to_post::processNew 2015-06-04 17:36:11 +02:00
Mikael Nordfeldth
5358fb3cce Use the same cache string in all places for file:notice-ids 2015-06-04 17:02:45 +02:00
Mikael Nordfeldth
9449235765 Remove some clutter from OMB plugin 2015-06-04 01:14:26 +02:00
Mikael Nordfeldth
a4739b0107 Move some last OpenID stuff into the OpenID plugin (XRDS) 2015-06-04 01:08:03 +02:00
Mikael Nordfeldth
bdd15cfe63 don't double the output for some PublicAction->extraHead 2015-06-04 00:56:46 +02:00
Mikael Nordfeldth
26631bf9e6 Show network public feed link in PublicGroupNav 2015-06-04 00:53:35 +02:00
Mikael Nordfeldth
551c69ed56 Extend Networkpublic and Public actions from SitestreamAction 2015-06-04 00:51:07 +02:00
Mikael Nordfeldth
2b2ebfc254 bump version to 1.2.0-alpha1
since we fixed the search issue and updated jquery
2015-06-03 22:57:58 +02:00
Mikael Nordfeldth
fd121f371a case insensitive indexing for content and nick/fullnames (search)
This broke when changing the database to utf8mb4 instead of utf8,
since utf8_general_ci wasn't accepted and the engine fell back to
utf8mb4_bin. Now we're back in case insensitive search business!
2015-06-03 22:43:51 +02:00
Mikael Nordfeldth
c5da7306bd return logic tidied up for command interpretation 2015-06-02 14:25:45 +02:00
Mikael Nordfeldth
2bf0ec719d initialize command interpretation result to false 2015-06-02 14:17:59 +02:00
Mikael Nordfeldth
7e388e697d fallback to local URL if reply->getUrl() is missing
Remote Activity notices generally don't have a proper HTTP URL associated.
2015-06-02 13:55:13 +02:00
Mikael Nordfeldth
0726dcd06c Start using NoParentNoticeException more widely 2015-06-02 13:50:52 +02:00
Mikael Nordfeldth
c84b21008e Notice->getParent code reuse
...not entirely sure whether to allow getParent calls on Notice
objects which have not been created, but we'll leave that in for now...
2015-06-02 13:46:23 +02:00
Mikael Nordfeldth
6b9a8b7b19 Reuse code from our classes, don't write own algorithms 2015-06-02 13:45:22 +02:00
Mikael Nordfeldth
e728e2aa81 typing, added typing to some common_* calls in util.php 2015-06-02 13:19:12 +02:00
Mikael Nordfeldth
2096c18e57 use array_key_exists instead of empty to test array key 2015-06-02 13:14:57 +02:00
Mikael Nordfeldth
2cebbead75 Accidentally presented Atom feed as ActivityStreams JSON 2015-06-02 12:58:30 +02:00
Mikael Nordfeldth
a2ddcc124f No redirect follow on HEAD request (bump to PHP5.5 minimum requirement)
We're using the try-catch-finally clause where "finally" wasn't introduced
until PHP 5.5, so our minimum requirement for GNU social is bumped to that.
2015-06-02 11:07:11 +02:00
Mikael Nordfeldth
82f9b6908c Fake recovery by email address, to hide registrants on the site 2015-05-30 23:29:58 +02:00
Mikael Nordfeldth
731d283159 Password recovery logic cleaned up 2015-05-30 23:18:17 +02:00
abjectio
1d85fd35a2 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social 2015-05-30 17:21:52 +02:00
Mikael Nordfeldth
b4b8cb57b3 slugify console.php prompt name
since PHP thought it was fun to crash on Quitter España and I
couldn't be bothered messing with readline
2015-05-30 16:40:00 +02:00
Mikael Nordfeldth
3294d704a4 scripts/nukefile.php for blasting crap from the server
Deletes notices and the locally stored file based on File id, as
you may want to just get rid of shit sometimes.
2015-05-30 15:41:04 +02:00
Mikael Nordfeldth
f926e27a65 urlhash will _be_ NULL on update, so NOT NULL won't work 2015-05-27 22:37:20 +02:00
Mikael Nordfeldth
c31d6608a8 remove _all_ file URLs not just the duplicates 2015-05-27 21:54:51 +02:00
Mikael Nordfeldth
cd0b70dbc1 upgrade fix for file URLs longer than 191 chars 2015-05-27 21:31:29 +02:00
Mikael Nordfeldth
0e6b80ded3 more debugging info on failed schema.php runSqlSet 2015-05-27 20:21:05 +02:00
abjectio
19e7cf5f4a Merge branch 'master' of git.gnu.io:gnu/gnu-social 2015-05-26 22:27:27 +02:00
abjectio
0200b1d784 Merge branch 'nightly' of git.gnu.io:gnu/gnu-social into nightly 2015-05-26 22:25:49 +02:00
Mikael Nordfeldth
6b717a6a69 Throw exception if Local_group has no corresponding User_group 2015-05-26 13:14:26 +02:00
Mikael Nordfeldth
925aacec1a Throw exception when a Conversation is not found 2015-05-26 13:11:08 +02:00
Mikael Nordfeldth
bb8d377b91 Redirect to group URL for /nickname pattern 2015-05-26 13:10:37 +02:00
aroquen
f7176e7aef Textarea resizes only vertically. 2015-05-25 00:31:55 +02:00
buttle
ef0385a25c fix for ./local/plugins/* translation files
translations for 3rd party plugins were not being detected
2015-05-21 09:35:52 +02:00
abjectio
5675f328b6 Fixing broken links during end of installation #26 - https://git.gnu.io/gnu/gnu-social/issues/26 2015-05-20 00:02:19 +02:00
abjectio
63ca11fc7d Changed URLs in README to reflect new host git.gnu.io 2015-05-19 00:00:22 +02:00
Mikael Nordfeldth
4a6f509aa5 More automated htaccess.sample 2015-05-10 19:41:36 +02:00
Mikael Nordfeldth
d010c5a581 magnet URL linkify support 2015-05-09 15:54:09 +02:00
Mikael Nordfeldth
e1822c2b66 schemacheck hint in newly installed config.php 2015-05-03 23:07:31 +02:00
Mikael Nordfeldth
cd42ee7e85 Allow adding preload token to HSTS header
Use by adding this to config.php:

addPlugin('StrictTransportSecurity', array('preloadToken'=>true));
2015-05-03 23:05:47 +02:00
Mikael Nordfeldth
6d72800098 StrictTransportSecurity syntax fix 2015-05-03 22:50:44 +02:00
Mikael Nordfeldth
2b58d6b774 Merge branch 'quitagram' into nightly
Fixes that make quitagram work better amongst other things.
2015-04-22 22:17:50 +02:00
Mikael Nordfeldth
7e2caa0bb1 Make it easier to hide attachments list header 2015-04-22 22:13:55 +02:00
Mikael Nordfeldth
51ecd6be15 Fix some ProfileAction stuff, add function profileActionPreparation
Split up files with multiple classes to multiple files for autoload, sorry
for baking this into this commit.
2015-04-22 21:22:38 +02:00
Mikael Nordfeldth
7601e10b6a getFullname function on Profile class
No need to fix it for User since we shouldn't use that mostly.
2015-04-22 20:57:05 +02:00
Mikael Nordfeldth
4210096a6f MobileProfile plugin cleanup 2015-04-22 19:50:56 +02:00
Mikael Nordfeldth
f642da201d Easier to modify DTD for HTMLOutputter 2015-04-22 19:44:58 +02:00
Mikael Nordfeldth
0a0455b8d2 minor coding fixes 2015-04-22 18:41:54 +02:00
Mikael Nordfeldth
529b2f6613 deleteuser script changed to deleteprofile
Delete remote profiles by providing their ID if known, or you can
provide their profile URI with --uri=https://...

Useful for cleaning up old, long gone and no longer desired profiles
and their notices.
2015-04-21 23:47:55 +02:00
Mikael Nordfeldth
f9f7c46751 Better typing in lib/framework.php 2015-04-20 00:26:16 +02:00
Mikael Nordfeldth
8a119dc970 We already throw exceptions for PEAR errors 2015-04-20 00:26:00 +02:00
Mikael Nordfeldth
b75e88f65f Get better PEAR:DB error logging (debuginfo included) 2015-04-20 00:02:13 +02:00
Mikael Nordfeldth
1194c47c5d ksort on commands array to avoid converting to numerical array 2015-04-19 21:12:26 +02:00
Mikael Nordfeldth
a55d1df585 Clearer warning message on non-existing file 2015-04-16 00:22:09 +02:00
Mikael Nordfeldth
0337a7b866 file_redirection duplicates are removed too in maintenance script 2015-04-15 23:56:23 +02:00
Mikael Nordfeldth
5f1e9e80c2 remove duplicate file URLs script 2015-04-15 23:26:24 +02:00
Mikael Nordfeldth
c5715bc756 File_to_post indexing and cleaning preparations 2015-04-15 23:25:12 +02:00
Mikael Nordfeldth
e4a17fedc6 Translation contained bad HTML 2015-04-14 22:01:22 +02:00
Mikael Nordfeldth
ee96a08700 latest php-gettext from https://code.launchpad.net/~danilo/php-gettext/ 2015-04-14 21:40:09 +02:00
Mikael Nordfeldth
4526c3f712 If /var/run was unwritable stopdaemons did not find pid file 2015-04-13 21:51:35 +02:00
Mikael Nordfeldth
eefef44378 filename is never used for indexing so let's make it text
varchar is pretty limited so let's avoid it if we can
2015-04-10 22:16:44 +02:00
Mikael Nordfeldth
d7d96806a4 removed unused config setting 2015-04-10 13:15:12 +02:00
Mikael Nordfeldth
5e834e8119 We're logging this already elsewhere 2015-04-09 12:27:04 +02:00
Mikael Nordfeldth
cbabab273f ImPlugin classes generally require background daemons in CLI
If someone designs an ImPlugin which _doesn't_ require the queues to be
handled in background daemons then they can set the requires_cli property
to false in their class.
2015-04-09 12:17:31 +02:00
Mikael Nordfeldth
0bd16b521c OpportunisticQM shouldn't try to handle what it can't find 2015-04-09 12:15:34 +02:00
Mikael Nordfeldth
07d92acfd0 Fix "unique notice form ID" code for reply forms 2015-04-06 17:35:29 +02:00
Mikael Nordfeldth
ef1f17fc2a Don't use <object> for application/ogg
...this was actually because it would autoplay in Iceweasel/Firefox
despite all parameters for autoplay/autostart being set to false or 0.
2015-04-05 21:35:54 +02:00
Mikael Nordfeldth
5081477e95 enclosures in Atom were not included 2015-04-05 20:24:17 +02:00
Mikael Nordfeldth
f094918cf6 Revert "Inverse logic error in OembedPlugin"
This reverts commit 9ab996d9e6.
2015-04-05 15:44:04 +02:00
Mikael Nordfeldth
c94d9994d8 Merge branch 'nightly'
Conflicts:
	INSTALL
2015-04-05 15:35:54 +02:00
Mikael Nordfeldth
f6432fdf48 File_thumbnail->getUrl now gives Attachment_thumbnail action URL
That in turn redirects to the statically stored file.
2015-04-03 21:21:39 +02:00
Mikael Nordfeldth
4fc4f91b9e Allow oEmbed data from all sites by default 2015-04-03 21:00:19 +02:00
Mikael Nordfeldth
9ab996d9e6 Inverse logic error in OembedPlugin 2015-04-03 20:59:56 +02:00
Mikael Nordfeldth
3cf5fe8795 bad URL is a clientError 2015-04-03 20:58:12 +02:00
Mikael Nordfeldth
94d3f50aee oEmbed Action logic simplified (early return) 2015-04-03 20:47:08 +02:00
Mikael Nordfeldth
3dc30b6b8f oEmbed helper never finished because of "break 2;" 2015-04-03 20:11:55 +02:00
Mikael Nordfeldth
8439efe77d Filter out img, video and audio tags in notice HTML
Because we don't want to auto-fetch items from a remote server. Such
items should be delivered as attachment metadata and portrayed in the
way the local instance chooses.

Choices for portrayal are either simply nullifying this and embedding
the data, linking the file remotely requiring a manual click or maybe
use remote oEmbed data etc. to download files locally so no remote
requests have to be made.
2015-03-15 14:35:29 +01:00
Mikael Nordfeldth
a1098fa153 Marcus Moeller fixed some layout bugs I introduced 2015-03-15 14:22:36 +01:00
Mikael Nordfeldth
0b81fbd590 imitate ->boolean with $def=false 2015-03-14 16:04:03 +01:00
Mikael Nordfeldth
f92fe73d03 php5-intl is required 2015-03-13 21:02:39 +01:00
Mikael Nordfeldth
4f2095ea00 No need to do pkeyGet if not repeated (thanks hannes)
Kind of reverts commit d9ed2a0
2015-03-13 09:49:09 +01:00
Mikael Nordfeldth
d9ed2a0fae repeated did not always find the Notice (deleted?) 2015-03-13 00:19:08 +01:00
Mikael Nordfeldth
4d6b5e70df Add repeated_id to SimpleStatusArray for Qvitter's benefit 2015-03-13 00:02:48 +01:00
Mikael Nordfeldth
a647a4f6b3 Moving some more repeat stuff into the Share plugin 2015-03-12 22:56:25 +01:00
Mikael Nordfeldth
2821be2bce Notice properties set in bad order on activityobject saving 2015-03-12 22:17:08 +01:00
Mikael Nordfeldth
0ff11ac978 Share never actually set the repeat_of value 2015-03-12 21:56:18 +01:00
Mikael Nordfeldth
5a235ffbf9 Event fixes for activityhandlerplugin 2015-03-12 21:53:44 +01:00
Mikael Nordfeldth
642f7f850d repeat actions to Share plugin 2015-03-12 21:46:10 +01:00
Mikael Nordfeldth
1adf5f2863 Merge branch 'nightly' into repeat_as_shareplugin 2015-03-12 20:54:06 +01:00
Mikael Nordfeldth
3ee673ac91 ImageFile->resize now totally replaced with resizeTo 2015-03-12 20:47:07 +01:00
Mikael Nordfeldth
20f8dd6565 Repeats shown in threaded noticelist now handled by plugin 2015-03-12 19:49:20 +01:00
Mikael Nordfeldth
940737a958 Finalize move of 'repeat' Command 2015-03-12 19:22:05 +01:00
Mikael Nordfeldth
1350987e2e Why did I keep these functions from FavoritePlugin? 2015-03-12 17:07:24 +01:00
Mikael Nordfeldth
dc31a3d7d6 Receiving Share activity fixes
Forgot to translate 'self' to 'Ostatus_profile' etc.
2015-03-12 16:59:50 +01:00
Mikael Nordfeldth
dcfcceb6f2 Share plugin now handles showing form in NoticeOptionItems 2015-03-12 16:33:34 +01:00
Mikael Nordfeldth
d24c4f349f Move share command interpretation to SharePlugin
Some fixes to the SharePlugin.php file came along
2015-03-12 15:58:57 +01:00
Mikael Nordfeldth
c85e78ac3c Remove unnecessary require lines and definitions
ProfileNoticeList and ProfileNoticeListItem are in their own files now too
2015-03-12 15:53:59 +01:00
Mikael Nordfeldth
7ddf586da7 Add ProfileNoticeList[Item] as separate lib class files 2015-03-12 15:53:28 +01:00
Mikael Nordfeldth
4e9e3cf0d5 Moving Ostatus_profile processShare to SharePlugin 2015-03-12 15:47:21 +01:00
Mikael Nordfeldth
5bea746fa8 Merge branch 'nightly' into repeat_as_shareplugin 2015-03-12 15:21:23 +01:00
Mikael Nordfeldth
5c8e56e8d2 Repeat confirmation dialogbox fix
until we use jQuery UI dialog stuff
2015-03-12 12:52:07 +01:00
Mikael Nordfeldth
6f0f8cd6c7 Initial work on making the Share verb handled in plugin 2015-03-12 12:27:49 +01:00
Mikael Nordfeldth
2a32af084f ImageFile $id argument is only for File objects
Remember to eliminate the Avatar/group logo call to ImageFile->resize!
2015-03-11 00:20:48 +01:00
Mikael Nordfeldth
6f76c8b59d Same as last commit, don't give ID if it's not for a File object 2015-03-11 00:14:14 +01:00
Mikael Nordfeldth
88411fb1ca ImageFile should not have an $id set if not a File 2015-03-10 23:52:50 +01:00
Mikael Nordfeldth
3302067aad javascript fixes for reply form fetching
It doesn't update the ID properly yet, which should be done to avoid
creating duplicate forms which are identical to HTML parsers...
2015-03-10 21:47:28 +01:00
Mikael Nordfeldth
0a2c51510c FormAction wants getInstructions to be protected
only showInstructions is public
2015-03-10 21:46:33 +01:00
Mikael Nordfeldth
c79b9a2289 more descriptive setting name 2015-03-10 18:10:27 +01:00
Mikael Nordfeldth
8cfe72c683 SiteNoticeInSidebar widget did not define divId() 2015-03-10 17:33:58 +01:00
Mikael Nordfeldth
9ef224fceb ActivityVerb is a core plugin 2015-03-10 17:24:23 +01:00
Mikael Nordfeldth
af67f15cf2 FavoritePlugin is now an "ActivityVerbHandlerPlugin" 2015-03-10 15:39:35 +01:00
Mikael Nordfeldth
e338931ffa Let the creator decide which HTML tag to use for NoticeListItem 2015-03-10 13:07:36 +01:00
Mikael Nordfeldth
502f26299b Notice elements don't need to be floating...
Maybe it was originally meant to be without a comma between .notice and .profile?
2015-03-10 12:09:20 +01:00
Mikael Nordfeldth
8a273eef20 Move AlreadyFulfilled check to Fave::addNew 2015-03-10 11:56:23 +01:00
Mikael Nordfeldth
fa080328cf favorite and like are synonyms of each other 2015-03-09 18:47:44 +01:00
Mikael Nordfeldth
fa3457f391 First ActivityVerbPlugin sketch 2015-03-09 17:36:21 +01:00
Mikael Nordfeldth
aa27c45230 Notice class got exception throwing getById
Less code, more happy.
2015-03-09 17:33:20 +01:00
Mikael Nordfeldth
f321c3c2ea Make 'activity' a blacklisted nickname
Will add a plugin called ActivityVerb which makes activity interaction
more standardized.
2015-03-09 17:08:10 +01:00
Mikael Nordfeldth
9bb648739b The argument formatting regexps are the third argument 2015-03-09 15:31:07 +01:00
Mikael Nordfeldth
3287ecc8d3 Delete Faves onProfileDeleteRelated instead of User 2015-03-09 15:01:05 +01:00
Mikael Nordfeldth
1e89540c3f Merge branch 'nightly', beginning of 1.2.x
Conflicts:
	plugins/APC/locale/APC.pot
	plugins/APC/locale/ast/LC_MESSAGES/APC.po
	plugins/APC/locale/be-tarask/LC_MESSAGES/APC.po
	plugins/APC/locale/br/LC_MESSAGES/APC.po
	plugins/APC/locale/de/LC_MESSAGES/APC.po
	plugins/APC/locale/es/LC_MESSAGES/APC.po
	plugins/APC/locale/eu/LC_MESSAGES/APC.po
	plugins/APC/locale/fr/LC_MESSAGES/APC.po
	plugins/APC/locale/gl/LC_MESSAGES/APC.po
	plugins/APC/locale/he/LC_MESSAGES/APC.po
	plugins/APC/locale/ia/LC_MESSAGES/APC.po
	plugins/APC/locale/id/LC_MESSAGES/APC.po
	plugins/APC/locale/it/LC_MESSAGES/APC.po
	plugins/APC/locale/mk/LC_MESSAGES/APC.po
	plugins/APC/locale/ms/LC_MESSAGES/APC.po
	plugins/APC/locale/nb/LC_MESSAGES/APC.po
	plugins/APC/locale/nl/LC_MESSAGES/APC.po
	plugins/APC/locale/pl/LC_MESSAGES/APC.po
	plugins/APC/locale/pt/LC_MESSAGES/APC.po
	plugins/APC/locale/pt_BR/LC_MESSAGES/APC.po
	plugins/APC/locale/ru/LC_MESSAGES/APC.po
	plugins/APC/locale/tl/LC_MESSAGES/APC.po
	plugins/APC/locale/uk/LC_MESSAGES/APC.po
	plugins/APC/locale/zh_CN/LC_MESSAGES/APC.po
	plugins/Adsense/locale/Adsense.pot
	plugins/Adsense/locale/be-tarask/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/br/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/ca/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/de/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/es/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/eu/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/fr/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/gl/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/he/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/ia/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/it/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/ja/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/ka/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/lb/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/lt/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/mk/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/ms/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/nb/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/nl/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/pl/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/pt/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/pt_BR/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/ru/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/sv/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/tl/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/tr/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/uk/LC_MESSAGES/Adsense.po
	plugins/Adsense/locale/zh_CN/LC_MESSAGES/Adsense.po
	plugins/Aim/locale/Aim.pot
	plugins/Aim/locale/af/LC_MESSAGES/Aim.po
	plugins/Aim/locale/ca/LC_MESSAGES/Aim.po
	plugins/Aim/locale/de/LC_MESSAGES/Aim.po
	plugins/Aim/locale/es/LC_MESSAGES/Aim.po
	plugins/Aim/locale/eu/LC_MESSAGES/Aim.po
	plugins/Aim/locale/fi/LC_MESSAGES/Aim.po
	plugins/Aim/locale/fr/LC_MESSAGES/Aim.po
	plugins/Aim/locale/gl/LC_MESSAGES/Aim.po
	plugins/Aim/locale/ia/LC_MESSAGES/Aim.po
	plugins/Aim/locale/it/LC_MESSAGES/Aim.po
	plugins/Aim/locale/mk/LC_MESSAGES/Aim.po
	plugins/Aim/locale/ms/LC_MESSAGES/Aim.po
	plugins/Aim/locale/nl/LC_MESSAGES/Aim.po
	plugins/Aim/locale/pl/LC_MESSAGES/Aim.po
	plugins/Aim/locale/pt/LC_MESSAGES/Aim.po
	plugins/Aim/locale/sv/LC_MESSAGES/Aim.po
	plugins/Aim/locale/tl/LC_MESSAGES/Aim.po
	plugins/Aim/locale/uk/LC_MESSAGES/Aim.po
	plugins/Blog/locale/Blog.pot
	plugins/Blog/locale/ar/LC_MESSAGES/Blog.po
	plugins/Blog/locale/br/LC_MESSAGES/Blog.po
	plugins/Blog/locale/ca/LC_MESSAGES/Blog.po
	plugins/Blog/locale/de/LC_MESSAGES/Blog.po
	plugins/Blog/locale/es/LC_MESSAGES/Blog.po
	plugins/Blog/locale/eu/LC_MESSAGES/Blog.po
	plugins/Blog/locale/fr/LC_MESSAGES/Blog.po
	plugins/Blog/locale/gl/LC_MESSAGES/Blog.po
	plugins/Blog/locale/ia/LC_MESSAGES/Blog.po
	plugins/Blog/locale/it/LC_MESSAGES/Blog.po
	plugins/Blog/locale/lt/LC_MESSAGES/Blog.po
	plugins/Blog/locale/mk/LC_MESSAGES/Blog.po
	plugins/Blog/locale/nl/LC_MESSAGES/Blog.po
	plugins/Blog/locale/tl/LC_MESSAGES/Blog.po
	plugins/Blog/locale/tr/LC_MESSAGES/Blog.po
	plugins/Irc/locale/Irc.pot
	plugins/Irc/locale/ca/LC_MESSAGES/Irc.po
	plugins/Irc/locale/de/LC_MESSAGES/Irc.po
	plugins/Irc/locale/es/LC_MESSAGES/Irc.po
	plugins/Irc/locale/eu/LC_MESSAGES/Irc.po
	plugins/Irc/locale/fi/LC_MESSAGES/Irc.po
	plugins/Irc/locale/fr/LC_MESSAGES/Irc.po
	plugins/Irc/locale/gl/LC_MESSAGES/Irc.po
	plugins/Irc/locale/ia/LC_MESSAGES/Irc.po
	plugins/Irc/locale/it/LC_MESSAGES/Irc.po
	plugins/Irc/locale/ja/LC_MESSAGES/Irc.po
	plugins/Irc/locale/mk/LC_MESSAGES/Irc.po
	plugins/Irc/locale/nl/LC_MESSAGES/Irc.po
	plugins/Irc/locale/sv/LC_MESSAGES/Irc.po
	plugins/Irc/locale/tl/LC_MESSAGES/Irc.po
	plugins/Irc/locale/tr/LC_MESSAGES/Irc.po
	plugins/Irc/locale/uk/LC_MESSAGES/Irc.po
	plugins/Spotify/locale/Spotify.pot
	plugins/Spotify/locale/de/LC_MESSAGES/Spotify.po
	plugins/Spotify/locale/es/LC_MESSAGES/Spotify.po
	plugins/Spotify/locale/fr/LC_MESSAGES/Spotify.po
	plugins/Spotify/locale/gl/LC_MESSAGES/Spotify.po
	plugins/Spotify/locale/he/LC_MESSAGES/Spotify.po
	plugins/Spotify/locale/ia/LC_MESSAGES/Spotify.po
	plugins/Spotify/locale/mk/LC_MESSAGES/Spotify.po
	plugins/Spotify/locale/nl/LC_MESSAGES/Spotify.po
	plugins/Spotify/locale/sv/LC_MESSAGES/Spotify.po
	plugins/Spotify/locale/tl/LC_MESSAGES/Spotify.po
	plugins/TinyMCE/locale/TinyMCE.pot
	plugins/TinyMCE/locale/ca/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/de/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/eo/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/es/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/fr/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/gl/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/he/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/ia/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/id/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/ja/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/mk/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/ms/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/nb/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/nl/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/pt/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/pt_BR/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/ru/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/tl/LC_MESSAGES/TinyMCE.po
	plugins/TinyMCE/locale/uk/LC_MESSAGES/TinyMCE.po
	plugins/XCache/locale/XCache.pot
	plugins/XCache/locale/ast/LC_MESSAGES/XCache.po
	plugins/XCache/locale/br/LC_MESSAGES/XCache.po
	plugins/XCache/locale/de/LC_MESSAGES/XCache.po
	plugins/XCache/locale/es/LC_MESSAGES/XCache.po
	plugins/XCache/locale/eu/LC_MESSAGES/XCache.po
	plugins/XCache/locale/fi/LC_MESSAGES/XCache.po
	plugins/XCache/locale/fr/LC_MESSAGES/XCache.po
	plugins/XCache/locale/gl/LC_MESSAGES/XCache.po
	plugins/XCache/locale/he/LC_MESSAGES/XCache.po
	plugins/XCache/locale/ia/LC_MESSAGES/XCache.po
	plugins/XCache/locale/id/LC_MESSAGES/XCache.po
	plugins/XCache/locale/mk/LC_MESSAGES/XCache.po
	plugins/XCache/locale/ms/LC_MESSAGES/XCache.po
	plugins/XCache/locale/nb/LC_MESSAGES/XCache.po
	plugins/XCache/locale/nl/LC_MESSAGES/XCache.po
	plugins/XCache/locale/pl/LC_MESSAGES/XCache.po
	plugins/XCache/locale/pt/LC_MESSAGES/XCache.po
	plugins/XCache/locale/pt_BR/LC_MESSAGES/XCache.po
	plugins/XCache/locale/ru/LC_MESSAGES/XCache.po
	plugins/XCache/locale/tl/LC_MESSAGES/XCache.po
	plugins/XCache/locale/tr/LC_MESSAGES/XCache.po
	plugins/XCache/locale/uk/LC_MESSAGES/XCache.po
	plugins/YammerImport/locale/YammerImport.pot
	plugins/YammerImport/locale/br/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/de/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/es/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/eu/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/fr/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/gl/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/ia/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/mk/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/ms/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/nl/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/pl/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/ru/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/tl/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/tr/LC_MESSAGES/YammerImport.po
	plugins/YammerImport/locale/uk/LC_MESSAGES/YammerImport.po
2015-03-09 11:01:17 +01:00
Mikael Nordfeldth
5a3a3420e3 Merge commit 'refs/merge-requests/56' of https://gitorious.org/social/mainline into merge-requests/56 2015-03-09 10:39:27 +01:00
Mikael Nordfeldth
8ae1328e71 DB_DataObject won't save value 0 because it is "false"
A bug which we fix here and there by sending the string '0' instead.
We will be able to remove these hacks ("booleanintstring") when the
next release of DB_DataObject comes as I patched it for them.
2015-03-08 23:32:56 +01:00
Mikael Nordfeldth
6f61b4488e Make the OpenID settings page use more of HTMLOutputter functions 2015-03-08 23:31:35 +01:00
Mikael Nordfeldth
474c8bef0a Don't display labels by default in notice forms
There are either descriptive placeholders or informational text next to
the elements.
2015-03-08 23:30:54 +01:00
Mikael Nordfeldth
7aa1e02d06 Don't use a placeholder value on the nickname field
(because the placeholder would be 'Nickname', and it might confuse
someone who thinks they can use capital letters...)
2015-03-08 23:29:38 +01:00
Mikael Nordfeldth
b41c6824c5 Let text inputs have placeholder values. 2015-03-08 23:29:16 +01:00
Mikael Nordfeldth
05006f687a Move more POST handling into ManagedAction 2015-03-08 21:08:27 +01:00
Mikael Nordfeldth
1ea876296d A bunch of FormAction and ManagedAction synchronization 2015-03-08 20:41:42 +01:00
Mikael Nordfeldth
55894f02c7 TagProfileAction and OStatusPlugin now use less redundant code 2015-03-08 20:14:46 +01:00
Mikael Nordfeldth
32dacb9369 ROLLBACK queries in the proper order 2015-03-08 19:44:23 +01:00
Mikael Nordfeldth
d6423bbbc8 Sensitive-test _is_ done in index.php 2015-03-08 19:17:01 +01:00
Mikael Nordfeldth
1442ca16b4 Some reply buttons wouldn't work the first time they were clicked. 2015-03-08 12:54:08 +01:00
Mikael Nordfeldth
1a1b992858 RepliesAction migrated to extend ManagedAction 2015-03-08 12:38:15 +01:00
digital dreamer
a452a3b1a0 Snapshot of the Transifex translation project - February 2015 2015-03-08 09:34:38 +01:00
Mikael Nordfeldth
3c5383b11c All the AJAX calls should be made with $.ajax
because we're gonna want to expect application/xml or text/xml back.

There's a weird thing in Firefox where the call on line ~703 makes the
web developer console output "not well formed" which is for the returned
data. It is assumed to be text/xml - which it is - but the doctype says
"html" (because it is first <?xml..?> followed by <!DOCTYPE html ...->

This doesn't cause any malfunction right now, just the output in the
console. I'm not exactly sure how to fix it. Probably remove the DOCTYPE
output for AJAX calls, but I'm not sure that's the best way. Could this
maybe even be a browser/javascript/whatever bug? Because the Content-Type
from the server _is_ right...
2015-03-08 01:36:59 +01:00
Mikael Nordfeldth
77190c9a87 Need to run the NoticeFormSetup if forms are prerendered 2015-03-07 23:42:27 +01:00
Mikael Nordfeldth
75d45dace9 Don't put submit button in reply field edge-to-edge 2015-03-07 23:38:03 +01:00
Mikael Nordfeldth
b604630a74 Don't put reply in a popup, it was undesired for users
Maybe could make that optional or something in the DefaultLayout plugin...
2015-03-07 23:36:27 +01:00
Mikael Nordfeldth
9deff7afaf Email settings fix for the DB_DataObject bug for saving value 0 2015-03-07 21:57:43 +01:00
Mikael Nordfeldth
4e682f3d88 Reply popup works now
...WHY does javascript let me define the same value twice in the same
definition without warning about it? I hate Javascript. :(
2015-03-07 20:35:05 +01:00
Mikael Nordfeldth
196df7d8a4 Change the title of NewnoticeAction if replying 2015-03-07 20:34:46 +01:00
Mikael Nordfeldth
98b65763b0 input elements outside of label elements
HTML lets us put the input element inside the label and then they
are automatically paired, but this is more explicit and clear imho.
2015-03-07 20:11:13 +01:00
Mikael Nordfeldth
aefaef3ead jquery-ui updated to 1.11.3
Checked all checkboxes on http://jqueryui.com/download/
2015-03-07 14:14:19 +01:00
Mikael Nordfeldth
be15ab47dc jquery infieldlabel.js removed from script list 2015-03-07 12:32:24 +01:00
Mikael Nordfeldth
bc1d667784 jQuery cookie plugin updated
Source: https://plugins.jquery.com/cookie/
2015-03-07 01:00:07 +01:00
Mikael Nordfeldth
395c2cc075 JSON in JavaScript library updated
...why we now need a JSON library... _in_ Javascript?!
Source: http://www.json.org/js.html
2015-03-07 00:57:21 +01:00
Mikael Nordfeldth
a6c03e3127 jQuery Form Plugin updated
Source: http://malsup.com/jquery/form/
2015-03-07 00:55:17 +01:00
Mikael Nordfeldth
6080f6673e "infield labels" should be migrated to HTML5 placeholder 2015-03-07 00:49:05 +01:00
Mikael Nordfeldth
fab08278ea jQuery v2.1.3 seems to work fine 2015-03-07 00:48:43 +01:00
Mikael Nordfeldth
6b147175bb Start working a bit more consistently with "notice-options"
Let's get the AJAX response and present it to the user with the same
mechanisms for all actions, instead of having a bunch of javascript
for various circumstances (fave, repeat, delete should all be the same
behaviour for example).
2015-03-07 00:32:26 +01:00
Mikael Nordfeldth
503c05b749 No need for NoticeFormMaster anymore 2015-03-07 00:00:02 +01:00
Mikael Nordfeldth
1303943424 Choose whether to prerender the inline reply forms
The DefaultLayout plugin will come with options to render various parts
of the page by default or not. The expected default behaviour will try
to imitate the original StatusNet/GNU social looks as far as possible.
2015-03-06 23:52:25 +01:00
Mikael Nordfeldth
8cf0628f28 Replies to replies without reloading page now work 2015-03-06 23:47:03 +01:00
Mikael Nordfeldth
c48a9191c8 Hide the threaded-replies gray line. 2015-03-06 23:46:08 +01:00
Mikael Nordfeldth
38f977d2f3 Make sure we save 0 as '0' because of DB_DataObject bug 2015-03-06 23:21:57 +01:00
Mikael Nordfeldth
45861c6d9c Reply notice forms are functional again, but not AJAX 2015-03-06 21:20:46 +01:00
Mikael Nordfeldth
8abc2d3b8d Remove some more reply-placeholder stuff 2015-03-06 20:42:50 +01:00
Mikael Nordfeldth
0da7512597 No separate placeholder input element for replyforms
Also we're moving the rendering of these out into a separate plugin. Not
done yet, some javascript should be moved out as well.
2015-03-06 20:22:01 +01:00
Mikael Nordfeldth
13c331fa81 Make NoticeForm return the proper placeholder text
Also fixes some comments and uncertainties in the beginning of the
class definition.
2015-03-06 20:04:09 +01:00
Mikael Nordfeldth
62596d462b Get the values from the correct object. 2015-03-06 00:47:18 +01:00
Mikael Nordfeldth
f35ea45e09 Values of 0 would not be updated because of DB_DataObject
Upstream bug causing (int)0 to be interpreted as a "null string":
https://pear.php.net/bugs/bug.php?id=20291
2015-03-06 00:44:00 +01:00
Mikael Nordfeldth
72015d499f Actually show all images as <img /> tags.
Browsers should be secure enough by now for this.
2015-03-05 11:59:31 +01:00
Mikael Nordfeldth
3755faf558 SVG files can be used as thumbnails I guess 2015-03-05 11:59:11 +01:00
Mikael Nordfeldth
ff6969302b Some SVG files are "text/html" according to 'file' 2015-03-05 11:54:30 +01:00
Mikael Nordfeldth
d7fd507d99 ImageMagick sucks at vector graphics, so only use for rasterization 2015-03-05 11:53:52 +01:00
Mikael Nordfeldth
4c2bbf2354 ImageMagick plugin can now make thumbnails of SVG files 2015-03-04 13:50:20 +01:00
Mikael Nordfeldth
22dbeaef29 Used ->find(true) on wrong object 2015-03-04 13:46:51 +01:00
Mikael Nordfeldth
9720fd8250 Reset imgPath variable if VideoThumbnails failed 2015-03-04 13:45:42 +01:00
Mikael Nordfeldth
f6df1f1dd3 Documentation and more understandable code. 2015-03-04 13:29:18 +01:00
Mikael Nordfeldth
286b54e527 Use getUrl() on File and File_thumbnail instead of ->url 2015-03-04 13:13:20 +01:00
Mikael Nordfeldth
a4af51b5ba Move thumbnail algorithm mainly to ImageFile class 2015-03-04 13:12:42 +01:00
Mikael Nordfeldth
dcfb813066 Free ImageMagick object from memory when done with it 2015-03-04 12:18:44 +01:00
Mikael Nordfeldth
fe9dc8e901 ImageMagickPlugin refactored so we can more easily add new formats 2015-03-04 12:09:22 +01:00
Mikael Nordfeldth
284705eeb8 Subscription_queue::start does not mean Subscription exists 2015-03-04 11:49:54 +01:00
Mikael Nordfeldth
ec4e432d55 Subscription::ensureStart skips AlreadyFulfilledException
Sometimes we just want to accept the user's wrong, but when it comes
to remote APIs etc. we probably want to let the client know it has
done something already (in this case multiple identical subscription
requests - which might indicate to it that it should refresh the sub
lists or something).
2015-03-04 11:38:04 +01:00
Mikael Nordfeldth
8c933a6c06 FillImageFileMetadata event documented 2015-03-02 20:37:59 +01:00
Mikael Nordfeldth
325199b5e6 Bug tracker link updated. 2015-03-01 16:48:01 +01:00
Mikael Nordfeldth
80bf185ad5 Blog depended on TinyMCE which was unmaintained 2015-03-01 14:32:48 +01:00
Mikael Nordfeldth
19bf975e56 Aahhh, gotta run php -l before pushing stuff!!! 2015-03-01 12:47:48 +01:00
Mikael Nordfeldth
1227d2d710 Forgot to make syntax edits on last commit. 2015-03-01 12:45:58 +01:00
Mikael Nordfeldth
bece816ec7 User class throws exception on register failure 2015-03-01 12:36:19 +01:00
Mikael Nordfeldth
7fdf2f50f4 Allow changing nick on EmailRegistration
Nothing said that the EmailRegistration plugin was meant to restrict
the nickname to the local part of the email address. Let users change
it before they actually register the user.
2015-03-01 12:31:52 +01:00
Mikael Nordfeldth
746e658f3e Don't log every included config file
but let us retrieve the list by running GNUsocial::configFiles()
2015-02-28 13:04:17 +01:00
Mikael Nordfeldth
caa0dd549e Avatar table url field now 'text', maybe use filename as unique key? 2015-02-28 12:56:42 +01:00
Mikael Nordfeldth
49cf19ccfe INSTALL file overhaul, mostly done. 2015-02-27 16:06:05 +01:00
Mikael Nordfeldth
6abcc24834 htaccess.sample updating 2015-02-27 15:42:45 +01:00
Mikael Nordfeldth
fd53bb404f ssh key based easy upgrade method documentation 2015-02-27 15:07:44 +01:00
Mikael Nordfeldth
dceb67c247 Better UPGRADE documentation for GS->GS upgrade 2015-02-27 15:03:07 +01:00
Mikael Nordfeldth
b3e299b931 StatusNet 1.1.x to GNU social 1.2.x instructions updated 2015-02-27 14:47:54 +01:00
Mikael Nordfeldth
818e895af1 UPGRADE file had incorrect commands 2015-02-27 14:12:01 +01:00
Mikael Nordfeldth
7e65f4f6ea StatusNet to GNU social renaming in minor places 2015-02-27 12:55:25 +01:00
Mikael Nordfeldth
8fac7a9f6c StatusNet class renamed GNUsocial
also added backward compatible StatusNet class for the two calls I know
third party plugins use, isHTTPS and getActivePlugins
2015-02-27 12:44:15 +01:00
Mikael Nordfeldth
f25e5e3860 Start and EndActionExecute added/fixed/documented 2015-02-27 12:11:43 +01:00
Mikael Nordfeldth
1cc96cd334 1.2.x branch started (utf8mb4 support is reason enough) 2015-02-27 10:09:57 +01:00
Mikael Nordfeldth
4e26527447 Change branch to 1.2.x since we're utf8mb4 now 2015-02-27 10:04:29 +01:00
Mikael Nordfeldth
c214aa5149 Merge branch 'master' into nightly
Conflicts:
	lib/apiaction.php
2015-02-27 10:01:55 +01:00
Mikael Nordfeldth
48f8f3f5cf php5-intl is recommended but not required 2015-02-26 22:58:36 +01:00
Mikael Nordfeldth
0b09e4bfcb Normalize username on AuthCrypt login
Because users login with mixed casing and whatnot.
2015-02-26 00:45:17 +01:00
Mikael Nordfeldth
4eab52ee75 Attachment list items have more sane margins around them 2015-02-26 00:12:34 +01:00
Mikael Nordfeldth
b8e4f41987 Notice character count now adapts to resized textbox 2015-02-25 16:40:59 +01:00
Mikael Nordfeldth
f7553d73a2 501 Not Implemented on timelines for remote users. 2015-02-25 16:31:02 +01:00
Mikael Nordfeldth
b0e767eaf1 NoticeOptions above NoticeAttachments 2015-02-25 16:22:59 +01:00
Mikael Nordfeldth
82ef687730 Removing some plugins that were moved to plugins-unmaintained
See https://gitorious.org/social/plugins-unmaintained/
2015-02-25 15:47:14 +01:00
Mikael Nordfeldth
7e6ba94241 Documentation + filename uniqueness in File class 2015-02-25 15:19:27 +01:00
Mikael Nordfeldth
46a41f48ca Merge commit 'refs/merge-requests/55' of https://gitorious.org/social/mainline into merge-requests/55 2015-02-25 12:59:19 +01:00
Mikael Nordfeldth
86a8f4c5c7 Removed Lato font from themes where it was unnecessary 2015-02-25 12:56:41 +01:00
Mikael Nordfeldth
afbb3ec37a Merge commit 'refs/merge-requests/51' of https://gitorious.org/social/mainline into merge-requests/51 2015-02-25 12:52:35 +01:00
Mikael Nordfeldth
d0ef37a487 trailing whitespace 2015-02-25 12:50:56 +01:00
Mikael Nordfeldth
611e5ab421 Merge commit 'refs/merge-requests/49' of https://gitorious.org/social/mainline into merge-requests/49 2015-02-25 12:49:58 +01:00
Mikael Nordfeldth
38729133e2 HTML5 media <source> tags are short tags 2015-02-25 12:49:15 +01:00
Mikael Nordfeldth
ce0b221573 avconv required -f image2 not -f mjpeg at least on my server 2015-02-25 01:44:00 +01:00
Mikael Nordfeldth
95b61a5e12 Only local files to be passed through VideoThumbnails 2015-02-25 01:37:57 +01:00
Mikael Nordfeldth
3bbb748a08 VideoThumbnails changed to use 'exec' call to avconv 2015-02-25 01:36:14 +01:00
Mikael Nordfeldth
9a843548c0 Save thumbnails with proper extension
This might cause double extensions, but that's ok since the filename
is stored in the database. We might want to look at it later though.
2015-02-25 01:34:00 +01:00
Mikael Nordfeldth
220a13b87f mimetype added to ImageFile 2015-02-25 01:33:32 +01:00
Mikael Nordfeldth
4b8301d39e ImageFile constructor changed to align with existing code
Noone passed type, width or height to new ImageFile(...) and
in all instances, both parameters were set (though $id is null on occasion).
2015-02-25 01:31:44 +01:00
Mikael Nordfeldth
0854a84eb9 Throw more explicit exception in ImageFile 2015-02-25 01:14:27 +01:00
Mikael Nordfeldth
ce91f1c0e6 Hide posts from users with private_stream in scoping streams
For example the public timeline would show notices from a user with
private_stream configured. (previously it would only hide _new_ notices
by this user as they would be the only ones with notice scoping set).
2015-02-24 22:59:58 +01:00
Mikael Nordfeldth
060dbe1b56 Removing whitespace and creating an early-return in inScope 2015-02-24 22:44:43 +01:00
buttle
b9d4a9c9f9 added DocNav EVENT
Event to overide DocNav
two small css changes to indent notice footer correctly.
2015-02-24 21:51:31 +01:00
Mikael Nordfeldth
325e784ccd Don't store duplicates of files.
If a new file is uploaded, it will be matched with a previously uploaded
file so we don't have to store duplicates. SHA256 is random enough and
also unlikely enough to cause collisions.
2015-02-24 21:11:25 +01:00
Mikael Nordfeldth
0d577584c3 Script to delete file table entries which have disappeared locally 2015-02-24 19:29:28 +01:00
Mikael Nordfeldth
27bc654b5b RSSCloud URL field too long for utf8mb4
Should be the last one. If any URLs are longer than 191 chars, it might
require more manual interaction. Any instance with problems running
scripts/upgrade.php will get special treatment, help and a lollipop.
2015-02-21 16:43:21 +01:00
Mikael Nordfeldth
c1e3cfe7a7 ensureFeedURL will return Ostatus_profile early if already stored 2015-02-20 14:47:12 +01:00
Mikael Nordfeldth
96099807e1 Default install to site profile community 2015-02-20 01:00:28 +01:00
Mikael Nordfeldth
b639a25856 Emoji support through utf8mb4 seems to be done!
scripts/upgrade.php will take a _long_ time though! But it's worth it.
2015-02-19 22:13:42 +01:00
Mikael Nordfeldth
0e6c83e521 Had to avoid using indexes in the migration phase
File and File_redirection still had their indexes in the temporary migration table definition.
2015-02-19 22:06:43 +01:00
Mikael Nordfeldth
987232a0a6 Remove common_debug call that slipped by 2015-02-19 21:26:55 +01:00
Mikael Nordfeldth
b54710950f unique keys and indexes must be NOT NULL or MySQL fucks up
If this merge throws exception on scripts/upgrade.php and you recently
tried a nightly (i.e. during 2015-02-19) then just go back a commit or two
and try again.

Or delete the duplicate entries. Find the entries like this:

SELECT COUNT(*), urlhash FROM file_redirection
    GROUP BY urlhash
    HAVING COUNT(*) > 1;

then for each urlhash (or come up with a smart SQL query) do:
DELETE FROM file_redirection WHERE urlhash='hashfrompreviousquery' LIMIT 1;

You'll have to remove duplicates more than once if you have >2 identical
urlhash entries. LIMIT -1 might do that for you. I'm not sure.
2015-02-19 21:21:39 +01:00
Mikael Nordfeldth
d061e1065e Some loose table definitions which needed 191 char indexes
because we want utf8mb4 so badly!
2015-02-19 21:02:14 +01:00
Mikael Nordfeldth
0590f2975e Merge branch 'utf8mb4' into nightly
Conflicts because of urlhash fixes:
	classes/File.php
	classes/File_redirection.php
	classes/File_thumbnail.php
2015-02-19 20:50:40 +01:00
Mikael Nordfeldth
5471c65c9a less newlines for the scripts/upgrade.php output 2015-02-19 19:40:36 +01:00
Mikael Nordfeldth
e299583eee Bad check on existing urlhash key 2015-02-19 19:36:59 +01:00
Mikael Nordfeldth
8ac8e2e734 Use new ::getByUrl for File and File_redirection
and make use of the exceptions instead endless if statements
2015-02-19 19:29:55 +01:00
Mikael Nordfeldth
5b940f255f Mediafile updated to insert urlhash and lookup properly 2015-02-19 19:19:47 +01:00
Mikael Nordfeldth
c05e9b118c function is hashurl, variable is urlhash 2015-02-19 19:07:43 +01:00
Mikael Nordfeldth
45dc76de26 File and File_redirection adhoc storage methods updated for urlhash 2015-02-19 19:05:24 +01:00
Mikael Nordfeldth
0dfe39ac87 File_thumbnail url is not an index 2015-02-19 18:59:13 +01:00
Mikael Nordfeldth
27480d8e8e File_redirection also got urlhash column 2015-02-19 18:34:48 +01:00
Mikael Nordfeldth
176bde269f Merge branch 'nightly' into file_urlhash 2015-02-19 18:13:09 +01:00
Mikael Nordfeldth
df2cc09362 $this->scoped->getUser() instead of common_current_user(); 2015-02-19 17:46:37 +01:00
Mikael Nordfeldth
042cb1604a ROLLBACK if query failed in email settings savePreferences 2015-02-19 17:43:08 +01:00
Mikael Nordfeldth
0acf3e0e30 Profile table cleaning script. 2015-02-18 14:01:35 +01:00
Mikael Nordfeldth
4ad7e8f459 UserdirectoryAction now ManagedAction and better SQL 2015-02-18 13:37:06 +01:00
Mikael Nordfeldth
d445e0c877 No need for return true 2015-02-18 13:18:32 +01:00
Mikael Nordfeldth
e64ac4c418 GroupdirectoryAction now has no direct SQL queries
also various fixes uppers
2015-02-18 12:17:26 +01:00
Mikael Nordfeldth
94e19e1ac6 escapedTableName for SQL query easy access 2015-02-18 12:15:46 +01:00
Mikael Nordfeldth
a254f38a23 Turn GroupdirectoryAction into a ManagedAction 2015-02-18 11:15:30 +01:00
Mikael Nordfeldth
52e0ce8b06 Directory group search code cleanup 2015-02-18 11:13:10 +01:00
Mikael Nordfeldth
b039d960f5 Make group search in Directory use PEAR::DB
No more direct SQL queries please.
2015-02-18 11:08:11 +01:00
Mikael Nordfeldth
66df043c19 Add php5-intl dependency to Installer class 2015-02-18 00:47:00 +01:00
Mikael Nordfeldth
8fbdb4b9ac New dependency: php5-intl
Internationalization support is required for transliteration, which is
currently used when creating the slugs for hashtags and such. It is a
much more stable and efficient solution than having an unmaintainable
list of unciode characters in an array...
2015-02-18 00:28:45 +01:00
Mikael Nordfeldth
0deaf6c50c use common_purify to purify HTML, one function to rule them all 2015-02-18 00:14:28 +01:00
Mikael Nordfeldth
3dce6d9f6a Implement a common_purify for htmLawed and more
We're removing unicode formatting characters as well, such as RTL marks.
For more info on why we're because extra cautious (but may accept the
characters in later versions) you can read:
https://blog.malwarebytes.org/online-security/2014/01/the-rtlo-method/
2015-02-18 00:10:31 +01:00
Mikael Nordfeldth
9aa59c7f62 forgot primary key column to updateWithKeys in SalmonAction 2015-02-17 21:31:35 +01:00
Mikael Nordfeldth
7ba7f43199 Don't linkify bare domains by default
It's too farfetched to assume any text.com in a notice is an HTTP URL.
For example stuff like pasting from log entries, with domain.com:1234
where 1234 is a _PID_ or something, not a port number for http://...
2015-02-17 20:54:32 +01:00
Mikael Nordfeldth
c31c2d10b9 PHP>=5.4.0 lets us use Transliterator, tags now asciified!
For example: #REVOLUCIÓN becomes #revolucion instead of #revolución
2015-02-17 20:17:22 +01:00
Mikael Nordfeldth
5a8f24eb46 urlhash first steps, including scripts/upgrade.php 2015-02-17 18:55:12 +01:00
Mikael Nordfeldth
59763ceecb SalmonAction now updates remote URI if it was stale.
After doublechecking two identities so that they match (like one that was
previously http:// but now is https://) we update the URI in our database
to match.

This has to be verified so it's not easy to fool our script and thus make
us replace legitimate URIs with fake ones. I believe the callback method
is safe, but I'm not sure how well it handles HTTP MITM attacks etc.
2015-02-17 17:35:45 +01:00
Mikael Nordfeldth
282f4d6a89 Various $this->scoped fixes and protected prepare/handle in API actions 2015-02-17 17:20:00 +01:00
Mikael Nordfeldth
6cdedf6049 Replace $this->user/auth_user with $this->scoped in lib/apiaction.php
We prefer handling a Profile class rather than the User class, as some
functions might be useful for remote users as well, which cannot be
handled via the User class.
2015-02-17 17:16:33 +01:00
Mikael Nordfeldth
61aa71ed34 Subscription class gets exception throwing getSubscription function 2015-02-17 17:15:47 +01:00
Mikael Nordfeldth
901a825b61 Non-functional "retweeted to me" API call modified (but not fixed)
For some reason the "retweeted to me" part of the Twitter API was removed
when Evan made some inbox changes back in the StatusNet days. We might
recover this functionality, but not yet. The proper function calls are
however fixed in this commit.
2015-02-17 16:48:24 +01:00
Mikael Nordfeldth
75f35bcfe7 apiauth action with ->user changed to ->scoped 2015-02-17 16:39:27 +01:00
Mikael Nordfeldth
406b6148f5 CSS: notice images no wider than 100%
We should actually not allow remote images to be given in the src attribute
because they can be used for tracking and other nasty stuff without being
seen by the enduser.

Also, allowing remote images linked like this won't work for users who run
plugins like RequestPolicy etc. anyway. A better method would be to make
them listed as attachments instead. Then we can use that subsystem for
making thumbnails to store locally, hotlinking sources and whatnot.
2015-02-17 01:26:18 +01:00
Marcus Moeller
f7b41625e5 opacity is not needed as it's transparent
use alignment instead of padding for padlock symbol
2015-02-16 17:50:43 +01:00
Marcus Moeller
0168fddae5 updated font settings to match qvitter 2015-02-16 17:37:13 +01:00
Chimo
ef780d5034 Move people tag autocomplete JS to plugin
As discussed in https://gitorious.org/social/mainline/merge_requests/47
2015-02-15 19:10:05 -05:00
Mikael Nordfeldth
6862184956 Merge commit 'refs/merge-requests/47' of https://gitorious.org/social/mainline into merge-requests/47 2015-02-15 23:06:22 +01:00
Mikael Nordfeldth
2b181b40f7 Merge commit 'refs/merge-requests/48' of https://gitorious.org/social/mainline into merge-requests/48 2015-02-15 22:54:48 +01:00
Marcus Moeller
4c457c82ef removed text beside lock icon and fixed alignment 2015-02-15 22:47:34 +01:00
Chimo
3f8a519980 JS: Fixes jQueryUI autocomplete 'undefined' errors
data("autocomplete") was renamed to data("ui-autocomplete") starting
from v1.9:
http://jqueryui.com/upgrade-guide/1.9/#changed-naming-convention-for-data-keys
2015-02-15 16:11:23 -05:00
Chimo
fb03fc073a ApiTimelineList: Fixes ServerErrorAction
"No matches for action 'ApiTimelineList' with arguments 'format=atom
id=1'"

for 'api/:user/lists/:id/statuses.:format' URLs
2015-02-15 16:00:23 -05:00
Mikael Nordfeldth
2b93643277 Don't default to publishing http: alias!
It seems to have caused a problem with at least an older codebase of
remote GNU social sites, but either way we shouldn't present the user
as aliased on an insecure connection if there is no real reason to.
2015-02-15 13:33:36 +01:00
Mikael Nordfeldth
396f1e92ca Present http:// alias by default in WebFinger output
because it might help us (and especially StatusNet sites) to recognize
profiles that have migrated from HTTP to HTTPS!
2015-02-15 13:17:51 +01:00
Mikael Nordfeldth
c60b6bdb38 Wrong order of start/end events. My hobby OCD was disturbed. 2015-02-14 17:37:35 +01:00
Mikael Nordfeldth
9c83ddc122 Fixed some recently added EVENTS documentation 2015-02-14 17:35:34 +01:00
Mikael Nordfeldth
b6b9036821 StartSubMenu and EndSubMenu events 2015-02-14 17:32:35 +01:00
Mikael Nordfeldth
dc0b62f636 Merge commit 'refs/merge-requests/45' of https://gitorious.org/social/mainline into merge-requests/45 2015-02-14 16:45:04 +01:00
Mikael Nordfeldth
9102429a13 neo-quitter unuglification by marcus, merge-request 44 2015-02-14 16:42:44 +01:00
Marcus Moeller
1fcb7afd3a fixed alignment of textarea
fixed event view
2015-02-13 23:09:34 +01:00
Marcus Moeller
fe14c64e5e just make sure that input box and input box label are the same color 2015-02-13 18:09:43 +01:00
Marcus Moeller
107ca92458 use Genericons and fontawesome instead of images 2015-02-13 18:00:57 +01:00
buttle
9a8ccbaef2 Call HomeStubNav instead of duplicating code
adminpanelnav.php adds a homeStub but does not use the code created for the job.
2015-02-13 16:38:22 +01:00
buttle
d0347bb98f Removing home stub if empty
Added an Event HomeStubNavItems
menu->subMenu() returns false if empty
2015-02-13 16:26:41 +01:00
Marcus Moeller
3fcb79bc1e moved profile/group patch from core to profile_list 2015-02-12 23:07:49 +01:00
Marcus Moeller
41da1d6403 updated neo-quitter favicon to match the theme style 2015-02-12 22:55:01 +01:00
Marcus Moeller
41baba9ed3 lock icon style fixed to match quitter style 2015-02-12 22:26:34 +01:00
Mikael Nordfeldth
2f86cd8602 utf8mb4 conversion on database with index adjusts 2015-02-12 18:18:55 +01:00
Marcus Moeller
a1061c7145 fixed neo-quitter web view 2015-02-11 09:58:28 +01:00
buttle
2a0a0287d4 Added EVENT to homestubnav
Changed menu->submenu(). if (! $menu->getItems()) then do nothing
2015-02-10 19:20:01 +01:00
9106 changed files with 1080215 additions and 360654 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
avatar/* avatar/*
background/*
files/* files/*
file/* file/*
local/* local/*

View File

@@ -100,6 +100,10 @@ ssl: Whether to use SSL and https:// URLs for some or all pages.
(don't use it for any pages), or 'sometimes' (use it for (don't use it for any pages), or 'sometimes' (use it for
sensitive pages that include passwords like login and registration, sensitive pages that include passwords like login and registration,
but not for regular pages). Default to 'never'. but not for regular pages). Default to 'never'.
sslproxy: Whether to force GNUsocial to think it is HTTPS when the
server gives no such information. I.e. when you're using a reverse
proxy that adds the encryption layer but the webserver that runs PHP
isn't configured with a key and certificate.
sslserver: use an alternate server name for SSL URLs, like sslserver: use an alternate server name for SSL URLs, like
'secure.example.org'. You should be careful to set cookie 'secure.example.org'. You should be careful to set cookie
parameters correctly so that both the SSL server and the parameters correctly so that both the SSL server and the
@@ -563,6 +567,11 @@ sslserver: if specified, this server will be used when creating HTTPS
sslpath: if this and the sslserver are specified, this path will be used sslpath: if this and the sslserver are specified, this path will be used
when creating HTTPS URLs. Otherwise, the attachments|path value when creating HTTPS URLs. Otherwise, the attachments|path value
will be used. will be used.
show_thumbs: show thumbnails in notice lists for uploaded images, and photos
and videos linked remotely that provide oEmbed info. Defaults to true.
show_html: show (filtered) text/html attachments (and oEmbed HTML etc.).
Doesn't affect AJAX calls. Defaults to false.
filename_base: for new files, choose one: 'upload', 'hash'. Defaults to hash.
group group
----- -----
@@ -601,23 +610,6 @@ handle: boolean. Whether we should register our own PHP session-handling
debug: whether to output debugging info for session storage. Can help debug: whether to output debugging info for session storage. Can help
with weird session bugs, sometimes. Default false. with weird session bugs, sometimes. Default false.
background
----------
Users can upload backgrounds for their pages; this section defines
their use.
server: the server to use for background. Using a separate (even
virtual) server for this can speed up load times. Default is
null; same as site server.
dir: directory to write backgrounds too. Default is '/background/'
subdir of install dir.
path: path to backgrounds. Default is sub-path of install path; note
that you may need to change this if you change site-path too.
sslserver: SSL server to use when page is HTTPS-encrypted. If
unspecified, site ssl server and so on will be used.
sslpath: If sslserver if defined, path to use when page is HTTPS-encrypted.
ping ping
---- ----
@@ -627,20 +619,6 @@ notify third-party servers of updates.
notify: an array of URLs for ping endpoints. Default is the empty notify: an array of URLs for ping endpoints. Default is the empty
array (no notification). array (no notification).
design
------
Default design (colors and background) for the site. Actual appearance
depends on the theme. Null values mean to use the theme defaults.
backgroundcolor: Hex color of the site background.
contentcolor: Hex color of the content area background.
sidebarcolor: Hex color of the sidebar background.
textcolor: Hex color of all non-link text.
linkcolor: Hex color of all links.
backgroundimage: Image to use for the background.
disposition: Flags for whether or not to tile the background image.
notice notice
------ ------

View File

@@ -2,6 +2,13 @@ InitializePlugin: a chance to initialize a plugin in a complete environment
CleanupPlugin: a chance to cleanup a plugin at the end of a program CleanupPlugin: a chance to cleanup a plugin at the end of a program
StartActionExecute: Right before the "prepare" call of the current Action
- $action: the current Action object
- &$args: array of arguments, referenced so you can modify the array
EndActionExecute: Right after the "handle" call of the current Action
- $action: the current Action object
StartPrimaryNav: Showing the primary nav menu StartPrimaryNav: Showing the primary nav menu
- $action: the current action - $action: the current action
@@ -608,12 +615,12 @@ EndCheckPassword: After checking a username/password pair
- $authenticatedUser: User object if credentials match a user, else null. - $authenticatedUser: User object if credentials match a user, else null.
StartChangePassword: Before changing a password StartChangePassword: Before changing a password
- $user: user - Profile $target: The profile of the User that is changing password
- $oldpassword: the user's old password - $oldpassword: the user's old password
- $newpassword: the desired new password - $newpassword: the desired new password
EndChangePassword: After changing a password EndChangePassword: After changing a password
- $user: user - Profile $target: The profile of the User that just changed its password
StartHashPassword: Generate a hashed version of the password (like a salted crypt) StartHashPassword: Generate a hashed version of the password (like a salted crypt)
- &$hashed: Hashed version of the password, later put in the database - &$hashed: Hashed version of the password, later put in the database
@@ -1444,6 +1451,9 @@ StartResizeImageFile: Hook to resize an image and output it to a file. No matchi
- $outpath: string with output filepath - $outpath: string with output filepath
- $box: array with size ('width', 'height') and boundary box('x', 'y', 'w', 'h'). - $box: array with size ('width', 'height') and boundary box('x', 'y', 'w', 'h').
FillImageFileMetadata: Get more metadata about the ImageFile if it is perhaps not a real local file
- $imagefile ImageFile object which we're getting metadata for (such as animated status, width/height etc.)
StartShowAttachmentRepresentation: Attachment representation, full file (or in rare cases thumbnails/previews). StartShowAttachmentRepresentation: Attachment representation, full file (or in rare cases thumbnails/previews).
- $out: HTMLOutputter class to use for outputting HTML. - $out: HTMLOutputter class to use for outputting HTML.
- $file: 'File' object which we're going to show representation for. - $file: 'File' object which we're going to show representation for.
@@ -1463,3 +1473,27 @@ StartNotifyMentioned: During notice distribution, we send notifications (email,
EndNotifyMentioned: During notice distribution, we send notifications (email, im...) to the profiles who were somehow mentioned. EndNotifyMentioned: During notice distribution, we send notifications (email, im...) to the profiles who were somehow mentioned.
- $stored: Notice object that is being distributed. - $stored: Notice object that is being distributed.
- $mentioned_ids: Array of profile IDs (not just for local users) who got mentioned by the notice. - $mentioned_ids: Array of profile IDs (not just for local users) who got mentioned by the notice.
StartHomeStubNavItems: Go back Home nav items. Default includes just one item 'home'
- $out: HTMLOutputter used to output (usually an Action, but not always!)
- &$items: Referenced array of items in the nav (add if desired)
EndHomeStubNavItems:
- $out: HTMLOutputter used to output (usually an Action, but not always!)
- $items: array of menu items
StartSubMenu: Before outputting a submenu (including enclosing tags) to HTML
- $out: HTMLOutputter used to output (usually an Action, but not always!)
- $menu: The Menu object outputted as a submenu.
- $label: Localized text which represents the menu item.
EndSubMenu: After outputting a submenu (including enclosing tags) to HTML
- $out: HTMLOutputter used to output (usually an Action, but not always!)
- $menu: The Menu object outputted as a submenu.
- $label: Localized text which represents the menu item.
StartDocNav: Before outputting the docs Nav
- $nav: The DoclNav widget
EndDocNav: After outputting the docs Nav
- $nav: The DoclNav widget

260
INSTALL
View File

@@ -6,13 +6,16 @@ TABLE OF CONTENTS
* Installation * Installation
- Getting it up and running - Getting it up and running
- Fancy URLs - Fancy URLs
- Themes
- Private
* Extra features
- Sphinx - Sphinx
- SMS - SMS
- Queues and daemons
- Themes
- Translation - Translation
- Queues and daemons
* After installation
- Backups - Backups
- Private - Upgrading
Prerequisites Prerequisites
============= =============
@@ -23,7 +26,7 @@ PHP modules
The following software packages are *required* for this software to The following software packages are *required* for this software to
run correctly. run correctly.
- PHP 5.4+ For newer versions, some functions that are used may be - PHP 5.5+ For newer versions, some functions that are used may be
disabled by default, such as the pcntl_* family. See the disabled by default, such as the pcntl_* family. See the
section on 'Queues and daemons' for more information. section on 'Queues and daemons' for more information.
- MariaDB 5+ GNU Social uses, by default, a MariaDB server for data - MariaDB 5+ GNU Social uses, by default, a MariaDB server for data
@@ -41,9 +44,10 @@ functional setup of GNU Social:
- php5-curl Fetching files by HTTP. - php5-curl Fetching files by HTTP.
- php5-gd Image manipulation (scaling). - php5-gd Image manipulation (scaling).
- php5-gmp For Salmon signatures (part of OStatus). - php5-gmp For Salmon signatures (part of OStatus).
- php5-intl Internationalization support (transliteration et al).
- php5-json For WebFinger lookups and more. - php5-json For WebFinger lookups and more.
- php5-mysqlnd The native driver for PHP5 MariaDB connections. If you - php5-mysqlnd The native driver for PHP5 MariaDB connections. If you
use MySQL, 'mysql' or 'mysqli' may work. use MySQL, 'php5-mysql' or 'php5-mysqli' may be enough.
The above package names are for Debian based systems. In the case of The above package names are for Debian based systems. In the case of
Arch Linux, PHP is compiled with support for most extensions but they Arch Linux, PHP is compiled with support for most extensions but they
@@ -68,7 +72,7 @@ For some functionality, you will also need the following extensions:
You may also experience better performance from your site if you configure You may also experience better performance from your site if you configure
a PHP cache/accelerator. Most distributions come with "opcache" support. a PHP cache/accelerator. Most distributions come with "opcache" support.
Enable it in your php.ini, it is documented there together with its settings. Enable it in your php.ini where it is documented together with its settings.
Installation Installation
============ ============
@@ -105,11 +109,13 @@ especially if you've previously installed PHP/MariaDB packages.
will enable "Fancy URL" support, which you can read more about if you will enable "Fancy URL" support, which you can read more about if you
scroll down a bit in this document. scroll down a bit in this document.
3. Make your target directory writeable by the Web server. 3. Make your target directory writeable by the Web server, please note
however that 'a+w' will give _all_ users write access and securing the
webserver is not within the scope of this document.
chmod a+w /var/www/gnusocial/ chmod a+w /var/www/gnusocial/
On some systems, this will probably work: On some systems, this will work as a more secure alternative:
chgrp www-data /var/www/gnusocial/ chgrp www-data /var/www/gnusocial/
chmod g+w /var/www/gnusocial/ chmod g+w /var/www/gnusocial/
@@ -118,21 +124,20 @@ especially if you've previously installed PHP/MariaDB packages.
that user's default group instead. As a last resort, you can create that user's default group instead. As a last resort, you can create
a new group like "gnusocial" and add the Web server's user to the group. a new group like "gnusocial" and add the Web server's user to the group.
4. You should also take this moment to make your avatar, background, and 4. You should also take this moment to make your 'avatar' and 'file' sub-
file subdirectories writeable by the Web server. An insecure way to do directories writeable by the Web server. The _insecure_ way to do
this is: this is:
chmod a+w /var/www/gnusocial/avatar chmod a+w /var/www/gnusocial/avatar
chmod a+w /var/www/gnusocial/background
chmod a+w /var/www/gnusocial/file chmod a+w /var/www/gnusocial/file
You can also make the avatar, background, and file directories You can also make the avatar, and file directories just writable by
writeable by the Web server group, as noted above. the Web server group, as noted above.
5. Create a database to hold your site data. Something like this 5. Create a database to hold your site data. Something like this
should work: should work (you will be prompted for your database password):
mysqladmin -u "root" --password="rootpassword" create gnusocial mysqladmin -u "root" -p create social
Note that GNU Social should have its own database; you should not share Note that GNU Social should have its own database; you should not share
the database with another program. You can name it whatever you want, the database with another program. You can name it whatever you want,
@@ -146,17 +151,17 @@ especially if you've previously installed PHP/MariaDB packages.
database. If you have shell access, this will probably work from the database. If you have shell access, this will probably work from the
MariaDB shell: MariaDB shell:
GRANT ALL on gnusocial.* GRANT ALL on social.*
TO 'gnusocial'@'localhost' TO 'social'@'localhost'
IDENTIFIED BY 'agoodpassword'; IDENTIFIED BY 'agoodpassword';
You should change the user identifier 'gnusocial' and 'agoodpassword' You should change the user identifier 'social' and 'agoodpassword'
to your preferred new database username and password. You may want to to your preferred new database username and password. You may want to
test logging in to MariaDB as this new user. test logging in to MariaDB as this new user.
7. In a browser, navigate to the GNU Social install script; something like: 7. In a browser, navigate to the GNU Social install script; something like:
http://social.example.net/install.php https://social.example.net/install.php
Enter the database connection information and your site name. The Enter the database connection information and your site name. The
install program will configure your site and install the initial, install program will configure your site and install the initial,
@@ -170,55 +175,102 @@ Fancy URLs
---------- ----------
By default, GNU Social will use URLs that include the main PHP program's By default, GNU Social will use URLs that include the main PHP program's
name in them. For example, a user's home profile might be found at: name in them. For example, a user's home profile might be found at either
of these URLS depending on the webserver's configuration and capabilities:
http://example.net/gnusocial/index.php/gnusocial/fred https://social.example.net/index.php/fred
https://social.example.net/index.php?p=fred
On certain systems that don't support this kind of syntax, they'll It's possible to configure the software to use fancy URLs so it looks like
look like this: this instead:
http://example.net/gnusocial/index.php?p=gnusocial/fred https://social.example.net/fred
It's possible to configure the software so it looks like this instead:
http://example.net/gnusocial/fred
These "fancy URLs" are more readable and memorable for users. To use These "fancy URLs" are more readable and memorable for users. To use
fancy URLs, you must either have Apache 2.x with .htaccess enabled and fancy URLs, you must either have Apache 2.x with .htaccess enabled and
mod_rewrite enabled, -OR- know how to configure "url redirection" in mod_rewrite enabled, -OR- know how to configure "url redirection" in
your server (like lighttpd or nginx). your server (like lighttpd or nginx).
1. Copy the htaccess.sample file to .htaccess in your StatusNet 1. See the instructions for each respective webserver software:
directory. * For Apache, inspect the "htaccess.sample" file and save it as
".htaccess" after making any necessary modifications. Our sample
2. Change the "RewriteBase" in the new .htaccess file to be the URL path file is well commented.
to your GNU Social installation on your server. Typically this will * For lighttpd, inspect the lighttpd.conf.example file and apply the
be the path to your GNU Social directory relative to your Web root. appropriate changes in your virtualhost configuration for lighttpd.
If you are installing it in the root directory, leave it as '/'. * For nginx, inspect the nginx.conf.sample file and apply the appropriate
changes.
3. Add, uncomment or change a line in your config.php file so it says: * For other webservers, we gladly accept contributions of
server configuration examples.
2. Assuming your webserver is properly configured and have its settings
applied (remember to reload/restart it), you can add this to your
GNU social's config.php file:
$config['site']['fancy'] = true; $config['site']['fancy'] = true;
You should now be able to navigate to a "fancy" URL on your server, You should now be able to navigate to a "fancy" URL on your server,
like: like:
http://example.net/gnusocial/main/register https://social.example.net/main/register
If you changed your HTTP server configuration, you may need to restart Themes
the server first. ------
If it doesn't work, double-check that AllowOverride for the GNU Social As of right now, your ability change the theme is limited to CSS
directory is 'All' in your Apache configuration file. This is usually stylesheets and some image files; you can't change the HTML output,
/etc/httpd.conf, /etc/apache/httpd.conf, or (on Debian and Ubuntu) like adding or removing menu items, without the help of a plugin.
/etc/apache2/sites-available/default. See the Apache documentation for
.htaccess files for more details:
http://httpd.apache.org/docs/2.2/howto/htaccess.html You can choose a theme using the $config['site']['theme'] element in
the config.php file. See below for details.
Also, check that mod_rewrite is installed and enabled: You can add your own theme by making a sub-directory of the 'theme'
subdirectory with the name of your theme. Each theme can have the
following files:
http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html display.css: a CSS2 file for "default" styling for all browsers.
logo.png: a logo image for the site.
default-avatar-profile.png: a 96x96 pixel image to use as the avatar for
users who don't upload their own.
default-avatar-stream.png: Ditto, but 48x48. For streams of notices.
default-avatar-mini.png: Ditto ditto, but 24x24. For subscriptions
listing on profile pages.
You may want to start by copying the files from the default theme to
your own directory.
Private
-------
A GNU social node can be configured as "private", which means it will not
federate with other nodes in the network. It is not a recommended method
of using GNU social and we cannot at the current state of development
guarantee that there are no leaks (what a public network sees as features,
private sites will likely see as bugs).
Private nodes are however an easy way to easily setup collaboration and
image sharing within a workgroup or a smaller community where federation
is not a desired feature. Also, it is possible to change this setting and
instantly gain full federation features.
Access to file attachments can also be restricted to logged-in users only:
1. Add a directory outside the web root where your file uploads will be
stored. Use this command as an initial guideline to create it:
mkdir /var/www/gnusocial-files
2. Make the file uploads directory writeable by the web server. An
insecure way to do this is (to do it properly, read up on UNIX file
permissions and configure your webserver accordingly):
chmod a+x /var/www/gnusocial-files
3. Tell GNU social to use this directory for file uploads. Add a line
like this to your config.php:
$config['attachments']['dir'] = '/var/www/gnusocial-files';
Extra features
==============
Sphinx Sphinx
------ ------
@@ -283,7 +335,21 @@ For this to work, there *must* be a domain or sub-domain for which all
$config['mail']['domain'] = 'yourdomain.example.net'; $config['mail']['domain'] = 'yourdomain.example.net';
Translations
------------
For info on helping with translations, see the platform currently in use
for translations: https://www.transifex.com/projects/p/gnu-social/
Translations use the gettext system <http://www.gnu.org/software/gettext/>.
If you for some reason do not wish to sign up to the Transifex service,
you can review the files in the "locale/" sub-directory of GNU social.
Each plugin also has its own translation files.
To get your own site to use all the translated languages, and you are
tracking the git repo, you will need to install at least 'gettext' on
your system and then run:
$ make translations
Queues and daemons Queues and daemons
------------------ ------------------
@@ -300,12 +366,12 @@ Two mechanisms are available to achieve offline operations:
### OpportunisticQM plugin ### OpportunisticQM plugin
This plugin is enabled by default. It tries its best to do background This plugin is enabled by default. It tries its best to do background
job during regular HTTP requests, like API or HTML pages calls. jobs during regular HTTP requests, like API or HTML pages calls.
Since queueing system is enabled by default, notices to be broadcasted Since queueing system is enabled by default, notices to be broadcasted
will be stored, by default, into DB (table queue_item). will be stored, by default, into DB (table queue_item).
Each time it can, OpportunisticQM will try to handle some of them. Whenever it has time, OpportunisticQM will try to handle some of them.
This is a good solution whether you: This is a good solution whether you:
@@ -345,16 +411,13 @@ separate server is probably a good idea for high-volume sites.
.htaccess file, but make sure that your config.php file is close .htaccess file, but make sure that your config.php file is close
to, or identical to, your Web server's version. to, or identical to, your Web server's version.
3. In your config.php files (both the Web server and the queues 3. In your config.php files (on the server where you run the queue
server!), set the following variable: daemon), set the following variable:
$config['queue']['enabled'] = true;
$config['queue']['daemon'] = true; $config['queue']['daemon'] = true;
You may also want to look at the 'daemon' section of this file for You may also want to look at the 'Queues and Daemons' section in
more daemon options. Note that if you set the 'user' and/or 'group' this file for more background processing options.
options, you'll need to create that user and/or group by hand.
They're not created automatically.
4. On the queues server, run the command scripts/startdaemons.sh. 4. On the queues server, run the command scripts/startdaemons.sh.
@@ -384,85 +447,20 @@ It is also possible to use a STOMP server instead of our kind of hacky
home-grown DB-based queue solution. This is strongly recommended for home-grown DB-based queue solution. This is strongly recommended for
best response time, especially when using XMPP. best response time, especially when using XMPP.
Themes After installation
------ ==================
Older themes (version 0.9.x and below) no longer work with StatusNet
1.0.x, due to major changes in the site layout. We ship with three new
themes for this version, 'neo', 'neo-blue' and 'neo-light'.
As of right now, your ability to change the theme is site-wide; users
can't choose their own theme. Additionally, the only thing you can
change in the theme is CSS stylesheets and some image files; you can't
change the HTML output, like adding or removing menu items.
You can choose a theme using the $config['site']['theme'] element in
the config.php file. See below for details.
You can add your own theme by making a sub-directory of the 'theme'
subdirectory with the name of your theme. Each theme can have the
following files:
display.css: a CSS2 file for "default" styling for all browsers.
logo.png: a logo image for the site.
default-avatar-profile.png: a 96x96 pixel image to use as the avatar for
users who don't upload their own.
default-avatar-stream.png: Ditto, but 48x48. For streams of notices.
default-avatar-mini.png: Ditto ditto, but 24x24. For subscriptions
listing on profile pages.
You may want to start by copying the files from the default theme to
your own directory.
Translation
-----------
Translations in StatusNet use the gettext system <http://www.gnu.org/software/gettext/>.
Theoretically, you can add your own sub-directory to the locale/
subdirectory to add a new language to your system. You'll need to
compile the ".po" files into ".mo" files, however.
Contributions of translation information to StatusNet are very easy:
you can use the Web interface at translatewiki.net to add one
or a few or lots of new translations -- or even new languages. You can
also download more up-to-date .po files there, if you so desire.
For info on helping with translations, see http://status.net/wiki/Translations
Backups Backups
------- -------
There is no built-in system for doing backups in StatusNet. You can make There is no built-in system for doing backups in GNU social. You can make
backups of a working StatusNet system by backing up the database and backups of a working StatusNet system by backing up the database and
the Web directory. To backup the database use mysqldump <http://ur1.ca/7xo> the Web directory. To backup the database use mysqldump <https://mariadb.com/kb/en/mariadb/mysqldump/>
and to backup the Web directory, try tar. and to backup the Web directory, try tar.
Private Upgrading
------- ---------
The administrator can set the "private" flag for a site so that it's Upgrading is strongly recommended to stay up to date with security fixes
not visible to non-logged-in users. (This is the default for new installs of version 1.0!) and new features. For instructions on how to upgrade GNU social code,
please see the UPGRADE file.
This might be useful for workgroups who want to share a social
networking site for project management, but host it on a public
server.
Total privacy is attempted but not guaranteed or ensured. Private sites
currently don't work well with OStatus federation.
Access to file attachments can also be restricted to logged-in users only.
1. Add a directory outside the web root where your file uploads will be
stored. Usually a command like this will work:
mkdir /var/www/statusnet-files
2. Make the file uploads directory writeable by the web server. An
insecure way to do this is:
chmod a+x /var/www/statusnet-files
3. Tell StatusNet to use this directory for file uploads. Add a line
like this to your config.php:
$config['attachments']['dir'] = '/var/www/statusnet-files';

View File

@@ -1,7 +1,7 @@
Plugins Plugins
======= =======
Beginning with the 0.7.x branch, StatusNet has supported a simple but GNU social supports a simple but
powerful plugin architecture. Important events in the code are named, powerful plugin architecture. Important events in the code are named,
like 'StartNoticeSave', and other software can register interest like 'StartNoticeSave', and other software can register interest
in those events. When the events happen, the other software is called in those events. When the events happen, the other software is called
@@ -37,7 +37,7 @@ can enable a plugin with the following line in config.php:
This will look for and load files named 'ExamplePlugin.php' or This will look for and load files named 'ExamplePlugin.php' or
'Example/ExamplePlugin.php' either in the plugins/ directory (for 'Example/ExamplePlugin.php' either in the plugins/ directory (for
plugins that ship with StatusNet) or in the local/ directory (for plugins that ship with GNU social) or in the local/ directory (for
plugins you write yourself or that you get from somewhere else) or plugins you write yourself or that you get from somewhere else) or
local/plugins/. local/plugins/.

View File

@@ -1,5 +1,5 @@
# GNU social 1.1.3 # GNU social 1.2.x
February 2015-02-27 2015
(c) Free Software Foundation, Inc (c) Free Software Foundation, Inc
(c) StatusNet, Inc (c) StatusNet, Inc
@@ -100,15 +100,19 @@ for additional terms.
## New this version ## New this version
This is a security fix and bug fix release since 1.1.3-beta2. This is the development branch for the 1.2.x version of GNU social.
All 1.1.x sites should upgrade to this version. All daring 1.1.x admins should upgrade to this version.
So far it includes the following changes: So far it includes the following changes:
- Backing up a user's account is more and more complete.
- Emojis 😸 (utf8mb4 support)
The last release, 1.1.3, gave us these improvements:
- XSS security fix (thanks Simon Waters, <https://www.surevine.com/>) - XSS security fix (thanks Simon Waters, <https://www.surevine.com/>)
- Many improvements to ease adoption of the Qvitter front-end <https://github.com/hannesmannerheim/qvitter> - Many improvements to ease adoption of the Qvitter front-end <https://github.com/hannesmannerheim/qvitter>
- Protocol adaptions for improved performance and stability - Protocol adaptions for improved performance and stability
- Backing up a user's account now appears to work as it should
Upgrades from _StatusNet_ 1.1.1 will also experience these improvements: Upgrades from _StatusNet_ 1.1.1 will also experience these improvements:
@@ -140,13 +144,13 @@ to install the development version of GNU social.
To get it, use the git version control tool To get it, use the git version control tool
<http://git-scm.com/> like so: <http://git-scm.com/> like so:
git clone git@gitorious.org:social/mainline.git git clone git@git.gnu.io:gnu/gnu-social.git
In the current phase of development it is probably In the current phase of development it is probably
recommended to use git as a means to stay up to date recommended to use git as a means to stay up to date
with the source code. You can choose between these with the source code. You can choose between these
branches: branches:
- 1.1.x "stable", few updates, well tested code - 1.2.x "stable", few updates, well tested code
- master "testing", more updates, usually working well - master "testing", more updates, usually working well
- nightly "unstable", most updates, not always working - nightly "unstable", most updates, not always working
@@ -162,8 +166,8 @@ There are several ways to get more information about GNU social.
* Following us on GNU social -- <https://quitter.se/gnusocial> * Following us on GNU social -- <https://quitter.se/gnusocial>
* GNU social has a bug tracker for any defects you may find, or ideas for * GNU social has a bug tracker for any defects you may find, or ideas for
making things better. <https://bugz.foocorp.net/> making things better. <https://git.gnu.io/gnu/gnu-social/issues/>
* Patches are welcome, preferrably to our repository on Gitorious. <https://gitorious.org/social/mainline> * Patches are welcome, preferrably to our repository on git.gnu.io. <https://git.gnu.io/gnu/gnu-social>
Credits Credits
======= =======

144
UPGRADE
View File

@@ -1,99 +1,97 @@
Upgrading Upgrading
========= =========
StatusNet 1.1.1 to GNU social GNU social 1.1.x to GNU social 1.2.x
----------------------------- ------------------------------------
If you are tracking the GNU social git repository, we currently recommend
using the "master" branch (or nightly if you want to use latest features)
and follow this procedure:
0. Backup your data. The StatusNet upgrade discussions below have some
guidelines to back up the database and files (mysqldump and rsync).
1. Stop your queue daemons (you can run this command even if you do not
use the queue daemons):
$ bash scripts/stopdaemons.sh
2. Run the command to fetch the latest sourcecode:
$ git pull
If you are not using git we recommend following the instructions below
for upgrading "StatusNet 1.1.x to GNU social 1.2.x" as they are similar.
3. Run the upgrade script:
$ php scripts/upgrade.php
The upgrade script will likely take a long time because it will
upgrade the tables to another character encoding and make other
automated upgrades. Make sure it ends without errors. If you get
errors, create a new task on https://git.gnu.io/gnu/gnu-social/issues
4. Start your queue daemons again (you can run this command even if you
do not use the queue daemons):
$ bash scripts/startdaemons.sh
5. Report any issues at https://git.gnu.io/gnu/gnu-social/issues
If you are using ssh keys to log in to your server, you can make this
procedure pretty painless (assuming you have automated backups already).
Make sure you "cd" into the correct directory (in this case "htdocs")
and use the correct login@hostname combo:
$ ssh social@domain.example 'cd htdocs
&& bash scripts/stopdaemons.sh
&& git pull
&& time php scripts/upgrade.php
&& bash scripts/startdaemons.sh'
StatusNet 1.1.x to GNU social 1.2.x
-----------------------------------
We cannot support migrating from any other version of StatusNet than We cannot support migrating from any other version of StatusNet than
1.1.1. If you are running a StatusNet version lower than this, please 1.1.1. If you are running a StatusNet version lower than this, please
follow the upgrade procedures for each respective StatusNet version. follow the upgrade procedures for each respective StatusNet version.
You are now running StatusNet 1.1.1 and want to migrate to GNU social. You are now running StatusNet 1.1.1 and want to migrate to GNU social
Beware there may be changes in minimum required version of PHP and the 1.2.x. Beware there may be changes in minimum required version of PHP
modules used, so double-check the INSTALL file's requirements list. and the modules required, so review the INSTALL file (php5-intl is a
newly added dependency for example).
Before you begin: Make backups. Always make backups. Of your entire * Before you begin: Make backups. Always make backups. Of your entire
directory structure and the database too. All tables. All data. Alles. directory structure and the database too. All tables. All data. Alles.
0. Stop your queue daemons 'php scripts/stopdaemon.php' should do it. 0. Make a backup of everything. To backup the database, you can use a
variant of this command (you will be prompted for the database password):
$ mysqldump -u dbuser -p dbname > social-backup.sql
1. Stop your queue daemons 'bash scripts/stopdaemons.sh' should do it.
Not everyone runs queue daemons, but the above command won't hurt. Not everyone runs queue daemons, but the above command won't hurt.
1. Unpack your GNU social code to a fresh directory. 2. Unpack your GNU social code to a fresh directory. You can do this
by cloning our git repository:
$ git clone https://git.gnu.io/gnu/gnu-social.git gnusocial
2. Synchronize your local files to the GNU social directory. These 3. Synchronize your local files to the GNU social directory. These
will be the local files such as avatars, config and files: will be the local files such as avatars, config and files:
avatar/* avatar/*
background/*
file/* file/*
local/* local/*
.htaccess .htaccess
config.php config.php
3. Replace your old StatusNet directory with the new GNU social This command will point you in the right direction on how to do it:
$ rsync -avP statusnet/{.htaccess,avatar,file,local,config.php} gnusocial/
4. Replace your old StatusNet directory with the new GNU social
directory in your webserver root. directory in your webserver root.
4. Run the upgrade script: 'php scripts/upgrade.php' 5. Run the upgrade script: 'php scripts/upgrade.php'
The upgrade script will likely take a long time because it will
upgrade the tables to another character encoding and make other
automated upgrades. Make sure it ends without errors. If you get
errors, create a new task on https://git.gnu.io/gnu/gnu-social/issues
5. Start your queue daemons: 'php scripts/startdaemons.php' 6. Start your queue daemons: 'bash scripts/startdaemons.sh'
6. Report any issues at https://bugz.foocorp.net/ (tag GNU social) 7. Report any issues at https://git.gnu.io/gnu/gnu-social/issues
Legacy StatusNet instructions
-----------------------------
These instructions are here for historical and perhaps informational
purposes.
If you've been using StatusNet 1.0 or lower, or if you've
been tracking the "git" version of the software, you will probably
want to upgrade and keep your existing data. Try these step-by-step
instructions; read to the end first before trying them.
0. Download StatusNet and set up all the prerequisites as if you were
doing a new install.
1. Make backups of both your database and your Web directory. UNDER NO
CIRCUMSTANCES should you try to do an upgrade without a known-good
backup. You have been warned.
2. Shut down Web access to your site, either by turning off your Web
server or by redirecting all pages to a "sorry, under maintenance"
page.
3. Shut down XMPP access to your site, typically by shutting down the
xmppdaemon.php process and all other daemons that you're running.
If you've got "monit" or "cron" automatically restarting your
daemons, make sure to turn that off, too.
4. Shut down SMS and email access to your site. The easy way to do
this is to comment out the line piping incoming email to your
maildaemon.php file, and running something like "newaliases".
5. Once all writing processes to your site are turned off, make a
final backup of the Web directory and database.
6. Move your StatusNet directory to a backup spot, like "statusnet.bak".
7. Unpack your StatusNet 1.1.1 tarball and move it to "statusnet" or
wherever your code used to be.
8. Copy the config.php file and the contents of the avatar/, background/,
file/, and local/ subdirectories from your old directory to your new
directory.
9. Copy htaccess.sample to .htaccess in the new directory. Change the
RewriteBase to use the correct path.
10. Upgrade the database.
NOTE: this step is destructive and cannot be
reversed. YOU CAN EASILY DESTROY YOUR SITE WITH THIS STEP. Don't
do it without a known-good backup!
In your new StatusNet 1.1.1 directory and AFTER YOU MAKE A
BACKUP run the upgrade.php script like this:
php ./scripts/upgrade.php
11. Use mysql or psql client to log into your database and make sure that
the notice, user, profile, subscription etc. tables are non-empty.
12. Turn back on the Web server, and check that things still work.
13. Turn back on XMPP bots and email maildaemon.
NOTE: the 1.0.0 version of StatusNet changed the URLs for all admin
panels from /admin/* to /panel/*. This now allows the (popular)
username 'admin', but blocks the considerably less popular username
'panel'. If you have an existing user named 'panel', you should rename
them before upgrading.

View File

@@ -35,38 +35,24 @@
* @link http://status.net * @link http://status.net
*/ */
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
class AllAction extends ProfileAction class AllAction extends ShowstreamAction
{ {
var $notice; public function getStream()
protected function prepare(array $args=array())
{ {
parent::prepare($args); if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
$user = common_current_user();
if (!empty($user) && $user->streamModeOnly()) {
$stream = new InboxNoticeStream($this->target, $this->scoped); $stream = new InboxNoticeStream($this->target, $this->scoped);
} else { } else {
$stream = new ThreadingInboxNoticeStream($this->target, $this->scoped); $stream = new ThreadingInboxNoticeStream($this->target, $this->scoped);
} }
$this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE, return $stream;
NOTICES_PER_PAGE + 1);
if ($this->page > 1 && $this->notice->N == 0) {
// TRANS: Client error when page not found (404).
$this->clientError(_('No such page.'), 404);
}
return true;
} }
function title() function title()
{ {
if (!empty($this->scoped) && $this->scoped->id == $this->target->id) { if (!empty($this->scoped) && $this->scoped->sameAs($this->target)) {
// TRANS: Title of a user's own start page. // TRANS: Title of a user's own start page.
return _('Home timeline'); return _('Home timeline');
} else { } else {
@@ -83,44 +69,44 @@ class AllAction extends ProfileAction
common_local_url( common_local_url(
'ApiTimelineFriends', array( 'ApiTimelineFriends', array(
'format' => 'as', 'format' => 'as',
'id' => $this->target->nickname 'id' => $this->target->getNickname()
) )
), ),
// TRANS: %s is user nickname. // TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->target->nickname)), sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->target->getNickname())),
new Feed(Feed::RSS1, new Feed(Feed::RSS1,
common_local_url( common_local_url(
'allrss', array( 'allrss', array(
'nickname' => 'nickname' =>
$this->target->nickname) $this->target->getNickname())
), ),
// TRANS: %s is user nickname. // TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->target->nickname)), sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->target->getNickname())),
new Feed(Feed::RSS2, new Feed(Feed::RSS2,
common_local_url( common_local_url(
'ApiTimelineFriends', array( 'ApiTimelineFriends', array(
'format' => 'rss', 'format' => 'rss',
'id' => $this->target->nickname 'id' => $this->target->getNickname()
) )
), ),
// TRANS: %s is user nickname. // TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->target->nickname)), sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->target->getNickname())),
new Feed(Feed::ATOM, new Feed(Feed::ATOM,
common_local_url( common_local_url(
'ApiTimelineFriends', array( 'ApiTimelineFriends', array(
'format' => 'atom', 'format' => 'atom',
'id' => $this->target->nickname 'id' => $this->target->getNickname()
) )
), ),
// TRANS: %s is user nickname. // TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (Atom)'), $this->target->nickname)) sprintf(_('Feed for friends of %s (Atom)'), $this->target->getNickname()))
); );
} }
function showEmptyListMessage() function showEmptyListMessage()
{ {
// TRANS: Empty list message. %s is a user nickname. // TRANS: Empty list message. %s is a user nickname.
$message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->target->nickname) . ' '; $message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->target->getNickname()) . ' ';
if (common_logged_in()) { if (common_logged_in()) {
if ($this->target->id === $this->scoped->id) { if ($this->target->id === $this->scoped->id) {
@@ -130,12 +116,12 @@ class AllAction extends ProfileAction
} else { } else {
// TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@". // TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@".
// TRANS: This message contains Markdown links. Keep "](" together. // TRANS: This message contains Markdown links. Keep "](" together.
$message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from their profile or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->target->nickname, $this->target->nickname, '@' . $this->target->nickname); $message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from their profile or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->target->getNickname(), $this->target->getNickname(), '@' . $this->target->getNickname());
} }
} else { } else {
// TRANS: Encouragement displayed on empty timeline user pages for anonymous users. // TRANS: Encouragement displayed on empty timeline user pages for anonymous users.
// TRANS: %s is a user nickname. This message contains Markdown links. Keep "](" together. // TRANS: %s is a user nickname. This message contains Markdown links. Keep "](" together.
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->nickname); $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->getNickname());
} }
$this->elementStart('div', 'guide'); $this->elementStart('div', 'guide');
@@ -146,19 +132,10 @@ class AllAction extends ProfileAction
function showContent() function showContent()
{ {
if (Event::handle('StartShowAllContent', array($this))) { if (Event::handle('StartShowAllContent', array($this))) {
if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
$profile = null;
$current_user = common_current_user();
if (!empty($current_user)) {
$profile = $current_user->getProfile();
}
if (!empty($current_user) && $current_user->streamModeOnly()) {
$nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE)); $nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
} else { } else {
$nl = new ThreadedNoticeList($this->notice, $this, $profile); $nl = new ThreadedNoticeList($this->notice, $this, $this->scoped);
} }
$cnt = $nl->show(); $cnt = $nl->show();
@@ -169,7 +146,7 @@ class AllAction extends ProfileAction
$this->pagination( $this->pagination(
$this->page > 1, $cnt > NOTICES_PER_PAGE, $this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'all', array('nickname' => $this->target->nickname) $this->page, 'all', array('nickname' => $this->target->getNickname())
); );
Event::handle('EndShowAllContent', array($this)); Event::handle('EndShowAllContent', array($this));

View File

@@ -28,11 +28,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/rssaction.php';
/** /**
* RSS feed for user and friends timeline. * RSS feed for user and friends timeline.
@@ -46,52 +42,12 @@ require_once INSTALLDIR.'/lib/rssaction.php';
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/ * @link http://status.net/
*/ */
class AllrssAction extends Rss10Action class AllrssAction extends TargetedRss10Action
{ {
var $user = null; protected function getNotices()
/**
* Initialization.
*
* @param array $args Web and URL arguments
*
* @return boolean false if user doesn't exist
*
*/
function prepare($args)
{ {
parent::prepare($args); $stream = new InboxNoticeStream($this->target);
$nickname = $this->trimmed('nickname'); return $stream->getNotices(0, $this->limit)->fetchAll();
$this->user = User::getKV('nickname', $nickname);
if (!$this->user) {
// TRANS: Client error when user not found for an rss related action.
$this->clientError(_('No such user.'));
} 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)
{
$stream = new InboxNoticeStream($this->user->getProfile());
$notice = $stream->getNotices(0, $limit, null, null);
$notices = array();
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
} }
/** /**
@@ -101,33 +57,17 @@ class AllrssAction extends Rss10Action
*/ */
function getChannel() function getChannel()
{ {
$user = $this->user;
$c = array('url' => common_local_url('allrss', $c = array('url' => common_local_url('allrss',
array('nickname' => array('nickname' =>
$user->nickname)), $this->target->getNickname())),
// TRANS: Message is used as link title. %s is a user nickname. // TRANS: Message is used as link title. %s is a user nickname.
'title' => sprintf(_('%s and friends'), $user->nickname), 'title' => sprintf(_('%s and friends'), $this->target->getNickname()),
'link' => common_local_url('all', 'link' => common_local_url('all',
array('nickname' => array('nickname' =>
$user->nickname)), $this->target->getNickname())),
// TRANS: Message is used as link description. %1$s is a username, %2$s is a site name. // TRANS: Message is used as link description. %1$s is a username, %2$s is a site name.
'description' => sprintf(_('Updates from %1$s and friends on %2$s!'), 'description' => sprintf(_('Updates from %1$s and friends on %2$s!'),
$user->nickname, common_config('site', 'name'))); $this->target->getNickname(), common_config('site', 'name')));
return $c; return $c;
} }
/**
* Get image.
*
* @return string user avatar URL or null
*/
function getImage()
{
$user = $this->user;
$profile = $user->getProfile();
if (!$profile) {
return null;
}
return $profile->avatarUrl(AVATAR_PROFILE_SIZE);
}
} }

View File

@@ -31,9 +31,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* We don't have a rate limit, but some clients check this method. * We don't have a rate limit, but some clients check this method.
@@ -58,9 +56,9 @@ class ApiAccountRateLimitStatusAction extends ApiBareAuthAction
* *
* @return void * @return void
*/ */
function handle($args) protected function handle()
{ {
parent::handle($args); parent::handle();
if (!in_array($this->format, array('xml', 'json'))) { if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError( $this->clientError(
@@ -69,7 +67,6 @@ class ApiAccountRateLimitStatusAction extends ApiBareAuthAction
404, 404,
$this->format $this->format
); );
return;
} }
$reset = new DateTime(); $reset = new DateTime();

View File

@@ -1,5 +1,4 @@
<?php <?php
/** /**
* StatusNet, the distributed open-source microblogging tool * StatusNet, the distributed open-source microblogging tool
* *
@@ -132,9 +131,6 @@ class ApiAccountRegisterAction extends ApiAction
!common_valid_http_url($homepage)) { !common_valid_http_url($homepage)) {
// TRANS: Form validation error displayed when trying to register with an invalid homepage URL. // TRANS: Form validation error displayed when trying to register with an invalid homepage URL.
$this->clientError(_('Homepage is not a valid URL.'), 400); $this->clientError(_('Homepage is not a valid URL.'), 400);
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
// TRANS: Form validation error displayed when trying to register with a too long full name.
$this->clientError(_('Full name is too long (maximum 255 characters).'), 400);
} else if (Profile::bioTooLong($bio)) { } else if (Profile::bioTooLong($bio)) {
// TRANS: Form validation error on registration page when providing too long a bio text. // TRANS: Form validation error on registration page when providing too long a bio text.
// TRANS: %d is the maximum number of characters for bio; used for plural. // TRANS: %d is the maximum number of characters for bio; used for plural.
@@ -142,9 +138,6 @@ class ApiAccountRegisterAction extends ApiAction
'Bio is too long (maximum %d characters).', 'Bio is too long (maximum %d characters).',
Profile::maxBio()), Profile::maxBio()),
Profile::maxBio()), 400); Profile::maxBio()), 400);
} else if (!is_null($location) && mb_strlen($location) > 255) {
// TRANS: Form validation error displayed when trying to register with a too long location.
$this->clientError(_('Location is too long (maximum 255 characters).'), 400);
} else if (strlen($password) < 6) { } else if (strlen($password) < 6) {
// TRANS: Form validation error displayed when trying to register with too short a password. // TRANS: Form validation error displayed when trying to register with too short a password.
$this->clientError(_('Password must be 6 or more characters.'), 400); $this->clientError(_('Password must be 6 or more characters.'), 400);
@@ -156,28 +149,25 @@ class ApiAccountRegisterAction extends ApiAction
// annoy spammers // annoy spammers
sleep(7); sleep(7);
if ($user = User::register(array('nickname' => $nickname, if (Event::handle('APIStartRegistrationTry', array($this))) {
try {
$user = User::register(array('nickname' => $nickname,
'password' => $password, 'password' => $password,
'email' => $email, 'email' => $email,
'fullname' => $fullname, 'fullname' => $fullname,
'homepage' => $homepage, 'homepage' => $homepage,
'bio' => $bio, 'bio' => $bio,
'location' => $location, 'location' => $location,
'code' => $this->code))) { 'code' => $this->code));
if (!$user instanceof User) {
// TRANS: Form validation error displayed when trying to register with an invalid username or password.
$this->clientError(_('Invalid username or password.'), 400);
}
Event::handle('EndRegistrationTry', array($this)); Event::handle('EndRegistrationTry', array($this));
$this->initDocument('json'); $this->initDocument('json');
$this->showJsonObjects($this->twitterUserArray($user->getProfile())); $this->showJsonObjects($this->twitterUserArray($user->getProfile()));
$this->endDocument('json'); $this->endDocument('json');
} else { } catch (Exception $e) {
// TRANS: Form validation error displayed when trying to register with an invalid username or password. $this->clientError($e->getMessage(), 400);
$this->clientError(_('Invalid username or password.'), 400); }
} }
} }
} }

View File

@@ -96,21 +96,12 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
$original = clone($profile); $original = clone($profile);
if (!empty($this->name)) {
$profile->fullname = $this->name; $profile->fullname = $this->name;
}
if (!empty($this->url)) {
$profile->homepage = $this->url; $profile->homepage = $this->url;
}
if (!empty($this->description)) {
$profile->bio = $this->description; $profile->bio = $this->description;
}
if (!empty($this->location)) {
$profile->location = $this->location; $profile->location = $this->location;
if (!empty($this->location)) {
$loc = Location::fromName($this->location); $loc = Location::fromName($this->location);
if (!empty($loc)) { if (!empty($loc)) {
@@ -119,6 +110,12 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
$profile->location_id = $loc->location_id; $profile->location_id = $loc->location_id;
$profile->location_ns = $loc->location_ns; $profile->location_ns = $loc->location_ns;
} }
} else {
// location is empty so reset the extrapolated information too
$profile->lat = '';
$profile->lon = '';
$profile->location_id = '';
$profile->location_ns = '';
} }
$result = $profile->update($original); $result = $profile->update($original);

View File

@@ -49,16 +49,9 @@ class ApiconversationAction extends ApiAuthAction
protected $conversation = null; protected $conversation = null;
protected $notices = null; protected $notices = null;
/** protected function prepare(array $args=array())
* For initializing members of the class.
*
* @param array $argarray misc. arguments
*
* @return boolean true
*/
function prepare($argarray)
{ {
parent::prepare($argarray); parent::prepare($args);
$convId = $this->trimmed('id'); $convId = $this->trimmed('id');

View File

@@ -29,9 +29,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Allows the authenticating users to follow (subscribe) the user specified in * Allows the authenticating users to follow (subscribe) the user specified in
@@ -90,7 +88,7 @@ class ApiFriendshipsCreateAction extends ApiAuthAction
$this->clientError(_('Could not follow user: profile not found.'), 403); $this->clientError(_('Could not follow user: profile not found.'), 403);
} }
if ($this->user->isSubscribed($this->other)) { if ($this->scoped->isSubscribed($this->other)) {
$errmsg = sprintf( $errmsg = sprintf(
// TRANS: Client error displayed when trying to follow a user that's already being followed. // TRANS: Client error displayed when trying to follow a user that's already being followed.
// TRANS: %s is the nickname of the user that is already being followed. // TRANS: %s is the nickname of the user that is already being followed.
@@ -101,9 +99,9 @@ class ApiFriendshipsCreateAction extends ApiAuthAction
} }
try { try {
Subscription::start($this->user->getProfile(), $this->other); Subscription::start($this->scoped, $this->other);
} catch (Exception $e) { } catch (AlreadyFulfilledException $e) {
$this->clientError($e->getMessage(), 403); $this->clientError($e->getMessage(), 409);
} }
$this->initDocument($this->format); $this->initDocument($this->format);

View File

@@ -29,9 +29,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Allows the authenticating users to unfollow (unsubscribe) the user specified in * Allows the authenticating users to unfollow (unsubscribe) the user specified in
@@ -48,7 +46,9 @@ if (!defined('STATUSNET')) {
*/ */
class ApiFriendshipsDestroyAction extends ApiAuthAction class ApiFriendshipsDestroyAction extends ApiAuthAction
{ {
var $other = null; protected $needPost = true;
protected $other = null;
/** /**
* Take arguments for running * Take arguments for running
@@ -58,11 +58,10 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction
* @return boolean success flag * @return boolean success flag
* *
*/ */
function prepare($args) protected function prepare(array $args=array())
{ {
parent::prepare($args); parent::prepare($args);
$this->user = $this->auth_user;
$this->other = $this->getTargetProfile($this->arg('id')); $this->other = $this->getTargetProfile($this->arg('id'));
return true; return true;
@@ -73,58 +72,40 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction
* *
* Check the format and show the user info * Check the format and show the user info
* *
* @param array $args $_REQUEST data (unused)
*
* @return void * @return void
*/ */
function handle($args) protected function handle()
{ {
parent::handle($args); parent::handle();
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
$this->clientError(
// TRANS: Client error. POST is a HTTP command. It should not be translated.
_('This method requires a POST.'),
400,
$this->format
);
return;
}
if (!in_array($this->format, array('xml', 'json'))) { if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError( $this->clientError(
// TRANS: Client error displayed when coming across a non-supported API method. // TRANS: Client error displayed when coming across a non-supported API method.
_('API method not found.'), _('API method not found.'),
404, 404
$this->format
); );
return;
} }
if (empty($this->other)) { if (!$this->other instanceof Profile) {
$this->clientError( $this->clientError(
// TRANS: Client error displayed when trying to unfollow a user that cannot be found. // TRANS: Client error displayed when trying to unfollow a user that cannot be found.
_('Could not unfollow user: User not found.'), _('Could not unfollow user: User not found.'),
403, 403
$this->format
); );
return;
} }
// Don't allow unsubscribing from yourself! // Don't allow unsubscribing from yourself!
if ($this->user->id == $this->other->id) { if ($this->scoped->id == $this->other->id) {
$this->clientError( $this->clientError(
// TRANS: Client error displayed when trying to unfollow self. // TRANS: Client error displayed when trying to unfollow self.
_("You cannot unfollow yourself."), _("You cannot unfollow yourself."),
403, 403
$this->format
); );
return;
} }
// throws an exception on error // throws an exception on error
Subscription::cancel($this->user->getProfile(), $this->other); Subscription::cancel($this->scoped, $this->other);
$this->initDocument($this->format); $this->initDocument($this->format);
$this->showProfile($this->other, $this->format); $this->showProfile($this->other, $this->format);

View File

@@ -29,9 +29,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Tests for the existence of friendship between two users. Will return true if * Tests for the existence of friendship between two users. Will return true if
@@ -57,7 +55,7 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction
* *
* @return boolean success flag * @return boolean success flag
*/ */
function prepare($args) protected function prepare(array $args=array())
{ {
parent::prepare($args); parent::prepare($args);
@@ -72,22 +70,18 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction
* *
* Check the format and show the user info * Check the format and show the user info
* *
* @param array $args $_REQUEST data (unused)
*
* @return void * @return void
*/ */
function handle($args) protected function handle()
{ {
parent::handle($args); parent::handle();
if (empty($this->profile_a) || empty($this->profile_b)) { if (empty($this->profile_a) || empty($this->profile_b)) {
$this->clientError( $this->clientError(
// TRANS: Client error displayed when supplying invalid parameters to an API call checking if a friendship exists. // TRANS: Client error displayed when supplying invalid parameters to an API call checking if a friendship exists.
_('Two valid IDs or nick names must be supplied.'), _('Two valid IDs or nick names must be supplied.'),
400, 400
$this->format
); );
return;
} }
$result = Subscription::exists($this->profile_a, $this->profile_b); $result = Subscription::exists($this->profile_a, $this->profile_b);

View File

@@ -29,9 +29,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Outputs detailed information about the relationship between two users * Outputs detailed information about the relationship between two users
@@ -56,7 +54,7 @@ class ApiFriendshipsShowAction extends ApiBareAuthAction
* *
* @return boolean success flag * @return boolean success flag
*/ */
function prepare($args) protected function prepare(array $args=array())
{ {
parent::prepare($args); parent::prepare($args);
@@ -109,13 +107,11 @@ class ApiFriendshipsShowAction extends ApiBareAuthAction
* *
* Check the format and show the user info * Check the format and show the user info
* *
* @param array $args $_REQUEST data (unused)
*
* @return void * @return void
*/ */
function handle($args) protected function handle()
{ {
parent::handle($args); parent::handle();
if (!in_array($this->format, array('xml', 'json'))) { if (!in_array($this->format, array('xml', 'json'))) {
// TRANS: Client error displayed when coming across a non-supported API method. // TRANS: Client error displayed when coming across a non-supported API method.

View File

@@ -115,11 +115,10 @@ class ApiListMembershipsAction extends ApiBareAuthAction
function getLists() function getLists()
{ {
$profile = $this->target; $fn = array($this->target, 'getOtherTags');
$fn = array($profile, 'getOtherTags');
# 20 lists # 20 lists
list($this->lists, $this->next_cursor, $this->prev_cursor) = list($this->lists, $this->next_cursor, $this->prev_cursor) =
Profile_list::getAtCursor($fn, array($this->auth_user), $this->cursor, 20); Profile_list::getAtCursor($fn, array($this->scoped), $this->cursor, 20);
} }
} }

View File

@@ -185,7 +185,7 @@ class ApiListsAction extends ApiBareAuthAction
list($this->lists, list($this->lists,
$this->next_cursor, $this->next_cursor,
$this->prev_cursor) = Profile_list::getAtCursor($fn, array($this->auth_user), $cursor, $count); $this->prev_cursor) = Profile_list::getAtCursor($fn, array($this->scoped), $cursor, $count);
} }
function isReadOnly($args) function isReadOnly($args)

View File

@@ -30,7 +30,7 @@ if (!defined('GNUSOCIAL')) { exit(1); }
/** /**
* Upload an image via the API. Returns a shortened URL for the image * Upload an image via the API. Returns a shortened URL for the image
* to the user. * to the user. Apparently modelled after a former Twitpic API.
* *
* @category API * @category API
* @package StatusNet * @package StatusNet
@@ -42,17 +42,20 @@ class ApiMediaUploadAction extends ApiAuthAction
{ {
protected $needPost = true; protected $needPost = true;
/** protected function prepare(array $args=array())
* Handle the request {
* parent::prepare($args);
* Grab the file from the 'media' param, then store, and shorten
* // fallback to xml for older clients etc
* @todo Upload throttle! if (empty($this->format)) {
* $this->format = 'xml';
* @param array $args $_REQUEST data (unused) }
* if (!in_array($this->format, ['json', 'xml'])) {
* @return void throw new ClientException('This API call does not support the format '._ve($this->format));
*/ }
return true;
}
protected function handle() protected function handle()
{ {
parent::handle(); parent::handle();
@@ -69,14 +72,31 @@ class ApiMediaUploadAction extends ApiAuthAction
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.', $msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.', 'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH'])); intval($_SERVER['CONTENT_LENGTH']));
$this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); throw new ClientException(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
} }
// we could catch "NoUploadedMediaException" as "no media uploaded", but here we _always_ want an upload try {
$upload = MediaFile::fromUpload('media', $this->scoped); $upload = MediaFile::fromUpload('media', $this->scoped);
} catch (NoUploadedMediaException $e) {
common_debug('No media file was uploaded to the _FILES array');
$fh = tmpfile();
if ($this->arg('media')) {
common_debug('Found media parameter which we hope contains a media file!');
fwrite($fh, $this->arg('media'));
} elseif ($this->arg('media_data')) {
common_debug('Found media_data parameter which we hope contains a base64-encoded media file!');
fwrite($fh, base64_decode($this->arg('media_data')));
} else {
common_debug('No media|media_data POST parameter was supplied');
fclose($fh);
throw $e;
}
common_debug('MediaFile importing the uploaded file with fromFilehandle');
$upload = MediaFile::fromFilehandle($fh, $this->scoped);
}
common_debug('MediaFile completed and saved us fileRecord with id=='._ve($upload->fileRecord->id));
// Thumbnails will be generated/cached on demand when accessed (such as with /attachment/:id/thumbnail) // Thumbnails will be generated/cached on demand when accessed (such as with /attachment/:id/thumbnail)
$this->showResponse($upload); $this->showResponse($upload);
} }
@@ -88,14 +108,61 @@ class ApiMediaUploadAction extends ApiAuthAction
* *
* @return void * @return void
*/ */
function showResponse(MediaFile $upload) protected function showResponse(MediaFile $upload)
{ {
$this->initDocument(); $this->initDocument($this->format);
$this->elementStart('rsp', array('stat' => 'ok')); switch ($this->format) {
case 'json':
return $this->showResponseJson($upload);
case 'xml':
return $this->showResponseXml($upload);
default:
throw new ClientException('This API call does not support the format '._ve($this->format));
}
$this->endDocument($this->format);
}
protected function showResponseJson(MediaFile $upload)
{
$enc = $upload->fileRecord->getEnclosure();
// note that we use media_id instead of mediaid which XML users might've gotten used to (nowadays we service media_id in both!)
$output = [
'media_id' => $upload->fileRecord->id,
'media_id_string' => (string)$upload->fileRecord->id,
'media_url' => $upload->shortUrl(),
'size' => $upload->fileRecord->size,
];
if (common_get_mime_media($enc->mimetype) === 'image') {
$output['image'] = [
'w' => $enc->width,
'h' => $enc->height,
'image_type' => $enc->mimetype,
];
}
print json_encode($output);
}
protected function showResponseXml(MediaFile $upload)
{
$this->elementStart('rsp', array('stat' => 'ok', 'xmlns:atom'=>Activity::ATOM));
$this->element('mediaid', null, $upload->fileRecord->id); $this->element('mediaid', null, $upload->fileRecord->id);
$this->element('mediaurl', null, $upload->shortUrl()); $this->element('mediaurl', null, $upload->shortUrl());
$this->element('media_url', null, $upload->shortUrl());
$this->element('size', null, $upload->fileRecord->size);
$enclosure = $upload->fileRecord->getEnclosure();
$this->element('atom:link', array('rel' => 'enclosure',
'href' => $enclosure->url,
'type' => $enclosure->mimetype));
// Twitter specific metadata expected in response since Twitter's Media upload API v1.1 (even though Twitter doesn't use XML)
$this->element('media_id', null, $upload->fileRecord->id);
$this->element('media_id_string', null, (string)$upload->fileRecord->id);
if (common_get_mime_media($enclosure->mimetype) === 'image') {
$this->element('image', ['w'=>$enclosure->width, 'h'=>$enclosure->height, 'image_type'=>$enclosure->mimetype]);
}
$this->elementEnd('rsp'); $this->elementEnd('rsp');
$this->endDocument();
} }
/** /**
@@ -103,9 +170,16 @@ class ApiMediaUploadAction extends ApiAuthAction
* *
* @param String $msg an error message * @param String $msg an error message
*/ */
function clientError($msg) function clientError($msg, $code=400, $format=null)
{ {
$this->initDocument(); $this->initDocument($this->format);
switch ($this->format) {
case 'json':
$error = ['errors' => array()];
$error['errors'][] = ['message'=>$msg, 'code'=>131];
print json_encode($error);
break;
case 'xml':
$this->elementStart('rsp', array('stat' => 'fail')); $this->elementStart('rsp', array('stat' => 'fail'));
// @todo add in error code // @todo add in error code
@@ -113,6 +187,9 @@ class ApiMediaUploadAction extends ApiAuthAction
$this->element('err', $errAttr, null); $this->element('err', $errAttr, null);
$this->elementEnd('rsp'); $this->elementEnd('rsp');
$this->endDocument(); break;
}
$this->endDocument($this->format);
exit;
} }
} }

View File

@@ -327,7 +327,7 @@ class ApiSearchAtomAction extends ApiPrivateAuthAction
'rel' => 'alternate', 'rel' => 'alternate',
'href' => $nurl)); 'href' => $nurl));
$this->element('title', null, common_xml_safe_str(trim($notice->content))); $this->element('title', null, common_xml_safe_str(trim($notice->content)));
$this->element('content', array('type' => 'html'), $notice->rendered); $this->element('content', array('type' => 'html'), $notice->getRendered());
$this->element('updated', null, common_date_w3dtf($notice->created)); $this->element('updated', null, common_date_w3dtf($notice->created));
$this->element('link', array('type' => 'image/png', $this->element('link', array('type' => 'image/png',
// XXX: Twitter uses rel="image" (not valid) // XXX: Twitter uses rel="image" (not valid)

View File

@@ -124,7 +124,7 @@ class ApiStatusesDestroyAction extends ApiAuthAction
if ($this->user->id == $this->notice->profile_id) { if ($this->user->id == $this->notice->profile_id) {
if (Event::handle('StartDeleteOwnNotice', array($this->user, $this->notice))) { if (Event::handle('StartDeleteOwnNotice', array($this->user, $this->notice))) {
$this->notice->delete(); $this->notice->deleteAs($this->scoped);
Event::handle('EndDeleteOwnNotice', array($this->user, $this->notice)); Event::handle('EndDeleteOwnNotice', array($this->user, $this->notice));
} }
$this->showNotice(); $this->showNotice();

View File

@@ -74,16 +74,21 @@ class ApiStatusesShowAction extends ApiPrivateAuthAction
$this->notice_id = (int)$this->trimmed('id'); $this->notice_id = (int)$this->trimmed('id');
$this->notice = Notice::getKV('id', $this->notice_id); $this->notice = null;
if (!$this->notice instanceof Notice) { try {
$deleted = Deleted_notice::getKV('id', $this->notice_id); $this->notice = Notice::getByID($this->notice_id);
if ($deleted instanceof Deleted_notice) { } catch (NoResultException $e) {
// No such notice was found, maybe it was deleted?
$deleted = null;
Event::handle('IsNoticeDeleted', array($this->notice_id, &$deleted));
if ($deleted === true) {
// TRANS: Client error displayed trying to show a deleted notice. // TRANS: Client error displayed trying to show a deleted notice.
$this->clientError(_('Notice deleted.'), 410); throw new ClientException(_('Notice deleted.'), 410);
} }
// TRANS: Client error displayed trying to show a non-existing notice. // TRANS: Client error displayed trying to show a non-existing notice.
$this->clientError(_('No such notice.'), 404); throw new ClientException(_('No such notice.'), 404);
} }
if (!$this->notice->inScope($this->scoped)) { if (!$this->notice->inScope($this->scoped)) {
// TRANS: Client exception thrown when trying a view a notice the user has no access to. // TRANS: Client exception thrown when trying a view a notice the user has no access to.
throw new ClientException(_('Access restricted.'), 403); throw new ClientException(_('Access restricted.'), 403);
@@ -128,7 +133,6 @@ class ApiStatusesShowAction extends ApiPrivateAuthAction
*/ */
function showNotice() function showNotice()
{ {
if (!empty($this->notice)) {
switch ($this->format) { switch ($this->format) {
case 'xml': case 'xml':
$this->showSingleXmlStatus($this->notice); $this->showSingleXmlStatus($this->notice);
@@ -144,28 +148,6 @@ class ApiStatusesShowAction extends ApiPrivateAuthAction
// TRANS: %s is the requested output format. // TRANS: %s is the requested output format.
throw new Exception(sprintf(_("Unsupported format: %s."), $this->format)); throw new Exception(sprintf(_("Unsupported format: %s."), $this->format));
} }
} else {
// XXX: Twitter just sets a 404 header and doens't bother
// to return an err msg
$deleted = Deleted_notice::getKV($this->notice_id);
if (!empty($deleted)) {
$this->clientError(
// TRANS: Client error displayed requesting a deleted status.
_('Status deleted.'),
410,
$this->format
);
} else {
$this->clientError(
// TRANS: Client error displayed requesting a status with an invalid ID.
_('No status with that ID found.'),
404,
$this->format
);
}
}
} }
/** /**
@@ -188,13 +170,9 @@ class ApiStatusesShowAction extends ApiPrivateAuthAction
*/ */
function lastModified() function lastModified()
{ {
if (!empty($this->notice)) {
return strtotime($this->notice->created); return strtotime($this->notice->created);
} }
return null;
}
/** /**
* An entity tag for this notice * An entity tag for this notice
* *
@@ -205,8 +183,6 @@ class ApiStatusesShowAction extends ApiPrivateAuthAction
*/ */
function etag() function etag()
{ {
if (!empty($this->notice)) {
return '"' . implode( return '"' . implode(
':', ':',
array($this->arg('action'), array($this->arg('action'),
@@ -218,9 +194,6 @@ class ApiStatusesShowAction extends ApiPrivateAuthAction
. '"'; . '"';
} }
return null;
}
function deleteNotice() function deleteNotice()
{ {
if ($this->format != 'atom') { if ($this->format != 'atom') {
@@ -236,7 +209,7 @@ class ApiStatusesShowAction extends ApiPrivateAuthAction
} }
if (Event::handle('StartDeleteOwnNotice', array($this->auth_user, $this->notice))) { if (Event::handle('StartDeleteOwnNotice', array($this->auth_user, $this->notice))) {
$this->notice->delete(); $this->notice->deleteAs($this->scoped);
Event::handle('EndDeleteOwnNotice', array($this->auth_user, $this->notice)); Event::handle('EndDeleteOwnNotice', array($this->auth_user, $this->notice));
} }

View File

@@ -152,6 +152,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
var $in_reply_to_status_id = null; var $in_reply_to_status_id = null;
var $lat = null; var $lat = null;
var $lon = null; var $lon = null;
var $media_ids = array(); // file_id in the keys
/** /**
* Take arguments for running * Take arguments for running
@@ -167,6 +168,19 @@ class ApiStatusesUpdateAction extends ApiAuthAction
$this->status = $this->trimmed('status'); $this->status = $this->trimmed('status');
$this->lat = $this->trimmed('lat'); $this->lat = $this->trimmed('lat');
$this->lon = $this->trimmed('long'); $this->lon = $this->trimmed('long');
$matches = array();
common_debug(get_called_class().': media_ids=='._ve($this->trimmed('media_ids')));
if (preg_match_all('/\d+/', $this->trimmed('media_ids'), $matches) !== false) {
foreach (array_unique($matches[0]) as $match) {
try {
$this->media_ids[$match] = File::getByID($match);
} catch (EmptyIdException $e) {
// got a zero from the client, at least Twidere does this on occasion
} catch (NoResultException $e) {
// File ID was not found. Do we abort and report to the client?
}
}
}
$this->in_reply_to_status_id $this->in_reply_to_status_id
= intval($this->trimmed('in_reply_to_status_id')); = intval($this->trimmed('in_reply_to_status_id'));
@@ -211,7 +225,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
$this->clientError(_('No such user.'), 404); $this->clientError(_('No such user.'), 404);
} }
/* Do not call shortenlinks until the whole notice has been build */ /* Do not call shortenLinks until the whole notice has been build */
// Check for commands // Check for commands
@@ -244,17 +258,24 @@ class ApiStatusesUpdateAction extends ApiAuthAction
} }
} }
foreach(array_keys($this->media_ids) as $media_id) {
// FIXME: Validation on this... Worst case is that if someone sends bad media_ids then
// we'll fill the notice with non-working links, so no real harm, done, but let's fix.
// The File objects are in the array, so we could get URLs from them directly.
$this->status .= ' ' . common_local_url('attachment', array('attachment' => $media_id));
}
$upload = null; $upload = null;
try { try {
$upload = MediaFile::fromUpload('media', $this->scoped); $upload = MediaFile::fromUpload('media', $this->scoped);
$this->status .= ' ' . $upload->shortUrl(); $this->status .= ' ' . $upload->shortUrl();
/* Do not call shortenlinks until the whole notice has been build */ /* Do not call shortenLinks until the whole notice has been build */
} catch (NoUploadedMediaException $e) { } catch (NoUploadedMediaException $e) {
// There was no uploaded media for us today. // There was no uploaded media for us today.
} }
/* Do call shortenlinks here & check notice length since notice is about to be saved & sent */ /* Do call shortenlinks here & check notice length since notice is about to be saved & sent */
$status_shortened = $this->auth_user->shortenlinks($this->status); $status_shortened = $this->auth_user->shortenLinks($this->status);
if (Notice::contentTooLong($status_shortened)) { if (Notice::contentTooLong($status_shortened)) {
if ($upload instanceof MediaFile) { if ($upload instanceof MediaFile) {

View File

@@ -51,19 +51,10 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction
{ {
var $notices = null; var $notices = null;
/** protected function prepare(array $args=array())
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
common_debug("apitimelinetag prepare()");
$this->tag = $this->arg('tag'); $this->tag = $this->arg('tag');
$this->notices = $this->getNotices(); $this->notices = $this->getNotices();
@@ -79,9 +70,9 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction
* *
* @return void * @return void
*/ */
function handle($args) protected function handle()
{ {
parent::handle($args); parent::handle();
$this->showTimeline(); $this->showTimeline();
} }
@@ -172,21 +163,12 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction
*/ */
function getNotices() function getNotices()
{ {
$notices = array(); $notice = Notice_tag::getStream($this->tag)->getNotices(($this->page - 1) * $this->count,
$notice = Notice_tag::getStream(
$this->tag,
($this->page - 1) * $this->count,
$this->count + 1, $this->count + 1,
$this->since_id, $this->since_id,
$this->max_id $this->max_id);
);
while ($notice->fetch()) { return $notice->fetchAll();
$notices[] = clone($notice);
}
return $notices;
} }
/** /**

View File

@@ -34,9 +34,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Returns the most recent notices (default 20) posted by the authenticating * Returns the most recent notices (default 20) posted by the authenticating
@@ -79,6 +77,10 @@ class ApiTimelineUserAction extends ApiBareAuthAction
$this->clientError(_('No such user.'), 404); $this->clientError(_('No such user.'), 404);
} }
if (!$this->target->isLocal()) {
$this->serverError(_('Remote user timelines are not available here yet.'), 501);
}
$this->notices = $this->getNotices(); $this->notices = $this->getNotices();
return true; return true;
@@ -111,11 +113,11 @@ class ApiTimelineUserAction extends ApiBareAuthAction
{ {
// We'll use the shared params from the Atom stub // We'll use the shared params from the Atom stub
// for other feed types. // for other feed types.
$atom = new AtomUserNoticeFeed($this->target->getUser(), $this->auth_user); $atom = new AtomUserNoticeFeed($this->target->getUser(), $this->scoped);
$link = common_local_url( $link = common_local_url(
'showstream', 'showstream',
array('nickname' => $this->target->nickname) array('nickname' => $this->target->getNickname())
); );
$self = $this->getSelfUri(); $self = $this->getSelfUri();
@@ -123,7 +125,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
// FriendFeed's SUP protocol // FriendFeed's SUP protocol
// Also added RSS and Atom feeds // Also added RSS and Atom feeds
$suplink = common_local_url('sup', null, null, $this->target->id); $suplink = common_local_url('sup', null, null, $this->target->getID());
header('X-SUP-ID: ' . $suplink); header('X-SUP-ID: ' . $suplink);
@@ -131,7 +133,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
$nextUrl = !empty($this->next_id) $nextUrl = !empty($this->next_id)
? common_local_url('ApiTimelineUser', ? common_local_url('ApiTimelineUser',
array('format' => $this->format, array('format' => $this->format,
'id' => $this->target->id), 'id' => $this->target->getID()),
array('max_id' => $this->next_id)) array('max_id' => $this->next_id))
: null; : null;
@@ -143,11 +145,11 @@ class ApiTimelineUserAction extends ApiBareAuthAction
$prevUrl = common_local_url('ApiTimelineUser', $prevUrl = common_local_url('ApiTimelineUser',
array('format' => $this->format, array('format' => $this->format,
'id' => $this->target->id), 'id' => $this->target->getID()),
$prevExtra); $prevExtra);
$firstUrl = common_local_url('ApiTimelineUser', $firstUrl = common_local_url('ApiTimelineUser',
array('format' => $this->format, array('format' => $this->format,
'id' => $this->target->id)); 'id' => $this->target->getID()));
switch($this->format) { switch($this->format) {
case 'xml': case 'xml':
@@ -202,7 +204,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
break; break;
case 'as': case 'as':
header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE); header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
$doc = new ActivityStreamJSONDocument($this->auth_user); $doc = new ActivityStreamJSONDocument($this->scoped);
$doc->setTitle($atom->title); $doc->setTitle($atom->title);
$doc->addLink($link, 'alternate', 'text/html'); $doc->addLink($link, 'alternate', 'text/html');
$doc->addItemsFromNotices($this->notices); $doc->addItemsFromNotices($this->notices);
@@ -303,9 +305,9 @@ class ApiTimelineUserAction extends ApiBareAuthAction
return '"' . implode( return '"' . implode(
':', ':',
array($this->arg('action'), array($this->arg('action'),
common_user_cache_hash($this->auth_user), common_user_cache_hash($this->scoped),
common_language(), common_language(),
$this->target->id, $this->target->getID(),
strtotime($this->notices[0]->created), strtotime($this->notices[0]->created),
strtotime($this->notices[$last]->created)) strtotime($this->notices[$last]->created))
) )
@@ -317,10 +319,10 @@ class ApiTimelineUserAction extends ApiBareAuthAction
function handlePost() function handlePost()
{ {
if (empty($this->auth_user) || if (!$this->scoped instanceof Profile ||
$this->auth_user->id != $this->target->id) { !$this->target->sameAs($this->scoped)) {
// TRANS: Client error displayed trying to add a notice to another user's timeline. // TRANS: Client error displayed trying to add a notice to another user's timeline.
$this->clientError(_('Only the user can add to their own timeline.')); $this->clientError(_('Only the user can add to their own timeline.'), 403);
} }
// Only handle posts for Atom // Only handle posts for Atom
@@ -352,165 +354,28 @@ class ApiTimelineUserAction extends ApiBareAuthAction
$activity = new Activity($dom->documentElement); $activity = new Activity($dom->documentElement);
$saved = null; common_debug('AtomPub: Ignoring right now, but this POST was made to collection: '.$activity->id);
if (Event::handle('StartAtomPubNewActivity', array(&$activity, $this->target->getUser(), &$saved))) { // Reset activity data so we can handle it in the same functions as with OStatus
if ($activity->verb != ActivityVerb::POST) { // because we don't let clients set their own UUIDs... Not sure what AtomPub thinks
// about that though.
$activity->id = null;
$activity->actor = null; // not used anyway, we use $this->target
$activity->objects[0]->id = null;
$stored = null;
if (Event::handle('StartAtomPubNewActivity', array($activity, $this->target, &$stored))) {
// TRANS: Client error displayed when not using the POST verb. Do not translate POST. // TRANS: Client error displayed when not using the POST verb. Do not translate POST.
$this->clientError(_('Can only handle POST activities.')); throw new ClientException(_('Could not handle this Atom Activity.'));
} }
if (!$stored instanceof Notice) {
$note = $activity->objects[0]; throw new ServerException('Server did not create a Notice object from handled AtomPub activity.');
if (!in_array($note->type, array(ActivityObject::NOTE,
ActivityObject::BLOGENTRY,
ActivityObject::STATUS))) {
// TRANS: Client error displayed when using an unsupported activity object type.
// TRANS: %s is the unsupported activity object type.
$this->clientError(sprintf(_('Cannot handle activity object type "%s".'),
$note->type));
} }
Event::handle('EndAtomPubNewActivity', array($activity, $this->target, $stored));
$saved = $this->postNote($activity);
Event::handle('EndAtomPubNewActivity', array($activity, $this->target->getUser(), $saved));
}
if (!empty($saved)) {
header('HTTP/1.1 201 Created'); header('HTTP/1.1 201 Created');
header("Location: " . common_local_url('ApiStatusesShow', array('id' => $saved->id, header("Location: " . common_local_url('ApiStatusesShow', array('id' => $stored->getID(),
'format' => 'atom'))); 'format' => 'atom')));
$this->showSingleAtomStatus($saved); $this->showSingleAtomStatus($stored);
}
}
function postNote($activity)
{
$note = $activity->objects[0];
// Use summary as fallback for content
if (!empty($note->content)) {
$sourceContent = $note->content;
} else if (!empty($note->summary)) {
$sourceContent = $note->summary;
} else if (!empty($note->title)) {
$sourceContent = $note->title;
} else {
// @fixme fetch from $sourceUrl?
// TRANS: Client error displayed when posting a notice without content through the API.
// TRANS: %d is the notice ID (number).
$this->clientError(sprintf(_('No content for notice %d.'), $note->id));
}
// Get (safe!) HTML and text versions of the content
$rendered = $this->purify($sourceContent);
$content = common_strip_html($rendered);
$shortened = $this->auth_user->shortenLinks($content);
$options = array('is_local' => Notice::LOCAL_PUBLIC,
'rendered' => $rendered,
'replies' => array(),
'groups' => array(),
'tags' => array(),
'urls' => array());
// accept remote URI (not necessarily a good idea)
common_debug("Note ID is {$note->id}");
if (!empty($note->id)) {
$notice = Notice::getKV('uri', trim($note->id));
if (!empty($notice)) {
// TRANS: Client error displayed when using another format than AtomPub.
// TRANS: %s is the notice URI.
$this->clientError(sprintf(_('Notice with URI "%s" already exists.'), $note->id));
}
common_log(LOG_NOTICE, "Saving client-supplied notice URI '$note->id'");
$options['uri'] = $note->id;
}
// accept remote create time (also maybe not such a good idea)
if (!empty($activity->time)) {
common_log(LOG_NOTICE, "Saving client-supplied create time {$activity->time}");
$options['created'] = common_sql_date($activity->time);
}
// Check for optional attributes...
if ($activity->context instanceof ActivityContext) {
foreach ($activity->context->attention as $uri=>$type) {
try {
$profile = Profile::fromUri($uri);
if ($profile->isGroup()) {
$options['groups'][] = $profile->id;
} else {
$options['replies'][] = $uri;
}
} catch (UnknownUriException $e) {
common_log(LOG_WARNING, sprintf('AtomPub post with unknown attention URI %s', $uri));
}
}
// Maintain direct reply associations
// @fixme what about conversation ID?
if (!empty($activity->context->replyToID)) {
$orig = Notice::getKV('uri',
$activity->context->replyToID);
if (!empty($orig)) {
$options['reply_to'] = $orig->id;
}
}
$location = $activity->context->location;
if ($location) {
$options['lat'] = $location->lat;
$options['lon'] = $location->lon;
if ($location->location_id) {
$options['location_ns'] = $location->location_ns;
$options['location_id'] = $location->location_id;
}
}
}
// Atom categories <-> hashtags
foreach ($activity->categories as $cat) {
if ($cat->term) {
$term = common_canonical_tag($cat->term);
if ($term) {
$options['tags'][] = $term;
}
}
}
// Atom enclosures -> attachment URLs
foreach ($activity->enclosures as $href) {
// @fixme save these locally or....?
$options['urls'][] = $href;
}
$saved = Notice::saveNew($this->target->id,
$content,
'atompub', // TODO: deal with this
$options);
return $saved;
}
function purify($content)
{
require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
$config = array('safe' => 1,
'deny_attribute' => 'id,style,on*');
return htmLawed($content, $config);
} }
} }

View File

@@ -230,18 +230,11 @@ class AtompubsubscriptionfeedAction extends AtompubAction
$this->clientError(sprintf(_('Unknown profile %s.'), $person->id)); $this->clientError(sprintf(_('Unknown profile %s.'), $person->id));
} }
if (Subscription::exists($this->_profile, $profile)) { try {
$sub = Subscription::start($this->_profile, $profile);
} catch (AlreadyFulfilledException $e) {
// 409 Conflict // 409 Conflict
// TRANS: Client error displayed trying to subscribe to an already subscribed profile. $this->clientError($e->getMessage(), 409);
// TRANS: %s is the profile the user already has a subscription on.
$this->clientError(sprintf(_('Already subscribed to %s.'),
$person->id),
409);
}
if (Subscription::start($this->_profile, $profile)) {
$sub = Subscription::pkeyGet(array('subscriber' => $this->_profile->id,
'subscribed' => $profile->id));
} }
Event::handle('EndAtomPubNewActivity', array($activity, $sub)); Event::handle('EndAtomPubNewActivity', array($activity, $sub));

View File

@@ -59,9 +59,9 @@ class Attachment_thumbnailAction extends AttachmentAction
try { try {
$thumbnail = $this->attachment->getThumbnail($this->thumb_w, $this->thumb_h, $this->thumb_c); $thumbnail = $this->attachment->getThumbnail($this->thumb_w, $this->thumb_h, $this->thumb_c);
} catch (UseFileAsThumbnailException $e) { } catch (UseFileAsThumbnailException $e) {
common_redirect($e->file->getUrl()); common_redirect($e->file->getUrl(), 302);
} }
common_redirect($thumbnail->getUrl()); common_redirect(File_thumbnail::url($thumbnail->filename), 302);
} }
} }

View File

@@ -28,13 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
define('MAX_ORIGINAL', 480);
/** /**
* Upload an avatar * Upload an avatar
@@ -82,11 +76,11 @@ class AvatarsettingsAction extends SettingsAction
/** /**
* Content area of the page * Content area of the page
* *
* Shows a form for uploading an avatar. * Shows a form for uploading an avatar. Currently overrides FormAction's showContent
* since we haven't made classes out of AvatarCropForm and AvatarUploadForm.
* *
* @return void * @return void
*/ */
function showContent() function showContent()
{ {
if ($this->mode == 'crop') { if ($this->mode == 'crop') {
@@ -249,51 +243,18 @@ class AvatarsettingsAction extends SettingsAction
$this->elementEnd('form'); $this->elementEnd('form');
} }
/** protected function doPost()
* Handle a post
*
* We mux on the button name to figure out what the user actually wanted.
*
* @return void
*/
function handlePost()
{ {
// 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)
) {
// TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
// TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->showForm(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
return;
}
// 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(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if (Event::handle('StartAvatarSaveForm', array($this))) { if (Event::handle('StartAvatarSaveForm', array($this))) {
if ($this->arg('upload')) { if ($this->trimmed('upload')) {
$this->uploadAvatar(); return $this->uploadAvatar();
} else if ($this->arg('crop')) { } else if ($this->trimmed('crop')) {
$this->cropAvatar(); return $this->cropAvatar();
} else if ($this->arg('delete')) { } else if ($this->trimmed('delete')) {
$this->deleteAvatar(); return $this->deleteAvatar();
} else { } else {
// TRANS: Unexpected validation error on avatar upload form. // TRANS: Unexpected validation error on avatar upload form.
$this->showForm(_('Unexpected form submission.')); throw new ClientException(_('Unexpected form submission.'));
} }
Event::handle('EndAvatarSaveForm', array($this)); Event::handle('EndAvatarSaveForm', array($this));
} }
@@ -309,21 +270,12 @@ class AvatarsettingsAction extends SettingsAction
*/ */
function uploadAvatar() function uploadAvatar()
{ {
try { // ImageFile throws exception if something goes wrong, which we'll
// pick up and show as an error message above the form.
$imagefile = ImageFile::fromUpload('avatarfile'); $imagefile = ImageFile::fromUpload('avatarfile');
} catch (Exception $e) {
$this->showForm($e->getMessage());
return;
}
if ($imagefile === null) {
// TRANS: Validation error on avatar upload form when no file was uploaded.
$this->showForm(_('No file uploaded.'));
return;
}
$cur = common_current_user();
$type = $imagefile->preferredType(); $type = $imagefile->preferredType();
$filename = Avatar::filename($cur->id, $filename = Avatar::filename($this->scoped->getID(),
image_type_to_extension($type), image_type_to_extension($type),
null, null,
'tmp'.common_timestamp()); 'tmp'.common_timestamp());
@@ -344,8 +296,7 @@ class AvatarsettingsAction extends SettingsAction
$this->mode = 'crop'; $this->mode = 'crop';
// TRANS: Avatar upload form instruction after uploading a file. // TRANS: Avatar upload form instruction after uploading a file.
$this->showForm(_('Pick a square area of the image to be your avatar.'), return _('Pick a square area of the image to be your avatar.');
true);
} }
/** /**
@@ -357,36 +308,46 @@ class AvatarsettingsAction extends SettingsAction
{ {
$filedata = $_SESSION['FILEDATA']; $filedata = $_SESSION['FILEDATA'];
if (!$filedata) { if (empty($filedata)) {
// TRANS: Server error displayed if an avatar upload went wrong somehow server side. // TRANS: Server error displayed if an avatar upload went wrong somehow server side.
$this->serverError(_('Lost our file data.')); throw new ServerException(_('Lost our file data.'));
} }
$file_d = ($filedata['width'] > $filedata['height']) $file_d = min($filedata['width'], $filedata['height']);
? $filedata['height'] : $filedata['width'];
$dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0; $dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0;
$dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0; $dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0;
$dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$file_d; $dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$file_d;
$dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$file_d; $dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$file_d;
$size = intval(min($dest_w, $dest_h, MAX_ORIGINAL)); $size = intval(min($dest_w, $dest_h, common_config('avatar', 'maxsize')));
$user = common_current_user(); $box = array('width' => $size, 'height' => $size,
$profile = $user->getProfile(); 'x' => $dest_x, 'y' => $dest_y,
'w' => $dest_w, 'h' => $dest_h);
$imagefile = new ImageFile($user->id, $filedata['filepath']); $imagefile = new ImageFile(null, $filedata['filepath']);
$filename = $imagefile->resize($size, $dest_x, $dest_y, $dest_w, $dest_h); $filename = Avatar::filename($this->scoped->getID(), image_type_to_extension($imagefile->preferredType()),
$size, common_timestamp());
try {
$imagefile->resizeTo(Avatar::path($filename), $box);
} catch (UseFileAsThumbnailException $e) {
common_debug('Using uploaded avatar directly without resizing, copying it to: '.$filename);
if (!copy($filedata['filepath'], Avatar::path($filename))) {
common_debug('Tried to copy image file '.$filedata['filepath'].' to destination '.Avatar::path($filename));
throw new ServerException('Could not copy file to destination.');
}
}
if ($profile->setOriginal($filename)) { if ($this->scoped->setOriginal($filename)) {
@unlink($filedata['filepath']); @unlink($filedata['filepath']);
unset($_SESSION['FILEDATA']); unset($_SESSION['FILEDATA']);
$this->mode = 'upload'; $this->mode = 'upload';
// TRANS: Success message for having updated a user avatar. // TRANS: Success message for having updated a user avatar.
$this->showForm(_('Avatar updated.'), true); return _('Avatar updated.');
} else {
// TRANS: Error displayed on the avatar upload page if the avatar could not be updated for an unknown reason.
$this->showForm(_('Failed updating avatar.'));
} }
// TRANS: Error displayed on the avatar upload page if the avatar could not be updated for an unknown reason.
throw new ServerException(_('Failed updating avatar.'));
} }
/** /**
@@ -396,13 +357,10 @@ class AvatarsettingsAction extends SettingsAction
*/ */
function deleteAvatar() function deleteAvatar()
{ {
$user = common_current_user(); Avatar::deleteFromProfile($this->scoped);
$profile = $user->getProfile();
Avatar::deleteFromProfile($profile);
// TRANS: Success message for deleting a user avatar. // TRANS: Success message for deleting a user avatar.
$this->showForm(_('Avatar deleted.'), true); return _('Avatar deleted.');
} }
/** /**

View File

@@ -28,11 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/** /**
* Download a backup of your own account to the browser * Download a backup of your own account to the browser
@@ -48,38 +44,19 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class BackupaccountAction extends Action class BackupaccountAction extends FormAction
{ {
/** protected $form = 'BackupAccount';
* Returns the title of the page
*
* @return string page title
*/
function title() function title()
{ {
// TRANS: Title for backup account page. // TRANS: Title for backup account page.
return _('Backup account'); return _('Backup account');
} }
/** protected function doPreparation()
* For initializing members of the class.
*
* @param array $argarray misc. arguments
*
* @return boolean true
*/
function prepare($argarray)
{ {
parent::prepare($argarray); if (!$this->scoped->hasRight(Right::BACKUPACCOUNT)) {
$cur = common_current_user();
if (empty($cur)) {
// TRANS: Client exception thrown when trying to backup an account while not logged in.
throw new ClientException(_('Only logged-in users can backup their account.'), 403);
}
if (!$cur->hasRight(Right::BACKUPACCOUNT)) {
// TRANS: Client exception thrown when trying to backup an account without having backup rights. // TRANS: Client exception thrown when trying to backup an account without having backup rights.
throw new ClientException(_('You may not backup your account.'), 403); throw new ClientException(_('You may not backup your account.'), 403);
} }
@@ -87,40 +64,11 @@ class BackupaccountAction extends Action
return true; return true;
} }
/** protected function doPost()
* Handler method
*
* @param array $argarray is ignored since it's now passed in in prepare()
*
* @return void
*/
function handle($argarray=null)
{ {
parent::handle($argarray); $stream = new UserActivityStream($this->scoped->getUser(), true, UserActivityStream::OUTPUT_RAW);
if ($this->isPost()) { header('Content-Disposition: attachment; filename='.urlencode($this->scoped->getNickname()).'.atom');
$this->sendFeed();
} else {
$this->showPage();
}
return;
}
/**
* Send a feed of the user's activities to the browser
*
* Uses the UserActivityStream class; may take a long time!
*
* @return void
*/
function sendFeed()
{
$cur = common_current_user();
$stream = new UserActivityStream($cur, true, UserActivityStream::OUTPUT_RAW);
header('Content-Disposition: attachment; filename='.$cur->nickname.'.atom');
header('Content-Type: application/atom+xml; charset=utf-8'); header('Content-Type: application/atom+xml; charset=utf-8');
// @fixme atom feed logic is in getString... // @fixme atom feed logic is in getString...
@@ -128,39 +76,10 @@ class BackupaccountAction extends Action
$this->raw($stream->getString()); $this->raw($stream->getString());
} }
/** public function isReadOnly($args) {
* Show a little form so that the person can request a backup.
*
* @return void
*/
function showContent()
{
$form = new BackupAccountForm($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 true; return true;
} }
/**
* Return last modified, if applicable.
*
* MAY override
*
* @return string last modified http header
*/
function lastModified() function lastModified()
{ {
// For comparison with If-Last-Modified // For comparison with If-Last-Modified
@@ -168,89 +87,8 @@ class BackupaccountAction extends Action
return null; return null;
} }
/**
* Return etag, if applicable.
*
* MAY override
*
* @return string etag http header
*/
function etag() function etag()
{ {
return null; return null;
} }
} }
/**
* 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 BackupAccountForm extends Form
{
/**
* Class of the form.
*
* @return string the form's class
*/
function formClass()
{
return 'form_profile_backup';
}
/**
* URL the form posts to
*
* @return string the form's action URL
*/
function action()
{
return common_local_url('backupaccount');
}
/**
* Output form data
*
* Really, just instructions for doing a backup.
*
* @return void
*/
function formData()
{
$msg =
// TRANS: Information displayed on the backup account page.
_('You can backup your account data in '.
'<a href="http://activitystrea.ms/">Activity Streams</a> '.
'format. This is an experimental feature and provides an '.
'incomplete backup; private account '.
'information like email and IM addresses is not backed up. '.
'Additionally, uploaded files and direct messages are not '.
'backed up.');
$this->out->elementStart('p');
$this->out->raw($msg);
$this->out->elementEnd('p');
}
/**
* Buttons for the form
*
* In this case, a single submit button
*
* @return void
*/
function formActions()
{
$this->out->submit('submit',
// TRANS: Submit button to backup an account on the backup account page.
_m('BUTTON', 'Backup'),
'submit',
null,
// TRANS: Title for submit button to backup an account on the backup account page.
_('Backup your account.'));
}
}

View File

@@ -43,23 +43,17 @@ class CancelsubscriptionAction extends FormAction
{ {
protected $needPost = true; protected $needPost = true;
protected function prepare(array $args=array()) protected function doPreparation()
{ {
parent::prepare($args);
$profile_id = $this->int('unsubscribeto'); $profile_id = $this->int('unsubscribeto');
$this->target = Profile::getKV('id', $profile_id); $this->target = Profile::getKV('id', $profile_id);
if (!$this->target instanceof Profile) { if (!$this->target instanceof Profile) {
throw new NoProfileException($profile_id); throw new NoProfileException($profile_id);
} }
return true;
} }
protected function handlePost() protected function doPost()
{ {
parent::handlePost();
try { try {
$request = Subscription_queue::pkeyGet(array('subscriber' => $this->scoped->id, $request = Subscription_queue::pkeyGet(array('subscriber' => $this->scoped->id,
'subscribed' => $this->target->id)); 'subscribed' => $this->target->id));
@@ -70,7 +64,7 @@ class CancelsubscriptionAction extends FormAction
common_debug('Tried to cancel a non-existing pending subscription'); common_debug('Tried to cancel a non-existing pending subscription');
} }
if (StatusNet::isAjax()) { if (GNUsocial::isAjax()) {
$this->startHTML('text/xml;charset=utf-8'); $this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head'); $this->elementStart('head');
// TRANS: Title after unsubscribing from a group. // TRANS: Title after unsubscribing from a group.
@@ -82,10 +76,7 @@ class CancelsubscriptionAction extends FormAction
$this->elementEnd('body'); $this->elementEnd('body');
$this->endHTML(); $this->endHTML();
exit(); exit();
} else { }
common_redirect(common_local_url('subscriptions', common_redirect(common_local_url('subscriptions', array('nickname' => $this->scoped->getNickname())), 303);
array('nickname' => $this->scoped->nickname)),
303);
}
} }
} }

View File

@@ -49,24 +49,9 @@ class ConversationAction extends ManagedAction
var $page = null; var $page = null;
var $notices = null; var $notices = null;
/** protected function doPreparation()
* Initialization.
*
* @param array $args Web and URL arguments
*
* @return boolean false if id not passed in
*/
protected function prepare(array $args=array())
{ {
parent::prepare($args); $this->conv = Conversation::getByID($this->int('id'));
$convId = $this->int('id');
$this->conv = Conversation::getKV('id', $convId);
if (!$this->conv instanceof Conversation) {
throw new ClientException('Could not find specified conversation');
}
return true;
} }
/** /**
@@ -90,14 +75,14 @@ class ConversationAction extends ManagedAction
function showContent() function showContent()
{ {
if (Event::handle('StartShowConversation', array($this, $this->conv, $this->scoped))) { if (Event::handle('StartShowConversation', array($this, $this->conv, $this->scoped))) {
$notices = $this->conv->getNotices(); $notices = $this->conv->getNotices($this->scoped);
$nl = new FullThreadedNoticeList($notices, $this, $this->scoped); $nl = new FullThreadedNoticeList($notices, $this, $this->scoped);
$cnt = $nl->show(); $cnt = $nl->show();
} }
Event::handle('EndShowConversation', array($this, $this->conv, $this->scoped)); Event::handle('EndShowConversation', array($this, $this->conv, $this->scoped));
} }
function isReadOnly() function isReadOnly($args)
{ {
return true; return true;
} }
@@ -108,7 +93,7 @@ class ConversationAction extends ManagedAction
return array(new Feed(Feed::JSON, return array(new Feed(Feed::JSON,
common_local_url('apiconversation', common_local_url('apiconversation',
array( array(
'id' => $this->conv->id, 'id' => $this->conv->getID(),
'format' => 'as')), 'format' => 'as')),
// TRANS: Title for link to notice feed. // TRANS: Title for link to notice feed.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
@@ -116,7 +101,7 @@ class ConversationAction extends ManagedAction
new Feed(Feed::RSS2, new Feed(Feed::RSS2,
common_local_url('apiconversation', common_local_url('apiconversation',
array( array(
'id' => $this->conv->id, 'id' => $this->conv->getID(),
'format' => 'rss')), 'format' => 'rss')),
// TRANS: Title for link to notice feed. // TRANS: Title for link to notice feed.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
@@ -124,11 +109,11 @@ class ConversationAction extends ManagedAction
new Feed(Feed::ATOM, new Feed(Feed::ATOM,
common_local_url('apiconversation', common_local_url('apiconversation',
array( array(
'id' => $this->conv->id, 'id' => $this->conv->getID(),
'format' => 'atom')), 'format' => 'atom')),
// TRANS: Title for link to notice feed. // TRANS: Title for link to notice feed.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
_('Conversation feed (Activity Streams JSON)'))); _('Conversation feed (Atom)')));
} }
} }

View File

@@ -28,80 +28,24 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
// @todo FIXME: documentation needed. // @todo FIXME: documentation needed.
class DeletenoticeAction extends Action class DeletenoticeAction extends FormAction
{ {
var $error = null; protected $notice = null;
var $user = null;
var $notice = null;
var $profile = null;
var $user_profile = null;
function prepare($args) protected function doPreparation()
{ {
parent::prepare($args); $this->notice = Notice::getByID($this->trimmed('notice'));
$this->user = common_current_user(); if (!$this->scoped->sameAs($this->notice->getProfile()) &&
!$this->scoped->hasRight(Right::DELETEOTHERSNOTICE)) {
if (!$this->user) {
// TRANS: Error message displayed when trying to perform an action that requires a logged in user.
common_user_error(_('Not logged in.'));
exit;
}
$notice_id = $this->trimmed('notice');
$this->notice = Notice::getKV($notice_id);
if (!$this->notice) {
// TRANS: Error message displayed trying to delete a non-existing notice.
common_user_error(_('No such notice.'));
exit;
}
$this->profile = $this->notice->getProfile();
$this->user_profile = $this->user->getProfile();
return true;
}
function handle($args)
{
parent::handle($args);
if ($this->notice->profile_id != $this->user_profile->id &&
!$this->user->hasRight(Right::DELETEOTHERSNOTICE)) {
// TRANS: Error message displayed trying to delete a notice that was not made by the current user. // TRANS: Error message displayed trying to delete a notice that was not made by the current user.
common_user_error(_('Cannot delete this notice.')); $this->clientError(_('Cannot delete this notice.'));
exit;
}
// XXX: Ajax!
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->deleteNotice();
} else if ($_SERVER['REQUEST_METHOD'] == 'GET') {
$this->showForm();
}
} }
/** $this->formOpts['notice'] = $this->notice;
* 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');
} }
function getInstructions() function getInstructions()
@@ -117,84 +61,15 @@ class DeletenoticeAction extends Action
return _('Delete notice'); return _('Delete notice');
} }
/** protected function doPost()
* Wrapper for showing a page
*
* Stores an error and shows the page
*
* @param string $error Error, if any
*
* @return void
*/
function showForm($error = null)
{ {
$this->error = $error;
$this->showPage();
}
/**
* Insert delete notice form into the content
*
* @return void
*/
function showContent()
{
$this->elementStart('form', array('id' => 'form_notice_delete',
'class' => 'form_settings',
'method' => 'post',
'action' => common_local_url('deletenotice')));
$this->elementStart('fieldset');
// TRANS: Fieldset legend for the delete notice form.
$this->element('legend', null, _('Delete notice'));
$this->hidden('token', common_session_token());
$this->hidden('notice', $this->trimmed('notice'));
// TRANS: Message for the delete notice form.
$this->element('p', null, _('Are you sure you want to delete this notice?'));
$this->submit('form_action-no',
// TRANS: Button label on the delete notice form.
_m('BUTTON','No'),
'submit form_action-primary',
'no',
// TRANS: Submit button title for 'No' when deleting a notice.
_('Do not delete this notice.'));
$this->submit('form_action-yes',
// TRANS: Button label on the delete notice form.
_m('BUTTON','Yes'),
'submit form_action-secondary',
'yes',
// TRANS: Submit button title for 'Yes' when deleting a notice.
_('Delete this notice.'));
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
function deleteNotice()
{
// 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(_('There was a problem with your session token. ' .
'Try again, please.'));
return;
}
if ($this->arg('yes')) { if ($this->arg('yes')) {
if (Event::handle('StartDeleteOwnNotice', array($this->user, $this->notice))) { if (Event::handle('StartDeleteOwnNotice', array($this->scoped->getUser(), $this->notice))) {
$this->notice->delete(); $this->notice->deleteAs($this->scoped);
Event::handle('EndDeleteOwnNotice', array($this->user, $this->notice)); Event::handle('EndDeleteOwnNotice', array($this->scoped->getUser(), $this->notice));
} }
} }
$url = common_get_returnto(); common_redirect(common_get_returnto(), 303);
if ($url) {
common_set_returnto(null);
} else {
$url = common_local_url('public');
}
common_redirect($url, 303);
} }
} }

View File

@@ -27,9 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Delete a user * Delete a user
@@ -44,33 +42,30 @@ class DeleteuserAction extends ProfileFormAction
{ {
var $user = null; var $user = null;
/** function prepare(array $args=array())
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
function prepare($args)
{ {
if (!parent::prepare($args)) { if (!parent::prepare($args)) {
return false; return false;
} }
$cur = common_current_user(); assert($this->scoped instanceof Profile);
assert(!empty($cur)); // checked by parent if (!$this->scoped->hasRight(Right::DELETEUSER)) {
if (!$cur->hasRight(Right::DELETEUSER)) {
// TRANS: Client error displayed when trying to delete a user without having the right to delete users. // TRANS: Client error displayed when trying to delete a user without having the right to delete users.
$this->clientError(_('You cannot delete users.')); throw new AuthorizationException(_('You cannot delete users.'));
} }
$this->user = User::getKV('id', $this->profile->id); try {
$this->user = $this->profile->getUser();
if (empty($this->user)) { } catch (NoSuchUserException $e) {
// TRANS: Client error displayed when trying to delete a non-local user. // TRANS: Client error displayed when trying to delete a non-local user.
$this->clientError(_('You can only delete local users.')); throw new ClientException(_('You can only delete local users.'));
}
// Only administrators can delete other privileged users (such as others who have the right to silence).
if ($this->profile->isPrivileged() && !$this->scoped->hasRole(Profile_role::ADMINISTRATOR)) {
// TRANS: Client error displayed when trying to delete a user that has been granted moderation privileges
throw new AuthorizationException(_('You cannot delete other privileged users.'));
} }
return true; return true;

View File

@@ -28,9 +28,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Documentation class. * Documentation class.
@@ -42,16 +40,14 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/ * @link http://status.net/
*/ */
class DocAction extends Action class DocAction extends ManagedAction
{ {
var $output = null; var $output = null;
var $filename = null; var $filename = null;
var $title = null; var $title = null;
function prepare($args) protected function doPreparation()
{ {
parent::prepare($args);
$this->title = $this->trimmed('title'); $this->title = $this->trimmed('title');
if (!preg_match('/^[a-zA-Z0-9_-]*$/', $this->title)) { if (!preg_match('/^[a-zA-Z0-9_-]*$/', $this->title)) {
$this->title = 'help'; $this->title = 'help';
@@ -59,52 +55,11 @@ class DocAction extends Action
$this->output = null; $this->output = null;
$this->loadDoc(); $this->loadDoc();
return true;
} }
/** public function title()
* Handle a request
*
* @param array $args array of arguments
*
* @return nothing
*/
function handle($args)
{ {
parent::handle($args); return ucfirst($this->title);
$this->showPage();
}
/**
* Page title
*
* Gives the page title of the document. Override default for hAtom entry.
*
* @return void
*/
function showPageTitle()
{
$this->element('h1', array('class' => 'entry-title'), $this->title());
}
/**
* Block for content.
*
* Overrides default from Action to wrap everything in an hAtom entry.
*
* @return void.
*/
function showContentBlock()
{
$this->elementStart('div', array('id' => 'content', 'class' => 'h-entry'));
$this->showPageTitle();
$this->showPageNoticeBlock();
$this->elementStart('div', array('id' => 'content_inner',
'class' => 'e-content'));
// show the actual content (forms, lists, whatever)
$this->showContent();
$this->elementEnd('div');
$this->elementEnd('div');
} }
/** /**
@@ -119,16 +74,9 @@ class DocAction extends Action
$this->raw($this->output); $this->raw($this->output);
} }
/** function showNoticeForm()
* Page title.
*
* Uses the title of the document.
*
* @return page title
*/
function title()
{ {
return ucfirst($this->title); // no notice form
} }
/** /**
@@ -174,11 +122,15 @@ class DocNav extends Menu
{ {
function show() function show()
{ {
if (Event::handle('StartDocNav', array($this))) {
$stub = new HomeStubNav($this->action); $stub = new HomeStubNav($this->action);
$this->submenu(_m('MENU','Home'), $stub); $this->submenu(_m('MENU','Home'), $stub);
$docs = new DocListNav($this->action); $docs = new DocListNav($this->action);
$this->submenu(_m('MENU','Docs'), $docs); $this->submenu(_m('MENU','Docs'), $docs);
Event::handle('EndDocNav', array($this));
}
} }
} }

View File

@@ -28,11 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Settings for email * Settings for email
@@ -91,7 +87,7 @@ class EmailsettingsAction extends SettingsAction
*/ */
function showContent() function showContent()
{ {
$user = common_current_user(); $user = $this->scoped->getUser();
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_email', 'id' => 'form_settings_email',
@@ -112,8 +108,8 @@ class EmailsettingsAction extends SettingsAction
// TRANS: Button label to remove a confirmed e-mail address. // TRANS: Button label to remove a confirmed e-mail address.
$this->submit('remove', _m('BUTTON','Remove')); $this->submit('remove', _m('BUTTON','Remove'));
} else { } else {
try {
$confirm = $this->getConfirmation(); $confirm = $this->getConfirmation();
if ($confirm) {
$this->element('p', array('id' => 'form_unconfirmed'), $confirm->address); $this->element('p', array('id' => 'form_unconfirmed'), $confirm->address);
$this->element('p', array('class' => 'form_note'), $this->element('p', array('class' => 'form_note'),
// TRANS: Form note in e-mail settings form. // TRANS: Form note in e-mail settings form.
@@ -123,12 +119,12 @@ class EmailsettingsAction extends SettingsAction
$this->hidden('email', $confirm->address); $this->hidden('email', $confirm->address);
// TRANS: Button label to cancel an e-mail address confirmation procedure. // TRANS: Button label to cancel an e-mail address confirmation procedure.
$this->submit('cancel', _m('BUTTON','Cancel')); $this->submit('cancel', _m('BUTTON','Cancel'));
} else { } catch (NoResultException $e) {
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label for e-mail address input in e-mail settings form. // TRANS: Field label for e-mail address input in e-mail settings form.
$this->input('email', _('Email address'), $this->input('email', _('Email address'),
($this->arg('email')) ? $this->arg('email') : null, $this->trimmed('email') ?: null,
// TRANS: Instructions for e-mail address input form. Do not translate // TRANS: Instructions for e-mail address input form. Do not translate
// TRANS: "example.org". It is one of the domain names reserved for // TRANS: "example.org". It is one of the domain names reserved for
// TRANS: use in examples by http://www.rfc-editor.org/rfc/rfc2606.txt. // TRANS: use in examples by http://www.rfc-editor.org/rfc/rfc2606.txt.
@@ -231,12 +227,6 @@ class EmailsettingsAction extends SettingsAction
_('Allow friends to nudge me and send me an email.'), _('Allow friends to nudge me and send me an email.'),
$user->emailnotifynudge); $user->emailnotifynudge);
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li');
$this->checkbox('emailmicroid',
// TRANS: Checkbox label in e-mail preferences form.
_('Publish a MicroID for my email address.'),
$user->emailmicroid);
$this->elementEnd('li');
Event::handle('EndEmailFormData', array($this, $this->scoped)); Event::handle('EndEmailFormData', array($this, $this->scoped));
} }
$this->elementEnd('ul'); $this->elementEnd('ul');
@@ -254,56 +244,36 @@ class EmailsettingsAction extends SettingsAction
*/ */
function getConfirmation() function getConfirmation()
{ {
$user = common_current_user();
$confirm = new Confirm_address(); $confirm = new Confirm_address();
$confirm->user_id = $user->id; $confirm->user_id = $this->scoped->getID();
$confirm->address_type = 'email'; $confirm->address_type = 'email';
if ($confirm->find(true)) { if ($confirm->find(true)) {
return $confirm; return $confirm;
} else {
return null;
}
} }
/** throw new NoResultException($confirm);
* Handle posts }
*
* Since there are a lot of different options on the page, we protected function doPost()
* figure out what we're supposed to do based on which button was
* pushed
*
* @return void
*/
function handlePost()
{ {
// CSRF protection if ($this->arg('save')) {
$token = $this->trimmed('token'); return $this->savePreferences();
if (!$token || $token != common_session_token()) { } else if ($this->arg('add')) {
// TRANS: Client error displayed when the session token does not match or is not given. return $this->addAddress();
$this->show_form(_('There was a problem with your session token. '. } else if ($this->arg('cancel')) {
'Try again, please.')); return $this->cancelConfirmation();
return; } else if ($this->arg('remove')) {
return $this->removeAddress();
} else if ($this->arg('removeincoming')) {
return $this->removeIncoming();
} else if ($this->arg('newincoming')) {
return $this->newIncoming();
} }
if ($this->arg('save')) {
$this->savePreferences();
} else if ($this->arg('add')) {
$this->addAddress();
} else if ($this->arg('cancel')) {
$this->cancelConfirmation();
} else if ($this->arg('remove')) {
$this->removeAddress();
} else if ($this->arg('removeincoming')) {
$this->removeIncoming();
} else if ($this->arg('newincoming')) {
$this->newIncoming();
} else {
// TRANS: Message given submitting a form with an unknown action in e-mail settings. // TRANS: Message given submitting a form with an unknown action in e-mail settings.
$this->showForm(_('Unexpected form submission.')); throw new ClientException(_('Unexpected form submission.'));
}
} }
/** /**
@@ -313,44 +283,38 @@ class EmailsettingsAction extends SettingsAction
*/ */
function savePreferences() function savePreferences()
{ {
$user = common_current_user();
if (Event::handle('StartEmailSaveForm', array($this, $this->scoped))) { if (Event::handle('StartEmailSaveForm', array($this, $this->scoped))) {
$emailnotifysub = $this->boolean('emailnotifysub'); $emailnotifysub = $this->booleanintstring('emailnotifysub');
$emailnotifymsg = $this->boolean('emailnotifymsg'); $emailnotifymsg = $this->booleanintstring('emailnotifymsg');
$emailnotifynudge = $this->boolean('emailnotifynudge'); $emailnotifynudge = $this->booleanintstring('emailnotifynudge');
$emailnotifyattn = $this->boolean('emailnotifyattn'); $emailnotifyattn = $this->booleanintstring('emailnotifyattn');
$emailmicroid = $this->boolean('emailmicroid'); $emailpost = $this->booleanintstring('emailpost');
$emailpost = $this->boolean('emailpost');
assert(!is_null($user)); // should already be checked
$user = $this->scoped->getUser();
$user->query('BEGIN'); $user->query('BEGIN');
$original = clone($user); $original = clone($user);
$user->emailnotifysub = $emailnotifysub; $user->emailnotifysub = $emailnotifysub;
$user->emailnotifymsg = $emailnotifymsg; $user->emailnotifymsg = $emailnotifymsg;
$user->emailnotifynudge = $emailnotifynudge; $user->emailnotifynudge = $emailnotifynudge;
$user->emailnotifyattn = $emailnotifyattn; $user->emailnotifyattn = $emailnotifyattn;
$user->emailmicroid = $emailmicroid;
$user->emailpost = $emailpost; $user->emailpost = $emailpost;
$result = $user->update($original); $result = $user->update($original);
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);
$user->query('ROLLBACK');
// TRANS: Server error thrown on database error updating e-mail preferences. // TRANS: Server error thrown on database error updating e-mail preferences.
$this->serverError(_('Could not update user.')); throw new ServerException(_('Could not update user.'));
} }
$user->query('COMMIT'); $user->query('COMMIT');
Event::handle('EndEmailSaveForm', array($this, $this->scoped)); Event::handle('EndEmailSaveForm', array($this, $this->scoped));
// TRANS: Confirmation message for successful e-mail preferences save.
$this->showForm(_('Email preferences saved.'), true);
} }
// TRANS: Confirmation message for successful e-mail preferences save.
return _('Email preferences saved.');
} }
/** /**
@@ -360,38 +324,32 @@ class EmailsettingsAction extends SettingsAction
*/ */
function addAddress() function addAddress()
{ {
$user = common_current_user(); $user = $this->scoped->getUser();
$email = $this->trimmed('email'); $email = $this->trimmed('email');
// Some validation // Some validation
if (!$email) { if (empty($email)) {
// TRANS: Message given saving e-mail address without having provided one. // TRANS: Message given saving e-mail address without having provided one.
$this->showForm(_('No email address.')); throw new ClientException(_('No email address.'));
return;
} }
$email = common_canonical_email($email); $email = common_canonical_email($email);
if (!$email) { if (empty($email)) {
// TRANS: Message given saving e-mail address that cannot be normalised. // TRANS: Message given saving e-mail address that cannot be normalised.
$this->showForm(_('Cannot normalize that email address.')); throw new ClientException(_('Cannot normalize that email address.'));
return;
} }
if (!Validate::email($email, common_config('email', 'check_domain'))) { if (!Validate::email($email, common_config('email', 'check_domain'))) {
// TRANS: Message given saving e-mail address that not valid. // TRANS: Message given saving e-mail address that not valid.
$this->showForm(_('Not a valid email address.')); throw new ClientException(_('Not a valid email address.'));
return;
} else if ($user->email == $email) { } else if ($user->email == $email) {
// TRANS: Message given saving e-mail address that is already set. // TRANS: Message given saving e-mail address that is already set.
$this->showForm(_('That is already your email address.')); throw new ClientException(_('That is already your email address.'));
return;
} else if ($this->emailExists($email)) { } else if ($this->emailExists($email)) {
// TRANS: Message given saving e-mail address that is already set for another user. // TRANS: Message given saving e-mail address that is already set for another user.
$this->showForm(_('That email address already belongs '. throw new ClientException(_('That email address already belongs to another user.'));
'to another user.'));
return;
} }
if (Event::handle('StartAddEmailAddress', array($user, $email))) { if (Event::handle('StartAddEmailAddress', array($user, $email))) {
@@ -400,7 +358,7 @@ class EmailsettingsAction extends SettingsAction
$confirm->address = $email; $confirm->address = $email;
$confirm->address_type = 'email'; $confirm->address_type = 'email';
$confirm->user_id = $user->id; $confirm->user_id = $user->getID();
$confirm->code = common_confirmation_code(64); $confirm->code = common_confirmation_code(64);
$result = $confirm->insert(); $result = $confirm->insert();
@@ -408,20 +366,19 @@ class EmailsettingsAction extends SettingsAction
if ($result === false) { if ($result === false) {
common_log_db_error($confirm, 'INSERT', __FILE__); common_log_db_error($confirm, 'INSERT', __FILE__);
// TRANS: Server error thrown on database error adding e-mail confirmation code. // TRANS: Server error thrown on database error adding e-mail confirmation code.
$this->serverError(_('Could not insert confirmation code.')); throw new ServerException(_('Could not insert confirmation code.'));
} }
mail_confirm_address($user, $confirm->code, $user->nickname, $email); common_debug('Sending confirmation address for user '.$user->getID().' to email '.$email);
mail_confirm_address($user, $confirm->code, $user->getNickname(), $email);
Event::handle('EndAddEmailAddress', array($user, $email)); Event::handle('EndAddEmailAddress', array($user, $email));
} }
// TRANS: Message given saving valid e-mail address that is to be confirmed. // TRANS: Message given saving valid e-mail address that is to be confirmed.
$msg = _('A confirmation code was sent to the email address you added. '. return _('A confirmation code was sent to the email address you added. '.
'Check your inbox (and spam box!) for the code and instructions '. 'Check your inbox (and spam box!) for the code and instructions '.
'on how to use it.'); 'on how to use it.');
$this->showForm($msg, true);
} }
/** /**
@@ -431,31 +388,29 @@ class EmailsettingsAction extends SettingsAction
*/ */
function cancelConfirmation() function cancelConfirmation()
{ {
$email = $this->arg('email'); $email = $this->trimmed('email');
try {
$confirm = $this->getConfirmation(); $confirm = $this->getConfirmation();
if ($confirm->address !== $email) {
if (!$confirm) {
// TRANS: Message given canceling e-mail address confirmation that is not pending.
$this->showForm(_('No pending confirmation to cancel.'));
return;
}
if ($confirm->address != $email) {
// TRANS: Message given canceling e-mail address confirmation for the wrong e-mail address. // TRANS: Message given canceling e-mail address confirmation for the wrong e-mail address.
$this->showForm(_('That is the wrong email address.')); throw new ClientException(_('That is the wrong email address.'));
return; }
} catch (NoResultException $e) {
// TRANS: Message given canceling e-mail address confirmation that is not pending.
throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
} }
$result = $confirm->delete(); $result = $confirm->delete();
if (!$result) { if ($result === false) {
common_log_db_error($confirm, 'DELETE', __FILE__); common_log_db_error($confirm, 'DELETE', __FILE__);
// TRANS: Server error thrown on database error canceling e-mail address confirmation. // TRANS: Server error thrown on database error canceling e-mail address confirmation.
$this->serverError(_('Could not delete email confirmation.')); throw new ServerException(_('Could not delete email confirmation.'));
} }
// TRANS: Message given after successfully canceling e-mail address confirmation. // TRANS: Message given after successfully canceling e-mail address confirmation.
$this->showForm(_('Email confirmation cancelled.'), true); return _('Email confirmation cancelled.');
} }
/** /**
@@ -467,26 +422,22 @@ class EmailsettingsAction extends SettingsAction
{ {
$user = common_current_user(); $user = common_current_user();
$email = $this->arg('email'); $email = $this->trimmed('email');
// Maybe an old tab open...? // Maybe an old tab open...?
if ($user->email !== $email) {
if ($user->email != $email) {
// TRANS: Message given trying to remove an e-mail address that is not // TRANS: Message given trying to remove an e-mail address that is not
// TRANS: registered for the active user. // TRANS: registered for the active user.
$this->showForm(_('That is not your email address.')); throw new ClientException(_('That is not your email address.'));
return;
} }
$original = clone($user); $original = clone($user);
$user->email = null; $user->email = null;
// Throws exception on failure. Also performs it within a transaction. // Throws exception on failure. Also performs it within a transaction.
$user->updateWithKeys($original); $user->updateWithKeys($original);
// TRANS: Message given after successfully removing a registered e-mail address. // TRANS: Message given after successfully removing a registered e-mail address.
$this->showForm(_('The email address was removed.'), true); return _('The email address was removed.');
} }
/** /**
@@ -498,22 +449,19 @@ class EmailsettingsAction extends SettingsAction
{ {
$user = common_current_user(); $user = common_current_user();
if (!$user->incomingemail) { if (empty($user->incomingemail)) {
// TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set. // TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set.
$this->showForm(_('No incoming email address.')); throw new AlreadyFulfilledException(_('No incoming email address.'));
return;
} }
$orig = clone($user); $orig = clone($user);
$user->incomingemail = null; $user->incomingemail = null;
$user->emailpost = 0; $user->emailpost = 0;
// Throws exception on failure. Also performs it within a transaction. // Throws exception on failure. Also performs it within a transaction.
$user->updateWithKeys($orig); $user->updateWithKeys($orig);
// TRANS: Message given after successfully removing an incoming e-mail address. // TRANS: Message given after successfully removing an incoming e-mail address.
$this->showForm(_('Incoming email address removed.'), true); return _('Incoming email address removed.');
} }
/** /**
@@ -524,17 +472,14 @@ class EmailsettingsAction extends SettingsAction
function newIncoming() function newIncoming()
{ {
$user = common_current_user(); $user = common_current_user();
$orig = clone($user); $orig = clone($user);
$user->incomingemail = mail_new_incoming_address(); $user->incomingemail = mail_new_incoming_address();
$user->emailpost = 1; $user->emailpost = 1;
// Throws exception on failure. Also performs it within a transaction. // Throws exception on failure. Also performs it within a transaction.
$user->updateWithKeys($orig); $user->updateWithKeys($orig);
// TRANS: Message given after successfully adding an incoming e-mail address. // TRANS: Message given after successfully adding an incoming e-mail address.
$this->showForm(_('New incoming email address added.'), true); return _('New incoming email address added.');
} }
/** /**
@@ -553,10 +498,10 @@ class EmailsettingsAction extends SettingsAction
$other = User::getKV('email', $email); $other = User::getKV('email', $email);
if (!$other) { if (!$other instanceof User) {
return false; return false;
} else { }
return $other->id != $user->id; return $other->id != $user->id;
} }
} }
}

View File

@@ -17,24 +17,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
define('LISTENER', 1); define('LISTENER', 1);
define('LISTENEE', -1); define('LISTENEE', -1);
define('BOTH', 0); define('BOTH', 0);
// @todo XXX: Documentation missing. // @todo XXX: Documentation missing.
class FoafAction extends Action class FoafAction extends ManagedAction
{ {
function isReadOnly($args) function isReadOnly($args)
{ {
return true; return true;
} }
function prepare($args) protected function doPreparation()
{ {
parent::prepare($args);
$nickname_arg = $this->arg('nickname'); $nickname_arg = $this->arg('nickname');
if (empty($nickname_arg)) { if (empty($nickname_arg)) {
@@ -69,10 +67,8 @@ class FoafAction extends Action
return true; return true;
} }
function handle($args) public function showPage()
{ {
parent::handle($args);
header('Content-Type: application/rdf+xml'); header('Content-Type: application/rdf+xml');
$this->startXML(); $this->startXML();
@@ -94,7 +90,7 @@ class FoafAction extends Action
// Would be nice to tell if they were a Person or not (e.g. a #person usertag?) // Would be nice to tell if they were a Person or not (e.g. a #person usertag?)
$this->elementStart('Agent', array('rdf:about' => $this->user->getUri())); $this->elementStart('Agent', array('rdf:about' => $this->user->getUri()));
if ($this->user->email) { if (common_config('foaf', 'mbox_sha1sum') && $this->user->email) {
$this->element('mbox_sha1sum', null, sha1('mailto:' . $this->user->email)); $this->element('mbox_sha1sum', null, sha1('mailto:' . $this->user->email));
} }
if ($this->profile->fullname) { if ($this->profile->fullname) {

View File

@@ -28,12 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/noticelist.php';
require_once INSTALLDIR.'/lib/feedlist.php';
/** /**
* Permalink for a group * Permalink for a group
@@ -47,53 +42,22 @@ require_once INSTALLDIR.'/lib/feedlist.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @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://status.net/
*/ */
class GroupbyidAction extends Action class GroupbyidAction extends ManagedAction
{ {
/** group we're viewing. */ /** group we're viewing. */
var $group = null; protected $group = null;
/**
* Is this page read-only?
*
* @return boolean true
*/
function isReadOnly($args) function isReadOnly($args)
{ {
return true; return true;
} }
function prepare($args) protected function doPreparation()
{ {
parent::prepare($args); $this->group = User_group::getByID($this->arg('id'));
$id = $this->arg('id');
if (!$id) {
// TRANS: Client error displayed referring to a group's permalink without providing a group ID.
$this->clientError(_('No ID.'));
} }
common_debug("Got ID $id"); public function showPage()
$this->group = User_group::getKV('id', $id);
if (!$this->group) {
// TRANS: Client error displayed referring to a group's permalink for a non-existing group ID.
$this->clientError(_('No such group.'), 404);
}
return true;
}
/**
* Handle the request
*
* Shows a profile for the group, some controls, and a list of
* group notices.
*
* @return void
*/
function handle($args)
{ {
common_redirect($this->group->homeUrl(), 303); common_redirect($this->group->homeUrl(), 303);
} }

View File

@@ -28,13 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
define('MAX_ORIGINAL', 480);
/** /**
* Upload an avatar * Upload an avatar
@@ -390,13 +384,20 @@ class GrouplogoAction extends GroupAction
$dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0; $dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0;
$dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$filedata['width']; $dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$filedata['width'];
$dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$filedata['height']; $dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$filedata['height'];
$size = min($dest_w, $dest_h); $size = min($dest_w, $dest_h, common_config('avatar', 'maxsize'));
$size = ($size > MAX_ORIGINAL) ? MAX_ORIGINAL:$size; $box = array('width' => $size, 'height' => $size,
'x' => $dest_x, 'y' => $dest_y,
'w' => $dest_w, 'h' => $dest_h);
$imagefile = new ImageFile($this->group->id, $filedata['filepath']); $profile = $this->group->getProfile();
$filename = $imagefile->resize($size, $dest_x, $dest_y, $dest_w, $dest_h);
if ($this->group->setOriginal($filename)) { $imagefile = new ImageFile(null, $filedata['filepath']);
$filename = Avatar::filename($profile->getID(), image_type_to_extension($imagefile->preferredType()),
$size, common_timestamp());
$imagefile->resizeTo(Avatar::path($filename), $box);
if ($profile->setOriginal($filename)) {
@unlink($filedata['filepath']); @unlink($filedata['filepath']);
unset($_SESSION['FILEDATA']); unset($_SESSION['FILEDATA']);
$this->mode = 'upload'; $this->mode = 'upload';

View File

@@ -28,11 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/rssaction.php';
define('MEMBERS_PER_SECTION', 27); define('MEMBERS_PER_SECTION', 27);
@@ -45,10 +41,10 @@ define('MEMBERS_PER_SECTION', 27);
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @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://status.net/
*/ */
class groupRssAction extends Rss10Action class GroupRssAction extends TargetedRss10Action
{ {
/** group we're viewing. */ /** group we're viewing. */
var $group = null; protected $group = null;
/** /**
* Is this page read-only? * Is this page read-only?
@@ -60,18 +56,8 @@ class groupRssAction extends Rss10Action
return true; return true;
} }
/** protected function doStreamPreparation()
* Prepare the action
*
* Reads and validates arguments and instantiates the attributes.
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
function prepare($args)
{ {
parent::prepare($args);
$nickname_arg = $this->arg('nickname'); $nickname_arg = $this->arg('nickname');
$nickname = common_canonical_nickname($nickname_arg); $nickname = common_canonical_nickname($nickname_arg);
@@ -90,52 +76,32 @@ class groupRssAction extends Rss10Action
$local = Local_group::getKV('nickname', $nickname); $local = Local_group::getKV('nickname', $nickname);
if (!$local) { if (!$local instanceof Local_group) {
// TRANS: Client error displayed when requesting a group RSS feed for group that does not exist. // TRANS: Client error displayed when requesting a group RSS feed for group that does not exist.
$this->clientError(_('No such group.'), 404); $this->clientError(_('No such group.'), 404);
} }
$this->group = User_group::getKV('id', $local->group_id); $this->group = $local->getGroup();
$this->target = $this->group->getProfile();
if (!$this->group) {
// TRANS: Client error displayed when requesting a group RSS feed for an object that is not a group.
$this->clientError(_('No such group.'), 404);
} }
$this->notices = $this->getNotices($this->limit); protected function getNotices()
return true;
}
function getNotices($limit=0)
{ {
$group = $this->group; $stream = $this->group->getNotices(0, $this->limit);
return $stream->fetchAll();
if (is_null($group)) {
return null;
}
$notices = array();
$notice = $group->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
} }
function getChannel() function getChannel()
{ {
$group = $this->group;
$c = array('url' => common_local_url('grouprss', $c = array('url' => common_local_url('grouprss',
array('nickname' => array('nickname' =>
$group->nickname)), $this->target->getNickname())),
// TRANS: Message is used as link title. %s is a user nickname. // TRANS: Message is used as link title. %s is a user nickname.
'title' => sprintf(_('%s timeline'), $group->nickname), 'title' => sprintf(_('%s timeline'), $this->target->getNickname()),
'link' => common_local_url('showgroup', array('nickname' => $group->nickname)), 'link' => common_local_url('showgroup', array('nickname' => $this->target->getNickname())),
// TRANS: Message is used as link description. %1$s is a group name, %2$s is a site name. // TRANS: Message is used as link description. %1$s is a group name, %2$s is a site name.
'description' => sprintf(_('Updates from members of %1$s on %2$s!'), 'description' => sprintf(_('Updates from members of %1$s on %2$s!'),
$group->nickname, common_config('site', 'name'))); $this->target->getNickname(), common_config('site', 'name')));
return $c; return $c;
} }

View File

@@ -27,9 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Settings for Jabber/XMPP integration * Settings for Jabber/XMPP integration
@@ -118,8 +116,8 @@ class ImsettingsAction extends SettingsAction
// TRANS: Button label to remove a confirmed IM address. // TRANS: Button label to remove a confirmed IM address.
$this->submit('remove', _m('BUTTON','Remove')); $this->submit('remove', _m('BUTTON','Remove'));
} else { } else {
try {
$confirm = $this->getConfirmation($transport); $confirm = $this->getConfirmation($transport);
if ($confirm) {
$this->element('p', 'form_unconfirmed', $confirm->address); $this->element('p', 'form_unconfirmed', $confirm->address);
// TRANS: Form note in IM settings form. // TRANS: Form note in IM settings form.
$this->element('p', 'form_note', $this->element('p', 'form_note',
@@ -134,7 +132,7 @@ class ImsettingsAction extends SettingsAction
$this->hidden('screenname', $confirm->address); $this->hidden('screenname', $confirm->address);
// TRANS: Button label to cancel an IM address confirmation procedure. // TRANS: Button label to cancel an IM address confirmation procedure.
$this->submit('cancel', _m('BUTTON','Cancel')); $this->submit('cancel', _m('BUTTON','Cancel'));
} else { } catch (NoResultException $e) {
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label for IM address. // TRANS: Field label for IM address.
@@ -179,8 +177,6 @@ class ImsettingsAction extends SettingsAction
// TRANS: Checkbox label in IM preferences form. // TRANS: Checkbox label in IM preferences form.
array('name'=>'replies', 'description'=>_('Send me replies '. array('name'=>'replies', 'description'=>_('Send me replies '.
'from people I\'m not subscribed to.')), 'from people I\'m not subscribed to.')),
// TRANS: Checkbox label in IM preferences form.
array('name'=>'microid', 'description'=>_('Publish a MicroID'))
); );
foreach($preferences as $preference) foreach($preferences as $preference)
{ {
@@ -211,57 +207,35 @@ class ImsettingsAction extends SettingsAction
*/ */
function getConfirmation($transport) function getConfirmation($transport)
{ {
$user = common_current_user();
$confirm = new Confirm_address(); $confirm = new Confirm_address();
$confirm->user_id = $user->id; $confirm->user_id = $this->scoped->getID();
$confirm->address_type = $transport; $confirm->address_type = $transport;
if ($confirm->find(true)) { if ($confirm->find(true)) {
return $confirm; return $confirm;
} else {
return null;
}
} }
/** throw new NoResultException($confirm);
* Handle posts to this form }
*
* Based on the button that was pressed, muxes out to other functions protected function doPost()
* 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(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if ($this->arg('save')) { if ($this->arg('save')) {
$this->savePreferences(); return $this->savePreferences();
} else if ($this->arg('add')) { } else if ($this->arg('add')) {
$this->addAddress(); return $this->addAddress();
} else if ($this->arg('cancel')) { } else if ($this->arg('cancel')) {
$this->cancelConfirmation(); return $this->cancelConfirmation();
} else if ($this->arg('remove')) { } else if ($this->arg('remove')) {
$this->removeAddress(); return $this->removeAddress();
} else {
// TRANS: Message given submitting a form with an unknown action in Instant Messaging settings.
$this->showForm(_('Unexpected form submission.'));
} }
// TRANS: Message given submitting a form with an unknown action in Instant Messaging settings.
throw new ClientException(_('Unexpected form submission.'));
} }
/** /**
* Save user's Jabber preferences * Save user's XMPP preferences
* *
* These are the checkboxes at the bottom of the page. They're used to * These are the checkboxes at the bottom of the page. They're used to
* set different settings * set different settings
@@ -270,14 +244,12 @@ class ImsettingsAction extends SettingsAction
*/ */
function savePreferences() function savePreferences()
{ {
$user = common_current_user();
$user_im_prefs = new User_im_prefs(); $user_im_prefs = new User_im_prefs();
$user_im_prefs->query('BEGIN'); $user_im_prefs->query('BEGIN');
$user_im_prefs->user_id = $user->id; $user_im_prefs->user_id = $this->scoped->getID();
if($user_im_prefs->find() && $user_im_prefs->fetch()) if($user_im_prefs->find() && $user_im_prefs->fetch())
{ {
$preferences = array('notify', 'updatefrompresence', 'replies', 'microid'); $preferences = array('notify', 'updatefrompresence', 'replies');
do do
{ {
$original = clone($user_im_prefs); $original = clone($user_im_prefs);
@@ -289,15 +261,15 @@ class ImsettingsAction extends SettingsAction
$result = $new->update($original); $result = $new->update($original);
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user_im_prefs, 'UPDATE', __FILE__);
// TRANS: Server error thrown on database error updating IM preferences. // TRANS: Server error thrown on database error updating IM preferences.
$this->serverError(_('Could not update IM preferences.')); throw new ServerException(_('Could not update IM preferences.'));
} }
}while($user_im_prefs->fetch()); }while($user_im_prefs->fetch());
} }
$user_im_prefs->query('COMMIT'); $user_im_prefs->query('COMMIT');
// TRANS: Confirmation message for successful IM preferences save. // TRANS: Confirmation message for successful IM preferences save.
$this->showForm(_('Preferences saved.'), true); return _('Preferences saved.');
} }
/** /**
@@ -310,49 +282,42 @@ class ImsettingsAction extends SettingsAction
*/ */
function addAddress() function addAddress()
{ {
$user = common_current_user();
$screenname = $this->trimmed('screenname'); $screenname = $this->trimmed('screenname');
$transport = $this->trimmed('transport'); $transport = $this->trimmed('transport');
// Some validation // Some validation
if (!$screenname) { if (empty($screenname)) {
// TRANS: Message given saving IM address without having provided one. // TRANS: Message given saving IM address without having provided one.
$this->showForm(_('No screenname.')); throw new ClientException(_('No screenname.'));
return;
} }
if (!$transport) { if (empty($transport)) {
// TRANS: Form validation error when no transport is available setting an IM address. // TRANS: Form validation error when no transport is available setting an IM address.
$this->showForm(_('No transport.')); throw new ClientException(_('No transport.'));
return;
} }
Event::handle('NormalizeImScreenname', array($transport, &$screenname)); Event::handle('NormalizeImScreenname', array($transport, &$screenname));
if (!$screenname) { if (empty($screenname)) {
// TRANS: Message given saving IM address that cannot be normalised. // TRANS: Message given saving IM address that cannot be normalised.
$this->showForm(_('Cannot normalize that screenname.')); throw new ClientException(_('Cannot normalize that screenname.'));
return;
} }
$valid = false; $valid = false;
Event::handle('ValidateImScreenname', array($transport, $screenname, &$valid)); Event::handle('ValidateImScreenname', array($transport, $screenname, &$valid));
if (!$valid) { if (!$valid) {
// TRANS: Message given saving IM address that not valid. // TRANS: Message given saving IM address that not valid.
$this->showForm(_('Not a valid screenname.')); throw new ClientException(_('Not a valid screenname.'));
return;
} else if ($this->screennameExists($transport, $screenname)) { } else if ($this->screennameExists($transport, $screenname)) {
// TRANS: Message given saving IM address that is already set for another user. // TRANS: Message given saving IM address that is already set for another user.
$this->showForm(_('Screenname already belongs to another user.')); throw new ClientException(_('Screenname already belongs to another user.'));
return;
} }
$confirm = new Confirm_address(); $confirm = new Confirm_address();
$confirm->address = $screenname; $confirm->address = $screenname;
$confirm->address_type = $transport; $confirm->address_type = $transport;
$confirm->user_id = $user->id; $confirm->user_id = $this->scoped->getID();
$confirm->code = common_confirmation_code(64); $confirm->code = common_confirmation_code(64);
$confirm->sent = common_sql_now(); $confirm->sent = common_sql_now();
$confirm->claimed = common_sql_now(); $confirm->claimed = common_sql_now();
@@ -365,13 +330,10 @@ class ImsettingsAction extends SettingsAction
$this->serverError(_('Could not insert confirmation code.')); $this->serverError(_('Could not insert confirmation code.'));
} }
Event::handle('SendImConfirmationCode', array($transport, $screenname, $confirm->code, $user)); Event::handle('SendImConfirmationCode', array($transport, $screenname, $confirm->code, $this->scoped));
// TRANS: Message given saving valid IM address that is to be confirmed. // TRANS: Message given saving valid IM address that is to be confirmed.
$msg = _('A confirmation code was sent '. return _('A confirmation code was sent to the IM address you added.');
'to the IM address you added.');
$this->showForm($msg, true);
} }
/** /**
@@ -386,29 +348,27 @@ class ImsettingsAction extends SettingsAction
$screenname = $this->trimmed('screenname'); $screenname = $this->trimmed('screenname');
$transport = $this->trimmed('transport'); $transport = $this->trimmed('transport');
try {
$confirm = $this->getConfirmation($transport); $confirm = $this->getConfirmation($transport);
if (!$confirm) {
// TRANS: Message given canceling Instant Messaging address confirmation that is not pending.
$this->showForm(_('No pending confirmation to cancel.'));
return;
}
if ($confirm->address != $screenname) { if ($confirm->address != $screenname) {
// TRANS: Message given canceling IM address confirmation for the wrong IM address. // TRANS: Message given canceling IM address confirmation for the wrong IM address.
$this->showForm(_('That is the wrong IM address.')); throw new ClientException(_('That is the wrong IM address.'));
return; }
} catch (NoResultException $e) {
// TRANS: Message given canceling Instant Messaging address confirmation that is not pending.
throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
} }
$result = $confirm->delete(); $result = $confirm->delete();
if (!$result) { if ($result === false) {
common_log_db_error($confirm, 'DELETE', __FILE__); common_log_db_error($confirm, 'DELETE', __FILE__);
// TRANS: Server error thrown on database error canceling IM address confirmation. // TRANS: Server error thrown on database error canceling IM address confirmation.
$this->serverError(_('Could not delete confirmation.')); throw new ServerException(_('Could not delete confirmation.'));
} }
// TRANS: Message given after successfully canceling IM address confirmation. // TRANS: Message given after successfully canceling IM address confirmation.
$this->showForm(_('IM confirmation cancelled.'), true); return _('IM confirmation cancelled.');
} }
/** /**
@@ -420,34 +380,32 @@ class ImsettingsAction extends SettingsAction
*/ */
function removeAddress() function removeAddress()
{ {
$user = common_current_user();
$screenname = $this->trimmed('screenname'); $screenname = $this->trimmed('screenname');
$transport = $this->trimmed('transport'); $transport = $this->trimmed('transport');
// Maybe an old tab open...? // Maybe an old tab open...?
$user_im_prefs = new User_im_prefs(); $user_im_prefs = new User_im_prefs();
$user_im_prefs->user_id = $user->id; $user_im_prefs->user_id = $this->scoped->getID();
if(! ($user_im_prefs->find() && $user_im_prefs->fetch())) { $user_im_prefs->transport = $transport;
if (!$user_im_prefs->find(true)) {
// TRANS: Message given trying to remove an IM address that is not // TRANS: Message given trying to remove an IM address that is not
// TRANS: registered for the active user. // TRANS: registered for the active user.
$this->showForm(_('That is not your screenname.')); throw new AlreadyFulfilledException(_('There were no preferences stored for this transport.'));
return;
} }
$result = $user_im_prefs->delete(); $result = $user_im_prefs->delete();
if (!$result) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user_im_prefs, 'UPDATE', __FILE__);
// TRANS: Server error thrown on database error removing a registered IM address. // TRANS: Server error thrown on database error removing a registered IM address.
$this->serverError(_('Could not update user IM preferences.')); throw new ServerException(_('Could not update user IM preferences.'));
} }
// XXX: unsubscribe to the old address // XXX: unsubscribe to the old address
// TRANS: Message given after successfully removing a registered Instant Messaging address. // TRANS: Message given after successfully removing a registered Instant Messaging address.
$this->showForm(_('The IM address was removed.'), true); return _('The IM address was removed.');
} }
/** /**
@@ -463,15 +421,9 @@ class ImsettingsAction extends SettingsAction
function screennameExists($transport, $screenname) function screennameExists($transport, $screenname)
{ {
$user = common_current_user();
$user_im_prefs = new User_im_prefs(); $user_im_prefs = new User_im_prefs();
$user_im_prefs->transport = $transport; $user_im_prefs->transport = $transport;
$user_im_prefs->screenname = $screenname; $user_im_prefs->screenname = $screenname;
if($user_im_prefs->find() && $user_im_prefs->fetch()){ return $user_im_prefs->find(true) ? true : false;
return true;
}else{
return false;
}
} }
} }

View File

@@ -118,7 +118,7 @@ class InviteAction extends Action
$this->already[] = $other; $this->already[] = $other;
} else { } else {
try { try {
Subscription::start($profile, $other); Subscription::ensureStart($profile, $other);
$this->subbed[] = $other; $this->subbed[] = $other;
} catch (Exception $e) { } catch (Exception $e) {
// subscription failed, but keep working // subscription failed, but keep working

View File

@@ -36,24 +36,6 @@ class LoginAction extends FormAction
{ {
protected $needLogin = false; protected $needLogin = false;
/**
* Prepare page to run
*
*
* @param $args
* @return string title
*/
protected function prepare(array $args=array())
{
// @todo this check should really be in index.php for all sensitive actions
$ssl = common_config('site', 'ssl');
if (empty($_SERVER['HTTPS']) && ($ssl == 'always' || $ssl == 'sometimes')) {
common_redirect(common_local_url('login'));
}
return parent::prepare($args);
}
/** /**
* Handle input, produce output * Handle input, produce output
* *
@@ -79,10 +61,8 @@ class LoginAction extends FormAction
* *
* @return void * @return void
*/ */
protected function handlePost() protected function doPost()
{ {
parent::handlePost();
// XXX: login throttle // XXX: login throttle
$nickname = $this->trimmed('nickname'); $nickname = $this->trimmed('nickname');
@@ -122,22 +102,6 @@ class LoginAction extends FormAction
common_redirect($url, 303); common_redirect($url, 303);
} }
/**
* Store an error and show the page
*
* This used to show the whole page; now, it's just a wrapper
* that stores the error in an attribute.
*
* @param string $error error, if any.
*
* @return void
*/
public function showForm($msg=null, $success=false)
{
common_ensure_session();
return parent::showForm($msg, $success);
}
function showScripts() function showScripts()
{ {
parent::showScripts(); parent::showScripts();
@@ -208,7 +172,7 @@ class LoginAction extends FormAction
* *
* @return void * @return void
*/ */
function getInstructions() protected function getInstructions()
{ {
if (common_logged_in() && !common_is_real_login() && if (common_logged_in() && !common_is_real_login() &&
common_get_returnto()) { common_get_returnto()) {

View File

@@ -63,7 +63,7 @@ class LogoutAction extends ManagedAction
} }
Event::handle('EndLogout', array($this)); Event::handle('EndLogout', array($this));
common_redirect(common_local_url('startpage')); common_redirect(common_local_url('top'));
} }
// Accessed through the action on events // Accessed through the action on events

View File

@@ -2,7 +2,7 @@
if (!defined('GNUSOCIAL')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
class NetworkpublicAction extends PublicAction class NetworkpublicAction extends SitestreamAction
{ {
protected function streamPrepare() protected function streamPrepare()
{ {
@@ -28,13 +28,6 @@ class NetworkpublicAction extends PublicAction
} }
} }
function extraHead()
{
// the PublicAction has some XRDS stuff that might be unique to the non-network public feed
// FIXME: Solve this with a call that doesn't rely on parent:: and is unique for each class.
ManagedAction::extraHead();
}
function showSections() function showSections()
{ {
// Show invite button, as long as site isn't closed, and // Show invite button, as long as site isn't closed, and

View File

@@ -28,9 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Add a new application * Add a new application
@@ -43,7 +41,7 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @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://status.net/
*/ */
class NewApplicationAction extends FormAction class NewApplicationAction extends SettingsAction
{ {
function title() function title()
{ {
@@ -51,13 +49,12 @@ class NewApplicationAction extends FormAction
return _('New application'); return _('New application');
} }
protected function handlePost() protected function doPost()
{ {
parent::handlePost();
if ($this->arg('cancel')) { if ($this->arg('cancel')) {
common_redirect(common_local_url('oauthappssettings'), 303); common_redirect(common_local_url('oauthappssettings'), 303);
} elseif ($this->arg('save')) { } elseif ($this->arg('save')) {
//trySave will never return, just throw exception or redirect
$this->trySave(); $this->trySave();
} }
@@ -65,30 +62,18 @@ class NewApplicationAction extends FormAction
$this->clientError(_('Unexpected form submission.')); $this->clientError(_('Unexpected form submission.'));
} }
function showForm($msg=null) protected function getForm()
{ {
$this->msg = $msg; return new ApplicationEditForm($this);
$this->showPage();
} }
function showContent() protected function getInstructions()
{ {
$form = new ApplicationEditForm($this);
$form->show();
}
function showPageNotice()
{
if ($this->msg) {
$this->element('p', 'error', $this->msg);
} else {
$this->element('p', 'instructions',
// TRANS: Form instructions for registering a new application. // TRANS: Form instructions for registering a new application.
_('Use this form to register a new application.')); return _('Use this form to register a new application.');
}
} }
private function trySave() protected function trySave()
{ {
$name = $this->trimmed('name'); $name = $this->trimmed('name');
$description = $this->trimmed('description'); $description = $this->trimmed('description');
@@ -153,7 +138,7 @@ class NewApplicationAction extends FormAction
$app->query('BEGIN'); $app->query('BEGIN');
$app->name = $name; $app->name = $name;
$app->owner = $this->scoped->id; $app->owner = $this->scoped->getID();
$app->description = $description; $app->description = $description;
$app->source_url = $source_url; $app->source_url = $source_url;
$app->organization = $organization; $app->organization = $organization;
@@ -181,6 +166,7 @@ class NewApplicationAction extends FormAction
if (!$result) { if (!$result) {
common_log_db_error($consumer, 'INSERT', __FILE__); common_log_db_error($consumer, 'INSERT', __FILE__);
$app->query('ROLLBACK');
// TRANS: Server error displayed when an application could not be registered in the database through the "New application" form. // TRANS: Server error displayed when an application could not be registered in the database through the "New application" form.
$this->serverError(_('Could not create application.')); $this->serverError(_('Could not create application.'));
} }
@@ -191,9 +177,9 @@ class NewApplicationAction extends FormAction
if (!$this->app_id) { if (!$this->app_id) {
common_log_db_error($app, 'INSERT', __FILE__); common_log_db_error($app, 'INSERT', __FILE__);
$app->query('ROLLBACK');
// TRANS: Server error displayed when an application could not be registered in the database through the "New application" form. // TRANS: Server error displayed when an application could not be registered in the database through the "New application" form.
$this->serverError(_('Could not create application.')); $this->serverError(_('Could not create application.'));
$app->query('ROLLBACK');
} }
try { try {

View File

@@ -29,9 +29,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Add a new group * Add a new group
@@ -48,6 +46,8 @@ class NewgroupAction extends FormAction
{ {
protected $group; protected $group;
protected $form = 'GroupEdit';
function getGroup() { function getGroup() {
return $this->group; return $this->group;
} }
@@ -58,39 +58,23 @@ class NewgroupAction extends FormAction
return _('New group'); return _('New group');
} }
/** protected function doPreparation()
* Prepare to run
*/
protected function prepare(array $args=array())
{ {
parent::prepare($args);
// $this->scoped is the current user profile // $this->scoped is the current user profile
if (!$this->scoped->hasRight(Right::CREATEGROUP)) { if (!$this->scoped->hasRight(Right::CREATEGROUP)) {
// TRANS: Client exception thrown when a user tries to create a group while banned. // TRANS: Client exception thrown when a user tries to create a group while banned.
$this->clientError(_('You are not allowed to create groups on this site.'), 403); $this->clientError(_('You are not allowed to create groups on this site.'), 403);
} }
return true;
} }
public function showContent() protected function getInstructions()
{ {
$form = new GroupEditForm($this);
$form->show();
}
public function showInstructions()
{
$this->element('p', 'instructions',
// TRANS: Form instructions for group create form. // TRANS: Form instructions for group create form.
_('Use this form to create a new group.')); return _('Use this form to create a new group.');
} }
protected function handlePost() protected function doPost()
{ {
parent::handlePost();
if (Event::handle('StartGroupSaveForm', array($this))) { if (Event::handle('StartGroupSaveForm', array($this))) {
$nickname = Nickname::normalize($this->trimmed('newnickname'), true); $nickname = Nickname::normalize($this->trimmed('newnickname'), true);
@@ -133,7 +117,6 @@ class NewgroupAction extends FormAction
'Too many aliases! Maximum %d allowed.', 'Too many aliases! Maximum %d allowed.',
common_config('group', 'maxaliases')), common_config('group', 'maxaliases')),
common_config('group', 'maxaliases'))); common_config('group', 'maxaliases')));
return;
} }
if ($private) { if ($private) {

View File

@@ -30,9 +30,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Action for posting new notices * Action for posting new notices
@@ -62,6 +60,9 @@ class NewnoticeAction extends FormAction
// TRANS: Page title after sending a notice. // TRANS: Page title after sending a notice.
return _('Notice posted'); return _('Notice posted');
} }
if ($this->int('inreplyto')) {
return _m('TITLE', 'New reply');
}
// TRANS: Page title for sending a new notice. // TRANS: Page title for sending a new notice.
return _m('TITLE','New notice'); return _m('TITLE','New notice');
} }
@@ -80,7 +81,7 @@ class NewnoticeAction extends FormAction
} }
/** /**
* This handlePost saves a new notice, based on arguments * This doPost saves a new notice, based on arguments
* *
* If successful, will show the notice, or return an Ajax-y result. * If successful, will show the notice, or return an Ajax-y result.
* If not, it will show an error message -- possibly Ajax-y. * If not, it will show an error message -- possibly Ajax-y.
@@ -90,27 +91,40 @@ class NewnoticeAction extends FormAction
* *
* @return void * @return void
*/ */
protected function handlePost() protected function doPost()
{ {
parent::handlePost(); assert($this->scoped instanceof Profile); // XXX: maybe an error instead...
assert($this->scoped); // XXX: maybe an error instead...
$user = $this->scoped->getUser(); $user = $this->scoped->getUser();
$content = $this->trimmed('status_textarea'); $content = $this->formOpts['content'];
$options = array(); $options = array('source' => 'web');
Event::handle('StartSaveNewNoticeWeb', array($this, $user, &$content, &$options)); Event::handle('StartSaveNewNoticeWeb', array($this, $user, &$content, &$options));
if (!$content) { $upload = null;
try {
// throws exception on failure
$upload = MediaFile::fromUpload('attach', $this->scoped);
if (Event::handle('StartSaveNewNoticeAppendAttachment', array($this, $upload, &$content, &$options))) {
$content .= ($content==='' ? '' : ' ') . $upload->shortUrl();
}
Event::handle('EndSaveNewNoticeAppendAttachment', array($this, $upload, &$content, &$options));
// We could check content length here if the URL was added, but I'll just let it slide for now...
$act->enclosures[] = $upload->getEnclosure();
} catch (NoUploadedMediaException $e) {
// simply no attached media to the new notice
if (empty($content)) {
// TRANS: Client error displayed trying to send a notice without content. // TRANS: Client error displayed trying to send a notice without content.
$this->clientError(_('No content!')); $this->clientError(_('No content!'));
} }
}
$inter = new CommandInterpreter(); $inter = new CommandInterpreter();
$cmd = $inter->handle_command($user, $content); $cmd = $inter->handle_command($user, $content);
if ($cmd) { if ($cmd) {
if (StatusNet::isAjax()) { if (GNUsocial::isAjax()) {
$cmd->execute(new AjaxWebChannel($this)); $cmd->execute(new AjaxWebChannel($this));
} else { } else {
$cmd->execute(new WebChannel($this)); $cmd->execute(new WebChannel($this));
@@ -118,43 +132,35 @@ class NewnoticeAction extends FormAction
return; return;
} }
$content_shortened = $user->shortenLinks($content); if ($this->int('inreplyto')) {
if (Notice::contentTooLong($content_shortened)) { // Throws exception if the inreplyto Notice is given but not found.
$parent = Notice::getByID($this->int('inreplyto'));
} else {
$parent = null;
}
$act = new Activity();
$act->verb = ActivityVerb::POST;
$act->time = time();
$act->actor = $this->scoped->asActivityObject();
// Reject notice if it is too long (without the HTML)
// This is done after MediaFile::fromUpload etc. just to act the same as the ApiStatusesUpdateAction
if (Notice::contentTooLong($content)) {
// TRANS: Client error displayed when the parameter "status" is missing. // TRANS: Client error displayed when the parameter "status" is missing.
// TRANS: %d is the maximum number of character for a notice. // TRANS: %d is the maximum number of character for a notice.
$this->clientError(sprintf(_m('That\'s too long. Maximum notice size is %d character.', throw new ClientException(sprintf(_m('That\'s too long. Maximum notice size is %d character.',
'That\'s too long. Maximum notice size is %d characters.', 'That\'s too long. Maximum notice size is %d characters.',
Notice::maxContent()), Notice::maxContent()),
Notice::maxContent())); Notice::maxContent()));
} }
$replyto = intval($this->trimmed('inreplyto')); $act->context = new ActivityContext();
if ($replyto) {
$options['reply_to'] = $replyto;
}
$upload = null; if ($parent instanceof Notice) {
try { $act->context->replyToID = $parent->getUri();
// throws exception on failure $act->context->replyToUrl = $parent->getUrl(true); // maybe we don't have to send true here to force a URL?
$upload = MediaFile::fromUpload('attach', $this->scoped);
if (Event::handle('StartSaveNewNoticeAppendAttachment', array($this, $upload, &$content_shortened, &$options))) {
$content_shortened .= ' ' . $upload->shortUrl();
} }
Event::handle('EndSaveNewNoticeAppendAttachment', array($this, $upload, &$content_shortened, &$options));
if (Notice::contentTooLong($content_shortened)) {
$upload->delete();
// TRANS: Client error displayed exceeding the maximum notice length.
// TRANS: %d is the maximum length for a notice.
$this->clientError(sprintf(_m('Maximum notice size is %d character, including attachment URL.',
'Maximum notice size is %d characters, including attachment URL.',
Notice::maxContent()),
Notice::maxContent()));
}
} catch (NoUploadedMediaException $e) {
// simply no attached media to the new notice
}
if ($this->scoped->shareLocation()) { if ($this->scoped->shareLocation()) {
// use browser data if checked; otherwise profile data // use browser data if checked; otherwise profile data
@@ -172,19 +178,29 @@ class NewnoticeAction extends FormAction
$this->scoped); $this->scoped);
} }
$options = array_merge($options, $locOptions); $act->context->location = Location::fromOptions($locOptions);
} }
$author_id = $this->scoped->id; $content = $this->scoped->shortenLinks($content);
$text = $content_shortened;
// Does the heavy-lifting for getting "To:" information // FIXME: Make sure NoticeTitle plugin gets a change to add the title to our activityobject!
if (Event::handle('StartNoticeSaveWeb', array($this, $this->scoped, &$content, &$options))) {
ToSelector::fillOptions($this, $options); // FIXME: We should be able to get the attentions from common_render_content!
// and maybe even directly save whether they're local or not!
$act->context->attention = common_get_attentions($content, $this->scoped, $parent);
if (Event::handle('StartNoticeSaveWeb', array($this, &$author_id, &$text, &$options))) { // $options gets filled with possible scoping settings
ToSelector::fillActivity($this, $act, $options);
$this->stored = Notice::saveNew($this->scoped->id, $content_shortened, 'web', $options); $actobj = new ActivityObject();
$actobj->type = ActivityObject::NOTE;
$actobj->content = common_render_content($content, $this->scoped, $parent);
// Finally add the activity object to our activity
$act->objects[] = $actobj;
$this->stored = Notice::saveActivity($act, $this->scoped, $options);
if ($upload instanceof MediaFile) { if ($upload instanceof MediaFile) {
$upload->attachToNotice($this->stored); $upload->attachToNotice($this->stored);
@@ -193,9 +209,9 @@ class NewnoticeAction extends FormAction
Event::handle('EndNoticeSaveWeb', array($this, $this->stored)); Event::handle('EndNoticeSaveWeb', array($this, $this->stored));
} }
Event::handle('EndSaveNewNoticeWeb', array($this, $user, &$content_shortened, &$options)); Event::handle('EndSaveNewNoticeWeb', array($this, $user, &$content, &$options));
if (!StatusNet::isAjax()) { if (!GNUsocial::isAjax()) {
$url = common_local_url('shownotice', array('notice' => $this->stored->id)); $url = common_local_url('shownotice', array('notice' => $this->stored->id));
common_redirect($url, 303); common_redirect($url, 303);
} }

View File

@@ -203,14 +203,7 @@ class SearchNoticeListItem extends NoticeListItem {
{ {
// FIXME: URL, image, video, audio // FIXME: URL, image, video, audio
$this->out->elementStart('p', array('class' => 'e-content')); $this->out->elementStart('p', array('class' => 'e-content'));
if ($this->notice->rendered) { $this->out->raw($this->highlight($this->notice->getRendered(), $this->terms));
$this->out->raw($this->highlight($this->notice->rendered, $this->terms));
} else {
// XXX: may be some uncooked notices in the DB,
// we cook them right now. This should probably disappear in future
// versions (>> 0.4.x)
$this->out->raw($this->highlight(common_render_content($this->notice->content, $this->notice), $this->terms));
}
$this->out->elementEnd('p'); $this->out->elementEnd('p');
} }

View File

@@ -28,11 +28,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/rssaction.php';
/** /**
* RSS feed for notice search action class. * RSS feed for notice search action class.
@@ -48,19 +44,7 @@ require_once INSTALLDIR.'/lib/rssaction.php';
*/ */
class NoticesearchrssAction extends Rss10Action class NoticesearchrssAction extends Rss10Action
{ {
function init() protected function getNotices()
{
return true;
}
function prepare($args)
{
parent::prepare($args);
$this->notices = $this->getNotices();
return true;
}
function getNotices($limit=0)
{ {
$q = $this->trimmed('q'); $q = $this->trimmed('q');
$notices = array(); $notices = array();
@@ -70,8 +54,7 @@ class NoticesearchrssAction extends Rss10Action
$search_engine = $notice->getSearchEngine('notice'); $search_engine = $notice->getSearchEngine('notice');
$search_engine->set_sort_mode('chron'); $search_engine->set_sort_mode('chron');
if (!$limit) $limit = 20; $search_engine->limit(0, $this->limit, true);
$search_engine->limit(0, $limit, true);
if (false === $search_engine->query($q)) { if (false === $search_engine->query($q)) {
$cnt = 0; $cnt = 0;
} else { } else {

View File

@@ -27,11 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR . '/lib/applicationlist.php';
/** /**
* Show a user's registered OAuth applications * Show a user's registered OAuth applications
@@ -47,19 +43,11 @@ require_once INSTALLDIR . '/lib/applicationlist.php';
class OauthappssettingsAction extends SettingsAction class OauthappssettingsAction extends SettingsAction
{ {
var $page = 0; protected $page = null;
function prepare($args) protected function doPreparation()
{ {
parent::prepare($args); $this->page = $this->int('page') ?: 1;
$this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1;
if (!common_logged_in()) {
// TRANS: Message displayed to an anonymous user trying to view OAuth application list.
$this->clientError(_('You must be logged in to list your applications.'));
}
return true;
} }
/** /**
@@ -86,21 +74,13 @@ class OauthappssettingsAction extends SettingsAction
return _('Applications you have registered'); return _('Applications you have registered');
} }
/**
* Content area of the page
*
* @return void
*/
function showContent() function showContent()
{ {
$user = common_current_user();
$offset = ($this->page - 1) * APPS_PER_PAGE; $offset = ($this->page - 1) * APPS_PER_PAGE;
$limit = APPS_PER_PAGE + 1; $limit = APPS_PER_PAGE + 1;
$application = new Oauth_application(); $application = new Oauth_application();
$application->owner = $user->id; $application->owner = $this->scoped->getID();
$application->whereAdd("name != 'anonymous'"); $application->whereAdd("name != 'anonymous'");
$application->limit($offset, $limit); $application->limit($offset, $limit);
$application->orderBy('created DESC'); $application->orderBy('created DESC');
@@ -109,7 +89,7 @@ class OauthappssettingsAction extends SettingsAction
$cnt = 0; $cnt = 0;
if ($application) { if ($application) {
$al = new ApplicationList($application, $user, $this); $al = new ApplicationList($application, $this->scoped, $this);
$cnt = $al->show(); $cnt = $al->show();
if (0 == $cnt) { if (0 == $cnt) {
$this->showEmptyListMessage(); $this->showEmptyListMessage();
@@ -135,34 +115,11 @@ class OauthappssettingsAction extends SettingsAction
function showEmptyListMessage() function showEmptyListMessage()
{ {
// TRANS: Empty list message on page with OAuth applications. // TRANS: Empty list message on page with OAuth applications. Markup allowed
$message = sprintf(_('You have not registered any applications yet.')); $message = sprintf(_('You have not registered any applications yet.'));
$this->elementStart('div', 'guide'); $this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message)); $this->raw(common_markup_to_html($message));
$this->elementEnd('div'); $this->elementEnd('div');
} }
/**
* 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()) {
$this->showForm(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
}
} }

View File

@@ -27,11 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR . '/lib/applicationlist.php';
/** /**
* Show connected OAuth applications * Show connected OAuth applications
@@ -47,14 +43,13 @@ require_once INSTALLDIR . '/lib/applicationlist.php';
class OauthconnectionssettingsAction extends SettingsAction class OauthconnectionssettingsAction extends SettingsAction
{ {
var $page = null; var $page = null;
var $oauth_token = null;
function prepare($args) protected $oauth_token = null;
protected function doPreparation()
{ {
parent::prepare($args);
$this->oauth_token = $this->arg('oauth_token'); $this->oauth_token = $this->arg('oauth_token');
$this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; $this->page = $this->int('page') ?: 1;
return true;
} }
/** /**
@@ -87,18 +82,15 @@ class OauthconnectionssettingsAction extends SettingsAction
function showContent() function showContent()
{ {
$user = common_current_user();
$profile = $user->getProfile();
$offset = ($this->page - 1) * APPS_PER_PAGE; $offset = ($this->page - 1) * APPS_PER_PAGE;
$limit = APPS_PER_PAGE + 1; $limit = APPS_PER_PAGE + 1;
$connection = $user->getConnectedApps($offset, $limit); $connection = $this->scoped->getConnectedApps($offset, $limit);
$cnt = 0; $cnt = 0;
if (!empty($connection)) { if (!empty($connection)) {
$cal = new ConnectedAppsList($connection, $user, $this); $cal = new ConnectedAppsList($connection, $this->scoped, $this);
$cnt = $cal->show(); $cnt = $cal->show();
} }
@@ -111,7 +103,7 @@ class OauthconnectionssettingsAction extends SettingsAction
$cnt > APPS_PER_PAGE, $cnt > APPS_PER_PAGE,
$this->page, $this->page,
'connectionssettings', 'connectionssettings',
array('nickname' => $user->nickname) array('nickname' => $this->scoped->getNickname())
); );
} }
@@ -125,24 +117,14 @@ class OauthconnectionssettingsAction extends SettingsAction
* *
* @return void * @return void
*/ */
function handlePost() protected function doPost()
{ {
// 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(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if ($this->arg('revoke')) { if ($this->arg('revoke')) {
$this->revokeAccess($this->oauth_token); return $this->revokeAccess($this->oauth_token);
} else {
// TRANS: Client error when submitting a form with unexpected information.
$this->clientError(_('Unexpected form submission.'), 401);
} }
// TRANS: Client error when submitting a form with unexpected information.
throw new ClientException(_('Unexpected form submission.'), 401);
} }
/** /**

View File

@@ -28,11 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/** /**
* Old-school settings * Old-school settings
@@ -77,35 +73,23 @@ class OldschoolsettingsAction extends SettingsAction
* @return boolean true * @return boolean true
*/ */
function prepare($argarray) protected function doPreparation()
{ {
if (!common_config('oldschool', 'enabled')) { if (!common_config('oldschool', 'enabled')) {
throw new ClientException("Old-school settings not enabled."); throw new ClientException("Old-school settings not enabled.");
} }
parent::prepare($argarray);
return true;
} }
/** function doPost()
* Handler method
*
* @param array $argarray is ignored since it's now passed in in prepare()
*
* @return void
*/
function handlePost()
{ {
$user = common_current_user(); $osp = Old_school_prefs::getKV('user_id', $this->scoped->getID());
$osp = Old_school_prefs::getKV('user_id', $user->id);
$orig = null; $orig = null;
if (!empty($osp)) { if (!empty($osp)) {
$orig = clone($osp); $orig = clone($osp);
} else { } else {
$osp = new Old_school_prefs(); $osp = new Old_school_prefs();
$osp->user_id = $user->id; $osp->user_id = $this->scoped->getID();
$osp->created = common_sql_now(); $osp->created = common_sql_now();
} }
@@ -113,34 +97,25 @@ class OldschoolsettingsAction extends SettingsAction
$osp->stream_nicknames = $this->boolean('stream_nicknames'); $osp->stream_nicknames = $this->boolean('stream_nicknames');
$osp->modified = common_sql_now(); $osp->modified = common_sql_now();
if (!empty($orig)) { if ($orig instanceof Old_school_prefs) {
$osp->update($orig); $osp->update($orig);
} else { } else {
$osp->insert(); $osp->insert();
} }
// TRANS: Confirmation shown when user profile settings are saved. // TRANS: Confirmation shown when user profile settings are saved.
$this->showForm(_('Settings saved.'), true); return _('Settings saved.');
return;
}
function showContent()
{
$user = common_current_user();
$form = new OldSchoolForm($this, $user);
$form->show();
} }
} }
class OldSchoolForm extends Form class OldSchoolSettingsForm extends Form
{ {
var $user; var $user;
function __construct($out, $user) function __construct(Action $out)
{ {
parent::__construct($out); parent::__construct($out);
$this->user = $user; $this->user = $out->getScoped()->getUser();
} }
/** /**

View File

@@ -28,11 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('STATUSNET')) { exit(1); }
exit(1);
}
/** /**
* Change password * Change password
@@ -77,18 +73,8 @@ class PasswordsettingsAction extends SettingsAction
$this->autofocus('oldpassword'); $this->autofocus('oldpassword');
} }
/**
* Content area of the page
*
* Shows a form for changing the password
*
* @return void
*/
function showContent() function showContent()
{ {
$user = common_current_user();
$this->elementStart('form', array('method' => 'POST', $this->elementStart('form', array('method' => 'POST',
'id' => 'form_password', 'id' => 'form_password',
'class' => 'form_settings', 'class' => 'form_settings',
@@ -102,7 +88,7 @@ class PasswordsettingsAction extends SettingsAction
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
// Users who logged in with OpenID won't have a pwd // Users who logged in with OpenID won't have a pwd
if ($user->password) { if ($this->scoped->hasPassword()) {
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label on page where to change password. // TRANS: Field label on page where to change password.
$this->password('oldpassword', _('Old password')); $this->password('oldpassword', _('Old password'));
@@ -129,29 +115,8 @@ class PasswordsettingsAction extends SettingsAction
$this->elementEnd('form'); $this->elementEnd('form');
} }
/** protected function doPost()
* Handle a post
*
* Validate input and save changes. Reload the form with a success
* or error message.
*
* @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(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
$user = common_current_user();
assert(!is_null($user)); // should already be checked
// FIXME: scrub input // FIXME: scrub input
$newpassword = $this->arg('newpassword'); $newpassword = $this->arg('newpassword');
@@ -161,49 +126,30 @@ class PasswordsettingsAction extends SettingsAction
if (strlen($newpassword) < 6) { if (strlen($newpassword) < 6) {
// TRANS: Form validation error on page where to change password. // TRANS: Form validation error on page where to change password.
$this->showForm(_('Password must be 6 or more characters.')); throw new ClientException(_('Password must be 6 or more characters.'));
return;
} else if (0 != strcmp($newpassword, $confirm)) { } else if (0 != strcmp($newpassword, $confirm)) {
// TRANS: Form validation error on password change when password confirmation does not match. // TRANS: Form validation error on password change when password confirmation does not match.
$this->showForm(_('Passwords do not match.')); throw new ClientException(_('Passwords do not match.'));
return;
} }
if ($user->password) { $oldpassword = null;
if ($this->scoped->hasPassword()) {
$oldpassword = $this->arg('oldpassword'); $oldpassword = $this->arg('oldpassword');
if (!common_check_user($user->nickname, $oldpassword)) { if (!common_check_user($this->scoped->getNickname(), $oldpassword)) {
// TRANS: Form validation error on page where to change password. // TRANS: Form validation error on page where to change password.
$this->showForm(_('Incorrect old password.')); throw new ClientException(_('Incorrect old password.'));
return;
} }
}else{
$oldpassword = null;
} }
$success = false; if (Event::handle('StartChangePassword', array($this->scoped, $oldpassword, $newpassword))) {
if(Event::handle('StartChangePassword', array($user, $oldpassword, $newpassword))){
//no handler changed the password, so change the password internally //no handler changed the password, so change the password internally
$original = clone($user); $user->setPassword($newpassword);
$user->password = common_munge_password($newpassword, $user->id); Event::handle('EndChangePassword', array($this->scoped));
$val = $user->validate();
if ($val !== true) {
// TRANS: Form validation error on page where to change password.
$this->showForm(_('Error saving user; invalid.'));
return;
}
if (!$user->update($original)) {
// TRANS: Server error displayed on page where to change password when password change
// TRANS: could not be made because of a server error.
$this->serverError(_('Cannot save new password.'));
}
Event::handle('EndChangePassword', array($user));
} }
// TRANS: Form validation notice on page where to change password. // TRANS: Form validation notice on page where to change password.
$this->showForm(_('Password saved.'), true); return _('Password saved.');
} }
} }

View File

@@ -418,8 +418,6 @@ class PathsAdminPanelForm extends AdminForm
// TRANS: Drop down option in Paths admin panel (option for "When to use SSL"). // TRANS: Drop down option in Paths admin panel (option for "When to use SSL").
$ssl = array('never' => _('Never'), $ssl = array('never' => _('Never'),
// TRANS: Drop down option in Paths admin panel (option for "When to use SSL"). // TRANS: Drop down option in Paths admin panel (option for "When to use SSL").
'sometimes' => _('Sometimes'),
// TRANS: Drop down option in Paths admin panel (option for "When to use SSL").
'always' => _('Always')); 'always' => _('Always'));
$this->out->dropdown('site-ssl', $this->out->dropdown('site-ssl',

View File

@@ -68,7 +68,7 @@ class PeopletagautocompleteAction extends Action
} }
$profile = $this->user->getProfile(); $profile = $this->user->getProfile();
$tags = $profile->getLists(common_current_user()); $tags = $profile->getLists($this->scoped);
$this->tags = array(); $this->tags = array();
while ($tags->fetch()) { while ($tags->fetch()) {

View File

@@ -116,7 +116,7 @@ class PeopletagsbyuserAction extends Action
$user = common_current_user(); $user = common_current_user();
if ($this->arg('public')) { if ($this->arg('public')) {
$this->tags = $this->tagger->getLists(false, $offset, $limit); $this->tags = $this->tagger->getLists(null, $offset, $limit);
} else if ($this->arg('private')) { } else if ($this->arg('private')) {
if (empty($user)) { if (empty($user)) {
// TRANS: Error message displayed when trying to perform an action that requires a logged in user. // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
@@ -130,7 +130,7 @@ class PeopletagsbyuserAction extends Action
$this->clientError(_('You cannot view others\' private lists'), 403); $this->clientError(_('You cannot view others\' private lists'), 403);
} }
} else { } else {
$this->tags = $this->tagger->getLists(common_current_user(), $offset, $limit); $this->tags = $this->tagger->getLists($this->scoped, $offset, $limit);
} }
return true; return true;
} }

View File

@@ -126,7 +126,7 @@ class PeopletagsforuserAction extends Action
$offset = ($this->page-1) * PEOPLETAGS_PER_PAGE; $offset = ($this->page-1) * PEOPLETAGS_PER_PAGE;
$limit = PEOPLETAGS_PER_PAGE + 1; $limit = PEOPLETAGS_PER_PAGE + 1;
$ptags = $this->tagged->getOtherTags(common_current_user(), $offset, $limit); $ptags = $this->tagged->getOtherTags($this->scoped, $offset, $limit);
$pl = new PeopletagList($ptags, $this); $pl = new PeopletagList($ptags, $this);
$cnt = $pl->show(); $cnt = $pl->show();

View File

@@ -62,7 +62,7 @@ class PluginsadminpanelAction extends AdminPanelAction
{ {
// TRANS: Instructions at top of plugin admin page. // TRANS: Instructions at top of plugin admin page.
return _('Additional plugins can be enabled and configured manually. ' . return _('Additional plugins can be enabled and configured manually. ' .
'See the <a href="http://status.net/wiki/Plugins">online plugin ' . 'See the <a href="https://git.gnu.io/gnu/gnu-social/blob/master/plugins/README.md">online plugin ' .
'documentation</a> for more details.'); 'documentation</a> for more details.');
} }

View File

@@ -82,8 +82,7 @@ class ProfilesettingsAction extends SettingsAction
*/ */
function showContent() function showContent()
{ {
$user = common_current_user(); $user = $this->scoped->getUser();
$profile = $user->getProfile();
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_profile', 'id' => 'form_settings_profile',
@@ -100,21 +99,26 @@ class ProfilesettingsAction extends SettingsAction
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings. // TRANS: Field label in form for profile settings.
$this->input('nickname', _('Nickname'), $this->input('nickname', _('Nickname'),
$this->arg('nickname') ?: $profile->nickname, $this->trimmed('nickname') ?: $this->scoped->getNickname(),
// TRANS: Tooltip for field label in form for profile settings. // TRANS: Tooltip for field label in form for profile settings.
_('1-64 lowercase letters or numbers, no punctuation or spaces.'), _('1-64 lowercase letters or numbers, no punctuation or spaces.'),
null, false, // "name" (will be set to id), then "required" null, false, // "name" (will be set to id), then "required"
!common_config('profile', 'changenick') ? array('disabled'=>'disabled') : array()); !common_config('profile', 'changenick')
? array('disabled' => 'disabled', 'placeholder' => null)
: array('placeholder' => null));
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings. // TRANS: Field label in form for profile settings.
$this->input('fullname', _('Full name'), $this->input('fullname', _('Full name'),
($this->arg('fullname')) ? $this->arg('fullname') : $profile->fullname); $this->trimmed('fullname') ?: $this->scoped->getFullname(),
// TRANS: Instructions for full name text field on profile settings
_('A full name is required, if empty it will be set to your nickname.'),
null, true);
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings. // TRANS: Field label in form for profile settings.
$this->input('homepage', _('Homepage'), $this->input('homepage', _('Homepage'),
($this->arg('homepage')) ? $this->arg('homepage') : $profile->homepage, $this->trimmed('homepage') ?: $this->scoped->getHomepage(),
// TRANS: Tooltip for field label in form for profile settings. // TRANS: Tooltip for field label in form for profile settings.
_('URL of your homepage, blog, or profile on another site.')); _('URL of your homepage, blog, or profile on another site.'));
$this->elementEnd('li'); $this->elementEnd('li');
@@ -135,13 +139,13 @@ class ProfilesettingsAction extends SettingsAction
// TRANS: Text area label in form for profile settings where users can provide // TRANS: Text area label in form for profile settings where users can provide
// TRANS: their biography. // TRANS: their biography.
$this->textarea('bio', _('Bio'), $this->textarea('bio', _('Bio'),
($this->arg('bio')) ? $this->arg('bio') : $profile->bio, $this->trimmed('bio') ?: $this->scoped->getDescription(),
$bioInstr); $bioInstr);
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings. // TRANS: Field label in form for profile settings.
$this->input('location', _('Location'), $this->input('location', _('Location'),
($this->arg('location')) ? $this->arg('location') : $profile->location, $this->trimmed('location') ?: $this->scoped->location,
// TRANS: Tooltip for field label in form for profile settings. // TRANS: Tooltip for field label in form for profile settings.
_('Where you are, like "City, State (or Region), Country".')); _('Where you are, like "City, State (or Region), Country".'));
$this->elementEnd('li'); $this->elementEnd('li');
@@ -150,14 +154,14 @@ class ProfilesettingsAction extends SettingsAction
// TRANS: Checkbox label in form for profile settings. // TRANS: Checkbox label in form for profile settings.
$this->checkbox('sharelocation', _('Share my current location when posting notices'), $this->checkbox('sharelocation', _('Share my current location when posting notices'),
($this->arg('sharelocation')) ? ($this->arg('sharelocation')) ?
$this->arg('sharelocation') : $this->scoped->shareLocation()); $this->boolean('sharelocation') : $this->scoped->shareLocation());
$this->elementEnd('li'); $this->elementEnd('li');
} }
Event::handle('EndProfileFormData', array($this)); Event::handle('EndProfileFormData', array($this));
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings. // TRANS: Field label in form for profile settings.
$this->input('tags', _('Tags'), $this->input('tags', _('Tags'),
($this->arg('tags')) ? $this->arg('tags') : implode(' ', $user->getSelfTags()), $this->trimmed('tags') ?: implode(' ', Profile_tag::getSelfTagsArray($this->scoped)),
// TRANS: Tooltip for field label in form for profile settings. // TRANS: Tooltip for field label in form for profile settings.
_('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated.')); _('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated.'));
$this->elementEnd('li'); $this->elementEnd('li');
@@ -203,6 +207,7 @@ class ProfilesettingsAction extends SettingsAction
(empty($user->subscribe_policy)) ? User::SUBSCRIBE_POLICY_OPEN : $user->subscribe_policy); (empty($user->subscribe_policy)) ? User::SUBSCRIBE_POLICY_OPEN : $user->subscribe_policy);
$this->elementEnd('li'); $this->elementEnd('li');
} }
if (common_config('profile', 'allowprivate') || $user->private_stream) {
$this->elementStart('li'); $this->elementStart('li');
$this->checkbox('private_stream', $this->checkbox('private_stream',
// TRANS: Checkbox label in profile settings. // TRANS: Checkbox label in profile settings.
@@ -210,6 +215,7 @@ class ProfilesettingsAction extends SettingsAction
($this->arg('private_stream')) ? ($this->arg('private_stream')) ?
$this->boolean('private_stream') : $user->private_stream); $this->boolean('private_stream') : $user->private_stream);
$this->elementEnd('li'); $this->elementEnd('li');
}
$this->elementEnd('ul'); $this->elementEnd('ul');
// TRANS: Button to save input in profile settings. // TRANS: Button to save input in profile settings.
$this->submit('save', _m('BUTTON','Save')); $this->submit('save', _m('BUTTON','Save'));
@@ -226,17 +232,8 @@ class ProfilesettingsAction extends SettingsAction
* *
* @return void * @return void
*/ */
function handlePost() protected function doPost()
{ {
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Form validation error.
$this->showForm(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if (Event::handle('StartProfileSaveForm', array($this))) { if (Event::handle('StartProfileSaveForm', array($this))) {
// $nickname will only be set if this changenick value is true. // $nickname will only be set if this changenick value is true.
@@ -244,15 +241,13 @@ class ProfilesettingsAction extends SettingsAction
try { try {
$nickname = Nickname::normalize($this->trimmed('nickname'), true); $nickname = Nickname::normalize($this->trimmed('nickname'), true);
} catch (NicknameTakenException $e) { } catch (NicknameTakenException $e) {
// Abort only if the nickname is occupied by another local profile // Abort only if the nickname is occupied by _another_ local user profile
if ($e->profile->id != $this->scoped->id) { if (!$this->scoped->sameAs($e->profile)) {
$this->showForm($e->getMessage()); throw $e;
return;
} }
$nickname = Nickname::normalize($this->trimmed('nickname')); // without in-use check this time // Since the variable wasn't set before the exception was thrown, let's run
} catch (NicknameException $e) { // the normalize sequence again, but without in-use check this time.
$this->showForm($e->getMessage()); $nickname = Nickname::normalize($this->trimmed('nickname'));
return;
} }
} }
@@ -260,9 +255,8 @@ class ProfilesettingsAction extends SettingsAction
$homepage = $this->trimmed('homepage'); $homepage = $this->trimmed('homepage');
$bio = $this->trimmed('bio'); $bio = $this->trimmed('bio');
$location = $this->trimmed('location'); $location = $this->trimmed('location');
$autosubscribe = $this->boolean('autosubscribe'); $autosubscribe = $this->booleanintstring('autosubscribe');
$subscribe_policy = $this->trimmed('subscribe_policy'); $subscribe_policy = $this->trimmed('subscribe_policy');
$private_stream = $this->boolean('private_stream');
$language = $this->trimmed('language'); $language = $this->trimmed('language');
$timezone = $this->trimmed('timezone'); $timezone = $this->trimmed('timezone');
$tagstring = $this->trimmed('tags'); $tagstring = $this->trimmed('tags');
@@ -271,33 +265,27 @@ class ProfilesettingsAction extends SettingsAction
if (!is_null($homepage) && (strlen($homepage) > 0) && if (!is_null($homepage) && (strlen($homepage) > 0) &&
!common_valid_http_url($homepage)) { !common_valid_http_url($homepage)) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
$this->showForm(_('Homepage is not a valid URL.')); throw new ClientException(_('Homepage is not a valid URL.'));
return; } else if (!is_null($fullname) && mb_strlen($fullname) > 191) {
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
$this->showForm(_('Full name is too long (maximum 255 characters).')); throw new ClientException(_('Full name is too long (maximum 191 characters).'));
return;
} else if (Profile::bioTooLong($bio)) { } else if (Profile::bioTooLong($bio)) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
// TRANS: Plural form is used based on the maximum number of allowed // TRANS: Plural form is used based on the maximum number of allowed
// TRANS: characters for the biography (%d). // TRANS: characters for the biography (%d).
$this->showForm(sprintf(_m('Bio is too long (maximum %d character).', throw new ClientException(sprintf(_m('Bio is too long (maximum %d character).',
'Bio is too long (maximum %d characters).', 'Bio is too long (maximum %d characters).',
Profile::maxBio()), Profile::maxBio()),
Profile::maxBio())); Profile::maxBio()));
return; } else if (!is_null($location) && mb_strlen($location) > 191) {
} else if (!is_null($location) && mb_strlen($location) > 255) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
$this->showForm(_('Location is too long (maximum 255 characters).')); throw new ClientException(_('Location is too long (maximum 191 characters).'));
return;
} else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) { } else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
$this->showForm(_('Timezone not selected.')); throw new ClientException(_('Timezone not selected.'));
return;
} else if (!is_null($language) && strlen($language) > 50) { } else if (!is_null($language) && strlen($language) > 50) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
$this->showForm(_('Language is too long (maximum 50 characters).')); throw new ClientException(_('Language is too long (maximum 50 characters).'));
return;
} }
$tags = array(); $tags = array();
@@ -313,17 +301,25 @@ class ProfilesettingsAction extends SettingsAction
if (!common_valid_profile_tag($tag)) { if (!common_valid_profile_tag($tag)) {
// TRANS: Validation error in form for profile settings. // TRANS: Validation error in form for profile settings.
// TRANS: %s is an invalid tag. // TRANS: %s is an invalid tag.
$this->showForm(sprintf(_('Invalid tag: "%s".'), $tag)); throw new ClientException(sprintf(_('Invalid tag: "%s".'), $tag));
return;
} }
$tag_priv[$tag] = $private; $tag_priv[$tag] = $private;
} }
} }
$user = common_current_user(); $user = $this->scoped->getUser();
$user->query('BEGIN'); $user->query('BEGIN');
// Only allow setting private_stream if site policy allows it
// (or user already _has_ a private stream, then you can unset it)
if (common_config('profile', 'allowprivate') || $user->private_stream) {
$private_stream = $this->booleanintstring('private_stream');
} else {
// if not allowed, we set to the existing value
$private_stream = $user->private_stream;
}
// $user->nickname is updated through Profile->update(); // $user->nickname is updated through Profile->update();
// XXX: XOR // XXX: XOR
@@ -344,61 +340,60 @@ class ProfilesettingsAction extends SettingsAction
$result = $user->update($original); $result = $user->update($original);
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);
$user->query('ROLLBACK');
// TRANS: Server error thrown when user profile settings could not be updated to // TRANS: Server error thrown when user profile settings could not be updated to
// TRANS: automatically subscribe to any subscriber. // TRANS: automatically subscribe to any subscriber.
$this->serverError(_('Could not update user for autosubscribe or subscribe_policy.')); throw new ServerException(_('Could not update user for autosubscribe or subscribe_policy.'));
} }
// Re-initialize language environment if it changed // Re-initialize language environment if it changed
common_init_language(); common_init_language();
} }
$profile = $user->getProfile(); $original = clone($this->scoped);
$orig_profile = clone($profile); if (common_config('profile', 'changenick') == true && $this->scoped->getNickname() !== $nickname) {
if (common_config('profile', 'changenick') == true && $profile->nickname !== $nickname) {
assert(Nickname::normalize($nickname)===$nickname); assert(Nickname::normalize($nickname)===$nickname);
common_debug("Changing user nickname from '{$profile->nickname}' to '{$nickname}'."); common_debug("Changing user nickname from '{$this->scoped->getNickname()}' to '{$nickname}'.");
$profile->nickname = $nickname; $this->scoped->nickname = $nickname;
$profile->profileurl = common_profile_url($profile->nickname); $this->scoped->profileurl = common_profile_url($this->scoped->getNickname());
} }
$profile->fullname = $fullname; $this->scoped->fullname = (mb_strlen($fullname)>0 ? $fullname : $this->scoped->nickname);
$profile->homepage = $homepage; $this->scoped->homepage = $homepage;
$profile->bio = $bio; $this->scoped->bio = $bio;
$profile->location = $location; $this->scoped->location = $location;
$loc = Location::fromName($location); $loc = Location::fromName($location);
if (empty($loc)) { if (empty($loc)) {
$profile->lat = null; $this->scoped->lat = null;
$profile->lon = null; $this->scoped->lon = null;
$profile->location_id = null; $this->scoped->location_id = null;
$profile->location_ns = null; $this->scoped->location_ns = null;
} else { } else {
$profile->lat = $loc->lat; $this->scoped->lat = $loc->lat;
$profile->lon = $loc->lon; $this->scoped->lon = $loc->lon;
$profile->location_id = $loc->location_id; $this->scoped->location_id = $loc->location_id;
$profile->location_ns = $loc->location_ns; $this->scoped->location_ns = $loc->location_ns;
} }
if (common_config('location', 'share') == 'user') { if (common_config('location', 'share') == 'user') {
$exists = false; $exists = false;
$prefs = User_location_prefs::getKV('user_id', $user->id); $prefs = User_location_prefs::getKV('user_id', $this->scoped->getID());
if (empty($prefs)) { if (empty($prefs)) {
$prefs = new User_location_prefs(); $prefs = new User_location_prefs();
$prefs->user_id = $user->id; $prefs->user_id = $this->scoped->getID();
$prefs->created = common_sql_now(); $prefs->created = common_sql_now();
} else { } else {
$exists = true; $exists = true;
$orig = clone($prefs); $orig = clone($prefs);
} }
$prefs->share_location = $this->boolean('sharelocation'); $prefs->share_location = $this->booleanintstring('sharelocation');
if ($exists) { if ($exists) {
$result = $prefs->update($orig); $result = $prefs->update($orig);
@@ -408,42 +403,37 @@ class ProfilesettingsAction extends SettingsAction
if ($result === false) { if ($result === false) {
common_log_db_error($prefs, ($exists) ? 'UPDATE' : 'INSERT', __FILE__); common_log_db_error($prefs, ($exists) ? 'UPDATE' : 'INSERT', __FILE__);
$user->query('ROLLBACK');
// TRANS: Server error thrown when user profile location preference settings could not be updated. // TRANS: Server error thrown when user profile location preference settings could not be updated.
$this->serverError(_('Could not save location prefs.')); throw new ServerException(_('Could not save location prefs.'));
} }
} }
common_debug('Old profile: ' . common_log_objstring($orig_profile), __FILE__); common_debug('Old profile: ' . common_log_objstring($original), __FILE__);
common_debug('New profile: ' . common_log_objstring($profile), __FILE__); common_debug('New profile: ' . common_log_objstring($this->scoped), __FILE__);
$result = $profile->update($orig_profile); $result = $this->scoped->update($original);
if ($result === false) { if ($result === false) {
common_log_db_error($profile, 'UPDATE', __FILE__); common_log_db_error($this->scoped, 'UPDATE', __FILE__);
$user->query('ROLLBACK');
// TRANS: Server error thrown when user profile settings could not be saved. // TRANS: Server error thrown when user profile settings could not be saved.
$this->serverError(_('Could not save profile.')); throw new ServerException(_('Could not save profile.'));
} }
// Set the user tags // Set the user tags
$result = $user->setSelfTags($tags, $tag_priv); $result = Profile_tag::setSelfTags($this->scoped, $tags, $tag_priv);
if (!$result) {
// TRANS: Server error thrown when user profile settings tags could not be saved.
$this->serverError(_('Could not save tags.'));
}
$user->query('COMMIT'); $user->query('COMMIT');
Event::handle('EndProfileSaveForm', array($this)); Event::handle('EndProfileSaveForm', array($this));
// TRANS: Confirmation shown when user profile settings are saved. // TRANS: Confirmation shown when user profile settings are saved.
$this->showForm(_('Settings saved.'), true); return _('Settings saved.');
} }
} }
function showAside() { function showAside() {
$user = common_current_user();
$this->elementStart('div', array('id' => 'aside_primary', $this->elementStart('div', array('id' => 'aside_primary',
'class' => 'aside')); 'class' => 'aside'));
@@ -451,7 +441,7 @@ class ProfilesettingsAction extends SettingsAction
'class' => 'section')); 'class' => 'section'));
$this->elementStart('ul'); $this->elementStart('ul');
if (Event::handle('StartProfileSettingsActions', array($this))) { if (Event::handle('StartProfileSettingsActions', array($this))) {
if ($user->hasRight(Right::BACKUPACCOUNT)) { if ($this->scoped->hasRight(Right::BACKUPACCOUNT)) {
$this->elementStart('li'); $this->elementStart('li');
$this->element('a', $this->element('a',
array('href' => common_local_url('backupaccount')), array('href' => common_local_url('backupaccount')),
@@ -459,7 +449,7 @@ class ProfilesettingsAction extends SettingsAction
_('Backup account')); _('Backup account'));
$this->elementEnd('li'); $this->elementEnd('li');
} }
if ($user->hasRight(Right::DELETEACCOUNT)) { if ($this->scoped->hasRight(Right::DELETEACCOUNT)) {
$this->elementStart('li'); $this->elementStart('li');
$this->element('a', $this->element('a',
array('href' => common_local_url('deleteaccount')), array('href' => common_local_url('deleteaccount')),
@@ -467,7 +457,7 @@ class ProfilesettingsAction extends SettingsAction
_('Delete account')); _('Delete account'));
$this->elementEnd('li'); $this->elementEnd('li');
} }
if ($user->hasRight(Right::RESTOREACCOUNT)) { if ($this->scoped->hasRight(Right::RESTOREACCOUNT)) {
$this->elementStart('li'); $this->elementStart('li');
$this->element('a', $this->element('a',
array('href' => common_local_url('restoreaccount')), array('href' => common_local_url('restoreaccount')),

View File

@@ -29,10 +29,6 @@
if (!defined('GNUSOCIAL')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
// Farther than any human will go
define('MAX_PUBLIC_PAGE', 100);
/** /**
* Action for displaying the public stream * Action for displaying the public stream
* *
@@ -43,54 +39,9 @@ define('MAX_PUBLIC_PAGE', 100);
* @link http://status.net/ * @link http://status.net/
* *
* @see PublicrssAction * @see PublicrssAction
* @see PublicxrdsAction
*/ */
class PublicAction extends ManagedAction class PublicAction extends SitestreamAction
{ {
/**
* page of the stream we're on; default = 1
*/
var $page = null;
var $notice;
protected $stream = null;
function isReadOnly($args)
{
return true;
}
protected function doPreparation()
{
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
if ($this->page > MAX_PUBLIC_PAGE) {
// TRANS: Client error displayed when requesting a public timeline page beyond the page limit.
// TRANS: %s is the page limit.
$this->clientError(sprintf(_('Beyond the page limit (%s).'), MAX_PUBLIC_PAGE));
}
common_set_returnto($this->selfUrl());
$this->streamPrepare();
$this->notice = $this->stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
NOTICES_PER_PAGE + 1);
if (!$this->notice) {
// TRANS: Server error displayed when a public timeline cannot be retrieved.
$this->serverError(_('Could not retrieve public timeline.'));
}
if ($this->page > 1 && $this->notice->N == 0){
// TRANS: Client error when page not found (404).
$this->clientError(_('No such page.'), 404);
}
return true;
}
protected function streamPrepare() protected function streamPrepare()
{ {
if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) { if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
@@ -117,100 +68,6 @@ class PublicAction extends ManagedAction
} }
} }
function extraHead()
{
parent::extraHead();
$this->element('meta', array('http-equiv' => 'X-XRDS-Location',
'content' => common_local_url('publicxrds')));
$rsd = common_local_url('rsd');
// RSD, http://tales.phrasewise.com/rfc/rsd
$this->element('link', array('rel' => 'EditURI',
'type' => 'application/rsd+xml',
'href' => $rsd));
if ($this->page != 1) {
$this->element('link', array('rel' => 'canonical',
'href' => common_local_url('public')));
}
}
/**
* Output <head> elements for RSS and Atom feeds
*
* @return void
*/
function getFeeds()
{
return array(new Feed(Feed::JSON,
common_local_url('ApiTimelinePublic',
array('format' => 'as')),
// TRANS: Link description for public timeline feed.
_('Public Timeline Feed (Activity Streams JSON)')),
new Feed(Feed::RSS1, common_local_url('publicrss'),
// TRANS: Link description for public timeline feed.
_('Public Timeline Feed (RSS 1.0)')),
new Feed(Feed::RSS2,
common_local_url('ApiTimelinePublic',
array('format' => 'rss')),
// TRANS: Link description for public timeline feed.
_('Public Timeline Feed (RSS 2.0)')),
new Feed(Feed::ATOM,
common_local_url('ApiTimelinePublic',
array('format' => 'atom')),
// TRANS: Link description for public timeline feed.
_('Public Timeline Feed (Atom)')));
}
function showEmptyList()
{
// TRANS: Text displayed for public feed when there are no public notices.
$message = _('This is the public timeline for %%site.name%% but no one has posted anything yet.') . ' ';
if (common_logged_in()) {
// TRANS: Additional text displayed for public feed when there are no public notices for a logged in user.
$message .= _('Be the first to post!');
}
else {
if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
// TRANS: Additional text displayed for public feed when there are no public notices for a not logged in user.
$message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
}
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
/**
* Fill the content area
*
* Shows a list of the notices in the public stream, with some pagination
* controls.
*
* @return void
*/
function showContent()
{
if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
$nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
} else {
$nl = new ThreadedNoticeList($this->notice, $this, $this->scoped);
}
$cnt = $nl->show();
if ($cnt == 0) {
$this->showEmptyList();
}
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, $this->action);
}
function showSections() function showSections()
{ {
// Show invite button, as long as site isn't closed, and // Show invite button, as long as site isn't closed, and
@@ -239,23 +96,30 @@ class PublicAction extends ManagedAction
$feat->show(); $feat->show();
} }
function showAnonymousMessage() /**
* Output <head> elements for RSS and Atom feeds
*
* @return void
*/
function getFeeds()
{ {
if (! (common_config('site','closed') || common_config('site','inviteonly'))) { return array(new Feed(Feed::JSON,
// TRANS: Message for not logged in users at an invite-only site trying to view the public feed of notices. common_local_url('ApiTimelinePublic',
// TRANS: This message contains Markdown links. Please mind the formatting. array('format' => 'as')),
$m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' . // TRANS: Link description for public timeline feed.
'based on the Free Software [StatusNet](http://status.net/) tool. ' . _('Public Timeline Feed (Activity Streams JSON)')),
'[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ' . new Feed(Feed::RSS1, common_local_url('publicrss'),
'([Read more](%%doc.help%%))'); // TRANS: Link description for public timeline feed.
} else { _('Public Timeline Feed (RSS 1.0)')),
// TRANS: Message for not logged in users at a closed site trying to view the public feed of notices. new Feed(Feed::RSS2,
// TRANS: This message contains Markdown links. Please mind the formatting. common_local_url('ApiTimelinePublic',
$m = _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' . array('format' => 'rss')),
'based on the Free Software [StatusNet](http://status.net/) tool.'); // TRANS: Link description for public timeline feed.
} _('Public Timeline Feed (RSS 2.0)')),
$this->elementStart('div', array('id' => 'anon_notice')); new Feed(Feed::ATOM,
$this->raw(common_markup_to_html($m)); common_local_url('ApiTimelinePublic',
$this->elementEnd('div'); array('format' => 'atom')),
// TRANS: Link description for public timeline feed.
_('Public Timeline Feed (Atom)')));
} }
} }

View File

@@ -28,11 +28,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/rssaction.php';
/** /**
* RSS feed for public timeline. * RSS feed for public timeline.
@@ -48,29 +44,6 @@ require_once INSTALLDIR.'/lib/rssaction.php';
*/ */
class PublicrssAction extends Rss10Action class PublicrssAction extends Rss10Action
{ {
/**
* Read arguments and initialize members
*
* @param array $args Arguments from $_REQUEST
* @return boolean success
*/
function prepare($args)
{
parent::prepare($args);
$this->notices = $this->getNotices($this->limit);
return true;
}
/**
* Initialization.
*
* @return boolean true
*/
function init()
{
return true;
}
/** /**
* Get notices * Get notices
* *
@@ -78,15 +51,10 @@ class PublicrssAction extends Rss10Action
* *
* @return array notices * @return array notices
*/ */
function getNotices($limit=0) protected function getNotices()
{ {
$notices = array(); $stream = Notice::publicStream(0, $this->limit);
$notice = Notice::publicStream(0, ($limit == 0) ? 48 : $limit); return $stream->fetchAll();
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
} }
/** /**

View File

@@ -272,10 +272,16 @@ class RecoverpasswordAction extends Action
try { try {
User::recoverPassword($nore); User::recoverPassword($nore);
$this->mode = 'sent'; $this->mode = 'sent';
if (common_is_email($nore) && common_config('site', 'fakeaddressrecovery')) {
// TRANS: User notification when recovering password by giving email address,
// regardless if the mail was sent or not (to hide registered email status).
$this->msg = _('If the email address you provided was found in the database, a recovery mail with instructions has been sent there.');
} else {
// TRANS: User notification after an e-mail with instructions was sent from the password recovery form. // TRANS: User notification after an e-mail with instructions was sent from the password recovery form.
$this->msg = _('Instructions for recovering your password ' . $this->msg = _('Instructions for recovering your password ' .
'have been sent to the email address registered to your ' . 'have been sent to the email address registered to your ' .
'account.'); 'account.');
}
$this->success = true; $this->success = true;
} catch (Exception $e) { } catch (Exception $e) {
$this->success = false; $this->success = false;
@@ -316,16 +322,7 @@ class RecoverpasswordAction extends Action
} }
// OK, we're ready to go // OK, we're ready to go
$user->setPassword($newpassword);
$original = clone($user);
$user->password = common_munge_password($newpassword, $user->id);
if (!$user->update($original)) {
common_log_db_error($user, 'UPDATE', __FILE__);
// TRANS: Reset password form validation error message.
$this->serverError(_('Cannot save new password.'));
}
$this->clearTempUser(); $this->clearTempUser();

View File

@@ -28,11 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/** /**
* Redirect to a given URL * Redirect to a given URL
@@ -47,75 +43,27 @@ if (!defined('STATUSNET')) {
* @link http://status.net/ * @link http://status.net/
*/ */
class RedirecturlAction extends Action class RedirecturlAction extends ManagedAction
{ {
protected $id = null;
protected $file = null; protected $file = null;
/** protected function doPreparation()
* For initializing members of the class.
*
* @param array $argarray misc. arguments
*
* @return boolean true
*/
function prepare($argarray)
{ {
parent::prepare($argarray); $this->file = File::getByID($this->int('id'));
$this->id = $this->trimmed('id');
if (empty($this->id)) {
// TRANS: Client exception thrown when no ID parameter was provided.
throw new ClientException(_('No id parameter.'));
}
$this->file = File::getKV('id', $this->id);
if (empty($this->file)) {
// TRANS: Client exception thrown when an invalid ID parameter was provided for a file.
// TRANS: %d is the provided ID for which the file is not present (number).
throw new ClientException(sprintf(_('No such file "%d".'),
$this->id),
404);
}
return true; return true;
} }
/** public function showPage()
* Handler method
*
* @param array $argarray is ignored since it's now passed in in prepare()
*
* @return void
*/
function handle($argarray=null)
{ {
common_redirect($this->file->url, 307); common_redirect($this->file->getUrl(false), 301);
} }
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args) function isReadOnly($args)
{ {
return true; return true;
} }
/**
* Return last modified, if applicable.
*
* MAY override
*
* @return string last modified http header
*/
function lastModified() function lastModified()
{ {
// For comparison with If-Last-Modified // For comparison with If-Last-Modified
@@ -133,9 +81,9 @@ class RedirecturlAction extends Action
*/ */
function etag() function etag()
{ {
return 'W/"' . implode(':', array($this->arg('action'), return 'W/"' . implode(':', array($this->getActionName(),
common_user_cache_hash(), common_user_cache_hash(),
common_language(), common_language(),
$this->file->id)) . '"'; $this->file->getID())) . '"';
} }
} }

View File

@@ -27,9 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
exit(1);
}
/** /**
* An action for registering a new user account * An action for registering a new user account
@@ -133,7 +131,11 @@ class RegisterAction extends Action
// TRANS: Client error displayed when trying to register while already logged in. // TRANS: Client error displayed when trying to register while already logged in.
$this->clientError(_('Already logged in.')); $this->clientError(_('Already logged in.'));
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') { } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
try {
$this->tryRegister(); $this->tryRegister();
} catch (ClientException $e) {
$this->showForm($e->getMessage());
}
} else { } else {
$this->showForm(); $this->showForm();
} }
@@ -210,9 +212,6 @@ class RegisterAction extends Action
!common_valid_http_url($homepage)) { !common_valid_http_url($homepage)) {
// TRANS: Form validation error displayed when trying to register with an invalid homepage URL. // TRANS: Form validation error displayed when trying to register with an invalid homepage URL.
$this->showForm(_('Homepage is not a valid URL.')); $this->showForm(_('Homepage is not a valid URL.'));
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
// TRANS: Form validation error displayed when trying to register with a too long full name.
$this->showForm(_('Full name is too long (maximum 255 characters).'));
} else if (Profile::bioTooLong($bio)) { } else if (Profile::bioTooLong($bio)) {
// TRANS: Form validation error on registration page when providing too long a bio text. // TRANS: Form validation error on registration page when providing too long a bio text.
// TRANS: %d is the maximum number of characters for bio; used for plural. // TRANS: %d is the maximum number of characters for bio; used for plural.
@@ -220,28 +219,22 @@ class RegisterAction extends Action
'Bio is too long (maximum %d characters).', 'Bio is too long (maximum %d characters).',
Profile::maxBio()), Profile::maxBio()),
Profile::maxBio())); Profile::maxBio()));
} else if (!is_null($location) && mb_strlen($location) > 255) {
// TRANS: Form validation error displayed when trying to register with a too long location.
$this->showForm(_('Location is too long (maximum 255 characters).'));
} else if (strlen($password) < 6) { } else if (strlen($password) < 6) {
// TRANS: Form validation error displayed when trying to register with too short a password. // TRANS: Form validation error displayed when trying to register with too short a password.
$this->showForm(_('Password must be 6 or more characters.')); $this->showForm(_('Password must be 6 or more characters.'));
} else if ($password != $confirm) { } else if ($password != $confirm) {
// TRANS: Form validation error displayed when trying to register with non-matching passwords. // TRANS: Form validation error displayed when trying to register with non-matching passwords.
$this->showForm(_('Passwords do not match.')); $this->showForm(_('Passwords do not match.'));
} else if ($user = User::register(array('nickname' => $nickname, } else {
try {
$user = User::register(array('nickname' => $nickname,
'password' => $password, 'password' => $password,
'email' => $email, 'email' => $email,
'fullname' => $fullname, 'fullname' => $fullname,
'homepage' => $homepage, 'homepage' => $homepage,
'bio' => $bio, 'bio' => $bio,
'location' => $location, 'location' => $location,
'code' => $code))) { 'code' => $code));
if (!($user instanceof User)) {
// TRANS: Form validation error displayed when trying to register with an invalid username or password.
$this->showForm(_('Invalid username or password.'));
return;
}
// success! // success!
if (!common_set_user($user)) { if (!common_set_user($user)) {
// TRANS: Server error displayed when saving fails during user registration. // TRANS: Server error displayed when saving fails during user registration.
@@ -260,9 +253,10 @@ class RegisterAction extends Action
Event::handle('EndRegistrationTry', array($this)); Event::handle('EndRegistrationTry', array($this));
$this->showSuccess(); $this->showSuccess();
} else { } catch (Exception $e) {
// TRANS: Form validation error displayed when trying to register with an invalid username or password. // TRANS: Form validation error displayed when trying to register with an invalid username or password.
$this->showForm(_('Invalid username or password.')); $this->showForm($e->getMessage());
}
} }
} }
} }
@@ -489,6 +483,7 @@ class RegisterAction extends Action
'id' => 'license', 'id' => 'license',
'class' => 'checkbox', 'class' => 'checkbox',
'name' => 'license', 'name' => 'license',
'required' => 'true',
'value' => 'true'); 'value' => 'true');
if ($this->boolean('license')) { if ($this->boolean('license')) {
$attrs['checked'] = 'checked'; $attrs['checked'] = 'checked';

View File

@@ -27,13 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/personalgroupnav.php';
require_once INSTALLDIR.'/lib/noticelist.php';
require_once INSTALLDIR.'/lib/feedlist.php';
/** /**
* List of replies * List of replies
@@ -44,72 +38,11 @@ require_once INSTALLDIR.'/lib/feedlist.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @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://status.net/
*/ */
class RepliesAction extends Action class RepliesAction extends ShowstreamAction
{ {
var $page = null; public function getStream()
var $notice;
/**
* Prepare the object
*
* Check the input values and initialize the object.
* Shows an error page on bad input.
*
* @param array $args $_REQUEST data
*
* @return boolean success flag
*/
function prepare($args)
{ {
parent::prepare($args); return new ReplyNoticeStream($this->target->getID(), $this->scoped);
$nickname = common_canonical_nickname($this->arg('nickname'));
$this->user = User::getKV('nickname', $nickname);
if (!$this->user) {
// TRANS: Client error displayed when trying to reply to a non-exsting user.
$this->clientError(_('No such user.'));
}
$profile = $this->user->getProfile();
if (!$profile) {
// TRANS: Error message displayed when referring to a user without a profile.
$this->serverError(_('User has no profile.'));
}
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
common_set_returnto($this->selfUrl());
$stream = new ReplyNoticeStream($this->user->id,
Profile::current());
$this->notice = $stream->getNotices(($this->page-1) * NOTICES_PER_PAGE,
NOTICES_PER_PAGE + 1);
if($this->page > 1 && $this->notice->N == 0){
// TRANS: Client error when page not found (404)
$this->clientError(_('No such page.'), 404);
}
return true;
}
/**
* Handle a request
*
* Just show the page. All args already handled.
*
* @param array $args $_REQUEST data
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$this->showPage();
} }
/** /**
@@ -124,12 +57,12 @@ class RepliesAction extends Action
if ($this->page == 1) { if ($this->page == 1) {
// TRANS: Title for first page of replies for a user. // TRANS: Title for first page of replies for a user.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
return sprintf(_("Replies to %s"), $this->user->nickname); return sprintf(_("Replies to %s"), $this->target->getNickname());
} else { } else {
// TRANS: Title for all but the first page of replies for a user. // TRANS: Title for all but the first page of replies for a user.
// TRANS: %1$s is a user nickname, %2$d is a page number. // TRANS: %1$s is a user nickname, %2$d is a page number.
return sprintf(_('Replies to %1$s, page %2$d'), return sprintf(_('Replies to %1$s, page %2$d'),
$this->user->nickname, $this->target->getNickname(),
$this->page); $this->page);
} }
} }
@@ -144,46 +77,39 @@ class RepliesAction extends Action
return array(new Feed(Feed::JSON, return array(new Feed(Feed::JSON,
common_local_url('ApiTimelineMentions', common_local_url('ApiTimelineMentions',
array( array(
'id' => $this->user->nickname, 'id' => $this->target->getNickname(),
'format' => 'as')), 'format' => 'as')),
// TRANS: Link for feed with replies for a user. // TRANS: Link for feed with replies for a user.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
sprintf(_('Replies feed for %s (Activity Streams JSON)'), sprintf(_('Replies feed for %s (Activity Streams JSON)'),
$this->user->nickname)), $this->target->getNickname())),
new Feed(Feed::RSS1, new Feed(Feed::RSS1,
common_local_url('repliesrss', common_local_url('repliesrss',
array('nickname' => $this->user->nickname)), array('nickname' => $this->target->getNickname())),
// TRANS: Link for feed with replies for a user. // TRANS: Link for feed with replies for a user.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
sprintf(_('Replies feed for %s (RSS 1.0)'), sprintf(_('Replies feed for %s (RSS 1.0)'),
$this->user->nickname)), $this->target->getNickname())),
new Feed(Feed::RSS2, new Feed(Feed::RSS2,
common_local_url('ApiTimelineMentions', common_local_url('ApiTimelineMentions',
array( array(
'id' => $this->user->nickname, 'id' => $this->target->getNickname(),
'format' => 'rss')), 'format' => 'rss')),
// TRANS: Link for feed with replies for a user. // TRANS: Link for feed with replies for a user.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
sprintf(_('Replies feed for %s (RSS 2.0)'), sprintf(_('Replies feed for %s (RSS 2.0)'),
$this->user->nickname)), $this->target->getNickname())),
new Feed(Feed::ATOM, new Feed(Feed::ATOM,
common_local_url('ApiTimelineMentions', common_local_url('ApiTimelineMentions',
array( array(
'id' => $this->user->nickname, 'id' => $this->target->getNickname(),
'format' => 'atom')), 'format' => 'atom')),
// TRANS: Link for feed with replies for a user. // TRANS: Link for feed with replies for a user.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
sprintf(_('Replies feed for %s (Atom)'), sprintf(_('Replies feed for %s (Atom)'),
$this->user->nickname))); $this->target->getNickname())));
} }
/**
* Show the content
*
* A list of notices that are replies to the user, plus pagination.
*
* @return void
*/
function showContent() function showContent()
{ {
$nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE)); $nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
@@ -195,33 +121,30 @@ class RepliesAction extends Action
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'replies', $this->page, 'replies',
array('nickname' => $this->user->nickname)); array('nickname' => $this->target->getNickname()));
} }
function showEmptyListMessage() function showEmptyListMessage()
{ {
// TRANS: Empty list message for page with replies for a user. // TRANS: Empty list message for page with replies for a user.
// TRANS: %1$s and %s$s are the user nickname. // TRANS: %1$s is the user nickname.
$message = sprintf(_('This is the timeline showing replies to %1$s but %2$s hasn\'t received a notice to them yet.'), $message = sprintf(_('This is the timeline showing replies to %1$s but no notices have arrived yet.'), $this->target->getNickname());
$this->user->nickname, $message .= ' '; // Spacing between this sentence and the next.
$this->user->nickname) . ' ';
if (common_logged_in()) { if (common_logged_in()) {
$current_user = common_current_user(); if ($this->target->getID() === $this->scoped->getID()) {
if ($this->user->id === $current_user->id) {
// TRANS: Empty list message for page with replies for a user for the logged in user. // TRANS: Empty list message for page with replies for a user for the logged in user.
// TRANS: This message contains a Markdown link in the form [link text](link). // TRANS: This message contains a Markdown link in the form [link text](link).
$message .= _('You can engage other users in a conversation, subscribe to more people or [join groups](%%action.groups%%).'); $message .= _('You can engage other users in a conversation, subscribe to more people or [join groups](%%action.groups%%).');
} else { } else {
// TRANS: Empty list message for page with replies for a user for all logged in users but the user themselves. // TRANS: Empty list message for page with replies for a user for all logged in users but the user themselves.
// TRANS: %1$s, %2$s and %3$s are a user nickname. This message contains a Markdown link in the form [link text](link). // TRANS: %1$s is a user nickname and %2$s is the same but with a prepended '@' character. This message contains a Markdown link in the form [link text](link).
$message .= sprintf(_('You can try to [nudge %1$s](../%2$s) or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname); $message .= sprintf(_('You can try to [nudge %1$s](../%1$s) or [post something to them](%%%%action.newnotice%%%%?content=%2$s).'), $this->target->getNickname(), '@' . $this->target->getNickname());
} }
} } else {
else {
// TRANS: Empty list message for page with replies for a user for not logged in users. // TRANS: Empty list message for page with replies for a user for not logged in users.
// TRANS: %1$s is a user nickname. This message contains a Markdown link in the form [link text](link). // TRANS: %1$s is a user nickname. This message contains a Markdown link in the form [link text](link).
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->user->nickname); $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->getNickname());
} }
$this->elementStart('div', 'guide'); $this->elementStart('div', 'guide');
@@ -229,7 +152,7 @@ class RepliesAction extends Action
$this->elementEnd('div'); $this->elementEnd('div');
} }
function isReadOnly($args) public function isReadOnly($args)
{ {
return true; return true;
} }

View File

@@ -17,70 +17,34 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
require_once(INSTALLDIR.'/lib/rssaction.php');
// Formatting of RSS handled by Rss10Action // Formatting of RSS handled by Rss10Action
class RepliesrssAction extends Rss10Action class RepliesrssAction extends TargetedRss10Action
{ {
var $user = null; protected function getNotices()
function prepare($args)
{ {
parent::prepare($args); $stream = $this->target->getReplies(0, $this->limit);
$nickname = $this->trimmed('nickname'); return $stream->fetchAll();
$this->user = User::getKV('nickname', $nickname);
if (!$this->user) {
// TRANS: Client error displayed when providing a non-existing nickname in a RSS 1.0 action.
$this->clientError(_('No such user.'));
} else {
$this->notices = $this->getNotices($this->limit);
return true;
}
}
function getNotices($limit=0)
{
$user = $this->user;
$notice = $user->getReplies(0, ($limit == 0) ? 48 : $limit);
$notices = array();
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
} }
function getChannel() function getChannel()
{ {
$user = $this->user;
$c = array('url' => common_local_url('repliesrss', $c = array('url' => common_local_url('repliesrss',
array('nickname' => array('nickname' =>
$user->nickname)), $this->target->getNickname())),
// TRANS: RSS reply feed title. %s is a user nickname. // TRANS: RSS reply feed title. %s is a user nickname.
'title' => sprintf(_("Replies to %s"), $user->nickname), 'title' => sprintf(_("Replies to %s"), $this->target->getNickname()),
'link' => common_local_url('replies', 'link' => common_local_url('replies',
array('nickname' => array('nickname' => $this->target->getNickname())),
$user->nickname)),
// TRANS: RSS reply feed description. // TRANS: RSS reply feed description.
// TRANS: %1$s is a user nickname, %2$s is the StatusNet site name. // TRANS: %1$s is a user nickname, %2$s is the StatusNet site name.
'description' => sprintf(_('Replies to %1$s on %2$s.'), 'description' => sprintf(_('Replies to %1$s on %2$s.'),
$user->nickname, common_config('site', 'name'))); $this->target->getNickname(), common_config('site', 'name')));
return $c; return $c;
} }
function getImage()
{
$profile = $this->user->getProfile();
return $profile->avatarUrl(AVATAR_PROFILE_SIZE);
}
function isReadOnly($args) function isReadOnly($args)
{ {
return true; return true;

View File

@@ -27,9 +27,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Prints out a static robots.txt * Prints out a static robots.txt
@@ -40,19 +38,9 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/ * @link http://status.net/
*/ */
class RobotstxtAction extends Action class RobotstxtAction extends ManagedAction
{ {
/** public function showPage()
* Handles requests
*
* Since this is a relatively static document, we
* don't do a prepare()
*
* @param array $args GET, POST, and URL params; unused.
*
* @return void
*/
function handle($args)
{ {
if (Event::handle('StartRobotsTxt', array($this))) { if (Event::handle('StartRobotsTxt', array($this))) {

View File

@@ -151,7 +151,7 @@ class RsdAction extends Action
$this->elementStart('api', $apiAttrs); $this->elementStart('api', $apiAttrs);
$this->elementStart('settings'); $this->elementStart('settings');
$this->element('docs', null, $this->element('docs', null,
'http://status.net/wiki/TwitterCompatibleAPI'); common_local_url('doc', array('title' => 'api')));
$this->element('setting', array('name' => 'OAuth'), $this->element('setting', array('name' => 'OAuth'),
'true'); 'true');
$this->elementEnd('settings'); $this->elementEnd('settings');

View File

@@ -70,10 +70,11 @@ class ShownoticeAction extends ManagedAction
{ {
parent::prepare($args); parent::prepare($args);
if ($this->boolean('ajax')) { if ($this->boolean('ajax')) {
StatusNet::setApi(true); GNUsocial::setApi(true);
} }
$this->notice = $this->getNotice(); $this->notice = $this->getNotice();
$this->target = $this->notice;
if (!$this->notice->inScope($this->scoped)) { if (!$this->notice->inScope($this->scoped)) {
// TRANS: Client exception thrown when trying a view a notice the user has no access to. // TRANS: Client exception thrown when trying a view a notice the user has no access to.
@@ -113,20 +114,22 @@ class ShownoticeAction extends ManagedAction
{ {
$id = $this->arg('notice'); $id = $this->arg('notice');
$notice = Notice::getKV('id', $id); $notice = null;
if ($notice instanceof Notice) { try {
$notice = Notice::getByID($id);
// Alright, got it! // Alright, got it!
return $notice; return $notice;
} } catch (NoResultException $e) {
// Hm, not found.
// Did we use to have it, and it got deleted? $deleted = null;
$deleted = Deleted_notice::getKV('id', $id); Event::handle('IsNoticeDeleted', array($id, &$deleted));
if ($deleted instanceof Deleted_notice) { if ($deleted === true) {
// TRANS: Client error displayed trying to show a deleted notice. // TRANS: Client error displayed trying to show a deleted notice.
$this->clientError(_('Notice deleted.'), 410); throw new ClientException(_('Notice deleted.'), 410);
}
} }
// TRANS: Client error displayed trying to show a non-existing notice. // TRANS: Client error displayed trying to show a non-existing notice.
$this->clientError(_('No such notice.'), 404); throw new ClientException(_('No such notice.'), 404);
} }
/** /**
@@ -211,36 +214,35 @@ class ShownoticeAction extends ManagedAction
{ {
} }
/** function getFeeds()
* Don't show aside {
* return array(new Feed(Feed::JSON,
* @return void common_local_url('ApiStatusesShow',
*/ array(
function showAside() { 'id' => $this->target->getID(),
'format' => 'json')),
// TRANS: Title for link to single notice representation.
// TRANS: %s is a user nickname.
sprintf(_('Single notice (JSON)'))),
new Feed(Feed::ATOM,
common_local_url('ApiStatusesShow',
array(
'id' => $this->target->getID(),
'format' => 'atom')),
// TRANS: Title for link to notice feed.
// TRANS: %s is a user nickname.
sprintf(_('Single notice (Atom)'))));
} }
/** /**
* Extra <head> content * Extra <head> content
* *
* We show the microid(s) for the author, if any. * Facebook OpenGraph metadata.
* *
* @return void * @return void
*/ */
function extraHead() function extraHead()
{ {
$user = User::getKV($this->profile->id);
if (!$user instanceof User) {
return;
}
if ($user->emailmicroid && $user->email && $this->notice->uri) {
$id = new Microid('mailto:'. $user->email,
$this->notice->uri);
$this->element('meta', array('name' => 'microid',
'content' => $id->toString()));
}
// Extras to aid in sharing notices to Facebook // Extras to aid in sharing notices to Facebook
$avatarUrl = $this->profile->avatarUrl(AVATAR_PROFILE_SIZE); $avatarUrl = $this->profile->avatarUrl(AVATAR_PROFILE_SIZE);
$this->element('meta', array('property' => 'og:image', $this->element('meta', array('property' => 'og:image',

View File

@@ -22,97 +22,31 @@
* @link http://status.net * @link http://status.net
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/profileminilist.php'; class ShowprofiletagAction extends ShowstreamAction
require_once INSTALLDIR.'/lib/peopletaglist.php';
require_once INSTALLDIR.'/lib/noticelist.php';
require_once INSTALLDIR.'/lib/feedlist.php';
class ShowprofiletagAction extends Action
{ {
var $notice, $tagger, $peopletag, $userProfile; var $notice, $peopletag;
function isReadOnly($args) protected function doStreamPreparation()
{ {
return true; $tag = common_canonical_tag($this->arg('tag'));
} try {
$this->peopletag = Profile_list::getByPK(array('tagger' => $this->target->getID(), 'tag' => $tag));
function prepare($args) } catch (NoResultException $e) {
{
parent::prepare($args);
if (common_config('singleuser', 'enabled')) {
$tagger_arg = User::singleUserNickname();
} else {
$tagger_arg = $this->arg('tagger');
}
$tag_arg = $this->arg('tag');
$tagger = common_canonical_nickname($tagger_arg);
$tag = common_canonical_tag($tag_arg);
// Permanent redirect on non-canonical nickname
if ($tagger_arg != $tagger || $tag_arg != $tag) {
$args = array('tagger' => $nickname, 'tag' => $tag);
if ($this->page != 1) {
$args['page'] = $this->page;
}
common_redirect(common_local_url('showprofiletag', $args), 301);
}
if (!$tagger) {
// TRANS: Client error displayed when a tagger is expected but not provided.
$this->clientError(_('No tagger.'), 404);
}
$user = User::getKV('nickname', $tagger);
if (!$user) {
// TRANS: Client error displayed trying to perform an action related to a non-existing user.
$this->clientError(_('No such user.'), 404);
}
$this->tagger = $user->getProfile();
$this->peopletag = Profile_list::pkeyGet(array('tagger' => $user->id, 'tag' => $tag));
$current = common_current_user();
$can_see = !empty($this->peopletag) && (!$this->peopletag->private ||
($this->peopletag->private && $this->peopletag->tagger === $current->id));
if (!$can_see) {
// TRANS: Client error displayed trying to reference a non-existing list. // TRANS: Client error displayed trying to reference a non-existing list.
$this->clientError(_('No such list.'), 404); throw new ClientException('No such list.');
} }
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; if ($this->peopletag->private && !$this->peopletag->getTagger()->sameAs($this->scoped)) {
$this->userProfile = Profile::current(); // TRANS: Client error displayed trying to reference a non-existing list.
throw new AuthorizationException('You do not have permission to see this list.');
$stream = new PeopletagNoticeStream($this->peopletag, $this->userProfile); }
$this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
NOTICES_PER_PAGE + 1);
if ($this->page > 1 && $this->notice->N == 0) {
// TRANS: Client error when page not found (404).
$this->clientError(_('No such page.'), 404);
} }
return true; public function getStream()
}
function handle($args)
{ {
parent::handle($args); return new PeopletagNoticeStream($this->peopletag, $this->scoped);
if (!$this->peopletag) {
// TRANS: Client error displayed trying to perform an action related to a non-existing user.
$this->clientError(_('No such user.'));
}
$this->showPage();
} }
function title() function title()
@@ -137,7 +71,7 @@ class ShowprofiletagAction extends Action
// TRANS: %1$s is a list, %2$s is the tagger's nickname, %3$d is a page number. // TRANS: %1$s is a list, %2$s is the tagger's nickname, %3$d is a page number.
return sprintf(_('Timeline for %1$s list by %2$s, page %3$d'), return sprintf(_('Timeline for %1$s list by %2$s, page %3$d'),
$this->peopletag->tag, $this->peopletag->tag,
$this->tagger->nickname, $this->target->getNickname(),
$this->page $this->page
); );
} else { } else {
@@ -160,7 +94,7 @@ class ShowprofiletagAction extends Action
// TRANS: %1$s is a list, %2$s is the tagger's nickname. // TRANS: %1$s is a list, %2$s is the tagger's nickname.
return sprintf(_('Timeline for %1$s list by %2$s'), return sprintf(_('Timeline for %1$s list by %2$s'),
$this->peopletag->tag, $this->peopletag->tag,
$this->tagger->nickname $this->target->getNickname()
); );
} }
} }
@@ -171,29 +105,29 @@ class ShowprofiletagAction extends Action
return array(new Feed(Feed::JSON, return array(new Feed(Feed::JSON,
common_local_url( common_local_url(
'ApiTimelineList', array( 'ApiTimelineList', array(
'user' => $this->tagger->id, 'user' => $this->target->id,
'id' => $this->peopletag->id, 'id' => $this->peopletag->id,
'format' => 'as' 'format' => 'as'
) )
), ),
// TRANS: Feed title. // TRANS: Feed title.
// TRANS: %s is tagger's nickname. // TRANS: %s is tagger's nickname.
sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->tagger->nickname)), sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->target->getNickname())),
new Feed(Feed::RSS2, new Feed(Feed::RSS2,
common_local_url( common_local_url(
'ApiTimelineList', array( 'ApiTimelineList', array(
'user' => $this->tagger->id, 'user' => $this->target->id,
'id' => $this->peopletag->id, 'id' => $this->peopletag->id,
'format' => 'rss' 'format' => 'rss'
) )
), ),
// TRANS: Feed title. // TRANS: Feed title.
// TRANS: %s is tagger's nickname. // TRANS: %s is tagger's nickname.
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->tagger->nickname)), sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->target->getNickname())),
new Feed(Feed::ATOM, new Feed(Feed::ATOM,
common_local_url( common_local_url(
'ApiTimelineList', array( 'ApiTimelineList', array(
'user' => $this->tagger->id, 'user' => $this->target->id,
'id' => $this->peopletag->id, 'id' => $this->peopletag->id,
'format' => 'atom' 'format' => 'atom'
) )
@@ -201,7 +135,7 @@ class ShowprofiletagAction extends Action
// TRANS: Feed title. // TRANS: Feed title.
// TRANS: %1$s is a list, %2$s is tagger's nickname. // TRANS: %1$s is a list, %2$s is tagger's nickname.
sprintf(_('Feed for %1$s list by %2$s (Atom)'), sprintf(_('Feed for %1$s list by %2$s (Atom)'),
$this->peopletag->tag, $this->tagger->nickname $this->peopletag->tag, $this->target->getNickname()
) )
) )
); );
@@ -219,11 +153,10 @@ class ShowprofiletagAction extends Action
// TRANS: %1$s is a list, %2$s is a tagger's nickname. // TRANS: %1$s is a list, %2$s is a tagger's nickname.
$message = sprintf(_('This is the timeline for %1$s list by %2$s but no one has posted anything yet.'), $message = sprintf(_('This is the timeline for %1$s list by %2$s but no one has posted anything yet.'),
$this->peopletag->tag, $this->peopletag->tag,
$this->tagger->nickname) . ' '; $this->target->getNickname()) . ' ';
if (common_logged_in()) { if (common_logged_in()) {
$current_user = common_current_user(); if ($this->target->sameAs($this->scoped)) {
if ($this->tagger->id == $current_user->id) {
// TRANS: Additional empty list message for list timeline for currently logged in user tagged tags. // TRANS: Additional empty list message for list timeline for currently logged in user tagged tags.
$message .= _('Try tagging more people.'); $message .= _('Try tagging more people.');
} }
@@ -238,16 +171,15 @@ class ShowprofiletagAction extends Action
$this->elementEnd('div'); $this->elementEnd('div');
} }
function showContent() protected function showContent()
{ {
$this->showPeopletag(); $this->showPeopletag();
$this->showNotices(); parent::showContent();
} }
function showPeopletag() function showPeopletag()
{ {
$cur = common_current_user(); $tag = new Peopletag($this->peopletag, $this->scoped, $this);
$tag = new Peopletag($this->peopletag, $cur, $this);
$tag->show(); $tag->show();
} }
@@ -267,7 +199,7 @@ class ShowprofiletagAction extends Action
$this->page, $this->page,
'showprofiletag', 'showprofiletag',
array('tag' => $this->peopletag->tag, array('tag' => $this->peopletag->tag,
'tagger' => $this->tagger->nickname) 'nickname' => $this->target->getNickname())
); );
Event::handle('EndShowProfileTagContent', array($this)); Event::handle('EndShowProfileTagContent', array($this));
@@ -283,11 +215,6 @@ class ShowprofiletagAction extends Action
# $this->showStatistics(); # $this->showStatistics();
} }
function showPageTitle()
{
$this->element('h1', null, $this->title());
}
function showTagged() function showTagged()
{ {
$profile = $this->peopletag->getTagged(0, PROFILES_PER_MINILIST + 1); $profile = $this->peopletag->getTagged(0, PROFILES_PER_MINILIST + 1);
@@ -314,7 +241,7 @@ class ShowprofiletagAction extends Action
if ($cnt > PROFILES_PER_MINILIST) { if ($cnt > PROFILES_PER_MINILIST) {
$this->elementStart('p'); $this->elementStart('p');
$this->element('a', array('href' => common_local_url('taggedprofiles', $this->element('a', array('href' => common_local_url('taggedprofiles',
array('nickname' => $this->tagger->nickname, array('nickname' => $this->target->getNickname(),
'profiletag' => $this->peopletag->tag)), 'profiletag' => $this->peopletag->tag)),
'class' => 'more'), 'class' => 'more'),
// TRANS: Link for more "People in list x by a user" // TRANS: Link for more "People in list x by a user"
@@ -356,20 +283,3 @@ class ShowprofiletagAction extends Action
$this->elementEnd('div'); $this->elementEnd('div');
} }
} }
class Peopletag extends PeopletagListItem
{
protected $avatarSize = AVATAR_PROFILE_SIZE;
function showStart()
{
$mode = $this->peopletag->private ? 'private' : 'public';
$this->out->elementStart('div', array('class' => 'h-entry peopletag peopletag-profile mode-'.$mode,
'id' => 'peopletag-' . $this->peopletag->id));
}
function showEnd()
{
$this->out->elementEnd('div');
}
}

View File

@@ -28,15 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/personalgroupnav.php';
require_once INSTALLDIR.'/lib/noticelist.php';
require_once INSTALLDIR.'/lib/profileminilist.php';
require_once INSTALLDIR.'/lib/groupminilist.php';
require_once INSTALLDIR.'/lib/feedlist.php';
/** /**
* User profile page * User profile page
@@ -53,29 +45,22 @@ require_once INSTALLDIR.'/lib/feedlist.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @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://status.net/
*/ */
class ShowstreamAction extends ProfileAction class ShowstreamAction extends NoticestreamAction
{ {
var $notice; public function getStream()
protected function prepare(array $args=array())
{ {
parent::prepare($args);
if (empty($this->tag)) { if (empty($this->tag)) {
$stream = new ProfileNoticeStream($this->profile, $this->scoped); $stream = new ProfileNoticeStream($this->target, $this->scoped);
} else { } else {
$stream = new TaggedProfileNoticeStream($this->profile, $this->tag, $this->scoped); $stream = new TaggedProfileNoticeStream($this->target, $this->tag, $this->scoped);
} }
$this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); return $stream;
return true;
} }
function title() function title()
{ {
$base = $this->profile->getFancyName(); $base = $this->target->getFancyName();
if (!empty($this->tag)) { if (!empty($this->tag)) {
if ($this->page == 1) { if ($this->page == 1) {
// TRANS: Page title showing tagged notices in one user's timeline. // TRANS: Page title showing tagged notices in one user's timeline.
@@ -88,7 +73,7 @@ class ShowstreamAction extends ProfileAction
} }
} else { } else {
if ($this->page == 1) { if ($this->page == 1) {
return $base; return sprintf(_('Notices by %s'), $base);
} else { } else {
// TRANS: Extended page title showing tagged notices in one user's timeline. // TRANS: Extended page title showing tagged notices in one user's timeline.
// TRANS: %1$s is the username, %2$d is the page number. // TRANS: %1$s is the username, %2$d is the page number.
@@ -99,14 +84,14 @@ class ShowstreamAction extends ProfileAction
} }
} }
function showContent() protected function showContent()
{ {
$this->showNotices(); $this->showNotices();
} }
function showProfileBlock() function showProfileBlock()
{ {
$block = new AccountProfileBlock($this, $this->profile); $block = new AccountProfileBlock($this, $this->target);
$block->show(); $block->show();
} }
@@ -120,78 +105,71 @@ class ShowstreamAction extends ProfileAction
if (!empty($this->tag)) { if (!empty($this->tag)) {
return array(new Feed(Feed::RSS1, return array(new Feed(Feed::RSS1,
common_local_url('userrss', common_local_url('userrss',
array('nickname' => $this->target->nickname, array('nickname' => $this->target->getNickname(),
'tag' => $this->tag)), 'tag' => $this->tag)),
// TRANS: Title for link to notice feed. // TRANS: Title for link to notice feed.
// TRANS: %1$s is a user nickname, %2$s is a hashtag. // TRANS: %1$s is a user nickname, %2$s is a hashtag.
sprintf(_('Notice feed for %1$s tagged %2$s (RSS 1.0)'), sprintf(_('Notice feed for %1$s tagged %2$s (RSS 1.0)'),
$this->target->nickname, $this->tag))); $this->target->getNickname(), $this->tag)));
} }
return array(new Feed(Feed::JSON, return array(new Feed(Feed::JSON,
common_local_url('ApiTimelineUser', common_local_url('ApiTimelineUser',
array( array(
'id' => $this->user->id, 'id' => $this->target->getID(),
'format' => 'as')), 'format' => 'as')),
// TRANS: Title for link to notice feed. // TRANS: Title for link to notice feed.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
sprintf(_('Notice feed for %s (Activity Streams JSON)'), sprintf(_('Notice feed for %s (Activity Streams JSON)'),
$this->target->nickname)), $this->target->getNickname())),
new Feed(Feed::RSS1, new Feed(Feed::RSS1,
common_local_url('userrss', common_local_url('userrss',
array('nickname' => $this->target->nickname)), array('nickname' => $this->target->getNickname())),
// TRANS: Title for link to notice feed. // TRANS: Title for link to notice feed.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
sprintf(_('Notice feed for %s (RSS 1.0)'), sprintf(_('Notice feed for %s (RSS 1.0)'),
$this->target->nickname)), $this->target->getNickname())),
new Feed(Feed::RSS2, new Feed(Feed::RSS2,
common_local_url('ApiTimelineUser', common_local_url('ApiTimelineUser',
array( array(
'id' => $this->user->id, 'id' => $this->target->getID(),
'format' => 'rss')), 'format' => 'rss')),
// TRANS: Title for link to notice feed. // TRANS: Title for link to notice feed.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
sprintf(_('Notice feed for %s (RSS 2.0)'), sprintf(_('Notice feed for %s (RSS 2.0)'),
$this->target->nickname)), $this->target->getNickname())),
new Feed(Feed::ATOM, new Feed(Feed::ATOM,
common_local_url('ApiTimelineUser', common_local_url('ApiTimelineUser',
array( array(
'id' => $this->user->id, 'id' => $this->target->getID(),
'format' => 'atom')), 'format' => 'atom')),
// TRANS: Title for link to notice feed. // TRANS: Title for link to notice feed.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
sprintf(_('Notice feed for %s (Atom)'), sprintf(_('Notice feed for %s (Atom)'),
$this->target->nickname)), $this->target->getNickname())),
new Feed(Feed::FOAF, new Feed(Feed::FOAF,
common_local_url('foaf', array('nickname' => common_local_url('foaf', array('nickname' =>
$this->target->nickname)), $this->target->getNickname())),
// TRANS: Title for link to notice feed. FOAF stands for Friend of a Friend. // TRANS: Title for link to notice feed. FOAF stands for Friend of a Friend.
// TRANS: More information at http://www.foaf-project.org. %s is a user nickname. // TRANS: More information at http://www.foaf-project.org. %s is a user nickname.
sprintf(_('FOAF for %s'), $this->target->nickname))); sprintf(_('FOAF for %s'), $this->target->getNickname())));
} }
function extraHead() function extraHead()
{ {
if ($this->profile->bio) { if ($this->target->bio) {
$this->element('meta', array('name' => 'description', $this->element('meta', array('name' => 'description',
'content' => $this->profile->bio)); 'content' => $this->target->getDescription()));
}
if ($this->user->emailmicroid && $this->user->email && $this->profile->profileurl) {
$id = new Microid('mailto:'.$this->user->email,
$this->selfUrl());
$this->element('meta', array('name' => 'microid',
'content' => $id->toString()));
} }
// See https://wiki.mozilla.org/Microsummaries // See https://wiki.mozilla.org/Microsummaries
$this->element('link', array('rel' => 'microsummary', $this->element('link', array('rel' => 'microsummary',
'href' => common_local_url('microsummary', 'href' => common_local_url('microsummary',
array('nickname' => $this->profile->nickname)))); array('nickname' => $this->target->getNickname()))));
$rsd = common_local_url('rsd', $rsd = common_local_url('rsd',
array('nickname' => $this->profile->nickname)); array('nickname' => $this->target->getNickname()));
// RSD, http://tales.phrasewise.com/rfc/rsd // RSD, http://tales.phrasewise.com/rfc/rsd
$this->element('link', array('rel' => 'EditURI', $this->element('link', array('rel' => 'EditURI',
@@ -200,30 +178,29 @@ class ShowstreamAction extends ProfileAction
if ($this->page != 1) { if ($this->page != 1) {
$this->element('link', array('rel' => 'canonical', $this->element('link', array('rel' => 'canonical',
'href' => $this->profile->profileurl)); 'href' => $this->target->getUrl()));
} }
} }
function showEmptyListMessage() function showEmptyListMessage()
{ {
// TRANS: First sentence of empty list message for a timeline. $1%s is a user nickname. // TRANS: First sentence of empty list message for a timeline. $1%s is a user nickname.
$message = sprintf(_('This is the timeline for %1$s, but %1$s hasn\'t posted anything yet.'), $this->target->nickname) . ' '; $message = sprintf(_('This is the timeline for %1$s, but %1$s hasn\'t posted anything yet.'), $this->target->getNickname()) . ' ';
if (common_logged_in()) { if ($this->scoped instanceof Profile) {
$current_user = common_current_user(); if ($this->target->getID() === $this->scoped->getID()) {
if ($this->user->id === $current_user->id) {
// TRANS: Second sentence of empty list message for a stream for the user themselves. // TRANS: Second sentence of empty list message for a stream for the user themselves.
$message .= _('Seen anything interesting recently? You haven\'t posted any notices yet, now would be a good time to start :)'); $message .= _('Seen anything interesting recently? You haven\'t posted any notices yet, now would be a good time to start :)');
} else { } else {
// TRANS: Second sentence of empty list message for a non-self timeline. %1$s is a user nickname, %2$s is a part of a URL. // TRANS: Second sentence of empty list message for a non-self timeline. %1$s is a user nickname, %2$s is a part of a URL.
// TRANS: This message contains a Markdown link. Keep "](" together. // TRANS: This message contains a Markdown link. Keep "](" together.
$message .= sprintf(_('You can try to nudge %1$s or [post something to them](%%%%action.newnotice%%%%?status_textarea=%2$s).'), $this->target->nickname, '@' . $this->target->nickname); $message .= sprintf(_('You can try to nudge %1$s or [post something to them](%%%%action.newnotice%%%%?status_textarea=%2$s).'), $this->target->getNickname(), '@' . $this->target->getNickname());
} }
} }
else { else {
// TRANS: Second sentence of empty message for anonymous users. %s is a user nickname. // TRANS: Second sentence of empty message for anonymous users. %s is a user nickname.
// TRANS: This message contains a Markdown link. Keep "](" together. // TRANS: This message contains a Markdown link. Keep "](" together.
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->nickname); $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->getNickname());
} }
$this->elementStart('div', 'guide'); $this->elementStart('div', 'guide');
@@ -233,16 +210,13 @@ class ShowstreamAction extends ProfileAction
function showNotices() function showNotices()
{ {
$pnl = null; $pnl = new NoticeList($this->notice, $this);
if (Event::handle('ShowStreamNoticeList', array($this->notice, $this, &$pnl))) {
$pnl = new ProfileNoticeList($this->notice, $this);
}
$cnt = $pnl->show(); $cnt = $pnl->show();
if (0 == $cnt) { if (0 == $cnt) {
$this->showEmptyListMessage(); $this->showEmptyListMessage();
} }
$args = array('nickname' => $this->target->nickname); $args = array('nickname' => $this->target->getNickname());
if (!empty($this->tag)) if (!empty($this->tag))
{ {
$args['tag'] = $this->tag; $args['tag'] = $this->tag;
@@ -259,13 +233,13 @@ class ShowstreamAction extends ProfileAction
$m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' . $m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'based on the Free Software [StatusNet](http://status.net/) tool. ' . 'based on the Free Software [StatusNet](http://status.net/) tool. ' .
'[Join now](%%%%action.register%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'), '[Join now](%%%%action.register%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'),
$this->target->nickname, $this->target->nickname); $this->target->getNickname(), $this->target->getNickname());
} else { } else {
// TRANS: Announcement for anonymous users showing a timeline if site registrations are closed or invite only. // TRANS: Announcement for anonymous users showing a timeline if site registrations are closed or invite only.
// TRANS: This message contains a Markdown link. Keep "](" together. // TRANS: This message contains a Markdown link. Keep "](" together.
$m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' . $m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'based on the Free Software [StatusNet](http://status.net/) tool.'), 'based on the Free Software [StatusNet](http://status.net/) tool.'),
$this->target->nickname, $this->target->nickname); $this->target->getNickname(), $this->target->getNickname());
} }
$this->elementStart('div', array('id' => 'anon_notice')); $this->elementStart('div', array('id' => 'anon_notice'));
$this->raw(common_markup_to_html($m)); $this->raw(common_markup_to_html($m));
@@ -276,7 +250,7 @@ class ShowstreamAction extends ProfileAction
{ {
parent::showSections(); parent::showSections();
if (!common_config('performance', 'high')) { if (!common_config('performance', 'high')) {
$cloud = new PersonalTagCloudSection($this, $this->user); $cloud = new PersonalTagCloudSection($this->target, $this);
$cloud->show(); $cloud->show();
} }
} }
@@ -284,66 +258,11 @@ class ShowstreamAction extends ProfileAction
function noticeFormOptions() function noticeFormOptions()
{ {
$options = parent::noticeFormOptions(); $options = parent::noticeFormOptions();
$cur = common_current_user();
if (empty($cur) || $cur->id != $this->profile->id) { if (!$this->scoped instanceof Profile || !$this->scoped->sameAs($this->target)) {
$options['to_profile'] = $this->profile; $options['to_profile'] = $this->target;
} }
return $options; return $options;
} }
} }
// We don't show the author for a profile, since we already know who it is!
/**
* Slightly modified from standard list; the author & avatar are hidden
* in CSS. We used to remove them here too, but as it turns out that
* confuses the inline reply code... and we hide them in CSS anyway
* since realtime updates come through in original form.
*
* Remaining customization right now is for the repeat marker, where
* it'll list who the original poster was instead of who did the repeat
* (since the repeater is you, and the repeatee isn't shown!)
* This will remain inconsistent if realtime updates come through,
* since those'll get rendered as a regular NoticeListItem.
*/
class ProfileNoticeList extends NoticeList
{
function newListItem($notice)
{
return new ProfileNoticeListItem($notice, $this->out);
}
}
class ProfileNoticeListItem extends DoFollowListItem
{
/**
* show a link to the author of repeat
*
* @return void
*/
function showRepeat()
{
if (!empty($this->repeat)) {
// FIXME: this code is almost identical to default; need to refactor
$attrs = array('href' => $this->profile->profileurl,
'class' => 'url');
if (!empty($this->profile->fullname)) {
$attrs['title'] = $this->profile->getFancyName();
}
$this->out->elementStart('span', 'repeat');
$text_link = XMLStringer::estring('a', $attrs, $this->profile->nickname);
// TRANS: Link to the author of a repeated notice. %s is a linked nickname.
$this->out->raw(sprintf(_('Repeat of %s'), $text_link));
$this->out->elementEnd('span');
}
}
}

View File

@@ -27,9 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Silence a user. * Silence a user.
@@ -42,45 +40,11 @@ if (!defined('STATUSNET')) {
*/ */
class SilenceAction extends ProfileFormAction class SilenceAction extends ProfileFormAction
{ {
/**
* Check parameters
*
* @param array $args action arguments (URL, GET, POST)
*
* @return boolean success flag
*/
function prepare($args)
{
if (!parent::prepare($args)) {
return false;
}
$cur = common_current_user();
assert(!empty($cur)); // checked by parent
if (!$cur->hasRight(Right::SILENCEUSER)) {
// TRANS: Client error displayed trying to silence a user on a site where the feature is not enabled.
$this->clientError(_('You cannot silence users on this site.'));
}
assert(!empty($this->profile)); // checked by parent
if ($this->profile->isSilenced()) {
// TRANS: Client error displayed trying to silence an already silenced user.
$this->clientError(_('User is already silenced.'));
}
return true;
}
/**
* Silence a user.
*
* @return void
*/
function handlePost() function handlePost()
{ {
$this->profile->silence(); assert($this->scoped instanceof Profile);
assert($this->profile instanceof Profile);
$this->profile->silenceAs($this->scoped);
} }
} }

View File

@@ -27,11 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
/** /**
* Update the site-wide notice text * Update the site-wide notice text
@@ -114,13 +110,9 @@ class SitenoticeadminpanelAction extends AdminPanelAction
} }
// scrub HTML input // scrub HTML input
require_once INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php';
$config = array( $purifier = new HTMLPurifier();
'safe' => 1, $siteNotice = $purifier->purify($siteNotice);
'deny_attribute' => 'id,style,on*'
);
$siteNotice = htmLawed($siteNotice, $config);
} }
} }

View File

@@ -27,9 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Settings for SMS * Settings for SMS
@@ -45,6 +43,14 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
class SmssettingsAction extends SettingsAction class SmssettingsAction extends SettingsAction
{ {
protected function doPreparation()
{
if (!common_config('sms', 'enabled')) {
// TRANS: Message given in the SMS settings if SMS is not enabled on the site.
throw new ServerException(_('SMS is not available.'));
}
}
/** /**
* Title of the page * Title of the page
* *
@@ -86,14 +92,7 @@ class SmssettingsAction extends SettingsAction
*/ */
function showContent() function showContent()
{ {
if (!common_config('sms', 'enabled')) { $user = $this->scoped->getUser();
$this->element('div', array('class' => 'error'),
// TRANS: Message given in the SMS settings if SMS is not enabled on the site.
_('SMS is not available.'));
return;
}
$user = common_current_user();
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_sms', 'id' => 'form_settings_sms',
@@ -118,8 +117,8 @@ class SmssettingsAction extends SettingsAction
// TRANS: Button label to remove a confirmed SMS address. // TRANS: Button label to remove a confirmed SMS address.
$this->submit('remove', _m('BUTTON','Remove')); $this->submit('remove', _m('BUTTON','Remove'));
} else { } else {
try {
$confirm = $this->getConfirmation(); $confirm = $this->getConfirmation();
if ($confirm) {
$carrier = Sms_carrier::getKV($confirm->address_extra); $carrier = Sms_carrier::getKV($confirm->address_extra);
$this->element('p', 'form_unconfirmed', $this->element('p', 'form_unconfirmed',
$confirm->address . ' (' . $carrier->name . ')'); $confirm->address . ' (' . $carrier->name . ')');
@@ -141,7 +140,7 @@ class SmssettingsAction extends SettingsAction
$this->elementEnd('ul'); $this->elementEnd('ul');
// TRANS: Button label to confirm SMS confirmation code in SMS settings. // TRANS: Button label to confirm SMS confirmation code in SMS settings.
$this->submit('confirm', _m('BUTTON','Confirm')); $this->submit('confirm', _m('BUTTON','Confirm'));
} else { } catch (NoResultException $e) {
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label for SMS phone number input in SMS settings form. // TRANS: Field label for SMS phone number input in SMS settings form.
@@ -216,60 +215,38 @@ class SmssettingsAction extends SettingsAction
*/ */
function getConfirmation() function getConfirmation()
{ {
$user = common_current_user();
$confirm = new Confirm_address(); $confirm = new Confirm_address();
$confirm->user_id = $user->id; $confirm->user_id = $this->scoped->getID();
$confirm->address_type = 'sms'; $confirm->address_type = 'sms';
if ($confirm->find(true)) { if ($confirm->find(true)) {
return $confirm; return $confirm;
} else {
return null;
}
} }
/** throw new NoResultException($confirm);
* Handle posts to this form }
*
* Based on the button that was pressed, muxes out to other functions protected function doPost()
* 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(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
if ($this->arg('save')) { if ($this->arg('save')) {
$this->savePreferences(); return $this->savePreferences();
} else if ($this->arg('add')) { } else if ($this->arg('add')) {
$this->addAddress(); return $this->addAddress();
} else if ($this->arg('cancel')) { } else if ($this->arg('cancel')) {
$this->cancelConfirmation(); return $this->cancelConfirmation();
} else if ($this->arg('remove')) { } else if ($this->arg('remove')) {
$this->removeAddress(); return $this->removeAddress();
} else if ($this->arg('removeincoming')) { } else if ($this->arg('removeincoming')) {
$this->removeIncoming(); return $this->removeIncoming();
} else if ($this->arg('newincoming')) { } else if ($this->arg('newincoming')) {
$this->newIncoming(); return $this->newIncoming();
} else if ($this->arg('confirm')) { } else if ($this->arg('confirm')) {
$this->confirmCode(); return $this->confirmCode();
} else {
// TRANS: Message given submitting a form with an unknown action in SMS settings.
$this->showForm(_('Unexpected form submission.'));
} }
// TRANS: Message given submitting a form with an unknown action in SMS settings.
throw new ClientException(_('Unexpected form submission.'));
} }
/** /**
@@ -281,30 +258,26 @@ class SmssettingsAction extends SettingsAction
*/ */
function savePreferences() function savePreferences()
{ {
$smsnotify = $this->boolean('smsnotify'); $user = $this->scoped->getUser();
$user = common_current_user();
assert(!is_null($user)); // should already be checked
$user->query('BEGIN'); $user->query('BEGIN');
$original = clone($user); $original = clone($user);
$user->smsnotify = $smsnotify; $user->smsnotify = $this->boolean('smsnotify');
$result = $user->update($original); $result = $user->update($original);
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);
// TRANS: Server error thrown on database error updating SMS preferences. // TRANS: Server error thrown on database error updating SMS preferences.
$this->serverError(_('Could not update user.')); throw new ServerException(_('Could not update user.'));
} }
$user->query('COMMIT'); $user->query('COMMIT');
// TRANS: Confirmation message for successful SMS preferences save. // TRANS: Confirmation message for successful SMS preferences save.
$this->showForm(_('SMS preferences saved.'), true); return _('SMS preferences saved.');
} }
/** /**
@@ -324,28 +297,24 @@ class SmssettingsAction extends SettingsAction
// Some validation // Some validation
if (!$sms) { if (empty($sms)) {
// TRANS: Message given saving SMS phone number without having provided one. // TRANS: Message given saving SMS phone number without having provided one.
$this->showForm(_('No phone number.')); throw new ClientException(_('No phone number.'));
return;
} }
if (!$carrier_id) { if (empty($carrier_id)) {
// TRANS: Message given saving SMS phone number without having selected a carrier. // TRANS: Message given saving SMS phone number without having selected a carrier.
$this->showForm(_('No carrier selected.')); throw new ClientException(_('No carrier selected.'));
return;
} }
$sms = common_canonical_sms($sms); $sms = common_canonical_sms($sms);
if ($user->sms == $sms) { if ($user->sms === $sms) {
// TRANS: Message given saving SMS phone number that is already set. // TRANS: Message given saving SMS phone number that is already set.
$this->showForm(_('That is already your phone number.')); throw new AlreadyFulfilledException(_('That is already your phone number.'));
return;
} else if ($this->smsExists($sms)) { } else if ($this->smsExists($sms)) {
// TRANS: Message given saving SMS phone number that is already set for another user. // TRANS: Message given saving SMS phone number that is already set for another user.
$this->showForm(_('That phone number already belongs to another user.')); throw new ClientException(_('That phone number already belongs to another user.'));
return;
} }
$confirm = new Confirm_address(); $confirm = new Confirm_address();
@@ -353,7 +322,7 @@ class SmssettingsAction extends SettingsAction
$confirm->address = $sms; $confirm->address = $sms;
$confirm->address_extra = $carrier_id; $confirm->address_extra = $carrier_id;
$confirm->address_type = 'sms'; $confirm->address_type = 'sms';
$confirm->user_id = $user->id; $confirm->user_id = $this->scoped->getID();
$confirm->code = common_confirmation_code(40); $confirm->code = common_confirmation_code(40);
$result = $confirm->insert(); $result = $confirm->insert();
@@ -371,11 +340,9 @@ class SmssettingsAction extends SettingsAction
$carrier->toEmailAddress($sms)); $carrier->toEmailAddress($sms));
// TRANS: Message given saving valid SMS phone number that is to be confirmed. // TRANS: Message given saving valid SMS phone number that is to be confirmed.
$msg = _('A confirmation code was sent to the phone number you added. '. return _('A confirmation code was sent to the phone number you added. '.
'Check your phone for the code and instructions '. 'Check your phone for the code and instructions '.
'on how to use it.'); 'on how to use it.');
$this->showForm($msg, true);
} }
/** /**
@@ -390,29 +357,27 @@ class SmssettingsAction extends SettingsAction
$sms = $this->trimmed('sms'); $sms = $this->trimmed('sms');
$carrier = $this->trimmed('carrier'); $carrier = $this->trimmed('carrier');
try {
$confirm = $this->getConfirmation(); $confirm = $this->getConfirmation();
if (!$confirm) {
// TRANS: Message given canceling SMS phone number confirmation that is not pending.
$this->showForm(_('No pending confirmation to cancel.'));
return;
}
if ($confirm->address != $sms) { if ($confirm->address != $sms) {
// TRANS: Message given canceling SMS phone number confirmation for the wrong phone number. // TRANS: Message given canceling SMS phone number confirmation for the wrong phone number.
$this->showForm(_('That is the wrong confirmation number.')); throw new ClientException(_('That is the wrong confirmation number.'));
return; }
} catch (NoResultException $e) {
// TRANS: Message given canceling SMS phone number confirmation that is not pending.
throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
} }
$result = $confirm->delete(); $result = $confirm->delete();
if (!$result) { if ($result === false) {
common_log_db_error($confirm, 'DELETE', __FILE__); common_log_db_error($confirm, 'DELETE', __FILE__);
// TRANS: Server error thrown on database error canceling SMS phone number confirmation. // TRANS: Server error thrown on database error canceling SMS phone number confirmation.
$this->serverError(_('Could not delete SMS confirmation.')); throw new ServerException(_('Could not delete SMS confirmation.'));
} }
// TRANS: Message given after successfully canceling SMS phone number confirmation. // TRANS: Message given after successfully canceling SMS phone number confirmation.
$this->showForm(_('SMS confirmation cancelled.'), true); return _('SMS confirmation cancelled.');
} }
/** /**
@@ -422,7 +387,7 @@ class SmssettingsAction extends SettingsAction
*/ */
function removeAddress() function removeAddress()
{ {
$user = common_current_user(); $user = $this->scoped->getUser();
$sms = $this->arg('sms'); $sms = $this->arg('sms');
$carrier = $this->arg('carrier'); $carrier = $this->arg('carrier');
@@ -432,8 +397,7 @@ class SmssettingsAction extends SettingsAction
if ($user->sms != $sms) { if ($user->sms != $sms) {
// TRANS: Message given trying to remove an SMS phone number that is not // TRANS: Message given trying to remove an SMS phone number that is not
// TRANS: registered for the active user. // TRANS: registered for the active user.
$this->showForm(_('That is not your phone number.')); throw new ClientException(_('That is not your phone number.'));
return;
} }
$original = clone($user); $original = clone($user);
@@ -446,7 +410,7 @@ class SmssettingsAction extends SettingsAction
$user->updateWithKeys($original); $user->updateWithKeys($original);
// TRANS: Message given after successfully removing a registered SMS phone number. // TRANS: Message given after successfully removing a registered SMS phone number.
$this->showForm(_('The SMS phone number was removed.'), true); return _('The SMS phone number was removed.');
} }
/** /**
@@ -460,15 +424,13 @@ class SmssettingsAction extends SettingsAction
*/ */
function smsExists($sms) function smsExists($sms)
{ {
$user = common_current_user();
$other = User::getKV('sms', $sms); $other = User::getKV('sms', $sms);
if (!$other) { if (!$other instanceof User) {
return false; return false;
} else {
return $other->id != $user->id;
} }
return !$this->scoped->sameAs($other->getProfile());
} }
/** /**
@@ -519,15 +481,12 @@ class SmssettingsAction extends SettingsAction
{ {
$code = $this->trimmed('code'); $code = $this->trimmed('code');
if (!$code) { if (empty($code)) {
// TRANS: Message given saving SMS phone number confirmation code without having provided one. // TRANS: Message given saving SMS phone number confirmation code without having provided one.
$this->showForm(_('No code entered.')); throw new ClientException(_('No code entered.'));
return;
} }
common_redirect(common_local_url('confirmaddress', common_redirect(common_local_url('confirmaddress', array('code' => $code)), 303);
array('code' => $code)),
303);
} }
/** /**
@@ -541,8 +500,7 @@ class SmssettingsAction extends SettingsAction
if (!$user->incomingemail) { if (!$user->incomingemail) {
// TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set. // TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set.
$this->showForm(_('No incoming email address.')); throw new ClientException(_('No incoming email address.'));
return;
} }
$orig = clone($user); $orig = clone($user);
@@ -553,7 +511,7 @@ class SmssettingsAction extends SettingsAction
$user->updateWithKeys($orig); $user->updateWithKeys($orig);
// TRANS: Confirmation text after updating SMS settings. // TRANS: Confirmation text after updating SMS settings.
$this->showForm(_('Incoming email address removed.'), true); return _('Incoming email address removed.');
} }
/** /**
@@ -565,7 +523,7 @@ class SmssettingsAction extends SettingsAction
*/ */
function newIncoming() function newIncoming()
{ {
$user = common_current_user(); $user = $this->scoped->getUser();
$orig = clone($user); $orig = clone($user);
@@ -575,6 +533,6 @@ class SmssettingsAction extends SettingsAction
$user->updateWithKeys($orig); $user->updateWithKeys($orig);
// TRANS: Confirmation text after updating SMS settings. // TRANS: Confirmation text after updating SMS settings.
$this->showForm(_('New incoming email address added.'), true); return _('New incoming email address added.');
} }
} }

View File

@@ -1,26 +0,0 @@
<?php
/**
* Startpage action. Decides what to show on the first page.
*/
if (!defined('GNUSOCIAL')) { exit(1); }
class StartpageAction extends ManagedAction
{
function isReadOnly($args)
{
return true;
}
function showPage()
{
if (common_config('singleuser', 'enabled')) {
$user = User::singleUser();
common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)), 303);
} elseif (common_config('public', 'localonly')) {
common_redirect(common_local_url('public'), 303);
} else {
common_redirect(common_local_url('networkpublic'), 303);
}
}
}

View File

@@ -27,11 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once(INSTALLDIR.'/lib/profilelist.php');
/** /**
* List of group members * List of group members
@@ -50,9 +46,9 @@ class SubqueueAction extends GalleryAction
{ {
parent::prepare($args); parent::prepare($args);
if ($this->scoped->id != $this->target->id) { if (!$this->target->sameAs($this->scoped)) {
// TRANS: Client error displayed when trying to approve group applicants without being a group administrator. // TRANS: Client error displayed when trying to approve group applicants without being a group administrator.
$this->clientError(_('You may only approve your own pending subscriptions.')); throw new ClientException(_('You may only approve your own pending subscriptions.'));
} }
return true; return true;
} }
@@ -88,47 +84,21 @@ class SubqueueAction extends GalleryAction
$cnt = 0; $cnt = 0;
$members = $this->target->getRequests($offset, $limit); try {
$subqueue = $this->target->getRequests($offset, $limit);
if ($members) { } catch (NoResultException $e) {
// @fixme change! // TRANS: If no pending subscription requests are found
$member_list = new SubQueueList($members, $this); $this->element('div', null, _m('You have no pending subscription requests.'));
$cnt = $member_list->show(); return;
} }
$members->free(); $list = new SubQueueList($subqueue, $this);
$cnt = $list->show();
$subqueue->free();
$this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE, $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
$this->page, 'subqueue', $this->page, 'subqueue',
array('nickname' => $this->target->getNickname())); // urgh array('nickname' => $this->target->getNickname())); // urgh
} }
} }
class SubQueueList extends ProfileList
{
function newListItem($profile)
{
return new SubQueueListItem($profile, $this->action);
}
}
class SubQueueListItem extends ProfileListItem
{
function showActions()
{
$this->startActions();
if (Event::handle('StartProfileListItemActionElements', array($this))) {
$this->showApproveButtons();
Event::handle('EndProfileListItemActionElements', array($this));
}
$this->endActions();
}
function showApproveButtons()
{
$this->out->elementStart('li', 'entity_approval');
$form = new ApproveSubForm($this->out, $this->profile);
$form->show();
$this->out->elementEnd('li');
}
}

View File

@@ -122,7 +122,7 @@ class SubscribeAction extends Action
{ {
// Throws exception on error // Throws exception on error
$sub = Subscription::start($this->user->getProfile(), $sub = Subscription::ensureStart($this->user->getProfile(),
$this->other); $this->other);
if ($this->boolean('ajax')) { if ($this->boolean('ajax')) {

View File

@@ -132,64 +132,3 @@ class SubscribersAction extends GalleryAction
parent::showSections(); parent::showSections();
} }
} }
class SubscribersList extends SubscriptionList
{
function newListItem($profile)
{
return new SubscribersListItem($profile, $this->owner, $this->action);
}
}
class SubscribersListItem extends SubscriptionListItem
{
function showActions()
{
$this->startActions();
if (Event::handle('StartProfileListItemActionElements', array($this))) {
$this->showSubscribeButton();
// Relevant code!
$this->showBlockForm();
Event::handle('EndProfileListItemActionElements', array($this));
}
$this->endActions();
}
function showBlockForm()
{
$user = common_current_user();
if (!empty($user) && $this->owner->id == $user->id) {
$returnto = array('action' => 'subscribers',
'nickname' => $this->owner->getNickname());
$page = $this->out->arg('page');
if ($page) {
$returnto['param-page'] = $page;
}
$bf = new BlockForm($this->out, $this->profile, $returnto);
$bf->show();
}
}
function linkAttributes()
{
$aAttrs = parent::linkAttributes();
if (common_config('nofollow', 'subscribers')) {
$aAttrs['rel'] .= ' nofollow';
}
return $aAttrs;
}
function homepageAttributes()
{
$aAttrs = parent::linkAttributes();
if (common_config('nofollow', 'subscribers')) {
$aAttrs['rel'] = 'nofollow';
}
return $aAttrs;
}
}

View File

@@ -28,9 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* A list of the user's subscriptions * A list of the user's subscriptions
@@ -60,7 +58,7 @@ class SubscriptionsAction extends GalleryAction
function showPageNotice() function showPageNotice()
{ {
if ($this->scoped instanceof Profile && $this->scoped->id === $this->target->id) { if ($this->scoped instanceof Profile && $this->scoped->sameAs($this->getTarget())) {
$this->element('p', null, $this->element('p', null,
// TRANS: Page notice for page with an overview of all subscriptions // TRANS: Page notice for page with an overview of all subscriptions
// TRANS: of the logged in user's own profile. // TRANS: of the logged in user's own profile.
@@ -156,71 +154,3 @@ class SubscriptionsAction extends GalleryAction
$this->target->getNickname()))); $this->target->getNickname())));
} }
} }
// XXX SubscriptionsList and SubscriptionList are dangerously close
class SubscriptionsList extends SubscriptionList
{
function newListItem($profile)
{
return new SubscriptionsListItem($profile, $this->owner, $this->action);
}
}
class SubscriptionsListItem extends SubscriptionListItem
{
function showOwnerControls()
{
$sub = Subscription::pkeyGet(array('subscriber' => $this->owner->id,
'subscribed' => $this->profile->id));
if (!$sub) {
return;
}
$transports = array();
Event::handle('GetImTransports', array(&$transports));
if (!$transports && !common_config('sms', 'enabled')) {
return;
}
$this->out->elementStart('form', array('id' => 'subedit-' . $this->profile->id,
'method' => 'post',
'class' => 'form_subscription_edit',
'action' => common_local_url('subedit')));
$this->out->hidden('token', common_session_token());
$this->out->hidden('profile', $this->profile->id);
if ($transports) {
$attrs = array('name' => 'jabber',
'type' => 'checkbox',
'class' => 'checkbox',
'id' => 'jabber-'.$this->profile->id);
if ($sub->jabber) {
$attrs['checked'] = 'checked';
}
$this->out->element('input', $attrs);
// TRANS: Checkbox label for enabling IM messages for a profile in a subscriptions list.
$this->out->element('label', array('for' => 'jabber-'.$this->profile->id), _m('LABEL','IM'));
} else {
$this->out->hidden('jabber', $sub->jabber);
}
if (common_config('sms', 'enabled')) {
$attrs = array('name' => 'sms',
'type' => 'checkbox',
'class' => 'checkbox',
'id' => 'sms-'.$this->profile->id);
if ($sub->sms) {
$attrs['checked'] = 'checked';
}
$this->out->element('input', $attrs);
// TRANS: Checkbox label for enabling SMS messages for a profile in a subscriptions list.
$this->out->element('label', array('for' => 'sms-'.$this->profile->id), _('SMS'));
} else {
$this->out->hidden('sms', $sub->sms);
}
// TRANS: Save button for settings for a profile in a subscriptions list.
$this->out->submit('save', _m('BUTTON','Save'));
$this->out->elementEnd('form');
}
}

View File

@@ -46,7 +46,8 @@ class TagAction extends ManagedAction
common_set_returnto($this->selfUrl()); common_set_returnto($this->selfUrl());
$this->notice = Notice_tag::getStream($this->tag, (($this->page-1)*NOTICES_PER_PAGE), NOTICES_PER_PAGE + 1); $this->notice = Notice_tag::getStream($this->tag)->getNotices(($this->page-1)*NOTICES_PER_PAGE,
NOTICES_PER_PAGE + 1);
if($this->page > 1 && $this->notice->N == 0){ if($this->page > 1 && $this->notice->N == 0){
// TRANS: Client error when page not found (404). // TRANS: Client error when page not found (404).

View File

@@ -19,9 +19,6 @@
if (!defined('GNUSOCIAL')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
require_once INSTALLDIR . '/lib/settingsaction.php';
require_once INSTALLDIR . '/lib/peopletags.php';
class TagprofileAction extends FormAction class TagprofileAction extends FormAction
{ {
var $error = null; var $error = null;
@@ -29,36 +26,32 @@ class TagprofileAction extends FormAction
protected $target = null; protected $target = null;
protected $form = 'TagProfile'; protected $form = 'TagProfile';
protected function prepare(array $args=array()) protected function doPreparation()
{ {
parent::prepare($args);
$id = $this->trimmed('id'); $id = $this->trimmed('id');
if (!$id) { $uri = $this->trimmed('uri');
$this->target = null; if (!empty($id)) {
} else {
$this->target = Profile::getKV('id', $id); $this->target = Profile::getKV('id', $id);
if (!$this->target instanceof Profile) { if (!$this->target instanceof Profile) {
// TRANS: Client error displayed when referring to non-existing profile ID. // TRANS: Client error displayed when referring to non-existing profile ID.
$this->clientError(_('No profile with that ID.')); $this->clientError(_('No profile with that ID.'));
} }
} elseif (!empty($uri)) {
$this->target = Profile::fromUri($uri);
} else {
// TRANS: Client error displayed when trying to tag a user but no ID or profile is provided.
$this->clientError(_('No profile identifier provided.'));
} }
if ($this->target instanceof Profile && !$this->scoped->canTag($this->target)) { if (!$this->scoped->canTag($this->target)) {
// TRANS: Client error displayed when trying to tag a user that cannot be tagged. // TRANS: Client error displayed when trying to tag a user that cannot be tagged.
$this->clientError(_('You cannot tag this user.')); $this->clientError(_('You cannot tag this user.'));
} }
return true; $this->formOpts = $this->target;
}
protected function handle() return true;
{
if (Event::handle('StartTagProfileAction', array($this, $this->target))) {
parent::handle();
Event::handle('EndTagProfileAction', array($this, $this->target));
}
} }
function title() function title()
@@ -72,6 +65,15 @@ class TagprofileAction extends FormAction
return sprintf(_m('ADDTOLIST','List %s'), $this->target->getNickname()); return sprintf(_m('ADDTOLIST','List %s'), $this->target->getNickname());
} }
function showPage()
{
// Only serve page content if we aren't POSTing via ajax
// otherwise, we serve XML content from doPost()
if (!$this->isPost() || !$this->boolean('ajax')) {
parent::showPage();
}
}
function showContent() function showContent()
{ {
$this->elementStart('div', 'entity_profile h-card'); $this->elementStart('div', 'entity_profile h-card');
@@ -115,17 +117,8 @@ class TagprofileAction extends FormAction
} }
} }
protected function getForm() protected function doPost()
{ {
$class = $this->form.'Form';
$form = new $class($this, $this->target);
return $form;
}
protected function handlePost()
{
parent::handlePost(); // Does nothing for now
$tagstring = $this->trimmed('tags'); $tagstring = $this->trimmed('tags');
$token = $this->trimmed('token'); $token = $this->trimmed('token');
@@ -144,22 +137,16 @@ class TagprofileAction extends FormAction
if (!common_valid_profile_tag($tag)) { if (!common_valid_profile_tag($tag)) {
// TRANS: Form validation error displayed if a given tag is invalid. // TRANS: Form validation error displayed if a given tag is invalid.
// TRANS: %s is the invalid tag. // TRANS: %s is the invalid tag.
$this->showForm(sprintf(_('Invalid tag: "%s".'), $tag)); throw new ClientException(sprintf(_('Invalid tag: "%s".'), $tag));
return;
} }
$tag_priv[$tag] = $private; $tag_priv[$tag] = $private;
} }
} }
try { $result = Profile_tag::setTags($this->scoped->getID(), $this->target->getID(), $tags, $tag_priv);
$result = Profile_tag::setTags($this->scoped->id, $this->target->id, $tags, $tag_priv);
if (!$result) { if (!$result) {
throw new Exception('The tags could not be saved.'); throw new ServerException('The tags could not be saved.');
}
} catch (Exception $e) {
$this->showForm($e->getMessage());
return false;
} }
if ($this->boolean('ajax')) { if ($this->boolean('ajax')) {
@@ -188,17 +175,4 @@ class TagprofileAction extends FormAction
Event::handle('EndSavePeopletags', array($this, $tagstring)); Event::handle('EndSavePeopletags', array($this, $tagstring));
} }
} }
function showPageNotice()
{
if ($this->error) {
$this->element('p', 'error', $this->error);
} else {
$this->elementStart('div', 'instructions');
$this->element('p', null,
// TRANS: Page notice.
_('Use this form to add your subscribers or subscriptions to lists.'));
$this->elementEnd('div');
}
}
} }

View File

@@ -17,42 +17,28 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
require_once(INSTALLDIR.'/lib/rssaction.php');
// Formatting of RSS handled by Rss10Action // Formatting of RSS handled by Rss10Action
class TagrssAction extends Rss10Action class TagrssAction extends Rss10Action
{ {
var $tag; protected $tag;
function prepare($args) { protected function doStreamPreparation()
parent::prepare($args); {
$tag = common_canonical_tag($this->trimmed('tag')); $tag = common_canonical_tag($this->trimmed('tag'));
$this->tag = Notice_tag::getKV('tag', $tag); $this->tag = Notice_tag::getKV('tag', $tag);
if (!$this->tag) { if (!$this->tag instanceof Notice_tag) {
// TRANS: Client error when requesting a tag feed for a non-existing tag. // TRANS: Client error when requesting a tag feed for a non-existing tag.
$this->clientError(_('No such tag.')); $this->clientError(_('No such tag.'));
} else {
$this->notices = $this->getNotices($this->limit);
return true;
} }
} }
function getNotices($limit=0) protected function getNotices()
{ {
$tag = $this->tag; $stream = Notice_tag::getStream($this->tag->tag)->getNotices(0, $this->limit);
return $stream->fetchAll();
if (is_null($tag)) {
return null;
}
$notice = Notice_tag::getStream($tag->tag, 0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
} }
function getChannel() function getChannel()

View File

@@ -20,67 +20,29 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* @category Top
* @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);
}
/**
* An action to redirect to the top of the site
*
* @category Action * @category Action
* @package StatusNet * @package GNUsocial
* @author Evan Prodromou <evan@status.net> * @author Evan Prodromou <evan@status.net>
* @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2010 StatusNet, Inc. * @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 * @copyright 2015 Free Software Foundation, Inc.
* @link http://status.net/ * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL 3.0
* @link https://gnu.io/social
*/ */
class TopAction extends Action if (!defined('GNUSOCIAL')) { exit(1); }
class TopAction extends ManagedAction
{ {
/** public function showPage()
* For initializing members of the class.
*
* @param array $argarray misc. arguments
*
* @return boolean true
*/
function prepare($argarray)
{
parent::prepare($argarray);
return true;
}
/**
* Handler method
*
* @param array $argarray is ignored since it's now passed in in prepare()
*
* @return void
*/
function handle($argarray=null)
{ {
if (common_config('singleuser', 'enabled')) { if (common_config('singleuser', 'enabled')) {
$url = common_local_url('showstream', array('nickname' => User::singleUserNickname())); $user = User::singleUser();
common_redirect(common_local_url('showstream', array('nickname' => $user->getNickname())), 303);
} elseif (common_config('public', 'localonly')) {
common_redirect(common_local_url('public'), 303);
} else { } else {
$url = common_local_url('public'); common_redirect(common_local_url('networkpublic'), 303);
} }
// XXX: Permanent? I think so.
common_redirect($url, 301);
return;
} }
} }

View File

@@ -27,12 +27,10 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Silence a user. * Unsilence a user.
* *
* @category Action * @category Action
* @package StatusNet * @package StatusNet
@@ -42,45 +40,11 @@ if (!defined('STATUSNET')) {
*/ */
class UnsilenceAction extends ProfileFormAction class UnsilenceAction extends ProfileFormAction
{ {
/**
* Check parameters
*
* @param array $args action arguments (URL, GET, POST)
*
* @return boolean success flag
*/
function prepare($args)
{
if (!parent::prepare($args)) {
return false;
}
$cur = common_current_user();
assert(!empty($cur)); // checked by parent
if (!$cur->hasRight(Right::SILENCEUSER)) {
// TRANS: Client error on page to unsilence a user when the feature is not enabled.
$this->clientError(_('You cannot silence users on this site.'));
}
assert(!empty($this->profile)); // checked by parent
if (!$this->profile->isSilenced()) {
// TRANS: Client error on page to unsilence a user when the to be unsilenced user has not been silenced.
$this->clientError(_('User is not silenced.'));
}
return true;
}
/**
* Silence a user.
*
* @return void
*/
function handlePost() function handlePost()
{ {
$this->profile->unsilence(); assert($this->scoped instanceof Profile);
assert($this->profile instanceof Profile);
$this->profile->unsilenceAs($this->scoped);
} }
} }

View File

@@ -28,9 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Miscellaneous settings actions * Miscellaneous settings actions
@@ -83,7 +81,7 @@ class UrlsettingsAction extends SettingsAction
*/ */
function showContent() function showContent()
{ {
$user = common_current_user(); $user = $this->scoped->getUser();
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_other', 'id' => 'form_settings_other',
@@ -154,31 +152,13 @@ class UrlsettingsAction extends SettingsAction
$this->elementEnd('form'); $this->elementEnd('form');
} }
/** protected function doPost()
* Handle a post
*
* Saves the changes to url-shortening prefs and shows a success or failure
* message.
*
* @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(_('There was a problem with your session token. '.
'Try again, please.'));
return;
}
$urlshorteningservice = $this->trimmed('urlshorteningservice'); $urlshorteningservice = $this->trimmed('urlshorteningservice');
if (!is_null($urlshorteningservice) && strlen($urlshorteningservice) > 50) { if (!is_null($urlshorteningservice) && strlen($urlshorteningservice) > 50) {
// TRANS: Form validation error for form "Other settings" in user profile. // TRANS: Form validation error for form "Other settings" in user profile.
$this->showForm(_('URL shortening service is too long (maximum 50 characters).')); throw new ClientException(_('URL shortening service is too long (maximum 50 characters).'));
return;
} }
$maxurllength = $this->trimmed('maxurllength'); $maxurllength = $this->trimmed('maxurllength');
@@ -195,9 +175,7 @@ class UrlsettingsAction extends SettingsAction
throw new ClientException(_('Invalid number for maximum notice length.')); throw new ClientException(_('Invalid number for maximum notice length.'));
} }
$user = common_current_user(); $user = $this->scoped->getUser();
assert(!is_null($user)); // should already be checked
$user->query('BEGIN'); $user->query('BEGIN');
@@ -209,14 +187,15 @@ class UrlsettingsAction extends SettingsAction
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);
$user->query('ROLLBACK');
// TRANS: Server error displayed when "Other" settings in user profile could not be updated on the server. // TRANS: Server error displayed when "Other" settings in user profile could not be updated on the server.
$this->serverError(_('Could not update user.')); throw new ServerException(_('Could not update user.'));
} }
$prefs = User_urlshortener_prefs::getPrefs($user); $prefs = User_urlshortener_prefs::getPrefs($user);
$orig = null; $orig = null;
if (empty($prefs)) { if (!$prefs instanceof User_urlshortener_prefs) {
$prefs = new User_urlshortener_prefs(); $prefs = new User_urlshortener_prefs();
$prefs->user_id = $user->id; $prefs->user_id = $user->id;
@@ -229,13 +208,14 @@ class UrlsettingsAction extends SettingsAction
$prefs->maxurllength = $maxurllength; $prefs->maxurllength = $maxurllength;
$prefs->maxnoticelength = $maxnoticelength; $prefs->maxnoticelength = $maxnoticelength;
if (!empty($orig)) { if ($orig instanceof User_urlshortener_prefs) {
$result = $prefs->update($orig); $result = $prefs->update($orig);
} else { } else {
$result = $prefs->insert(); $result = $prefs->insert();
} }
if (!$result) { if ($result === null) {
$user->query('ROLLBACK');
// TRANS: Server exception thrown in profile URL settings when preferences could not be saved. // TRANS: Server exception thrown in profile URL settings when preferences could not be saved.
throw new ServerException(_('Error saving user URL shortening preferences.')); throw new ServerException(_('Error saving user URL shortening preferences.'));
} }
@@ -243,6 +223,6 @@ class UrlsettingsAction extends SettingsAction
$user->query('COMMIT'); $user->query('COMMIT');
// TRANS: Confirmation message after saving preferences. // TRANS: Confirmation message after saving preferences.
$this->showForm(_('Preferences saved.'), true); return _('Preferences saved.');
} }
} }

View File

@@ -28,9 +28,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* User by ID action class. * User by ID action class.
@@ -42,50 +40,27 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/ * @link http://status.net/
*/ */
class UserbyidAction extends Action class UserbyidAction extends ShowstreamAction
{ {
/** protected function doPreparation()
* Is read only?
*
* @return boolean true
*/
function isReadOnly($args)
{ {
return true; // accessing by ID just requires an ID, not a nickname
} $this->target = Profile::getByID($this->trimmed('id'));
/**
* Class handler.
*
* @param array $args array of arguments
*
* @return nothing
*/
protected function handle()
{
parent::handle();
$id = $this->trimmed('id');
if (!$id) {
// TRANS: Client error displayed trying to find a user by ID without providing an ID.
$this->clientError(_('No ID.'));
}
$user = User::getKV($id);
if (!$user) {
// TRANS: Client error displayed trying to find a user by ID for a non-existing ID.
$this->clientError(_('No such user.'));
}
// For local users when accessed by id number, redirect with
// the nickname as argument instead of id.
if ($this->target->isLocal()) {
// Support redirecting to FOAF rdf/xml if the agent prefers it... // Support redirecting to FOAF rdf/xml if the agent prefers it...
// Internet Explorer doesn't specify "text/html" and does list "*/*" // Internet Explorer doesn't specify "text/html" and does list "*/*"
// at least through version 8. We need to list text/html up front to // at least through version 8. We need to list text/html up front to
// ensure that only user-agents who specifically ask for RDF get it. // ensure that only user-agents who specifically ask for RDF get it.
$page_prefs = 'text/html,application/xhtml+xml,application/rdf+xml,application/xml;q=0.3,text/xml;q=0.2'; $page_prefs = 'text/html,application/xhtml+xml,application/rdf+xml,application/xml;q=0.3,text/xml;q=0.2';
$httpaccept = isset($_SERVER['HTTP_ACCEPT']) $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : null;
? $_SERVER['HTTP_ACCEPT'] : null;
$type = common_negotiate_type(common_accept_to_prefs($httpaccept), $type = common_negotiate_type(common_accept_to_prefs($httpaccept),
common_accept_to_prefs($page_prefs)); common_accept_to_prefs($page_prefs));
$page = $type == 'application/rdf+xml' ? 'foaf' : 'showstream'; $page = $type === 'application/rdf+xml' ? 'foaf' : 'showstream';
$url = common_local_url($page, array('nickname' => $user->nickname)); $url = common_local_url($page, array('nickname' => $this->target->getNickname()));
common_redirect($url, 303); common_redirect($url, 303);
} }
} }
}

View File

@@ -52,46 +52,71 @@ class UsergroupsAction extends GalleryAction
if ($this->page == 1) { if ($this->page == 1) {
// TRANS: Page title for first page of groups for a user. // TRANS: Page title for first page of groups for a user.
// TRANS: %s is a nickname. // TRANS: %s is a nickname.
return sprintf(_('%s groups'), $this->user->nickname); return sprintf(_('%s groups'), $this->getTarget()->getNickname());
} else { } else {
// TRANS: Page title for all but the first page of groups for a user. // TRANS: Page title for all but the first page of groups for a user.
// TRANS: %1$s is a nickname, %2$d is a page number. // TRANS: %1$s is a nickname, %2$d is a page number.
return sprintf(_('%1$s groups, page %2$d'), return sprintf(_('%1$s groups, page %2$d'),
$this->user->nickname, $this->getTarget()->getNickname(),
$this->page); $this->page);
} }
} }
function showPageNotice()
{
if ($this->scoped instanceof Profile && $this->scoped->sameAs($this->getTarget())) {
$this->element('p', 'instructions',
// TRANS: Page notice for page with an overview of all subscribed groups
// TRANS: of the logged in user's own profile.
_('These are the groups whose notices '.
'you listen to.'));
} else {
$this->element('p', 'instructions',
// TRANS: Page notice for page with an overview of all groups a user other
// TRANS: than the logged in user. %s is the user nickname.
sprintf(_('These are the groups whose '.
'notices %s listens to.'),
$this->target->getNickname()));
}
}
function showContent() function showContent()
{ {
$this->elementStart('p', array('id' => 'new_group')); if ($this->scoped instanceof Profile && $this->scoped->sameAs($this->getTarget())) {
$this->element('a', array('href' => common_local_url('newgroup'), $notice =
'class' => 'more'), // TRANS: Page notice of user's groups page.
// TRANS: Link text on group page to create a new group. // TRANS: %%%%action.groupsearch%%%% and %%%%action.newgroup%%%% are URLs. Do not change them.
_('Create a new group')); // TRANS: This message contains Markdown links in the form [link text](link).
$this->elementEnd('p'); sprintf(_('Groups let you find and talk with ' .
'people of similar interests. ' .
$this->elementStart('p', array('id' => 'group_search')); 'You can [search for groups](%%%%action.groups%%%%) in your instance or ' .
$this->element('a', array('href' => common_local_url('groupsearch'), '[create a new group](%%%%action.newgroup%%%%). ' .
'class' => 'more'), 'You can also follow groups ' .
// TRANS: Link text on group page to search for groups. 'from other GNU social instances: click on the remote button below ' .
_('Search for more groups')); 'and copy the group\'s link. ' .
$this->elementEnd('p'); 'You can find a list of GNU social groups [here](http://skilledtests.com/wiki/List_of_federated_GNU_social_groups)' .
''));
$this->elementStart('div', 'instructions');
$this->raw(common_markup_to_html($notice));
$this->elementEnd('div');
}
if (Event::handle('StartShowUserGroupsContent', array($this))) { if (Event::handle('StartShowUserGroupsContent', array($this))) {
$offset = ($this->page-1) * GROUPS_PER_PAGE; $offset = ($this->page-1) * GROUPS_PER_PAGE;
$limit = GROUPS_PER_PAGE + 1; $limit = GROUPS_PER_PAGE + 1;
$groups = $this->user->getGroups($offset, $limit); $groups = $this->getTarget()->getGroups($offset, $limit);
if ($groups instanceof User_group) { if ($groups instanceof User_group) {
$gl = new GroupList($groups, $this->user, $this); $gl = new GroupList($groups, $this->getTarget(), $this);
$cnt = $gl->show(); $cnt = $gl->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
} else {
$this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE, $this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE,
$this->page, 'usergroups', $this->page, 'usergroups',
array('nickname' => $this->user->nickname)); array('nickname' => $this->getTarget()->getNickname()));
} else { }
$this->showEmptyListMessage();
} }
Event::handle('EndShowUserGroupsContent', array($this)); Event::handle('EndShowUserGroupsContent', array($this));
@@ -102,14 +127,14 @@ class UsergroupsAction extends GalleryAction
{ {
// TRANS: Text on group page for a user that is not a member of any group. // TRANS: Text on group page for a user that is not a member of any group.
// TRANS: %s is a user nickname. // TRANS: %s is a user nickname.
$message = sprintf(_('%s is not a member of any group.'), $this->user->nickname) . ' '; $message = sprintf(_('%s is not a member of any group.'), $this->getTarget()->getNickname()) . ' ';
if (common_logged_in()) { if (common_logged_in()) {
$current_user = common_current_user(); if ($this->scoped->sameAs($this->getTarget())) {
if ($this->user->id === $current_user->id) {
// TRANS: Text on group page for a user that is not a member of any group. This message contains // TRANS: Text on group page for a user that is not a member of any group. This message contains
// TRANS: a Markdown link in the form [link text](link) and a variable that should not be changed. // TRANS: a Markdown link in the form [link text](link) and a variable that should not be changed.
$message .= _('Try [searching for groups](%%action.groupsearch%%) and joining them.'); $message = _('You are not member of any group yet. After you join a group ' .
'you can send messages to its members using the ' .
'syntax "!groupname".');
} }
} }
$this->elementStart('div', 'guide'); $this->elementStart('div', 'guide');
@@ -119,7 +144,7 @@ class UsergroupsAction extends GalleryAction
function showProfileBlock() function showProfileBlock()
{ {
$block = new AccountProfileBlock($this, $this->profile); $block = new AccountProfileBlock($this, $this->getTarget());
$block->show(); $block->show();
} }
} }

View File

@@ -17,100 +17,54 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } if (!defined('GNUSOCIAL')) { exit(1); }
require_once(INSTALLDIR.'/lib/rssaction.php');
// Formatting of RSS handled by Rss10Action // Formatting of RSS handled by Rss10Action
class UserrssAction extends Rss10Action class UserrssAction extends TargetedRss10Action
{ {
var $tag = null; protected $tag = null;
function prepare($args) protected function doStreamPreparation()
{ {
parent::prepare($args); parent::doStreamPreparation();
$nickname = $this->trimmed('nickname');
$this->user = User::getKV('nickname', $nickname);
$this->tag = $this->trimmed('tag'); $this->tag = $this->trimmed('tag');
if (!$this->user) {
// TRANS: Client error displayed when user not found for an action.
$this->clientError(_('No such user.'));
} }
protected function getNotices()
{
if (!empty($this->tag)) { if (!empty($this->tag)) {
$this->notices = $this->getTaggedNotices(); $stream = $this->getTarget()->getTaggedNotices($this->tag, 0, $this->limit);
} else { return $stream->fetchAll();
$this->notices = $this->getNotices();
} }
// otherwise we fetch a normal user stream
return true; $stream = $this->getTarget()->getNotices(0, $this->limit);
} return $stream->fetchAll();
function getTaggedNotices()
{
$notice = $this->user->getTaggedNotices(
$this->tag,
0,
($this->limit == 0) ? NOTICES_PER_PAGE : $this->limit,
0,
0
);
$notices = array();
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
}
function getNotices()
{
$notice = $this->user->getNotices(
0,
($this->limit == 0) ? NOTICES_PER_PAGE : $this->limit
);
$notices = array();
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
} }
function getChannel() function getChannel()
{ {
$user = $this->user;
$profile = $user->getProfile();
$c = array('url' => common_local_url('userrss', $c = array('url' => common_local_url('userrss',
array('nickname' => array('nickname' =>
$user->nickname)), $this->target->getNickname())),
// TRANS: Message is used as link title. %s is a user nickname. // TRANS: Message is used as link title. %s is a user nickname.
'title' => sprintf(_('%s timeline'), $user->nickname), 'title' => sprintf(_('%s timeline'), $this->target->getNickname()),
'link' => $profile->profileurl, 'link' => $this->target->getUrl(),
// TRANS: Message is used as link description. %1$s is a username, %2$s is a site name. // TRANS: Message is used as link description. %1$s is a username, %2$s is a site name.
'description' => sprintf(_('Updates from %1$s on %2$s!'), 'description' => sprintf(_('Updates from %1$s on %2$s!'),
$user->nickname, common_config('site', 'name'))); $this->target->getNickname(), common_config('site', 'name')));
return $c; return $c;
} }
function getImage()
{
$profile = $this->user->getProfile();
return $profile->avatarUrl(AVATAR_PROFILE_SIZE);
}
// override parent to add X-SUP-ID URL // override parent to add X-SUP-ID URL
function initRss($limit=0) function initRss()
{ {
$url = common_local_url('sup', null, null, $this->user->id); $url = common_local_url('sup', null, null, $this->target->getID());
header('X-SUP-ID: '.$url); header('X-SUP-ID: '.$url);
parent::initRss($limit); parent::initRss();
} }
function isReadOnly($args) function isReadOnly($args)

0
avatar/.gitignore vendored
View File

View File

View File

@@ -22,7 +22,7 @@ class Attention extends Managed_DataObject
public $__table = 'attention'; // table name public $__table = 'attention'; // table name
public $notice_id; // int(4) primary_key not_null public $notice_id; // int(4) primary_key not_null
public $profile_id; // int(4) primary_key not_null public $profile_id; // int(4) primary_key not_null
public $reason; // varchar(255) public $reason; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime() not_null public $created; // datetime() not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@@ -33,7 +33,7 @@ class Attention extends Managed_DataObject
'fields' => array( 'fields' => array(
'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice_id to give attention'), 'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice_id to give attention'),
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile_id for feed receiver'), 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile_id for feed receiver'),
'reason' => array('type' => 'varchar', 'length' => 255, 'description' => 'Optional reason why this was brought to the attention of profile_id'), 'reason' => array('type' => 'varchar', 'length' => 191, 'description' => 'Optional reason why this was brought to the attention of profile_id'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), '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'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
), ),
@@ -49,18 +49,23 @@ class Attention extends Managed_DataObject
); );
} }
public static function saveNew(Notice $notice, Profile $profile, $reason=null) public static function saveNew(Notice $notice, Profile $target, $reason=null)
{ {
try {
$att = Attention::getByKeys(['notice_id'=>$notice->getID(), 'profile_id'=>$target->getID()]);
throw new AlreadyFulfilledException('Attention already exists with reason: '.var_export($att->reason,true));
} catch (NoResultException $e) {
$att = new Attention(); $att = new Attention();
$att->notice_id = $notice->getID(); $att->notice_id = $notice->getID();
$att->profile_id = $profile->getID(); $att->profile_id = $target->getID();
$att->reason = $reason; $att->reason = $reason;
$att->created = common_sql_now(); $att->created = common_sql_now();
$result = $att->insert(); $result = $att->insert();
if ($result === false) { if ($result === false) {
throw new Exception('Could not saveNew in Attention'); throw new Exception('Failed Attention::saveNew for notice id=='.$notice->getID().' target id=='.$target->getID().', reason=="'.$reason.'"');
}
} }
return $att; return $att;
} }

View File

@@ -1,28 +1,23 @@
<?php <?php
if (!defined('GNUSOCIAL')) { exit(1); }
/** /**
* Table Definition for avatar * Table Definition for avatar
*/ */
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Avatar extends Managed_DataObject class Avatar extends Managed_DataObject
{ {
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'avatar'; // table name public $__table = 'avatar'; // table name
public $profile_id; // int(4) primary_key not_null public $profile_id; // int(4) primary_key not_null
public $original; // tinyint(1) public $original; // tinyint(1)
public $width; // int(4) primary_key not_null public $width; // int(4) primary_key not_null
public $height; // int(4) primary_key not_null public $height; // int(4) primary_key not_null
public $mediatype; // varchar(32) not_null public $mediatype; // varchar(32) not_null
public $filename; // varchar(255) public $filename; // varchar(191) not 255 because utf8mb4 takes more space
public $url; // varchar(255) unique_key
public $created; // datetime() not_null public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP 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() public static function schemaDef()
{ {
return array( return array(
@@ -32,14 +27,13 @@ class Avatar extends Managed_DataObject
'width' => array('type' => 'int', 'not null' => true, 'description' => 'image width'), 'width' => array('type' => 'int', 'not null' => true, 'description' => 'image width'),
'height' => array('type' => 'int', 'not null' => true, 'description' => 'image height'), 'height' => array('type' => 'int', 'not null' => true, 'description' => 'image height'),
'mediatype' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'file type'), 'mediatype' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'file type'),
'filename' => array('type' => 'varchar', 'length' => 255, 'description' => 'local filename, if local'), 'filename' => array('type' => 'varchar', 'length' => 191, 'description' => 'local filename, if local'),
'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'avatar location'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), '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'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
), ),
'primary key' => array('profile_id', 'width', 'height'), 'primary key' => array('profile_id', 'width', 'height'),
'unique keys' => array( 'unique keys' => array(
'avatar_url_key' => array('url'), // 'avatar_filename_key' => array('filename'),
), ),
'foreign keys' => array( 'foreign keys' => array(
'avatar_profile_id_fkey' => array('profile', array('profile_id' => 'id')), 'avatar_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
@@ -193,16 +187,7 @@ class Avatar extends Managed_DataObject
$server = common_config('site', 'server'); $server = common_config('site', 'server');
} }
$ssl = common_config('avatar', 'ssl'); $ssl = (common_config('avatar', 'ssl') || GNUsocial::useHTTPS());
if (is_null($ssl)) { // null -> guess
if (common_config('site', 'ssl') == 'always' &&
!common_config('avatar', 'server')) {
$ssl = true;
} else {
$ssl = false;
}
}
$protocol = ($ssl) ? 'https' : 'http'; $protocol = ($ssl) ? 'https' : 'http';
@@ -211,12 +196,7 @@ class Avatar extends Managed_DataObject
function displayUrl() function displayUrl()
{ {
$server = common_config('avatar', 'server');
if ($server && !empty($this->filename)) {
return Avatar::url($this->filename); return Avatar::url($this->filename);
} else {
return $this->url;
}
} }
static function urlByProfile(Profile $target, $width=null, $height=null) { static function urlByProfile(Profile $target, $width=null, $height=null) {
@@ -241,17 +221,21 @@ class Avatar extends Managed_DataObject
// TRANS: An error message when avatar size is unreasonable // TRANS: An error message when avatar size is unreasonable
throw new Exception(_m('Avatar size too large')); throw new Exception(_m('Avatar size too large'));
} }
// So far we only have square avatars and I don't have time to
// rewrite support for non-square ones right now ;)
$height = $width;
$original = Avatar::getUploaded($target); $original = Avatar::getUploaded($target);
$imagefile = new ImageFile($target->id, Avatar::path($original->filename)); $imagefile = new ImageFile(null, Avatar::path($original->filename));
$filename = $imagefile->resize($width); $filename = Avatar::filename($target->getID(), image_type_to_extension($imagefile->preferredType()),
$width, common_timestamp());
$imagefile->resizeTo(Avatar::path($filename), array('width'=>$width, 'height'=>$height));
$scaled = clone($original); $scaled = clone($original);
$scaled->original = false; $scaled->original = false;
$scaled->width = $width; $scaled->width = $width;
$scaled->height = $width; $scaled->height = $height;
$scaled->url = Avatar::url($filename);
$scaled->filename = $filename; $scaled->filename = $filename;
$scaled->created = common_sql_now(); $scaled->created = common_sql_now();

View File

@@ -17,16 +17,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('STATUSNET')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
/** /**
* Table Definition for config * Table Definition for config
*/ */
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Config extends Managed_DataObject class Config extends Managed_DataObject
{ {
###START_AUTOCODE ###START_AUTOCODE
@@ -35,7 +31,7 @@ class Config extends Managed_DataObject
public $__table = 'config'; // table name public $__table = 'config'; // table name
public $section; // varchar(32) primary_key not_null public $section; // varchar(32) primary_key not_null
public $setting; // varchar(32) primary_key not_null public $setting; // varchar(32) primary_key not_null
public $value; // varchar(255) public $value; // text
/* the code above is auto generated do not remove the tag below */ /* the code above is auto generated do not remove the tag below */
###END_AUTOCODE ###END_AUTOCODE
@@ -46,7 +42,7 @@ class Config extends Managed_DataObject
'fields' => array( 'fields' => array(
'section' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'configuration section'), 'section' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'configuration section'),
'setting' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'configuration setting'), 'setting' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'configuration setting'),
'value' => array('type' => 'varchar', 'length' => 255, 'description' => 'configuration value'), 'value' => array('type' => 'text', 'description' => 'configuration value'),
), ),
'primary key' => array('section', 'setting'), 'primary key' => array('section', 'setting'),
); );

View File

@@ -2,34 +2,27 @@
/** /**
* Table Definition for confirm_address * Table Definition for confirm_address
*/ */
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Confirm_address extends Managed_DataObject class Confirm_address extends Managed_DataObject
{ {
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'confirm_address'; // table name public $__table = 'confirm_address'; // table name
public $code; // varchar(32) primary_key not_null public $code; // varchar(32) primary_key not_null
public $user_id; // int(4) not_null public $user_id; // int(4) not_null
public $address; // varchar(255) not_null public $address; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $address_extra; // varchar(255) not_null public $address_extra; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $address_type; // varchar(8) not_null public $address_type; // varchar(8) not_null
public $claimed; // datetime() public $claimed; // datetime()
public $sent; // datetime() public $sent; // datetime()
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP 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() public static function schemaDef()
{ {
return array( return array(
'fields' => array( 'fields' => array(
'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'good random code'), 'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'good random code'),
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user who requested confirmation'), 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user who requested confirmation'),
'address' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'address (email, xmpp, SMS, etc.)'), 'address' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'address (email, xmpp, SMS, etc.)'),
'address_extra' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'carrier ID, for SMS'), 'address_extra' => array('type' => 'varchar', 'length' => 191, 'description' => 'carrier ID, for SMS'),
'address_type' => array('type' => 'varchar', 'length' => 8, 'not null' => true, 'description' => 'address type ("email", "xmpp", "sms")'), 'address_type' => array('type' => 'varchar', 'length' => 8, 'not null' => true, 'description' => 'address type ("email", "xmpp", "sms")'),
'claimed' => array('type' => 'datetime', 'description' => 'date this was claimed for queueing'), 'claimed' => array('type' => 'datetime', 'description' => 'date this was claimed for queueing'),
'sent' => array('type' => 'datetime', 'description' => 'date this was sent for queueing'), 'sent' => array('type' => 'datetime', 'description' => 'date this was sent for queueing'),

View File

@@ -10,8 +10,8 @@ class Consumer extends Managed_DataObject
/* the code below is auto generated do not remove the above tag */ /* the code below is auto generated do not remove the above tag */
public $__table = 'consumer'; // table name public $__table = 'consumer'; // table name
public $consumer_key; // varchar(255) primary_key not_null public $consumer_key; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
public $consumer_secret; // varchar(255) not_null public $consumer_secret; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $seed; // char(32) not_null public $seed; // char(32) not_null
public $created; // datetime not_null public $created; // datetime not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@@ -24,8 +24,8 @@ class Consumer extends Managed_DataObject
return array( return array(
'description' => 'OAuth consumer record', 'description' => 'OAuth consumer record',
'fields' => array( 'fields' => array(
'consumer_key' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'unique identifier, root URL'), 'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'),
'consumer_secret' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'secret value'), 'consumer_secret' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'secret value'),
'seed' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'seed for new tokens by this consumer'), 'seed' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'seed for new tokens by this consumer'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),

View File

@@ -34,8 +34,8 @@ if (!defined('GNUSOCIAL')) { exit(1); }
class Conversation extends Managed_DataObject class Conversation extends Managed_DataObject
{ {
public $__table = 'conversation'; // table name public $__table = 'conversation'; // table name
public $id; // int(4) primary_key not_null public $id; // int(4) primary_key not_null auto_increment
public $uri; // varchar(255) unique_key public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $created; // datetime not_null public $created; // datetime not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@@ -43,8 +43,8 @@ class Conversation extends Managed_DataObject
{ {
return array( return array(
'fields' => array( 'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'should be set from root notice id (since 2014-03-01 commit)'), 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique identifier, (again) unrelated to notice id since 2016-01-06'),
'uri' => array('type' => 'varchar', 'not null'=>true, 'length' => 255, 'description' => 'URI of the conversation'), 'uri' => array('type' => 'varchar', 'not null'=>true, 'length' => 191, 'description' => 'URI of the conversation'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), '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'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
), ),
@@ -55,6 +55,32 @@ class Conversation extends Managed_DataObject
); );
} }
static public function beforeSchemaUpdate()
{
$table = strtolower(get_called_class());
$schema = Schema::get();
$schemadef = $schema->getTableDef($table);
// 2016-01-06 We have to make sure there is no conversation with id==0 since it will screw up auto increment resequencing
if ($schemadef['fields']['id']['auto_increment']) {
// since we already have auto incrementing ('serial') we can continue
return;
}
// The conversation will be recreated in upgrade.php, which will
// generate a new URI, but that's collateral damage for you.
$conv = new Conversation();
$conv->id = 0;
if ($conv->find()) {
while ($conv->fetch()) {
// Since we have filtered on 0 this only deletes such entries
// which I have been afraid wouldn't work, but apparently does!
// (I thought it would act as null or something and find _all_ conversation entries)
$conv->delete();
}
}
}
/** /**
* Factory method for creating a new conversation. * Factory method for creating a new conversation.
* *
@@ -63,25 +89,17 @@ class Conversation extends Managed_DataObject
* *
* @return Conversation the new conversation DO * @return Conversation the new conversation DO
*/ */
static function create(Notice $notice, $uri=null) static function create($uri=null, $created=null)
{ {
if (empty($notice->id)) { // Be aware that the Notice does not have an id yet since it's not inserted!
throw new ServerException(_('Tried to create conversation for not yet inserted notice'));
}
$conv = new Conversation(); $conv = new Conversation();
$conv->created = common_sql_now(); $conv->created = $created ?: common_sql_now();
$conv->id = $notice->id; $conv->uri = $uri ?: sprintf('%s%s=%s:%s=%s',
$conv->uri = $uri ?: sprintf('%s%s=%d:%s=%s:%s=%x',
TagURI::mint(), TagURI::mint(),
'noticeId', $notice->id,
'objectType', 'thread', 'objectType', 'thread',
'crc32', crc32($notice->content)); 'nonce', common_random_hexstr(8));
$result = $conv->insert(); // This insert throws exceptions on failure
$conv->insert();
if ($result === false) {
common_log_db_error($conv, 'INSERT', __FILE__);
throw new ServerException(_('Failed to create conversation for notice'));
}
return $conv; return $conv;
} }
@@ -108,8 +126,8 @@ class Conversation extends Managed_DataObject
static public function getUrlFromNotice(Notice $notice, $anchor=true) static public function getUrlFromNotice(Notice $notice, $anchor=true)
{ {
$conv = self::getKV('id', $notice->conversation); $conv = Conversation::getByID($notice->conversation);
return $conv->getUrl($anchor ? $notice->id : null); return $conv->getUrl($anchor ? $notice->getID() : null);
} }
public function getUri() public function getUri()
@@ -120,18 +138,25 @@ class Conversation extends Managed_DataObject
public function getUrl($noticeId=null) public function getUrl($noticeId=null)
{ {
// FIXME: the URL router should take notice-id as an argument... // FIXME: the URL router should take notice-id as an argument...
return common_local_url('conversation', array('id' => $this->id)) . return common_local_url('conversation', array('id' => $this->getID())) .
($noticeId===null ? '' : "#notice-{$noticeId}"); ($noticeId===null ? '' : "#notice-{$noticeId}");
} }
// FIXME: ...will 500 ever be too low? Taken from ConversationAction::MAX_NOTICES // FIXME: ...will 500 ever be too low? Taken from ConversationAction::MAX_NOTICES
public function getNotices($offset=0, $limit=500, Profile $scoped=null) public function getNotices(Profile $scoped=null, $offset=0, $limit=500)
{ {
if ($scoped === null) { $stream = new ConversationNoticeStream($this->getID(), $scoped);
$scoped = Profile::current();
}
$stream = new ConversationNoticeStream($this->id, $scoped);
$notices = $stream->getNotices($offset, $limit); $notices = $stream->getNotices($offset, $limit);
return $notices; return $notices;
} }
public function insert()
{
$result = parent::insert();
if ($result === false) {
common_log_db_error($this, 'INSERT', __FILE__);
throw new ServerException(_('Failed to insert Conversation into database'));
}
return $result;
}
} }

View File

@@ -1,63 +0,0 @@
<?php
/*
* Laconica - a distributed open-source microblogging tool
* Copyright (C) 2008, 2009, Control Yourself, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Table Definition for notice
*/
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Deleted_notice extends Managed_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'deleted_notice'; // table name
public $id; // int(4) primary_key not_null
public $profile_id; // int(4) not_null
public $uri; // varchar(255) unique_key
public $created; // datetime() not_null
public $deleted; // datetime() not_null
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
public static function schemaDef()
{
return array(
'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'identity of notice'),
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'author of the notice'),
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier, usually a tag URI'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was created'),
'deleted' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was created'),
),
'primary key' => array('id'),
'unique keys' => array(
'deleted_notice_uri_key' => array('uri'),
),
'indexes' => array(
'deleted_notice_profile_id_idx' => array('profile_id'),
),
);
}
}

View File

@@ -26,29 +26,36 @@ class File extends Managed_DataObject
{ {
public $__table = 'file'; // table name public $__table = 'file'; // table name
public $id; // int(4) primary_key not_null public $id; // int(4) primary_key not_null
public $url; // varchar(255) unique_key public $urlhash; // varchar(64) unique_key
public $url; // text
public $filehash; // varchar(64) indexed
public $mimetype; // varchar(50) public $mimetype; // varchar(50)
public $size; // int(4) public $size; // int(4)
public $title; // varchar(255) public $title; // text()
public $date; // int(4) public $date; // int(4)
public $protected; // int(4) public $protected; // int(4)
public $filename; // varchar(255) public $filename; // text()
public $width; // int(4) public $width; // int(4)
public $height; // int(4) public $height; // int(4)
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
const URLHASH_ALG = 'sha256';
const FILEHASH_ALG = 'sha256';
public static function schemaDef() public static function schemaDef()
{ {
return array( return array(
'fields' => array( 'fields' => array(
'id' => array('type' => 'serial', 'not null' => true), 'id' => array('type' => 'serial', 'not null' => true),
'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'destination URL after following redirections'), 'urlhash' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'sha256 of destination URL (url field)'),
'url' => array('type' => 'text', 'description' => 'destination URL after following possible redirections'),
'filehash' => array('type' => 'varchar', 'length' => 64, 'not null' => false, 'description' => 'sha256 of the file contents, only for locally stored files of course'),
'mimetype' => array('type' => 'varchar', 'length' => 50, 'description' => 'mime type of resource'), 'mimetype' => array('type' => 'varchar', 'length' => 50, 'description' => 'mime type of resource'),
'size' => array('type' => 'int', 'description' => 'size of resource when available'), 'size' => array('type' => 'int', 'description' => 'size of resource when available'),
'title' => array('type' => 'varchar', 'length' => 255, 'description' => 'title of resource when available'), 'title' => array('type' => 'text', 'description' => 'title of resource when available'),
'date' => array('type' => 'int', 'description' => 'date of resource according to http query'), 'date' => array('type' => 'int', 'description' => 'date of resource according to http query'),
'protected' => array('type' => 'int', 'description' => 'true when URL is private (needs login)'), 'protected' => array('type' => 'int', 'description' => 'true when URL is private (needs login)'),
'filename' => array('type' => 'varchar', 'length' => 255, 'description' => 'if a local file, name of the file'), 'filename' => array('type' => 'text', 'description' => 'if file is stored locally (too) this is the filename'),
'width' => array('type' => 'int', 'description' => 'width in pixels, if it can be described as such and data is available'), 'width' => array('type' => 'int', 'description' => 'width in pixels, if it can be described as such and data is available'),
'height' => array('type' => 'int', 'description' => 'height in pixels, if it can be described as such and data is available'), 'height' => array('type' => 'int', 'description' => 'height in pixels, if it can be described as such and data is available'),
@@ -56,13 +63,28 @@ class File extends Managed_DataObject
), ),
'primary key' => array('id'), 'primary key' => array('id'),
'unique keys' => array( 'unique keys' => array(
'file_url_key' => array('url'), 'file_urlhash_key' => array('urlhash'),
),
'indexes' => array(
'file_filehash_idx' => array('filehash'),
), ),
); );
} }
function isProtected($url) { public static function isProtected($url) {
return 'http://www.facebook.com/login.php' === $url;
$protected_urls_exps = array(
'https://www.facebook.com/login.php',
common_path('main/login')
);
foreach ($protected_urls_exps as $protected_url_exp) {
if (preg_match('!^'.preg_quote($protected_url_exp).'(.*)$!i', $url) === 1) {
return true;
}
}
return false;
} }
/** /**
@@ -72,14 +94,18 @@ class File extends Managed_DataObject
* @param string $given_url * @param string $given_url
* @return File * @return File
*/ */
public static function saveNew(array $redir_data, $given_url) { public static function saveNew(array $redir_data, $given_url)
{
// I don't know why we have to keep doing this but I'm adding this last check to avoid $file = null;
try {
// I don't know why we have to keep doing this but we run a last check to avoid
// uniqueness bugs. // uniqueness bugs.
$file = File::getByUrl($given_url);
return $file;
} catch (NoResultException $e) {
// We don't have the file's URL since before, so let's continue.
}
$file = File::getKV('url', $given_url);
if (!$file instanceof File) {
$file = new File; $file = new File;
$file->url = $given_url; $file->url = $given_url;
if (!empty($redir_data['protected'])) $file->protected = $redir_data['protected']; if (!empty($redir_data['protected'])) $file->protected = $redir_data['protected'];
@@ -87,12 +113,24 @@ class File extends Managed_DataObject
if (!empty($redir_data['type'])) $file->mimetype = $redir_data['type']; if (!empty($redir_data['type'])) $file->mimetype = $redir_data['type'];
if (!empty($redir_data['size'])) $file->size = intval($redir_data['size']); if (!empty($redir_data['size'])) $file->size = intval($redir_data['size']);
if (isset($redir_data['time']) && $redir_data['time'] > 0) $file->date = intval($redir_data['time']); if (isset($redir_data['time']) && $redir_data['time'] > 0) $file->date = intval($redir_data['time']);
$file_id = $file->insert(); $file->saveFile();
return $file;
} }
Event::handle('EndFileSaveNew', array($file, $redir_data, $given_url)); public function saveFile() {
assert ($file instanceof File); $this->urlhash = self::hashurl($this->url);
return $file;
if (!Event::handle('StartFileSaveNew', array(&$this))) {
throw new ServerException('File not saved due to an aborted StartFileSaveNew event.');
}
$this->id = $this->insert();
if ($this->id === false) {
throw new ServerException('File/URL metadata could not be saved to the database.');
}
Event::handle('EndFileSaveNew', array($this));
} }
/** /**
@@ -103,16 +141,15 @@ class File extends Managed_DataObject
* - optionally save a file_to_post record * - optionally save a file_to_post record
* - return the File object with the full reference * - return the File object with the full reference
* *
* @fixme refactor this mess, it's gotten pretty scary.
* @param string $given_url the URL we're looking at * @param string $given_url the URL we're looking at
* @param int $notice_id (optional) * @param Notice $notice (optional)
* @param bool $followRedirects defaults to true * @param bool $followRedirects defaults to true
* *
* @return mixed File on success, -1 on some errors * @return mixed File on success, -1 on some errors
* *
* @throws ServerException on failure * @throws ServerException on failure
*/ */
public static function processNew($given_url, $notice_id=null, $followRedirects=true) { public static function processNew($given_url, Notice $notice=null, $followRedirects=true) {
if (empty($given_url)) { if (empty($given_url)) {
throw new ServerException('No given URL to process'); throw new ServerException('No given URL to process');
} }
@@ -122,64 +159,18 @@ class File extends Managed_DataObject
throw new ServerException('No canonical URL from given URL to process'); throw new ServerException('No canonical URL from given URL to process');
} }
$file = File::getKV('url', $given_url); $redir = File_redirection::where($given_url);
if (!$file instanceof File) { $file = $redir->getFile();
// First check if we have a lookup trace for this URL already
$file_redir = File_redirection::getKV('url', $given_url);
if ($file_redir instanceof File_redirection) {
$file = File::getKV('id', $file_redir->file_id);
if (!$file instanceof File) {
// File did not exist, let's clean up the File_redirection entry
$file_redir->delete();
}
}
// If we still don't have a File object, let's create one now! if (!$file instanceof File || empty($file->id)) {
if (!$file instanceof File) { // This should not happen
// @fixme for new URLs this also looks up non-redirect data
// such as target content type, size, etc, which we need
// for File::saveNew(); so we call it even if not following
// new redirects.
$redir_data = File_redirection::where($given_url);
if (is_array($redir_data)) {
$redir_url = $redir_data['url'];
} elseif (is_string($redir_data)) {
$redir_url = $redir_data;
$redir_data = array();
} else {
// TRANS: Server exception thrown when a URL cannot be processed.
throw new ServerException(sprintf(_("Cannot process URL '%s'"), $given_url));
}
// TODO: max field length
if ($redir_url === $given_url || strlen($redir_url) > 255 || !$followRedirects) {
// Save the File object based on our lookup trace
$file = File::saveNew($redir_data, $given_url);
} else {
// This seems kind of messed up... for now skipping this part
// if we're already under a redirect, so we don't go into
// horrible infinite loops if we've been given an unstable
// redirect (where the final destination of the first request
// doesn't match what we get when we ask for it again).
//
// Seen in the wild with clojure.org, which redirects through
// wikispaces for auth and appends session data in the URL params.
$file = self::processNew($redir_url, $notice_id, /*followRedirects*/false);
File_redirection::saveNew($redir_data, $file->id, $given_url);
}
}
if (!$file instanceof File) {
// This should only happen if File::saveNew somehow did not return a File object,
// though we have an assert for that in case the event there might've gone wrong.
// If anything else goes wrong, there should've been an exception thrown.
throw new ServerException('URL processing failed without new File object'); throw new ServerException('URL processing failed without new File object');
} }
if ($notice instanceof Notice) {
File_to_post::processNew($file, $notice);
} }
if (!empty($notice_id)) {
File_to_post::processNew($file->id, $notice_id);
}
return $file; return $file;
} }
@@ -233,16 +224,20 @@ class File extends Managed_DataObject
return true; return true;
} }
public function getFilename()
{
if (!self::validFilename($this->filename)) {
// TRANS: Client exception thrown if a file upload does not have a valid name.
throw new ClientException(_("Invalid filename."));
}
return $this->filename;
}
// where should the file go? // where should the file go?
static function filename(Profile $profile, $origname, $mimetype) static function filename(Profile $profile, $origname, $mimetype)
{ {
try { $ext = self::guessMimeExtension($mimetype);
$ext = common_supported_mime_to_ext($mimetype);
} catch (Exception $e) {
// We don't support this mimetype, but let's guess the extension
$ext = substr(strrchr($mimetype, '/'), 1);
}
// Normalize and make the original filename more URL friendly. // Normalize and make the original filename more URL friendly.
$origname = basename($origname, ".$ext"); $origname = basename($origname, ".$ext");
@@ -263,6 +258,21 @@ class File extends Managed_DataObject
return $filename; return $filename;
} }
static function guessMimeExtension($mimetype)
{
try {
$ext = common_supported_mime_to_ext($mimetype);
} catch (Exception $e) {
// We don't support this mimetype, but let's guess the extension
$matches = array();
if (!preg_match('/\/([a-z0-9]+)/', mb_strtolower($mimetype), $matches)) {
throw new Exception('Malformed mimetype: '.$mimetype);
}
$ext = $matches[1];
}
return mb_strtolower($ext);
}
/** /**
* Validation for as-saved base filenames * Validation for as-saved base filenames
*/ */
@@ -303,7 +313,7 @@ class File extends Managed_DataObject
} }
if (StatusNet::useHTTPS()) { if (GNUsocial::useHTTPS()) {
$sslserver = common_config('attachments', 'sslserver'); $sslserver = common_config('attachments', 'sslserver');
@@ -350,28 +360,48 @@ class File extends Managed_DataObject
return $protocol.'://'.$server.$path.$filename; return $protocol.'://'.$server.$path.$filename;
} }
static $_enclosures = array();
function getEnclosure(){ function getEnclosure(){
$enclosure = (object) array(); if (isset(self::$_enclosures[$this->getID()])) {
foreach (array('title', 'url', 'date', 'modified', 'size', 'mimetype') as $key) { return self::$_enclosures[$this->getID()];
$enclosure->$key = $this->$key;
} }
$needMoreMetadataMimetypes = array(null, 'application/xhtml+xml'); $enclosure = (object) array();
foreach (array('title', 'url', 'date', 'modified', 'size', 'mimetype', 'width', 'height') as $key) {
if ($this->$key !== '') {
$enclosure->$key = $this->$key;
}
}
$needMoreMetadataMimetypes = array(null, 'application/xhtml+xml', 'text/html');
if (!isset($this->filename) && in_array(common_bare_mime($enclosure->mimetype), $needMoreMetadataMimetypes)) { if (!isset($this->filename) && in_array(common_bare_mime($enclosure->mimetype), $needMoreMetadataMimetypes)) {
// This fetches enclosure metadata for non-local links with unset/HTML mimetypes, // This fetches enclosure metadata for non-local links with unset/HTML mimetypes,
// which may be enriched through oEmbed or similar (implemented as plugins) // which may be enriched through oEmbed or similar (implemented as plugins)
Event::handle('FileEnclosureMetadata', array($this, &$enclosure)); Event::handle('FileEnclosureMetadata', array($this, &$enclosure));
} }
if (empty($enclosure->mimetype) || in_array(common_bare_mime($enclosure->mimetype), $needMoreMetadataMimetypes)) { if (empty($enclosure->mimetype)) {
// This means we either don't know what it is, so it can't // This means we either don't know what it is, so it can't
// be shown as an enclosure, or it is an HTML link which // be shown as an enclosure, or it is an HTML link which
// does not link to a resource with further metadata. // does not link to a resource with further metadata.
throw new ServerException('Unknown enclosure mimetype, not enough metadata'); throw new ServerException('Unknown enclosure mimetype, not enough metadata');
} }
self::$_enclosures[$this->getID()] = $enclosure;
return $enclosure; return $enclosure;
} }
public function hasThumbnail()
{
try {
$this->getThumbnail();
} catch (Exception $e) {
return false;
}
return true;
}
/** /**
* Get the attachment's thumbnail record, if any. * Get the attachment's thumbnail record, if any.
* Make sure you supply proper 'int' typed variables (or null). * Make sure you supply proper 'int' typed variables (or null).
@@ -381,8 +411,12 @@ class File extends Managed_DataObject
* @param $crop bool Crop to the max-values' aspect ratio * @param $crop bool Crop to the max-values' aspect ratio
* *
* @return File_thumbnail * @return File_thumbnail
*
* @throws UseFileAsThumbnailException if the file is considered an image itself and should be itself as thumbnail
* @throws UnsupportedMediaException if, despite trying, we can't understand how to make a thumbnail for this format
* @throws ServerException on various other errors
*/ */
public function getThumbnail($width=null, $height=null, $crop=false, $force_still=true) public function getThumbnail($width=null, $height=null, $crop=false, $force_still=true, $upscale=null)
{ {
// Get some more information about this file through our ImageFile class // Get some more information about this file through our ImageFile class
$image = ImageFile::fromFileObject($this); $image = ImageFile::fromFileObject($this);
@@ -394,99 +428,67 @@ class File extends Managed_DataObject
} }
} }
if ($width === null) { return $image->getFileThumbnail($width, $height, $crop,
$width = common_config('thumbnail', 'width'); !is_null($upscale) ? $upscale : common_config('thumbnail', 'upscale'));
$height = common_config('thumbnail', 'height');
$crop = common_config('thumbnail', 'crop');
}
if ($height === null) {
$height = $width;
$crop = true;
}
// Get proper aspect ratio width and height before lookup
// We have to do it through an ImageFile object because of orientation etc.
// Only other solution would've been to rotate + rewrite uploaded files.
list($width, $height, $x, $y, $w, $h) =
$image->scaleToFit($width, $height, $crop);
$params = array('file_id'=> $this->id,
'width' => $width,
'height' => $height);
$thumb = File_thumbnail::pkeyGet($params);
if ($thumb instanceof File_thumbnail) {
return $thumb;
}
// throws exception on failure to generate thumbnail
$outname = "thumb-{$width}x{$height}-" . $image->filename;
$outpath = self::path($outname);
// The boundary box for our resizing
$box = array('width'=>$width, 'height'=>$height,
'x'=>$x, 'y'=>$y,
'w'=>$w, 'h'=>$h);
// Doublecheck that parameters are sane and integers.
if ($box['width'] < 1 || $box['width'] > common_config('thumbnail', 'maxsize')
|| $box['height'] < 1 || $box['height'] > common_config('thumbnail', 'maxsize')
|| $box['w'] < 1 || $box['x'] >= $image->width
|| $box['h'] < 1 || $box['y'] >= $image->height) {
// Fail on bad width parameter. If this occurs, it's due to algorithm in ImageFile->scaleToFit
common_debug("Boundary box parameters for resize of {$image->filepath} : ".var_export($box,true));
throw new ServerException('Bad thumbnail size parameters.');
}
common_debug(sprintf('Generating a thumbnail of File id==%u of size %ux%u', $this->id, $width, $height));
// Perform resize and store into file
$image->resizeTo($outpath, $box);
// Avoid deleting the original
if ($image->getPath() != self::path($image->filename)) {
$image->unlink();
}
return File_thumbnail::saveThumbnail($this->id,
self::url($outname),
$width, $height,
$outname);
} }
public function getPath() public function getPath()
{ {
return self::path($this->filename); $filepath = self::path($this->filename);
if (!file_exists($filepath)) {
throw new FileNotFoundException($filepath);
}
return $filepath;
} }
public function getUrl() public function getUrl($prefer_local=true)
{ {
if (!empty($this->filename)) { if ($prefer_local && !empty($this->filename)) {
// A locally stored file, so let's generate a URL for our instance. // A locally stored file, so let's generate a URL for our instance.
$url = self::url($this->filename); return self::url($this->filename);
if ($url != $this->url) {
// For indexing purposes, in case we do a lookup on the 'url' field.
// also we're fixing possible changes from http to https, or paths
$this->updateUrl($url);
}
return $url;
} }
// No local filename available, return the URL we have stored // No local filename available, return the URL we have stored
return $this->url; return $this->url;
} }
static public function getByUrl($url)
{
$file = new File();
$file->urlhash = self::hashurl($url);
if (!$file->find(true)) {
throw new NoResultException($file);
}
return $file;
}
/**
* @param string $hashstr String of (preferrably lower case) hexadecimal characters, same as result of 'hash_file(...)'
*/
static public function getByHash($hashstr)
{
$file = new File();
$file->filehash = strtolower($hashstr);
if (!$file->find(true)) {
throw new NoResultException($file);
}
return $file;
}
public function updateUrl($url) public function updateUrl($url)
{ {
$file = File::getKV('url', $url); $file = File::getKV('urlhash', self::hashurl($url));
if ($file instanceof File) { if ($file instanceof File) {
throw new ServerException('URL already exists in DB'); throw new ServerException('URL already exists in DB');
} }
$sql = 'UPDATE %1$s SET url=%2$s WHERE url=%3$s;'; $sql = 'UPDATE %1$s SET urlhash=%2$s, url=%3$s WHERE urlhash=%4$s;';
$result = $this->query(sprintf($sql, $this->__table, $result = $this->query(sprintf($sql, $this->tableName(),
$this->_quote((string)self::hashurl($url)),
$this->_quote((string)$url), $this->_quote((string)$url),
$this->_quote((string)$this->url))); $this->_quote((string)$this->urlhash)));
if ($result === false) { if ($result === false) {
common_log_db_error($this, 'UPDATE', __FILE__); common_log_db_error($this, 'UPDATE', __FILE__);
throw new ServerException("Could not UPDATE {$this->__table}.url"); throw new ServerException("Could not UPDATE {$this->tableName()}.url");
} }
return $result; return $result;
@@ -502,9 +504,9 @@ class File extends Managed_DataObject
function blowCache($last=false) function blowCache($last=false)
{ {
self::blow('file:notice-ids:%s', $this->url); self::blow('file:notice-ids:%s', $this->id);
if ($last) { if ($last) {
self::blow('file:notice-ids:%s;last', $this->url); self::blow('file:notice-ids:%s;last', $this->id);
} }
self::blow('file:notice-count:%d', $this->id); self::blow('file:notice-count:%d', $this->id);
} }
@@ -570,6 +572,14 @@ class File extends Managed_DataObject
$thumbs->delete(); $thumbs->delete();
} }
} }
$f2p = new File_to_post();
$f2p->file_id = $this->id;
if ($f2p->find()) {
while ($f2p->fetch()) {
$f2p->delete();
}
}
} }
// And finally remove the entry from the database // And finally remove the entry from the database
@@ -582,4 +592,87 @@ class File extends Managed_DataObject
return $title ?: null; return $title ?: null;
} }
static public function hashurl($url)
{
if (empty($url)) {
throw new Exception('No URL provided to hash algorithm.');
}
return hash(self::URLHASH_ALG, $url);
}
static public function beforeSchemaUpdate()
{
$table = strtolower(get_called_class());
$schema = Schema::get();
$schemadef = $schema->getTableDef($table);
// 2015-02-19 We have to upgrade our table definitions to have the urlhash field populated
if (isset($schemadef['fields']['urlhash']) && isset($schemadef['unique keys']['file_urlhash_key'])) {
// We already have the urlhash field, so no need to migrate it.
return;
}
echo "\nFound old $table table, upgrading it to contain 'urlhash' field...";
$file = new File();
$file->query(sprintf('SELECT id, LEFT(url, 191) AS shortenedurl, COUNT(*) AS c FROM %1$s WHERE LENGTH(url)>191 GROUP BY shortenedurl HAVING c > 1', $schema->quoteIdentifier($table)));
print "\nFound {$file->N} URLs with too long entries in file table\n";
while ($file->fetch()) {
// We've got a URL that is too long for our future file table
// so we'll cut it. We could save the original URL, but there is
// no guarantee it is complete anyway since the previous max was 255 chars.
$dupfile = new File();
// First we find file entries that would be duplicates of this when shortened
// ... and we'll just throw the dupes out the window for now! It's already so borken.
$dupfile->query(sprintf('SELECT * FROM file WHERE LEFT(url, 191) = "%1$s"', $file->shortenedurl));
// Leave one of the URLs in the database by using ->find(true) (fetches first entry)
if ($dupfile->find(true)) {
print "\nShortening url entry for $table id: {$file->id} [";
$orig = clone($dupfile);
$dupfile->url = $file->shortenedurl; // make sure it's only 191 chars from now on
$dupfile->update($orig);
print "\nDeleting duplicate entries of too long URL on $table id: {$file->id} [";
// only start deleting with this fetch.
while($dupfile->fetch()) {
print ".";
$dupfile->delete();
}
print "]\n";
} else {
print "\nWarning! URL suddenly disappeared from database: {$file->url}\n";
}
}
echo "...and now all the non-duplicates which are longer than 191 characters...\n";
$file->query('UPDATE file SET url=LEFT(url, 191) WHERE LENGTH(url)>191');
echo "\n...now running hacky pre-schemaupdate change for $table:";
// We have to create a urlhash that is _not_ the primary key,
// transfer data and THEN run checkSchema
$schemadef['fields']['urlhash'] = array (
'type' => 'varchar',
'length' => 64,
'not null' => false, // this is because when adding column, all entries will _be_ NULL!
'description' => 'sha256 of destination URL (url field)',
);
$schemadef['fields']['url'] = array (
'type' => 'text',
'description' => 'destination URL after following possible redirections',
);
unset($schemadef['unique keys']);
$schema->ensureTable($table, $schemadef);
echo "DONE.\n";
$classname = ucfirst($table);
$tablefix = new $classname;
// urlhash is hash('sha256', $url) in the File table
echo "Updating urlhash fields in $table table...";
// Maybe very MySQL specific :(
$tablefix->query(sprintf('UPDATE %1$s SET %2$s=%3$s;',
$schema->quoteIdentifier($table),
'urlhash',
// The line below is "result of sha256 on column `url`"
'SHA2(url, 256)'));
echo "DONE.\n";
echo "Resuming core schema upgrade...";
}
} }

View File

@@ -29,7 +29,8 @@ class File_redirection extends Managed_DataObject
/* the code below is auto generated do not remove the above tag */ /* the code below is auto generated do not remove the above tag */
public $__table = 'file_redirection'; // table name public $__table = 'file_redirection'; // table name
public $url; // varchar(255) primary_key not_null public $urlhash; // varchar(64) primary_key not_null
public $url; // text
public $file_id; // int(4) public $file_id; // int(4)
public $redirections; // int(4) public $redirections; // int(4)
public $httpcode; // int(4) public $httpcode; // int(4)
@@ -38,29 +39,37 @@ class File_redirection extends Managed_DataObject
/* the code above is auto generated do not remove the tag below */ /* the code above is auto generated do not remove the tag below */
###END_AUTOCODE ###END_AUTOCODE
protected $file; /* Cache the associated file sometimes */
public static function schemaDef() public static function schemaDef()
{ {
return array( return array(
'fields' => array( 'fields' => array(
'url' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'short URL (or any other kind of redirect) for file (id)'), 'urlhash' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'sha256 hash of the URL'),
'url' => array('type' => 'text', 'description' => 'short URL (or any other kind of redirect) for file (id)'),
'file_id' => array('type' => 'int', 'description' => 'short URL for what URL/file'), 'file_id' => array('type' => 'int', 'description' => 'short URL for what URL/file'),
'redirections' => array('type' => 'int', 'description' => 'redirect count'), 'redirections' => array('type' => 'int', 'description' => 'redirect count'),
'httpcode' => array('type' => 'int', 'description' => 'HTTP status code (20x, 30x, etc.)'), 'httpcode' => array('type' => 'int', 'description' => 'HTTP status code (20x, 30x, etc.)'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
), ),
'primary key' => array('url'), 'primary key' => array('urlhash'),
'foreign keys' => array( 'foreign keys' => array(
'file_redirection_file_id_fkey' => array('file' => array('file_id' => 'id')), 'file_redirection_file_id_fkey' => array('file' => array('file_id' => 'id')),
), ),
); );
} }
static public function getByUrl($url)
{
return self::getByPK(array('urlhash' => File::hashurl($url)));
}
static function _commonHttp($url, $redirs) { static function _commonHttp($url, $redirs) {
$request = new HTTPClient($url); $request = new HTTPClient($url);
$request->setConfig(array( $request->setConfig(array(
'connect_timeout' => 10, // # seconds to wait 'connect_timeout' => 10, // # seconds to wait
'max_redirs' => $redirs, // # max number of http redirections to follow 'max_redirs' => $redirs, // # max number of http redirections to follow
'follow_redirects' => true, // Follow redirects 'follow_redirects' => false, // We follow redirects ourselves in lib/httpclient.php
'store_body' => false, // We won't need body content here. 'store_body' => false, // We won't need body content here.
)); ));
return $request; return $request;
@@ -87,7 +96,7 @@ class File_redirection extends Managed_DataObject
* size (optional): byte size from Content-Length header * size (optional): byte size from Content-Length header
* time (optional): timestamp from Last-Modified header * time (optional): timestamp from Last-Modified header
*/ */
public function lookupWhere($short_url, $redirs = 10, $protected = false) { static function lookupWhere($short_url, $redirs = 10, $protected = false) {
if ($redirs < 0) return false; if ($redirs < 0) return false;
if(strpos($short_url,'://') === false){ if(strpos($short_url,'://') === false){
@@ -111,6 +120,8 @@ class File_redirection extends Managed_DataObject
// no content it'll be cheap. :) // no content it'll be cheap. :)
$request = self::_commonHttp($short_url, $redirs); $request = self::_commonHttp($short_url, $redirs);
$response = $request->send(); $response = $request->send();
} elseif (400 == $response->getStatus()) {
throw new Exception('Got error 400 on HEAD request, will not go further.');
} }
} catch (Exception $e) { } catch (Exception $e) {
// Invalid URL or failure to reach server // Invalid URL or failure to reach server
@@ -118,14 +129,17 @@ class File_redirection extends Managed_DataObject
return $short_url; return $short_url;
} }
if ($response->getRedirectCount() && File::isProtected($response->getUrl())) { // if last url after all redirections is protected,
// Bump back up the redirect chain until we find a non-protected URL // use the url before it in the redirection chain
return self::lookupWhere($short_url, $response->getRedirectCount() - 1, true); if ($response->getRedirectCount() && File::isProtected($response->getEffectiveUrl())) {
$return_url = $response->redirUrls[$response->getRedirectCount()-1];
} else {
$return_url = $response->getEffectiveUrl();
} }
$ret = array('code' => $response->getStatus() $ret = array('code' => $response->getStatus()
, 'redirects' => $response->getRedirectCount() , 'redirects' => $response->getRedirectCount()
, 'url' => $response->getUrl()); , 'url' => $return_url);
$type = $response->getHeader('Content-Type'); $type = $response->getHeader('Content-Type');
if ($type) $ret['type'] = $type; if ($type) $ret['type'] = $type;
@@ -148,40 +162,70 @@ class File_redirection extends Managed_DataObject
* *
* @param string $in_url * @param string $in_url
* @param boolean $discover true to attempt dereferencing the redirect if we don't know it already * @param boolean $discover true to attempt dereferencing the redirect if we don't know it already
* @return mixed one of: * @return File_redirection
* string - target URL, if this is a direct link or a known redirect
* array - redirect info if this is an *unknown* redirect:
* associative array with the following elements:
* code: HTTP status code
* redirects: count of redirects followed
* url: URL string of final target
* type (optional): MIME type from Content-Type header
* size (optional): byte size from Content-Length header
* time (optional): timestamp from Last-Modified header
*/ */
public function where($in_url, $discover=true) { static function where($in_url, $discover=true) {
// let's see if we know this... $redir = new File_redirection();
$a = File::getKV('url', $in_url); $redir->url = $in_url;
$redir->urlhash = File::hashurl($redir->url);
$redir->redirections = 0;
if (!empty($a)) { try {
// this is a direct link to $a->url $r = File_redirection::getByUrl($in_url);
return $a->url; if($r instanceof File_redirection) {
} else { try {
$b = File_redirection::getKV('url', $in_url); $f = File::getKV('id',$r->file_id);
if (!empty($b)) { $r->file = $f;
// this is a redirect to $b->file_id $r->redir_url = $f->url;
$a = File::getKV('id', $b->file_id); } catch (NoResultException $e) {
return $a->url; // Invalid entry, delete and run again
common_log(LOG_ERR, "Could not find File with id=".$r->file_id." referenced in File_redirection, deleting File redirection entry and creating new File and File_redirection entries.");
$r->delete();
return self::where($in_url);
}
return $r;
}
} catch (NoResultException $e) {
try {
$f = File::getByUrl($in_url);
$redir->file_id = $f->id;
$redir->file = $f;
return $redir;
} catch (NoResultException $e) {
// Oh well, let's keep going
} }
} }
if ($discover) { if ($discover) {
$ret = File_redirection::lookupWhere($in_url); $redir_info = File_redirection::lookupWhere($in_url);
return $ret; if(is_string($redir_info)) {
} else { $redir_info = array('url' => $redir_info);
// No manual dereferencing; leave the unknown URL as is.
return $in_url;
} }
// Save the file if we don't have it already
$redir->file = File::saveNew($redir_info,$redir_info['url']);
// If this is a redirection, save it
// (if it hasn't been saved yet by some other process while we we
// were running lookupWhere())
if($redir_info['url'] != $in_url) {
try {
$file_redir = File_redirection::getByUrl($in_url);
} catch (NoResultException $e) {
$file_redir = new File_redirection();
$file_redir->urlhash = File::hashurl($in_url);
$file_redir->url = $in_url;
$file_redir->file_id = $redir->file->getID();
$file_redir->insert();
$file_redir->redir_url = $redir->file->url;
}
$file_redir->file = $redir->file;
return $file_redir;
}
}
return $redir;
} }
/** /**
@@ -198,7 +242,7 @@ class File_redirection extends Managed_DataObject
* @param User $user whose shortening options to use; defaults to the current web session user * @param User $user whose shortening options to use; defaults to the current web session user
* @return string * @return string
*/ */
function makeShort($long_url, $user=null) static function makeShort($long_url, $user=null)
{ {
$canon = File_redirection::_canonUrl($long_url); $canon = File_redirection::_canonUrl($long_url);
@@ -206,11 +250,7 @@ class File_redirection extends Managed_DataObject
// Did we get one? Is it shorter? // Did we get one? Is it shorter?
if (!empty($short_url)) { return !empty($short_url) ? $short_url : $long_url;
return $short_url;
} else {
return $long_url;
}
} }
/** /**
@@ -227,55 +267,37 @@ class File_redirection extends Managed_DataObject
* @return string * @return string
*/ */
function forceShort($long_url, $user) static function forceShort($long_url, $user)
{ {
$canon = File_redirection::_canonUrl($long_url); $canon = File_redirection::_canonUrl($long_url);
$short_url = File_redirection::_userMakeShort($canon, $user, true); $short_url = File_redirection::_userMakeShort($canon, $user, true);
// Did we get one? Is it shorter? // Did we get one? Is it shorter?
if (!empty($short_url)) { return !empty($short_url) ? $short_url : $long_url;
return $short_url;
} else {
return $long_url;
}
} }
function _userMakeShort($long_url, User $user=null, $force = false) { static function _userMakeShort($long_url, User $user=null, $force = false) {
$short_url = common_shorten_url($long_url, $user, $force); $short_url = common_shorten_url($long_url, $user, $force);
if (!empty($short_url) && $short_url != $long_url) { if (!empty($short_url) && $short_url != $long_url) {
$short_url = (string)$short_url; $short_url = (string)$short_url;
// store it // store it
$file = File::getKV('url', $long_url); try {
if ($file instanceof File) { $file = File::getByUrl($long_url);
$file_id = $file->id; } catch (NoResultException $e) {
} else {
// Check if the target URL is itself a redirect... // Check if the target URL is itself a redirect...
$redir_data = File_redirection::where($long_url); // This should already have happened in processNew in common_shorten_url()
if (is_array($redir_data)) { $redir = File_redirection::where($long_url);
// We haven't seen the target URL before. $file = $redir->file;
// Save file and embedding data about it!
$file = File::saveNew($redir_data, $long_url);
$file_id = $file->id;
} else if (is_string($redir_data)) {
// The file is a known redirect target.
$file = File::getKV('url', $redir_data);
if (empty($file)) {
// @fixme should we save a new one?
// this case was triggering sometimes for redirects
// with unresolvable targets; found while fixing
// "can't linkify" bugs with shortened links to
// SSL sites with cert issues.
return null;
} }
$file_id = $file->id; // Now we definitely have a File object in $file
} try {
} $file_redir = File_redirection::getByUrl($short_url);
$file_redir = File_redirection::getKV('url', $short_url); } catch (NoResultException $e) {
if (!$file_redir instanceof File_redirection) { $file_redir = new File_redirection();
$file_redir = new File_redirection; $file_redir->urlhash = File::hashurl($short_url);
$file_redir->url = $short_url; $file_redir->url = $short_url;
$file_redir->file_id = $file_id; $file_redir->file_id = $file->getID();
$file_redir->insert(); $file_redir->insert();
} }
return $short_url; return $short_url;
@@ -295,7 +317,7 @@ class File_redirection extends Managed_DataObject
* @param string $default_scheme if given a bare link; defaults to 'http://' * @param string $default_scheme if given a bare link; defaults to 'http://'
* @return string * @return string
*/ */
function _canonUrl($in_url, $default_scheme = 'http://') { static function _canonUrl($in_url, $default_scheme = 'http://') {
if (empty($in_url)) return false; if (empty($in_url)) return false;
$out_url = $in_url; $out_url = $in_url;
$p = parse_url($out_url); $p = parse_url($out_url);
@@ -307,6 +329,8 @@ class File_redirection extends Managed_DataObject
$out_url = str_replace('.-()', '', $out_url); $out_url = str_replace('.-()', '', $out_url);
break; break;
// non-HTTP schemes, so no redirects
case 'bitcoin':
case 'mailto': case 'mailto':
case 'aim': case 'aim':
case 'jabber': case 'jabber':
@@ -314,6 +338,16 @@ class File_redirection extends Managed_DataObject
// don't touch anything // don't touch anything
break; break;
// URLs without domain name, so no redirects
case 'magnet':
// don't touch anything
break;
// URLs with coordinates, not browsable domain names
case 'geo':
// don't touch anything
break;
default: default:
$out_url = $default_scheme . ltrim($out_url, '/'); $out_url = $default_scheme . ltrim($out_url, '/');
$p = parse_url($out_url); $p = parse_url($out_url);
@@ -332,12 +366,63 @@ class File_redirection extends Managed_DataObject
return $out_url; return $out_url;
} }
function saveNew($data, $file_id, $url) { static function saveNew($data, $file_id, $url) {
$file_redir = new File_redirection; $file_redir = new File_redirection;
$file_redir->urlhash = File::hashurl($url);
$file_redir->url = $url; $file_redir->url = $url;
$file_redir->file_id = $file_id; $file_redir->file_id = $file_id;
$file_redir->redirections = intval($data['redirects']); $file_redir->redirections = intval($data['redirects']);
$file_redir->httpcode = intval($data['code']); $file_redir->httpcode = intval($data['code']);
$file_redir->insert(); $file_redir->insert();
} }
static public function beforeSchemaUpdate()
{
$table = strtolower(get_called_class());
$schema = Schema::get();
$schemadef = $schema->getTableDef($table);
// 2015-02-19 We have to upgrade our table definitions to have the urlhash field populated
if (isset($schemadef['fields']['urlhash']) && in_array('urlhash', $schemadef['primary key'])) {
// We already have the urlhash field, so no need to migrate it.
return;
}
echo "\nFound old $table table, upgrading it to contain 'urlhash' field...";
// We have to create a urlhash that is _not_ the primary key,
// transfer data and THEN run checkSchema
$schemadef['fields']['urlhash'] = array (
'type' => 'varchar',
'length' => 64,
'not null' => true,
'description' => 'sha256 hash of the URL',
);
$schemadef['fields']['url'] = array (
'type' => 'text',
'description' => 'short URL (or any other kind of redirect) for file (id)',
);
unset($schemadef['primary key']);
$schema->ensureTable($table, $schemadef);
echo "DONE.\n";
$classname = ucfirst($table);
$tablefix = new $classname;
// urlhash is hash('sha256', $url) in the File table
echo "Updating urlhash fields in $table table...";
// Maybe very MySQL specific :(
$tablefix->query(sprintf('UPDATE %1$s SET %2$s=%3$s;',
$schema->quoteIdentifier($table),
'urlhash',
// The line below is "result of sha256 on column `url`"
'SHA2(url, 256)'));
echo "DONE.\n";
echo "Resuming core schema upgrade...";
}
public function getFile() {
if(empty($this->file) && $this->file_id) {
$this->file = File::getKV('id', $this->file_id);
}
return $this->file;
}
} }

Some files were not shown because too many files have changed in this diff Show More