Merge branch 'nightly'

Conflicts:
	INSTALL
This commit is contained in:
Mikael Nordfeldth 2015-04-05 15:35:54 +02:00
commit c94d9994d8
1245 changed files with 12709 additions and 133216 deletions

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
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
- $action: the current action
@ -1444,6 +1451,9 @@ StartResizeImageFile: Hook to resize an image and output it to a file. No matchi
- $outpath: string with output filepath
- $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).
- $out: HTMLOutputter class to use for outputting HTML.
- $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.
- $stored: Notice object that is being distributed.
- $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

238
INSTALL
View File

@ -6,13 +6,16 @@ TABLE OF CONTENTS
* Installation
- Getting it up and running
- Fancy URLs
- Themes
- Private
* Extra features
- Sphinx
- SMS
- Queues and daemons
- Themes
- Translation
- Queues and daemons
* After installation
- Backups
- Private
- Upgrading
Prerequisites
=============
@ -41,10 +44,10 @@ functional setup of GNU Social:
- php5-curl Fetching files by HTTP.
- php5-gd Image manipulation (scaling).
- php5-gmp For Salmon signatures (part of OStatus).
- php5-intl Internationalization support, part of core.
- php5-intl Internationalization support (transliteration et al).
- php5-json For WebFinger lookups and more.
- 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
Arch Linux, PHP is compiled with support for most extensions but they
@ -69,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
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
============
@ -131,9 +134,9 @@ especially if you've previously installed PHP/MariaDB packages.
writeable by the Web server group, as noted above.
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
the database with another program. You can name it whatever you want,
@ -147,17 +150,17 @@ especially if you've previously installed PHP/MariaDB packages.
database. If you have shell access, this will probably work from the
MariaDB shell:
GRANT ALL on gnusocial.*
TO 'gnusocial'@'localhost'
GRANT ALL on social.*
TO 'social'@'localhost'
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
test logging in to MariaDB as this new user.
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
install program will configure your site and install the initial,
@ -171,55 +174,100 @@ Fancy URLs
----------
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
look like this:
It's possible to configure the software to use fancy URLs so it looks like
this instead:
http://example.net/gnusocial/index.php?p=gnusocial/fred
It's possible to configure the software so it looks like this instead:
http://example.net/gnusocial/fred
https://social.example.net/fred
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
mod_rewrite enabled, -OR- know how to configure "url redirection" in
your server (like lighttpd or nginx).
1. Copy the htaccess.sample file to .htaccess in your StatusNet
directory.
2. Change the "RewriteBase" in the new .htaccess file to be the URL path
to your GNU Social installation on your server. Typically this will
be the path to your GNU Social directory relative to your Web root.
If you are installing it in the root directory, leave it as '/'.
3. Add, uncomment or change a line in your config.php file so it says:
1. See the instructions for each respective webserver software:
* For Apache, inspect the "htaccess.sample" file and save it as
".htaccess" after making any necessary modifications. Our sample
file is well commented.
* For lighttpd, inspect the lighttpd.conf.example file and apply the
appropriate changes in your virtualhost configuration for lighttpd.
* For nginx and 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;
You should now be able to navigate to a "fancy" URL on your server,
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
the server first.
Themes
------
If it doesn't work, double-check that AllowOverride for the GNU Social
directory is 'All' in your Apache configuration file. This is usually
/etc/httpd.conf, /etc/apache/httpd.conf, or (on Debian and Ubuntu)
/etc/apache2/sites-available/default. See the Apache documentation for
.htaccess files for more details:
As of right now, your ability change the theme is limited to CSS
stylesheets and some image files; you can't change the HTML output,
like adding or removing menu items, without the help of a plugin.
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
------
@ -284,7 +332,21 @@ For this to work, there *must* be a domain or sub-domain for which all
$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
------------------
@ -346,16 +408,13 @@ separate server is probably a good idea for high-volume sites.
.htaccess file, but make sure that your config.php file is close
to, or identical to, your Web server's version.
3. In your config.php files (both the Web server and the queues
server!), set the following variable:
3. In your config.php files (on the server where you run the queue
daemon), set the following variable:
$config['queue']['enabled'] = true;
$config['queue']['daemon'] = true;
You may also want to look at the 'daemon' section of this file for
more daemon options. Note that if you set the 'user' and/or 'group'
options, you'll need to create that user and/or group by hand.
They're not created automatically.
You may also want to look at the 'Queues and Daemons' section in
this file for more background processing options.
4. On the queues server, run the command scripts/startdaemons.sh.
@ -385,85 +444,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
best response time, especially when using XMPP.
Themes
------
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
After installation
==================
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
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.
Private
-------
Upgrading
---------
The administrator can set the "private" flag for a site so that it's
not visible to non-logged-in users. (This is the default for new installs of version 1.0!)
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';
Upgrading is strongly recommended to stay up to date with security fixes
and new features. For instructions on how to upgrade GNU social code,
please see the UPGRADE file.

View File

@ -1,5 +1,5 @@
# GNU social 1.1.3
February 2015-02-27
# GNU social 1.2.x
2015
(c) Free Software Foundation, Inc
(c) StatusNet, Inc
@ -100,15 +100,19 @@ for additional terms.
## New this version
This is a security fix and bug fix release since 1.1.3-beta2.
All 1.1.x sites should upgrade to this version.
This is the development branch for the 1.2.x version of GNU social.
All daring 1.1.x admins should upgrade to this version.
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/>)
- Many improvements to ease adoption of the Qvitter front-end <https://github.com/hannesmannerheim/qvitter>
- 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:
@ -146,7 +150,7 @@ In the current phase of development it is probably
recommended to use git as a means to stay up to date
with the source code. You can choose between these
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
- nightly "unstable", most updates, not always working

157
UPGRADE
View File

@ -1,99 +1,98 @@
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://bugz.foocorp.net/
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://bugz.foocorp.net/ (tag GNU social)
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
1.1.1. If you are running a StatusNet version lower than this, please
follow the upgrade procedures for each respective StatusNet version.
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
modules used, so double-check the INSTALL file's requirements list.
You are now running StatusNet 1.1.1 and want to migrate to GNU social
1.2.x. Beware there may be changes in minimum required version of PHP
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.
0. Stop your queue daemons 'php scripts/stopdaemon.php' should do it.
Not everyone runs queue daemons, but the above command won't hurt.
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. Unpack your GNU social code to a fresh directory.
1. Stop your queue daemons 'bash scripts/stopdaemons.sh' should do it.
Not everyone runs queue daemons, but the above command won't hurt.
2. Synchronize your local files to the GNU social directory. These
will be the local files such as avatars, config and files:
2. Unpack your GNU social code to a fresh directory. You can do this
by cloning our git repository:
$ git clone https://gitorious.org/social/mainline.git gnusocial
avatar/*
background/*
file/*
local/*
.htaccess
config.php
3. Synchronize your local files to the GNU social directory. These
will be the local files such as avatars, config and files:
3. Replace your old StatusNet directory with the new GNU social
directory in your webserver root.
avatar/*
background/*
file/*
local/*
.htaccess
config.php
4. Run the upgrade script: 'php scripts/upgrade.php'
This command will point you in the right direction on how to do it:
$ rsync -avP statusnet/{.htaccess,avatar,background,file,local,config.php} gnusocial/
5. Start your queue daemons: 'php scripts/startdaemons.php'
4. Replace your old StatusNet directory with the new GNU social
directory in your webserver root.
6. Report any issues at https://bugz.foocorp.net/ (tag GNU social)
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://bugz.foocorp.net/
6. Start your queue daemons: 'bash scripts/startdaemons.sh'
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.
7. Report any issues at https://bugz.foocorp.net/ (tag GNU social)

View File

@ -1,5 +1,4 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
@ -153,32 +152,27 @@ class ApiAccountRegisterAction extends ApiAction
$this->clientError(_('Passwords do not match.'), 400);
} else {
// annoy spammers
sleep(7);
// annoy spammers
sleep(7);
if ($user = User::register(array('nickname' => $nickname,
'password' => $password,
'email' => $email,
'fullname' => $fullname,
'homepage' => $homepage,
'bio' => $bio,
'location' => $location,
'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);
}
try {
$user = User::register(array('nickname' => $nickname,
'password' => $password,
'email' => $email,
'fullname' => $fullname,
'homepage' => $homepage,
'bio' => $bio,
'location' => $location,
'code' => $this->code));
Event::handle('EndRegistrationTry', array($this));
Event::handle('EndRegistrationTry', array($this));
$this->initDocument('json');
$this->showJsonObjects($this->twitterUserArray($user->getProfile()));
$this->endDocument('json');
$this->initDocument('json');
$this->showJsonObjects($this->twitterUserArray($user->getProfile()));
$this->endDocument('json');
} else {
// TRANS: Form validation error displayed when trying to register with an invalid username or password.
$this->clientError(_('Invalid username or password.'), 400);
}
} catch (Exception $e) {
$this->clientError($e->getMessage(), 400);
}
}
}

View File

@ -29,9 +29,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* 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);
}
if ($this->user->isSubscribed($this->other)) {
if ($this->scoped->isSubscribed($this->other)) {
$errmsg = sprintf(
// 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.
@ -101,9 +99,9 @@ class ApiFriendshipsCreateAction extends ApiAuthAction
}
try {
Subscription::start($this->user->getProfile(), $this->other);
} catch (Exception $e) {
$this->clientError($e->getMessage(), 403);
Subscription::start($this->scoped, $this->other);
} catch (AlreadyFulfilledException $e) {
$this->clientError($e->getMessage(), 409);
}
$this->initDocument($this->format);

View File

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

View File

@ -29,9 +29,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* 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
*/
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);
@ -72,22 +70,18 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction
*
* Check the format and show the user info
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
function handle($args)
protected function handle()
{
parent::handle($args);
parent::handle();
if (empty($this->profile_a) || empty($this->profile_b)) {
$this->clientError(
// 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.'),
400,
$this->format
400
);
return;
}
$result = Subscription::exists($this->profile_a, $this->profile_b);

View File

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

View File

@ -79,6 +79,10 @@ class ApiTimelineUserAction extends ApiBareAuthAction
$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();
return true;
@ -405,7 +409,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
// Get (safe!) HTML and text versions of the content
$rendered = $this->purify($sourceContent);
$rendered = common_purify($sourceContent);
$content = common_strip_html($rendered);
$shortened = $this->auth_user->shortenLinks($content);
@ -504,13 +508,4 @@ class ApiTimelineUserAction extends ApiBareAuthAction
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));
}
if (Subscription::exists($this->_profile, $profile)) {
try {
$sub = Subscription::start($this->_profile, $profile);
} catch (AlreadyFulfilledException $e) {
// 409 Conflict
// TRANS: Client error displayed trying to subscribe to an already subscribed profile.
// 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));
$this->clientError($e->getMessage(), 409);
}
Event::handle('EndAtomPubNewActivity', array($activity, $sub));

View File

@ -62,6 +62,6 @@ class Attachment_thumbnailAction extends AttachmentAction
common_redirect($e->file->getUrl());
}
common_redirect($thumbnail->getUrl());
common_redirect(File_thumbnail::url($thumbnail->filename));
}
}

View File

@ -28,13 +28,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
define('MAX_ORIGINAL', 480);
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Upload an avatar
@ -369,13 +363,27 @@ class AvatarsettingsAction extends SettingsAction
$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_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')));
$box = array('width' => $size, 'height' => $size,
'x' => $dest_x, 'y' => $dest_y,
'w' => $dest_w, 'h' => $dest_h);
$user = common_current_user();
$profile = $user->getProfile();
$imagefile = new ImageFile($user->id, $filedata['filepath']);
$filename = $imagefile->resize($size, $dest_x, $dest_y, $dest_w, $dest_h);
$imagefile = new ImageFile(null, $filedata['filepath']);
$filename = Avatar::filename($profile->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)) {
@unlink($filedata['filepath']);

View File

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

View File

@ -174,11 +174,15 @@ class DocNav extends Menu
{
function show()
{
$stub = new HomeStubNav($this->action);
$this->submenu(_m('MENU','Home'), $stub);
if (Event::handle('StartDocNav', array($this))) {
$stub = new HomeStubNav($this->action);
$this->submenu(_m('MENU','Home'), $stub);
$docs = new DocListNav($this->action);
$this->submenu(_m('MENU','Docs'), $docs);
$docs = new DocListNav($this->action);
$this->submenu(_m('MENU','Docs'), $docs);
Event::handle('EndDocNav', array($this));
}
}
}

View File

@ -91,7 +91,7 @@ class EmailsettingsAction extends SettingsAction
*/
function showContent()
{
$user = common_current_user();
$user = $this->scoped->getUser();
$this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_email',
@ -313,17 +313,15 @@ class EmailsettingsAction extends SettingsAction
*/
function savePreferences()
{
$user = common_current_user();
$user = $this->scoped->getUser();
if (Event::handle('StartEmailSaveForm', array($this, $this->scoped))) {
$emailnotifysub = $this->boolean('emailnotifysub');
$emailnotifymsg = $this->boolean('emailnotifymsg');
$emailnotifynudge = $this->boolean('emailnotifynudge');
$emailnotifyattn = $this->boolean('emailnotifyattn');
$emailmicroid = $this->boolean('emailmicroid');
$emailpost = $this->boolean('emailpost');
assert(!is_null($user)); // should already be checked
$emailnotifysub = $this->booleanintstring('emailnotifysub');
$emailnotifymsg = $this->booleanintstring('emailnotifymsg');
$emailnotifynudge = $this->booleanintstring('emailnotifynudge');
$emailnotifyattn = $this->booleanintstring('emailnotifyattn');
$emailmicroid = $this->booleanintstring('emailmicroid');
$emailpost = $this->booleanintstring('emailpost');
$user->query('BEGIN');
@ -340,6 +338,7 @@ class EmailsettingsAction extends SettingsAction
if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__);
$user->query('ROLLBACK');
// TRANS: Server error thrown on database error updating e-mail preferences.
$this->serverError(_('Could not update user.'));
}

View File

@ -28,13 +28,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
define('MAX_ORIGINAL', 480);
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* 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_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'];
$size = min($dest_w, $dest_h);
$size = ($size > MAX_ORIGINAL) ? MAX_ORIGINAL:$size;
$size = min($dest_w, $dest_h, common_config('avatar', 'maxsize'));
$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']);
$filename = $imagefile->resize($size, $dest_x, $dest_y, $dest_w, $dest_h);
$profile = $this->group->getProfile();
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']);
unset($_SESSION['FILEDATA']);
$this->mode = 'upload';

View File

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

View File

@ -36,24 +36,6 @@ class LoginAction extends FormAction
{
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
*
@ -79,10 +61,8 @@ class LoginAction extends FormAction
*
* @return void
*/
protected function handlePost()
protected function doPost()
{
parent::handlePost();
// XXX: login throttle
$nickname = $this->trimmed('nickname');
@ -122,22 +102,6 @@ class LoginAction extends FormAction
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()
{
parent::showScripts();
@ -208,7 +172,7 @@ class LoginAction extends FormAction
*
* @return void
*/
function getInstructions()
protected function getInstructions()
{
if (common_logged_in() && !common_is_real_login() &&
common_get_returnto()) {

View File

@ -28,9 +28,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Add a new application
@ -51,10 +49,8 @@ class NewApplicationAction extends FormAction
return _('New application');
}
protected function handlePost()
protected function doPost()
{
parent::handlePost();
if ($this->arg('cancel')) {
common_redirect(common_local_url('oauthappssettings'), 303);
} elseif ($this->arg('save')) {
@ -65,27 +61,15 @@ class NewApplicationAction extends FormAction
$this->clientError(_('Unexpected form submission.'));
}
function showForm($msg=null)
protected function getForm()
{
$this->msg = $msg;
$this->showPage();
return new ApplicationEditForm($this);
}
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.
_('Use this form to register a new application.'));
}
// TRANS: Form instructions for registering a new application.
return _('Use this form to register a new application.');
}
private function trySave()
@ -181,6 +165,7 @@ class NewApplicationAction extends FormAction
if (!$result) {
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.
$this->serverError(_('Could not create application.'));
}
@ -191,9 +176,9 @@ class NewApplicationAction extends FormAction
if (!$this->app_id) {
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.
$this->serverError(_('Could not create application.'));
$app->query('ROLLBACK');
}
try {

View File

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

View File

@ -30,9 +30,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Action for posting new notices
@ -62,6 +60,9 @@ class NewnoticeAction extends FormAction
// TRANS: Page title after sending a notice.
return _('Notice posted');
}
if ($this->int('inreplyto')) {
return _m('TITLE', 'New reply');
}
// TRANS: Page title for sending a 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 not, it will show an error message -- possibly Ajax-y.
@ -90,17 +91,15 @@ class NewnoticeAction extends FormAction
*
* @return void
*/
protected function handlePost()
protected function doPost()
{
parent::handlePost();
assert($this->scoped); // XXX: maybe an error instead...
assert($this->scoped instanceof Profile); // XXX: maybe an error instead...
$user = $this->scoped->getUser();
$content = $this->trimmed('status_textarea');
$options = array();
Event::handle('StartSaveNewNoticeWeb', array($this, $user, &$content, &$options));
if (!$content) {
if (empty($content)) {
// TRANS: Client error displayed trying to send a notice without content.
$this->clientError(_('No content!'));
}
@ -110,7 +109,7 @@ class NewnoticeAction extends FormAction
$cmd = $inter->handle_command($user, $content);
if ($cmd) {
if (StatusNet::isAjax()) {
if (GNUsocial::isAjax()) {
$cmd->execute(new AjaxWebChannel($this));
} else {
$cmd->execute(new WebChannel($this));
@ -128,7 +127,7 @@ class NewnoticeAction extends FormAction
Notice::maxContent()));
}
$replyto = intval($this->trimmed('inreplyto'));
$replyto = $this->int('inreplyto');
if ($replyto) {
$options['reply_to'] = $replyto;
}
@ -195,7 +194,7 @@ class NewnoticeAction extends FormAction
Event::handle('EndSaveNewNoticeWeb', array($this, $user, &$content_shortened, &$options));
if (!StatusNet::isAjax()) {
if (!GNUsocial::isAjax()) {
$url = common_local_url('shownotice', array('notice' => $this->stored->id));
common_redirect($url, 303);
}

View File

@ -82,8 +82,8 @@ class ProfilesettingsAction extends SettingsAction
*/
function showContent()
{
$user = common_current_user();
$profile = $user->getProfile();
$profile = $this->scoped;
$user = $this->scoped->getUser();
$this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_profile',
@ -104,7 +104,9 @@ class ProfilesettingsAction extends SettingsAction
// TRANS: Tooltip for field label in form for profile settings.
_('1-64 lowercase letters or numbers, no punctuation or spaces.'),
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->elementStart('li');
// TRANS: Field label in form for profile settings.
@ -260,9 +262,9 @@ class ProfilesettingsAction extends SettingsAction
$homepage = $this->trimmed('homepage');
$bio = $this->trimmed('bio');
$location = $this->trimmed('location');
$autosubscribe = $this->boolean('autosubscribe');
$autosubscribe = $this->booleanintstring('autosubscribe');
$subscribe_policy = $this->trimmed('subscribe_policy');
$private_stream = $this->boolean('private_stream');
$private_stream = $this->booleanintstring('private_stream');
$language = $this->trimmed('language');
$timezone = $this->trimmed('timezone');
$tagstring = $this->trimmed('tags');
@ -398,7 +400,7 @@ class ProfilesettingsAction extends SettingsAction
$orig = clone($prefs);
}
$prefs->share_location = $this->boolean('sharelocation');
$prefs->share_location = $this->booleanintstring('sharelocation');
if ($exists) {
$result = $prefs->update($orig);

View File

@ -27,9 +27,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
/**
* An action for registering a new user account
@ -229,40 +227,38 @@ class RegisterAction extends Action
} else if ($password != $confirm) {
// TRANS: Form validation error displayed when trying to register with non-matching passwords.
$this->showForm(_('Passwords do not match.'));
} else if ($user = User::register(array('nickname' => $nickname,
} else {
try {
$user = User::register(array('nickname' => $nickname,
'password' => $password,
'email' => $email,
'fullname' => $fullname,
'homepage' => $homepage,
'bio' => $bio,
'location' => $location,
'code' => $code))) {
if (!($user instanceof User)) {
'code' => $code));
// success!
if (!common_set_user($user)) {
// TRANS: Server error displayed when saving fails during user registration.
$this->serverError(_('Error setting user.'));
}
// this is a real login
common_real_login(true);
if ($this->boolean('rememberme')) {
common_debug('Adding rememberme cookie for ' . $nickname);
common_rememberme($user);
}
// Re-init language env in case it changed (not yet, but soon)
common_init_language();
Event::handle('EndRegistrationTry', array($this));
$this->showSuccess();
} catch (Exception $e) {
// TRANS: Form validation error displayed when trying to register with an invalid username or password.
$this->showForm(_('Invalid username or password.'));
return;
$this->showForm($e->getMessage());
}
// success!
if (!common_set_user($user)) {
// TRANS: Server error displayed when saving fails during user registration.
$this->serverError(_('Error setting user.'));
}
// this is a real login
common_real_login(true);
if ($this->boolean('rememberme')) {
common_debug('Adding rememberme cookie for ' . $nickname);
common_rememberme($user);
}
// Re-init language env in case it changed (not yet, but soon)
common_init_language();
Event::handle('EndRegistrationTry', array($this));
$this->showSuccess();
} else {
// TRANS: Form validation error displayed when trying to register with an invalid username or password.
$this->showForm(_('Invalid username or password.'));
}
}
}

View File

@ -27,13 +27,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/personalgroupnav.php';
require_once INSTALLDIR.'/lib/noticelist.php';
require_once INSTALLDIR.'/lib/feedlist.php';
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* List of replies
@ -44,72 +38,42 @@ 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
* @link http://status.net/
*/
class RepliesAction extends Action
class RepliesAction extends ManagedAction
{
var $page = null;
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)
protected function doPreparation()
{
parent::prepare($args);
$nickname = common_canonical_nickname($this->arg('nickname'));
$this->user = User::getKV('nickname', $nickname);
if (!$this->user) {
if (!$this->user instanceof User) {
// TRANS: Client error displayed when trying to reply to a non-exsting user.
$this->clientError(_('No such user.'));
}
$profile = $this->user->getProfile();
$this->target = $this->user->getProfile();
if (!$profile) {
if (!$this->target instanceof 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;
$this->page = $this->int('page') ?: 1;
common_set_returnto($this->selfUrl());
$stream = new ReplyNoticeStream($this->user->id,
Profile::current());
$stream = new ReplyNoticeStream($this->target->getID(), $this->scoped);
$this->notice = $stream->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)
$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 +88,12 @@ class RepliesAction extends Action
if ($this->page == 1) {
// TRANS: Title for first page of replies for a user.
// TRANS: %s is a user nickname.
return sprintf(_("Replies to %s"), $this->user->nickname);
return sprintf(_("Replies to %s"), $this->target->getNickname());
} else {
// 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.
return sprintf(_('Replies to %1$s, page %2$d'),
$this->user->nickname,
$this->target->getNickname(),
$this->page);
}
}
@ -144,7 +108,7 @@ class RepliesAction extends Action
return array(new Feed(Feed::JSON,
common_local_url('ApiTimelineMentions',
array(
'id' => $this->user->nickname,
'id' => $this->target->getNickname(),
'format' => 'as')),
// TRANS: Link for feed with replies for a user.
// TRANS: %s is a user nickname.
@ -152,38 +116,31 @@ class RepliesAction extends Action
$this->user->nickname)),
new Feed(Feed::RSS1,
common_local_url('repliesrss',
array('nickname' => $this->user->nickname)),
array('nickname' => $this->target->getNickname())),
// TRANS: Link for feed with replies for a user.
// TRANS: %s is a user nickname.
sprintf(_('Replies feed for %s (RSS 1.0)'),
$this->user->nickname)),
$this->target->getNickname())),
new Feed(Feed::RSS2,
common_local_url('ApiTimelineMentions',
array(
'id' => $this->user->nickname,
'id' => $this->target->getNickname(),
'format' => 'rss')),
// TRANS: Link for feed with replies for a user.
// TRANS: %s is a user nickname.
sprintf(_('Replies feed for %s (RSS 2.0)'),
$this->user->nickname)),
$this->target->getNickname())),
new Feed(Feed::ATOM,
common_local_url('ApiTimelineMentions',
array(
'id' => $this->user->nickname,
'id' => $this->target->getNickname(),
'format' => 'atom')),
// TRANS: Link for feed with replies for a user.
// TRANS: %s is a user nickname.
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()
{
$nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
@ -195,33 +152,30 @@ class RepliesAction extends Action
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'replies',
array('nickname' => $this->user->nickname));
array('nickname' => $this->target->getNickname()));
}
function showEmptyListMessage()
{
// TRANS: Empty list message for page with replies for a user.
// TRANS: %1$s and %s$s are 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.'),
$this->user->nickname,
$this->user->nickname) . ' ';
// TRANS: %1$s is the user nickname.
$message = sprintf(_('This is the timeline showing replies to %1$s but no notices have arrived yet.'), $this->target->getNickname());
$message .= ' '; // Spacing between this sentence and the next.
if (common_logged_in()) {
$current_user = common_current_user();
if ($this->user->id === $current_user->id) {
if ($this->target->getID() === $this->scoped->getID()) {
// 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).
$message .= _('You can engage other users in a conversation, subscribe to more people or [join groups](%%action.groups%%).');
} else {
// 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).
$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);
// 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](../%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: %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');
@ -229,7 +183,7 @@ class RepliesAction extends Action
$this->elementEnd('div');
}
function isReadOnly($args)
public function isReadOnly($args)
{
return true;
}

View File

@ -70,7 +70,7 @@ class ShownoticeAction extends ManagedAction
{
parent::prepare($args);
if ($this->boolean('ajax')) {
StatusNet::setApi(true);
GNUsocial::setApi(true);
}
$this->notice = $this->getNotice();

View File

@ -28,15 +28,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
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';
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* User profile page
@ -233,10 +225,7 @@ class ShowstreamAction extends ProfileAction
function showNotices()
{
$pnl = null;
if (Event::handle('ShowStreamNoticeList', array($this->notice, $this, &$pnl))) {
$pnl = new ProfileNoticeList($this->notice, $this);
}
$pnl = new NoticeList($this->notice, $this);
$cnt = $pnl->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
@ -293,57 +282,3 @@ class ShowstreamAction extends ProfileAction
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

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

View File

@ -19,7 +19,6 @@
if (!defined('GNUSOCIAL')) { exit(1); }
require_once INSTALLDIR . '/lib/settingsaction.php';
require_once INSTALLDIR . '/lib/peopletags.php';
class TagprofileAction extends FormAction
@ -29,36 +28,32 @@ class TagprofileAction extends FormAction
protected $target = null;
protected $form = 'TagProfile';
protected function prepare(array $args=array())
protected function doPreparation()
{
parent::prepare($args);
$id = $this->trimmed('id');
if (!$id) {
$this->target = null;
} else {
$uri = $this->trimmed('uri');
if (!empty($id)) {
$this->target = Profile::getKV('id', $id);
if (!$this->target instanceof Profile) {
// TRANS: Client error displayed when referring to non-existing profile 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.
$this->clientError(_('You cannot tag this user.'));
}
return true;
}
$this->formOpts = $this->target;
protected function handle()
{
if (Event::handle('StartTagProfileAction', array($this, $this->target))) {
parent::handle();
Event::handle('EndTagProfileAction', array($this, $this->target));
}
return true;
}
function title()
@ -115,17 +110,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');
$token = $this->trimmed('token');
@ -144,22 +130,16 @@ class TagprofileAction extends FormAction
if (!common_valid_profile_tag($tag)) {
// TRANS: Form validation error displayed if a given tag is invalid.
// TRANS: %s is the invalid tag.
$this->showForm(sprintf(_('Invalid tag: "%s".'), $tag));
return;
throw new ClientException(sprintf(_('Invalid tag: "%s".'), $tag));
}
$tag_priv[$tag] = $private;
}
}
try {
$result = Profile_tag::setTags($this->scoped->id, $this->target->id, $tags, $tag_priv);
if (!$result) {
throw new Exception('The tags could not be saved.');
}
} catch (Exception $e) {
$this->showForm($e->getMessage());
return false;
$result = Profile_tag::setTags($this->scoped->getID(), $this->target->getID(), $tags, $tag_priv);
if (!$result) {
throw new ServerException('The tags could not be saved.');
}
if ($this->boolean('ajax')) {
@ -188,17 +168,4 @@ class TagprofileAction extends FormAction
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

@ -22,7 +22,7 @@ class Attention extends Managed_DataObject
public $__table = 'attention'; // table name
public $notice_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 $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@ -33,7 +33,7 @@ class Attention extends Managed_DataObject
'fields' => array(
'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'),
'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'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),

View File

@ -15,8 +15,8 @@ class Avatar extends Managed_DataObject
public $width; // int(4) primary_key not_null
public $height; // int(4) primary_key not_null
public $mediatype; // varchar(32) not_null
public $filename; // varchar(255)
public $url; // varchar(255) unique_key
public $filename; // varchar(191) not 255 because utf8mb4 takes more space
public $url; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -32,14 +32,14 @@ class Avatar extends Managed_DataObject
'width' => array('type' => 'int', 'not null' => true, 'description' => 'image width'),
'height' => array('type' => 'int', 'not null' => true, 'description' => 'image height'),
'mediatype' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'file type'),
'filename' => array('type' => 'varchar', 'length' => 255, 'description' => 'local filename, if local'),
'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'avatar location'),
'filename' => array('type' => 'varchar', 'length' => 191, 'description' => 'local filename, if local'),
'url' => array('type' => 'text', 'description' => 'avatar location, not indexed - do not use in WHERE statement'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),
'primary key' => array('profile_id', 'width', 'height'),
'unique keys' => array(
'avatar_url_key' => array('url'),
// 'avatar_filename_key' => array('filename'),
),
'foreign keys' => array(
'avatar_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
@ -241,16 +241,21 @@ class Avatar extends Managed_DataObject
// TRANS: An error message when avatar size is unreasonable
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);
$imagefile = new ImageFile($target->id, Avatar::path($original->filename));
$filename = $imagefile->resize($width);
$imagefile = new ImageFile(null, Avatar::path($original->filename));
$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->original = false;
$scaled->width = $width;
$scaled->height = $width;
$scaled->height = $height;
$scaled->url = Avatar::url($filename);
$scaled->filename = $filename;
$scaled->created = common_sql_now();

View File

@ -35,7 +35,7 @@ class Config extends Managed_DataObject
public $__table = 'config'; // table name
public $section; // varchar(32) primary_key not_null
public $setting; // varchar(32) primary_key not_null
public $value; // varchar(255)
public $value; // varchar(191) not 255 because utf8mb4 takes more space
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
@ -46,7 +46,7 @@ class Config extends Managed_DataObject
'fields' => array(
'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'),
'value' => array('type' => 'varchar', 'length' => 255, 'description' => 'configuration value'),
'value' => array('type' => 'varchar', 'length' => 191, 'description' => 'configuration value'),
),
'primary key' => array('section', 'setting'),
);

View File

@ -12,8 +12,8 @@ class Confirm_address extends Managed_DataObject
public $__table = 'confirm_address'; // table name
public $code; // varchar(32) primary_key not_null
public $user_id; // int(4) not_null
public $address; // varchar(255) not_null
public $address_extra; // varchar(255) not_null
public $address; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $address_extra; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $address_type; // varchar(8) not_null
public $claimed; // datetime()
public $sent; // datetime()
@ -28,8 +28,8 @@ class Confirm_address extends Managed_DataObject
'fields' => array(
'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'),
'address' => array('type' => 'varchar', 'length' => 255, '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' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'address (email, xmpp, SMS, etc.)'),
'address_extra' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'carrier ID, for 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'),
'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 */
public $__table = 'consumer'; // table name
public $consumer_key; // varchar(255) primary_key not_null
public $consumer_secret; // varchar(255) not_null
public $consumer_key; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
public $consumer_secret; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $seed; // char(32) not_null
public $created; // datetime not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@ -24,8 +24,8 @@ class Consumer extends Managed_DataObject
return array(
'description' => 'OAuth consumer record',
'fields' => array(
'consumer_key' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'unique identifier, root URL'),
'consumer_secret' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'secret value'),
'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'),
'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'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),

View File

@ -35,7 +35,7 @@ class Conversation extends Managed_DataObject
{
public $__table = 'conversation'; // table name
public $id; // int(4) primary_key not_null
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 $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@ -44,7 +44,7 @@ class Conversation extends Managed_DataObject
return array(
'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'should be set from root notice id (since 2014-03-01 commit)'),
'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'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),

View File

@ -34,7 +34,7 @@ class Deleted_notice extends Managed_DataObject
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 $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $created; // datetime() not_null
public $deleted; // datetime() not_null
@ -47,7 +47,7 @@ class Deleted_notice extends Managed_DataObject
'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'),
'uri' => array('type' => 'varchar', 'length' => 191, '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'),
),

View File

@ -26,29 +26,36 @@ class File extends Managed_DataObject
{
public $__table = 'file'; // table name
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 $size; // int(4)
public $title; // varchar(255)
public $title; // varchar(191) not 255 because utf8mb4 takes more space
public $date; // int(4)
public $protected; // int(4)
public $filename; // varchar(255)
public $filename; // varchar(191) not 255 because utf8mb4 takes more space
public $width; // int(4)
public $height; // int(4)
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
const URLHASH_ALG = 'sha256';
const FILEHASH_ALG = 'sha256';
public static function schemaDef()
{
return array(
'fields' => array(
'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'),
'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' => 'varchar', 'length' => 191, 'description' => 'title of resource when available'),
'date' => array('type' => 'int', 'description' => 'date of resource according to http query'),
'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' => 'varchar', 'length' => 191, 'description' => 'if a local file, name of the file'),
'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'),
@ -56,7 +63,10 @@ class File extends Managed_DataObject
),
'primary key' => array('id'),
'unique keys' => array(
'file_url_key' => array('url'),
'file_urlhash_key' => array('urlhash'),
),
'indexes' => array(
'file_filehash_idx' => array('filehash'),
),
);
}
@ -77,10 +87,11 @@ class File extends Managed_DataObject
// I don't know why we have to keep doing this but I'm adding this last check to avoid
// uniqueness bugs.
$file = File::getKV('url', $given_url);
$file = File::getKV('urlhash', self::hashurl($given_url));
if (!$file instanceof File) {
$file = new File;
$file->urlhash = self::hashurl($given_url);
$file->url = $given_url;
if (!empty($redir_data['protected'])) $file->protected = $redir_data['protected'];
if (!empty($redir_data['title'])) $file->title = $redir_data['title'];
@ -122,51 +133,56 @@ class File extends Managed_DataObject
throw new ServerException('No canonical URL from given URL to process');
}
$file = File::getKV('url', $given_url);
if (!$file instanceof File) {
$file = null;
try {
$file = File::getByUrl($given_url);
} catch (NoResultException $e) {
// 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) {
try {
$file_redir = File_redirection::getByUrl($given_url);
$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();
}
} catch (NoResultException $e) {
// We just wanted to doublecheck whether a File_thumbnail we might've had
// actually referenced an existing File object.
}
}
// If we still don't have a File object, let's create one now!
if (!$file instanceof File) {
// @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));
}
// If we still don't have a File object, let's create one now!
if (!$file instanceof File) {
// @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 ($redir_url === $given_url || !$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) {
@ -237,12 +253,7 @@ class File extends Managed_DataObject
static function filename(Profile $profile, $origname, $mimetype)
{
try {
$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);
}
$ext = self::guessMimeExtension($mimetype);
// Normalize and make the original filename more URL friendly.
$origname = basename($origname, ".$ext");
@ -263,6 +274,17 @@ class File extends Managed_DataObject
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
$ext = substr(strrchr($mimetype, '/'), 1);
}
return strtolower($ext);
}
/**
* Validation for as-saved base filenames
*/
@ -303,7 +325,7 @@ class File extends Managed_DataObject
}
if (StatusNet::useHTTPS()) {
if (GNUsocial::useHTTPS()) {
$sslserver = common_config('attachments', 'sslserver');
@ -381,6 +403,10 @@ class File extends Managed_DataObject
* @param $crop bool Crop to the max-values' aspect ratio
*
* @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)
{
@ -394,67 +420,16 @@ class File extends Managed_DataObject
}
}
if ($width === null) {
$width = common_config('thumbnail', 'width');
$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);
return $image->getFileThumbnail($width, $height, $crop);
}
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()
@ -462,7 +437,7 @@ class File extends Managed_DataObject
if (!empty($this->filename)) {
// A locally stored file, so let's generate a URL for our instance.
$url = self::url($this->filename);
if ($url != $this->url) {
if (self::hashurl($url) !== $this->urlhash) {
// 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);
@ -474,16 +449,40 @@ class File extends Managed_DataObject
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, $alg=File::FILEHASH_ALG)
{
$file = new File();
$file->filehash = strtolower($hashstr);
if (!$file->find(true)) {
throw new NoResultException($file);
}
return $file;
}
public function updateUrl($url)
{
$file = File::getKV('url', $url);
$file = File::getKV('urlhash', self::hashurl($url));
if ($file instanceof File) {
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,
$this->_quote((string)self::hashurl($url)),
$this->_quote((string)$url),
$this->_quote((string)$this->url)));
$this->_quote((string)$this->urlhash)));
if ($result === false) {
common_log_db_error($this, 'UPDATE', __FILE__);
throw new ServerException("Could not UPDATE {$this->__table}.url");
@ -502,9 +501,9 @@ class File extends Managed_DataObject
function blowCache($last=false)
{
self::blow('file:notice-ids:%s', $this->url);
self::blow('file:notice-ids:%s', $this->urlhash);
if ($last) {
self::blow('file:notice-ids:%s;last', $this->url);
self::blow('file:notice-ids:%s;last', $this->urlhash);
}
self::blow('file:notice-count:%d', $this->id);
}
@ -582,4 +581,54 @@ class File extends Managed_DataObject
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...";
// 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 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 */
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 $redirections; // int(4)
public $httpcode; // int(4)
@ -42,19 +43,30 @@ class File_redirection extends Managed_DataObject
{
return 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'),
'redirections' => array('type' => 'int', 'description' => 'redirect count'),
'httpcode' => array('type' => 'int', 'description' => 'HTTP status code (20x, 30x, etc.)'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),
'primary key' => array('url'),
'primary key' => array('urlhash'),
'foreign keys' => array(
'file_redirection_file_id_fkey' => array('file' => array('file_id' => 'id')),
),
);
}
static public function getByUrl($url)
{
$file = new File_redirection();
$file->urlhash = File::hashurl($url);
if (!$file->find(true)) {
throw new NoResultException($file);
}
return $file;
}
static function _commonHttp($url, $redirs) {
$request = new HTTPClient($url);
$request->setConfig(array(
@ -161,17 +173,18 @@ class File_redirection extends Managed_DataObject
*/
public function where($in_url, $discover=true) {
// let's see if we know this...
$a = File::getKV('url', $in_url);
if (!empty($a)) {
try {
$a = File::getByUrl($in_url);
// this is a direct link to $a->url
return $a->url;
} else {
$b = File_redirection::getKV('url', $in_url);
if (!empty($b)) {
} catch (NoResultException $e) {
try {
$b = File_redirection::getByUrl($in_url);
// this is a redirect to $b->file_id
$a = File::getKV('id', $b->file_id);
return $a->url;
} catch (NoResultException $e) {
// Oh well, let's keep going
}
}
@ -274,6 +287,7 @@ class File_redirection extends Managed_DataObject
$file_redir = File_redirection::getKV('url', $short_url);
if (!$file_redir instanceof File_redirection) {
$file_redir = new File_redirection;
$file_redir->urlhash = File::hashurl($short_url);
$file_redir->url = $short_url;
$file_redir->file_id = $file_id;
$file_redir->insert();
@ -334,10 +348,53 @@ class File_redirection extends Managed_DataObject
function saveNew($data, $file_id, $url) {
$file_redir = new File_redirection;
$file_redir->urlhash = File::hashurl($short_url);
$file_redir->url = $url;
$file_redir->file_id = $file_id;
$file_redir->redirections = intval($data['redirects']);
$file_redir->httpcode = intval($data['code']);
$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...";
}
}

View File

@ -27,8 +27,8 @@ class File_thumbnail extends Managed_DataObject
{
public $__table = 'file_thumbnail'; // table name
public $file_id; // int(4) primary_key not_null
public $url; // varchar(255) unique_key
public $filename; // varchar(255)
public $url; // text
public $filename; // varchar(191) not 255 because utf8mb4 takes more space
public $width; // int(4) primary_key
public $height; // int(4) primary_key
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -38,8 +38,8 @@ class File_thumbnail extends Managed_DataObject
return array(
'fields' => array(
'file_id' => array('type' => 'int', 'not null' => true, 'description' => 'thumbnail for what URL/file'),
'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL of thumbnail'),
'filename' => array('type' => 'varchar', 'length' => 255, 'description' => 'if stored locally, filename is put here'),
'url' => array('type' => 'text', 'not null' => false, 'description' => 'URL of thumbnail'),
'filename' => array('type' => 'varchar', 'length' => 191, 'description' => 'if stored locally, filename is put here'),
'width' => array('type' => 'int', 'description' => 'width of thumbnail'),
'height' => array('type' => 'int', 'description' => 'height of thumbnail'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
@ -117,46 +117,42 @@ class File_thumbnail extends Managed_DataObject
return File::path($filename);
}
static function url($filename)
{
// TODO: Store thumbnails in their own directory and don't use File::url here
return File::url($filename);
}
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()
{
if (!empty($this->getFile()->filename)) {
// A locally stored File, so let's generate a URL for our instance.
$url = File::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);
// A locally stored File, so we can dynamically generate a URL.
if (!empty($this->url)) {
// Let's just clear this field as there is no point in having it for local files.
$orig = clone($this);
$this->url = null;
$this->update($orig);
}
return $url;
$url = common_local_url('attachment_thumbnail', array('attachment'=>$this->file_id));
if (strpos($url, '?') === false) {
$url .= '?';
}
return $url . http_build_query(array('w'=>$this->width, 'h'=>$this->height));
}
// No local filename available, return the URL we have stored
return $this->url;
}
public function updateUrl($url)
{
$file = File_thumbnail::getKV('url', $url);
if ($file instanceof File_thumbnail) {
throw new ServerException('URL already exists in DB');
}
$sql = 'UPDATE %1$s SET url=%2$s WHERE url=%3$s;';
$result = $this->query(sprintf($sql, $this->__table,
$this->_quote((string)$url),
$this->_quote((string)$this->url)));
if ($result === false) {
common_log_db_error($this, 'UPDATE', __FILE__);
throw new ServerException("Could not UPDATE {$this->__table}.url");
}
return $result;
}
public function delete($useWhere=false)
{
if (!empty($this->filename) && file_exists(File_thumbnail::path($this->filename))) {

View File

@ -13,7 +13,7 @@ class Foreign_link extends Managed_DataObject
public $user_id; // int(4) primary_key not_null
public $foreign_id; // bigint(8) primary_key not_null unsigned
public $service; // int(4) primary_key not_null
public $credentials; // varchar(255)
public $credentials; // varchar(191) not 255 because utf8mb4 takes more space
public $noticesync; // tinyint(1) not_null default_1
public $friendsync; // tinyint(1) not_null default_2
public $profilesync; // tinyint(1) not_null default_1
@ -32,7 +32,7 @@ class Foreign_link extends Managed_DataObject
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'link to user on this system, if exists'),
'foreign_id' => array('type' => 'int', 'size' => 'big', 'unsigned' => true, 'not null' => true, 'description' => 'link to user on foreign service, if exists'),
'service' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to service'),
'credentials' => array('type' => 'varchar', 'length' => 255, 'description' => 'authc credentials, typically a password'),
'credentials' => array('type' => 'varchar', 'length' => 191, 'description' => 'authc credentials, typically a password'),
'noticesync' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'notice synchronization, bit 1 = sync outgoing, bit 2 = sync incoming, bit 3 = filter local replies'),
'friendsync' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 2, 'description' => 'friend synchronization, bit 1 = sync outgoing, bit 2 = sync incoming'),
'profilesync' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'profile synchronization, bit 1 = sync outgoing, bit 2 = sync incoming'),

View File

@ -12,7 +12,7 @@ class Foreign_service extends Managed_DataObject
public $__table = 'foreign_service'; // table name
public $id; // int(4) primary_key not_null
public $name; // varchar(32) unique_key not_null
public $description; // varchar(255)
public $description; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -25,7 +25,7 @@ class Foreign_service extends Managed_DataObject
'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'numeric key for service'),
'name' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'name of the service'),
'description' => array('type' => 'varchar', 'length' => 255, 'description' => 'description'),
'description' => array('type' => 'varchar', 'length' => 191, 'description' => 'description'),
'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'),
),

View File

@ -12,8 +12,8 @@ class Foreign_user extends Managed_DataObject
public $__table = 'foreign_user'; // table name
public $id; // bigint(8) primary_key not_null
public $service; // int(4) primary_key not_null
public $uri; // varchar(255) unique_key not_null
public $nickname; // varchar(255)
public $uri; // varchar(191) unique_key not_null not 255 because utf8mb4 takes more space
public $nickname; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -26,8 +26,8 @@ class Foreign_user extends Managed_DataObject
'fields' => array(
'id' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'unique numeric key on foreign service'),
'service' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to service'),
'uri' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'identifying URI'),
'nickname' => array('type' => 'varchar', 'length' => 255, 'description' => 'nickname on foreign service'),
'uri' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'identifying URI'),
'nickname' => array('type' => 'varchar', 'length' => 191, 'description' => 'nickname on foreign service'),
'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'),
),

View File

@ -12,7 +12,7 @@ class Group_member extends Managed_DataObject
public $group_id; // int(4) primary_key not_null
public $profile_id; // int(4) primary_key not_null
public $is_admin; // tinyint(1)
public $uri; // varchar(255)
public $uri; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -26,7 +26,7 @@ class Group_member extends Managed_DataObject
'group_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to user_group'),
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
'is_admin' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'is this user an admin?'),
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universal identifier'),
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'),
'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'),
),

View File

@ -12,7 +12,7 @@ class Invitation extends Managed_DataObject
public $__table = 'invitation'; // table name
public $code; // varchar(32) primary_key not_null
public $user_id; // int(4) not_null
public $address; // varchar(255) multiple_key not_null
public $address; // varchar(191) multiple_key not_null not 255 because utf8mb4 takes more space
public $address_type; // varchar(8) multiple_key not_null
public $registered_user_id; // int(4) not_null
public $created; // datetime() not_null
@ -34,7 +34,7 @@ class Invitation extends Managed_DataObject
'fields' => array(
'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'random code for an invitation'),
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'who sent the invitation'),
'address' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'invitation sent to'),
'address' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'invitation sent to'),
'address_type' => array('type' => 'varchar', 'length' => 8, 'not null' => true, 'description' => 'address type ("email", "xmpp", "sms")'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'registered_user_id' => array('type' => 'int', 'not null' => false, 'description' => 'if the invitation is converted, who the new user is'),

View File

@ -32,7 +32,7 @@ class Location_namespace extends Managed_DataObject
public $__table = 'location_namespace'; // table name
public $id; // int(4) primary_key not_null
public $description; // varchar(255)
public $description; // varchar(191)
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -44,7 +44,7 @@ class Location_namespace extends Managed_DataObject
return array(
'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'identity for this namespace'),
'description' => array('type' => 'varchar', 'length' => 255, 'description' => 'description of the namespace'),
'description' => array('type' => 'varchar', 'length' => 191, 'description' => 'description of the namespace'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),

View File

@ -299,6 +299,11 @@ abstract class Managed_DataObject extends Memcached_DataObject
return $ckeys;
}
public function escapedTableName()
{
return common_database_tablename($this->tableName());
}
/**
* Returns an ID, checked that it is set and reasonably valid
*
@ -391,4 +396,9 @@ abstract class Managed_DataObject extends Memcached_DataObject
// @FIXME return true only if something changed (otherwise 0)
return $result;
}
static public function beforeSchemaUpdate()
{
// NOOP
}
}

View File

@ -734,7 +734,7 @@ class Memcached_DataObject extends Safe_DataObject
return $string;
}
// We overload so that 'SET NAMES "utf8"' is called for
// We overload so that 'SET NAMES "utf8mb4"' is called for
// each connection
function _connect()
@ -784,9 +784,9 @@ class Memcached_DataObject extends Safe_DataObject
$conn = $DB->connection;
if (!empty($conn)) {
if ($DB instanceof DB_mysqli || $DB instanceof MDB2_Driver_mysqli) {
mysqli_set_charset($conn, 'utf8');
mysqli_set_charset($conn, 'utf8mb4');
} else if ($DB instanceof DB_mysql || $DB instanceof MDB2_Driver_mysql) {
mysql_set_charset('utf8', $conn);
mysql_set_charset('utf8mb4', $conn);
}
}
}

View File

@ -10,7 +10,7 @@ class Nonce extends Managed_DataObject
/* the code below is auto generated do not remove the above tag */
public $__table = 'nonce'; // 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 $tok; // char(32)
public $nonce; // char(32) primary_key not_null
public $ts; // datetime() primary_key not_null
@ -39,7 +39,7 @@ class Nonce extends Managed_DataObject
return array(
'description' => 'OAuth nonce record',
'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'),
'tok' => array('type' => 'char', 'length' => 32, 'description' => 'buggy old value, ignored'),
'nonce' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'nonce'),
'ts' => array('type' => 'datetime', 'not null' => true, 'description' => 'timestamp sent'),

View File

@ -55,10 +55,10 @@ class Notice extends Managed_DataObject
public $__table = 'notice'; // table name
public $id; // int(4) primary_key not_null
public $profile_id; // int(4) multiple_key not_null
public $uri; // varchar(255) unique_key
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $content; // text
public $rendered; // text
public $url; // varchar(255)
public $url; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime multiple_key not_null default_0000-00-00%2000%3A00%3A00
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
public $reply_to; // int(4)
@ -70,8 +70,8 @@ class Notice extends Managed_DataObject
public $location_id; // int(4)
public $location_ns; // int(4)
public $repeat_of; // int(4)
public $verb; // varchar(255)
public $object_type; // varchar(255)
public $verb; // varchar(191) not 255 because utf8mb4 takes more space
public $object_type; // varchar(191) not 255 because utf8mb4 takes more space
public $scope; // int(4)
/* the code above is auto generated do not remove the tag below */
@ -83,10 +83,10 @@ class Notice extends Managed_DataObject
'fields' => array(
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'who made the update'),
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier, usually a tag URI'),
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'),
'content' => array('type' => 'text', 'description' => 'update content', 'collate' => 'utf8_general_ci'),
'rendered' => array('type' => 'text', 'description' => 'HTML version of the content'),
'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL of any attachment (image, video, bookmark, whatever)'),
'url' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL of any attachment (image, video, bookmark, whatever)'),
'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'),
'reply_to' => array('type' => 'int', 'description' => 'notice replied to (usually a guess)'),
@ -98,8 +98,8 @@ class Notice extends Managed_DataObject
'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
'location_ns' => array('type' => 'int', 'description' => 'namespace for location'),
'repeat_of' => array('type' => 'int', 'description' => 'notice this is a repeat of'),
'object_type' => array('type' => 'varchar', 'length' => 255, 'description' => 'URI representing activity streams object type', 'default' => 'http://activitystrea.ms/schema/1.0/note'),
'verb' => array('type' => 'varchar', 'length' => 255, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'),
'object_type' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams object type', 'default' => 'http://activitystrea.ms/schema/1.0/note'),
'verb' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'),
'scope' => array('type' => 'int',
'description' => 'bit map for distribution scope; 0 = everywhere; 1 = this server only; 2 = addressees; 4 = followers; null = default'),
),
@ -313,6 +313,16 @@ class Notice extends Managed_DataObject
return $notice;
}
public static function getById($id)
{
$notice = new Notice();
$notice->id = $id;
if (!$notice->find(true)) {
throw new NoResultException($notice);
}
return $notice;
}
/**
* Extract #hashtags from this notice's content and save them to the database.
*/
@ -892,6 +902,12 @@ class Notice extends Managed_DataObject
$stored->insert(); // throws exception on error
$orig = clone($stored); // for updating later in this try clause
$object = null;
Event::handle('StoreActivityObject', array($act, $stored, $options, &$object));
if (empty($object)) {
throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->uri . ': '.$act->asString());
}
// If it's not part of a conversation, it's
// the beginning of a new conversation.
if (empty($stored->conversation)) {
@ -900,12 +916,6 @@ class Notice extends Managed_DataObject
$stored->conversation = $conv->id;
}
$object = null;
Event::handle('StoreActivityObject', array($act, $stored, $options, &$object));
if (empty($object)) {
throw new ServerException('No object from StoreActivityObject '.$stored->uri . ': '.$act->asString());
}
$stored->object_type = ActivityUtils::resolveUri($object->getObjectType(), true);
$stored->update($orig);
} catch (Exception $e) {
if (empty($stored->id)) {
@ -1067,13 +1077,9 @@ class Notice extends Managed_DataObject
}
$args = func_get_args();
$format = array_shift($args);
$keyPart = vsprintf($format, $args);
$cacheKey = Cache::key($keyPart);
$c->delete($cacheKey);
// delete the "last" stream, too, if this notice is
@ -1194,17 +1200,13 @@ class Notice extends Managed_DataObject
}
$f2ps = File_to_post::listGet('post_id', array($this->id));
$ids = array();
foreach ($f2ps[$this->id] as $f2p) {
$ids[] = $f2p->file_id;
}
$files = File::multiGet('id', $ids);
$this->_attachments[$this->id] = $files->fetchAll();
return $this->_attachments[$this->id];
}
@ -1766,15 +1768,12 @@ class Notice extends Managed_DataObject
$ids = array();
foreach ($gis[$this->id] as $gi)
{
foreach ($gis[$this->id] as $gi) {
$ids[] = $gi->group_id;
}
$groups = User_group::multiGet('id', $ids);
$this->_groups[$this->id] = $groups->fetchAll();
return $this->_groups[$this->id];
}
@ -1819,17 +1818,7 @@ class Notice extends Managed_DataObject
$act->verb = $this->verb;
if ($this->repeat_of) {
$repeated = Notice::getKV('id', $this->repeat_of);
if ($repeated instanceof Notice) {
// TRANS: A repeat activity's title. %1$s is repeater's nickname
// and %2$s is the repeated user's nickname.
$act->title = sprintf(_('%1$s repeated a notice by %2$s'),
$this->getProfile()->getNickname(),
$repeated->getProfile()->getNickname());
$act->objects[] = $repeated->asActivity($scoped);
}
} else {
if (!$this->repeat_of) {
$act->objects[] = $this->asActivityObject();
}
@ -2687,85 +2676,65 @@ class Notice extends Managed_DataObject
$scope = self::defaultScope();
}
// If there's no scope, anyone (even anon) is in scope.
if ($scope == 0) { // Not private
return !$this->isHiddenSpam($profile);
} else { // Private, somehow
// If there's scope, anon cannot be in scope
if (empty($profile)) {
return false;
}
// Author is always in scope
if ($this->profile_id == $profile->id) {
return true;
}
// Only for users on this site
if (($scope & Notice::SITE_SCOPE) && !$profile->isLocal()) {
return false;
}
// Only for users mentioned in the notice
if ($scope & Notice::ADDRESSEE_SCOPE) {
$reply = Reply::pkeyGet(array('notice_id' => $this->id,
'profile_id' => $profile->id));
if (!$reply instanceof Reply) {
return false;
}
}
// Only for members of the given group
if ($scope & Notice::GROUP_SCOPE) {
// XXX: just query for the single membership
$groups = $this->getGroups();
$foundOne = false;
foreach ($groups as $group) {
if ($profile->isMember($group)) {
$foundOne = true;
break;
}
}
if (!$foundOne) {
return false;
}
}
// Only for followers of the author
$author = null;
if ($scope & Notice::FOLLOWER_SCOPE) {
try {
$author = $this->getProfile();
} catch (Exception $e) {
return false;
}
if (!Subscription::exists($profile, $author)) {
return false;
}
}
if ($scope == 0 && !$this->getProfile()->isPrivateStream()) { // Not scoping, so it is public.
return !$this->isHiddenSpam($profile);
}
// If there's scope, anon cannot be in scope
if (empty($profile)) {
return false;
}
// Author is always in scope
if ($this->profile_id == $profile->id) {
return true;
}
// Only for users on this site
if (($scope & Notice::SITE_SCOPE) && !$profile->isLocal()) {
return false;
}
// Only for users mentioned in the notice
if ($scope & Notice::ADDRESSEE_SCOPE) {
$reply = Reply::pkeyGet(array('notice_id' => $this->id,
'profile_id' => $profile->id));
if (!$reply instanceof Reply) {
return false;
}
}
// Only for members of the given group
if ($scope & Notice::GROUP_SCOPE) {
// XXX: just query for the single membership
$groups = $this->getGroups();
$foundOne = false;
foreach ($groups as $group) {
if ($profile->isMember($group)) {
$foundOne = true;
break;
}
}
if (!$foundOne) {
return false;
}
}
if ($scope & Notice::FOLLOWER_SCOPE || $this->getProfile()->isPrivateStream()) {
if (!Subscription::exists($profile, $this->getProfile())) {
return false;
}
}
return !$this->isHiddenSpam($profile);
}
function isHiddenSpam($profile) {
@ -2836,7 +2805,6 @@ class Notice extends Managed_DataObject
static function fillProfiles($notices)
{
$map = self::getProfiles($notices);
foreach ($notices as $entry=>$notice) {
try {
if (array_key_exists($notice->profile_id, $map)) {
@ -2857,22 +2825,17 @@ class Notice extends Managed_DataObject
foreach ($notices as $notice) {
$ids[] = $notice->profile_id;
}
$ids = array_unique($ids);
return Profile::pivotGet('id', $ids);
}
static function fillGroups(&$notices)
{
$ids = self::_idsOf($notices);
$gis = Group_inbox::listGet('notice_id', $ids);
$gids = array();
foreach ($gis as $id => $gi)
{
foreach ($gis as $id => $gi) {
foreach ($gi as $g)
{
$gids[] = $g->group_id;
@ -2880,9 +2843,7 @@ class Notice extends Managed_DataObject
}
$gids = array_unique($gids);
$group = User_group::pivotGet('id', $gids);
foreach ($notices as $notice)
{
$grps = array();
@ -2906,11 +2867,8 @@ class Notice extends Managed_DataObject
static function fillAttachments(&$notices)
{
$ids = self::_idsOf($notices);
$f2pMap = File_to_post::listGet('post_id', $ids);
$fileIds = array();
foreach ($f2pMap as $noticeId => $f2ps) {
foreach ($f2ps as $f2p) {
$fileIds[] = $f2p->file_id;
@ -2918,9 +2876,7 @@ class Notice extends Managed_DataObject
}
$fileIds = array_unique($fileIds);
$fileMap = File::pivotGet('id', $fileIds);
foreach ($notices as $notice)
{
$files = array();
@ -2945,31 +2901,4 @@ class Notice extends Managed_DataObject
$notice->_setReplies($ids);
}
}
protected $_repeats = array();
function getRepeats()
{
if (isset($this->_repeats[$this->id])) {
return $this->_repeats[$this->id];
}
$repeatMap = Notice::listGet('repeat_of', array($this->id));
$this->_repeats[$this->id] = $repeatMap[$this->id];
return $this->_repeats[$this->id];
}
function _setRepeats($repeats)
{
$this->_repeats[$this->id] = $repeats;
}
static function fillRepeats(&$notices)
{
$ids = self::_idsOf($notices);
$repeatMap = Notice::listGet('repeat_of', $ids);
foreach ($notices as $notice) {
$repeats = $repeatMap[$notice->id];
$notice->_setRepeats($repeats);
}
}
}

View File

@ -11,8 +11,8 @@ class Notice_source extends Managed_DataObject
public $__table = 'notice_source'; // table name
public $code; // varchar(32) primary_key not_null
public $name; // varchar(255) not_null
public $url; // varchar(255) not_null
public $name; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $url; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -24,8 +24,8 @@ class Notice_source extends Managed_DataObject
return array(
'fields' => array(
'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'source code'),
'name' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'name of the source'),
'url' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'url to link to'),
'name' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'name of the source'),
'url' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'url to link to'),
'notice_id' => array('type' => 'int', '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'),

View File

@ -12,14 +12,14 @@ class Oauth_application extends Managed_DataObject
public $__table = 'oauth_application'; // table name
public $id; // int(4) primary_key not_null
public $owner; // int(4) not_null
public $consumer_key; // varchar(255) not_null
public $name; // varchar(255) not_null
public $description; // varchar(255)
public $icon; // varchar(255) not_null
public $source_url; // varchar(255)
public $organization; // varchar(255)
public $homepage; // varchar(255)
public $callback_url; // varchar(255) not_null
public $consumer_key; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $name; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $description; // varchar(191) not 255 because utf8mb4 takes more space
public $icon; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $source_url; // varchar(191) not 255 because utf8mb4 takes more space
public $organization; // varchar(191) not 255 because utf8mb4 takes more space
public $homepage; // varchar(191) not 255 because utf8mb4 takes more space
public $callback_url; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $type; // tinyint(1)
public $access_type; // tinyint(1)
public $created; // datetime not_null
@ -43,12 +43,12 @@ class Oauth_application extends Managed_DataObject
static function maxDesc()
{
// This used to default to textlimit or allow unlimited descriptions,
// but this isn't part of a notice and the field's limited to 255 chars
// in the DB, so those seem silly.
// but this isn't part of a notice and the field's limited to 191 chars
// in the DB, so those seem silly. (utf8mb4 takes up more space, so can't use 255)
//
// Now just defaulting to 255 max unless a smaller application desclimit
// Now just defaulting to 191 max unless a smaller application desclimit
// is actually set. Setting to 0 will use the maximum.
$max = 255;
$max = 191;
$desclimit = intval(common_config('application', 'desclimit'));
if ($desclimit > 0 && $desclimit < $max) {
return $desclimit;
@ -80,7 +80,7 @@ class Oauth_application extends Managed_DataObject
function setOriginal($filename)
{
$imagefile = new ImageFile($this->id, Avatar::path($filename));
$imagefile = new ImageFile(null, Avatar::path($filename));
// XXX: Do we want to have a bunch of different size icons? homepage, stream, mini?
// or just one and control size via CSS? --Zach
@ -163,14 +163,14 @@ class Oauth_application extends Managed_DataObject
'fields' => array(
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
'owner' => array('type' => 'int', 'not null' => true, 'description' => 'owner of the application'),
'consumer_key' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'application consumer key'),
'name' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'name of the application'),
'description' => array('type' => 'varchar', 'length' => 255, 'description' => 'description of the application'),
'icon' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'application icon'),
'source_url' => array('type' => 'varchar', 'length' => 255, 'description' => 'application homepage - used for source link'),
'organization' => array('type' => 'varchar', 'length' => 255, 'description' => 'name of the organization running the application'),
'homepage' => array('type' => 'varchar', 'length' => 255, 'description' => 'homepage for the organization'),
'callback_url' => array('type' => 'varchar', 'length' => 255, 'description' => 'url to redirect to after authentication'),
'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'application consumer key'),
'name' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'name of the application'),
'description' => array('type' => 'varchar', 'length' => 191, 'description' => 'description of the application'),
'icon' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'application icon'),
'source_url' => array('type' => 'varchar', 'length' => 191, 'description' => 'application homepage - used for source link'),
'organization' => array('type' => 'varchar', 'length' => 191, 'description' => 'name of the organization running the application'),
'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'homepage for the organization'),
'callback_url' => array('type' => 'varchar', 'length' => 191, 'description' => 'url to redirect to after authentication'),
'type' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'type of app, 1 = browser, 2 = desktop'),
'access_type' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'default access type, bit 1 = read, bit 2 = write'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),

View File

@ -13,7 +13,7 @@ class Oauth_application_user extends Managed_DataObject
public $profile_id; // int(4) primary_key not_null
public $application_id; // int(4) primary_key not_null
public $access_type; // tinyint(1)
public $token; // varchar(255)
public $token; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@ -27,7 +27,7 @@ class Oauth_application_user extends Managed_DataObject
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'user of the application'),
'application_id' => array('type' => 'int', 'not null' => true, 'description' => 'id of the application'),
'access_type' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'access type, bit 1 = read, bit 2 = write'),
'token' => array('type' => 'varchar', 'length' => 255, 'description' => 'request or access token'),
'token' => array('type' => 'varchar', 'length' => 191, 'description' => 'request or access token'),
'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'),
),

View File

@ -12,7 +12,7 @@ class Oauth_token_association extends Managed_DataObject
public $__table = 'oauth_token_association'; // table name
public $profile_id; // int(4) primary_key not_null
public $application_id; // int(4) primary_key not_null
public $token; // varchar(255) primary key not null
public $token; // varchar(191) primary key not null not 255 because utf8mb4 takes more space
public $created; // datetime not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@ -43,7 +43,7 @@ class Oauth_token_association extends Managed_DataObject
'fields' => array(
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'associated user'),
'application_id' => array('type' => 'int', 'not null' => true, 'description' => 'the application'),
'token' => array('type' => 'varchar', 'length' => '255', 'not null' => true, 'description' => 'token used for this association'),
'token' => array('type' => 'varchar', 'length' => '191', 'not null' => true, 'description' => 'token used for this association'),
'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'),
),

View File

@ -30,11 +30,11 @@ class Profile extends Managed_DataObject
public $__table = 'profile'; // table name
public $id; // int(4) primary_key not_null
public $nickname; // varchar(64) multiple_key not_null
public $fullname; // varchar(255) multiple_key
public $profileurl; // varchar(255)
public $homepage; // varchar(255) multiple_key
public $fullname; // varchar(191) multiple_key not 255 because utf8mb4 takes more space
public $profileurl; // varchar(191) not 255 because utf8mb4 takes more space
public $homepage; // varchar(191) multiple_key not 255 because utf8mb4 takes more space
public $bio; // text() multiple_key
public $location; // varchar(255) multiple_key
public $location; // varchar(191) multiple_key not 255 because utf8mb4 takes more space
public $lat; // decimal(10,7)
public $lon; // decimal(10,7)
public $location_id; // int(4)
@ -49,11 +49,11 @@ class Profile extends Managed_DataObject
'fields' => array(
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
'nickname' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username', 'collate' => 'utf8_general_ci'),
'fullname' => array('type' => 'varchar', 'length' => 255, 'description' => 'display name', 'collate' => 'utf8_general_ci'),
'profileurl' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL, cached so we dont regenerate'),
'homepage' => array('type' => 'varchar', 'length' => 255, 'description' => 'identifying URL', 'collate' => 'utf8_general_ci'),
'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name', 'collate' => 'utf8_general_ci'),
'profileurl' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL, cached so we dont regenerate'),
'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'identifying URL', 'collate' => 'utf8_general_ci'),
'bio' => array('type' => 'text', 'description' => 'descriptive biography', 'collate' => 'utf8_general_ci'),
'location' => array('type' => 'varchar', 'length' => 255, 'description' => 'physical location', 'collate' => 'utf8_general_ci'),
'location' => array('type' => 'varchar', 'length' => 191, 'description' => 'physical location', 'collate' => 'utf8_general_ci'),
'lat' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'),
'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'),
'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
@ -160,7 +160,7 @@ class Profile extends Managed_DataObject
return $this->getGroup()->setOriginal($filename);
}
$imagefile = new ImageFile($this->id, Avatar::path($filename));
$imagefile = new ImageFile(null, Avatar::path($filename));
$avatar = new Avatar();
$avatar->profile_id = $this->id;
@ -1451,6 +1451,12 @@ class Profile extends Managed_DataObject
return $feed;
}
public function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
{
// TRANS: Exception thrown when trying view "repeated to me".
throw new Exception(_('Not implemented since inbox change.'));
}
/*
* Get a Profile object by URI. Will call external plugins for help
* using the event StartGetProfileFromURI.
@ -1566,6 +1572,15 @@ class Profile extends Managed_DataObject
return $this->getUser()->shortenLinks($text, $always);
}
public function isPrivateStream()
{
// We only know of public remote users as of yet...
if (!$this->isLocal()) {
return false;
}
return $this->getUser()->private_stream ? true : false;
}
public function delPref($namespace, $topic) {
return Profile_prefs::setData($this, $namespace, $topic, null);
}

View File

@ -43,8 +43,8 @@ class Profile_list extends Managed_DataObject
public $private; // tinyint(1)
public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
public $uri; // varchar(255) unique_key
public $mainpage; // varchar(255)
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $mainpage; // varchar(191) not 255 because utf8mb4 takes more space
public $tagged_count; // smallint
public $subscriber_count; // smallint
@ -64,8 +64,8 @@ class Profile_list extends Managed_DataObject
'created' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date the tag was added'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date the tag was modified'),
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universal identifier'),
'mainpage' => array('type' => 'varchar', 'length' => 255, 'description' => 'page to link to'),
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'),
'mainpage' => array('type' => 'varchar', 'length' => 191, 'description' => 'page to link to'),
'tagged_count' => array('type' => 'int', 'default' => 0, 'description' => 'number of people tagged with this tag by this user'),
'subscriber_count' => array('type' => 'int', 'default' => 0, 'description' => 'number of subscribers to this tag'),
),

View File

@ -31,8 +31,8 @@ class Profile_prefs extends Managed_DataObject
{
public $__table = 'profile_prefs'; // table name
public $profile_id; // int(4) primary_key not_null
public $namespace; // varchar(255) not_null
public $topic; // varchar(255) not_null
public $namespace; // varchar(191) not_null
public $topic; // varchar(191) not_null
public $data; // text
public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@ -42,8 +42,8 @@ class Profile_prefs extends Managed_DataObject
return array(
'fields' => array(
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
'namespace' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'namespace, like pluginname or category'),
'topic' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'preference key, i.e. description, age...'),
'namespace' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'namespace, like pluginname or category'),
'topic' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'preference key, i.e. description, age...'),
'data' => array('type' => 'blob', 'description' => 'topic data, may be anything'),
'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'),

View File

@ -12,7 +12,7 @@ class Sms_carrier extends Managed_DataObject
public $__table = 'sms_carrier'; // table name
public $id; // int(4) primary_key not_null
public $name; // varchar(64) unique_key
public $email_pattern; // varchar(255) not_null
public $email_pattern; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -30,7 +30,7 @@ class Sms_carrier extends Managed_DataObject
'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'primary key for SMS carrier'),
'name' => array('type' => 'varchar', 'length' => 64, 'description' => 'name of the carrier'),
'email_pattern' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'sprintf pattern for making an email address from a phone number'),
'email_pattern' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'sprintf pattern for making an email address from a phone number'),
'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'),
),

View File

@ -29,15 +29,15 @@ class Status_network extends Safe_DataObject
public $__table = 'status_network'; // table name
public $site_id; // int(4) primary_key not_null
public $nickname; // varchar(64) unique_key not_null
public $hostname; // varchar(255) unique_key
public $pathname; // varchar(255) unique_key
public $dbhost; // varchar(255)
public $dbuser; // varchar(255)
public $dbpass; // varchar(255)
public $dbname; // varchar(255)
public $sitename; // varchar(255)
public $theme; // varchar(255)
public $logo; // varchar(255)
public $hostname; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $pathname; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $dbhost; // varchar(191) not 255 because utf8mb4 takes more space
public $dbuser; // varchar(191) not 255 because utf8mb4 takes more space
public $dbpass; // varchar(191) not 255 because utf8mb4 takes more space
public $dbname; // varchar(191) not 255 because utf8mb4 takes more space
public $sitename; // varchar(191) not 255 because utf8mb4 takes more space
public $theme; // varchar(191) not 255 because utf8mb4 takes more space
public $logo; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP

View File

@ -32,9 +32,9 @@ class Subscription extends Managed_DataObject
public $subscribed; // int(4) primary_key not_null
public $jabber; // tinyint(1) default_1
public $sms; // tinyint(1) default_1
public $token; // varchar(255)
public $secret; // varchar(255)
public $uri; // varchar(255)
public $token; // varchar(191) not 255 because utf8mb4 takes more space
public $secret; // varchar(191) not 255 because utf8mb4 takes more space
public $uri; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -46,9 +46,9 @@ class Subscription extends Managed_DataObject
'subscribed' => array('type' => 'int', 'not null' => true, 'description' => 'profile being listened to'),
'jabber' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'deliver jabber messages'),
'sms' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'deliver sms messages'),
'token' => array('type' => 'varchar', 'length' => 255, 'description' => 'authorization token'),
'secret' => array('type' => 'varchar', 'length' => 255, 'description' => 'token secret'),
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier'),
'token' => array('type' => 'varchar', 'length' => 191, 'description' => 'authorization token'),
'secret' => array('type' => 'varchar', 'length' => 191, 'description' => 'token secret'),
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier'),
'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'),
),
@ -94,8 +94,12 @@ class Subscription extends Managed_DataObject
if (Event::handle('StartSubscribe', array($subscriber, $other))) {
$otherUser = User::getKV('id', $other->id);
if ($otherUser instanceof User && $otherUser->subscribe_policy == User::SUBSCRIBE_POLICY_MODERATE && !$force) {
$sub = Subscription_queue::saveNew($subscriber, $other);
$sub->notify();
try {
$sub = Subscription_queue::saveNew($subscriber, $other);
$sub->notify();
} catch (AlreadyFulfilledException $e) {
$sub = Subscription_queue::getSubQueue($subscriber, $other);
}
} else {
$sub = self::saveNew($subscriber->id, $other->id);
$sub->notify();
@ -124,7 +128,7 @@ class Subscription extends Managed_DataObject
}
}
if ($sub instanceof Subscription) { // i.e. not SubscriptionQueue
if ($sub instanceof Subscription) { // i.e. not Subscription_queue
Event::handle('EndSubscribe', array($subscriber, $other));
}
}
@ -132,6 +136,16 @@ class Subscription extends Managed_DataObject
return $sub;
}
static function ensureStart(Profile $subscriber, Profile $other, $force=false)
{
try {
$sub = self::start($subscriber, $other, $force);
} catch (AlreadyFulfilledException $e) {
return self::getSubscription($subscriber, $other);
}
return $sub;
}
/**
* Low-level subscription save.
* Outside callers should use Subscription::start()
@ -232,9 +246,25 @@ class Subscription extends Managed_DataObject
static function exists(Profile $subscriber, Profile $other)
{
$sub = Subscription::pkeyGet(array('subscriber' => $subscriber->id,
'subscribed' => $other->id));
return ($sub instanceof Subscription);
try {
$sub = self::getSubscription($subscriber, $other);
} catch (NoResultException $e) {
return false;
}
return true;
}
static function getSubscription(Profile $subscriber, Profile $other)
{
// This is essentially a pkeyGet but we have an object to return in NoResultException
$sub = new Subscription();
$sub->subscriber = $subscriber->id;
$sub->subscribed = $other->id;
if (!$sub->find(true)) {
throw new NoResultException($sub);
}
return $sub;
}
function asActivity()

View File

@ -36,6 +36,9 @@ class Subscription_queue extends Managed_DataObject
public static function saveNew(Profile $subscriber, Profile $subscribed)
{
if (self::exists($subscriber, $subscribed)) {
throw new AlreadyFulfilledException(_('This subscription request is already in progress.'));
}
$rq = new Subscription_queue();
$rq->subscriber = $subscriber->id;
$rq->subscribed = $subscribed->id;
@ -51,6 +54,18 @@ class Subscription_queue extends Managed_DataObject
return ($sub instanceof Subscription_queue);
}
static function getSubQueue(Profile $subscriber, Profile $other)
{
// This is essentially a pkeyGet but we have an object to return in NoResultException
$sub = new Subscription_queue();
$sub->subscriber = $subscriber->id;
$sub->subscribed = $other->id;
if (!$sub->find(true)) {
throw new NoResultException($sub);
}
return $sub;
}
/**
* Complete a pending subscription, as we've got approval of some sort.
*

View File

@ -10,13 +10,13 @@ class Token extends Managed_DataObject
/* the code below is auto generated do not remove the above tag */
public $__table = 'token'; // 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 $tok; // char(32) primary_key not_null
public $secret; // char(32) not_null
public $type; // tinyint(1) not_null
public $state; // tinyint(1)
public $verifier; // varchar(255)
public $verified_callback; // varchar(255)
public $verifier; // varchar(191) not 255 because utf8mb4 takes more space
public $verified_callback; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -27,13 +27,13 @@ class Token extends Managed_DataObject
return array(
'description' => 'OAuth token record',
'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'),
'tok' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'identifying value'),
'secret' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'secret value'),
'type' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'request or access'),
'state' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'for requests, 0 = initial, 1 = authorized, 2 = used'),
'verifier' => array('type' => 'varchar', 'length' => 255, 'description' => 'verifier string for OAuth 1.0a'),
'verified_callback' => array('type' => 'varchar', 'length' => 255, 'description' => 'verified callback URL for OAuth 1.0a'),
'verifier' => array('type' => 'varchar', 'length' => 191, 'description' => 'verifier string for OAuth 1.0a'),
'verified_callback' => array('type' => 'varchar', 'length' => 191, 'description' => 'verified callback URL for OAuth 1.0a'),
'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'),

View File

@ -34,9 +34,9 @@ class User extends Managed_DataObject
public $__table = 'user'; // table name
public $id; // int(4) primary_key not_null
public $nickname; // varchar(64) unique_key
public $password; // varchar(255)
public $email; // varchar(255) unique_key
public $incomingemail; // varchar(255) unique_key
public $password; // varchar(191) not 255 because utf8mb4 takes more space
public $email; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $incomingemail; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $emailnotifysub; // tinyint(1) default_1
public $emailnotifyfav; // tinyint(1) default_1
public $emailnotifynudge; // tinyint(1) default_1
@ -50,8 +50,8 @@ class User extends Managed_DataObject
public $carrier; // int(4)
public $smsnotify; // tinyint(1)
public $smsreplies; // tinyint(1)
public $smsemail; // varchar(255)
public $uri; // varchar(255) unique_key
public $smsemail; // varchar(191) not 255 because utf8mb4 takes more space
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $autosubscribe; // tinyint(1)
public $subscribe_policy; // tinyint(1)
public $urlshorteningservice; // varchar(50) default_ur1.ca
@ -69,9 +69,9 @@ class User extends Managed_DataObject
'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
'nickname' => array('type' => 'varchar', 'length' => 64, 'description' => 'nickname or username, duped in profile'),
'password' => array('type' => 'varchar', 'length' => 255, 'description' => 'salted password, can be null for OpenID users'),
'email' => array('type' => 'varchar', 'length' => 255, 'description' => 'email address for password recovery etc.'),
'incomingemail' => array('type' => 'varchar', 'length' => 255, 'description' => 'email address for post-by-email'),
'password' => array('type' => 'varchar', 'length' => 191, 'description' => 'salted password, can be null for OpenID users'),
'email' => array('type' => 'varchar', 'length' => 191, 'description' => 'email address for password recovery etc.'),
'incomingemail' => array('type' => 'varchar', 'length' => 191, 'description' => 'email address for post-by-email'),
'emailnotifysub' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of subscriptions'),
'emailnotifyfav' => array('type' => 'int', 'size' => 'tiny', 'default' => null, 'description' => 'Notify by email of favorites'),
'emailnotifynudge' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of nudges'),
@ -85,8 +85,8 @@ class User extends Managed_DataObject
'carrier' => array('type' => 'int', 'description' => 'foreign key to sms_carrier'),
'smsnotify' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'whether to send notices to SMS'),
'smsreplies' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'whether to send notices to SMS on replies'),
'smsemail' => array('type' => 'varchar', 'length' => 255, 'description' => 'built from sms and carrier'),
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier, usually a tag URI'),
'smsemail' => array('type' => 'varchar', 'length' => 191, 'description' => 'built from sms and carrier'),
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'),
'autosubscribe' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'automatically subscribe to users who subscribe to us'),
'subscribe_policy' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => '0 = anybody can subscribe; 1 = require approval'),
'urlshorteningservice' => array('type' => 'varchar', 'length' => 50, 'default' => 'internal', 'description' => 'service to use for auto-shortening URLs'),
@ -191,7 +191,8 @@ class User extends Managed_DataObject
* string 'password' (may be missing for eg OpenID registrations)
* string 'code' invite code
* ?string 'uri' permalink to notice; defaults to local notice URL
* @return mixed User object or false on failure
* @return User object
* @throws Exception on failure
*/
static function register(array $fields) {
@ -205,12 +206,8 @@ class User extends Managed_DataObject
$email = common_canonical_email($email);
}
try {
$profile->nickname = Nickname::normalize($nickname, true);
} catch (NicknameException $e) {
common_log(LOG_WARNING, sprintf('Bad nickname during User registration for %s: %s', $nickname, $e->getMessage()), __FILE__);
return false;
}
// Normalize _and_ check whether it is in use. Throw NicknameException on failure.
$profile->nickname = Nickname::normalize($nickname, true);
$profile->profileurl = common_profile_url($profile->nickname);
@ -277,7 +274,9 @@ class User extends Managed_DataObject
$id = $profile->insert();
if ($id === false) {
common_log_db_error($profile, 'INSERT', __FILE__);
return false;
$profile->query('ROLLBACK');
// TRANS: Profile data could not be inserted for some reason.
throw new ServerException(_m('Could not insert profile data for new user.'));
}
$user->id = $id;
@ -297,7 +296,8 @@ class User extends Managed_DataObject
if ($result === false) {
common_log_db_error($user, 'INSERT', __FILE__);
$profile->query('ROLLBACK');
return false;
// TRANS: User data could not be inserted for some reason.
throw new ServerException(_m('Could not insert user data for new user.'));
}
// Everyone is subscribed to themself
@ -312,7 +312,8 @@ class User extends Managed_DataObject
if (!$result) {
common_log_db_error($subscription, 'INSERT', __FILE__);
$profile->query('ROLLBACK');
return false;
// TRANS: Subscription data could not be inserted for some reason.
throw new ServerException(_m('Could not insert subscription data for new user.'));
}
// Mark that this invite was converted
@ -334,7 +335,8 @@ class User extends Managed_DataObject
if (!$result) {
common_log_db_error($confirm, 'INSERT', __FILE__);
$profile->query('ROLLBACK');
return false;
// TRANS: Email confirmation data could not be inserted for some reason.
throw new ServerException(_m('Could not insert email confirmation data for new user.'));
}
}
@ -352,7 +354,7 @@ class User extends Managed_DataObject
common_log(LOG_WARNING, sprintf("Default user %s does not exist.", $defnick),
__FILE__);
} else {
Subscription::start($profile, $defuser->getProfile());
Subscription::ensureStart($profile, $defuser->getProfile());
}
}
@ -385,6 +387,10 @@ class User extends Managed_DataObject
Event::handle('EndUserRegister', array($profile));
}
if (!$user instanceof User) {
throw new ServerException('User could not be registered. Probably an event hook that failed.');
}
return $user;
}
@ -687,11 +693,9 @@ class User extends Managed_DataObject
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
public function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
{
// TRANS: Exception thrown when trying view "repeated to me".
throw new Exception(_('Not implemented since inbox change.'));
return $this->getProfile()->repeatedToMe($offset, $limit, $since_id, $max_id);
}
public static function siteOwner()
@ -994,6 +998,11 @@ class User extends Managed_DataObject
return $act;
}
public function isPrivateStream()
{
return $this->getProfile()->isPrivateStream();
}
public function delPref($namespace, $topic)
{
return $this->getProfile()->delPref($namespace, $topic);

View File

@ -15,18 +15,18 @@ class User_group extends Managed_DataObject
public $__table = 'user_group'; // table name
public $id; // int(4) primary_key not_null
public $nickname; // varchar(64)
public $fullname; // varchar(255)
public $homepage; // varchar(255)
public $fullname; // varchar(191) not 255 because utf8mb4 takes more space
public $homepage; // varchar(191) not 255 because utf8mb4 takes more space
public $description; // text
public $location; // varchar(255)
public $original_logo; // varchar(255)
public $homepage_logo; // varchar(255)
public $stream_logo; // varchar(255)
public $mini_logo; // varchar(255)
public $location; // varchar(191) not 255 because utf8mb4 takes more space
public $original_logo; // varchar(191) not 255 because utf8mb4 takes more space
public $homepage_logo; // varchar(191) not 255 because utf8mb4 takes more space
public $stream_logo; // varchar(191) not 255 because utf8mb4 takes more space
public $mini_logo; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
public $uri; // varchar(255) unique_key
public $mainpage; // varchar(255)
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $mainpage; // varchar(191) not 255 because utf8mb4 takes more space
public $join_policy; // tinyint
public $force_scope; // tinyint
@ -41,21 +41,21 @@ class User_group extends Managed_DataObject
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
'nickname' => array('type' => 'varchar', 'length' => 64, 'description' => 'nickname for addressing'),
'fullname' => array('type' => 'varchar', 'length' => 255, 'description' => 'display name'),
'homepage' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL, cached so we dont regenerate'),
'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name'),
'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL, cached so we dont regenerate'),
'description' => array('type' => 'text', 'description' => 'group description'),
'location' => array('type' => 'varchar', 'length' => 255, 'description' => 'related physical location, if any'),
'location' => array('type' => 'varchar', 'length' => 191, 'description' => 'related physical location, if any'),
'original_logo' => array('type' => 'varchar', 'length' => 255, 'description' => 'original size logo'),
'homepage_logo' => array('type' => 'varchar', 'length' => 255, 'description' => 'homepage (profile) size logo'),
'stream_logo' => array('type' => 'varchar', 'length' => 255, 'description' => 'stream-sized logo'),
'mini_logo' => array('type' => 'varchar', 'length' => 255, 'description' => 'mini logo'),
'original_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'original size logo'),
'homepage_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'homepage (profile) size logo'),
'stream_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'stream-sized logo'),
'mini_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'mini logo'),
'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'),
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universal identifier'),
'mainpage' => array('type' => 'varchar', 'length' => 255, 'description' => 'page for group info to link to'),
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'),
'mainpage' => array('type' => 'varchar', 'length' => 191, 'description' => 'page for group info to link to'),
'join_policy' => array('type' => 'int', 'size' => 'tiny', 'description' => '0=open; 1=requires admin approval'),
'force_scope' => array('type' => 'int', 'size' => 'tiny', 'description' => '0=never,1=sometimes,-1=always'),
),
@ -312,13 +312,21 @@ class User_group extends Managed_DataObject
function setOriginal($filename)
{
$imagefile = new ImageFile($this->id, Avatar::path($filename));
// This should be handled by the Profile->setOriginal function so user and group avatars are handled the same
$imagefile = new ImageFile(null, Avatar::path($filename));
$sizes = array('homepage_logo' => AVATAR_PROFILE_SIZE,
'stream_logo' => AVATAR_STREAM_SIZE,
'mini_logo' => AVATAR_MINI_SIZE);
$orig = clone($this);
$this->original_logo = Avatar::url($filename);
$this->homepage_logo = Avatar::url($imagefile->resize(AVATAR_PROFILE_SIZE));
$this->stream_logo = Avatar::url($imagefile->resize(AVATAR_STREAM_SIZE));
$this->mini_logo = Avatar::url($imagefile->resize(AVATAR_MINI_SIZE));
foreach ($sizes as $name=>$size) {
$filename = Avatar::filename($this->profile_id, image_type_to_extension($imagefile->preferredType()),
$size, common_timestamp());
$imagefile->resizeTo(Avatar::path($filename), array('width'=>$size, 'height'=>$size));
$this->$name = Avatar::url($filename);
}
common_debug(common_log_objstring($this));
return $this->update($orig);
}

View File

@ -36,8 +36,8 @@ class User_im_prefs extends Managed_DataObject
public $__table = 'user_im_prefs'; // table name
public $user_id; // int(4) primary_key not_null
public $screenname; // varchar(255) not_null
public $transport; // varchar(255) not_null
public $screenname; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $transport; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $notify; // tinyint(1)
public $replies; // tinyint(1)
public $microid; // tinyint(1)
@ -53,8 +53,8 @@ class User_im_prefs extends Managed_DataObject
return array(
'fields' => array(
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
'screenname' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'screenname on this service'),
'transport' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'transport (ex xmpp, aim)'),
'screenname' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'screenname on this service'),
'transport' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'transport (ex xmpp, aim)'),
'notify' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Notify when a new notice is sent'),
'replies' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Send replies from people not subscribed to'),
'microid' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'Publish a MicroID'),

View File

@ -11,8 +11,8 @@ class User_username extends Managed_DataObject
public $__table = 'user_username'; // table name
public $user_id; // int(4) not_null
public $provider_name; // varchar(255) primary_key not_null
public $username; // varchar(255) primary_key not_null
public $provider_name; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
public $username; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -23,8 +23,8 @@ class User_username extends Managed_DataObject
{
return array(
'fields' => array(
'provider_name' => array('type' => 'varchar', 'length' => 255, 'description' => 'provider name'),
'username' => array('type' => 'varchar', 'length' => 255, 'description' => 'username'),
'provider_name' => array('type' => 'varchar', 'length' => 191, 'description' => 'provider name'),
'username' => array('type' => 'varchar', 'length' => 191, 'description' => 'username'),
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice id this title relates to'),
'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'),

View File

@ -21,7 +21,7 @@ create table status_network (
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified'
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
create table status_network_tag (
site_id integer comment 'unique id',
@ -30,5 +30,5 @@ create table status_network_tag (
constraint primary key (site_id, tag),
index status_network_tag_tag_idx (tag)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

View File

@ -1,11 +1,31 @@
### GNU social "fancy URL" setup
#
# Change the "RewriteBase" in the new .htaccess file to be the URL path
# to your GNU Social installation on your server. Typically this will
# be the path to your GNU Social directory relative to your Web root.
# If you are installing it in the root directory, leave it as '/'.
#
# If it doesn't work, double-check that AllowOverride for the GNU Social
# directory is 'All' in your Apache configuration file. This can be
# * /etc/apache2/apache2.conf (generic)
# * /etc/apache2/sites-available/default(on Debian and Ubuntu)
# * ...many other variations depending on distribution...
#
# See the Apache documentation for .htaccess files for more details:
# https://httpd.apache.org/docs/2.4/howto/htaccess.html
#
# Also, check that mod_rewrite is installed and enabled:
# https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html
<IfModule mod_rewrite.c>
RewriteEngine On
# NOTE: change this to your actual StatusNet base URL path,
# NOTE: change this to your actual GNU social base URL path,
# minus the domain part:
#
# http://example.com/ => /
# http://example.com/mublog/ => /mublog/
# https://social.example.com/ => /
# https://example.com/social/ => /social/
#
RewriteBase /
#RewriteBase /mublog/
@ -26,7 +46,7 @@
<FilesMatch "\.(ini)">
# For mod_access_compat in Apache <2.4
Order allow,deny
#Order allow,deny
# Use this instead for Apache >2.4 (mod_authz_host)
# Require all denied

View File

@ -265,7 +265,7 @@ function main()
$site_ssl = common_config('site', 'ssl');
// If the request is HTTP and it should be HTTPS...
if ($site_ssl != 'never' && !StatusNet::isHTTPS() && common_is_sensitive($args['action'])) {
if ($site_ssl != 'never' && !GNUsocial::isHTTPS() && common_is_sensitive($args['action'])) {
common_redirect(common_local_url($args['action'], $args));
}

View File

@ -265,10 +265,10 @@ class WebInstaller extends Installer
<li>
<label for="site_profile">Type of site</label>
<select id="site_profile" name="site_profile">
<option value="private">Private</option>
<option value="community">Community</option>
<option value ="public">Public</option>
<option value ="singleuser">Single User</option>
<option value="public">Public (open registration)</option>
<option value="singleuser">Single User</option>
<option value="private">Private (no federation)</option>
</select>
<p class="form_guide">Initial access settings for your site</p>
</li>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 B

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 B

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 B

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 B

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 332 B

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 B

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -1,8 +1,8 @@
/*! jQuery UI - v1.10.3 - 2013-09-12
/*! jQuery UI - v1.11.3 - 2015-03-07
* http://jqueryui.com
* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css
* Includes: core.css, draggable.css, resizable.css, selectable.css, sortable.css, accordion.css, autocomplete.css, button.css, datepicker.css, dialog.css, menu.css, progressbar.css, selectmenu.css, slider.css, spinner.css, tabs.css, tooltip.css, theme.css
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
* Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */
* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */
/* Layout helpers
----------------------------------*/
@ -48,7 +48,7 @@
left: 0;
position: absolute;
opacity: 0;
filter:Alpha(Opacity=0);
filter:Alpha(Opacity=0); /* support: IE8 */
}
.ui-front {
@ -86,6 +86,10 @@
width: 100%;
height: 100%;
}
.ui-draggable-handle {
-ms-touch-action: none;
touch-action: none;
}
.ui-resizable {
position: relative;
}
@ -93,6 +97,8 @@
position: absolute;
font-size: 0.1px;
display: block;
-ms-touch-action: none;
touch-action: none;
}
.ui-resizable-disabled .ui-resizable-handle,
.ui-resizable-autohide .ui-resizable-handle {
@ -154,25 +160,31 @@
right: -5px;
top: -5px;
}
.ui-selectable {
-ms-touch-action: none;
touch-action: none;
}
.ui-selectable-helper {
position: absolute;
z-index: 100;
border: 1px dotted black;
}
.ui-sortable-handle {
-ms-touch-action: none;
touch-action: none;
}
.ui-accordion .ui-accordion-header {
display: block;
cursor: pointer;
position: relative;
margin-top: 2px;
margin: 2px 0 0 0;
padding: .5em .5em .5em .7em;
min-height: 0; /* support: IE7 */
font-size: 100%;
}
.ui-accordion .ui-accordion-icons {
padding-left: 2.2em;
}
.ui-accordion .ui-accordion-noicons {
padding-left: .7em;
}
.ui-accordion .ui-accordion-icons .ui-accordion-icons {
padding-left: 2.2em;
}
@ -347,12 +359,9 @@ button.ui-button::-moz-focus-inner {
font-size: 1em;
margin: 1px 0;
}
.ui-datepicker select.ui-datepicker-month-year {
width: 100%;
}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year {
width: 49%;
width: 45%;
}
.ui-datepicker table {
width: 100%;
@ -466,6 +475,7 @@ button.ui-button::-moz-focus-inner {
border-left-width: 1px;
}
.ui-dialog {
overflow: hidden;
position: absolute;
top: 0;
left: 0;
@ -488,7 +498,7 @@ button.ui-button::-moz-focus-inner {
position: absolute;
right: .3em;
top: 50%;
width: 21px;
width: 20px;
margin: -10px 0 0 0;
padding: 1px;
height: 20px;
@ -526,72 +536,56 @@ button.ui-button::-moz-focus-inner {
}
.ui-menu {
list-style: none;
padding: 2px;
padding: 0;
margin: 0;
display: block;
outline: none;
}
.ui-menu .ui-menu {
margin-top: -3px;
position: absolute;
}
.ui-menu .ui-menu-item {
position: relative;
margin: 0;
padding: 0;
width: 100%;
padding: 3px 1em 3px .4em;
cursor: pointer;
min-height: 0; /* support: IE7 */
/* support: IE10, see #8844 */
list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
}
.ui-menu .ui-menu-divider {
margin: 5px -2px 5px -2px;
margin: 5px 0;
height: 0;
font-size: 0;
line-height: 0;
border-width: 1px 0 0 0;
}
.ui-menu .ui-menu-item a {
text-decoration: none;
display: block;
padding: 2px .4em;
line-height: 1.5;
min-height: 0; /* support: IE7 */
font-weight: normal;
}
.ui-menu .ui-menu-item a.ui-state-focus,
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
.ui-menu .ui-state-focus,
.ui-menu .ui-state-active {
margin: -1px;
}
.ui-menu .ui-state-disabled {
font-weight: normal;
margin: .4em 0 .2em;
line-height: 1.5;
}
.ui-menu .ui-state-disabled a {
cursor: default;
}
/* icon support */
.ui-menu-icons {
position: relative;
}
.ui-menu-icons .ui-menu-item a {
position: relative;
.ui-menu-icons .ui-menu-item {
padding-left: 2em;
}
/* left-aligned */
.ui-menu .ui-icon {
position: absolute;
top: .2em;
top: 0;
bottom: 0;
left: .2em;
margin: auto 0;
}
/* right-aligned */
.ui-menu .ui-menu-icon {
position: static;
float: right;
left: auto;
right: 0;
}
.ui-progressbar {
height: 2em;
@ -603,14 +597,63 @@ button.ui-button::-moz-focus-inner {
height: 100%;
}
.ui-progressbar .ui-progressbar-overlay {
background: url("images/animated-overlay.gif");
background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
height: 100%;
filter: alpha(opacity=25);
filter: alpha(opacity=25); /* support: IE8 */
opacity: 0.25;
}
.ui-progressbar-indeterminate .ui-progressbar-value {
background-image: none;
}
.ui-selectmenu-menu {
padding: 0;
margin: 0;
position: absolute;
top: 0;
left: 0;
display: none;
}
.ui-selectmenu-menu .ui-menu {
overflow: auto;
/* Support: IE7 */
overflow-x: hidden;
padding-bottom: 1px;
}
.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup {
font-size: 1em;
font-weight: bold;
line-height: 1.5;
padding: 2px 0.4em;
margin: 0.5em 0 0 0;
height: auto;
border: 0;
}
.ui-selectmenu-open {
display: block;
}
.ui-selectmenu-button {
display: inline-block;
overflow: hidden;
position: relative;
text-decoration: none;
cursor: pointer;
}
.ui-selectmenu-button span.ui-icon {
right: 0.5em;
left: auto;
margin-top: -8px;
position: absolute;
top: 50%;
}
.ui-selectmenu-button span.ui-selectmenu-text {
text-align: left;
padding: 0.4em 2.1em 0.4em 1em;
display: block;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ui-slider {
position: relative;
text-align: left;
@ -621,6 +664,8 @@ button.ui-button::-moz-focus-inner {
width: 1.2em;
height: 1.2em;
cursor: default;
-ms-touch-action: none;
touch-action: none;
}
.ui-slider .ui-slider-range {
position: absolute;
@ -631,7 +676,7 @@ button.ui-button::-moz-focus-inner {
background-position: 0 0;
}
/* For IE8 - See #6727 */
/* support: IE8 - See #6727 */
.ui-slider.ui-state-disabled .ui-slider-handle,
.ui-slider.ui-state-disabled .ui-slider-range {
filter: inherit;
@ -704,13 +749,13 @@ button.ui-button::-moz-focus-inner {
overflow: hidden;
right: 0;
}
/* more specificity required here to overide default borders */
/* more specificity required here to override default borders */
.ui-spinner a.ui-spinner-button {
border-top: none;
border-bottom: none;
border-right: none;
}
/* vertical centre icon */
/* vertically center icon */
.ui-spinner .ui-icon {
position: absolute;
margin-top: -8px;
@ -747,7 +792,7 @@ button.ui-button::-moz-focus-inner {
padding: 0;
white-space: nowrap;
}
.ui-tabs .ui-tabs-nav li a {
.ui-tabs .ui-tabs-nav .ui-tabs-anchor {
float: left;
padding: .5em 1em;
text-decoration: none;
@ -756,13 +801,12 @@ button.ui-button::-moz-focus-inner {
margin-bottom: -1px;
padding-bottom: 1px;
}
.ui-tabs .ui-tabs-nav li.ui-tabs-active a,
.ui-tabs .ui-tabs-nav li.ui-state-disabled a,
.ui-tabs .ui-tabs-nav li.ui-tabs-loading a {
.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
cursor: text;
}
.ui-tabs .ui-tabs-nav li a, /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a {
.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {
cursor: pointer;
}
.ui-tabs .ui-tabs-panel {
@ -801,7 +845,7 @@ body .ui-tooltip {
}
.ui-widget-content {
border: 1px solid #aaaaaa;
background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x;
background: #ffffff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x;
color: #222222;
}
.ui-widget-content a {
@ -809,7 +853,7 @@ body .ui-tooltip {
}
.ui-widget-header {
border: 1px solid #aaaaaa;
background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;
background: #cccccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x;
color: #222222;
font-weight: bold;
}
@ -823,7 +867,7 @@ body .ui-tooltip {
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default {
border: 1px solid #d3d3d3;
background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x;
background: #e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x;
font-weight: normal;
color: #555555;
}
@ -840,14 +884,18 @@ body .ui-tooltip {
.ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus {
border: 1px solid #999999;
background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x;
background: #dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x;
font-weight: normal;
color: #212121;
}
.ui-state-hover a,
.ui-state-hover a:hover,
.ui-state-hover a:link,
.ui-state-hover a:visited {
.ui-state-hover a:visited,
.ui-state-focus a,
.ui-state-focus a:hover,
.ui-state-focus a:link,
.ui-state-focus a:visited {
color: #212121;
text-decoration: none;
}
@ -855,7 +903,7 @@ body .ui-tooltip {
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active {
border: 1px solid #aaaaaa;
background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;
background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;
font-weight: normal;
color: #212121;
}
@ -872,7 +920,7 @@ body .ui-tooltip {
.ui-widget-content .ui-state-highlight,
.ui-widget-header .ui-state-highlight {
border: 1px solid #fcefa1;
background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x;
background: #fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;
color: #363636;
}
.ui-state-highlight a,
@ -884,7 +932,7 @@ body .ui-tooltip {
.ui-widget-content .ui-state-error,
.ui-widget-header .ui-state-error {
border: 1px solid #cd0a0a;
background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;
background: #fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;
color: #cd0a0a;
}
.ui-state-error a,
@ -906,18 +954,18 @@ body .ui-tooltip {
.ui-widget-content .ui-priority-secondary,
.ui-widget-header .ui-priority-secondary {
opacity: .7;
filter:Alpha(Opacity=70);
filter:Alpha(Opacity=70); /* support: IE8 */
font-weight: normal;
}
.ui-state-disabled,
.ui-widget-content .ui-state-disabled,
.ui-widget-header .ui-state-disabled {
opacity: .35;
filter:Alpha(Opacity=35);
filter:Alpha(Opacity=35); /* support: IE8 */
background-image: none;
}
.ui-state-disabled .ui-icon {
filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */
}
/* Icons
@ -930,27 +978,27 @@ body .ui-tooltip {
}
.ui-icon,
.ui-widget-content .ui-icon {
background-image: url(images/ui-icons_222222_256x240.png);
background-image: url("images/ui-icons_222222_256x240.png");
}
.ui-widget-header .ui-icon {
background-image: url(images/ui-icons_222222_256x240.png);
background-image: url("images/ui-icons_222222_256x240.png");
}
.ui-state-default .ui-icon {
background-image: url(images/ui-icons_888888_256x240.png);
background-image: url("images/ui-icons_888888_256x240.png");
}
.ui-state-hover .ui-icon,
.ui-state-focus .ui-icon {
background-image: url(images/ui-icons_454545_256x240.png);
background-image: url("images/ui-icons_454545_256x240.png");
}
.ui-state-active .ui-icon {
background-image: url(images/ui-icons_454545_256x240.png);
background-image: url("images/ui-icons_454545_256x240.png");
}
.ui-state-highlight .ui-icon {
background-image: url(images/ui-icons_2e83ff_256x240.png);
background-image: url("images/ui-icons_2e83ff_256x240.png");
}
.ui-state-error .ui-icon,
.ui-state-error-text .ui-icon {
background-image: url(images/ui-icons_cd0a0a_256x240.png);
background-image: url("images/ui-icons_cd0a0a_256x240.png");
}
/* positioning */
@ -1163,15 +1211,15 @@ body .ui-tooltip {
/* Overlays */
.ui-widget-overlay {
background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;
opacity: .3;
filter: Alpha(Opacity=30);
filter: Alpha(Opacity=30); /* support: IE8 */
}
.ui-widget-shadow {
margin: -8px 0 0 -8px;
padding: 8px;
background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;
opacity: .3;
filter: Alpha(Opacity=30);
filter: Alpha(Opacity=30); /* support: IE8 */
border-radius: 8px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*!
* jQuery Cookie Plugin v1.3.1
* jQuery Cookie Plugin v1.4.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2013 Klaus Hartl
@ -7,57 +7,65 @@
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as anonymous module.
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
factory(require('jquery'));
} else {
// Browser globals.
// Browser globals
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function decode(s) {
if (config.raw) {
return s;
}
try {
// If we can't decode the cookie, ignore it, it's unusable.
return decodeURIComponent(s.replace(pluses, ' '));
} catch(e) {}
function encode(s) {
return config.raw ? s : encodeURIComponent(s);
}
function decodeAndParse(s) {
function decode(s) {
return config.raw ? s : decodeURIComponent(s);
}
function stringifyCookieValue(value) {
return encode(config.json ? JSON.stringify(value) : String(value));
}
function parseCookieValue(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape...
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
s = decode(s);
try {
// Replace server-side written pluses with spaces.
// If we can't decode the cookie, ignore it, it's unusable.
// If we can't parse the cookie, ignore it, it's unusable.
s = decodeURIComponent(s.replace(pluses, ' '));
return config.json ? JSON.parse(s) : s;
} catch(e) {}
}
function read(s, converter) {
var value = config.raw ? s : parseCookieValue(s);
return $.isFunction(converter) ? converter(value) : value;
}
var config = $.cookie = function (key, value, options) {
// Write
if (value !== undefined) {
if (value !== undefined && !$.isFunction(value)) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setDate(t.getDate() + days);
t.setTime(+t + days * 864e+5);
}
value = config.json ? JSON.stringify(value) : String(value);
return (document.cookie = [
config.raw ? key : encodeURIComponent(key),
'=',
config.raw ? value : encodeURIComponent(value),
encode(key), '=', stringifyCookieValue(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
@ -80,12 +88,13 @@
var cookie = parts.join('=');
if (key && key === name) {
result = decodeAndParse(cookie);
// If second argument (value) is a function it's a converter...
result = read(cookie, value);
break;
}
// Prevent storing a cookie that we couldn't decode.
if (!key && (cookie = decodeAndParse(cookie)) !== undefined) {
if (!key && (cookie = read(cookie)) !== undefined) {
result[name] = cookie;
}
}
@ -96,12 +105,13 @@
config.defaults = {};
$.removeCookie = function (key, options) {
if ($.cookie(key) !== undefined) {
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return true;
if ($.cookie(key) === undefined) {
return false;
}
return false;
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return !$.cookie(key);
};
}));

View File

@ -1,15 +1,28 @@
/*!
* jQuery Form Plugin
* version: 3.43.0-2013.09.03
* version: 3.51.0-2014.06.20
* Requires jQuery v1.5 or later
* Copyright (c) 2013 M. Alsup
* Copyright (c) 2014 M. Alsup
* Examples and documentation at: http://malsup.com/jquery/form/
* Project repository: https://github.com/malsup/form
* Dual licensed under the MIT and GPL licenses.
* https://github.com/malsup/form#copyright-and-license
*/
/*global ActiveXObject */
;(function($) {
// AMD support
(function (factory) {
"use strict";
if (typeof define === 'function' && define.amd) {
// using AMD; register as anon module
define(['jquery'], factory);
} else {
// no AMD; invoke directly
factory( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );
}
}
(function($) {
"use strict";
/*
@ -63,11 +76,13 @@ var hasProp = !!$.fn.prop;
// contains inputs with names like "action" or "method"; in those
// cases "prop" returns the element
$.fn.attr2 = function() {
if ( ! hasProp )
if ( ! hasProp ) {
return this.attr.apply(this, arguments);
}
var val = this.prop.apply(this, arguments);
if ( ( val && val.jquery ) || typeof val === 'string' )
if ( ( val && val.jquery ) || typeof val === 'string' ) {
return val;
}
return this.attr.apply(this, arguments);
};
@ -209,7 +224,7 @@ $.fn.ajaxSubmit = function(options) {
// [value] (issue #113), also see comment:
// https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() != ''; });
var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; });
var hasFileInputs = fileInputs.length > 0;
var mp = 'multipart/form-data';
@ -245,8 +260,9 @@ $.fn.ajaxSubmit = function(options) {
$form.removeData('jqxhr').data('jqxhr', jqxhr);
// clear element array
for (var k=0; k < elements.length; k++)
for (var k=0; k < elements.length; k++) {
elements[k] = null;
}
// fire 'notify' event
this.trigger('form-submit-notify', [this, options]);
@ -278,9 +294,11 @@ $.fn.ajaxSubmit = function(options) {
if (options.extraData) {
var serializedData = deepSerialize(options.extraData);
for (i=0; i < serializedData.length; i++)
if (serializedData[i])
for (i=0; i < serializedData.length; i++) {
if (serializedData[i]) {
formdata.append(serializedData[i][0], serializedData[i][1]);
}
}
}
options.data = null;
@ -312,11 +330,18 @@ $.fn.ajaxSubmit = function(options) {
}
s.data = null;
var beforeSend = s.beforeSend;
s.beforeSend = function(xhr, o) {
var beforeSend = s.beforeSend;
s.beforeSend = function(xhr, o) {
//Send FormData() provided by user
if (options.formData) {
o.data = options.formData;
}
else {
o.data = formdata;
if(beforeSend)
beforeSend.call(this, xhr, o);
}
if(beforeSend) {
beforeSend.call(this, xhr, o);
}
};
return $.ajax(s);
}
@ -335,10 +360,12 @@ $.fn.ajaxSubmit = function(options) {
// ensure that every serialized input is still enabled
for (i=0; i < elements.length; i++) {
el = $(elements[i]);
if ( hasProp )
if ( hasProp ) {
el.prop('disabled', false);
else
}
else {
el.removeAttr('disabled');
}
}
}
@ -348,10 +375,12 @@ $.fn.ajaxSubmit = function(options) {
if (s.iframeTarget) {
$io = $(s.iframeTarget);
n = $io.attr2('name');
if (!n)
$io.attr2('name', id);
else
if (!n) {
$io.attr2('name', id);
}
else {
id = n;
}
}
else {
$io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
@ -383,12 +412,15 @@ $.fn.ajaxSubmit = function(options) {
$io.attr('src', s.iframeSrc); // abort op in progress
xhr.error = e;
if (s.error)
if (s.error) {
s.error.call(s.context, xhr, e, status);
if (g)
}
if (g) {
$.event.trigger("ajaxError", [xhr, s, e]);
if (s.complete)
}
if (s.complete) {
s.complete.call(s.context, xhr, e);
}
}
};
@ -475,7 +507,10 @@ $.fn.ajaxSubmit = function(options) {
// take a breath so that pending repaints get some cpu time before the upload starts
function doSubmit() {
// make sure form attrs are set
var t = $form.attr2('target'), a = $form.attr2('action');
var t = $form.attr2('target'),
a = $form.attr2('action'),
mp = 'multipart/form-data',
et = $form.attr('enctype') || $form.attr('encoding') || mp;
// update form attrs in IE friendly way
form.setAttribute('target',id);
@ -504,14 +539,16 @@ $.fn.ajaxSubmit = function(options) {
try {
var state = getDoc(io).readyState;
log('state = ' + state);
if (state && state.toLowerCase() == 'uninitialized')
if (state && state.toLowerCase() == 'uninitialized') {
setTimeout(checkState,50);
}
}
catch(e) {
log('Server abort: ' , e, ' (', e.name, ')');
cb(SERVER_ABORT);
if (timeoutHandle)
if (timeoutHandle) {
clearTimeout(timeoutHandle);
}
timeoutHandle = undefined;
}
}
@ -540,10 +577,12 @@ $.fn.ajaxSubmit = function(options) {
// add iframe to doc and submit the form
$io.appendTo('body');
}
if (io.attachEvent)
if (io.attachEvent) {
io.attachEvent('onload', cb);
else
}
else {
io.addEventListener('load', cb, false);
}
setTimeout(checkState,15);
try {
@ -557,6 +596,7 @@ $.fn.ajaxSubmit = function(options) {
finally {
// reset attrs and remove "extra" input elements
form.setAttribute('action',a);
form.setAttribute('enctype', et); // #380
if(t) {
form.setAttribute('target', t);
} else {
@ -598,13 +638,16 @@ $.fn.ajaxSubmit = function(options) {
if (!doc || doc.location.href == s.iframeSrc) {
// response not received yet
if (!timedOut)
if (!timedOut) {
return;
}
}
if (io.detachEvent)
if (io.detachEvent) {
io.detachEvent('onload', cb);
else
}
else {
io.removeEventListener('load', cb, false);
}
var status = 'success', errMsg;
try {
@ -631,8 +674,9 @@ $.fn.ajaxSubmit = function(options) {
var docRoot = doc.body ? doc.body : doc.documentElement;
xhr.responseText = docRoot ? docRoot.innerHTML : null;
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
if (isXml)
if (isXml) {
s.dataType = 'xml';
}
xhr.getResponseHeader = function(header){
var headers = {'content-type': s.dataType};
return headers[header.toLowerCase()];
@ -695,42 +739,52 @@ $.fn.ajaxSubmit = function(options) {
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
if (status === 'success') {
if (s.success)
if (s.success) {
s.success.call(s.context, data, 'success', xhr);
}
deferred.resolve(xhr.responseText, 'success', xhr);
if (g)
if (g) {
$.event.trigger("ajaxSuccess", [xhr, s]);
}
}
else if (status) {
if (errMsg === undefined)
if (errMsg === undefined) {
errMsg = xhr.statusText;
if (s.error)
}
if (s.error) {
s.error.call(s.context, xhr, status, errMsg);
}
deferred.reject(xhr, 'error', errMsg);
if (g)
if (g) {
$.event.trigger("ajaxError", [xhr, s, errMsg]);
}
}
if (g)
if (g) {
$.event.trigger("ajaxComplete", [xhr, s]);
}
if (g && ! --$.active) {
$.event.trigger("ajaxStop");
}
if (s.complete)
if (s.complete) {
s.complete.call(s.context, xhr, status);
}
callbackProcessed = true;
if (s.timeout)
if (s.timeout) {
clearTimeout(timeoutHandle);
}
// clean up
setTimeout(function() {
if (!s.iframeTarget)
if (!s.iframeTarget) {
$io.remove();
else //adding else to clean up existing iframe response.
}
else { //adding else to clean up existing iframe response.
$io.attr('src', s.iframeSrc);
}
xhr.responseXML = null;
}, 100);
}
@ -758,8 +812,9 @@ $.fn.ajaxSubmit = function(options) {
data = xml ? xhr.responseXML : xhr.responseText;
if (xml && data.documentElement.nodeName === 'parsererror') {
if ($.error)
if ($.error) {
$.error('parsererror');
}
}
if (s && s.dataFilter) {
data = s.dataFilter(data, type);
@ -832,7 +887,7 @@ function doAjaxSubmit(e) {
var options = e.data;
if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
e.preventDefault();
$(this).ajaxSubmit(options);
$(e.target).ajaxSubmit(options); // #365
}
}
@ -891,8 +946,23 @@ $.fn.formToArray = function(semantic, elements) {
}
var form = this[0];
var formId = this.attr('id');
var els = semantic ? form.getElementsByTagName('*') : form.elements;
if (!els) {
var els2;
if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390
els = $(els).get(); // convert to standard array
}
// #386; account for inputs outside the form which use the 'form' attribute
if ( formId ) {
els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet
if ( els2.length ) {
els = (els || []).concat(els2);
}
}
if (!els || !els.length) {
return a;
}
@ -915,15 +985,17 @@ $.fn.formToArray = function(semantic, elements) {
v = $.fieldValue(el, true);
if (v && v.constructor == Array) {
if (elements)
if (elements) {
elements.push(el);
}
for(j=0, jmax=v.length; j < jmax; j++) {
a.push({name: n, value: v[j]});
}
}
else if (feature.fileapi && el.type == 'file') {
if (elements)
if (elements) {
elements.push(el);
}
var files = el.files;
if (files.length) {
for (j=0; j < files.length; j++) {
@ -936,8 +1008,9 @@ $.fn.formToArray = function(semantic, elements) {
}
}
else if (v !== null && typeof v != 'undefined') {
if (elements)
if (elements) {
elements.push(el);
}
a.push({name: n, value: v, type: el.type, required: el.required});
}
}
@ -1033,10 +1106,12 @@ $.fn.fieldValue = function(successful) {
if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
continue;
}
if (v.constructor == Array)
if (v.constructor == Array) {
$.merge(val, v);
else
}
else {
val.push(v);
}
}
return val;
};
@ -1070,7 +1145,7 @@ $.fieldValue = function(el, successful) {
if (op.selected) {
var v = op.value;
if (!v) { // extra pain for IE...
v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
}
if (one) {
return v;
@ -1113,21 +1188,22 @@ $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
else if (tag == 'select') {
this.selectedIndex = -1;
}
else if (t == "file") {
if (/MSIE/.test(navigator.userAgent)) {
$(this).replaceWith($(this).clone(true));
} else {
$(this).val('');
}
}
else if (t == "file") {
if (/MSIE/.test(navigator.userAgent)) {
$(this).replaceWith($(this).clone(true));
} else {
$(this).val('');
}
}
else if (includeHidden) {
// includeHidden can be the value true, or it can be a selector string
// indicating a special test; for example:
// $('#myForm').clearForm('.special:hidden')
// the above would clean hidden inputs that have the class of 'special'
if ( (includeHidden === true && /hidden/.test(t)) ||
(typeof includeHidden == 'string' && $(this).is(includeHidden)) )
(typeof includeHidden == 'string' && $(this).is(includeHidden)) ) {
this.value = '';
}
}
});
};
@ -1186,8 +1262,9 @@ $.fn.ajaxSubmit.debug = false;
// helper fn for console logging
function log() {
if (!$.fn.ajaxSubmit.debug)
if (!$.fn.ajaxSubmit.debug) {
return;
}
var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
if (window.console && window.console.log) {
window.console.log(msg);
@ -1197,4 +1274,4 @@ function log() {
}
}
})( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );
}));

View File

@ -1,168 +0,0 @@
/**
* @license In-Field Label jQuery Plugin
* http://fuelyourcoding.com/scripts/infield.html
* http://github.com/streetpc/jquery-infieldlabels
*
* Copyright (c) 2009 Doug Neiner, Adrien Lavoillotte
* Dual licensed under the MIT and GPL licenses, see:
* http://docs.jquery.com/License
*
* @version 0.2.1
*/
(function ($) {
// private constants
// - states
var BLUR = 0, // field is empty & unfocused
FOCUS = 1, // field is empty & focused
NOT_EMPTY = 2, // field is not empty
// - accepted input type
INPUT_TYPE = /^(?:text|password|search|number|tel|url|email|date(?:time(?:-local)?)?|time|month|week)?$/,
// - state transitions
T = function(from, to) { return (from << 3) | to; },
TRANSITIONS = {};
// init transitions
TRANSITIONS[T( FOCUS, BLUR )] = function(base) { base.fadeTo(1.0); };
TRANSITIONS[T( NOT_EMPTY, BLUR )] = function(base) { base.$label.css({opacity: 1.0}).show(); base.emptied(true); };
TRANSITIONS[T( BLUR, FOCUS )] = function(base) { base.fadeTo(base.options.fadeOpacity); };
TRANSITIONS[T( NOT_EMPTY, FOCUS )] = function(base) { base.$label.css({opacity: base.options.fadeOpacity}).show(); base.emptied(true); };
TRANSITIONS[T( BLUR, NOT_EMPTY )] = function(base) { base.$label.hide(); base.emptied(false); };
TRANSITIONS[T( FOCUS, NOT_EMPTY )] = TRANSITIONS[T( BLUR, NOT_EMPTY )];
$.InFieldLabels = function (label, field, options) {
// To avoid scope issues, use 'base' instead of 'this'
// to reference this class from internal events and functions.
var base = this;
// Access to jQuery and DOM versions of each element
base.$label = $(label);
base.label = label;
base.$field = $(field);
base.field = field;
base.$label.data('InFieldLabels', base);
base.state = BLUR;
base.init = function () {
// Merge supplied options with default options
base.options = $.extend({}, $.InFieldLabels.defaultOptions, options);
if (base.options.labelClass) {
base.$label.addClass(base.options.labelClass);
}
if (base.options.disableAutocomplete) {
base.$field.attr('autocomplete', 'off');
}
base.$field
.bind('blur focus change keyup.infield cut', base.updateState)
// paste cannot be empty
.bind('paste', function(e){ base.setState(NOT_EMPTY); });
base.updateState();
};
base.emptied = function(empty) {
if (!base.options.emptyWatch) {
if (empty) {
// namespace ensures we unbind only our handler
base.$field.bind('keyup.infield', base.updateState);
} else {
// save CPU but won't detect empty until blur
base.$field.unbind('keyup.infield', base.updateState);
}
}
};
base.fadeTo = function (opacity) {
if (!base.options.fadeDuration) {
base.$label.css({ opacity: opacity });
} else {
base.$label.stop().animate({ opacity: opacity }, base.options.fadeDuration);
}
};
base.updateState = function (e, nl) {
var state = NOT_EMPTY;
if (base.field.value === '') {
var focus = e && e.type;
if (focus === 'focus' || focus === 'keyup') {
focus = true;
} else if (focus === 'blur' || focus === 'change') {
focus = false;
} else { // last resort because slowest
focus = base.$field.is(':focus');
}
state = focus ? FOCUS : BLUR;
}
base.setState(state, nl);
};
base.setState = function (state, nl) {
if (state === base.state) {
return;
}
var transition = TRANSITIONS[T(base.state, state)];
if (typeof transition === 'function') {
transition(base);
base.state = state;
} else { // unkown transition - shouldn't happen
// nl avoids looping
nl || base.updateState(null, true);
}
};
// Run the initialization method
base.init();
};
$.InFieldLabels.defaultOptions = {
emptyWatch: true, // Keep watching the field as the user types (slower but brings back the label immediately when the field is emptied)
disableAutocomplete: true, // Disable autocomplete on the matched fields
fadeOpacity: 0.5, // Once a field has focus, how transparent should the label be
fadeDuration: 300, // How long should it take to animate from 1.0 opacity to the fadeOpacity
labelClass: 'in-field' // CSS class to apply to the label when it gets in-field
};
$.fn.inFieldLabels = function (options) {
return this.each(function () {
if (this.tagName !== 'LABEL') {
return;
}
// Find input or textarea based on for= attribute
// The for attribute on the label must contain the ID
// of the input or textarea element
var for_attr = this.getAttribute('for') || this.htmlFor,
field, valid = true;
if (!for_attr) {
return; // Nothing to attach, since the for field wasn't used
}
// Find the referenced input or textarea element
field = document.getElementById(for_attr);
if (!field) {
return;
}
if (field.tagName === 'INPUT') {
valid = INPUT_TYPE.test(field.type.toLowerCase());
} else if (field.tagName !== 'TEXTAREA') {
valid = false;
}
valid = valid && !field.getAttribute('placeholder');
if (!valid) {
return; // Again, nothing to attach
}
// Only create object for input[text], input[password], or textarea
(new $.InFieldLabels(this, field, options));
});
};
}(jQuery));

7890
js/extlib/jquery.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*
json2.js
2013-05-26
2015-02-25
Public Domain.
@ -48,7 +48,9 @@
Date.prototype.toJSON = function (key) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
return n < 10
? '0' + n
: n;
}
return this.getUTCFullYear() + '-' +
@ -146,10 +148,12 @@
redistribute.
*/
/*jslint evil: true, regexp: true */
/*jslint
eval, for, this
*/
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
/*property
JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf
@ -168,7 +172,13 @@ if (typeof JSON !== 'object') {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
return n < 10
? '0' + n
: n;
}
function this_value() {
return this.valueOf();
}
if (typeof Date.prototype.toJSON !== 'function') {
@ -176,35 +186,25 @@ if (typeof JSON !== 'object') {
Date.prototype.toJSON = function () {
return isFinite(this.valueOf())
? this.getUTCFullYear() + '-' +
? this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z'
: null;
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z'
: null;
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function () {
return this.valueOf();
};
Boolean.prototype.toJSON = this_value;
Number.prototype.toJSON = this_value;
String.prototype.toJSON = this_value;
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
var cx,
escapable,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
meta,
rep;
@ -216,12 +216,14 @@ if (typeof JSON !== 'object') {
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
return escapable.test(string)
? '"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string'
? c
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + string + '"';
? c
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"'
: '"' + string + '"';
}
@ -261,7 +263,9 @@ if (typeof JSON !== 'object') {
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
return isFinite(value)
? String(value)
: 'null';
case 'boolean':
case 'null':
@ -305,10 +309,10 @@ if (typeof JSON !== 'object') {
// brackets.
v = partial.length === 0
? '[]'
: gap
? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
: '[' + partial.join(',') + ']';
? '[]'
: gap
? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
: '[' + partial.join(',') + ']';
gap = mind;
return v;
}
@ -322,7 +326,11 @@ if (typeof JSON !== 'object') {
k = rep[i];
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
partial.push(quote(k) + (
gap
? ': '
: ':'
) + v);
}
}
}
@ -334,7 +342,11 @@ if (typeof JSON !== 'object') {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
partial.push(quote(k) + (
gap
? ': '
: ':'
) + v);
}
}
}
@ -344,10 +356,10 @@ if (typeof JSON !== 'object') {
// and wrap them in braces.
v = partial.length === 0
? '{}'
: gap
? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
: '{' + partial.join(',') + '}';
? '{}'
: gap
? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
: '{' + partial.join(',') + '}';
gap = mind;
return v;
}
@ -356,6 +368,16 @@ if (typeof JSON !== 'object') {
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"': '\\"',
'\\': '\\\\'
};
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
@ -403,6 +425,7 @@ if (typeof JSON !== 'object') {
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
@ -441,7 +464,7 @@ if (typeof JSON !== 'object') {
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
@ -458,10 +481,13 @@ if (typeof JSON !== 'object') {
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
if (
/^[\],:{}\s]*$/.test(
text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
.replace(/(?:^|:|,)(?:\s*\[)+/g, '')
)
) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
@ -474,8 +500,8 @@ if (typeof JSON !== 'object') {
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function'
? walk({'': j}, '')
: j;
? walk({'': j}, '')
: j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.

View File

@ -32,7 +32,6 @@ var SN = { // StatusNet
MaxLength: 140,
PatternUsername: /^[0-9a-zA-Z\-_.]*$/,
HTTP20x30x: [200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304, 305, 306, 307],
NoticeFormMaster: null // to be cloned from the one at top
},
/**
@ -58,6 +57,9 @@ var SN = { // StatusNet
}
},
V: { // Variables
},
/**
* Map of localized message strings exported to script from the PHP
* side via Action::getScriptMessages().
@ -94,12 +96,12 @@ var SN = { // StatusNet
* @access private
*/
FormNoticeEnhancements: function (form) {
if (jQuery.data(form[0], 'ElementData') === undefined) {
if ($.data(form[0], 'ElementData') === undefined) {
var MaxLength = form.find('.count').text();
if (MaxLength === undefined) {
MaxLength = SN.C.I.MaxLength;
}
jQuery.data(form[0], 'ElementData', {MaxLength: MaxLength});
$.data(form[0], 'ElementData', {MaxLength: MaxLength});
SN.U.Counter(form);
@ -122,7 +124,7 @@ var SN = { // StatusNet
NDT.on('cut', delayedUpdate)
.on('paste', delayedUpdate);
} else {
form.find('.count').text(jQuery.data(form[0], 'ElementData').MaxLength);
form.find('.count').text($.data(form[0], 'ElementData').MaxLength);
}
},
@ -143,7 +145,7 @@ var SN = { // StatusNet
Counter: function (form) {
SN.C.I.FormNoticeCurrent = form;
var MaxLength = jQuery.data(form[0], 'ElementData').MaxLength;
var MaxLength = $.data(form[0], 'ElementData').MaxLength;
if (MaxLength <= 0) {
return;
@ -217,6 +219,18 @@ var SN = { // StatusNet
return url;
},
FormNoticeUniqueID: function (form) {
var oldId = form.attr('id');
var newId = 'form_notice_' + Math.floor(Math.random()*999999999);
var attrs = ['name', 'for', 'id'];
for (var key in attrs) {
form.find("[" + attrs[key] + "~='" + oldId + "']").each(function () {
var newAttr = $(this).attr(attrs[key]).replace(oldId, newId);
$(this).attr(attrs[key], newAttr);
});
}
},
/**
* Grabs form data and submits it asynchronously, with 'ajax=1'
* parameter added to the rest.
@ -375,7 +389,7 @@ var SN = { // StatusNet
if ($('.' + SN.C.S.Error, response).length > 0) {
form.append(document._importNode($('.' + SN.C.S.Error, response)[0], true));
} else {
if (parseInt(xhr.status) === 0 || jQuery.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) >= 0) {
if (parseInt(xhr.status) === 0 || $.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) >= 0) {
form
.resetForm()
.find('.attach-status').remove();
@ -405,16 +419,14 @@ var SN = { // StatusNet
if (replyItem.length > 0) {
// If this is an inline reply, remove the form...
var list = form.closest('.threaded-replies');
var placeholder = list.find('.notice-reply-placeholder');
replyItem.remove();
var id = $(notice).attr('id');
if ($('#' + id).length == 0) {
$(notice).insertBefore(placeholder);
$(notice).insertBefore(replyItem);
} // else Realtime came through before us...
// ...and show the placeholder form.
placeholder.show();
replyItem.remove();
} else if (notices.length > 0 && SN.U.belongsOnTimeline(notice)) {
// Not a reply. If on our timeline, show it at the top!
@ -578,6 +590,44 @@ var SN = { // StatusNet
}
},
/**
* Setup function -- DOES NOT trigger actions immediately.
*
* Sets up event handlers on all visible notice's option <a> elements
* with the "popup" class so they behave as expected with AJAX.
*
* (without javascript the link goes to a page that expects you to verify
* the action through a form)
*
* @access private
*/
NoticeOptionsAjax: function () {
$(document).on('click', '.notice-options > a.popup', function (e) {
e.preventDefault();
var noticeEl = $(this).closest('.notice');
$.ajax({
url: $(this).attr('href'),
data: {ajax: 1},
success: function (data, textStatus, xhr) {
SN.U.NoticeOptionPopup(data, noticeEl);
},
});
return false;
});
},
NoticeOptionPopup: function (data, noticeEl) {
title = $('head > title', data).text();
body = $('body', data).html();
dialog = $(body).dialog({
height: "auto",
width: "auto",
modal: true,
resizable: true,
title: title,
});
},
/**
* Setup function -- DOES NOT trigger actions immediately.
*
@ -616,41 +666,18 @@ var SN = { // StatusNet
NoticeInlineReplyTrigger: function (notice, initialText) {
// Find the notice we're replying to...
var id = $($('.notice_id', notice)[0]).text();
var replyForm, placeholder;
var replyForm;
var parentNotice = notice;
var stripForm = true; // strip a couple things out of reply forms that are inline
// Find the threaded replies view we'll be adding to...
var list = notice.closest('.notices');
if (list.closest('.old-school').length) {
// We're replying to an old-school conversation thread;
// use the old-style ping into the top form.
SN.U.switchInputFormTab("status");
replyForm = $('#input_form_status').find('form');
stripForm = false;
} else if (list.hasClass('threaded-replies')) {
// We're replying to a reply; use reply form on the end of this list.
// We'll add our form at the end of this; grab the root notice.
parentNotice = list.closest('.notice');
// See if the form's already open...
replyForm = $('.notice-reply-form', list);
} else {
// We're replying to a parent notice; pull its threaded list
// and we'll add on the end of it. Will add if needed.
list = $('ul.threaded-replies', notice);
if (list.length == 0) {
SN.U.NoticeInlineReplyPlaceholder(notice);
list = $('ul.threaded-replies', notice);
} else {
placeholder = $('li.notice-reply-placeholder', notice);
if (placeholder.length == 0) {
SN.U.NoticeInlineReplyPlaceholder(notice);
}
}
// See if the form's already open...
replyForm = $('.notice-reply-form', list);
var list = notice.find('.threaded-replies');
if (list.length == 0) {
list = notice.closest('.threaded-replies');
}
if (list.length == 0) {
list = $('<ul class="notices threaded-replies xoxo"></ul>');
notice.append(list);
list = notice.find('.threaded-replies');
}
var nextStep = function () {
@ -663,6 +690,7 @@ var SN = { // StatusNet
replyForm.find('label[for=notice_to]').hide();
replyForm.find('label[for=notice_private]').hide();
}
replyItem.show();
// Set focus...
var text = replyForm.find('textarea');
@ -681,82 +709,64 @@ var SN = { // StatusNet
text[0].setSelectionRange(len, len);
}
};
if (replyForm.length > 0) {
// Update the existing form...
nextStep();
} else {
// Hide the placeholder...
placeholder = list.find('li.notice-reply-placeholder').hide();
// Create the reply form entry at the end
var replyItem = $('li.notice-reply', list);
if (replyItem.length == 0) {
replyItem = $('<li class="notice-reply"></li>');
// Create the reply form entry
var replyItem = $('li.notice-reply', list);
if (replyItem.length == 0) {
replyItem = $('<li class="notice-reply"></li>');
}
replyForm = replyItem.children('form');
if (replyForm.length == 0) {
// Let's try another trick to avoid fetching by URL
var noticeForm = $('#input_form_status > form');
if (noticeForm.length == 0) {
// No notice form found on the page, so let's just
// fetch a fresh copy of the notice form over AJAX.
$.ajax({
url: SN.V.urlNewNotice,
data: {ajax: 1, inreplyto: id},
success: function (data, textStatus, xhr) {
var formEl = document._importNode($('form', data)[0], true);
replyForm = $(formEl);
replyItem.append(replyForm);
list.append(replyItem);
var intermediateStep = function (formMaster) {
var formEl = document._importNode(formMaster, true);
replyItem.append(formEl);
list.append(replyItem); // *after* the placeholder
var form = $(formEl);
replyForm = form;
SN.Init.NoticeFormSetup(form);
nextStep();
};
if (SN.C.I.NoticeFormMaster) {
// We've already saved a master copy of the form.
// Clone it in!
intermediateStep(SN.C.I.NoticeFormMaster);
} else {
// Fetch a fresh copy of the notice form over AJAX.
// Warning: this can have a delay, which looks bad.
// @fixme this fallback may or may not work
var url = $('#form_notice').attr('action');
$.get(url, {ajax: 1}, function (data, textStatus, xhr) {
intermediateStep($('form', data)[0]);
});
}
SN.Init.NoticeFormSetup(replyForm);
nextStep();
},
});
// We do everything relevant in 'success' above
return;
}
replyForm = noticeForm.clone();
SN.Init.NoticeFormSetup(replyForm);
replyItem.append(replyForm);
list.append(replyItem);
}
},
NoticeInlineReplyPlaceholder: function (notice) {
var list = notice.find('ul.threaded-replies');
if (list.length == 0) {
list = $('<ul class="notices threaded-replies xoxo"></ul>');
notice.append(list);
list = notice.find('ul.threaded-replies');
}
var placeholder = $('<li class="notice-reply-placeholder">' +
'<input class="placeholder" />' +
'</li>');
placeholder.find('input')
.val(SN.msg('reply_placeholder'));
list.append(placeholder);
// replyForm is set, we're not fetching by URL...
// Next setp is to configure in-reply-to etc.
nextStep();
},
/**
* Setup function -- DOES NOT apply immediately.
*
* Sets up event handlers for inline reply mini-form placeholders.
* Uses 'on' rather than 'live' or 'bind', so applies to future as well as present items.
*/
NoticeInlineReplySetup: function () {
$('li.notice-reply-placeholder input')
.on('focus', function () {
var notice = $(this).closest('li.notice');
SN.U.NoticeInlineReplyTrigger(notice);
return false;
});
// Expand conversation links
$(document).on('click', 'li.notice-reply-comments a', function () {
var url = $(this).attr('href');
var area = $(this).closest('.threaded-replies');
$.get(url, {ajax: 1}, function (data, textStatus, xhr) {
var replies = $('.threaded-replies', data);
if (replies.length) {
area.replaceWith(document._importNode(replies[0], true));
}
$.ajax({
url: url,
data: {ajax: 1},
success: function (data, textStatus, xhr) {
var replies = $('.threaded-replies', data);
if (replies.length) {
area.replaceWith(document._importNode(replies[0], true));
}
},
});
return false;
});
@ -1043,7 +1053,7 @@ var SN = { // StatusNet
function removeNoticeDataGeo(error) {
label
.attr('title', jQuery.trim(label.text()))
.attr('title', $.trim(label.text()))
.removeClass('checked');
form.find('[name=lat]').val('');
@ -1460,16 +1470,13 @@ var SN = { // StatusNet
// Only close if there's been no edit.
if (cur == '' || cur == textarea.data('initialText')) {
var parentNotice = replyItem.closest('li.notice');
replyItem.remove();
replyItem.hide();
parentNotice.find('li.notice-reply-placeholder').show();
}
}
});
}
});
// Infield labels for notice form inputs.
$('.input_forms fieldset fieldset label').inFieldLabels({ fadeOpacity:0 });
}
},
@ -1481,13 +1488,15 @@ var SN = { // StatusNet
* @param {jQuery} form
*/
NoticeFormSetup: function (form) {
if (!form.data('NoticeFormSetup')) {
SN.U.NoticeLocationAttach(form);
SN.U.FormNoticeXHR(form);
SN.U.FormNoticeEnhancements(form);
SN.U.NoticeDataAttach(form);
form.data('NoticeFormSetup', true);
if (form.data('NoticeFormSetup')) {
return false;
}
SN.U.NoticeLocationAttach(form);
SN.U.FormNoticeUniqueID(form);
SN.U.FormNoticeXHR(form);
SN.U.FormNoticeEnhancements(form);
SN.U.NoticeDataAttach(form);
form.data('NoticeFormSetup', true);
},
/**
@ -1498,13 +1507,10 @@ var SN = { // StatusNet
*/
Notices: function () {
if ($('body.user_in').length > 0) {
var masterForm = $('.form_notice:first');
if (masterForm.length > 0) {
SN.C.I.NoticeFormMaster = document._importNode(masterForm[0], true);
}
SN.U.NoticeRepeat();
SN.U.NoticeReply();
SN.U.NoticeInlineReplySetup();
SN.U.NoticeOptionsAjax();
}
SN.U.NoticeAttachments();
@ -1562,60 +1568,6 @@ var SN = { // StatusNet
});
},
/**
* Called when a people tag edit box is shown in the interface
*
* - loads the jQuery UI autocomplete plugin
* - sets event handlers for tag completion
*
*/
PeopletagAutocomplete: function (txtBox) {
var split = function (val) {
return val.split( /\s+/ );
}
var extractLast = function (term) {
return split(term).pop();
}
// don't navigate away from the field on tab when selecting an item
txtBox.on( "keydown", function ( event ) {
if ( event.keyCode === $.ui.keyCode.TAB &&
$(this).data( "autocomplete" ).menu.active ) {
event.preventDefault();
}
}).autocomplete({
minLength: 0,
source: function (request, response) {
// delegate back to autocomplete, but extract the last term
response($.ui.autocomplete.filter(
SN.C.PtagACData, extractLast(request.term)));
},
focus: function () {
return false;
},
select: function (event, ui) {
var terms = split(this.value);
terms.pop();
terms.push(ui.item.value);
terms.push("");
this.value = terms.join(" ");
return false;
}
}).data('autocomplete')._renderItem = function (ul, item) {
// FIXME: with jQuery UI you cannot have it highlight the match
var _l = '<a class="ptag-ac-line-tag">' + item.tag
+ ' <em class="privacy_mode">' + item.mode + '</em>'
+ '<span class="freq">' + item.freq + '</span></a>'
return $("<li/>")
.addClass('mode-' + item.mode)
.addClass('ptag-ac-line')
.data("item.autocomplete", item)
.append(_l)
.appendTo(ul);
}
},
/**
* Run setup for the ajax people tags editor
*
@ -1644,7 +1596,6 @@ var SN = { // StatusNet
}
SN.C.PtagACData = data;
SN.Init.PeopletagAutocomplete(form.find('#tags'));
}
});

View File

@ -118,16 +118,18 @@ class Action extends HTMLOutputter // lawsuit
common_config_set('db', 'database', $mirror);
}
$status = $this->prepare($args);
if ($status) {
$this->handle($args);
} else {
common_debug('Prepare failed for Action.');
if (Event::handle('StartActionExecute', array($this, &$args))) {
$prepared = $this->prepare($args);
if ($prepared) {
$this->handle($args);
} else {
common_debug('Prepare failed for Action.');
}
}
$this->flush();
Event::handle('EndActionExecute', array($status, $this));
Event::handle('EndActionExecute', array($this));
}
/**
@ -156,8 +158,8 @@ class Action extends HTMLOutputter // lawsuit
$this->action = strtolower($this->trimmed('action'));
if ($this->ajax || $this->boolean('ajax')) {
// check with StatusNet::isAjax()
StatusNet::setAjax(true);
// check with GNUsocial::isAjax()
GNUsocial::setAjax(true);
}
if ($this->needLogin) {
@ -206,7 +208,7 @@ class Action extends HTMLOutputter // lawsuit
*/
function showPage()
{
if (StatusNet::isAjax()) {
if (GNUsocial::isAjax()) {
self::showAjax();
return;
}
@ -326,7 +328,7 @@ class Action extends HTMLOutputter // lawsuit
} else {
// favicon.ico should be HTTPS if the rest of the page is
$this->element('link', array('rel' => 'shortcut icon',
'href' => common_path('favicon.ico', StatusNet::isHTTPS())));
'href' => common_path('favicon.ico', GNUsocial::isHTTPS())));
}
if (common_config('site', 'mobile')) {
@ -415,8 +417,7 @@ class Action extends HTMLOutputter // lawsuit
$this->script('extlib/jquery.form.js');
$this->script('extlib/jquery-ui/jquery-ui.js');
$this->script('extlib/jquery.cookie.js');
$this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/extlib/json2.js', StatusNet::isHTTPS()).'"); }');
$this->script('extlib/jquery.infieldlabel.js');
$this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/extlib/json2.js', GNUsocial::isHTTPS()).'"); }');
Event::handle('EndShowJQueryScripts', array($this));
}
@ -430,6 +431,7 @@ class Action extends HTMLOutputter // lawsuit
$this->inlineScript('var _peopletagAC = "' .
common_local_url('peopletagautocomplete') . '";');
$this->showScriptMessages();
$this->showScriptVariables();
// Anti-framing code to avoid clickjacking attacks in older browsers.
// This will show a blank page if the page is being framed, which is
// consistent with the behavior of the 'X-Frame-Options: SAMEORIGIN'
@ -460,12 +462,6 @@ class Action extends HTMLOutputter // lawsuit
// TRANS: Localized tooltip for '...' expansion button on overlong remote messages.
$messages['showmore_tooltip'] = _m('TOOLTIP', 'Show more');
// TRANS: Inline reply form submit button: submits a reply comment.
$messages['reply_submit'] = _m('BUTTON', 'Reply');
// TRANS: Placeholder text for inline reply form. Clicking in this box will turn it into a mini notice form.
$messages['reply_placeholder'] = _m('Write a reply...');
$messages = array_merge($messages, $this->getScriptMessages());
Event::handle('EndScriptMessages', array($this, &$messages));
@ -478,6 +474,19 @@ class Action extends HTMLOutputter // lawsuit
return $messages;
}
protected function showScriptVariables()
{
$vars = array();
if (Event::handle('StartScriptVariables', array($this, &$vars))) {
$vars['urlNewNotice'] = common_local_url('newnotice');
}
if (!empty($vars)) {
$this->inlineScript('SN.V = ' . json_encode($vars));
}
return $vars;
}
/**
* If the action will need localizable text strings, export them here like so:
*
@ -638,7 +647,7 @@ class Action extends HTMLOutputter // lawsuit
$this->elementStart('a', array('class' => 'home bookmark',
'href' => $url));
if (StatusNet::isHTTPS()) {
if (GNUsocial::isHTTPS()) {
$logoUrl = common_config('site', 'ssllogo');
if (empty($logoUrl)) {
// if logo is an uploaded file, try to fall back to HTTPS file URL
@ -1144,7 +1153,7 @@ class Action extends HTMLOutputter // lawsuit
$image = common_config('license', 'image');
$sslimage = common_config('license', 'sslimage');
if (StatusNet::isHTTPS()) {
if (GNUsocial::isHTTPS()) {
if (!empty($sslimage)) {
$url = $sslimage;
} else if (preg_match('#^http://i.creativecommons.org/#', $image)) {
@ -1354,6 +1363,19 @@ class Action extends HTMLOutputter // lawsuit
}
}
/**
* This is a cheap hack to avoid a bug in DB_DataObject
* where '' is non-type-aware compared to 0, which means it
* will always be true for values like false and 0 too...
*
* Upstream bug is::
* https://pear.php.net/bugs/bug.php?id=20291
*/
function booleanintstring($key, $def=false)
{
return $this->boolean($key, $def) ? '1' : '0';
}
/**
* Integer value of an argument
*

View File

@ -160,10 +160,11 @@ abstract class ActivityHandlerPlugin extends Plugin
* @fixme are there any standard options?
*
* @param Activity $activity
* @param Profile $actor
* @param Notice $stored The notice in our database for this certain object
* @param array $options=array()
*
* @return Notice the resulting notice
* @return object If the verb handling plugin creates an object, it can be returned here (otherwise true)
* @throws exception On any error.
*/
protected function saveObjectFromActivity(Activity $activity, Notice $stored, array $options=array())
{
@ -174,7 +175,7 @@ abstract class ActivityHandlerPlugin extends Plugin
* This usually gets called from Notice::saveActivity after a Notice object has been created,
* so it contains a proper id and a uri for the object to be saved.
*/
public function onStoreActivityObject(Activity $act, Notice $stored, array $options=array(), &$object) {
public function onStoreActivityObject(Activity $act, Notice $stored, array $options, &$object) {
// $this->oldSaveNew is there during a migration period of plugins, to start using
// Notice::saveActivity instead of Notice::saveNew
if (!$this->isMyActivity($act) || isset($this->oldSaveNew)) {
@ -182,7 +183,13 @@ abstract class ActivityHandlerPlugin extends Plugin
}
$object = $this->saveObjectFromActivity($act, $stored, $options);
try {
$act->context->attention = array_merge($act->context->attention, $object->getAttentionArray());
// In the future we probably want to use something like ActivityVerb_DataObject for the kind
// of objects which are returned from saveObjectFromActivity.
if ($object instanceof Managed_DataObject) {
// If the verb handling plugin figured out some more attention URIs, add them here to the
// original activity. This is only done if a separate object is actually needed to be saved.
$act->context->attention = array_merge($act->context->attention, $object->getAttentionArray());
}
} catch (Exception $e) {
common_debug('WARNING: Could not get attention list from object '.get_class($object).'!');
}
@ -595,7 +602,6 @@ abstract class ActivityHandlerPlugin extends Plugin
$nli->showNoticeSource();
$nli->showNoticeLocation();
$nli->showPermalink();
$nli->showRepeat();
$nli->showNoticeOptions();
}

View File

@ -109,7 +109,7 @@ class ActivityImporter extends QueueHandler
// XXX: don't do this for untrusted input!
Subscription::start($otherProfile, $profile);
Subscription::ensureStart($otherProfile, $profile);
} else if (empty($activity->actor)
|| $activity->actor->id == $author->id) {
@ -123,7 +123,7 @@ class ActivityImporter extends QueueHandler
throw new ClientException(_('Unknown profile.'));
}
Subscription::start($profile, $otherProfile);
Subscription::ensureStart($profile, $otherProfile);
} else {
// TRANS: Client exception thrown when trying to import an event not related to the importing user.
throw new Exception(_('This activity seems unrelated to our user.'));
@ -213,7 +213,7 @@ class ActivityImporter extends QueueHandler
// Get (safe!) HTML and text versions of the content
$rendered = $this->purify($sourceContent);
$rendered = common_purify($sourceContent);
$content = common_strip_html($rendered);
$shortened = $user->shortenLinks($content);
@ -338,15 +338,4 @@ class ActivityImporter extends QueueHandler
return array($groups, $replies);
}
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

@ -146,7 +146,7 @@ class ActivityMover extends QueueHandler
"Changing sub to {$act->objects[0]->id}".
"by {$act->actor->id} to {$remote->nickname}.");
$otherProfile = $otherUser->getProfile();
Subscription::start($otherProfile, $remote);
Subscription::ensureStart($otherProfile, $remote);
Subscription::cancel($otherProfile, $user->getProfile());
} else {
$this->log(LOG_NOTICE,

View File

@ -512,11 +512,11 @@ class ActivityObject
switch (self::canonicalType($object->type)) {
case 'image':
$object->largerImage = $file->url;
$object->largerImage = $file->getUrl();
break;
case 'video':
case 'audio':
$object->stream = $file->url;
$object->stream = $file->getUrl();
break;
}
@ -861,7 +861,7 @@ class ActivityObject
if (is_string($this->thumbnail)) {
$object['image'] = array('url' => $this->thumbnail);
} else {
$object['image'] = array('url' => $this->thumbnail->url);
$object['image'] = array('url' => $this->thumbnail->getUrl());
if ($this->thumbnail->width) {
$object['image']['width'] = $this->thumbnail->width;
}

View File

@ -48,6 +48,7 @@ class ActivityVerb
const SHARE = 'http://activitystrea.ms/schema/1.0/share';
const SAVE = 'http://activitystrea.ms/schema/1.0/save';
const FAVORITE = 'http://activitystrea.ms/schema/1.0/favorite';
const LIKE = 'http://activitystrea.ms/schema/1.0/like'; // This is a synonym of favorite
const PLAY = 'http://activitystrea.ms/schema/1.0/play';
const FOLLOW = 'http://activitystrea.ms/schema/1.0/follow';
const FRIEND = 'http://activitystrea.ms/schema/1.0/make-friend';
@ -56,7 +57,8 @@ class ActivityVerb
// Custom OStatus verbs for the flipside until they're standardized
const DELETE = 'http://ostatus.org/schema/1.0/unfollow';
const UNFAVORITE = 'http://ostatus.org/schema/1.0/unfavorite';
const UNFAVORITE = 'http://activitystrea.ms/schema/1.0/unfavorite';
const UNLIKE = 'http://activitystrea.ms/schema/1.0/unlike'; // This is a synonym of unfavorite
const UNFOLLOW = 'http://ostatus.org/schema/1.0/unfollow';
const LEAVE = 'http://ostatus.org/schema/1.0/leave';
const UNTAG = 'http://ostatus.org/schema/1.0/untag';

View File

@ -59,24 +59,8 @@ class AdminPanelNav extends Menu
$nickname = $user->nickname;
$name = $user->getProfile()->getBestName();
// Stub section w/ home link
$this->action->elementStart('ul');
$this->action->elementStart('li');
// TRANS: Header in administrator navigation panel.
$this->action->element('h3', null, _m('HEADER','Home'));
$this->action->elementStart('ul', 'nav');
$this->out->menuItem(common_local_url('all', array('nickname' =>
$nickname)),
// TRANS: Menu item in administrator navigation panel.
_m('MENU','Home'),
// TRANS: Menu item title in administrator navigation panel.
// TRANS: %s is a username.
sprintf(_('%s and friends'), $name),
$this->action == 'all', 'nav_timeline_personal');
$this->action->elementEnd('ul');
$this->action->elementEnd('li');
$this->action->elementEnd('ul');
$stub = new HomeStubNav($this->action);
$this->submenu(_m('MENU','Home'), $stub);
$this->action->elementStart('ul');
$this->action->elementStart('li');

View File

@ -144,7 +144,7 @@ class ApiAction extends Action
*/
protected function prepare(array $args=array())
{
StatusNet::setApi(true); // reduce exception reports to aid in debugging
GNUsocial::setApi(true); // reduce exception reports to aid in debugging
parent::prepare($args);
$this->format = $this->arg('format');
@ -159,7 +159,7 @@ class ApiAction extends Action
$this->limit = $this->count + 1;
if ($this->arg('since')) {
header('X-StatusNet-Warning: since parameter is disabled; use since_id');
header('X-GNUsocial-Warning: since parameter is disabled; use since_id');
}
$this->source = $this->trimmed('source');
@ -264,22 +264,20 @@ class ApiAction extends Action
$twitter_user['statuses_count'] = $profile->noticeCount();
// Is the requesting user following this user?
// These values might actually also mean "unknown". Ambiguity issues?
$twitter_user['following'] = false;
$twitter_user['statusnet_blocking'] = false;
$twitter_user['notifications'] = false;
if (isset($this->auth_user)) {
$twitter_user['following'] = $this->auth_user->isSubscribed($profile);
$twitter_user['statusnet_blocking'] = $this->auth_user->hasBlocked($profile);
// Notifications on?
$sub = Subscription::pkeyGet(array('subscriber' =>
$this->auth_user->id,
'subscribed' => $profile->id));
if ($sub) {
if ($this->scoped instanceof Profile) {
try {
$sub = Subscription::getSubscription($this->scoped, $profile);
// Notifications on?
$twitter_user['following'] = true;
$twitter_user['statusnet_blocking'] = $this->scoped->hasBlocked($profile);
$twitter_user['notifications'] = ($sub->jabber || $sub->sms);
} catch (NoResultException $e) {
// well, the values are already false...
}
}
@ -305,6 +303,7 @@ class ApiAction extends Action
{
$base = $this->twitterSimpleStatusArray($notice, $include_user);
// FIXME: MOVE TO SHARE PLUGIN
if (!empty($notice->repeat_of)) {
$original = Notice::getKV('id', $notice->repeat_of);
if ($original instanceof Notice) {
@ -376,12 +375,6 @@ class ApiAction extends Action
$twitter_status['geo'] = null;
}
if (!is_null($this->scoped)) {
$twitter_status['repeated'] = $this->scoped->hasRepeated($notice);
} else {
$twitter_status['repeated'] = false;
}
// Enclosures
$attachments = $notice->attachments();
@ -430,11 +423,11 @@ class ApiAction extends Action
$twitter_group['nickname'] = $group->nickname;
$twitter_group['fullname'] = $group->fullname;
if (isset($this->auth_user)) {
$twitter_group['member'] = $this->auth_user->isMember($group);
if ($this->scoped instanceof Profile) {
$twitter_group['member'] = $this->scoped->isMember($group);
$twitter_group['blocked'] = Group_block::isBlocked(
$group,
$this->auth_user->getProfile()
$this->scoped
);
}
@ -485,8 +478,8 @@ class ApiAction extends Action
$twitter_list['member_count'] = $list->taggedCount();
$twitter_list['uri'] = $list->getUri();
if (isset($this->auth_user)) {
$twitter_list['following'] = $list->hasSubscriber($this->auth_user);
if ($this->scoped instanceof Profile) {
$twitter_list['following'] = $list->hasSubscriber($this->scoped);
} else {
$twitter_list['following'] = false;
}
@ -575,37 +568,30 @@ class ApiAction extends Action
$relationship = array();
$relationship['source'] =
$this->relationshipDetailsArray($source, $target);
$this->relationshipDetailsArray($source->getProfile(), $target->getProfile());
$relationship['target'] =
$this->relationshipDetailsArray($target, $source);
$this->relationshipDetailsArray($target->getProfile(), $source->getProfile());
return array('relationship' => $relationship);
}
function relationshipDetailsArray($source, $target)
function relationshipDetailsArray(Profile $source, Profile $target)
{
$details = array();
$source_profile = $source->getProfile();
$target_profile = $target->getProfile();
$details['screen_name'] = $source->getNickname();
$details['followed_by'] = $target->isSubscribed($source);
$details['screen_name'] = $source->nickname;
$details['followed_by'] = $target->isSubscribed($source_profile);
$details['following'] = $source->isSubscribed($target_profile);
$notifications = false;
if ($source->isSubscribed($target_profile)) {
$sub = Subscription::pkeyGet(array('subscriber' =>
$source->id, 'subscribed' => $target->id));
if (!empty($sub)) {
$notifications = ($sub->jabber || $sub->sms);
}
try {
$sub = Subscription::getSubscription($source, $target);
$details['following'] = true;
$details['notifications_enabled'] = ($sub->jabber || $sub->sms);
} catch (NoResultException $e) {
$details['following'] = false;
$details['notifications_enabled'] = false;
}
$details['notifications_enabled'] = $notifications;
$details['blocking'] = $source->hasBlocked($target_profile);
$details['blocking'] = $source->hasBlocked($target);
$details['id'] = intval($source->id);
return $details;
@ -655,6 +641,7 @@ class ApiAction extends Action
$this->showGeoXML($value);
break;
case 'retweeted_status':
// FIXME: MOVE TO SHARE PLUGIN
$this->showTwitterXmlStatus($value, 'retweeted_status');
break;
default:
@ -788,7 +775,7 @@ class ApiAction extends Action
function showSingleAtomStatus($notice)
{
header('Content-Type: application/atom+xml; charset=utf-8');
print $notice->asAtomEntry(true, true, true, $this->auth_user->getProfile());
print $notice->asAtomEntry(true, true, true, $this->scoped);
}
function show_single_json_status($notice)
@ -1352,7 +1339,7 @@ class ApiAction extends Action
return User::getKV('nickname', $nickname);
} else {
// Fall back to trying the currently authenticated user
return $this->auth_user;
return $this->scoped->getUser();
}
} else if (self::is_decimal($id)) {
@ -1448,7 +1435,7 @@ class ApiAction extends Action
}
if (!empty($list) && $list->private) {
if ($this->auth_user->id == $list->tagger) {
if ($this->scoped->id == $list->tagger) {
return $list;
}
} else {
@ -1516,6 +1503,11 @@ class ApiAction extends Action
$aargs['id'] = $id;
}
$user = $this->arg('user');
if (!empty($user)) {
$aargs['user'] = $user;
}
$tag = $this->arg('tag');
if (!empty($tag)) {
$aargs['tag'] = $tag;

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