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 CleanupPlugin: a chance to cleanup a plugin at the end of a program
StartActionExecute: Right before the "prepare" call of the current Action
- $action: the current Action object
- &$args: array of arguments, referenced so you can modify the array
EndActionExecute: Right after the "handle" call of the current Action
- $action: the current Action object
StartPrimaryNav: Showing the primary nav menu StartPrimaryNav: Showing the primary nav menu
- $action: the current action - $action: the current action
@ -1444,6 +1451,9 @@ StartResizeImageFile: Hook to resize an image and output it to a file. No matchi
- $outpath: string with output filepath - $outpath: string with output filepath
- $box: array with size ('width', 'height') and boundary box('x', 'y', 'w', 'h'). - $box: array with size ('width', 'height') and boundary box('x', 'y', 'w', 'h').
FillImageFileMetadata: Get more metadata about the ImageFile if it is perhaps not a real local file
- $imagefile ImageFile object which we're getting metadata for (such as animated status, width/height etc.)
StartShowAttachmentRepresentation: Attachment representation, full file (or in rare cases thumbnails/previews). StartShowAttachmentRepresentation: Attachment representation, full file (or in rare cases thumbnails/previews).
- $out: HTMLOutputter class to use for outputting HTML. - $out: HTMLOutputter class to use for outputting HTML.
- $file: 'File' object which we're going to show representation for. - $file: 'File' object which we're going to show representation for.
@ -1463,3 +1473,27 @@ StartNotifyMentioned: During notice distribution, we send notifications (email,
EndNotifyMentioned: During notice distribution, we send notifications (email, im...) to the profiles who were somehow mentioned. EndNotifyMentioned: During notice distribution, we send notifications (email, im...) to the profiles who were somehow mentioned.
- $stored: Notice object that is being distributed. - $stored: Notice object that is being distributed.
- $mentioned_ids: Array of profile IDs (not just for local users) who got mentioned by the notice. - $mentioned_ids: Array of profile IDs (not just for local users) who got mentioned by the notice.
StartHomeStubNavItems: Go back Home nav items. Default includes just one item 'home'
- $out: HTMLOutputter used to output (usually an Action, but not always!)
- &$items: Referenced array of items in the nav (add if desired)
EndHomeStubNavItems:
- $out: HTMLOutputter used to output (usually an Action, but not always!)
- $items: array of menu items
StartSubMenu: Before outputting a submenu (including enclosing tags) to HTML
- $out: HTMLOutputter used to output (usually an Action, but not always!)
- $menu: The Menu object outputted as a submenu.
- $label: Localized text which represents the menu item.
EndSubMenu: After outputting a submenu (including enclosing tags) to HTML
- $out: HTMLOutputter used to output (usually an Action, but not always!)
- $menu: The Menu object outputted as a submenu.
- $label: Localized text which represents the menu item.
StartDocNav: Before outputting the docs Nav
- $nav: The DoclNav widget
EndDocNav: After outputting the docs Nav
- $nav: The DoclNav widget

238
INSTALL
View File

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

View File

@ -1,5 +1,5 @@
# GNU social 1.1.3 # GNU social 1.2.x
February 2015-02-27 2015
(c) Free Software Foundation, Inc (c) Free Software Foundation, Inc
(c) StatusNet, Inc (c) StatusNet, Inc
@ -100,15 +100,19 @@ for additional terms.
## New this version ## New this version
This is a security fix and bug fix release since 1.1.3-beta2. This is the development branch for the 1.2.x version of GNU social.
All 1.1.x sites should upgrade to this version. All daring 1.1.x admins should upgrade to this version.
So far it includes the following changes: So far it includes the following changes:
- Backing up a user's account is more and more complete.
- Emojis 😸 (utf8mb4 support)
The last release, 1.1.3, gave us these improvements:
- XSS security fix (thanks Simon Waters, <https://www.surevine.com/>) - XSS security fix (thanks Simon Waters, <https://www.surevine.com/>)
- Many improvements to ease adoption of the Qvitter front-end <https://github.com/hannesmannerheim/qvitter> - Many improvements to ease adoption of the Qvitter front-end <https://github.com/hannesmannerheim/qvitter>
- Protocol adaptions for improved performance and stability - Protocol adaptions for improved performance and stability
- Backing up a user's account now appears to work as it should
Upgrades from _StatusNet_ 1.1.1 will also experience these improvements: Upgrades from _StatusNet_ 1.1.1 will also experience these improvements:
@ -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 recommended to use git as a means to stay up to date
with the source code. You can choose between these with the source code. You can choose between these
branches: branches:
- 1.1.x "stable", few updates, well tested code - 1.2.x "stable", few updates, well tested code
- master "testing", more updates, usually working well - master "testing", more updates, usually working well
- nightly "unstable", most updates, not always working - nightly "unstable", most updates, not always working

157
UPGRADE
View File

@ -1,99 +1,98 @@
Upgrading Upgrading
========= =========
StatusNet 1.1.1 to GNU social GNU social 1.1.x to GNU social 1.2.x
----------------------------- ------------------------------------
If you are tracking the GNU social git repository, we currently recommend
using the "master" branch (or nightly if you want to use latest features)
and follow this procedure:
0. Backup your data. The StatusNet upgrade discussions below have some
guidelines to back up the database and files (mysqldump and rsync).
1. Stop your queue daemons (you can run this command even if you do not
use the queue daemons):
$ bash scripts/stopdaemons.sh
2. Run the command to fetch the latest sourcecode:
$ git pull
If you are not using git we recommend following the instructions below
for upgrading "StatusNet 1.1.x to GNU social 1.2.x" as they are similar.
3. Run the upgrade script:
$ php scripts/upgrade.php
The upgrade script will likely take a long time because it will
upgrade the tables to another character encoding and make other
automated upgrades. Make sure it ends without errors. If you get
errors, create a new task on https://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 We cannot support migrating from any other version of StatusNet than
1.1.1. If you are running a StatusNet version lower than this, please 1.1.1. If you are running a StatusNet version lower than this, please
follow the upgrade procedures for each respective StatusNet version. follow the upgrade procedures for each respective StatusNet version.
You are now running StatusNet 1.1.1 and want to migrate to GNU social. You are now running StatusNet 1.1.1 and want to migrate to GNU social
Beware there may be changes in minimum required version of PHP and the 1.2.x. Beware there may be changes in minimum required version of PHP
modules used, so double-check the INSTALL file's requirements list. and the modules required, so review the INSTALL file (php5-intl is a
newly added dependency for example).
Before you begin: Make backups. Always make backups. Of your entire * Before you begin: Make backups. Always make backups. Of your entire
directory structure and the database too. All tables. All data. Alles. directory structure and the database too. All tables. All data. Alles.
0. Stop your queue daemons 'php scripts/stopdaemon.php' should do it. 0. Make a backup of everything. To backup the database, you can use a
Not everyone runs queue daemons, but the above command won't hurt. 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:
avatar/* 2. Unpack your GNU social code to a fresh directory. You can do this
background/* by cloning our git repository:
file/* $ git clone https://gitorious.org/social/mainline.git gnusocial
local/*
.htaccess
config.php
3. Replace your old StatusNet directory with the new GNU social 3. Synchronize your local files to the GNU social directory. These
directory in your webserver root. will be the local files such as avatars, config and files:
4. Run the upgrade script: 'php scripts/upgrade.php'
5. Start your queue daemons: 'php scripts/startdaemons.php'
6. Report any issues at https://bugz.foocorp.net/ (tag GNU social)
avatar/*
background/*
file/*
local/*
.htaccess
config.php
Legacy StatusNet instructions 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/
These instructions are here for historical and perhaps informational 4. Replace your old StatusNet directory with the new GNU social
purposes. directory in your webserver root.
If you've been using StatusNet 1.0 or lower, or if you've 5. Run the upgrade script: 'php scripts/upgrade.php'
been tracking the "git" version of the software, you will probably The upgrade script will likely take a long time because it will
want to upgrade and keep your existing data. Try these step-by-step upgrade the tables to another character encoding and make other
instructions; read to the end first before trying them. automated upgrades. Make sure it ends without errors. If you get
errors, create a new task on https://bugz.foocorp.net/
0. Download StatusNet and set up all the prerequisites as if you were 6. Start your queue daemons: 'bash scripts/startdaemons.sh'
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 7. Report any issues at https://bugz.foocorp.net/ (tag GNU social)
reversed. YOU CAN EASILY DESTROY YOUR SITE WITH THIS STEP. Don't
do it without a known-good backup!
In your new StatusNet 1.1.1 directory and AFTER YOU MAKE A
BACKUP run the upgrade.php script like this:
php ./scripts/upgrade.php
11. Use mysql or psql client to log into your database and make sure that
the notice, user, profile, subscription etc. tables are non-empty.
12. Turn back on the Web server, and check that things still work.
13. Turn back on XMPP bots and email maildaemon.
NOTE: the 1.0.0 version of StatusNet changed the URLs for all admin
panels from /admin/* to /panel/*. This now allows the (popular)
username 'admin', but blocks the considerably less popular username
'panel'. If you have an existing user named 'panel', you should rename
them before upgrading.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -79,6 +79,10 @@ class ApiTimelineUserAction extends ApiBareAuthAction
$this->clientError(_('No such user.'), 404); $this->clientError(_('No such user.'), 404);
} }
if (!$this->target->isLocal()) {
$this->serverError(_('Remote user timelines are not available here yet.'), 501);
}
$this->notices = $this->getNotices(); $this->notices = $this->getNotices();
return true; return true;
@ -405,7 +409,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
// Get (safe!) HTML and text versions of the content // Get (safe!) HTML and text versions of the content
$rendered = $this->purify($sourceContent); $rendered = common_purify($sourceContent);
$content = common_strip_html($rendered); $content = common_strip_html($rendered);
$shortened = $this->auth_user->shortenLinks($content); $shortened = $this->auth_user->shortenLinks($content);
@ -504,13 +508,4 @@ class ApiTimelineUserAction extends ApiBareAuthAction
return $saved; return $saved;
} }
function purify($content)
{
require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
$config = array('safe' => 1,
'deny_attribute' => 'id,style,on*');
return htmLawed($content, $config);
}
} }

View File

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

View File

@ -62,6 +62,6 @@ class Attachment_thumbnailAction extends AttachmentAction
common_redirect($e->file->getUrl()); 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/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
define('MAX_ORIGINAL', 480);
/** /**
* Upload an avatar * Upload an avatar
@ -369,13 +363,27 @@ class AvatarsettingsAction extends SettingsAction
$dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0; $dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0;
$dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$file_d; $dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$file_d;
$dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$file_d; $dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$file_d;
$size = intval(min($dest_w, $dest_h, MAX_ORIGINAL)); $size = intval(min($dest_w, $dest_h, common_config('avatar', 'maxsize')));
$box = array('width' => $size, 'height' => $size,
'x' => $dest_x, 'y' => $dest_y,
'w' => $dest_w, 'h' => $dest_h);
$user = common_current_user(); $user = common_current_user();
$profile = $user->getProfile(); $profile = $user->getProfile();
$imagefile = new ImageFile($user->id, $filedata['filepath']); $imagefile = new ImageFile(null, $filedata['filepath']);
$filename = $imagefile->resize($size, $dest_x, $dest_y, $dest_w, $dest_h); $filename = Avatar::filename($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)) { if ($profile->setOriginal($filename)) {
@unlink($filedata['filepath']); @unlink($filedata['filepath']);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -82,8 +82,8 @@ class ProfilesettingsAction extends SettingsAction
*/ */
function showContent() function showContent()
{ {
$user = common_current_user(); $profile = $this->scoped;
$profile = $user->getProfile(); $user = $this->scoped->getUser();
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_profile', 'id' => 'form_settings_profile',
@ -104,7 +104,9 @@ class ProfilesettingsAction extends SettingsAction
// TRANS: Tooltip for field label in form for profile settings. // TRANS: Tooltip for field label in form for profile settings.
_('1-64 lowercase letters or numbers, no punctuation or spaces.'), _('1-64 lowercase letters or numbers, no punctuation or spaces.'),
null, false, // "name" (will be set to id), then "required" null, false, // "name" (will be set to id), then "required"
!common_config('profile', 'changenick') ? array('disabled'=>'disabled') : array()); !common_config('profile', 'changenick')
? array('disabled' => 'disabled', 'placeholder' => null)
: array('placeholder' => null));
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings. // TRANS: Field label in form for profile settings.
@ -260,9 +262,9 @@ class ProfilesettingsAction extends SettingsAction
$homepage = $this->trimmed('homepage'); $homepage = $this->trimmed('homepage');
$bio = $this->trimmed('bio'); $bio = $this->trimmed('bio');
$location = $this->trimmed('location'); $location = $this->trimmed('location');
$autosubscribe = $this->boolean('autosubscribe'); $autosubscribe = $this->booleanintstring('autosubscribe');
$subscribe_policy = $this->trimmed('subscribe_policy'); $subscribe_policy = $this->trimmed('subscribe_policy');
$private_stream = $this->boolean('private_stream'); $private_stream = $this->booleanintstring('private_stream');
$language = $this->trimmed('language'); $language = $this->trimmed('language');
$timezone = $this->trimmed('timezone'); $timezone = $this->trimmed('timezone');
$tagstring = $this->trimmed('tags'); $tagstring = $this->trimmed('tags');
@ -398,7 +400,7 @@ class ProfilesettingsAction extends SettingsAction
$orig = clone($prefs); $orig = clone($prefs);
} }
$prefs->share_location = $this->boolean('sharelocation'); $prefs->share_location = $this->booleanintstring('sharelocation');
if ($exists) { if ($exists) {
$result = $prefs->update($orig); $result = $prefs->update($orig);

View File

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

View File

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

View File

@ -28,15 +28,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('GNUSOCIAL')) { exit(1); }
exit(1);
}
require_once INSTALLDIR.'/lib/personalgroupnav.php';
require_once INSTALLDIR.'/lib/noticelist.php';
require_once INSTALLDIR.'/lib/profileminilist.php';
require_once INSTALLDIR.'/lib/groupminilist.php';
require_once INSTALLDIR.'/lib/feedlist.php';
/** /**
* User profile page * User profile page
@ -233,10 +225,7 @@ class ShowstreamAction extends ProfileAction
function showNotices() function showNotices()
{ {
$pnl = null; $pnl = new NoticeList($this->notice, $this);
if (Event::handle('ShowStreamNoticeList', array($this->notice, $this, &$pnl))) {
$pnl = new ProfileNoticeList($this->notice, $this);
}
$cnt = $pnl->show(); $cnt = $pnl->show();
if (0 == $cnt) { if (0 == $cnt) {
$this->showEmptyListMessage(); $this->showEmptyListMessage();
@ -293,57 +282,3 @@ class ShowstreamAction extends ProfileAction
return $options; return $options;
} }
} }
// We don't show the author for a profile, since we already know who it is!
/**
* Slightly modified from standard list; the author & avatar are hidden
* in CSS. We used to remove them here too, but as it turns out that
* confuses the inline reply code... and we hide them in CSS anyway
* since realtime updates come through in original form.
*
* Remaining customization right now is for the repeat marker, where
* it'll list who the original poster was instead of who did the repeat
* (since the repeater is you, and the repeatee isn't shown!)
* This will remain inconsistent if realtime updates come through,
* since those'll get rendered as a regular NoticeListItem.
*/
class ProfileNoticeList extends NoticeList
{
function newListItem($notice)
{
return new ProfileNoticeListItem($notice, $this->out);
}
}
class ProfileNoticeListItem extends DoFollowListItem
{
/**
* show a link to the author of repeat
*
* @return void
*/
function showRepeat()
{
if (!empty($this->repeat)) {
// FIXME: this code is almost identical to default; need to refactor
$attrs = array('href' => $this->profile->profileurl,
'class' => 'url');
if (!empty($this->profile->fullname)) {
$attrs['title'] = $this->profile->getFancyName();
}
$this->out->elementStart('span', 'repeat');
$text_link = XMLStringer::estring('a', $attrs, $this->profile->nickname);
// TRANS: Link to the author of a repeated notice. %s is a linked nickname.
$this->out->raw(sprintf(_('Repeat of %s'), $text_link));
$this->out->elementEnd('span');
}
}
}

View File

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

View File

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

View File

@ -22,7 +22,7 @@ class Attention extends Managed_DataObject
public $__table = 'attention'; // table name public $__table = 'attention'; // table name
public $notice_id; // int(4) primary_key not_null public $notice_id; // int(4) primary_key not_null
public $profile_id; // int(4) primary_key not_null public $profile_id; // int(4) primary_key not_null
public $reason; // varchar(255) public $reason; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime() not_null public $created; // datetime() not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@ -33,7 +33,7 @@ class Attention extends Managed_DataObject
'fields' => array( 'fields' => array(
'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice_id to give attention'), 'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice_id to give attention'),
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile_id for feed receiver'), 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile_id for feed receiver'),
'reason' => array('type' => 'varchar', 'length' => 255, 'description' => 'Optional reason why this was brought to the attention of profile_id'), 'reason' => array('type' => 'varchar', 'length' => 191, 'description' => 'Optional reason why this was brought to the attention of profile_id'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
), ),

View File

@ -15,8 +15,8 @@ class Avatar extends Managed_DataObject
public $width; // int(4) primary_key not_null public $width; // int(4) primary_key not_null
public $height; // int(4) primary_key not_null public $height; // int(4) primary_key not_null
public $mediatype; // varchar(32) not_null public $mediatype; // varchar(32) not_null
public $filename; // varchar(255) public $filename; // varchar(191) not 255 because utf8mb4 takes more space
public $url; // varchar(255) unique_key public $url; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $created; // datetime() not_null public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -32,14 +32,14 @@ class Avatar extends Managed_DataObject
'width' => array('type' => 'int', 'not null' => true, 'description' => 'image width'), 'width' => array('type' => 'int', 'not null' => true, 'description' => 'image width'),
'height' => array('type' => 'int', 'not null' => true, 'description' => 'image height'), 'height' => array('type' => 'int', 'not null' => true, 'description' => 'image height'),
'mediatype' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'file type'), 'mediatype' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'file type'),
'filename' => array('type' => 'varchar', 'length' => 255, 'description' => 'local filename, if local'), 'filename' => array('type' => 'varchar', 'length' => 191, 'description' => 'local filename, if local'),
'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'avatar location'), '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'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
), ),
'primary key' => array('profile_id', 'width', 'height'), 'primary key' => array('profile_id', 'width', 'height'),
'unique keys' => array( 'unique keys' => array(
'avatar_url_key' => array('url'), // 'avatar_filename_key' => array('filename'),
), ),
'foreign keys' => array( 'foreign keys' => array(
'avatar_profile_id_fkey' => array('profile', array('profile_id' => 'id')), 'avatar_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
@ -241,16 +241,21 @@ class Avatar extends Managed_DataObject
// TRANS: An error message when avatar size is unreasonable // TRANS: An error message when avatar size is unreasonable
throw new Exception(_m('Avatar size too large')); throw new Exception(_m('Avatar size too large'));
} }
// So far we only have square avatars and I don't have time to
// rewrite support for non-square ones right now ;)
$height = $width;
$original = Avatar::getUploaded($target); $original = Avatar::getUploaded($target);
$imagefile = new ImageFile($target->id, Avatar::path($original->filename)); $imagefile = new ImageFile(null, Avatar::path($original->filename));
$filename = $imagefile->resize($width); $filename = Avatar::filename($target->getID(), image_type_to_extension($imagefile->preferredType()),
$width, common_timestamp());
$imagefile->resizeTo(Avatar::path($filename), array('width'=>$width, 'height'=>$height));
$scaled = clone($original); $scaled = clone($original);
$scaled->original = false; $scaled->original = false;
$scaled->width = $width; $scaled->width = $width;
$scaled->height = $width; $scaled->height = $height;
$scaled->url = Avatar::url($filename); $scaled->url = Avatar::url($filename);
$scaled->filename = $filename; $scaled->filename = $filename;
$scaled->created = common_sql_now(); $scaled->created = common_sql_now();

View File

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

View File

@ -12,8 +12,8 @@ class Confirm_address extends Managed_DataObject
public $__table = 'confirm_address'; // table name public $__table = 'confirm_address'; // table name
public $code; // varchar(32) primary_key not_null public $code; // varchar(32) primary_key not_null
public $user_id; // int(4) not_null public $user_id; // int(4) not_null
public $address; // varchar(255) not_null public $address; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $address_extra; // varchar(255) not_null public $address_extra; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $address_type; // varchar(8) not_null public $address_type; // varchar(8) not_null
public $claimed; // datetime() public $claimed; // datetime()
public $sent; // datetime() public $sent; // datetime()
@ -28,8 +28,8 @@ class Confirm_address extends Managed_DataObject
'fields' => array( 'fields' => array(
'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'good random code'), 'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'good random code'),
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user who requested confirmation'), 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user who requested confirmation'),
'address' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'address (email, xmpp, SMS, etc.)'), 'address' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'address (email, xmpp, SMS, etc.)'),
'address_extra' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'carrier ID, for SMS'), 'address_extra' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'carrier ID, for SMS'),
'address_type' => array('type' => 'varchar', 'length' => 8, 'not null' => true, 'description' => 'address type ("email", "xmpp", "sms")'), 'address_type' => array('type' => 'varchar', 'length' => 8, 'not null' => true, 'description' => 'address type ("email", "xmpp", "sms")'),
'claimed' => array('type' => 'datetime', 'description' => 'date this was claimed for queueing'), 'claimed' => array('type' => 'datetime', 'description' => 'date this was claimed for queueing'),
'sent' => array('type' => 'datetime', 'description' => 'date this was sent for queueing'), 'sent' => array('type' => 'datetime', 'description' => 'date this was sent for queueing'),

View File

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

View File

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

View File

@ -34,7 +34,7 @@ class Deleted_notice extends Managed_DataObject
public $__table = 'deleted_notice'; // table name public $__table = 'deleted_notice'; // table name
public $id; // int(4) primary_key not_null public $id; // int(4) primary_key not_null
public $profile_id; // int(4) 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 $created; // datetime() not_null
public $deleted; // datetime() not_null public $deleted; // datetime() not_null
@ -47,7 +47,7 @@ class Deleted_notice extends Managed_DataObject
'fields' => array( 'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'identity of notice'), 'id' => array('type' => 'int', 'not null' => true, 'description' => 'identity of notice'),
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'author of the 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'), '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'), '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 $__table = 'file'; // table name
public $id; // int(4) primary_key not_null public $id; // int(4) primary_key not_null
public $url; // varchar(255) unique_key public $urlhash; // varchar(64) unique_key
public $url; // text
public $filehash; // varchar(64) indexed
public $mimetype; // varchar(50) public $mimetype; // varchar(50)
public $size; // int(4) public $size; // int(4)
public $title; // varchar(255) public $title; // varchar(191) not 255 because utf8mb4 takes more space
public $date; // int(4) public $date; // int(4)
public $protected; // 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 $width; // int(4)
public $height; // int(4) public $height; // int(4)
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
const URLHASH_ALG = 'sha256';
const FILEHASH_ALG = 'sha256';
public static function schemaDef() public static function schemaDef()
{ {
return array( return array(
'fields' => array( 'fields' => array(
'id' => array('type' => 'serial', 'not null' => true), 'id' => array('type' => 'serial', 'not null' => true),
'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'destination URL after following redirections'), 'urlhash' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'sha256 of destination URL (url field)'),
'url' => array('type' => 'text', 'description' => 'destination URL after following possible redirections'),
'filehash' => array('type' => 'varchar', 'length' => 64, 'not null' => false, 'description' => 'sha256 of the file contents, only for locally stored files of course'),
'mimetype' => array('type' => 'varchar', 'length' => 50, 'description' => 'mime type of resource'), 'mimetype' => array('type' => 'varchar', 'length' => 50, 'description' => 'mime type of resource'),
'size' => array('type' => 'int', 'description' => 'size of resource when available'), 'size' => array('type' => 'int', 'description' => 'size of resource when available'),
'title' => array('type' => 'varchar', 'length' => 255, 'description' => 'title of resource when available'), 'title' => array('type' => 'varchar', 'length' => 191, 'description' => 'title of resource when available'),
'date' => array('type' => 'int', 'description' => 'date of resource according to http query'), 'date' => array('type' => 'int', 'description' => 'date of resource according to http query'),
'protected' => array('type' => 'int', 'description' => 'true when URL is private (needs login)'), 'protected' => array('type' => 'int', 'description' => 'true when URL is private (needs login)'),
'filename' => array('type' => 'varchar', 'length' => 255, 'description' => 'if a local file, name of the file'), 'filename' => array('type' => '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'), 'width' => array('type' => 'int', 'description' => 'width in pixels, if it can be described as such and data is available'),
'height' => array('type' => 'int', 'description' => 'height in pixels, if it can be described as such and data is available'), 'height' => array('type' => 'int', 'description' => 'height in pixels, if it can be described as such and data is available'),
@ -56,7 +63,10 @@ class File extends Managed_DataObject
), ),
'primary key' => array('id'), 'primary key' => array('id'),
'unique keys' => array( 'unique keys' => array(
'file_url_key' => array('url'), 'file_urlhash_key' => array('urlhash'),
),
'indexes' => array(
'file_filehash_idx' => array('filehash'),
), ),
); );
} }
@ -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 // I don't know why we have to keep doing this but I'm adding this last check to avoid
// uniqueness bugs. // uniqueness bugs.
$file = File::getKV('url', $given_url); $file = File::getKV('urlhash', self::hashurl($given_url));
if (!$file instanceof File) { if (!$file instanceof File) {
$file = new File; $file = new File;
$file->urlhash = self::hashurl($given_url);
$file->url = $given_url; $file->url = $given_url;
if (!empty($redir_data['protected'])) $file->protected = $redir_data['protected']; if (!empty($redir_data['protected'])) $file->protected = $redir_data['protected'];
if (!empty($redir_data['title'])) $file->title = $redir_data['title']; 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'); throw new ServerException('No canonical URL from given URL to process');
} }
$file = File::getKV('url', $given_url); $file = null;
if (!$file instanceof File) {
try {
$file = File::getByUrl($given_url);
} catch (NoResultException $e) {
// First check if we have a lookup trace for this URL already // First check if we have a lookup trace for this URL already
$file_redir = File_redirection::getKV('url', $given_url); try {
if ($file_redir instanceof File_redirection) { $file_redir = File_redirection::getByUrl($given_url);
$file = File::getKV('id', $file_redir->file_id); $file = File::getKV('id', $file_redir->file_id);
if (!$file instanceof File) { if (!$file instanceof File) {
// File did not exist, let's clean up the File_redirection entry // File did not exist, let's clean up the File_redirection entry
$file_redir->delete(); $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 ($redir_url === $given_url || !$followRedirects) {
if (!$file instanceof File) { // Save the File object based on our lookup trace
// @fixme for new URLs this also looks up non-redirect data $file = File::saveNew($redir_data, $given_url);
// such as target content type, size, etc, which we need } else {
// for File::saveNew(); so we call it even if not following // This seems kind of messed up... for now skipping this part
// new redirects. // if we're already under a redirect, so we don't go into
$redir_data = File_redirection::where($given_url); // horrible infinite loops if we've been given an unstable
if (is_array($redir_data)) { // redirect (where the final destination of the first request
$redir_url = $redir_data['url']; // doesn't match what we get when we ask for it again).
} elseif (is_string($redir_data)) { //
$redir_url = $redir_data; // Seen in the wild with clojure.org, which redirects through
$redir_data = array(); // wikispaces for auth and appends session data in the URL params.
} else { $file = self::processNew($redir_url, $notice_id, /*followRedirects*/false);
// TRANS: Server exception thrown when a URL cannot be processed. File_redirection::saveNew($redir_data, $file->id, $given_url);
throw new ServerException(sprintf(_("Cannot process URL '%s'"), $given_url));
}
// TODO: max field length
if ($redir_url === $given_url || strlen($redir_url) > 255 || !$followRedirects) {
// Save the File object based on our lookup trace
$file = File::saveNew($redir_data, $given_url);
} else {
// This seems kind of messed up... for now skipping this part
// if we're already under a redirect, so we don't go into
// horrible infinite loops if we've been given an unstable
// redirect (where the final destination of the first request
// doesn't match what we get when we ask for it again).
//
// Seen in the wild with clojure.org, which redirects through
// wikispaces for auth and appends session data in the URL params.
$file = self::processNew($redir_url, $notice_id, /*followRedirects*/false);
File_redirection::saveNew($redir_data, $file->id, $given_url);
}
} }
if (!$file instanceof File) { if (!$file instanceof File) {
@ -237,12 +253,7 @@ class File extends Managed_DataObject
static function filename(Profile $profile, $origname, $mimetype) static function filename(Profile $profile, $origname, $mimetype)
{ {
try { $ext = self::guessMimeExtension($mimetype);
$ext = common_supported_mime_to_ext($mimetype);
} catch (Exception $e) {
// We don't support this mimetype, but let's guess the extension
$ext = substr(strrchr($mimetype, '/'), 1);
}
// Normalize and make the original filename more URL friendly. // Normalize and make the original filename more URL friendly.
$origname = basename($origname, ".$ext"); $origname = basename($origname, ".$ext");
@ -263,6 +274,17 @@ class File extends Managed_DataObject
return $filename; return $filename;
} }
static function guessMimeExtension($mimetype)
{
try {
$ext = common_supported_mime_to_ext($mimetype);
} catch (Exception $e) {
// We don't support this mimetype, but let's guess the extension
$ext = substr(strrchr($mimetype, '/'), 1);
}
return strtolower($ext);
}
/** /**
* Validation for as-saved base filenames * 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'); $sslserver = common_config('attachments', 'sslserver');
@ -381,6 +403,10 @@ class File extends Managed_DataObject
* @param $crop bool Crop to the max-values' aspect ratio * @param $crop bool Crop to the max-values' aspect ratio
* *
* @return File_thumbnail * @return File_thumbnail
*
* @throws UseFileAsThumbnailException if the file is considered an image itself and should be itself as thumbnail
* @throws UnsupportedMediaException if, despite trying, we can't understand how to make a thumbnail for this format
* @throws ServerException on various other errors
*/ */
public function getThumbnail($width=null, $height=null, $crop=false, $force_still=true) public function getThumbnail($width=null, $height=null, $crop=false, $force_still=true)
{ {
@ -394,67 +420,16 @@ class File extends Managed_DataObject
} }
} }
if ($width === null) { return $image->getFileThumbnail($width, $height, $crop);
$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);
} }
public function getPath() public function getPath()
{ {
return self::path($this->filename); $filepath = self::path($this->filename);
if (!file_exists($filepath)) {
throw new FileNotFoundException($filepath);
}
return $filepath;
} }
public function getUrl() public function getUrl()
@ -462,7 +437,7 @@ class File extends Managed_DataObject
if (!empty($this->filename)) { if (!empty($this->filename)) {
// A locally stored file, so let's generate a URL for our instance. // A locally stored file, so let's generate a URL for our instance.
$url = self::url($this->filename); $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. // 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 // also we're fixing possible changes from http to https, or paths
$this->updateUrl($url); $this->updateUrl($url);
@ -474,16 +449,40 @@ class File extends Managed_DataObject
return $this->url; return $this->url;
} }
static public function getByUrl($url)
{
$file = new File();
$file->urlhash = self::hashurl($url);
if (!$file->find(true)) {
throw new NoResultException($file);
}
return $file;
}
/**
* @param string $hashstr String of (preferrably lower case) hexadecimal characters, same as result of 'hash_file(...)'
*/
static public function getByHash($hashstr, $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) public function updateUrl($url)
{ {
$file = File::getKV('url', $url); $file = File::getKV('urlhash', self::hashurl($url));
if ($file instanceof File) { if ($file instanceof File) {
throw new ServerException('URL already exists in DB'); throw new ServerException('URL already exists in DB');
} }
$sql = 'UPDATE %1$s SET url=%2$s WHERE url=%3$s;'; $sql = 'UPDATE %1$s SET urlhash=%2$s, url=%3$s WHERE urlhash=%4$s;';
$result = $this->query(sprintf($sql, $this->__table, $result = $this->query(sprintf($sql, $this->__table,
$this->_quote((string)self::hashurl($url)),
$this->_quote((string)$url), $this->_quote((string)$url),
$this->_quote((string)$this->url))); $this->_quote((string)$this->urlhash)));
if ($result === false) { if ($result === false) {
common_log_db_error($this, 'UPDATE', __FILE__); common_log_db_error($this, 'UPDATE', __FILE__);
throw new ServerException("Could not UPDATE {$this->__table}.url"); throw new ServerException("Could not UPDATE {$this->__table}.url");
@ -502,9 +501,9 @@ class File extends Managed_DataObject
function blowCache($last=false) function blowCache($last=false)
{ {
self::blow('file:notice-ids:%s', $this->url); self::blow('file:notice-ids:%s', $this->urlhash);
if ($last) { 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); self::blow('file:notice-count:%d', $this->id);
} }
@ -582,4 +581,54 @@ class File extends Managed_DataObject
return $title ?: null; return $title ?: null;
} }
static public function hashurl($url)
{
if (empty($url)) {
throw new Exception('No URL provided to hash algorithm.');
}
return hash(self::URLHASH_ALG, $url);
}
static public function beforeSchemaUpdate()
{
$table = strtolower(get_called_class());
$schema = Schema::get();
$schemadef = $schema->getTableDef($table);
// 2015-02-19 We have to upgrade our table definitions to have the urlhash field populated
if (isset($schemadef['fields']['urlhash']) && isset($schemadef['unique keys']['file_urlhash_key'])) {
// We already have the urlhash field, so no need to migrate it.
return;
}
echo "\nFound old $table table, upgrading it to contain 'urlhash' field...";
// 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 */ /* the code below is auto generated do not remove the above tag */
public $__table = 'file_redirection'; // table name public $__table = 'file_redirection'; // table name
public $url; // varchar(255) primary_key not_null public $urlhash; // varchar(64) primary_key not_null
public $url; // text
public $file_id; // int(4) public $file_id; // int(4)
public $redirections; // int(4) public $redirections; // int(4)
public $httpcode; // int(4) public $httpcode; // int(4)
@ -42,19 +43,30 @@ class File_redirection extends Managed_DataObject
{ {
return array( return array(
'fields' => array( 'fields' => array(
'url' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'short URL (or any other kind of redirect) for file (id)'), 'urlhash' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'sha256 hash of the URL'),
'url' => array('type' => 'text', 'description' => 'short URL (or any other kind of redirect) for file (id)'),
'file_id' => array('type' => 'int', 'description' => 'short URL for what URL/file'), 'file_id' => array('type' => 'int', 'description' => 'short URL for what URL/file'),
'redirections' => array('type' => 'int', 'description' => 'redirect count'), 'redirections' => array('type' => 'int', 'description' => 'redirect count'),
'httpcode' => array('type' => 'int', 'description' => 'HTTP status code (20x, 30x, etc.)'), 'httpcode' => array('type' => 'int', 'description' => 'HTTP status code (20x, 30x, etc.)'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
), ),
'primary key' => array('url'), 'primary key' => array('urlhash'),
'foreign keys' => array( 'foreign keys' => array(
'file_redirection_file_id_fkey' => array('file' => array('file_id' => 'id')), 'file_redirection_file_id_fkey' => array('file' => array('file_id' => 'id')),
), ),
); );
} }
static public function getByUrl($url)
{
$file = new File_redirection();
$file->urlhash = File::hashurl($url);
if (!$file->find(true)) {
throw new NoResultException($file);
}
return $file;
}
static function _commonHttp($url, $redirs) { static function _commonHttp($url, $redirs) {
$request = new HTTPClient($url); $request = new HTTPClient($url);
$request->setConfig(array( $request->setConfig(array(
@ -161,17 +173,18 @@ class File_redirection extends Managed_DataObject
*/ */
public function where($in_url, $discover=true) { public function where($in_url, $discover=true) {
// let's see if we know this... // let's see if we know this...
$a = File::getKV('url', $in_url); try {
$a = File::getByUrl($in_url);
if (!empty($a)) {
// this is a direct link to $a->url // this is a direct link to $a->url
return $a->url; return $a->url;
} else { } catch (NoResultException $e) {
$b = File_redirection::getKV('url', $in_url); try {
if (!empty($b)) { $b = File_redirection::getByUrl($in_url);
// this is a redirect to $b->file_id // this is a redirect to $b->file_id
$a = File::getKV('id', $b->file_id); $a = File::getKV('id', $b->file_id);
return $a->url; 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); $file_redir = File_redirection::getKV('url', $short_url);
if (!$file_redir instanceof File_redirection) { if (!$file_redir instanceof File_redirection) {
$file_redir = new File_redirection; $file_redir = new File_redirection;
$file_redir->urlhash = File::hashurl($short_url);
$file_redir->url = $short_url; $file_redir->url = $short_url;
$file_redir->file_id = $file_id; $file_redir->file_id = $file_id;
$file_redir->insert(); $file_redir->insert();
@ -334,10 +348,53 @@ class File_redirection extends Managed_DataObject
function saveNew($data, $file_id, $url) { function saveNew($data, $file_id, $url) {
$file_redir = new File_redirection; $file_redir = new File_redirection;
$file_redir->urlhash = File::hashurl($short_url);
$file_redir->url = $url; $file_redir->url = $url;
$file_redir->file_id = $file_id; $file_redir->file_id = $file_id;
$file_redir->redirections = intval($data['redirects']); $file_redir->redirections = intval($data['redirects']);
$file_redir->httpcode = intval($data['code']); $file_redir->httpcode = intval($data['code']);
$file_redir->insert(); $file_redir->insert();
} }
static public function beforeSchemaUpdate()
{
$table = strtolower(get_called_class());
$schema = Schema::get();
$schemadef = $schema->getTableDef($table);
// 2015-02-19 We have to upgrade our table definitions to have the urlhash field populated
if (isset($schemadef['fields']['urlhash']) && in_array('urlhash', $schemadef['primary key'])) {
// We already have the urlhash field, so no need to migrate it.
return;
}
echo "\nFound old $table table, upgrading it to contain 'urlhash' field...";
// We have to create a urlhash that is _not_ the primary key,
// transfer data and THEN run checkSchema
$schemadef['fields']['urlhash'] = array (
'type' => 'varchar',
'length' => 64,
'not null' => true,
'description' => 'sha256 hash of the URL',
);
$schemadef['fields']['url'] = array (
'type' => 'text',
'description' => 'short URL (or any other kind of redirect) for file (id)',
);
unset($schemadef['primary key']);
$schema->ensureTable($table, $schemadef);
echo "DONE.\n";
$classname = ucfirst($table);
$tablefix = new $classname;
// urlhash is hash('sha256', $url) in the File table
echo "Updating urlhash fields in $table table...";
// Maybe very MySQL specific :(
$tablefix->query(sprintf('UPDATE %1$s SET %2$s=%3$s;',
$schema->quoteIdentifier($table),
'urlhash',
// The line below is "result of sha256 on column `url`"
'SHA2(url, 256)'));
echo "DONE.\n";
echo "Resuming core schema upgrade...";
}
} }

View File

@ -27,8 +27,8 @@ class File_thumbnail extends Managed_DataObject
{ {
public $__table = 'file_thumbnail'; // table name public $__table = 'file_thumbnail'; // table name
public $file_id; // int(4) primary_key not_null public $file_id; // int(4) primary_key not_null
public $url; // varchar(255) unique_key public $url; // text
public $filename; // varchar(255) public $filename; // varchar(191) not 255 because utf8mb4 takes more space
public $width; // int(4) primary_key public $width; // int(4) primary_key
public $height; // int(4) primary_key public $height; // int(4) primary_key
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -38,8 +38,8 @@ class File_thumbnail extends Managed_DataObject
return array( return array(
'fields' => array( 'fields' => array(
'file_id' => array('type' => 'int', 'not null' => true, 'description' => 'thumbnail for what URL/file'), 'file_id' => array('type' => 'int', 'not null' => true, 'description' => 'thumbnail for what URL/file'),
'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL of thumbnail'), 'url' => array('type' => 'text', 'not null' => false, 'description' => 'URL of thumbnail'),
'filename' => array('type' => 'varchar', 'length' => 255, 'description' => 'if stored locally, filename is put here'), 'filename' => array('type' => 'varchar', 'length' => 191, 'description' => 'if stored locally, filename is put here'),
'width' => array('type' => 'int', 'description' => 'width of thumbnail'), 'width' => array('type' => 'int', 'description' => 'width of thumbnail'),
'height' => array('type' => 'int', 'description' => 'height of thumbnail'), 'height' => array('type' => 'int', 'description' => 'height of thumbnail'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
@ -117,46 +117,42 @@ class File_thumbnail extends Managed_DataObject
return File::path($filename); 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() public function getPath()
{ {
return self::path($this->filename); $filepath = self::path($this->filename);
if (!file_exists($filepath)) {
throw new FileNotFoundException($filepath);
}
return $filepath;
} }
public function getUrl() public function getUrl()
{ {
if (!empty($this->getFile()->filename)) { if (!empty($this->getFile()->filename)) {
// A locally stored File, so let's generate a URL for our instance. // A locally stored File, so we can dynamically generate a URL.
$url = File::url($this->filename); if (!empty($this->url)) {
if ($url != $this->url) { // Let's just clear this field as there is no point in having it for local files.
// For indexing purposes, in case we do a lookup on the 'url' field. $orig = clone($this);
// also we're fixing possible changes from http to https, or paths $this->url = null;
$this->updateUrl($url); $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 // No local filename available, return the URL we have stored
return $this->url; 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) public function delete($useWhere=false)
{ {
if (!empty($this->filename) && file_exists(File_thumbnail::path($this->filename))) { 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 $user_id; // int(4) primary_key not_null
public $foreign_id; // bigint(8) primary_key not_null unsigned public $foreign_id; // bigint(8) primary_key not_null unsigned
public $service; // int(4) primary_key not_null 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 $noticesync; // tinyint(1) not_null default_1
public $friendsync; // tinyint(1) not_null default_2 public $friendsync; // tinyint(1) not_null default_2
public $profilesync; // tinyint(1) not_null default_1 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'), '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'), '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'), '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'), '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'), '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'), '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 $__table = 'foreign_service'; // table name
public $id; // int(4) primary_key not_null public $id; // int(4) primary_key not_null
public $name; // varchar(32) unique_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 $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -25,7 +25,7 @@ class Foreign_service extends Managed_DataObject
'fields' => array( 'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'numeric key for service'), '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'), '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'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
), ),

View File

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

View File

@ -12,7 +12,7 @@ class Group_member extends Managed_DataObject
public $group_id; // int(4) primary_key not_null public $group_id; // int(4) primary_key not_null
public $profile_id; // int(4) primary_key not_null public $profile_id; // int(4) primary_key not_null
public $is_admin; // tinyint(1) 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 $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP 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'), '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'), '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?'), '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'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
), ),

View File

@ -12,7 +12,7 @@ class Invitation extends Managed_DataObject
public $__table = 'invitation'; // table name public $__table = 'invitation'; // table name
public $code; // varchar(32) primary_key not_null public $code; // varchar(32) primary_key not_null
public $user_id; // int(4) not_null public $user_id; // int(4) not_null
public $address; // varchar(255) 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 $address_type; // varchar(8) multiple_key not_null
public $registered_user_id; // int(4) not_null public $registered_user_id; // int(4) not_null
public $created; // datetime() not_null public $created; // datetime() not_null
@ -34,7 +34,7 @@ class Invitation extends Managed_DataObject
'fields' => array( 'fields' => array(
'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'random code for an invitation'), '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'), '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")'), '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'), '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'), '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 $__table = 'location_namespace'; // table name
public $id; // int(4) primary_key not_null public $id; // int(4) primary_key not_null
public $description; // varchar(255) public $description; // varchar(191)
public $created; // datetime() not_null public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -44,7 +44,7 @@ class Location_namespace extends Managed_DataObject
return array( return array(
'fields' => array( 'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'identity for this namespace'), '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'), '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'), '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; return $ckeys;
} }
public function escapedTableName()
{
return common_database_tablename($this->tableName());
}
/** /**
* Returns an ID, checked that it is set and reasonably valid * 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) // @FIXME return true only if something changed (otherwise 0)
return $result; return $result;
} }
static public function beforeSchemaUpdate()
{
// NOOP
}
} }

View File

@ -734,7 +734,7 @@ class Memcached_DataObject extends Safe_DataObject
return $string; return $string;
} }
// We overload so that 'SET NAMES "utf8"' is called for // We overload so that 'SET NAMES "utf8mb4"' is called for
// each connection // each connection
function _connect() function _connect()
@ -784,9 +784,9 @@ class Memcached_DataObject extends Safe_DataObject
$conn = $DB->connection; $conn = $DB->connection;
if (!empty($conn)) { if (!empty($conn)) {
if ($DB instanceof DB_mysqli || $DB instanceof MDB2_Driver_mysqli) { 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) { } 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 */ /* the code below is auto generated do not remove the above tag */
public $__table = 'nonce'; // table name 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 $tok; // char(32)
public $nonce; // char(32) primary_key not_null public $nonce; // char(32) primary_key not_null
public $ts; // datetime() primary_key not_null public $ts; // datetime() primary_key not_null
@ -39,7 +39,7 @@ class Nonce extends Managed_DataObject
return array( return array(
'description' => 'OAuth nonce record', 'description' => 'OAuth nonce record',
'fields' => array( 'fields' => array(
'consumer_key' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'unique identifier, root URL'), 'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'),
'tok' => array('type' => 'char', 'length' => 32, 'description' => 'buggy old value, ignored'), 'tok' => array('type' => 'char', 'length' => 32, 'description' => 'buggy old value, ignored'),
'nonce' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'nonce'), 'nonce' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'nonce'),
'ts' => array('type' => 'datetime', 'not null' => true, 'description' => 'timestamp sent'), '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 $__table = 'notice'; // table name
public $id; // int(4) primary_key not_null public $id; // int(4) primary_key not_null
public $profile_id; // int(4) multiple_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 $content; // text
public $rendered; // 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 $created; // datetime multiple_key not_null default_0000-00-00%2000%3A00%3A00
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
public $reply_to; // int(4) public $reply_to; // int(4)
@ -70,8 +70,8 @@ class Notice extends Managed_DataObject
public $location_id; // int(4) public $location_id; // int(4)
public $location_ns; // int(4) public $location_ns; // int(4)
public $repeat_of; // int(4) public $repeat_of; // int(4)
public $verb; // varchar(255) public $verb; // varchar(191) not 255 because utf8mb4 takes more space
public $object_type; // varchar(255) public $object_type; // varchar(191) not 255 because utf8mb4 takes more space
public $scope; // int(4) public $scope; // int(4)
/* the code above is auto generated do not remove the tag below */ /* the code above is auto generated do not remove the tag below */
@ -83,10 +83,10 @@ class Notice extends Managed_DataObject
'fields' => array( 'fields' => array(
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'), 'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'who made the update'), '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'), 'content' => array('type' => 'text', 'description' => 'update content', 'collate' => 'utf8_general_ci'),
'rendered' => array('type' => 'text', 'description' => 'HTML version of the content'), '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'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
'reply_to' => array('type' => 'int', 'description' => 'notice replied to (usually a guess)'), '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_id' => array('type' => 'int', 'description' => 'location id if possible'),
'location_ns' => array('type' => 'int', 'description' => 'namespace for location'), 'location_ns' => array('type' => 'int', 'description' => 'namespace for location'),
'repeat_of' => array('type' => 'int', 'description' => 'notice this is a repeat of'), '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'), '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' => 255, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'), 'verb' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'),
'scope' => array('type' => 'int', 'scope' => array('type' => 'int',
'description' => 'bit map for distribution scope; 0 = everywhere; 1 = this server only; 2 = addressees; 4 = followers; null = default'), 'description' => 'bit map for distribution scope; 0 = everywhere; 1 = this server only; 2 = addressees; 4 = followers; null = default'),
), ),
@ -128,7 +128,7 @@ class Notice extends Managed_DataObject
return $def; return $def;
} }
/* Notice types */ /* Notice types */
const LOCAL_PUBLIC = 1; const LOCAL_PUBLIC = 1;
const REMOTE = 0; const REMOTE = 0;
@ -142,7 +142,7 @@ class Notice extends Managed_DataObject
const FOLLOWER_SCOPE = 8; const FOLLOWER_SCOPE = 8;
protected $_profile = array(); protected $_profile = array();
/** /**
* Will always return a profile, if anything fails it will * Will always return a profile, if anything fails it will
* (through _setProfile) throw a NoProfileException. * (through _setProfile) throw a NoProfileException.
@ -157,7 +157,7 @@ class Notice extends Managed_DataObject
} }
return $this->_profile[$this->profile_id]; return $this->_profile[$this->profile_id];
} }
public function _setProfile(Profile $profile=null) public function _setProfile(Profile $profile=null)
{ {
if (!$profile instanceof Profile) { if (!$profile instanceof Profile) {
@ -268,7 +268,7 @@ class Notice extends Managed_DataObject
} }
return $title; return $title;
} }
public function getContent() public function getContent()
{ {
return $this->content; return $this->content;
@ -313,6 +313,16 @@ class Notice extends Managed_DataObject
return $notice; 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. * Extract #hashtags from this notice's content and save them to the database.
*/ */
@ -674,7 +684,7 @@ class Notice extends Managed_DataObject
$notice->insert(); // throws exception on failure $notice->insert(); // throws exception on failure
// If it's not part of a conversation, it's // If it's not part of a conversation, it's
// the beginning of a new conversation. // the beginning of a new conversation.
if (empty($notice->conversation)) { if (empty($notice->conversation)) {
$orig = clone($notice); $orig = clone($notice);
// $act->context->conversation will be null if it was not provided // $act->context->conversation will be null if it was not provided
$conv = Conversation::create($notice, $options['conversation']); $conv = Conversation::create($notice, $options['conversation']);
@ -777,7 +787,7 @@ class Notice extends Managed_DataObject
'distribute' => true); 'distribute' => true);
// options will have default values when nothing has been supplied // options will have default values when nothing has been supplied
$options = array_merge($defaults, $options); $options = array_merge($defaults, $options);
foreach (array_keys($defaults) as $key) { foreach (array_keys($defaults) as $key) {
// Only convert the keynames we specify ourselves from 'defaults' array into variables // Only convert the keynames we specify ourselves from 'defaults' array into variables
$$key = $options[$key]; $$key = $options[$key];
@ -892,6 +902,12 @@ class Notice extends Managed_DataObject
$stored->insert(); // throws exception on error $stored->insert(); // throws exception on error
$orig = clone($stored); // for updating later in this try clause $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 // If it's not part of a conversation, it's
// the beginning of a new conversation. // the beginning of a new conversation.
if (empty($stored->conversation)) { if (empty($stored->conversation)) {
@ -900,12 +916,6 @@ class Notice extends Managed_DataObject
$stored->conversation = $conv->id; $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); $stored->update($orig);
} catch (Exception $e) { } catch (Exception $e) {
if (empty($stored->id)) { if (empty($stored->id)) {
@ -957,7 +967,7 @@ class Notice extends Managed_DataObject
// Prepare inbox delivery, may be queued to background. // Prepare inbox delivery, may be queued to background.
$stored->distribute(); $stored->distribute();
} }
return $stored; return $stored;
} }
@ -1067,13 +1077,9 @@ class Notice extends Managed_DataObject
} }
$args = func_get_args(); $args = func_get_args();
$format = array_shift($args); $format = array_shift($args);
$keyPart = vsprintf($format, $args); $keyPart = vsprintf($format, $args);
$cacheKey = Cache::key($keyPart); $cacheKey = Cache::key($keyPart);
$c->delete($cacheKey); $c->delete($cacheKey);
// delete the "last" stream, too, if this notice is // delete the "last" stream, too, if this notice is
@ -1187,24 +1193,20 @@ class Notice extends Managed_DataObject
} }
protected $_attachments = array(); protected $_attachments = array();
function attachments() { function attachments() {
if (isset($this->_attachments[$this->id])) { if (isset($this->_attachments[$this->id])) {
return $this->_attachments[$this->id]; return $this->_attachments[$this->id];
} }
$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);
$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(); $this->_attachments[$this->id] = $files->fetchAll();
return $this->_attachments[$this->id]; return $this->_attachments[$this->id];
} }
@ -1286,7 +1288,7 @@ class Notice extends Managed_DataObject
$root->free(); $root->free();
return $root; return $root;
} }
if (is_null($profile)) { if (is_null($profile)) {
$keypart = sprintf('notice:conversation_root:%d:null', $this->id); $keypart = sprintf('notice:conversation_root:%d:null', $this->id);
} else { } else {
@ -1294,7 +1296,7 @@ class Notice extends Managed_DataObject
$this->id, $this->id,
$profile->id); $profile->id);
} }
$root = self::cacheGet($keypart); $root = self::cacheGet($keypart);
if ($root !== false && $root->inScope($profile)) { if ($root !== false && $root->inScope($profile)) {
@ -1707,9 +1709,9 @@ class Notice extends Managed_DataObject
function getReplyProfiles() function getReplyProfiles()
{ {
$ids = $this->getReplies(); $ids = $this->getReplies();
$profiles = Profile::multiGet('id', $ids); $profiles = Profile::multiGet('id', $ids);
return $profiles->fetchAll(); return $profiles->fetchAll();
} }
@ -1747,9 +1749,9 @@ class Notice extends Managed_DataObject
* *
* @return array of Group objects * @return array of Group objects
*/ */
protected $_groups = array(); protected $_groups = array();
function getGroups() function getGroups()
{ {
// Don't save groups for repeats // Don't save groups for repeats
@ -1757,27 +1759,24 @@ class Notice extends Managed_DataObject
if (!empty($this->repeat_of)) { if (!empty($this->repeat_of)) {
return array(); return array();
} }
if (isset($this->_groups[$this->id])) { if (isset($this->_groups[$this->id])) {
return $this->_groups[$this->id]; return $this->_groups[$this->id];
} }
$gis = Group_inbox::listGet('notice_id', array($this->id)); $gis = Group_inbox::listGet('notice_id', array($this->id));
$ids = array(); $ids = array();
foreach ($gis[$this->id] as $gi) foreach ($gis[$this->id] as $gi) {
{
$ids[] = $gi->group_id; $ids[] = $gi->group_id;
} }
$groups = User_group::multiGet('id', $ids); $groups = User_group::multiGet('id', $ids);
$this->_groups[$this->id] = $groups->fetchAll(); $this->_groups[$this->id] = $groups->fetchAll();
return $this->_groups[$this->id]; return $this->_groups[$this->id];
} }
function _setGroups($groups) function _setGroups($groups)
{ {
$this->_groups[$this->id] = $groups; $this->_groups[$this->id] = $groups;
@ -1819,17 +1818,7 @@ class Notice extends Managed_DataObject
$act->verb = $this->verb; $act->verb = $this->verb;
if ($this->repeat_of) { 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 {
$act->objects[] = $this->asActivityObject(); $act->objects[] = $this->asActivityObject();
} }
@ -2159,7 +2148,7 @@ class Notice extends Managed_DataObject
// Unfortunately this is likely to lose tags or URLs // Unfortunately this is likely to lose tags or URLs
// at the end of long notices. // at the end of long notices.
$content = mb_substr($content, 0, $maxlen - 4) . ' ...'; $content = mb_substr($content, 0, $maxlen - 4) . ' ...';
} }
// Scope is same as this one's // Scope is same as this one's
@ -2687,89 +2676,69 @@ class Notice extends Managed_DataObject
$scope = self::defaultScope(); $scope = self::defaultScope();
} }
// If there's no scope, anyone (even anon) is in scope. if ($scope == 0 && !$this->getProfile()->isPrivateStream()) { // Not scoping, so it is public.
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;
}
}
return !$this->isHiddenSpam($profile); 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) { function isHiddenSpam($profile) {
// Hide posts by silenced users from everyone but moderators. // Hide posts by silenced users from everyone but moderators.
if (common_config('notice', 'hidespam')) { if (common_config('notice', 'hidespam')) {
@ -2819,7 +2788,7 @@ class Notice extends Managed_DataObject
$skip = array('_profile', '_groups', '_attachments', '_faves', '_replies', '_repeats'); $skip = array('_profile', '_groups', '_attachments', '_faves', '_replies', '_repeats');
return array_diff($vars, $skip); return array_diff($vars, $skip);
} }
static function defaultScope() static function defaultScope()
{ {
$scope = common_config('notice', 'defaultscope'); $scope = common_config('notice', 'defaultscope');
@ -2836,7 +2805,6 @@ class Notice extends Managed_DataObject
static function fillProfiles($notices) static function fillProfiles($notices)
{ {
$map = self::getProfiles($notices); $map = self::getProfiles($notices);
foreach ($notices as $entry=>$notice) { foreach ($notices as $entry=>$notice) {
try { try {
if (array_key_exists($notice->profile_id, $map)) { if (array_key_exists($notice->profile_id, $map)) {
@ -2847,42 +2815,35 @@ class Notice extends Managed_DataObject
unset($notices[$entry]); unset($notices[$entry]);
} }
} }
return array_values($map); return array_values($map);
} }
static function getProfiles(&$notices) static function getProfiles(&$notices)
{ {
$ids = array(); $ids = array();
foreach ($notices as $notice) { foreach ($notices as $notice) {
$ids[] = $notice->profile_id; $ids[] = $notice->profile_id;
} }
$ids = array_unique($ids); $ids = array_unique($ids);
return Profile::pivotGet('id', $ids);
return Profile::pivotGet('id', $ids);
} }
static function fillGroups(&$notices) static function fillGroups(&$notices)
{ {
$ids = self::_idsOf($notices); $ids = self::_idsOf($notices);
$gis = Group_inbox::listGet('notice_id', $ids); $gis = Group_inbox::listGet('notice_id', $ids);
$gids = array(); $gids = array();
foreach ($gis as $id => $gi) foreach ($gis as $id => $gi) {
{
foreach ($gi as $g) foreach ($gi as $g)
{ {
$gids[] = $g->group_id; $gids[] = $g->group_id;
} }
} }
$gids = array_unique($gids); $gids = array_unique($gids);
$group = User_group::pivotGet('id', $gids); $group = User_group::pivotGet('id', $gids);
foreach ($notices as $notice) foreach ($notices as $notice)
{ {
$grps = array(); $grps = array();
@ -2906,21 +2867,16 @@ class Notice extends Managed_DataObject
static function fillAttachments(&$notices) static function fillAttachments(&$notices)
{ {
$ids = self::_idsOf($notices); $ids = self::_idsOf($notices);
$f2pMap = File_to_post::listGet('post_id', $ids); $f2pMap = File_to_post::listGet('post_id', $ids);
$fileIds = array(); $fileIds = array();
foreach ($f2pMap as $noticeId => $f2ps) { foreach ($f2pMap as $noticeId => $f2ps) {
foreach ($f2ps as $f2p) { foreach ($f2ps as $f2p) {
$fileIds[] = $f2p->file_id; $fileIds[] = $f2p->file_id;
} }
} }
$fileIds = array_unique($fileIds); $fileIds = array_unique($fileIds);
$fileMap = File::pivotGet('id', $fileIds); $fileMap = File::pivotGet('id', $fileIds);
foreach ($notices as $notice) foreach ($notices as $notice)
{ {
$files = array(); $files = array();
@ -2945,31 +2901,4 @@ class Notice extends Managed_DataObject
$notice->_setReplies($ids); $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 $__table = 'notice_source'; // table name
public $code; // varchar(32) primary_key not_null public $code; // varchar(32) primary_key not_null
public $name; // varchar(255) not_null public $name; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $url; // varchar(255) not_null public $url; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $created; // datetime() not_null public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -24,8 +24,8 @@ class Notice_source extends Managed_DataObject
return array( return array(
'fields' => array( 'fields' => array(
'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'source code'), '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'), 'name' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'name of the source'),
'url' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'url to link to'), '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'), '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'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),

View File

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

View File

@ -12,7 +12,7 @@ class Oauth_token_association extends Managed_DataObject
public $__table = 'oauth_token_association'; // table name public $__table = 'oauth_token_association'; // table name
public $profile_id; // int(4) primary_key not_null public $profile_id; // int(4) primary_key not_null
public $application_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 $created; // datetime not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@ -43,7 +43,7 @@ class Oauth_token_association extends Managed_DataObject
'fields' => array( 'fields' => array(
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'associated user'), 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'associated user'),
'application_id' => array('type' => 'int', 'not null' => true, 'description' => 'the application'), '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'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
), ),

View File

@ -30,11 +30,11 @@ class Profile extends Managed_DataObject
public $__table = 'profile'; // table name public $__table = 'profile'; // table name
public $id; // int(4) primary_key not_null public $id; // int(4) primary_key not_null
public $nickname; // varchar(64) multiple_key not_null public $nickname; // varchar(64) multiple_key not_null
public $fullname; // varchar(255) multiple_key public $fullname; // varchar(191) multiple_key not 255 because utf8mb4 takes more space
public $profileurl; // varchar(255) public $profileurl; // varchar(191) not 255 because utf8mb4 takes more space
public $homepage; // varchar(255) multiple_key public $homepage; // varchar(191) multiple_key not 255 because utf8mb4 takes more space
public $bio; // text() multiple_key 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 $lat; // decimal(10,7)
public $lon; // decimal(10,7) public $lon; // decimal(10,7)
public $location_id; // int(4) public $location_id; // int(4)
@ -49,11 +49,11 @@ class Profile extends Managed_DataObject
'fields' => array( 'fields' => array(
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'), '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'), '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'), 'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name', 'collate' => 'utf8_general_ci'),
'profileurl' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL, cached so we dont regenerate'), 'profileurl' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL, cached so we dont regenerate'),
'homepage' => array('type' => 'varchar', 'length' => 255, 'description' => 'identifying URL', 'collate' => 'utf8_general_ci'), 'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'identifying URL', 'collate' => 'utf8_general_ci'),
'bio' => array('type' => 'text', 'description' => 'descriptive biography', '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'), 'lat' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'),
'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'), 'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'),
'location_id' => array('type' => 'int', 'description' => 'location id if possible'), 'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
@ -160,7 +160,7 @@ class Profile extends Managed_DataObject
return $this->getGroup()->setOriginal($filename); return $this->getGroup()->setOriginal($filename);
} }
$imagefile = new ImageFile($this->id, Avatar::path($filename)); $imagefile = new ImageFile(null, Avatar::path($filename));
$avatar = new Avatar(); $avatar = new Avatar();
$avatar->profile_id = $this->id; $avatar->profile_id = $this->id;
@ -1451,6 +1451,12 @@ class Profile extends Managed_DataObject
return $feed; 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 * Get a Profile object by URI. Will call external plugins for help
* using the event StartGetProfileFromURI. * using the event StartGetProfileFromURI.
@ -1566,6 +1572,15 @@ class Profile extends Managed_DataObject
return $this->getUser()->shortenLinks($text, $always); 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) { public function delPref($namespace, $topic) {
return Profile_prefs::setData($this, $namespace, $topic, null); 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 $private; // tinyint(1)
public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00 public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
public $uri; // varchar(255) unique_key public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $mainpage; // varchar(255) public $mainpage; // varchar(191) not 255 because utf8mb4 takes more space
public $tagged_count; // smallint public $tagged_count; // smallint
public $subscriber_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'), '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'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date the tag was modified'),
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universal identifier'), 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'),
'mainpage' => array('type' => 'varchar', 'length' => 255, 'description' => 'page to link to'), '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'), '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'), '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 $__table = 'profile_prefs'; // table name
public $profile_id; // int(4) primary_key not_null public $profile_id; // int(4) primary_key not_null
public $namespace; // varchar(255) not_null public $namespace; // varchar(191) not_null
public $topic; // varchar(255) not_null public $topic; // varchar(191) not_null
public $data; // text public $data; // text
public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00 public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@ -42,8 +42,8 @@ class Profile_prefs extends Managed_DataObject
return array( return array(
'fields' => array( 'fields' => array(
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'), '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'), 'namespace' => array('type' => 'varchar', 'length' => 191, '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...'), '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'), 'data' => array('type' => 'blob', 'description' => 'topic data, may be anything'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),

View File

@ -12,7 +12,7 @@ class Sms_carrier extends Managed_DataObject
public $__table = 'sms_carrier'; // table name public $__table = 'sms_carrier'; // table name
public $id; // int(4) primary_key not_null public $id; // int(4) primary_key not_null
public $name; // varchar(64) unique_key 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 $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -30,7 +30,7 @@ class Sms_carrier extends Managed_DataObject
'fields' => array( 'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'primary key for SMS carrier'), 'id' => array('type' => 'int', 'not null' => true, 'description' => 'primary key for SMS carrier'),
'name' => array('type' => 'varchar', 'length' => 64, 'description' => 'name of the 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'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
), ),

View File

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

View File

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

View File

@ -36,6 +36,9 @@ class Subscription_queue extends Managed_DataObject
public static function saveNew(Profile $subscriber, Profile $subscribed) 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 = new Subscription_queue();
$rq->subscriber = $subscriber->id; $rq->subscriber = $subscriber->id;
$rq->subscribed = $subscribed->id; $rq->subscribed = $subscribed->id;
@ -51,6 +54,18 @@ class Subscription_queue extends Managed_DataObject
return ($sub instanceof Subscription_queue); 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. * 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 */ /* the code below is auto generated do not remove the above tag */
public $__table = 'token'; // table name 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 $tok; // char(32) primary_key not_null
public $secret; // char(32) not_null public $secret; // char(32) not_null
public $type; // tinyint(1) not_null public $type; // tinyint(1) not_null
public $state; // tinyint(1) public $state; // tinyint(1)
public $verifier; // varchar(255) public $verifier; // varchar(191) not 255 because utf8mb4 takes more space
public $verified_callback; // varchar(255) public $verified_callback; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime() not_null public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -27,13 +27,13 @@ class Token extends Managed_DataObject
return array( return array(
'description' => 'OAuth token record', 'description' => 'OAuth token record',
'fields' => array( 'fields' => array(
'consumer_key' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'unique identifier, root URL'), 'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'),
'tok' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'identifying value'), 'tok' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'identifying value'),
'secret' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'secret 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'), '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'), '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'), 'verifier' => array('type' => 'varchar', 'length' => 191, 'description' => 'verifier string for OAuth 1.0a'),
'verified_callback' => array('type' => 'varchar', 'length' => 255, 'description' => 'verified callback URL 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'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),

View File

@ -34,9 +34,9 @@ class User extends Managed_DataObject
public $__table = 'user'; // table name public $__table = 'user'; // table name
public $id; // int(4) primary_key not_null public $id; // int(4) primary_key not_null
public $nickname; // varchar(64) unique_key public $nickname; // varchar(64) unique_key
public $password; // varchar(255) public $password; // varchar(191) not 255 because utf8mb4 takes more space
public $email; // varchar(255) unique_key public $email; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $incomingemail; // varchar(255) unique_key public $incomingemail; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $emailnotifysub; // tinyint(1) default_1 public $emailnotifysub; // tinyint(1) default_1
public $emailnotifyfav; // tinyint(1) default_1 public $emailnotifyfav; // tinyint(1) default_1
public $emailnotifynudge; // tinyint(1) default_1 public $emailnotifynudge; // tinyint(1) default_1
@ -50,8 +50,8 @@ class User extends Managed_DataObject
public $carrier; // int(4) public $carrier; // int(4)
public $smsnotify; // tinyint(1) public $smsnotify; // tinyint(1)
public $smsreplies; // tinyint(1) public $smsreplies; // tinyint(1)
public $smsemail; // varchar(255) public $smsemail; // varchar(191) not 255 because utf8mb4 takes more space
public $uri; // varchar(255) unique_key public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $autosubscribe; // tinyint(1) public $autosubscribe; // tinyint(1)
public $subscribe_policy; // tinyint(1) public $subscribe_policy; // tinyint(1)
public $urlshorteningservice; // varchar(50) default_ur1.ca public $urlshorteningservice; // varchar(50) default_ur1.ca
@ -69,9 +69,9 @@ class User extends Managed_DataObject
'fields' => array( 'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'), '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'), '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'), 'password' => array('type' => 'varchar', 'length' => 191, 'description' => 'salted password, can be null for OpenID users'),
'email' => array('type' => 'varchar', 'length' => 255, 'description' => 'email address for password recovery etc.'), 'email' => array('type' => 'varchar', 'length' => 191, 'description' => 'email address for password recovery etc.'),
'incomingemail' => array('type' => 'varchar', 'length' => 255, 'description' => 'email address for post-by-email'), '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'), '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'), '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'), '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'), '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'), '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'), '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'), 'smsemail' => array('type' => 'varchar', 'length' => 191, 'description' => 'built from sms and carrier'),
'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'),
'autosubscribe' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'automatically subscribe to users who subscribe to us'), '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'), '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'), '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 'password' (may be missing for eg OpenID registrations)
* string 'code' invite code * string 'code' invite code
* ?string 'uri' permalink to notice; defaults to local notice URL * ?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) { static function register(array $fields) {
@ -205,12 +206,8 @@ class User extends Managed_DataObject
$email = common_canonical_email($email); $email = common_canonical_email($email);
} }
try { // Normalize _and_ check whether it is in use. Throw NicknameException on failure.
$profile->nickname = Nickname::normalize($nickname, true); $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;
}
$profile->profileurl = common_profile_url($profile->nickname); $profile->profileurl = common_profile_url($profile->nickname);
@ -277,7 +274,9 @@ class User extends Managed_DataObject
$id = $profile->insert(); $id = $profile->insert();
if ($id === false) { if ($id === false) {
common_log_db_error($profile, 'INSERT', __FILE__); 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; $user->id = $id;
@ -297,7 +296,8 @@ class User extends Managed_DataObject
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'INSERT', __FILE__); common_log_db_error($user, 'INSERT', __FILE__);
$profile->query('ROLLBACK'); $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 // Everyone is subscribed to themself
@ -312,7 +312,8 @@ class User extends Managed_DataObject
if (!$result) { if (!$result) {
common_log_db_error($subscription, 'INSERT', __FILE__); common_log_db_error($subscription, 'INSERT', __FILE__);
$profile->query('ROLLBACK'); $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 // Mark that this invite was converted
@ -334,7 +335,8 @@ class User extends Managed_DataObject
if (!$result) { if (!$result) {
common_log_db_error($confirm, 'INSERT', __FILE__); common_log_db_error($confirm, 'INSERT', __FILE__);
$profile->query('ROLLBACK'); $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), common_log(LOG_WARNING, sprintf("Default user %s does not exist.", $defnick),
__FILE__); __FILE__);
} else { } 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)); 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; return $user;
} }
@ -687,11 +693,9 @@ class User extends Managed_DataObject
return $stream->getNotices($offset, $limit, $since_id, $max_id); return $stream->getNotices($offset, $limit, $since_id, $max_id);
} }
public function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
{ {
// TRANS: Exception thrown when trying view "repeated to me". return $this->getProfile()->repeatedToMe($offset, $limit, $since_id, $max_id);
throw new Exception(_('Not implemented since inbox change.'));
} }
public static function siteOwner() public static function siteOwner()
@ -994,6 +998,11 @@ class User extends Managed_DataObject
return $act; return $act;
} }
public function isPrivateStream()
{
return $this->getProfile()->isPrivateStream();
}
public function delPref($namespace, $topic) public function delPref($namespace, $topic)
{ {
return $this->getProfile()->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 $__table = 'user_group'; // table name
public $id; // int(4) primary_key not_null public $id; // int(4) primary_key not_null
public $nickname; // varchar(64) public $nickname; // varchar(64)
public $fullname; // varchar(255) public $fullname; // varchar(191) not 255 because utf8mb4 takes more space
public $homepage; // varchar(255) public $homepage; // varchar(191) not 255 because utf8mb4 takes more space
public $description; // text public $description; // text
public $location; // varchar(255) public $location; // varchar(191) not 255 because utf8mb4 takes more space
public $original_logo; // varchar(255) public $original_logo; // varchar(191) not 255 because utf8mb4 takes more space
public $homepage_logo; // varchar(255) public $homepage_logo; // varchar(191) not 255 because utf8mb4 takes more space
public $stream_logo; // varchar(255) public $stream_logo; // varchar(191) not 255 because utf8mb4 takes more space
public $mini_logo; // varchar(255) 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 $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
public $uri; // varchar(255) unique_key public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $mainpage; // varchar(255) public $mainpage; // varchar(191) not 255 because utf8mb4 takes more space
public $join_policy; // tinyint public $join_policy; // tinyint
public $force_scope; // 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'), 'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
'nickname' => array('type' => 'varchar', 'length' => 64, 'description' => 'nickname for addressing'), 'nickname' => array('type' => 'varchar', 'length' => 64, 'description' => 'nickname for addressing'),
'fullname' => array('type' => 'varchar', 'length' => 255, 'description' => 'display name'), 'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name'),
'homepage' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL, cached so we dont regenerate'), 'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL, cached so we dont regenerate'),
'description' => array('type' => 'text', 'description' => 'group description'), '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'), 'original_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'original size logo'),
'homepage_logo' => array('type' => 'varchar', 'length' => 255, 'description' => 'homepage (profile) size logo'), 'homepage_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'homepage (profile) size logo'),
'stream_logo' => array('type' => 'varchar', 'length' => 255, 'description' => 'stream-sized logo'), 'stream_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'stream-sized logo'),
'mini_logo' => array('type' => 'varchar', 'length' => 255, 'description' => 'mini logo'), 'mini_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'mini logo'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universal identifier'), 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'),
'mainpage' => array('type' => 'varchar', 'length' => 255, 'description' => 'page for group info to link to'), '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'), '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'), '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) 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); $orig = clone($this);
$this->original_logo = Avatar::url($filename); $this->original_logo = Avatar::url($filename);
$this->homepage_logo = Avatar::url($imagefile->resize(AVATAR_PROFILE_SIZE)); foreach ($sizes as $name=>$size) {
$this->stream_logo = Avatar::url($imagefile->resize(AVATAR_STREAM_SIZE)); $filename = Avatar::filename($this->profile_id, image_type_to_extension($imagefile->preferredType()),
$this->mini_logo = Avatar::url($imagefile->resize(AVATAR_MINI_SIZE)); $size, common_timestamp());
$imagefile->resizeTo(Avatar::path($filename), array('width'=>$size, 'height'=>$size));
$this->$name = Avatar::url($filename);
}
common_debug(common_log_objstring($this)); common_debug(common_log_objstring($this));
return $this->update($orig); 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 $__table = 'user_im_prefs'; // table name
public $user_id; // int(4) primary_key not_null public $user_id; // int(4) primary_key not_null
public $screenname; // varchar(255) not_null public $screenname; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $transport; // varchar(255) not_null public $transport; // varchar(191) not_null not 255 because utf8mb4 takes more space
public $notify; // tinyint(1) public $notify; // tinyint(1)
public $replies; // tinyint(1) public $replies; // tinyint(1)
public $microid; // tinyint(1) public $microid; // tinyint(1)
@ -53,8 +53,8 @@ class User_im_prefs extends Managed_DataObject
return array( return array(
'fields' => array( 'fields' => array(
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'), 'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
'screenname' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'screenname on this service'), 'screenname' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'screenname on this service'),
'transport' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'transport (ex xmpp, aim)'), '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'), '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'), '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'), '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 $__table = 'user_username'; // table name
public $user_id; // int(4) not_null public $user_id; // int(4) not_null
public $provider_name; // 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(255) primary_key not_null public $username; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
public $created; // datetime() not_null public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@ -23,8 +23,8 @@ class User_username extends Managed_DataObject
{ {
return array( return array(
'fields' => array( 'fields' => array(
'provider_name' => array('type' => 'varchar', 'length' => 255, 'description' => 'provider name'), 'provider_name' => array('type' => 'varchar', 'length' => 191, 'description' => 'provider name'),
'username' => array('type' => 'varchar', 'length' => 255, 'description' => 'username'), 'username' => array('type' => 'varchar', 'length' => 191, 'description' => 'username'),
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice id this title relates to'), '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'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'), 'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),

View File

@ -21,7 +21,7 @@ create table status_network (
created datetime not null comment 'date this record was created', created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified' 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 ( create table status_network_tag (
site_id integer comment 'unique id', site_id integer comment 'unique id',
@ -30,5 +30,5 @@ create table status_network_tag (
constraint primary key (site_id, tag), constraint primary key (site_id, tag),
index status_network_tag_tag_idx (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> <IfModule mod_rewrite.c>
RewriteEngine On 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: # minus the domain part:
# #
# http://example.com/ => / # https://social.example.com/ => /
# http://example.com/mublog/ => /mublog/ # https://example.com/social/ => /social/
# #
RewriteBase / RewriteBase /
#RewriteBase /mublog/ #RewriteBase /mublog/
@ -26,7 +46,7 @@
<FilesMatch "\.(ini)"> <FilesMatch "\.(ini)">
# For mod_access_compat in Apache <2.4 # For mod_access_compat in Apache <2.4
Order allow,deny #Order allow,deny
# Use this instead for Apache >2.4 (mod_authz_host) # Use this instead for Apache >2.4 (mod_authz_host)
# Require all denied # Require all denied

View File

@ -265,7 +265,7 @@ function main()
$site_ssl = common_config('site', 'ssl'); $site_ssl = common_config('site', 'ssl');
// If the request is HTTP and it should be HTTPS... // 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)); common_redirect(common_local_url($args['action'], $args));
} }

View File

@ -265,10 +265,10 @@ class WebInstaller extends Installer
<li> <li>
<label for="site_profile">Type of site</label> <label for="site_profile">Type of site</label>
<select id="site_profile" name="site_profile"> <select id="site_profile" name="site_profile">
<option value="private">Private</option>
<option value="community">Community</option> <option value="community">Community</option>
<option value ="public">Public</option> <option value="public">Public (open registration)</option>
<option value ="singleuser">Single User</option> <option value="singleuser">Single User</option>
<option value="private">Private (no federation)</option>
</select> </select>
<p class="form_guide">Initial access settings for your site</p> <p class="form_guide">Initial access settings for your site</p>
</li> </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 * 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 * 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 /* Layout helpers
----------------------------------*/ ----------------------------------*/
@ -48,7 +48,7 @@
left: 0; left: 0;
position: absolute; position: absolute;
opacity: 0; opacity: 0;
filter:Alpha(Opacity=0); filter:Alpha(Opacity=0); /* support: IE8 */
} }
.ui-front { .ui-front {
@ -86,6 +86,10 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.ui-draggable-handle {
-ms-touch-action: none;
touch-action: none;
}
.ui-resizable { .ui-resizable {
position: relative; position: relative;
} }
@ -93,6 +97,8 @@
position: absolute; position: absolute;
font-size: 0.1px; font-size: 0.1px;
display: block; display: block;
-ms-touch-action: none;
touch-action: none;
} }
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-disabled .ui-resizable-handle,
.ui-resizable-autohide .ui-resizable-handle { .ui-resizable-autohide .ui-resizable-handle {
@ -154,25 +160,31 @@
right: -5px; right: -5px;
top: -5px; top: -5px;
} }
.ui-selectable {
-ms-touch-action: none;
touch-action: none;
}
.ui-selectable-helper { .ui-selectable-helper {
position: absolute; position: absolute;
z-index: 100; z-index: 100;
border: 1px dotted black; border: 1px dotted black;
} }
.ui-sortable-handle {
-ms-touch-action: none;
touch-action: none;
}
.ui-accordion .ui-accordion-header { .ui-accordion .ui-accordion-header {
display: block; display: block;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
margin-top: 2px; margin: 2px 0 0 0;
padding: .5em .5em .5em .7em; padding: .5em .5em .5em .7em;
min-height: 0; /* support: IE7 */ min-height: 0; /* support: IE7 */
font-size: 100%;
} }
.ui-accordion .ui-accordion-icons { .ui-accordion .ui-accordion-icons {
padding-left: 2.2em; padding-left: 2.2em;
} }
.ui-accordion .ui-accordion-noicons {
padding-left: .7em;
}
.ui-accordion .ui-accordion-icons .ui-accordion-icons { .ui-accordion .ui-accordion-icons .ui-accordion-icons {
padding-left: 2.2em; padding-left: 2.2em;
} }
@ -347,12 +359,9 @@ button.ui-button::-moz-focus-inner {
font-size: 1em; font-size: 1em;
margin: 1px 0; margin: 1px 0;
} }
.ui-datepicker select.ui-datepicker-month-year {
width: 100%;
}
.ui-datepicker select.ui-datepicker-month, .ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year { .ui-datepicker select.ui-datepicker-year {
width: 49%; width: 45%;
} }
.ui-datepicker table { .ui-datepicker table {
width: 100%; width: 100%;
@ -466,6 +475,7 @@ button.ui-button::-moz-focus-inner {
border-left-width: 1px; border-left-width: 1px;
} }
.ui-dialog { .ui-dialog {
overflow: hidden;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -488,7 +498,7 @@ button.ui-button::-moz-focus-inner {
position: absolute; position: absolute;
right: .3em; right: .3em;
top: 50%; top: 50%;
width: 21px; width: 20px;
margin: -10px 0 0 0; margin: -10px 0 0 0;
padding: 1px; padding: 1px;
height: 20px; height: 20px;
@ -526,72 +536,56 @@ button.ui-button::-moz-focus-inner {
} }
.ui-menu { .ui-menu {
list-style: none; list-style: none;
padding: 2px; padding: 0;
margin: 0; margin: 0;
display: block; display: block;
outline: none; outline: none;
} }
.ui-menu .ui-menu { .ui-menu .ui-menu {
margin-top: -3px;
position: absolute; position: absolute;
} }
.ui-menu .ui-menu-item { .ui-menu .ui-menu-item {
position: relative;
margin: 0; margin: 0;
padding: 0; padding: 3px 1em 3px .4em;
width: 100%; cursor: pointer;
min-height: 0; /* support: IE7 */
/* support: IE10, see #8844 */ /* 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 { .ui-menu .ui-menu-divider {
margin: 5px -2px 5px -2px; margin: 5px 0;
height: 0; height: 0;
font-size: 0; font-size: 0;
line-height: 0; line-height: 0;
border-width: 1px 0 0 0; border-width: 1px 0 0 0;
} }
.ui-menu .ui-menu-item a { .ui-menu .ui-state-focus,
text-decoration: none; .ui-menu .ui-state-active {
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;
margin: -1px; 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 */ /* icon support */
.ui-menu-icons { .ui-menu-icons {
position: relative; position: relative;
} }
.ui-menu-icons .ui-menu-item a { .ui-menu-icons .ui-menu-item {
position: relative;
padding-left: 2em; padding-left: 2em;
} }
/* left-aligned */ /* left-aligned */
.ui-menu .ui-icon { .ui-menu .ui-icon {
position: absolute; position: absolute;
top: .2em; top: 0;
bottom: 0;
left: .2em; left: .2em;
margin: auto 0;
} }
/* right-aligned */ /* right-aligned */
.ui-menu .ui-menu-icon { .ui-menu .ui-menu-icon {
position: static; left: auto;
float: right; right: 0;
} }
.ui-progressbar { .ui-progressbar {
height: 2em; height: 2em;
@ -603,14 +597,63 @@ button.ui-button::-moz-focus-inner {
height: 100%; height: 100%;
} }
.ui-progressbar .ui-progressbar-overlay { .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%; height: 100%;
filter: alpha(opacity=25); filter: alpha(opacity=25); /* support: IE8 */
opacity: 0.25; opacity: 0.25;
} }
.ui-progressbar-indeterminate .ui-progressbar-value { .ui-progressbar-indeterminate .ui-progressbar-value {
background-image: none; 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 { .ui-slider {
position: relative; position: relative;
text-align: left; text-align: left;
@ -621,6 +664,8 @@ button.ui-button::-moz-focus-inner {
width: 1.2em; width: 1.2em;
height: 1.2em; height: 1.2em;
cursor: default; cursor: default;
-ms-touch-action: none;
touch-action: none;
} }
.ui-slider .ui-slider-range { .ui-slider .ui-slider-range {
position: absolute; position: absolute;
@ -631,7 +676,7 @@ button.ui-button::-moz-focus-inner {
background-position: 0 0; 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-handle,
.ui-slider.ui-state-disabled .ui-slider-range { .ui-slider.ui-state-disabled .ui-slider-range {
filter: inherit; filter: inherit;
@ -704,13 +749,13 @@ button.ui-button::-moz-focus-inner {
overflow: hidden; overflow: hidden;
right: 0; right: 0;
} }
/* more specificity required here to overide default borders */ /* more specificity required here to override default borders */
.ui-spinner a.ui-spinner-button { .ui-spinner a.ui-spinner-button {
border-top: none; border-top: none;
border-bottom: none; border-bottom: none;
border-right: none; border-right: none;
} }
/* vertical centre icon */ /* vertically center icon */
.ui-spinner .ui-icon { .ui-spinner .ui-icon {
position: absolute; position: absolute;
margin-top: -8px; margin-top: -8px;
@ -747,7 +792,7 @@ button.ui-button::-moz-focus-inner {
padding: 0; padding: 0;
white-space: nowrap; white-space: nowrap;
} }
.ui-tabs .ui-tabs-nav li a { .ui-tabs .ui-tabs-nav .ui-tabs-anchor {
float: left; float: left;
padding: .5em 1em; padding: .5em 1em;
text-decoration: none; text-decoration: none;
@ -756,13 +801,12 @@ button.ui-button::-moz-focus-inner {
margin-bottom: -1px; margin-bottom: -1px;
padding-bottom: 1px; padding-bottom: 1px;
} }
.ui-tabs .ui-tabs-nav li.ui-tabs-active a, .ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
.ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
.ui-tabs .ui-tabs-nav li.ui-tabs-loading a { .ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
cursor: text; 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 .ui-tabs-anchor {
.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a {
cursor: pointer; cursor: pointer;
} }
.ui-tabs .ui-tabs-panel { .ui-tabs .ui-tabs-panel {
@ -801,7 +845,7 @@ body .ui-tooltip {
} }
.ui-widget-content { .ui-widget-content {
border: 1px solid #aaaaaa; 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; color: #222222;
} }
.ui-widget-content a { .ui-widget-content a {
@ -809,7 +853,7 @@ body .ui-tooltip {
} }
.ui-widget-header { .ui-widget-header {
border: 1px solid #aaaaaa; 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; color: #222222;
font-weight: bold; font-weight: bold;
} }
@ -823,7 +867,7 @@ body .ui-tooltip {
.ui-widget-content .ui-state-default, .ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default { .ui-widget-header .ui-state-default {
border: 1px solid #d3d3d3; 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; font-weight: normal;
color: #555555; color: #555555;
} }
@ -840,14 +884,18 @@ body .ui-tooltip {
.ui-widget-content .ui-state-focus, .ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus { .ui-widget-header .ui-state-focus {
border: 1px solid #999999; 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; font-weight: normal;
color: #212121; color: #212121;
} }
.ui-state-hover a, .ui-state-hover a,
.ui-state-hover a:hover, .ui-state-hover a:hover,
.ui-state-hover a:link, .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; color: #212121;
text-decoration: none; text-decoration: none;
} }
@ -855,7 +903,7 @@ body .ui-tooltip {
.ui-widget-content .ui-state-active, .ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active { .ui-widget-header .ui-state-active {
border: 1px solid #aaaaaa; 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; font-weight: normal;
color: #212121; color: #212121;
} }
@ -872,7 +920,7 @@ body .ui-tooltip {
.ui-widget-content .ui-state-highlight, .ui-widget-content .ui-state-highlight,
.ui-widget-header .ui-state-highlight { .ui-widget-header .ui-state-highlight {
border: 1px solid #fcefa1; 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; color: #363636;
} }
.ui-state-highlight a, .ui-state-highlight a,
@ -884,7 +932,7 @@ body .ui-tooltip {
.ui-widget-content .ui-state-error, .ui-widget-content .ui-state-error,
.ui-widget-header .ui-state-error { .ui-widget-header .ui-state-error {
border: 1px solid #cd0a0a; 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; color: #cd0a0a;
} }
.ui-state-error a, .ui-state-error a,
@ -906,18 +954,18 @@ body .ui-tooltip {
.ui-widget-content .ui-priority-secondary, .ui-widget-content .ui-priority-secondary,
.ui-widget-header .ui-priority-secondary { .ui-widget-header .ui-priority-secondary {
opacity: .7; opacity: .7;
filter:Alpha(Opacity=70); filter:Alpha(Opacity=70); /* support: IE8 */
font-weight: normal; font-weight: normal;
} }
.ui-state-disabled, .ui-state-disabled,
.ui-widget-content .ui-state-disabled, .ui-widget-content .ui-state-disabled,
.ui-widget-header .ui-state-disabled { .ui-widget-header .ui-state-disabled {
opacity: .35; opacity: .35;
filter:Alpha(Opacity=35); filter:Alpha(Opacity=35); /* support: IE8 */
background-image: none; background-image: none;
} }
.ui-state-disabled .ui-icon { .ui-state-disabled .ui-icon {
filter:Alpha(Opacity=35); /* For IE8 - See #6059 */ filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */
} }
/* Icons /* Icons
@ -930,27 +978,27 @@ body .ui-tooltip {
} }
.ui-icon, .ui-icon,
.ui-widget-content .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 { .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 { .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-hover .ui-icon,
.ui-state-focus .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 { .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 { .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 .ui-icon,
.ui-state-error-text .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 */ /* positioning */
@ -1163,15 +1211,15 @@ body .ui-tooltip {
/* Overlays */ /* Overlays */
.ui-widget-overlay { .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; opacity: .3;
filter: Alpha(Opacity=30); filter: Alpha(Opacity=30); /* support: IE8 */
} }
.ui-widget-shadow { .ui-widget-shadow {
margin: -8px 0 0 -8px; margin: -8px 0 0 -8px;
padding: 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; opacity: .3;
filter: Alpha(Opacity=30); filter: Alpha(Opacity=30); /* support: IE8 */
border-radius: 8px; 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 * https://github.com/carhartl/jquery-cookie
* *
* Copyright 2013 Klaus Hartl * Copyright 2013 Klaus Hartl
@ -7,57 +7,65 @@
*/ */
(function (factory) { (function (factory) {
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
// AMD. Register as anonymous module. // AMD
define(['jquery'], factory); define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
factory(require('jquery'));
} else { } else {
// Browser globals. // Browser globals
factory(jQuery); factory(jQuery);
} }
}(function ($) { }(function ($) {
var pluses = /\+/g; var pluses = /\+/g;
function decode(s) { function encode(s) {
if (config.raw) { return config.raw ? s : encodeURIComponent(s);
return s;
}
try {
// If we can't decode the cookie, ignore it, it's unusable.
return decodeURIComponent(s.replace(pluses, ' '));
} catch(e) {}
} }
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) { if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape... // This is a quoted cookie as according to RFC2068, unescape...
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
} }
s = decode(s);
try { 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. // If we can't parse the cookie, ignore it, it's unusable.
s = decodeURIComponent(s.replace(pluses, ' '));
return config.json ? JSON.parse(s) : s; return config.json ? JSON.parse(s) : s;
} catch(e) {} } 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) { var config = $.cookie = function (key, value, options) {
// Write // Write
if (value !== undefined) {
if (value !== undefined && !$.isFunction(value)) {
options = $.extend({}, config.defaults, options); options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') { if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date(); 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 = [ return (document.cookie = [
config.raw ? key : encodeURIComponent(key), encode(key), '=', stringifyCookieValue(value),
'=',
config.raw ? value : encodeURIComponent(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '', options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '', options.domain ? '; domain=' + options.domain : '',
@ -80,12 +88,13 @@
var cookie = parts.join('='); var cookie = parts.join('=');
if (key && key === name) { if (key && key === name) {
result = decodeAndParse(cookie); // If second argument (value) is a function it's a converter...
result = read(cookie, value);
break; break;
} }
// Prevent storing a cookie that we couldn't decode. // Prevent storing a cookie that we couldn't decode.
if (!key && (cookie = decodeAndParse(cookie)) !== undefined) { if (!key && (cookie = read(cookie)) !== undefined) {
result[name] = cookie; result[name] = cookie;
} }
} }
@ -96,12 +105,13 @@
config.defaults = {}; config.defaults = {};
$.removeCookie = function (key, options) { $.removeCookie = function (key, options) {
if ($.cookie(key) !== undefined) { if ($.cookie(key) === undefined) {
// Must not alter options, thus extending a fresh object... return false;
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return true;
} }
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 * jQuery Form Plugin
* version: 3.43.0-2013.09.03 * version: 3.51.0-2014.06.20
* Requires jQuery v1.5 or later * 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/ * Examples and documentation at: http://malsup.com/jquery/form/
* Project repository: https://github.com/malsup/form * Project repository: https://github.com/malsup/form
* Dual licensed under the MIT and GPL licenses. * Dual licensed under the MIT and GPL licenses.
* https://github.com/malsup/form#copyright-and-license * https://github.com/malsup/form#copyright-and-license
*/ */
/*global ActiveXObject */ /*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"; "use strict";
/* /*
@ -63,11 +76,13 @@ var hasProp = !!$.fn.prop;
// contains inputs with names like "action" or "method"; in those // contains inputs with names like "action" or "method"; in those
// cases "prop" returns the element // cases "prop" returns the element
$.fn.attr2 = function() { $.fn.attr2 = function() {
if ( ! hasProp ) if ( ! hasProp ) {
return this.attr.apply(this, arguments); return this.attr.apply(this, arguments);
}
var val = this.prop.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 val;
}
return this.attr.apply(this, arguments); return this.attr.apply(this, arguments);
}; };
@ -209,7 +224,7 @@ $.fn.ajaxSubmit = function(options) {
// [value] (issue #113), also see comment: // [value] (issue #113), also see comment:
// https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219 // 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 hasFileInputs = fileInputs.length > 0;
var mp = 'multipart/form-data'; var mp = 'multipart/form-data';
@ -245,8 +260,9 @@ $.fn.ajaxSubmit = function(options) {
$form.removeData('jqxhr').data('jqxhr', jqxhr); $form.removeData('jqxhr').data('jqxhr', jqxhr);
// clear element array // clear element array
for (var k=0; k < elements.length; k++) for (var k=0; k < elements.length; k++) {
elements[k] = null; elements[k] = null;
}
// fire 'notify' event // fire 'notify' event
this.trigger('form-submit-notify', [this, options]); this.trigger('form-submit-notify', [this, options]);
@ -278,9 +294,11 @@ $.fn.ajaxSubmit = function(options) {
if (options.extraData) { if (options.extraData) {
var serializedData = deepSerialize(options.extraData); var serializedData = deepSerialize(options.extraData);
for (i=0; i < serializedData.length; i++) for (i=0; i < serializedData.length; i++) {
if (serializedData[i]) if (serializedData[i]) {
formdata.append(serializedData[i][0], serializedData[i][1]); formdata.append(serializedData[i][0], serializedData[i][1]);
}
}
} }
options.data = null; options.data = null;
@ -312,11 +330,18 @@ $.fn.ajaxSubmit = function(options) {
} }
s.data = null; s.data = null;
var beforeSend = s.beforeSend; var beforeSend = s.beforeSend;
s.beforeSend = function(xhr, o) { s.beforeSend = function(xhr, o) {
//Send FormData() provided by user
if (options.formData) {
o.data = options.formData;
}
else {
o.data = formdata; o.data = formdata;
if(beforeSend) }
beforeSend.call(this, xhr, o); if(beforeSend) {
beforeSend.call(this, xhr, o);
}
}; };
return $.ajax(s); return $.ajax(s);
} }
@ -335,10 +360,12 @@ $.fn.ajaxSubmit = function(options) {
// ensure that every serialized input is still enabled // ensure that every serialized input is still enabled
for (i=0; i < elements.length; i++) { for (i=0; i < elements.length; i++) {
el = $(elements[i]); el = $(elements[i]);
if ( hasProp ) if ( hasProp ) {
el.prop('disabled', false); el.prop('disabled', false);
else }
else {
el.removeAttr('disabled'); el.removeAttr('disabled');
}
} }
} }
@ -348,10 +375,12 @@ $.fn.ajaxSubmit = function(options) {
if (s.iframeTarget) { if (s.iframeTarget) {
$io = $(s.iframeTarget); $io = $(s.iframeTarget);
n = $io.attr2('name'); n = $io.attr2('name');
if (!n) if (!n) {
$io.attr2('name', id); $io.attr2('name', id);
else }
else {
id = n; id = n;
}
} }
else { else {
$io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />'); $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
@ -383,12 +412,15 @@ $.fn.ajaxSubmit = function(options) {
$io.attr('src', s.iframeSrc); // abort op in progress $io.attr('src', s.iframeSrc); // abort op in progress
xhr.error = e; xhr.error = e;
if (s.error) if (s.error) {
s.error.call(s.context, xhr, e, status); s.error.call(s.context, xhr, e, status);
if (g) }
if (g) {
$.event.trigger("ajaxError", [xhr, s, e]); $.event.trigger("ajaxError", [xhr, s, e]);
if (s.complete) }
if (s.complete) {
s.complete.call(s.context, xhr, e); 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 // take a breath so that pending repaints get some cpu time before the upload starts
function doSubmit() { function doSubmit() {
// make sure form attrs are set // 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 // update form attrs in IE friendly way
form.setAttribute('target',id); form.setAttribute('target',id);
@ -504,14 +539,16 @@ $.fn.ajaxSubmit = function(options) {
try { try {
var state = getDoc(io).readyState; var state = getDoc(io).readyState;
log('state = ' + state); log('state = ' + state);
if (state && state.toLowerCase() == 'uninitialized') if (state && state.toLowerCase() == 'uninitialized') {
setTimeout(checkState,50); setTimeout(checkState,50);
}
} }
catch(e) { catch(e) {
log('Server abort: ' , e, ' (', e.name, ')'); log('Server abort: ' , e, ' (', e.name, ')');
cb(SERVER_ABORT); cb(SERVER_ABORT);
if (timeoutHandle) if (timeoutHandle) {
clearTimeout(timeoutHandle); clearTimeout(timeoutHandle);
}
timeoutHandle = undefined; timeoutHandle = undefined;
} }
} }
@ -540,10 +577,12 @@ $.fn.ajaxSubmit = function(options) {
// add iframe to doc and submit the form // add iframe to doc and submit the form
$io.appendTo('body'); $io.appendTo('body');
} }
if (io.attachEvent) if (io.attachEvent) {
io.attachEvent('onload', cb); io.attachEvent('onload', cb);
else }
else {
io.addEventListener('load', cb, false); io.addEventListener('load', cb, false);
}
setTimeout(checkState,15); setTimeout(checkState,15);
try { try {
@ -557,6 +596,7 @@ $.fn.ajaxSubmit = function(options) {
finally { finally {
// reset attrs and remove "extra" input elements // reset attrs and remove "extra" input elements
form.setAttribute('action',a); form.setAttribute('action',a);
form.setAttribute('enctype', et); // #380
if(t) { if(t) {
form.setAttribute('target', t); form.setAttribute('target', t);
} else { } else {
@ -598,13 +638,16 @@ $.fn.ajaxSubmit = function(options) {
if (!doc || doc.location.href == s.iframeSrc) { if (!doc || doc.location.href == s.iframeSrc) {
// response not received yet // response not received yet
if (!timedOut) if (!timedOut) {
return; return;
}
} }
if (io.detachEvent) if (io.detachEvent) {
io.detachEvent('onload', cb); io.detachEvent('onload', cb);
else }
else {
io.removeEventListener('load', cb, false); io.removeEventListener('load', cb, false);
}
var status = 'success', errMsg; var status = 'success', errMsg;
try { try {
@ -631,8 +674,9 @@ $.fn.ajaxSubmit = function(options) {
var docRoot = doc.body ? doc.body : doc.documentElement; var docRoot = doc.body ? doc.body : doc.documentElement;
xhr.responseText = docRoot ? docRoot.innerHTML : null; xhr.responseText = docRoot ? docRoot.innerHTML : null;
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
if (isXml) if (isXml) {
s.dataType = 'xml'; s.dataType = 'xml';
}
xhr.getResponseHeader = function(header){ xhr.getResponseHeader = function(header){
var headers = {'content-type': s.dataType}; var headers = {'content-type': s.dataType};
return headers[header.toLowerCase()]; 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 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
if (status === 'success') { if (status === 'success') {
if (s.success) if (s.success) {
s.success.call(s.context, data, 'success', xhr); s.success.call(s.context, data, 'success', xhr);
}
deferred.resolve(xhr.responseText, 'success', xhr); deferred.resolve(xhr.responseText, 'success', xhr);
if (g) if (g) {
$.event.trigger("ajaxSuccess", [xhr, s]); $.event.trigger("ajaxSuccess", [xhr, s]);
}
} }
else if (status) { else if (status) {
if (errMsg === undefined) if (errMsg === undefined) {
errMsg = xhr.statusText; errMsg = xhr.statusText;
if (s.error) }
if (s.error) {
s.error.call(s.context, xhr, status, errMsg); s.error.call(s.context, xhr, status, errMsg);
}
deferred.reject(xhr, 'error', errMsg); deferred.reject(xhr, 'error', errMsg);
if (g) if (g) {
$.event.trigger("ajaxError", [xhr, s, errMsg]); $.event.trigger("ajaxError", [xhr, s, errMsg]);
}
} }
if (g) if (g) {
$.event.trigger("ajaxComplete", [xhr, s]); $.event.trigger("ajaxComplete", [xhr, s]);
}
if (g && ! --$.active) { if (g && ! --$.active) {
$.event.trigger("ajaxStop"); $.event.trigger("ajaxStop");
} }
if (s.complete) if (s.complete) {
s.complete.call(s.context, xhr, status); s.complete.call(s.context, xhr, status);
}
callbackProcessed = true; callbackProcessed = true;
if (s.timeout) if (s.timeout) {
clearTimeout(timeoutHandle); clearTimeout(timeoutHandle);
}
// clean up // clean up
setTimeout(function() { setTimeout(function() {
if (!s.iframeTarget) if (!s.iframeTarget) {
$io.remove(); $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); $io.attr('src', s.iframeSrc);
}
xhr.responseXML = null; xhr.responseXML = null;
}, 100); }, 100);
} }
@ -758,8 +812,9 @@ $.fn.ajaxSubmit = function(options) {
data = xml ? xhr.responseXML : xhr.responseText; data = xml ? xhr.responseXML : xhr.responseText;
if (xml && data.documentElement.nodeName === 'parsererror') { if (xml && data.documentElement.nodeName === 'parsererror') {
if ($.error) if ($.error) {
$.error('parsererror'); $.error('parsererror');
}
} }
if (s && s.dataFilter) { if (s && s.dataFilter) {
data = s.dataFilter(data, type); data = s.dataFilter(data, type);
@ -832,7 +887,7 @@ function doAjaxSubmit(e) {
var options = e.data; var options = e.data;
if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
e.preventDefault(); e.preventDefault();
$(this).ajaxSubmit(options); $(e.target).ajaxSubmit(options); // #365
} }
} }
@ -891,8 +946,23 @@ $.fn.formToArray = function(semantic, elements) {
} }
var form = this[0]; var form = this[0];
var formId = this.attr('id');
var els = semantic ? form.getElementsByTagName('*') : form.elements; 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; return a;
} }
@ -915,15 +985,17 @@ $.fn.formToArray = function(semantic, elements) {
v = $.fieldValue(el, true); v = $.fieldValue(el, true);
if (v && v.constructor == Array) { if (v && v.constructor == Array) {
if (elements) if (elements) {
elements.push(el); elements.push(el);
}
for(j=0, jmax=v.length; j < jmax; j++) { for(j=0, jmax=v.length; j < jmax; j++) {
a.push({name: n, value: v[j]}); a.push({name: n, value: v[j]});
} }
} }
else if (feature.fileapi && el.type == 'file') { else if (feature.fileapi && el.type == 'file') {
if (elements) if (elements) {
elements.push(el); elements.push(el);
}
var files = el.files; var files = el.files;
if (files.length) { if (files.length) {
for (j=0; j < files.length; j++) { for (j=0; j < files.length; j++) {
@ -936,8 +1008,9 @@ $.fn.formToArray = function(semantic, elements) {
} }
} }
else if (v !== null && typeof v != 'undefined') { else if (v !== null && typeof v != 'undefined') {
if (elements) if (elements) {
elements.push(el); elements.push(el);
}
a.push({name: n, value: v, type: el.type, required: el.required}); 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)) { if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
continue; continue;
} }
if (v.constructor == Array) if (v.constructor == Array) {
$.merge(val, v); $.merge(val, v);
else }
else {
val.push(v); val.push(v);
}
} }
return val; return val;
}; };
@ -1070,7 +1145,7 @@ $.fieldValue = function(el, successful) {
if (op.selected) { if (op.selected) {
var v = op.value; var v = op.value;
if (!v) { // extra pain for IE... 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) { if (one) {
return v; return v;
@ -1113,21 +1188,22 @@ $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
else if (tag == 'select') { else if (tag == 'select') {
this.selectedIndex = -1; this.selectedIndex = -1;
} }
else if (t == "file") { else if (t == "file") {
if (/MSIE/.test(navigator.userAgent)) { if (/MSIE/.test(navigator.userAgent)) {
$(this).replaceWith($(this).clone(true)); $(this).replaceWith($(this).clone(true));
} else { } else {
$(this).val(''); $(this).val('');
} }
} }
else if (includeHidden) { else if (includeHidden) {
// includeHidden can be the value true, or it can be a selector string // includeHidden can be the value true, or it can be a selector string
// indicating a special test; for example: // indicating a special test; for example:
// $('#myForm').clearForm('.special:hidden') // $('#myForm').clearForm('.special:hidden')
// the above would clean hidden inputs that have the class of 'special' // the above would clean hidden inputs that have the class of 'special'
if ( (includeHidden === true && /hidden/.test(t)) || if ( (includeHidden === true && /hidden/.test(t)) ||
(typeof includeHidden == 'string' && $(this).is(includeHidden)) ) (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) {
this.value = ''; this.value = '';
}
} }
}); });
}; };
@ -1186,8 +1262,9 @@ $.fn.ajaxSubmit.debug = false;
// helper fn for console logging // helper fn for console logging
function log() { function log() {
if (!$.fn.ajaxSubmit.debug) if (!$.fn.ajaxSubmit.debug) {
return; return;
}
var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,''); var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
if (window.console && window.console.log) { if (window.console && window.console.log) {
window.console.log(msg); 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 json2.js
2013-05-26 2015-02-25
Public Domain. Public Domain.
@ -48,7 +48,9 @@
Date.prototype.toJSON = function (key) { Date.prototype.toJSON = function (key) {
function f(n) { function f(n) {
// Format integers to have at least two digits. // Format integers to have at least two digits.
return n < 10 ? '0' + n : n; return n < 10
? '0' + n
: n;
} }
return this.getUTCFullYear() + '-' + return this.getUTCFullYear() + '-' +
@ -146,10 +148,12 @@
redistribute. redistribute.
*/ */
/*jslint evil: true, regexp: true */ /*jslint
eval, for, this
*/
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, /*property
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify, lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf test, toJSON, toString, valueOf
@ -168,7 +172,13 @@ if (typeof JSON !== 'object') {
function f(n) { function f(n) {
// Format integers to have at least two digits. // 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') { if (typeof Date.prototype.toJSON !== 'function') {
@ -176,35 +186,25 @@ if (typeof JSON !== 'object') {
Date.prototype.toJSON = function () { Date.prototype.toJSON = function () {
return isFinite(this.valueOf()) return isFinite(this.valueOf())
? this.getUTCFullYear() + '-' + ? this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' + f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' + f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' + f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' + f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z' f(this.getUTCSeconds()) + 'Z'
: null; : null;
}; };
String.prototype.toJSON = Boolean.prototype.toJSON = this_value;
Number.prototype.toJSON = Number.prototype.toJSON = this_value;
Boolean.prototype.toJSON = function () { String.prototype.toJSON = this_value;
return this.valueOf();
};
} }
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, var cx,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable,
gap, gap,
indent, indent,
meta = { // table of character substitutions meta,
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep; rep;
@ -216,12 +216,14 @@ if (typeof JSON !== 'object') {
// sequences. // sequences.
escapable.lastIndex = 0; 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]; var c = meta[a];
return typeof c === 'string' return typeof c === 'string'
? c ? c
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + string + '"'; }) + '"'
: '"' + string + '"';
} }
@ -261,7 +263,9 @@ if (typeof JSON !== 'object') {
// JSON numbers must be finite. Encode non-finite numbers as null. // 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 'boolean':
case 'null': case 'null':
@ -305,10 +309,10 @@ if (typeof JSON !== 'object') {
// brackets. // brackets.
v = partial.length === 0 v = partial.length === 0
? '[]' ? '[]'
: gap : gap
? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
: '[' + partial.join(',') + ']'; : '[' + partial.join(',') + ']';
gap = mind; gap = mind;
return v; return v;
} }
@ -322,7 +326,11 @@ if (typeof JSON !== 'object') {
k = rep[i]; k = rep[i];
v = str(k, value); v = str(k, value);
if (v) { 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)) { if (Object.prototype.hasOwnProperty.call(value, k)) {
v = str(k, value); v = str(k, value);
if (v) { 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. // and wrap them in braces.
v = partial.length === 0 v = partial.length === 0
? '{}' ? '{}'
: gap : gap
? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
: '{' + partial.join(',') + '}'; : '{' + partial.join(',') + '}';
gap = mind; gap = mind;
return v; 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 the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') { 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) { JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional // 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 the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') { 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) { JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns // 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)) { if (cx.test(text)) {
text = text.replace(cx, function (a) { text = text.replace(cx, function (a) {
return '\\u' + 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 // 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. // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/ if (
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') /^[\],:{}\s]*$/.test(
text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/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 // 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 // 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. // each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' return typeof reviver === 'function'
? walk({'': j}, '') ? walk({'': j}, '')
: j; : j;
} }
// If the text is not JSON parseable, then a SyntaxError is thrown. // If the text is not JSON parseable, then a SyntaxError is thrown.

View File

@ -32,7 +32,6 @@ var SN = { // StatusNet
MaxLength: 140, MaxLength: 140,
PatternUsername: /^[0-9a-zA-Z\-_.]*$/, PatternUsername: /^[0-9a-zA-Z\-_.]*$/,
HTTP20x30x: [200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304, 305, 306, 307], 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 * Map of localized message strings exported to script from the PHP
* side via Action::getScriptMessages(). * side via Action::getScriptMessages().
@ -94,12 +96,12 @@ var SN = { // StatusNet
* @access private * @access private
*/ */
FormNoticeEnhancements: function (form) { FormNoticeEnhancements: function (form) {
if (jQuery.data(form[0], 'ElementData') === undefined) { if ($.data(form[0], 'ElementData') === undefined) {
var MaxLength = form.find('.count').text(); var MaxLength = form.find('.count').text();
if (MaxLength === undefined) { if (MaxLength === undefined) {
MaxLength = SN.C.I.MaxLength; MaxLength = SN.C.I.MaxLength;
} }
jQuery.data(form[0], 'ElementData', {MaxLength: MaxLength}); $.data(form[0], 'ElementData', {MaxLength: MaxLength});
SN.U.Counter(form); SN.U.Counter(form);
@ -122,7 +124,7 @@ var SN = { // StatusNet
NDT.on('cut', delayedUpdate) NDT.on('cut', delayedUpdate)
.on('paste', delayedUpdate); .on('paste', delayedUpdate);
} else { } 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) { Counter: function (form) {
SN.C.I.FormNoticeCurrent = form; SN.C.I.FormNoticeCurrent = form;
var MaxLength = jQuery.data(form[0], 'ElementData').MaxLength; var MaxLength = $.data(form[0], 'ElementData').MaxLength;
if (MaxLength <= 0) { if (MaxLength <= 0) {
return; return;
@ -217,6 +219,18 @@ var SN = { // StatusNet
return url; 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' * Grabs form data and submits it asynchronously, with 'ajax=1'
* parameter added to the rest. * parameter added to the rest.
@ -375,7 +389,7 @@ var SN = { // StatusNet
if ($('.' + SN.C.S.Error, response).length > 0) { if ($('.' + SN.C.S.Error, response).length > 0) {
form.append(document._importNode($('.' + SN.C.S.Error, response)[0], true)); form.append(document._importNode($('.' + SN.C.S.Error, response)[0], true));
} else { } 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 form
.resetForm() .resetForm()
.find('.attach-status').remove(); .find('.attach-status').remove();
@ -405,16 +419,14 @@ var SN = { // StatusNet
if (replyItem.length > 0) { if (replyItem.length > 0) {
// If this is an inline reply, remove the form... // If this is an inline reply, remove the form...
var list = form.closest('.threaded-replies'); var list = form.closest('.threaded-replies');
var placeholder = list.find('.notice-reply-placeholder');
replyItem.remove();
var id = $(notice).attr('id'); var id = $(notice).attr('id');
if ($('#' + id).length == 0) { if ($('#' + id).length == 0) {
$(notice).insertBefore(placeholder); $(notice).insertBefore(replyItem);
} // else Realtime came through before us... } // else Realtime came through before us...
// ...and show the placeholder form. replyItem.remove();
placeholder.show();
} else if (notices.length > 0 && SN.U.belongsOnTimeline(notice)) { } else if (notices.length > 0 && SN.U.belongsOnTimeline(notice)) {
// Not a reply. If on our timeline, show it at the top! // 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. * Setup function -- DOES NOT trigger actions immediately.
* *
@ -616,41 +666,18 @@ var SN = { // StatusNet
NoticeInlineReplyTrigger: function (notice, initialText) { NoticeInlineReplyTrigger: function (notice, initialText) {
// Find the notice we're replying to... // Find the notice we're replying to...
var id = $($('.notice_id', notice)[0]).text(); var id = $($('.notice_id', notice)[0]).text();
var replyForm, placeholder; var replyForm;
var parentNotice = notice; var parentNotice = notice;
var stripForm = true; // strip a couple things out of reply forms that are inline 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.find('.threaded-replies');
var list = notice.closest('.notices'); if (list.length == 0) {
if (list.closest('.old-school').length) { list = notice.closest('.threaded-replies');
// We're replying to an old-school conversation thread; }
// use the old-style ping into the top form. if (list.length == 0) {
SN.U.switchInputFormTab("status"); list = $('<ul class="notices threaded-replies xoxo"></ul>');
replyForm = $('#input_form_status').find('form'); notice.append(list);
stripForm = false; list = notice.find('.threaded-replies');
} 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 nextStep = function () { var nextStep = function () {
@ -663,6 +690,7 @@ var SN = { // StatusNet
replyForm.find('label[for=notice_to]').hide(); replyForm.find('label[for=notice_to]').hide();
replyForm.find('label[for=notice_private]').hide(); replyForm.find('label[for=notice_private]').hide();
} }
replyItem.show();
// Set focus... // Set focus...
var text = replyForm.find('textarea'); var text = replyForm.find('textarea');
@ -681,82 +709,64 @@ var SN = { // StatusNet
text[0].setSelectionRange(len, len); 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 // Create the reply form entry
var replyItem = $('li.notice-reply', list); var replyItem = $('li.notice-reply', list);
if (replyItem.length == 0) { if (replyItem.length == 0) {
replyItem = $('<li class="notice-reply"></li>'); 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) { SN.Init.NoticeFormSetup(replyForm);
var formEl = document._importNode(formMaster, true); nextStep();
replyItem.append(formEl); },
list.append(replyItem); // *after* the placeholder });
// We do everything relevant in 'success' above
var form = $(formEl); return;
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]);
});
}
} }
replyForm = noticeForm.clone();
SN.Init.NoticeFormSetup(replyForm);
replyItem.append(replyForm);
list.append(replyItem);
} }
}, // replyForm is set, we're not fetching by URL...
// Next setp is to configure in-reply-to etc.
NoticeInlineReplyPlaceholder: function (notice) { nextStep();
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);
}, },
/** /**
* Setup function -- DOES NOT apply immediately. * 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. * Uses 'on' rather than 'live' or 'bind', so applies to future as well as present items.
*/ */
NoticeInlineReplySetup: function () { NoticeInlineReplySetup: function () {
$('li.notice-reply-placeholder input') // Expand conversation links
.on('focus', function () {
var notice = $(this).closest('li.notice');
SN.U.NoticeInlineReplyTrigger(notice);
return false;
});
$(document).on('click', 'li.notice-reply-comments a', function () { $(document).on('click', 'li.notice-reply-comments a', function () {
var url = $(this).attr('href'); var url = $(this).attr('href');
var area = $(this).closest('.threaded-replies'); var area = $(this).closest('.threaded-replies');
$.get(url, {ajax: 1}, function (data, textStatus, xhr) { $.ajax({
var replies = $('.threaded-replies', data); url: url,
if (replies.length) { data: {ajax: 1},
area.replaceWith(document._importNode(replies[0], true)); success: function (data, textStatus, xhr) {
} var replies = $('.threaded-replies', data);
if (replies.length) {
area.replaceWith(document._importNode(replies[0], true));
}
},
}); });
return false; return false;
}); });
@ -1043,7 +1053,7 @@ var SN = { // StatusNet
function removeNoticeDataGeo(error) { function removeNoticeDataGeo(error) {
label label
.attr('title', jQuery.trim(label.text())) .attr('title', $.trim(label.text()))
.removeClass('checked'); .removeClass('checked');
form.find('[name=lat]').val(''); form.find('[name=lat]').val('');
@ -1460,16 +1470,13 @@ var SN = { // StatusNet
// Only close if there's been no edit. // Only close if there's been no edit.
if (cur == '' || cur == textarea.data('initialText')) { if (cur == '' || cur == textarea.data('initialText')) {
var parentNotice = replyItem.closest('li.notice'); var parentNotice = replyItem.closest('li.notice');
replyItem.remove(); replyItem.hide();
parentNotice.find('li.notice-reply-placeholder').show(); 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 * @param {jQuery} form
*/ */
NoticeFormSetup: function (form) { NoticeFormSetup: function (form) {
if (!form.data('NoticeFormSetup')) { if (form.data('NoticeFormSetup')) {
SN.U.NoticeLocationAttach(form); return false;
SN.U.FormNoticeXHR(form);
SN.U.FormNoticeEnhancements(form);
SN.U.NoticeDataAttach(form);
form.data('NoticeFormSetup', true);
} }
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 () { Notices: function () {
if ($('body.user_in').length > 0) { 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.NoticeRepeat();
SN.U.NoticeReply(); SN.U.NoticeReply();
SN.U.NoticeInlineReplySetup(); SN.U.NoticeInlineReplySetup();
SN.U.NoticeOptionsAjax();
} }
SN.U.NoticeAttachments(); 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 * Run setup for the ajax people tags editor
* *
@ -1644,7 +1596,6 @@ var SN = { // StatusNet
} }
SN.C.PtagACData = data; 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); common_config_set('db', 'database', $mirror);
} }
$status = $this->prepare($args); if (Event::handle('StartActionExecute', array($this, &$args))) {
if ($status) { $prepared = $this->prepare($args);
$this->handle($args); if ($prepared) {
} else { $this->handle($args);
common_debug('Prepare failed for Action.'); } else {
common_debug('Prepare failed for Action.');
}
} }
$this->flush(); $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')); $this->action = strtolower($this->trimmed('action'));
if ($this->ajax || $this->boolean('ajax')) { if ($this->ajax || $this->boolean('ajax')) {
// check with StatusNet::isAjax() // check with GNUsocial::isAjax()
StatusNet::setAjax(true); GNUsocial::setAjax(true);
} }
if ($this->needLogin) { if ($this->needLogin) {
@ -206,7 +208,7 @@ class Action extends HTMLOutputter // lawsuit
*/ */
function showPage() function showPage()
{ {
if (StatusNet::isAjax()) { if (GNUsocial::isAjax()) {
self::showAjax(); self::showAjax();
return; return;
} }
@ -326,7 +328,7 @@ class Action extends HTMLOutputter // lawsuit
} else { } else {
// favicon.ico should be HTTPS if the rest of the page is // favicon.ico should be HTTPS if the rest of the page is
$this->element('link', array('rel' => 'shortcut icon', $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')) { if (common_config('site', 'mobile')) {
@ -415,8 +417,7 @@ class Action extends HTMLOutputter // lawsuit
$this->script('extlib/jquery.form.js'); $this->script('extlib/jquery.form.js');
$this->script('extlib/jquery-ui/jquery-ui.js'); $this->script('extlib/jquery-ui/jquery-ui.js');
$this->script('extlib/jquery.cookie.js'); $this->script('extlib/jquery.cookie.js');
$this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/extlib/json2.js', StatusNet::isHTTPS()).'"); }'); $this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/extlib/json2.js', GNUsocial::isHTTPS()).'"); }');
$this->script('extlib/jquery.infieldlabel.js');
Event::handle('EndShowJQueryScripts', array($this)); Event::handle('EndShowJQueryScripts', array($this));
} }
@ -430,6 +431,7 @@ class Action extends HTMLOutputter // lawsuit
$this->inlineScript('var _peopletagAC = "' . $this->inlineScript('var _peopletagAC = "' .
common_local_url('peopletagautocomplete') . '";'); common_local_url('peopletagautocomplete') . '";');
$this->showScriptMessages(); $this->showScriptMessages();
$this->showScriptVariables();
// Anti-framing code to avoid clickjacking attacks in older browsers. // Anti-framing code to avoid clickjacking attacks in older browsers.
// This will show a blank page if the page is being framed, which is // This will show a blank page if the page is being framed, which is
// consistent with the behavior of the 'X-Frame-Options: SAMEORIGIN' // 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. // TRANS: Localized tooltip for '...' expansion button on overlong remote messages.
$messages['showmore_tooltip'] = _m('TOOLTIP', 'Show more'); $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()); $messages = array_merge($messages, $this->getScriptMessages());
Event::handle('EndScriptMessages', array($this, &$messages)); Event::handle('EndScriptMessages', array($this, &$messages));
@ -478,6 +474,19 @@ class Action extends HTMLOutputter // lawsuit
return $messages; 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: * 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', $this->elementStart('a', array('class' => 'home bookmark',
'href' => $url)); 'href' => $url));
if (StatusNet::isHTTPS()) { if (GNUsocial::isHTTPS()) {
$logoUrl = common_config('site', 'ssllogo'); $logoUrl = common_config('site', 'ssllogo');
if (empty($logoUrl)) { if (empty($logoUrl)) {
// if logo is an uploaded file, try to fall back to HTTPS file URL // 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'); $image = common_config('license', 'image');
$sslimage = common_config('license', 'sslimage'); $sslimage = common_config('license', 'sslimage');
if (StatusNet::isHTTPS()) { if (GNUsocial::isHTTPS()) {
if (!empty($sslimage)) { if (!empty($sslimage)) {
$url = $sslimage; $url = $sslimage;
} else if (preg_match('#^http://i.creativecommons.org/#', $image)) { } 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 * Integer value of an argument
* *

View File

@ -160,10 +160,11 @@ abstract class ActivityHandlerPlugin extends Plugin
* @fixme are there any standard options? * @fixme are there any standard options?
* *
* @param Activity $activity * @param Activity $activity
* @param Profile $actor * @param Notice $stored The notice in our database for this certain object
* @param array $options=array() * @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()) 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, * 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. * 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 // $this->oldSaveNew is there during a migration period of plugins, to start using
// Notice::saveActivity instead of Notice::saveNew // Notice::saveActivity instead of Notice::saveNew
if (!$this->isMyActivity($act) || isset($this->oldSaveNew)) { if (!$this->isMyActivity($act) || isset($this->oldSaveNew)) {
@ -182,7 +183,13 @@ abstract class ActivityHandlerPlugin extends Plugin
} }
$object = $this->saveObjectFromActivity($act, $stored, $options); $object = $this->saveObjectFromActivity($act, $stored, $options);
try { 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) { } catch (Exception $e) {
common_debug('WARNING: Could not get attention list from object '.get_class($object).'!'); 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->showNoticeSource();
$nli->showNoticeLocation(); $nli->showNoticeLocation();
$nli->showPermalink(); $nli->showPermalink();
$nli->showRepeat();
$nli->showNoticeOptions(); $nli->showNoticeOptions();
} }

View File

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

View File

@ -512,11 +512,11 @@ class ActivityObject
switch (self::canonicalType($object->type)) { switch (self::canonicalType($object->type)) {
case 'image': case 'image':
$object->largerImage = $file->url; $object->largerImage = $file->getUrl();
break; break;
case 'video': case 'video':
case 'audio': case 'audio':
$object->stream = $file->url; $object->stream = $file->getUrl();
break; break;
} }
@ -861,7 +861,7 @@ class ActivityObject
if (is_string($this->thumbnail)) { if (is_string($this->thumbnail)) {
$object['image'] = array('url' => $this->thumbnail); $object['image'] = array('url' => $this->thumbnail);
} else { } else {
$object['image'] = array('url' => $this->thumbnail->url); $object['image'] = array('url' => $this->thumbnail->getUrl());
if ($this->thumbnail->width) { if ($this->thumbnail->width) {
$object['image']['width'] = $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 SHARE = 'http://activitystrea.ms/schema/1.0/share';
const SAVE = 'http://activitystrea.ms/schema/1.0/save'; const SAVE = 'http://activitystrea.ms/schema/1.0/save';
const FAVORITE = 'http://activitystrea.ms/schema/1.0/favorite'; 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 PLAY = 'http://activitystrea.ms/schema/1.0/play';
const FOLLOW = 'http://activitystrea.ms/schema/1.0/follow'; const FOLLOW = 'http://activitystrea.ms/schema/1.0/follow';
const FRIEND = 'http://activitystrea.ms/schema/1.0/make-friend'; 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 // Custom OStatus verbs for the flipside until they're standardized
const DELETE = 'http://ostatus.org/schema/1.0/unfollow'; 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 UNFOLLOW = 'http://ostatus.org/schema/1.0/unfollow';
const LEAVE = 'http://ostatus.org/schema/1.0/leave'; const LEAVE = 'http://ostatus.org/schema/1.0/leave';
const UNTAG = 'http://ostatus.org/schema/1.0/untag'; const UNTAG = 'http://ostatus.org/schema/1.0/untag';

View File

@ -59,24 +59,8 @@ class AdminPanelNav extends Menu
$nickname = $user->nickname; $nickname = $user->nickname;
$name = $user->getProfile()->getBestName(); $name = $user->getProfile()->getBestName();
// Stub section w/ home link $stub = new HomeStubNav($this->action);
$this->action->elementStart('ul'); $this->submenu(_m('MENU','Home'), $stub);
$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');
$this->action->elementStart('ul'); $this->action->elementStart('ul');
$this->action->elementStart('li'); $this->action->elementStart('li');

View File

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

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